/* -*- 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.
 *
 ************************************************************************/

#include "precompiled_vcl.hxx"

#include "vcl/arrange.hxx"
#include "vcl/edit.hxx"

#include "osl/diagnose.h"

using namespace vcl;

// ----------------------------------------
// vcl::WindowArranger
//-----------------------------------------

WindowArranger::~WindowArranger()
{}

void WindowArranger::setParent( WindowArranger* i_pParent )
{
    OSL_VERIFY( i_pParent->m_pParentWindow == m_pParentWindow || m_pParentWindow == NULL );

    m_pParentArranger = i_pParent;
    m_pParentWindow = i_pParent->m_pParentWindow;
    setParentWindow( m_pParentWindow );
}

void WindowArranger::setParentWindow( Window* i_pNewParent )
{
    m_pParentWindow = i_pNewParent;

    size_t nEle = countElements();
    for( size_t i = 0; i < nEle; i++ )
    {
        Element* pEle = getElement( i );
        if( pEle ) // sanity check
        {
            #if OSL_DEBUG_LEVEL > 0
            if( pEle->m_pElement )
            {
                OSL_VERIFY( pEle->m_pElement->GetParent() == i_pNewParent );
            }
            #endif
            if( pEle->m_pChild )
                pEle->m_pChild->setParentWindow( i_pNewParent );
        }
    }
}

void WindowArranger::show( bool i_bShow, bool i_bImmediateUpdate )
{
    size_t nEle = countElements();
    for( size_t i = 0; i < nEle; i++ )
    {
        Element* pEle = getElement( i );
        if( pEle ) // sanity check
        {
            pEle->m_bHidden = ! i_bShow;
            if( pEle->m_pElement )
                pEle->m_pElement->Show( i_bShow );
            if( pEle->m_pChild.get() )
                pEle->m_pChild->show( i_bShow, false );
        }
    }
    if( m_pParentArranger )
    {
        nEle = m_pParentArranger->countElements();
        for( size_t i = 0; i < nEle; i++ )
        {
            Element* pEle = m_pParentArranger->getElement( i );
            if( pEle && pEle->m_pChild.get() == this )
            {
                pEle->m_bHidden = ! i_bShow;
                break;
            }
        }
    }
    if( i_bImmediateUpdate )
    {
        // find the topmost parent
        WindowArranger* pResize = this;
        while( pResize->m_pParentArranger )
            pResize = pResize->m_pParentArranger;
        pResize->resize();
    }
}

bool WindowArranger::isVisible() const
{
    size_t nEle = countElements();
    for( size_t i = 0; i < nEle; i++ )
    {
        const Element* pEle = getConstElement( i );
        if( pEle->isVisible() )
            return true;
    }
    return false;
}

bool WindowArranger::Element::isVisible() const
{
    bool bVisible = false;
    if( ! m_bHidden )
    {
        if( m_pElement )
            bVisible = m_pElement->IsVisible();
        else if( m_pChild )
            bVisible = m_pChild->isVisible();
    }
    return bVisible;
}

sal_Int32 WindowArranger::Element::getExpandPriority() const
{
    sal_Int32 nPrio = m_nExpandPriority;
    if( m_pChild && m_nExpandPriority >= 0 )
    {
        size_t nElements = m_pChild->countElements();
        for( size_t i = 0; i < nElements; i++ )
        {
            sal_Int32 nCPrio = m_pChild->getExpandPriority( i );
            if( nCPrio > nPrio )
                nPrio = nCPrio;
        }
    }
    return nPrio;
}

Size WindowArranger::Element::getOptimalSize( WindowSizeType i_eType ) const
{
    Size aResult;
    if( ! m_bHidden )
    {
        if( m_pElement && m_pElement->IsVisible() )
            aResult = m_pElement->GetOptimalSize( i_eType );
        else if( m_pChild )
            aResult = m_pChild->getOptimalSize( i_eType );
        if( aResult.Width() < m_aMinSize.Width() )
            aResult.Width() = m_aMinSize.Width();
        if( aResult.Height() < m_aMinSize.Height() )
            aResult.Height() = m_aMinSize.Height();
        aResult.Width() += m_nLeftBorder + m_nRightBorder;
        aResult.Height() += m_nTopBorder + m_nBottomBorder;
    }

    return aResult;
}

void WindowArranger::Element::setPosSize( const Point& i_rPos, const Size& i_rSize )
{
    Point aPoint( i_rPos );
    Size aSize( i_rSize );
    aPoint.X() += m_nLeftBorder;
    aPoint.Y() += m_nTopBorder;
    aSize.Width() -= m_nLeftBorder + m_nRightBorder;
    aSize.Height() -= m_nTopBorder + m_nBottomBorder;
    if( m_pElement )
        m_pElement->SetPosSizePixel( aPoint, aSize );
    else if( m_pChild )
        m_pChild->setManagedArea( Rectangle( aPoint, aSize ) );
}

// ----------------------------------------
// vcl::RowOrColumn
//-----------------------------------------

RowOrColumn::~RowOrColumn()
{
    for( std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
         it != m_aElements.end(); ++it )
    {
        it->deleteChild();
    }
}

Size RowOrColumn::getOptimalSize( WindowSizeType i_eType ) const
{
    Size aRet( 0, 0 );
    for( std::vector< WindowArranger::Element >::const_iterator it = m_aElements.begin();
         it != m_aElements.end(); ++it )
    {
        if( it->isVisible() )
        {
            // get the size of type of the managed element
            Size aElementSize( it->getOptimalSize( i_eType ) );
            if( m_bColumn )
            {
                // add the distance between elements
                aRet.Height() += m_nBorderWidth;
                // check if the width needs adjustment
                if( aRet.Width() < aElementSize.Width() )
                    aRet.Width() = aElementSize.Width();
                aRet.Height() += aElementSize.Height();
            }
            else
            {
                // add the distance between elements
                aRet.Width() += m_nBorderWidth;
                // check if the height needs adjustment
                if( aRet.Height() < aElementSize.Height() )
                    aRet.Height() = aElementSize.Height();
                aRet.Width() += aElementSize.Width();
            }
        }
    }

    if( aRet.Width() != 0 || aRet.Height() != 0 )
    {
        // subtract the border for the first element
        if( m_bColumn )
            aRet.Height() -= m_nBorderWidth;
        else
            aRet.Width() -= m_nBorderWidth;

        // add the outer border
        aRet.Width() += 2*m_nOuterBorder;
        aRet.Height() += 2*m_nOuterBorder;
    }

    return aRet;
}

void RowOrColumn::distributeRowWidth( std::vector<Size>& io_rSizes, long /*i_nUsedWidth*/, long i_nExtraWidth )
{
    if( ! io_rSizes.empty() && io_rSizes.size() == m_aElements.size() )
    {
        // find all elements with the highest expand priority
        size_t nElements = m_aElements.size();
        std::vector< size_t > aIndices;
        sal_Int32 nHighPrio = 0;
        for( size_t i = 0; i < nElements; i++ )
        {
            if( m_aElements[ i ].isVisible() )
            {
                sal_Int32 nCurPrio = m_aElements[ i ].getExpandPriority();
                if( nCurPrio > nHighPrio )
                {
                    aIndices.clear();
                    nHighPrio = nCurPrio;
                }
                if( nCurPrio == nHighPrio )
                    aIndices.push_back( i );
            }
        }

        // distribute extra space evenly among collected elements
        nElements = aIndices.size();
        if( nElements > 0 )
        {
            long nDelta = i_nExtraWidth / nElements;
            for( size_t i = 0; i < nElements; i++ )
            {
                io_rSizes[ aIndices[i] ].Width() += nDelta;
                i_nExtraWidth -= nDelta;
            }
            // add the last pixels to the last row element
            if( i_nExtraWidth > 0 && nElements > 0 )
                io_rSizes[aIndices.back()].Width() += i_nExtraWidth;
        }
    }
}

void RowOrColumn::distributeColumnHeight( std::vector<Size>& io_rSizes, long /*i_nUsedHeight*/, long i_nExtraHeight )
{
    if( ! io_rSizes.empty() && io_rSizes.size() == m_aElements.size() )
    {
        // find all elements with the highest expand priority
        size_t nElements = m_aElements.size();
        std::vector< size_t > aIndices;
        sal_Int32 nHighPrio = 3;
        for( size_t i = 0; i < nElements; i++ )
        {
            if( m_aElements[ i ].isVisible() )
            {
                sal_Int32 nCurPrio = m_aElements[ i ].getExpandPriority();
                if( nCurPrio > nHighPrio )
                {
                    aIndices.clear();
                    nHighPrio = nCurPrio;
                }
                if( nCurPrio == nHighPrio )
                    aIndices.push_back( i );
            }
        }

        // distribute extra space evenly among collected elements
        nElements = aIndices.size();
        if( nElements > 0 )
        {
            long nDelta = i_nExtraHeight / nElements;
            for( size_t i = 0; i < nElements; i++ )
            {
                io_rSizes[ aIndices[i] ].Height() += nDelta;
                i_nExtraHeight -= nDelta;
            }
            // add the last pixels to the last row element
            if( i_nExtraHeight > 0 && nElements > 0 )
                io_rSizes[aIndices.back()].Height() += i_nExtraHeight;
        }
    }
}

void RowOrColumn::resize()
{
    // check if we can get optimal size, else fallback to minimal size
    Size aOptSize( getOptimalSize( WINDOWSIZE_PREFERRED ) );
    WindowSizeType eType = WINDOWSIZE_PREFERRED;
    if( m_bColumn )
    {
        if( aOptSize.Height() > m_aManagedArea.GetHeight() )
            eType = WINDOWSIZE_MINIMUM;
    }
    else
    {
        if( aOptSize.Width() > m_aManagedArea.GetWidth() )
            eType = WINDOWSIZE_MINIMUM;
    }

    size_t nElements = m_aElements.size();
    // get all element sizes for sizing
    std::vector<Size> aElementSizes( nElements );
    long nUsedWidth = 2*m_nOuterBorder - (nElements ? m_nBorderWidth : 0);
    for( size_t i = 0; i < nElements; i++ )
    {
        if( m_aElements[i].isVisible() )
        {
            aElementSizes[i] = m_aElements[i].getOptimalSize( eType );
            if( m_bColumn )
            {
                aElementSizes[i].Width() = m_aManagedArea.GetWidth() - 2* m_nOuterBorder;
                nUsedWidth += aElementSizes[i].Height() + m_nBorderWidth;
            }
            else
            {
                aElementSizes[i].Height() = m_aManagedArea.GetHeight() - 2* m_nOuterBorder;
                nUsedWidth += aElementSizes[i].Width() + m_nBorderWidth;
            }
        }
    }

    long nExtraWidth = (m_bColumn ? m_aManagedArea.GetHeight() : m_aManagedArea.GetWidth()) - nUsedWidth;
    if( nExtraWidth > 0 )
    {
        if( m_bColumn )
            distributeColumnHeight( aElementSizes, nUsedWidth, nExtraWidth );
        else
            distributeRowWidth( aElementSizes, nUsedWidth, nExtraWidth );
    }

    // get starting position
    Point aElementPos( m_aManagedArea.TopLeft() );
    // outer border
    aElementPos.X() += m_nOuterBorder;
    aElementPos.Y() += m_nOuterBorder;

    // position managed windows
    for( size_t i = 0; i < nElements; i++ )
    {
        // get the size of type of the managed element
        if( m_aElements[i].isVisible() )
        {
            m_aElements[i].setPosSize( aElementPos, aElementSizes[i] );
            if( m_bColumn )
                aElementPos.Y() += m_nBorderWidth + aElementSizes[i].Height();
            else
                aElementPos.X() += m_nBorderWidth + aElementSizes[i].Width();
        }
    }
}

size_t RowOrColumn::addWindow( Window* i_pWindow, sal_Int32 i_nExpandPrio, size_t i_nIndex )
{
    size_t nIndex = i_nIndex;
    if( i_nIndex >= m_aElements.size() )
    {
        nIndex = m_aElements.size();
        m_aElements.push_back( WindowArranger::Element( i_pWindow, boost::shared_ptr<WindowArranger>(), i_nExpandPrio ) );
    }
    else
    {
        std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
        while( i_nIndex-- )
            ++it;
        m_aElements.insert( it, WindowArranger::Element( i_pWindow, boost::shared_ptr<WindowArranger>(), i_nExpandPrio ) );
    }
    return nIndex;
}

size_t RowOrColumn::addChild( boost::shared_ptr<WindowArranger> const & i_pChild, sal_Int32 i_nExpandPrio, size_t i_nIndex )
{
    size_t nIndex = i_nIndex;
    if( i_nIndex >= m_aElements.size() )
    {
        nIndex = m_aElements.size();
        m_aElements.push_back( WindowArranger::Element( NULL, i_pChild, i_nExpandPrio ) );
    }
    else
    {
        std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
        while( i_nIndex-- )
            ++it;
        m_aElements.insert( it, WindowArranger::Element( NULL, i_pChild, i_nExpandPrio ) );
    }
    return nIndex;
}

void RowOrColumn::remove( Window* i_pWindow )
{
    if( i_pWindow )
    {
        for( std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
            it != m_aElements.end(); ++it )
        {
            if( it->m_pElement == i_pWindow )
            {
                m_aElements.erase( it );
                return;
            }
        }
    }
}

void RowOrColumn::remove( boost::shared_ptr<WindowArranger> const & i_pChild )
{
    if( i_pChild )
    {
        for( std::vector< WindowArranger::Element >::iterator it = m_aElements.begin();
            it != m_aElements.end(); ++it )
        {
            if( it->m_pChild == i_pChild )
            {
                m_aElements.erase( it );
                return;
            }
        }
    }
}

// ----------------------------------------
// vcl::LabeledElement
//-----------------------------------------

LabeledElement::~LabeledElement()
{
    m_aLabel.deleteChild();
    m_aElement.deleteChild();
}

Size LabeledElement::getOptimalSize( WindowSizeType i_eType ) const
{
    Size aRet( m_aLabel.getOptimalSize( WINDOWSIZE_MINIMUM ) );
    if( aRet.Width() != 0 )
    {
        if( m_nLabelColumnWidth != 0 )
            aRet.Width() = m_nLabelColumnWidth;
        else
            aRet.Width() += m_nDistance;
    }
    Size aElementSize( m_aElement.getOptimalSize( i_eType ) );
    aRet.Width() += aElementSize.Width();
    if( aElementSize.Height() > aRet.Height() )
        aRet.Height() = aElementSize.Height();
    if( aRet.Height() != 0 )
        aRet.Height() += 2*m_nOuterBorder;

    return aRet;
}

void LabeledElement::resize()
{
    Size aLabelSize( m_aLabel.getOptimalSize( WINDOWSIZE_MINIMUM ) );
    Size aElementSize( m_aElement.getOptimalSize( WINDOWSIZE_PREFERRED ) );
    if( m_nDistance + aLabelSize.Width() + aElementSize.Width() > m_aManagedArea.GetWidth() )
        aElementSize = m_aElement.getOptimalSize( WINDOWSIZE_MINIMUM );

    // align label and element vertically in LabeledElement
    long nYOff = (m_aManagedArea.GetHeight() - 2*m_nOuterBorder - aLabelSize.Height()) / 2;
    Point aPos( m_aManagedArea.Left(),
                m_aManagedArea.Top() + m_nOuterBorder + nYOff );
    Size aSize( aLabelSize );
    if( m_nLabelColumnWidth != 0 )
        aSize.Width() = m_nLabelColumnWidth;
    m_aLabel.setPosSize( aPos, aSize );

    aPos.X() += aSize.Width() + m_nDistance;
    nYOff = (m_aManagedArea.GetHeight() - 2*m_nOuterBorder - aElementSize.Height()) / 2;
    aPos.Y() = m_aManagedArea.Top() + m_nOuterBorder + nYOff;
    aSize.Width() = aElementSize.Width();
    aSize.Height() = m_aManagedArea.GetHeight() - 2*m_nOuterBorder;

    // label style
    // 0: position left and right
    // 1: keep the element close to label and grow it
    // 2: keep the element close and don't grow it
    if( m_nLabelStyle == 0)
    {
        if( aPos.X() + aSize.Width() < m_aManagedArea.Right() )
            aPos.X() = m_aManagedArea.Right() - aSize.Width();
    }
    else if( m_nLabelStyle == 1 )
    {
        if( aPos.X() + aSize.Width() < m_aManagedArea.Right() )
            aSize.Width() = m_aManagedArea.Right() - aPos.X();
    }
    m_aElement.setPosSize( aPos, aSize );
}

void LabeledElement::setLabel( Window* i_pLabel )
{
    m_aLabel.m_pElement = i_pLabel;
    m_aLabel.m_pChild.reset();
}

void LabeledElement::setLabel( boost::shared_ptr<WindowArranger> const & i_pLabel )
{
    m_aLabel.m_pElement = NULL;
    m_aLabel.m_pChild = i_pLabel;
}

void LabeledElement::setElement( Window* i_pElement )
{
    m_aElement.m_pElement = i_pElement;
    m_aElement.m_pChild.reset();
}

void LabeledElement::setElement( boost::shared_ptr<WindowArranger> const & i_pElement )
{
    m_aElement.m_pElement = NULL;
    m_aElement.m_pChild = i_pElement;
}

// ----------------------------------------
// vcl::LabelColumn
//-----------------------------------------
LabelColumn::~LabelColumn()
{
}

long LabelColumn::getLabelWidth() const
{
    long nWidth = 0;

    size_t nEle = countElements();
    for( size_t i = 0; i < nEle; i++ )
    {
        const Element* pEle = getConstElement( i );
        if( pEle && pEle->m_pChild.get() )
        {
            const LabeledElement* pLabel = dynamic_cast< const LabeledElement* >(pEle->m_pChild.get());
            if( pLabel )
            {
                Window* pLW = pLabel->getWindow( 0 );
                if( pLW )
                {
                    Size aLabSize( pLW->GetOptimalSize( WINDOWSIZE_MINIMUM ) );
                    if( aLabSize.Width() > nWidth )
                        nWidth = aLabSize.Width();
                }
            }
        }
    }
    return nWidth + getBorderWidth();
}

Size LabelColumn::getOptimalSize( WindowSizeType i_eType ) const
{
    long nWidth = getLabelWidth();
    Size aColumnSize;

    // every child is a LabeledElement
    size_t nEle = countElements();
    for( size_t i = 0; i < nEle; i++ )
    {
        Size aElementSize;
        const Element* pEle = getConstElement( i );
        if( pEle && pEle->m_pChild.get() )
        {
            const LabeledElement* pLabel = dynamic_cast< const LabeledElement* >(pEle->m_pChild.get());
            if( pLabel ) // we have a label
            {
                aElementSize = pLabel->getLabelSize( WINDOWSIZE_MINIMUM );
                if( aElementSize.Width() )
                    aElementSize.Width() = nWidth;
                Size aSize( pLabel->getElementSize( i_eType ) );
                aElementSize.Width() += aSize.Width();
                if( aSize.Height() > aElementSize.Height() )
                    aElementSize.Height() = aSize.Height();
            }
            else // a non label, just treat it as a row
            {
                aElementSize = pEle->getOptimalSize( i_eType );
            }
        }
        else if( pEle && pEle->m_pElement ) // a general window, treat is as a row
        {
            aElementSize = pEle->getOptimalSize( i_eType );
        }
        if( aElementSize.Width() )
        {
            aElementSize.Width() += 2*m_nOuterBorder;
            if( aElementSize.Width() > aColumnSize.Width() )
                aColumnSize.Width() = aElementSize.Width();
        }
        if( aElementSize.Height() )
        {
            aColumnSize.Height() += getBorderWidth() + aElementSize.Height();
        }
    }
    if( nEle > 0 && aColumnSize.Height() )
    {
        aColumnSize.Height() -= getBorderWidth(); // for the first element
        aColumnSize.Height() += 2*m_nOuterBorder;
    }
    return aColumnSize;
}

void LabelColumn::resize()
{
    long nWidth = getLabelWidth();
    size_t nEle = countElements();
    for( size_t i = 0; i < nEle; i++ )
    {
        Element* pEle = getElement( i );
        if( pEle && pEle->m_pChild.get() )
        {
            LabeledElement* pLabel = dynamic_cast< LabeledElement* >(pEle->m_pChild.get());
            if( pLabel )
                pLabel->setLabelColumnWidth( nWidth );
        }
    }
    RowOrColumn::resize();
}

size_t LabelColumn::addRow( Window* i_pLabel, boost::shared_ptr<WindowArranger> const& i_rElement, long i_nIndent )
{
    boost::shared_ptr< LabeledElement > xLabel( new LabeledElement( this, 1 ) );
    xLabel->setLabel( i_pLabel );
    xLabel->setBorders( 0, i_nIndent, 0, 0, 0 );
    xLabel->setElement( i_rElement );
    size_t nIndex = addChild( xLabel );
    resize();
    return nIndex;
}

size_t LabelColumn::addRow( Window* i_pLabel, Window* i_pElement, long i_nIndent )
{
    boost::shared_ptr< LabeledElement > xLabel( new LabeledElement( this, 1 ) );
    xLabel->setLabel( i_pLabel );
    xLabel->setBorders( 0, i_nIndent, 0, 0, 0 );
    xLabel->setElement( i_pElement );
    size_t nIndex = addChild( xLabel );
    resize();
    return nIndex;
}

// ----------------------------------------
// vcl::Indenter
//-----------------------------------------

Indenter::~Indenter()
{
    m_aElement.deleteChild();
}

Size Indenter::getOptimalSize( WindowSizeType i_eType ) const
{
    Size aSize( m_aElement.getOptimalSize( i_eType ) );
    aSize.Width()  += 2*m_nOuterBorder + m_nIndent;
    aSize.Height() += 2*m_nOuterBorder;
    return aSize;
}

void Indenter::resize()
{
    Point aPt( m_aManagedArea.TopLeft() );
    aPt.X() += m_nOuterBorder + m_nIndent;
    aPt.Y() += m_nOuterBorder;
    Size aSz( m_aManagedArea.GetSize() );
    aSz.Width()  -= 2*m_nOuterBorder + m_nIndent;
    aSz.Height() -= 2*m_nOuterBorder;
    m_aElement.setPosSize( aPt, aSz );
}

void Indenter::setWindow( Window* i_pWindow, sal_Int32 i_nExpandPrio )
{
    OSL_VERIFY( (m_aElement.m_pElement == 0 && m_aElement.m_pChild == 0) || i_pWindow == 0 );
    OSL_VERIFY( i_pWindow == 0 || i_pWindow->GetParent() == m_pParentWindow );
    m_aElement.m_pElement = i_pWindow;
    m_aElement.m_nExpandPriority = i_nExpandPrio;
}

void Indenter::setChild( boost::shared_ptr<WindowArranger> const & i_pChild, sal_Int32 i_nExpandPrio )
{
    OSL_VERIFY( (m_aElement.m_pElement == 0 && m_aElement.m_pChild == 0 ) || i_pChild == 0 );
    m_aElement.m_pChild = i_pChild;
    m_aElement.m_nExpandPriority = i_nExpandPrio;
}

// ----------------------------------------
// vcl::MatrixArranger
//-----------------------------------------
MatrixArranger::~MatrixArranger()
{
}

Size MatrixArranger::getOptimalSize( WindowSizeType i_eType, std::vector<long>& o_rColumnWidths, std::vector<long>& o_rRowHeights ) const
{
    Size aMatrixSize( 2*m_nOuterBorder, 2*m_nOuterBorder );

    // first find out the current number of rows and columns
    sal_uInt32 nRows = 0, nColumns = 0;
    for( std::vector< MatrixElement >::const_iterator it = m_aElements.begin();
            it != m_aElements.end(); ++it )
    {
        if( it->m_nX >= nColumns )
            nColumns = it->m_nX+1;
        if( it->m_nY >= nRows )
            nRows = it->m_nY+1;
    }

    // now allocate row and column depth vectors
    o_rColumnWidths = std::vector< long >( nColumns, 0 );
    o_rRowHeights   = std::vector< long >( nRows, 0 );

    // get sizes an allocate them into rows/columns
    for( std::vector< MatrixElement >::const_iterator it = m_aElements.begin();
            it != m_aElements.end(); ++it )
    {
        Size aSize( it->getOptimalSize( i_eType ) );
        if( aSize.Width() > o_rColumnWidths[ it->m_nX ] )
            o_rColumnWidths[ it->m_nX ] = aSize.Width();
        if( aSize.Height() > o_rRowHeights[ it->m_nY ] )
            o_rRowHeights[ it->m_nY ] = aSize.Height();
    }

    // add up sizes
    for( sal_uInt32 i = 0; i < nColumns; i++ )
        aMatrixSize.Width() += o_rColumnWidths[i] + m_nBorderX;
    if( nColumns > 0 )
        aMatrixSize.Width() -= m_nBorderX;

    for( sal_uInt32 i = 0; i < nRows; i++ )
        aMatrixSize.Height() += o_rRowHeights[i] + m_nBorderY;
    if( nRows > 0 )
        aMatrixSize.Height() -= m_nBorderY;

    return aMatrixSize;
}

Size MatrixArranger::getOptimalSize( WindowSizeType i_eType ) const
{
    std::vector<long> aColumnWidths, aRowHeights;
    return getOptimalSize( i_eType, aColumnWidths, aRowHeights );
}

void MatrixArranger::resize()
{
    // assure that we have at least one row and column
    if( m_aElements.empty() )
        return;

    // check if we can get optimal size, else fallback to minimal size
    std::vector<long> aColumnWidths, aRowHeights;
    Size aOptSize( getOptimalSize( WINDOWSIZE_PREFERRED, aColumnWidths, aRowHeights ) );
    if( aOptSize.Height() > m_aManagedArea.GetHeight() ||
        aOptSize.Width() > m_aManagedArea.GetWidth() )
    {
        std::vector<long> aMinColumnWidths, aMinRowHeights;
        getOptimalSize( WINDOWSIZE_MINIMUM, aMinColumnWidths, aMinRowHeights );
        if( aOptSize.Height() > m_aManagedArea.GetHeight() )
            aRowHeights = aMinRowHeights;
        if( aOptSize.Width() > m_aManagedArea.GetWidth() )
            aColumnWidths = aMinColumnWidths;
    }

    // FIXME: distribute extra space available

    // prepare offsets
    std::vector<long> aColumnX( aColumnWidths.size() );
    aColumnX[0] = m_aManagedArea.Left() + m_nOuterBorder;
    for( size_t i = 1; i < aColumnX.size(); i++ )
        aColumnX[i] = aColumnX[i-1] + aColumnWidths[i-1] + m_nBorderX;

    std::vector<long> aRowY( aRowHeights.size() );
    aRowY[0] = m_aManagedArea.Top() + m_nOuterBorder;
    for( size_t i = 1; i < aRowY.size(); i++ )
        aRowY[i] = aRowY[i-1] + aRowHeights[i-1] + m_nBorderY;

    // now iterate over the elements and assign their positions
    for( std::vector< MatrixElement >::iterator it = m_aElements.begin();
         it != m_aElements.end(); ++it )
    {
        Point aCellPos( aColumnX[it->m_nX], aRowY[it->m_nY] );
        Size  aCellSize( aColumnWidths[it->m_nX], aRowHeights[it->m_nY] );
        it->setPosSize( aCellPos, aCellSize );
    }
}

size_t MatrixArranger::addWindow( Window* i_pWindow, sal_uInt32 i_nX, sal_uInt32 i_nY, sal_Int32 i_nExpandPrio )
{
    sal_uInt64 nMapValue = getMap( i_nX, i_nY );
    std::map< sal_uInt64, size_t >::const_iterator it = m_aMatrixMap.find( nMapValue );
    size_t nIndex = 0;
    if( it == m_aMatrixMap.end() )
    {
        m_aMatrixMap[ nMapValue ] = nIndex = m_aElements.size();
        m_aElements.push_back( MatrixElement( i_pWindow, i_nX, i_nY, boost::shared_ptr<WindowArranger>(), i_nExpandPrio ) );
    }
    else
    {
        MatrixElement& rEle( m_aElements[ it->second ] );
        rEle.m_pElement = i_pWindow;
        rEle.m_pChild.reset();
        rEle.m_nExpandPriority = i_nExpandPrio;
        rEle.m_nX = i_nX;
        rEle.m_nY = i_nY;
        nIndex = it->second;
    }
    return nIndex;
}

void MatrixArranger::remove( Window* i_pWindow )
{
    if( i_pWindow )
    {
        for( std::vector< MatrixElement >::iterator it = m_aElements.begin();
            it != m_aElements.end(); ++it )
        {
            if( it->m_pElement == i_pWindow )
            {
                m_aMatrixMap.erase( getMap( it->m_nX, it->m_nY ) );
                m_aElements.erase( it );
                return;
            }
        }
    }
}

size_t MatrixArranger::addChild( boost::shared_ptr<WindowArranger> const &i_pChild, sal_uInt32 i_nX, sal_uInt32 i_nY, sal_Int32 i_nExpandPrio )
{
    sal_uInt64 nMapValue = getMap( i_nX, i_nY );
    std::map< sal_uInt64, size_t >::const_iterator it = m_aMatrixMap.find( nMapValue );
    size_t nIndex = 0;
    if( it == m_aMatrixMap.end() )
    {
        m_aMatrixMap[ nMapValue ] = nIndex = m_aElements.size();
        m_aElements.push_back( MatrixElement( NULL, i_nX, i_nY, i_pChild, i_nExpandPrio ) );
    }
    else
    {
        MatrixElement& rEle( m_aElements[ it->second ] );
        rEle.m_pElement = 0;
        rEle.m_pChild = i_pChild;
        rEle.m_nExpandPriority = i_nExpandPrio;
        rEle.m_nX = i_nX;
        rEle.m_nY = i_nY;
        nIndex = it->second;
    }
    return nIndex;
}

void MatrixArranger::remove( boost::shared_ptr<WindowArranger> const &i_pChild )
{
    if( i_pChild )
    {
        for( std::vector< MatrixElement >::iterator it = m_aElements.begin();
            it != m_aElements.end(); ++it )
        {
            if( it->m_pChild == i_pChild )
            {
                m_aMatrixMap.erase( getMap( it->m_nX, it->m_nY ) );
                m_aElements.erase( it );
                return;
            }
        }
    }
}

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