/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <string.h>

#include <o3tl/safeint.hxx>
#include <sal/log.hxx>

#include <vcl/event.hxx>
#include <vcl/wall.hxx>
#include <vcl/help.hxx>
#include <vcl/splitwin.hxx>
#include <vcl/settings.hxx>
#include <vcl/ptrstyle.hxx>

#include <svdata.hxx>
#include <strings.hrc>


#define SPLITWIN_SPLITSIZE              4
#define SPLITWIN_SPLITSIZEEX            4
#define SPLITWIN_SPLITSIZEEXLN          7
#define SPLITWIN_SPLITSIZEAUTOHIDE      72
#define SPLITWIN_SPLITSIZEFADE          72

#define SPLIT_HORZ              (sal_uInt16(0x0001))
#define SPLIT_VERT              (sal_uInt16(0x0002))
#define SPLIT_WINDOW            (sal_uInt16(0x0004))
#define SPLIT_NOSPLIT           (sal_uInt16(0x8000))

namespace {

class ImplSplitItem
{
public:
    ImplSplitItem();

    long                mnSize;
    long                mnPixSize;
    long                mnLeft;
    long                mnTop;
    long                mnWidth;
    long                mnHeight;
    long                mnSplitPos;
    long                mnSplitSize;
    long                mnOldSplitPos;
    long                mnOldSplitSize;
    long                mnOldWidth;
    long                mnOldHeight;
    std::unique_ptr<ImplSplitSet> mpSet;
    VclPtr<vcl::Window> mpWindow;
    VclPtr<vcl::Window> mpOrgParent;
    sal_uInt16          mnId;
    SplitWindowItemFlags mnBits;
    bool                mbFixed;
    bool                mbSubSize;
    /// Minimal width or height of the item.  -1 means no restriction.
    long                mnMinSize;
    /// Maximal width or height of the item.  -1 means no restriction.
    long                mnMaxSize;
};

}

class ImplSplitSet
{
public:
    ImplSplitSet();

    std::vector< ImplSplitItem > mvItems;
    long                mnLastSize;
    long                mnSplitSize;
    sal_uInt16          mnId;
    bool                mbCalcPix;
};

ImplSplitItem::ImplSplitItem()
    : mnSize(0)
    , mnPixSize(0)
    , mnLeft(0)
    , mnTop(0)
    , mnWidth(0)
    , mnHeight(0)
    , mnSplitPos(0)
    , mnSplitSize(0)
    , mnOldSplitPos(0)
    , mnOldSplitSize(0)
    , mnOldWidth(0)
    , mnOldHeight(0)
    , mnId(0)
    , mnBits(SplitWindowItemFlags::NONE)
    , mbFixed(false)
    , mbSubSize(false)
    , mnMinSize(-1)
    , mnMaxSize(-1)
{
}

ImplSplitSet::ImplSplitSet() :
    mnLastSize( 0 ),
    mnSplitSize( SPLITWIN_SPLITSIZE ),
    mnId( 0 ),
    mbCalcPix( true )
{
}

/** Check whether the given size is inside the valid range defined by
    [rItem.mnMinSize,rItem.mnMaxSize].  When it is not inside it then return
    the upper or lower bound, respectively. Otherwise return the given size
    unmodified.
    Note that either mnMinSize and/or mnMaxSize can be -1 in which case the
    size has not lower or upper bound.
*/
namespace {
    long ValidateSize (const long nSize, const ImplSplitItem & rItem)
    {
        if (rItem.mnMinSize>=0 && nSize<rItem.mnMinSize)
            return rItem.mnMinSize;
        else if (rItem.mnMaxSize>0 && nSize>rItem.mnMaxSize)
            return rItem.mnMaxSize;
        else
            return nSize;
    }
}

static void ImplCalcBorder( WindowAlign eAlign,
                            long& rLeft, long& rTop,
                            long& rRight, long& rBottom )
{
    switch ( eAlign )
    {
    case WindowAlign::Top:
        rLeft   = 2;
        rTop    = 2;
        rRight  = 2;
        rBottom = 0;
        break;
    case WindowAlign::Left:
        rLeft   = 0;
        rTop    = 2;
        rRight  = 2;
        rBottom = 2;
        break;
    case WindowAlign::Bottom:
        rLeft   = 2;
        rTop    = 0;
        rRight  = 2;
        rBottom = 2;
        break;
    default:
        rLeft   = 0;
        rTop    = 2;
        rRight  = 2;
        rBottom = 2;
        break;
    }
}

void SplitWindow::ImplDrawBorder(vcl::RenderContext& rRenderContext)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
    long nDX = mnDX;
    long nDY = mnDY;

    switch (meAlign)
    {
    case WindowAlign::Bottom:
        rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
        rRenderContext.DrawLine(Point(0, 0), Point(nDX - 1, 0));
        rRenderContext.DrawLine(Point(0, nDY - 2), Point(nDX - 1, nDY - 2));

        rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
        rRenderContext.DrawLine(Point(0, 1), Point(nDX - 1, 1));
        rRenderContext.DrawLine(Point(0, nDY - 1), Point(nDX - 1, nDY - 1));
        break;
    case WindowAlign::Top:
        rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
        rRenderContext.DrawLine(Point(0, nDY - 2), Point(nDX - 1, nDY - 2));
        rRenderContext.DrawLine(Point(0, 0), Point(nDX - 1, 0));

        rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
        rRenderContext.DrawLine(Point(0, nDY - 1), Point(nDX - 1, nDY - 1));
        rRenderContext.DrawLine(Point(0, 1), Point(nDX - 1, 1));
        break;
    case WindowAlign::Left:
        rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
        rRenderContext.DrawLine(Point(nDX - 2, 0), Point(nDX - 2, nDY - 2));
        rRenderContext.DrawLine(Point(0, 0), Point(nDX - 1, 0));
        rRenderContext.DrawLine(Point(0, nDY - 2), Point(nDX - 2, nDY - 2));

        rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
        rRenderContext.DrawLine(Point(nDX - 1, 0), Point(nDX - 1, nDY - 1));
        rRenderContext.DrawLine(Point(0, 1), Point(nDX - 3, 1));
        rRenderContext.DrawLine(Point(0, nDY - 1), Point(nDX - 2, nDY - 1));
        break;
    default:
        rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
        rRenderContext.DrawLine(Point(0, 0), Point( 0, nDY - 2));
        rRenderContext.DrawLine(Point(0, 0), Point( nDX - 1, 0));
        rRenderContext.DrawLine(Point(0, nDY - 2), Point(nDX - 1, nDY - 2));

        rRenderContext.SetLineColor( rStyleSettings.GetLightColor());
        rRenderContext.DrawLine(Point(1, 1), Point(1, nDY - 3));
        rRenderContext.DrawLine(Point(1, 1), Point(nDX - 1, 1));
        rRenderContext.DrawLine(Point(0, nDY - 1), Point(nDX - 1, nDY - 1));
    }
}

void SplitWindow::ImplDrawBorderLine(vcl::RenderContext& rRenderContext)
{
    if (mbFadeOut)
    {
        const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
        long nDX = mnDX;
        long  nDY = mnDY;

        switch (meAlign)
        {
        case WindowAlign::Left:
            rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
            rRenderContext.DrawLine( Point( nDX-SPLITWIN_SPLITSIZEEXLN-1, 1 ), Point( nDX-SPLITWIN_SPLITSIZEEXLN-1, nDY-2 ) );

            rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
            rRenderContext.DrawLine( Point( nDX-SPLITWIN_SPLITSIZEEXLN, 1 ), Point( nDX-SPLITWIN_SPLITSIZEEXLN, nDY-3 ) );
            break;
        case WindowAlign::Right:
            rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
            rRenderContext.DrawLine( Point( SPLITWIN_SPLITSIZEEXLN-1, 0 ), Point( SPLITWIN_SPLITSIZEEXLN-1, nDY-2 ) );

            rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
            rRenderContext.DrawLine( Point( SPLITWIN_SPLITSIZEEXLN, 1 ), Point( SPLITWIN_SPLITSIZEEXLN, nDY-3 ) );
            break;
        case WindowAlign::Top:
            rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
            rRenderContext.DrawLine( Point( 0, nDY-SPLITWIN_SPLITSIZEEXLN-1 ), Point( nDX-1, nDY-SPLITWIN_SPLITSIZEEXLN-1 ) );

            rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
            rRenderContext.DrawLine( Point( 0, nDY-SPLITWIN_SPLITSIZEEXLN ), Point( nDX-1, nDY-SPLITWIN_SPLITSIZEEXLN ) );
            break;
        case WindowAlign::Bottom:
            rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
            rRenderContext.DrawLine( Point( 0, 5 ), Point( nDX-1, 5 ) );

            rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
            rRenderContext.DrawLine( Point( 0, SPLITWIN_SPLITSIZEEXLN ), Point( nDX-1, SPLITWIN_SPLITSIZEEXLN ) );
            break;
        }
    }
}

static ImplSplitSet* ImplFindSet( ImplSplitSet* pSet, sal_uInt16 nId )
{
    if ( pSet->mnId == nId )
        return pSet;

    std::vector< ImplSplitItem >&     rItems = pSet->mvItems;

    for ( const auto& rItem : rItems )
    {
        if ( rItem.mnId == nId )
            return rItem.mpSet.get();
    }

    for ( auto& rItem : rItems )
    {
        if ( rItem.mpSet )
        {
            ImplSplitSet* pFindSet = ImplFindSet( rItem.mpSet.get(), nId );
            if ( pFindSet )
                return pFindSet;
        }
    }

    return nullptr;
}

static ImplSplitSet* ImplFindItem( ImplSplitSet* pSet, sal_uInt16 nId, sal_uInt16& rPos )
{
    size_t              nItems = pSet->mvItems.size();
    std::vector< ImplSplitItem >&     rItems = pSet->mvItems;

    for ( size_t i = 0; i < nItems; i++ )
    {
        if ( rItems[i].mnId == nId )
        {
            rPos = i;
            return pSet;
        }
    }

    for ( auto& rItem : rItems )
    {
        if ( rItem.mpSet )
        {
            ImplSplitSet* pFindSet = ImplFindItem( rItem.mpSet.get(), nId, rPos );
            if ( pFindSet )
                return pFindSet;
        }
    }

    return nullptr;
}

static sal_uInt16 ImplFindItem( ImplSplitSet* pSet, vcl::Window* pWindow )
{
    std::vector< ImplSplitItem >&     rItems = pSet->mvItems;

    for ( auto& rItem : rItems )
    {
        if ( rItem.mpWindow == pWindow )
            return rItem.mnId;
        else
        {
            if ( rItem.mpSet )
            {
                sal_uInt16 nId = ImplFindItem( rItem.mpSet.get(), pWindow );
                if ( nId )
                    return nId;
            }
        }
    }

    return 0;
}

static sal_uInt16 ImplFindItem( ImplSplitSet* pSet, const Point& rPos,
                            bool bRows, bool bDown = true )
{
    std::vector< ImplSplitItem >&     rItems = pSet->mvItems;

    for ( auto& rItem : rItems )
    {
        if ( rItem.mnWidth && rItem.mnHeight )
        {
            Point       aPoint( rItem.mnLeft, rItem.mnTop );
            Size        aSize( rItem.mnWidth, rItem.mnHeight );
            tools::Rectangle   aRect( aPoint, aSize );
            if ( bRows )
            {
                if ( bDown )
                    aRect.AdjustBottom(pSet->mnSplitSize );
                else
                    aRect.AdjustTop( -(pSet->mnSplitSize) );
            }
            else
            {
                if ( bDown )
                    aRect.AdjustRight(pSet->mnSplitSize );
                else
                    aRect.AdjustLeft( -(pSet->mnSplitSize) );
            }

            if ( aRect.IsInside( rPos ) )
            {
                if ( rItem.mpSet && !rItem.mpSet->mvItems.empty() )
                {
                    return ImplFindItem( rItem.mpSet.get(), rPos,
                                        !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
                }
                else
                    return rItem.mnId;
            }
        }
    }

    return 0;
}

static void ImplCalcSet( ImplSplitSet* pSet,
                         long nSetLeft, long nSetTop,
                         long nSetWidth, long nSetHeight,
                         bool bRows, bool bDown = true )
{
    if ( pSet->mvItems.empty() )
        return;

    sal_uInt16          nMins;
    sal_uInt16          nCalcItems;
    size_t              nItems = pSet->mvItems.size();
    sal_uInt16          nAbsItems;
    long                nCalcSize;
    long                nPos;
    long                nMaxPos;
    std::vector< ImplSplitItem >&     rItems = pSet->mvItems;
    bool                bEmpty;

    // calculate sizes
    if ( bRows )
        nCalcSize = nSetHeight;
    else
        nCalcSize = nSetWidth;
    nCalcSize -= (rItems.size()-1)*pSet->mnSplitSize;
    if ( pSet->mbCalcPix || (pSet->mnLastSize != nCalcSize) )
    {
        long nPercentFactor = 10;
        long nRelCount      = 0;
        long nPercent       = 0;
        long nRelPercent    = 0;
        long nAbsSize       = 0;
        long nCurSize       = 0;
        for ( const auto& rItem : rItems )
        {
            if ( rItem.mnBits & SplitWindowItemFlags::RelativeSize )
                nRelCount += rItem.mnSize;
            else if ( rItem.mnBits & SplitWindowItemFlags::PercentSize )
                nPercent += rItem.mnSize;
            else
                nAbsSize += rItem.mnSize;
        }
        // map relative values to percentages (percentage here one tenth of a percent)
        nPercent *= nPercentFactor;
        if ( nRelCount )
        {
            long nRelPercentBase = 1000;
            while ( (nRelCount > nRelPercentBase) && (nPercentFactor < 100000) )
            {
                nRelPercentBase *= 10;
                nPercentFactor *= 10;
            }
            if ( nPercent < nRelPercentBase )
            {
                nRelPercent = (nRelPercentBase-nPercent)/nRelCount;
                nPercent += nRelPercent*nRelCount;
            }
            else
                nRelPercent = 0;
        }
        if ( !nPercent )
            nPercent = 1;
        long nSizeDelta = nCalcSize-nAbsSize;
        for ( auto& rItem : rItems )
        {
            if ( rItem.mnBits & SplitWindowItemFlags::RelativeSize )
            {
                if ( nSizeDelta <= 0 )
                    rItem.mnPixSize = 0;
                else
                    rItem.mnPixSize = (nSizeDelta*rItem.mnSize*nRelPercent)/nPercent;
            }
            else if ( rItem.mnBits & SplitWindowItemFlags::PercentSize )
            {
                if ( nSizeDelta <= 0 )
                    rItem.mnPixSize = 0;
                else
                    rItem.mnPixSize = (nSizeDelta*rItem.mnSize*nPercentFactor)/nPercent;
            }
            else
                rItem.mnPixSize = rItem.mnSize;
            nCurSize += rItem.mnPixSize;
        }

        pSet->mbCalcPix  = false;
        pSet->mnLastSize = nCalcSize;

        // adapt window
        nSizeDelta  = nCalcSize-nCurSize;
        if ( nSizeDelta )
        {
            nAbsItems       = 0;
            long nSizeWinSize    = 0;

            // first resize absolute items relative
            for ( const auto& rItem : rItems )
            {
                if ( !(rItem.mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize)) )
                {
                    nAbsItems++;
                    nSizeWinSize += rItem.mnPixSize;
                }
            }
            // do not compensate rounding errors here
            if ( (nAbsItems < o3tl::make_unsigned(std::abs( nSizeDelta ))) && nSizeWinSize )
            {
                long nNewSizeWinSize = 0;

                for ( auto& rItem : rItems )
                {
                    if ( !(rItem.mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize)) )
                    {
                        rItem.mnPixSize += (nSizeDelta*rItem.mnPixSize)/nSizeWinSize;
                        nNewSizeWinSize += rItem.mnPixSize;
                    }
                }

                nSizeDelta -= nNewSizeWinSize-nSizeWinSize;
            }

            // compensate rounding errors now
            sal_uInt16 j = 0;
            nMins       = 0;
            while ( nSizeDelta && (nItems != nMins) )
            {
                // determine which items we can calculate
                nCalcItems = 0;
                while ( !nCalcItems )
                {
                    for ( auto& rItem : rItems )
                    {
                        rItem.mbSubSize = false;

                        if ( j >= 2 )
                            rItem.mbSubSize = true;
                        else
                        {
                            if ( (nSizeDelta > 0) || rItem.mnPixSize )
                            {
                                if ( j >= 1 )
                                    rItem.mbSubSize = true;
                                else
                                {
                                    if ( (j == 0) && (rItem.mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize)) )
                                        rItem.mbSubSize = true;
                                }
                            }
                        }

                        if ( rItem.mbSubSize )
                            nCalcItems++;
                    }

                    j++;
                }

                // subtract size of individual items
                long nErrorSum       = nSizeDelta % nCalcItems;
                long nCurSizeDelta   = nSizeDelta / nCalcItems;
                nMins           = 0;
                for ( auto& rItem : rItems )
                {
                    if ( rItem.mbSubSize )
                    {
                        long* pSize = &(rItem.mnPixSize);
                        long  nTempErr;

                        if ( nErrorSum )
                        {
                            if ( nErrorSum < 0 )
                                nTempErr = -1;
                            else
                                nTempErr = 1;
                        }
                        else
                            nTempErr = 0;

                        if ( (*pSize+nCurSizeDelta+nTempErr) <= 0 )
                        {
                            long nTemp = *pSize;
                            if ( nTemp )
                            {
                                *pSize -= nTemp;
                                nSizeDelta += nTemp;
                            }
                            nMins++;
                        }
                        else
                        {
                            *pSize += nCurSizeDelta;
                            nSizeDelta -= nCurSizeDelta;
                            if ( nTempErr && (*pSize || (nTempErr > 0)) )
                            {
                                *pSize += nTempErr;
                                nSizeDelta -= nTempErr;
                                nErrorSum -= nTempErr;
                            }
                        }
                    }
                }
            }
        }
    }

    // calculate maximum size
    if ( bRows )
    {
        nPos = nSetTop;
        if ( !bDown )
            nMaxPos = nSetTop-nSetHeight;
        else
            nMaxPos = nSetTop+nSetHeight;
    }
    else
    {
        nPos = nSetLeft;
        if ( !bDown )
            nMaxPos = nSetLeft-nSetWidth;
        else
            nMaxPos = nSetLeft+nSetWidth;
    }

    // order windows and adept values
    for ( size_t i = 0; i < nItems; i++ )
    {
        rItems[i].mnOldSplitPos    = rItems[i].mnSplitPos;
        rItems[i].mnOldSplitSize   = rItems[i].mnSplitSize;
        rItems[i].mnOldWidth       = rItems[i].mnWidth;
        rItems[i].mnOldHeight      = rItems[i].mnHeight;

        bEmpty = false;
        if ( bDown )
        {
            if ( nPos+rItems[i].mnPixSize > nMaxPos )
                bEmpty = true;
        }
        else
        {
            nPos -= rItems[i].mnPixSize;
            if ( nPos < nMaxPos )
                bEmpty = true;
        }

        if ( bEmpty )
        {
            rItems[i].mnWidth     = 0;
            rItems[i].mnHeight    = 0;
            rItems[i].mnSplitSize = 0;
        }
        else
        {
            if ( bRows )
            {
                rItems[i].mnLeft   = nSetLeft;
                rItems[i].mnTop    = nPos;
                rItems[i].mnWidth  = nSetWidth;
                rItems[i].mnHeight = rItems[i].mnPixSize;
            }
            else
            {
                rItems[i].mnLeft   = nPos;
                rItems[i].mnTop    = nSetTop;
                rItems[i].mnWidth  = rItems[i].mnPixSize;
                rItems[i].mnHeight = nSetHeight;
            }

            if ( i > nItems-1 )
                rItems[i].mnSplitSize = 0;
            else
            {
                rItems[i].mnSplitSize = pSet->mnSplitSize;
                if ( bDown )
                {
                    rItems[i].mnSplitPos  = nPos+rItems[i].mnPixSize;
                    if ( rItems[i].mnSplitPos+rItems[i].mnSplitSize > nMaxPos )
                        rItems[i].mnSplitSize = nMaxPos-rItems[i].mnSplitPos;
                }
                else
                {
                    rItems[i].mnSplitPos = nPos-pSet->mnSplitSize;
                    if ( rItems[i].mnSplitPos < nMaxPos )
                        rItems[i].mnSplitSize = rItems[i].mnSplitPos+pSet->mnSplitSize-nMaxPos;
                }
            }
        }

        if ( !bDown )
            nPos -= pSet->mnSplitSize;
        else
            nPos += rItems[i].mnPixSize+pSet->mnSplitSize;
    }

    // calculate Sub-Set's
    for ( auto& rItem : rItems )
    {
        if ( rItem.mpSet && rItem.mnWidth && rItem.mnHeight )
        {
            ImplCalcSet( rItem.mpSet.get(),
                         rItem.mnLeft, rItem.mnTop,
                         rItem.mnWidth, rItem.mnHeight,
                         !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
        }
    }

    // set fixed
    for ( auto& rItem : rItems )
    {
        rItem.mbFixed = false;
        if ( rItem.mnBits & SplitWindowItemFlags::Fixed )
            rItem.mbFixed = true;
        else
        {
            // this item is also fixed if Child-Set is available,
            // if a child is fixed
            if ( rItem.mpSet )
            {
                for ( auto const & j: rItem.mpSet->mvItems )
                {
                    if ( j.mbFixed )
                    {
                        rItem.mbFixed = true;
                        break;
                    }
                }
            }
        }
    }
}

void SplitWindow::ImplCalcSet2( SplitWindow* pWindow, ImplSplitSet* pSet, bool bHide,
                                bool bRows )
{
    std::vector< ImplSplitItem >&     rItems = pSet->mvItems;

    if ( pWindow->IsReallyVisible() && pWindow->IsUpdateMode() && pWindow->mbInvalidate )
    {
        for ( const auto& rItem : rItems )
        {
            if ( rItem.mnSplitSize )
            {
                // invalidate all, if applicable or only a small part
                if ( (rItem.mnOldSplitPos  != rItem.mnSplitPos)  ||
                     (rItem.mnOldSplitSize != rItem.mnSplitSize) ||
                     (rItem.mnOldWidth     != rItem.mnWidth)     ||
                     (rItem.mnOldHeight    != rItem.mnHeight) )
                {
                    tools::Rectangle aRect;

                    // invalidate old rectangle
                    if ( bRows )
                    {
                        aRect.SetLeft( rItem.mnLeft );
                        aRect.SetRight( rItem.mnLeft+rItem.mnOldWidth-1 );
                        aRect.SetTop( rItem.mnOldSplitPos );
                        aRect.SetBottom( aRect.Top() + rItem.mnOldSplitSize );
                    }
                    else
                    {
                        aRect.SetTop( rItem.mnTop );
                        aRect.SetBottom( rItem.mnTop+rItem.mnOldHeight-1 );
                        aRect.SetLeft( rItem.mnOldSplitPos );
                        aRect.SetRight( aRect.Left() + rItem.mnOldSplitSize );
                    }
                    pWindow->Invalidate( aRect );
                    // invalidate new rectangle
                    if ( bRows )
                    {
                        aRect.SetLeft( rItem.mnLeft );
                        aRect.SetRight( rItem.mnLeft+rItem.mnWidth-1 );
                        aRect.SetTop( rItem.mnSplitPos );
                        aRect.SetBottom( aRect.Top() + rItem.mnSplitSize );
                    }
                    else
                    {
                        aRect.SetTop( rItem.mnTop );
                        aRect.SetBottom( rItem.mnTop+rItem.mnHeight-1 );
                        aRect.SetLeft( rItem.mnSplitPos );
                        aRect.SetRight( aRect.Left() + rItem.mnSplitSize );
                    }
                    pWindow->Invalidate( aRect );

                    // invalidate complete set, as these areas
                    // are not cluttered by windows
                    if ( rItem.mpSet && rItem.mpSet->mvItems.empty() )
                    {
                        aRect.SetLeft( rItem.mnLeft );
                        aRect.SetTop( rItem.mnTop );
                        aRect.SetRight( rItem.mnLeft+rItem.mnWidth-1 );
                        aRect.SetBottom( rItem.mnTop+rItem.mnHeight-1 );
                        pWindow->Invalidate( aRect );
                    }
                }
            }
        }
    }

    // position windows
    for ( auto& rItem : rItems )
    {
        if ( rItem.mpSet )
        {
            bool bTempHide = bHide;
            if ( !rItem.mnWidth || !rItem.mnHeight )
                bTempHide = true;
            ImplCalcSet2( pWindow, rItem.mpSet.get(), bTempHide,
                          !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
        }
        else
        {
            if ( rItem.mnWidth && rItem.mnHeight && !bHide )
            {
                Point aPos( rItem.mnLeft, rItem.mnTop );
                Size  aSize( rItem.mnWidth, rItem.mnHeight );
                rItem.mpWindow->SetPosSizePixel( aPos, aSize );
            }
            else
                rItem.mpWindow->Hide();
        }
    }

    // show windows and reset flag
    for ( auto& rItem : rItems )
    {
        if ( rItem.mpWindow && rItem.mnWidth && rItem.mnHeight && !bHide )
            rItem.mpWindow->Show();
    }
}

static void ImplCalcLogSize( std::vector< ImplSplitItem > & rItems, size_t nItems )
{
    // update original sizes
    size_t  i;
    long    nRelSize = 0;
    long    nPerSize = 0;

    for ( i = 0; i < nItems; i++ )
    {
        if ( rItems[i].mnBits & SplitWindowItemFlags::RelativeSize )
            nRelSize += rItems[i].mnPixSize;
        else if ( rItems[i].mnBits & SplitWindowItemFlags::PercentSize )
            nPerSize += rItems[i].mnPixSize;
    }
    nPerSize += nRelSize;
    for ( i = 0; i < nItems; i++ )
    {
        if ( rItems[i].mnBits & SplitWindowItemFlags::RelativeSize )
        {
            if ( nRelSize )
                rItems[i].mnSize = (rItems[i].mnPixSize+(nRelSize/2))/nRelSize;
            else
                rItems[i].mnSize = 1;
        }
        else if ( rItems[i].mnBits & SplitWindowItemFlags::PercentSize )
        {
            if ( nPerSize )
                rItems[i].mnSize = (rItems[i].mnPixSize*100)/nPerSize;
            else
                rItems[i].mnSize = 1;
        }
        else
            rItems[i].mnSize = rItems[i].mnPixSize;
    }
}

static void ImplDrawSplit(vcl::RenderContext& rRenderContext, ImplSplitSet* pSet, bool bRows, bool bDown)
{
    if (pSet->mvItems.empty())
        return;

    size_t     nItems = pSet->mvItems.size();
    long       nPos;
    long       nTop;
    long       nBottom;
    std::vector< ImplSplitItem >& rItems = pSet->mvItems;
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    for (size_t i = 0; i < nItems-1; i++)
    {
        if (rItems[i].mnSplitSize)
        {
            nPos = rItems[i].mnSplitPos;

            long nItemSplitSize = rItems[i].mnSplitSize;
            long nSplitSize = pSet->mnSplitSize;
            if (bRows)
            {
                nTop    = rItems[i].mnLeft;
                nBottom = rItems[i].mnLeft+rItems[i].mnWidth-1;

                if (bDown || (nItemSplitSize >= nSplitSize))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
                    rRenderContext.DrawLine(Point(nTop, nPos + 1), Point(nBottom, nPos + 1));
                }
                nPos += nSplitSize-2;
                if ((!bDown && (nItemSplitSize >= 2)) ||
                    (bDown  && (nItemSplitSize >= nSplitSize - 1)))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                    rRenderContext.DrawLine(Point(nTop, nPos), Point(nBottom, nPos));
                }
                nPos++;
                if (!bDown || (nItemSplitSize >= nSplitSize))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
                    rRenderContext.DrawLine(Point(nTop, nPos), Point(nBottom, nPos));
                }
            }
            else
            {
                nTop    = rItems[i].mnTop;
                nBottom = rItems[i].mnTop+pSet->mvItems[i].mnHeight-1;

                if (bDown || (nItemSplitSize >= nSplitSize))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
                    rRenderContext.DrawLine(Point(nPos + 1, nTop), Point(nPos+1, nBottom));
                }
                nPos += pSet->mnSplitSize - 2;
                if ((!bDown && (nItemSplitSize >= 2)) ||
                    (bDown  && (nItemSplitSize >= nSplitSize - 1)))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
                    rRenderContext.DrawLine(Point(nPos, nTop), Point(nPos, nBottom));
                }
                nPos++;
                if (!bDown || (nItemSplitSize >= nSplitSize))
                {
                    rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
                    rRenderContext.DrawLine(Point(nPos, nTop), Point(nPos, nBottom));
                }
            }
        }
    }

    for ( auto& rItem : rItems )
    {
        if (rItem.mpSet && rItem.mnWidth && rItem.mnHeight)
        {
            ImplDrawSplit(rRenderContext, rItem.mpSet.get(), !(rItem.mnBits & SplitWindowItemFlags::ColSet), true/*bDown*/);
        }
    }
}

sal_uInt16 SplitWindow::ImplTestSplit( ImplSplitSet* pSet, const Point& rPos,
                                   long& rMouseOff, ImplSplitSet** ppFoundSet, sal_uInt16& rFoundPos,
                                   bool bRows )
{
    if ( pSet->mvItems.empty() )
        return 0;

    sal_uInt16      nSplitTest;
    size_t          nItems = pSet->mvItems.size();
    long            nMPos1;
    long            nMPos2;
    long            nPos;
    long            nTop;
    long            nBottom;
    std::vector< ImplSplitItem >& rItems = pSet->mvItems;

    if ( bRows )
    {
        nMPos1 = rPos.X();
        nMPos2 = rPos.Y();
    }
    else
    {
        nMPos1 = rPos.Y();
        nMPos2 = rPos.X();
    }

    for ( size_t i = 0; i < nItems-1; i++ )
    {
        if ( rItems[i].mnSplitSize )
        {
            if ( bRows )
            {
                nTop    = rItems[i].mnLeft;
                nBottom = rItems[i].mnLeft+rItems[i].mnWidth-1;
            }
            else
            {
                nTop    = rItems[i].mnTop;
                nBottom = rItems[i].mnTop+rItems[i].mnHeight-1;
            }
            nPos = rItems[i].mnSplitPos;

            if ( (nMPos1 >= nTop) && (nMPos1 <= nBottom) &&
                 (nMPos2 >= nPos) && (nMPos2 <= nPos+rItems[i].mnSplitSize) )
            {
                if ( !rItems[i].mbFixed && !rItems[i+1].mbFixed )
                {
                    rMouseOff = nMPos2-nPos;
                    *ppFoundSet = pSet;
                    rFoundPos = i;
                    if ( bRows )
                        return SPLIT_VERT;
                    else
                        return SPLIT_HORZ;
                }
                else
                    return SPLIT_NOSPLIT;
            }
        }
    }

    for ( auto& rItem : rItems )
    {
        if ( rItem.mpSet )
        {
            nSplitTest = ImplTestSplit( rItem.mpSet.get(), rPos,
                                       rMouseOff, ppFoundSet, rFoundPos,
                                       !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
            if ( nSplitTest )
                return nSplitTest;
        }
    }

    return 0;
}

sal_uInt16 SplitWindow::ImplTestSplit( const SplitWindow* pWindow, const Point& rPos,
                                   long& rMouseOff, ImplSplitSet** ppFoundSet, sal_uInt16& rFoundPos )
{
    // Resizable SplitWindow should be treated different
    if ( pWindow->mnWinStyle & WB_SIZEABLE )
    {
        long    nTPos;
        long    nPos;
        long    nBorder;

        if ( pWindow->mbHorz )
        {
            if ( pWindow->mbBottomRight )
            {
                nBorder = pWindow->mnBottomBorder;
                nPos = 0;
            }
            else
            {
                nBorder = pWindow->mnTopBorder;
                nPos = pWindow->mnDY-nBorder;
            }
            nTPos = rPos.Y();
        }
        else
        {
            if ( pWindow->mbBottomRight )
            {
                nBorder = pWindow->mnRightBorder;
                nPos = 0;
            }
            else
            {
                nBorder = pWindow->mnLeftBorder;
                nPos = pWindow->mnDX-nBorder;
            }
            nTPos = rPos.X();
        }
        long nSplitSize = pWindow->mpMainSet->mnSplitSize-2;
        if (pWindow->mbFadeOut)
            nSplitSize += SPLITWIN_SPLITSIZEEXLN;
        if ( !pWindow->mbBottomRight )
            nPos -= nSplitSize;
        if ( (nTPos >= nPos) && (nTPos <= nPos+nSplitSize+nBorder) )
        {
            rMouseOff = nTPos-nPos;
            *ppFoundSet = pWindow->mpMainSet.get();
            if ( !pWindow->mpMainSet->mvItems.empty() )
                rFoundPos = pWindow->mpMainSet->mvItems.size() - 1;
            else
                rFoundPos = 0;
            if ( pWindow->mbHorz )
                return SPLIT_VERT | SPLIT_WINDOW;
            else
                return SPLIT_HORZ | SPLIT_WINDOW;
        }
    }

    return ImplTestSplit( pWindow->mpMainSet.get(), rPos, rMouseOff, ppFoundSet, rFoundPos,
                         pWindow->mbHorz );
}

void SplitWindow::ImplDrawSplitTracking(const Point& rPos)
{
    tools::Rectangle aRect;

    if (mnSplitTest & SPLIT_HORZ)
    {
        aRect.SetTop( maDragRect.Top() );
        aRect.SetBottom( maDragRect.Bottom() );
        aRect.SetLeft( rPos.X() );
        aRect.SetRight( aRect.Left() + mpSplitSet->mnSplitSize - 1 );
        if (!(mnWinStyle & WB_NOSPLITDRAW))
            aRect.AdjustRight( -1 );
        if ((mnSplitTest & SPLIT_WINDOW) && mbFadeOut)
        {
            aRect.AdjustLeft(SPLITWIN_SPLITSIZEEXLN );
            aRect.AdjustRight(SPLITWIN_SPLITSIZEEXLN );
        }
    }
    else
    {
        aRect.SetLeft( maDragRect.Left() );
        aRect.SetRight( maDragRect.Right() );
        aRect.SetTop( rPos.Y() );
        aRect.SetBottom( aRect.Top() + mpSplitSet->mnSplitSize - 1 );
        if (!(mnWinStyle & WB_NOSPLITDRAW))
            aRect.AdjustBottom( -1 );
        if ((mnSplitTest & SPLIT_WINDOW) && mbFadeOut)
        {
            aRect.AdjustTop(SPLITWIN_SPLITSIZEEXLN );
            aRect.AdjustBottom(SPLITWIN_SPLITSIZEEXLN );
        }
    }
    ShowTracking(aRect, ShowTrackFlags::Split);
}

void SplitWindow::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    mpMainSet.reset(new ImplSplitSet());
    mpBaseSet               = mpMainSet.get();
    mpSplitSet              = nullptr;
    mpLastSizes             = nullptr;
    mnDX                    = 0;
    mnDY                    = 0;
    mnLeftBorder            = 0;
    mnTopBorder             = 0;
    mnRightBorder           = 0;
    mnBottomBorder          = 0;
    mnMaxSize               = 0;
    mnMouseOff              = 0;
    meAlign                 = WindowAlign::Top;
    mnWinStyle              = nStyle;
    mnSplitTest             = 0;
    mnSplitPos              = 0;
    mnMouseModifier         = 0;
    mnMStartPos             = 0;
    mnMSplitPos             = 0;
    mbDragFull              = false;
    mbHorz                  = true;
    mbBottomRight           = false;
    mbCalc                  = false;
    mbRecalc                = true;
    mbInvalidate            = true;
    mbFadeIn                = false;
    mbFadeOut               = false;
    mbFadeInDown            = false;
    mbFadeOutDown           = false;
    mbFadeInPressed         = false;
    mbFadeOutPressed        = false;
    mbFadeNoButtonMode      = false;

    if ( nStyle & WB_NOSPLITDRAW )
    {
        mpMainSet->mnSplitSize -= 2;
        mbInvalidate = false;
    }

    if ( nStyle & WB_BORDER )
    {
        ImplCalcBorder( meAlign, mnLeftBorder, mnTopBorder,
                        mnRightBorder, mnBottomBorder );
    }
    else
    {
        mnLeftBorder   = 0;
        mnTopBorder    = 0;
        mnRightBorder  = 0;
        mnBottomBorder = 0;
    }

    DockingWindow::ImplInit( pParent, (nStyle | WB_CLIPCHILDREN) & ~(WB_BORDER | WB_SIZEABLE) );

    ImplInitSettings();
}

void SplitWindow::ImplInitSettings()
{
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();

    Color aColor;
    if ( IsControlBackground() )
        aColor = GetControlBackground();
    else if ( Window::GetStyle() & WB_3DLOOK )
        aColor = rStyleSettings.GetFaceColor();
    else
        aColor = rStyleSettings.GetWindowColor();
    SetBackground( aColor );
}

SplitWindow::SplitWindow( vcl::Window* pParent, WinBits nStyle ) :
    DockingWindow( WindowType::SPLITWINDOW )
{
    ImplInit( pParent, nStyle );
    DockingWindow::SetIdleDebugName( "vcl::SplitWindow maLayoutIdle" );
}

SplitWindow::~SplitWindow()
{
    disposeOnce();
}

void SplitWindow::dispose()
{
    // delete Sets
    mpMainSet.reset();
    DockingWindow::dispose();
}

void SplitWindow::ImplSetWindowSize( long nDelta )
{
    if ( !nDelta )
        return;

    Size aSize = GetSizePixel();
    switch ( meAlign )
    {
    case WindowAlign::Top:
        aSize.AdjustHeight(nDelta );
        SetSizePixel( aSize );
        break;
    case WindowAlign::Bottom:
    {
        maDragRect.AdjustTop(nDelta );
        Point aPos = GetPosPixel();
        aPos.AdjustY( -nDelta );
        aSize.AdjustHeight(nDelta );
        SetPosSizePixel( aPos, aSize );
        break;
    }
    case WindowAlign::Left:
        aSize.AdjustWidth(nDelta );
        SetSizePixel( aSize );
        break;
    case WindowAlign::Right:
    default:
    {
        maDragRect.AdjustLeft(nDelta );
        Point aPos = GetPosPixel();
        aPos.AdjustX( -nDelta );
        aSize.AdjustWidth(nDelta );
        SetPosSizePixel( aPos, aSize );
        break;
    }
    }

    SplitResize();
}

Size SplitWindow::CalcLayoutSizePixel( const Size& aNewSize )
{
    Size aSize( aNewSize );
    long nSplitSize = mpMainSet->mnSplitSize-2;

    if (mbFadeOut)
        nSplitSize += SPLITWIN_SPLITSIZEEXLN;

    // if the window is sizeable and if it does not contain a relative window,
    // the size is determined according to MainSet
    if ( mnWinStyle & WB_SIZEABLE )
    {
        long    nCalcSize = 0;
        std::vector< ImplSplitItem* >::size_type i;

        for ( i = 0; i < mpMainSet->mvItems.size(); i++ )
        {
            if ( mpMainSet->mvItems[i].mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize) )
                break;
            else
                nCalcSize += mpMainSet->mvItems[i].mnSize;
        }

        if ( i == mpMainSet->mvItems.size() )
        {
            long    nDelta = 0;
            long    nCurSize;

            if ( mbHorz )
                nCurSize = aNewSize.Height()-mnTopBorder-mnBottomBorder;
            else
                nCurSize = aNewSize.Width()-mnLeftBorder-mnRightBorder;
            nCurSize -= nSplitSize;
            nCurSize -= (mpMainSet->mvItems.size()-1)*mpMainSet->mnSplitSize;

            nDelta = nCalcSize-nCurSize;
            if ( !nDelta )
                return aSize;

            switch ( meAlign )
            {
            case WindowAlign::Top:
                aSize.AdjustHeight(nDelta );
                break;
            case WindowAlign::Bottom:
                aSize.AdjustHeight(nDelta );
                break;
            case WindowAlign::Left:
                aSize.AdjustWidth(nDelta );
                break;
            case WindowAlign::Right:
            default:
                aSize.AdjustWidth(nDelta );
                break;
            }
        }
    }

    return aSize;
}

void SplitWindow::ImplCalcLayout()
{
    if ( !mbCalc || !mbRecalc || mpMainSet->mvItems.empty() )
        return;

    long nSplitSize = mpMainSet->mnSplitSize-2;
    if (mbFadeOut)
        nSplitSize += SPLITWIN_SPLITSIZEEXLN;

    // if the window is sizeable and if it does not contain a relative window,
    // the size is determined according to MainSet
    if ( mnWinStyle & WB_SIZEABLE )
    {
        long    nCalcSize = 0;
        std::vector<ImplSplitItem *>::size_type i;

        for ( i = 0; i < mpMainSet->mvItems.size(); i++ )
        {
            if ( mpMainSet->mvItems[i].mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize) )
                break;
            else
                nCalcSize += mpMainSet->mvItems[i].mnSize;
        }

        if ( i == mpMainSet->mvItems.size() )
        {
            long    nCurSize;
            if ( mbHorz )
                nCurSize = mnDY-mnTopBorder-mnBottomBorder;
            else
                nCurSize = mnDX-mnLeftBorder-mnRightBorder;
            nCurSize -= nSplitSize;
            nCurSize -= (mpMainSet->mvItems.size()-1)*mpMainSet->mnSplitSize;

            mbRecalc = false;
            ImplSetWindowSize( nCalcSize-nCurSize );
            mbRecalc = true;
        }
    }

    if ( (mnDX <= 0) || (mnDY <= 0) )
        return;

    // pre-calculate sizes/position
    long    nL;
    long    nT;
    long    nW;
    long    nH;

    if ( mbHorz )
    {
        if ( mbBottomRight )
            nT = mnDY-mnBottomBorder;
        else
            nT = mnTopBorder;
        nL = mnLeftBorder;
    }
    else
    {
        if ( mbBottomRight )
            nL = mnDX-mnRightBorder;
        else
            nL = mnLeftBorder;
        nT = mnTopBorder;
    }
    nW = mnDX-mnLeftBorder-mnRightBorder;
    nH = mnDY-mnTopBorder-mnBottomBorder;
    if ( mnWinStyle & WB_SIZEABLE )
    {
        if ( mbHorz )
            nH -= nSplitSize;
        else
            nW -= nSplitSize;
    }

    // calculate sets recursive
    ImplCalcSet( mpMainSet.get(), nL, nT, nW, nH, mbHorz, !mbBottomRight );
    ImplCalcSet2( this, mpMainSet.get(), false, mbHorz );
    mbCalc = false;
}

void SplitWindow::ImplUpdate()
{
    mbCalc = true;

    if ( IsReallyShown() && IsUpdateMode() && mbRecalc )
    {
        if ( !mpMainSet->mvItems.empty() )
            ImplCalcLayout();
        else
            Invalidate();
    }
}

void SplitWindow::ImplSplitMousePos( Point& rMousePos )
{
    if ( mnSplitTest & SPLIT_HORZ )
    {
        rMousePos.AdjustX( -mnMouseOff );
        if ( rMousePos.X() < maDragRect.Left() )
            rMousePos.setX( maDragRect.Left() );
        else if ( rMousePos.X()+mpSplitSet->mnSplitSize+1 > maDragRect.Right() )
            rMousePos.setX( maDragRect.Right()-mpSplitSet->mnSplitSize+1 );
        // store in screen coordinates due to FullDrag
        mnMSplitPos = OutputToScreenPixel( rMousePos ).X();
    }
    else
    {
        rMousePos.AdjustY( -mnMouseOff );
        if ( rMousePos.Y() < maDragRect.Top() )
            rMousePos.setY( maDragRect.Top() );
        else if ( rMousePos.Y()+mpSplitSet->mnSplitSize+1 > maDragRect.Bottom() )
            rMousePos.setY( maDragRect.Bottom()-mpSplitSet->mnSplitSize+1 );
        mnMSplitPos = OutputToScreenPixel( rMousePos ).Y();
    }
}

void SplitWindow::ImplGetButtonRect( tools::Rectangle& rRect, bool bTest ) const
{
    long nSplitSize = mpMainSet->mnSplitSize-1;
    if (mbFadeOut || mbFadeIn)
        nSplitSize += SPLITWIN_SPLITSIZEEX;

    long nButtonSize = 0;
    if ( mbFadeIn )
        nButtonSize += SPLITWIN_SPLITSIZEFADE+1;
    if ( mbFadeOut )
        nButtonSize += SPLITWIN_SPLITSIZEFADE+1;
    long nCenterEx = 0;
    if ( mbHorz )
        nCenterEx += ((mnDX-mnLeftBorder-mnRightBorder)-nButtonSize)/2;
    else
        nCenterEx += ((mnDY-mnTopBorder-mnBottomBorder)-nButtonSize)/2;
    long nEx = 0;
    if ( nCenterEx > 0 )
        nEx += nCenterEx;

    switch ( meAlign )
    {
    case WindowAlign::Top:
        rRect.SetLeft( mnLeftBorder+nEx );
        rRect.SetTop( mnDY-mnBottomBorder-nSplitSize );
        rRect.SetRight( rRect.Left()+SPLITWIN_SPLITSIZEAUTOHIDE );
        rRect.SetBottom( mnDY-mnBottomBorder-1 );
        if ( bTest )
        {
            rRect.AdjustTop( -mnTopBorder );
            rRect.AdjustBottom(mnBottomBorder );
        }
        break;
    case WindowAlign::Bottom:
        rRect.SetLeft( mnLeftBorder+nEx );
        rRect.SetTop( mnTopBorder );
        rRect.SetRight( rRect.Left()+SPLITWIN_SPLITSIZEAUTOHIDE );
        rRect.SetBottom( mnTopBorder+nSplitSize-1 );
        if ( bTest )
        {
            rRect.AdjustTop( -mnTopBorder );
            rRect.AdjustBottom(mnBottomBorder );
        }
        break;
    case WindowAlign::Left:
        rRect.SetLeft( mnDX-mnRightBorder-nSplitSize );
        rRect.SetTop( mnTopBorder+nEx );
        rRect.SetRight( mnDX-mnRightBorder-1 );
        rRect.SetBottom( rRect.Top()+SPLITWIN_SPLITSIZEAUTOHIDE );
        if ( bTest )
        {
            rRect.AdjustLeft( -mnLeftBorder );
            rRect.AdjustRight(mnRightBorder );
        }
        break;
    case WindowAlign::Right:
        rRect.SetLeft( mnLeftBorder );
        rRect.SetTop( mnTopBorder+nEx );
        rRect.SetRight( mnLeftBorder+nSplitSize-1 );
        rRect.SetBottom( rRect.Top()+SPLITWIN_SPLITSIZEAUTOHIDE );
        if ( bTest )
        {
            rRect.AdjustLeft( -mnLeftBorder );
            rRect.AdjustRight(mnRightBorder );
        }
        break;
    }
}

void SplitWindow::ImplGetFadeInRect( tools::Rectangle& rRect, bool bTest ) const
{
    tools::Rectangle aRect;

    if ( mbFadeIn )
        ImplGetButtonRect( aRect, bTest );

    rRect = aRect;
}

void SplitWindow::ImplGetFadeOutRect( tools::Rectangle& rRect ) const
{
    tools::Rectangle aRect;

    if ( mbFadeOut )
        ImplGetButtonRect( aRect, false );

    rRect = aRect;
}

void SplitWindow::ImplDrawGrip(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, bool bHorizontal, bool bLeft)
{
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    Color aColor;

    if (rRect.IsInside(GetPointerPosPixel()))
    {
        vcl::RenderTools::DrawSelectionBackground(rRenderContext, *this, rRect, 2, false, false, false);

        aColor = rStyleSettings.GetDarkShadowColor();
    }
    else
    {
        rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
        rRenderContext.SetFillColor(rStyleSettings.GetDarkShadowColor());

        rRenderContext.DrawRect(rRect);

        aColor = rStyleSettings.GetFaceColor();
    }

    AntialiasingFlags nAA = rRenderContext.GetAntialiasing();
    rRenderContext.SetAntialiasing(nAA | AntialiasingFlags::PixelSnapHairline | AntialiasingFlags::EnableB2dDraw);

    long nWidth = rRect.getWidth();
    long nWidthHalf = nWidth / 2;
    long nHeight = rRect.getHeight();
    long nHeightHalf = nHeight / 2;

    long nLeft = rRect.Left();
    long nRight = rRect.Right();
    long nTop = rRect.Top();
    long nBottom = rRect.Bottom();
    long nMargin = 1;

    rRenderContext.SetLineColor(aColor);
    rRenderContext.SetFillColor(aColor);

    tools::Polygon aPoly(3);

    if (bHorizontal)
    {
        long nCenter = nLeft + nWidthHalf;

        if (bLeft)
        {
            aPoly.SetPoint(Point(nCenter,               nTop    + nMargin), 0);
            aPoly.SetPoint(Point(nCenter - nHeightHalf, nBottom - nMargin), 1);
            aPoly.SetPoint(Point(nCenter - nHeightHalf, nBottom - nMargin), 2);
        }
        else
        {
            aPoly.SetPoint(Point(nCenter,               nBottom - nMargin), 0);
            aPoly.SetPoint(Point(nCenter - nHeightHalf, nTop    + nMargin), 1);
            aPoly.SetPoint(Point(nCenter + nHeightHalf, nTop    + nMargin), 2);
        }
        rRenderContext.DrawPolygon(aPoly);
    }
    else
    {
        long nCenter = nTop + nHeightHalf;

        if (bLeft)
        {
            aPoly.SetPoint(Point(nLeft  + nMargin, nCenter), 0);
            aPoly.SetPoint(Point(nRight - nMargin, nCenter - nWidthHalf), 1);
            aPoly.SetPoint(Point(nRight - nMargin, nCenter + nWidthHalf), 2);
        }
        else
        {
            aPoly.SetPoint(Point(nRight - nMargin, nCenter), 0);
            aPoly.SetPoint(Point(nLeft  + nMargin, nCenter - nWidthHalf), 1);
            aPoly.SetPoint(Point(nLeft  + nMargin, nCenter + nWidthHalf), 2);
        }
        rRenderContext.DrawPolygon(aPoly);
    }

    rRenderContext.SetAntialiasing(nAA);
}

void SplitWindow::ImplDrawFadeIn(vcl::RenderContext& rRenderContext)
{
    if (mbFadeIn)
    {
        tools::Rectangle aTempRect;
        ImplGetFadeInRect(aTempRect);

        bool bLeft = true;
        switch (meAlign)
        {
        case WindowAlign::Top:
        case WindowAlign::Left:
            bLeft = false;
            break;
        case WindowAlign::Bottom:
        case WindowAlign::Right:
        default:
            bLeft = true;
            break;
        }

        ImplDrawGrip(rRenderContext, aTempRect, (meAlign == WindowAlign::Top) || (meAlign == WindowAlign::Bottom), bLeft);
    }
}

void SplitWindow::ImplDrawFadeOut(vcl::RenderContext& rRenderContext)
{
    if (mbFadeOut)
    {
        tools::Rectangle aTempRect;
        ImplGetFadeOutRect(aTempRect);

        bool bLeft = true;
        switch (meAlign)
        {
        case WindowAlign::Bottom:
        case WindowAlign::Right:
            bLeft = false;
            break;
        case WindowAlign::Top:
        case WindowAlign::Left:
        default:
            bLeft = true;
            break;
        }

        ImplDrawGrip(rRenderContext, aTempRect, (meAlign == WindowAlign::Top) || (meAlign == WindowAlign::Bottom), bLeft);
    }
}

void SplitWindow::ImplStartSplit( const MouseEvent& rMEvt )
{
    Point aMousePosPixel = rMEvt.GetPosPixel();
    mnSplitTest = ImplTestSplit( this, aMousePosPixel, mnMouseOff, &mpSplitSet, mnSplitPos );

    if ( !mnSplitTest || (mnSplitTest & SPLIT_NOSPLIT) )
        return;

    ImplSplitItem*  pSplitItem;
    long            nCurMaxSize;
    bool            bPropSmaller;

    mnMouseModifier = rMEvt.GetModifier();
    bPropSmaller = (mnMouseModifier & KEY_SHIFT) && (o3tl::make_unsigned(mnSplitPos+1) < mpSplitSet->mvItems.size());

    // here we can set the maximum size
    StartSplit();

    if ( mnMaxSize )
        nCurMaxSize = mnMaxSize;
    else
    {
        Size aSize = GetParent()->GetOutputSizePixel();
        if ( mbHorz )
            nCurMaxSize = aSize.Height();
        else
            nCurMaxSize = aSize.Width();
    }

    if ( !mpSplitSet->mvItems.empty() )
    {
        bool bDown = true;
        if ( (mpSplitSet == mpMainSet.get()) && mbBottomRight )
            bDown = false;

        pSplitItem          = &mpSplitSet->mvItems[mnSplitPos];
        maDragRect.SetLeft( pSplitItem->mnLeft );
        maDragRect.SetTop( pSplitItem->mnTop );
        maDragRect.SetRight( pSplitItem->mnLeft+pSplitItem->mnWidth-1 );
        maDragRect.SetBottom( pSplitItem->mnTop+pSplitItem->mnHeight-1 );

        if ( mnSplitTest & SPLIT_HORZ )
        {
            if ( bDown )
                maDragRect.AdjustRight(mpSplitSet->mnSplitSize );
            else
                maDragRect.AdjustLeft( -(mpSplitSet->mnSplitSize) );
        }
        else
        {
            if ( bDown )
                maDragRect.AdjustBottom(mpSplitSet->mnSplitSize );
            else
                maDragRect.AdjustTop( -(mpSplitSet->mnSplitSize) );
        }

        if ( mnSplitPos )
        {
            long nTemp = mnSplitPos;
            while ( nTemp )
            {
                pSplitItem = &mpSplitSet->mvItems[nTemp-1];
                if ( pSplitItem->mbFixed )
                    break;
                else
                {
                    if ( mnSplitTest & SPLIT_HORZ )
                    {
                        if ( bDown )
                            maDragRect.AdjustLeft( -(pSplitItem->mnPixSize) );
                        else
                            maDragRect.AdjustRight(pSplitItem->mnPixSize );
                    }
                    else
                    {
                        if ( bDown )
                            maDragRect.AdjustTop( -(pSplitItem->mnPixSize) );
                        else
                            maDragRect.AdjustBottom(pSplitItem->mnPixSize );
                    }
                }
                nTemp--;
            }
        }

        if ( (mpSplitSet == mpMainSet.get()) && (mnWinStyle & WB_SIZEABLE) && !bPropSmaller )
        {
            if ( bDown )
            {
                if ( mbHorz )
                    maDragRect.AdjustBottom(nCurMaxSize-mnDY-mnTopBorder );
                else
                    maDragRect.AdjustRight(nCurMaxSize-mnDX-mnLeftBorder );
            }
            else
            {
                if ( mbHorz )
                    maDragRect.AdjustTop( -(nCurMaxSize-mnDY-mnBottomBorder) );
                else
                    maDragRect.AdjustLeft( -(nCurMaxSize-mnDX-mnRightBorder) );
            }
        }
        else
        {
            std::vector<ImplSplitItem *>::size_type nTemp = mnSplitPos+1;
            while ( nTemp < mpSplitSet->mvItems.size() )
            {
                pSplitItem = &mpSplitSet->mvItems[nTemp];
                if ( pSplitItem->mbFixed )
                    break;
                else
                {
                    if ( mnSplitTest & SPLIT_HORZ )
                    {
                        if ( bDown )
                            maDragRect.AdjustRight(pSplitItem->mnPixSize );
                        else
                            maDragRect.AdjustLeft( -(pSplitItem->mnPixSize) );
                    }
                    else
                    {
                        if ( bDown )
                            maDragRect.AdjustBottom(pSplitItem->mnPixSize );
                        else
                            maDragRect.AdjustTop( -(pSplitItem->mnPixSize) );
                    }
                }
                nTemp++;
            }
        }
    }
    else
    {
        maDragRect.SetLeft( mnLeftBorder );
        maDragRect.SetTop( mnTopBorder );
        maDragRect.SetRight( mnDX-mnRightBorder-1 );
        maDragRect.SetBottom( mnDY-mnBottomBorder-1 );
        if ( mbHorz )
        {
            if ( mbBottomRight )
                maDragRect.AdjustTop( -(nCurMaxSize-mnDY-mnBottomBorder) );
            else
                maDragRect.AdjustBottom(nCurMaxSize-mnDY-mnTopBorder );
        }
        else
        {
            if ( mbBottomRight )
                maDragRect.AdjustLeft( -(nCurMaxSize-mnDX-mnRightBorder) );
            else
                maDragRect.AdjustRight(nCurMaxSize-mnDX-mnLeftBorder );
        }
    }

    StartTracking();

    mbDragFull = bool(GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split);

    ImplSplitMousePos( aMousePosPixel );

    if (!mbDragFull)
    {
        ImplDrawSplitTracking(aMousePosPixel);
    }
    else
    {
        std::vector< ImplSplitItem >&  rItems = mpSplitSet->mvItems;
        sal_uInt16       nItems = mpSplitSet->mvItems.size();
        mpLastSizes = new long[nItems*2];
        for ( sal_uInt16 i = 0; i < nItems; i++ )
        {
            mpLastSizes[i*2]   = rItems[i].mnSize;
            mpLastSizes[i*2+1] = rItems[i].mnPixSize;
        }
    }
    mnMStartPos = mnMSplitPos;

    PointerStyle eStyle = PointerStyle::Arrow;
    if ( mnSplitTest & SPLIT_HORZ )
        eStyle = PointerStyle::HSplit;
    else if ( mnSplitTest & SPLIT_VERT )
        eStyle = PointerStyle::VSplit;

    SetPointer( eStyle );
}

void SplitWindow::StartSplit()
{
}

void SplitWindow::Split()
{
    maSplitHdl.Call( this );
}

void SplitWindow::SplitResize()
{
}

void SplitWindow::FadeIn()
{
}

void SplitWindow::FadeOut()
{
}

void SplitWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( !rMEvt.IsLeft() || rMEvt.IsMod2() )
    {
        DockingWindow::MouseButtonDown( rMEvt );
        return;
    }

    Point           aMousePosPixel = rMEvt.GetPosPixel();
    tools::Rectangle       aTestRect;

    mbFadeNoButtonMode = false;

    ImplGetFadeOutRect( aTestRect );
    if ( aTestRect.IsInside( aMousePosPixel ) )
    {
        mbFadeOutDown = true;
        mbFadeOutPressed = true;
        Invalidate();
    }
    else
    {
        ImplGetFadeInRect( aTestRect, true );
        if ( aTestRect.IsInside( aMousePosPixel ) )
        {
            mbFadeInDown = true;
            mbFadeInPressed = true;
            Invalidate();
        }
        else if ( !aTestRect.IsEmpty() && !(mnWinStyle & WB_SIZEABLE) )
        {
            mbFadeNoButtonMode = true;
            FadeIn();
            return;
        }
    }

    if ( mbFadeInDown || mbFadeOutDown )
        StartTracking();
    else
        ImplStartSplit( rMEvt );
}

void SplitWindow::MouseMove( const MouseEvent& rMEvt )
{
    if ( IsTracking() )
        return;

    Point           aPos = rMEvt.GetPosPixel();
    long            nTemp;
    ImplSplitSet*   pTempSplitSet;
    sal_uInt16          nTempSplitPos;
    sal_uInt16          nSplitTest = ImplTestSplit( this, aPos, nTemp, &pTempSplitSet, nTempSplitPos );
    PointerStyle    eStyle = PointerStyle::Arrow;
    tools::Rectangle       aFadeInRect;
    tools::Rectangle       aFadeOutRect;

    ImplGetFadeInRect( aFadeInRect );
    ImplGetFadeOutRect( aFadeOutRect );
    if ( !aFadeInRect.IsInside( aPos ) &&
         !aFadeOutRect.IsInside( aPos ) )
    {
        if ( nSplitTest && !(nSplitTest & SPLIT_NOSPLIT) )
        {
            if ( nSplitTest & SPLIT_HORZ )
                eStyle = PointerStyle::HSplit;
            else if ( nSplitTest & SPLIT_VERT )
                eStyle = PointerStyle::VSplit;
        }
    }

    SetPointer( eStyle );
}

void SplitWindow::Tracking( const TrackingEvent& rTEvt )
{
    Point aMousePosPixel = rTEvt.GetMouseEvent().GetPosPixel();

    if ( mbFadeInDown )
    {
        if ( rTEvt.IsTrackingEnded() )
        {
            mbFadeInDown = false;
            if ( mbFadeInPressed )
            {
                mbFadeInPressed = false;
                Invalidate();

                if ( !rTEvt.IsTrackingCanceled() )
                    FadeIn();
            }
        }
        else
        {
            tools::Rectangle aTestRect;
            ImplGetFadeInRect( aTestRect, true );
            bool bNewPressed = aTestRect.IsInside( aMousePosPixel );
            if ( bNewPressed != mbFadeInPressed )
            {
                mbFadeInPressed = bNewPressed;
                Invalidate();
            }
        }
    }
    else if ( mbFadeOutDown )
    {
        if ( rTEvt.IsTrackingEnded() )
        {
            mbFadeOutDown = false;
            if ( mbFadeOutPressed )
            {
                mbFadeOutPressed = false;
                Invalidate();

                if ( !rTEvt.IsTrackingCanceled() )
                    FadeOut();
            }
        }
        else
        {
            tools::Rectangle aTestRect;
            ImplGetFadeOutRect( aTestRect );
            bool bNewPressed = aTestRect.IsInside( aMousePosPixel );
            if ( !bNewPressed )
            {
                mbFadeOutPressed = bNewPressed;
                Invalidate();

                // We need a mouseevent with a position inside the button for the
                // ImplStartSplit function!
                MouseEvent aOrgMEvt = rTEvt.GetMouseEvent();
                MouseEvent aNewMEvt( aTestRect.Center(), aOrgMEvt.GetClicks(),
                                     aOrgMEvt.GetMode(), aOrgMEvt.GetButtons(),
                                     aOrgMEvt.GetModifier() );

                ImplStartSplit( aNewMEvt );
                mbFadeOutDown = false;
            }
        }
    }
    else
    {
        ImplSplitMousePos( aMousePosPixel );
        bool bSplit = true;
        if ( mbDragFull )
        {
            if ( rTEvt.IsTrackingEnded() )
            {
                if ( rTEvt.IsTrackingCanceled() )
                {
                    std::vector< ImplSplitItem >& rItems = mpSplitSet->mvItems;
                    size_t          nItems = rItems.size();
                    for ( size_t i = 0; i < nItems; i++ )
                    {
                        rItems[i].mnSize     = mpLastSizes[i*2];
                        rItems[i].mnPixSize  = mpLastSizes[i*2+1];
                    }
                    ImplUpdate();
                    Split();
                }
                bSplit = false;
            }
        }
        else
        {
            if ( rTEvt.IsTrackingEnded() )
            {
                HideTracking();
                bSplit = !rTEvt.IsTrackingCanceled();
            }
            else
            {
                ImplDrawSplitTracking(aMousePosPixel);
                bSplit = false;
            }
        }

        if ( bSplit )
        {
            bool    bPropSmaller = (mnMouseModifier & KEY_SHIFT) != 0;
            bool    bPropGreater = (mnMouseModifier & KEY_MOD1) != 0;
            long    nDelta = mnMSplitPos-mnMStartPos;

            if ( (mnSplitTest & SPLIT_WINDOW) && mpMainSet->mvItems.empty() )
            {
                if ( (mpSplitSet == mpMainSet.get()) && mbBottomRight )
                    nDelta *= -1;
                ImplSetWindowSize( nDelta );
            }
            else
            {
                long nNewSize = mpSplitSet->mvItems[mnSplitPos].mnPixSize;
                if ( (mpSplitSet == mpMainSet.get()) && mbBottomRight )
                    nNewSize -= nDelta;
                else
                    nNewSize += nDelta;
                SplitItem( mpSplitSet->mvItems[mnSplitPos].mnId, nNewSize,
                           bPropSmaller, bPropGreater );
            }

            Split();

            if ( mbDragFull )
            {
                PaintImmediately();
                mnMStartPos = mnMSplitPos;
            }
        }

        if ( rTEvt.IsTrackingEnded() )
        {
            delete [] mpLastSizes;
            mpLastSizes     = nullptr;
            mpSplitSet      = nullptr;
            mnMouseOff      = 0;
            mnMStartPos     = 0;
            mnMSplitPos     = 0;
            mnMouseModifier = 0;
            mnSplitTest     = 0;
            mnSplitPos      = 0;
        }
    }
}

bool SplitWindow::PreNotify( NotifyEvent& rNEvt )
{
    if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
    {
        const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
        if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
        {
            // trigger redraw if mouse over state has changed
            tools::Rectangle aFadeInRect;
            tools::Rectangle aFadeOutRect;
            ImplGetFadeInRect( aFadeInRect );
            ImplGetFadeOutRect( aFadeOutRect );

            if ( aFadeInRect.IsInside( GetPointerPosPixel() ) != aFadeInRect.IsInside( GetLastPointerPosPixel() ) )
                Invalidate( aFadeInRect );
            if ( aFadeOutRect.IsInside( GetPointerPosPixel() ) != aFadeOutRect.IsInside( GetLastPointerPosPixel() ) )
                Invalidate( aFadeOutRect );

            if( pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow() )
            {
                Invalidate( aFadeInRect );
                Invalidate( aFadeOutRect );
            }
        }
    }
    return Window::PreNotify( rNEvt );
}

void SplitWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
{
    if (mnWinStyle & WB_BORDER)
        ImplDrawBorder(rRenderContext);

    ImplDrawBorderLine(rRenderContext);
    ImplDrawFadeOut(rRenderContext);
    ImplDrawFadeIn(rRenderContext);

    // draw splitter
    if (!(mnWinStyle & WB_NOSPLITDRAW))
    {
        ImplDrawSplit(rRenderContext, mpMainSet.get(), mbHorz, !mbBottomRight);
    }
}

void SplitWindow::Resize()
{
    Size aSize = GetOutputSizePixel();
    mnDX = aSize.Width();
    mnDY = aSize.Height();

    ImplUpdate();
    Invalidate();
}

void SplitWindow::RequestHelp( const HelpEvent& rHEvt )
{
    // no keyboard help for splitwin
    if ( rHEvt.GetMode() & (HelpEventMode::BALLOON | HelpEventMode::QUICK) && !rHEvt.KeyboardActivated() )
    {
        Point       aMousePosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
        tools::Rectangle   aHelpRect;
        const char* pHelpResId = nullptr;

        ImplGetFadeInRect( aHelpRect, true );
        if ( aHelpRect.IsInside( aMousePosPixel ) )
            pHelpResId = SV_HELPTEXT_FADEIN;
        else
        {
            ImplGetFadeOutRect( aHelpRect );
            if ( aHelpRect.IsInside( aMousePosPixel ) )
                pHelpResId = SV_HELPTEXT_FADEOUT;
        }

        // get rectangle
        if (pHelpResId)
        {
            Point aPt = OutputToScreenPixel( aHelpRect.TopLeft() );
            aHelpRect.SetLeft( aPt.X() );
            aHelpRect.SetTop( aPt.Y() );
            aPt = OutputToScreenPixel( aHelpRect.BottomRight() );
            aHelpRect.SetRight( aPt.X() );
            aHelpRect.SetBottom( aPt.Y() );

            // get and draw text
            OUString aStr = VclResId(pHelpResId);
            if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
                Help::ShowBalloon( this, aHelpRect.Center(), aHelpRect, aStr );
            else
                Help::ShowQuickHelp( this, aHelpRect, aStr );
            return;
        }
    }

    DockingWindow::RequestHelp( rHEvt );
}

void SplitWindow::StateChanged( StateChangedType nType )
{
    switch ( nType )
    {
    case StateChangedType::InitShow:
        if ( IsUpdateMode() )
            ImplCalcLayout();
        break;
    case StateChangedType::UpdateMode:
        if ( IsUpdateMode() && IsReallyShown() )
            ImplCalcLayout();
        break;
    case StateChangedType::ControlBackground:
        ImplInitSettings();
        Invalidate();
        break;
    default:;
    }

    DockingWindow::StateChanged( nType );
}

void SplitWindow::DataChanged( const DataChangedEvent& rDCEvt )
{
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
         (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
    {
        ImplInitSettings();
        Invalidate();
    }
    else
        DockingWindow::DataChanged( rDCEvt );
}

void SplitWindow::InsertItem( sal_uInt16 nId, vcl::Window* pWindow, long nSize,
                              sal_uInt16 nPos, sal_uInt16 nIntoSetId,
                              SplitWindowItemFlags nBits )
{
#ifdef DBG_UTIL
    sal_uInt16 nDbgDummy;
    SAL_WARN_IF( ImplFindItem( mpMainSet.get(), nId, nDbgDummy ), "vcl", "SplitWindow::InsertItem() - Id already exists" );
#endif

    // Size has to be at least 1.
    if ( nSize < 1 )
        nSize = 1;

    ImplSplitSet* pSet       = ImplFindSet( mpMainSet.get(), nIntoSetId );
#ifdef DBG_UTIL
    SAL_WARN_IF( !pSet, "vcl", "SplitWindow::InsertItem() - Set not exists" );
#endif
    if(!pSet)
    {
        return;
    }

    // Don't insert further than the end
    if ( nPos > pSet->mvItems.size() )
        nPos = pSet->mvItems.size();

    // Insert in set
    pSet->mvItems.emplace( pSet->mvItems.begin() + nPos );

    // init new item
    ImplSplitItem & aItem = pSet->mvItems[nPos];
    aItem.mnSize   = nSize;
    aItem.mnPixSize = 0;
    aItem.mnId     = nId;
    aItem.mnBits   = nBits;
    aItem.mnMinSize=-1;
    aItem.mnMaxSize=-1;

    if ( pWindow )
    {
        // New VclPtr reference
        aItem.mpWindow         = pWindow;
        aItem.mpOrgParent      = pWindow->GetParent();

        // Attach window to SplitWindow.
        pWindow->Hide();
        pWindow->SetParent( this );
    }
    else
    {
        ImplSplitSet * pNewSet  = new ImplSplitSet();
        pNewSet->mnId           = nId;
        pNewSet->mnSplitSize    = pSet->mnSplitSize;

        aItem.mpSet.reset(pNewSet);
    }

    pSet->mbCalcPix = true;

    ImplUpdate();
}

void SplitWindow::InsertItem( sal_uInt16 nId, long nSize,
                              sal_uInt16 nPos, sal_uInt16 nIntoSetId,
                              SplitWindowItemFlags nBits )
{
    InsertItem( nId, nullptr, nSize, nPos, nIntoSetId, nBits );
}

void SplitWindow::RemoveItem( sal_uInt16 nId )
{
#ifdef DBG_UTIL
    sal_uInt16 nDbgDummy;
    SAL_WARN_IF( !ImplFindItem( mpMainSet.get(), nId, nDbgDummy ), "vcl", "SplitWindow::RemoveItem() - Id not found" );
#endif

    // search set
    sal_uInt16     nPos;
    ImplSplitSet*  pSet    = ImplFindItem( mpMainSet.get(), nId, nPos );

    if (!pSet)
        return;

    ImplSplitItem* pItem = &pSet->mvItems[nPos];
    VclPtr<vcl::Window> pWindow = pItem->mpWindow;
    VclPtr<vcl::Window> pOrgParent = pItem->mpOrgParent;

    // delete set if required
    if ( !pWindow )
        pItem->mpSet.reset();

    // remove item
    pSet->mbCalcPix = true;
    pSet->mvItems.erase( pSet->mvItems.begin() + nPos );

    ImplUpdate();

    // to have the least amounts of paints delete window only here
    if ( pWindow )
    {
        // restore window
        pWindow->Hide();
        pWindow->SetParent( pOrgParent );
    }

    // Clear and delete
    pWindow.clear();
    pOrgParent.clear();
}

void SplitWindow::SplitItem( sal_uInt16 nId, long nNewSize,
                             bool bPropSmall, bool bPropGreat )
{
    sal_uInt16      nPos;
    ImplSplitSet*   pSet = ImplFindItem( mpBaseSet, nId, nPos );

    if (!pSet)
        return;

    size_t           nItems = pSet->mvItems.size();
    std::vector< ImplSplitItem >&  rItems = pSet->mvItems;

    // When there is an explicit minimum or maximum size then move nNewSize
    // into that range (when it is not yet already in it.)
    nNewSize = ValidateSize(nNewSize, rItems[nPos]);

    if ( mbCalc )
    {
        rItems[nPos].mnSize = nNewSize;
        return;
    }

    long nDelta = nNewSize-rItems[nPos].mnPixSize;
    if ( !nDelta )
        return;

    // calculate area, which could be affected by splitting
    sal_uInt16 nMin = 0;
    sal_uInt16 nMax = nItems;
    for (size_t i = 0; i < nItems; ++i)
    {
        if ( rItems[i].mbFixed )
        {
            if ( i < nPos )
                nMin = i+1;
            else
                nMax = i;
        }
    }

    // treat TopSet different if the window is sizeable
    bool bSmall  = true;
    bool bGreat  = true;
    if ( (pSet == mpMainSet.get()) && (mnWinStyle & WB_SIZEABLE) )
    {
        if ( nPos < pSet->mvItems.size()-1 )
        {
            if ( !((bPropSmall && bPropGreat) ||
                   ((nDelta > 0) && bPropSmall) ||
                   ((nDelta < 0) && bPropGreat)) )
            {
                if ( nDelta < 0 )
                    bGreat = false;
                else
                    bSmall = false;
            }
        }
        else
        {
            if ( nDelta < 0 )
                bGreat = false;
            else
                bSmall = false;
        }
    }
    else if ( nPos >= nMax )
    {
        bSmall = false;
        bGreat = false;
    }
    else if ( nPos && (nPos >= pSet->mvItems.size()-1) )
    {
        nPos--;
        nDelta *= -1;
        bool bTemp = bPropSmall;
        bPropSmall = bPropGreat;
        bPropGreat = bTemp;
    }

    sal_uInt16          n;
    // now splitt the windows
    if ( nDelta < 0 )
    {
        if ( bGreat )
        {
            if ( bPropGreat )
            {
                long nTempDelta = nDelta;
                do
                {
                    n = nPos+1;
                    do
                    {
                        if ( nTempDelta )
                        {
                            rItems[n].mnPixSize++;
                            nTempDelta++;
                        }
                        n++;
                    }
                    while ( n < nMax );
                }
                while ( nTempDelta );
            }
            else
                rItems[nPos+1].mnPixSize -= nDelta;
        }

        if ( bSmall )
        {
            if ( bPropSmall )
            {
                do
                {
                    n = nPos+1;
                    do
                    {
                        if ( nDelta && rItems[n-1].mnPixSize )
                        {
                            rItems[n-1].mnPixSize--;
                            nDelta++;
                        }

                        n--;
                    }
                    while ( n > nMin );
                }
                while ( nDelta );
            }
            else
            {
                n = nPos+1;
                do
                {
                    if ( rItems[n-1].mnPixSize+nDelta < 0 )
                    {
                        nDelta += rItems[n-1].mnPixSize;
                        rItems[n-1].mnPixSize = 0;
                    }
                    else
                    {
                        rItems[n-1].mnPixSize += nDelta;
                        break;
                    }
                    n--;
                }
                while ( n > nMin );
            }
        }
    }
    else
    {
        if ( bGreat )
        {
            if ( bPropGreat )
            {
                long nTempDelta = nDelta;
                do
                {
                    n = nPos+1;
                    do
                    {
                        if ( nTempDelta )
                        {
                            rItems[n-1].mnPixSize++;
                            nTempDelta--;
                        }
                        n--;
                    }
                    while ( n > nMin );
                }
                while ( nTempDelta );
            }
            else
                rItems[nPos].mnPixSize += nDelta;
        }

        if ( bSmall )
        {
            if ( bPropSmall )
            {
                do
                {
                    n = nPos+1;
                    do
                    {
                        if ( nDelta && rItems[n].mnPixSize )
                        {
                            rItems[n].mnPixSize--;
                            nDelta--;
                        }

                        n++;
                    }
                    while ( n < nMax );
                }
                while ( nDelta );
            }
            else
            {
                n = nPos+1;
                do
                {
                    if ( rItems[n].mnPixSize-nDelta < 0 )
                    {
                        nDelta -= rItems[n].mnPixSize;
                        rItems[n].mnPixSize = 0;
                    }
                    else
                    {
                        rItems[n].mnPixSize -= nDelta;
                        break;
                    }
                    n++;
                }
                while ( n < nMax );
            }
        }
    }

    // update original sizes
    ImplCalcLogSize( rItems, nItems );

    ImplUpdate();
}

void SplitWindow::SetItemSize( sal_uInt16 nId, long nNewSize )
{
    sal_uInt16          nPos;
    ImplSplitSet*   pSet = ImplFindItem( mpBaseSet, nId, nPos );
    ImplSplitItem*  pItem;

    if ( !pSet )
        return;

    // check if size is changed
    pItem = &pSet->mvItems[nPos];
    if ( pItem->mnSize != nNewSize )
    {
        // set new size and re-calculate
        pItem->mnSize = nNewSize;
        pSet->mbCalcPix = true;
        ImplUpdate();
    }
}

long SplitWindow::GetItemSize( sal_uInt16 nId ) const
{
    sal_uInt16          nPos;
    ImplSplitSet*   pSet = ImplFindItem( mpBaseSet, nId, nPos );

    if ( pSet )
        return pSet->mvItems[nPos].mnSize;
    else
        return 0;
}

long SplitWindow::GetItemSize( sal_uInt16 nId, SplitWindowItemFlags nBits ) const
{
    sal_uInt16          nPos;
    ImplSplitSet*   pSet = ImplFindItem( mpBaseSet, nId, nPos );

    if ( pSet )
    {
        if ( nBits == pSet->mvItems[nPos].mnBits )
            return pSet->mvItems[nPos].mnSize;
        else
        {
            const_cast<SplitWindow*>(this)->ImplCalcLayout();

            long                nRelSize = 0;
            long                nPerSize = 0;
            size_t              nItems;
            SplitWindowItemFlags nTempBits;
            nItems = pSet->mvItems.size();
            std::vector< ImplSplitItem >& rItems = pSet->mvItems;
            for ( size_t i = 0; i < nItems; i++ )
            {
                if ( i == nPos )
                    nTempBits = nBits;
                else
                    nTempBits = rItems[i].mnBits;
                if ( nTempBits & SplitWindowItemFlags::RelativeSize )
                    nRelSize += rItems[i].mnPixSize;
                else if ( nTempBits & SplitWindowItemFlags::PercentSize )
                    nPerSize += rItems[i].mnPixSize;
            }
            nPerSize += nRelSize;
            if ( nBits & SplitWindowItemFlags::RelativeSize )
            {
                if ( nRelSize )
                    return (rItems[nPos].mnPixSize+(nRelSize/2))/nRelSize;
                else
                    return 1;
            }
            else if ( nBits & SplitWindowItemFlags::PercentSize )
            {
                if ( nPerSize )
                    return (rItems[nPos].mnPixSize*100)/nPerSize;
                else
                    return 1;
            }
            else
                return rItems[nPos].mnPixSize;
        }
    }
    else
        return 0;
}

void SplitWindow::SetItemSizeRange (sal_uInt16 nId, const Range& rRange)
{
    sal_uInt16 nPos;
    ImplSplitSet* pSet = ImplFindItem(mpBaseSet, nId, nPos);

    if (pSet != nullptr)
    {
        pSet->mvItems[nPos].mnMinSize = rRange.Min();
        pSet->mvItems[nPos].mnMaxSize = rRange.Max();
    }
}

sal_uInt16 SplitWindow::GetSet( sal_uInt16 nId ) const
{
    sal_uInt16          nPos;
    ImplSplitSet*   pSet = ImplFindItem( mpBaseSet, nId, nPos );

    if ( pSet )
        return pSet->mnId;
    else
        return 0;
}

bool SplitWindow::IsItemValid( sal_uInt16 nId ) const
{
    sal_uInt16          nPos;
    ImplSplitSet* pSet = mpBaseSet ? ImplFindItem(mpBaseSet, nId, nPos) : nullptr;

    return pSet != nullptr;
}

sal_uInt16 SplitWindow::GetItemId( vcl::Window* pWindow ) const
{
    return ImplFindItem( mpBaseSet, pWindow );
}

sal_uInt16 SplitWindow::GetItemId( const Point& rPos ) const
{
    return ImplFindItem( mpBaseSet, rPos, mbHorz, !mbBottomRight );
}

sal_uInt16 SplitWindow::GetItemPos( sal_uInt16 nId, sal_uInt16 nSetId ) const
{
    ImplSplitSet*   pSet = ImplFindSet( mpBaseSet, nSetId );
    sal_uInt16          nPos = SPLITWINDOW_ITEM_NOTFOUND;

    if ( pSet )
    {
        for ( size_t i = 0; i < pSet->mvItems.size(); i++ )
        {
            if ( pSet->mvItems[i].mnId == nId )
            {
                nPos = i;
                break;
            }
        }
    }

    return nPos;
}

sal_uInt16 SplitWindow::GetItemId( sal_uInt16 nPos ) const
{
    ImplSplitSet* pSet = ImplFindSet( mpBaseSet, 0/*nSetId*/ );
    if ( pSet && (nPos < pSet->mvItems.size()) )
        return pSet->mvItems[nPos].mnId;
    else
        return 0;
}

sal_uInt16 SplitWindow::GetItemCount( sal_uInt16 nSetId ) const
{
    ImplSplitSet* pSet = ImplFindSet( mpBaseSet, nSetId );
    if ( pSet )
        return pSet->mvItems.size();
    else
        return 0;
}

void SplitWindow::ImplNewAlign()
{
    switch ( meAlign )
    {
    case WindowAlign::Top:
        mbHorz        = true;
        mbBottomRight = false;
        break;
    case WindowAlign::Bottom:
        mbHorz        = true;
        mbBottomRight = true;
        break;
    case WindowAlign::Left:
        mbHorz        = false;
        mbBottomRight = false;
        break;
    case WindowAlign::Right:
        mbHorz        = false;
        mbBottomRight = true;
        break;
    }

    if ( mnWinStyle & WB_BORDER )
    {
        ImplCalcBorder( meAlign, mnLeftBorder, mnTopBorder,
                        mnRightBorder, mnBottomBorder );
    }

    if ( IsReallyVisible() && IsUpdateMode() )
        Invalidate();
    ImplUpdate();
}

void SplitWindow::SetAlign( WindowAlign eNewAlign )
{
    if ( meAlign != eNewAlign )
    {
        meAlign = eNewAlign;
        ImplNewAlign();
    }
}

void SplitWindow::ShowFadeInHideButton()
{
    mbFadeIn = true;
    ImplUpdate();
}

void SplitWindow::ShowFadeOutButton()
{
    mbFadeOut = true;
    ImplUpdate();
}

long SplitWindow::GetFadeInSize() const
{
    long n = 0;

    if ( mbHorz )
        n = mnTopBorder+mnBottomBorder;
    else
        n = mnLeftBorder+mnRightBorder;

    return n+SPLITWIN_SPLITSIZE+SPLITWIN_SPLITSIZEEX-2;
}

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