/* -*- 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 .
 */
 
#ifdef __sun
#include <ctime>
#endif
 
#include <unotools/viewoptions.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <tools/debug.hxx>
 
#include <vcl/dialoghelper.hxx>
#include <vcl/event.hxx>
#include <vcl/timer.hxx>
#include <vcl/svapp.hxx>
 
#include <splitwin.hxx>
#include <workwin.hxx>
#include <sfx2/dockwin.hxx>
#include <o3tl/string_view.hxx>
 
#include <cassert>
#include <memory>
#include <vector>
#include <utility>
 
using namespace ::com::sun::star::uno;
 
#define VERSION 1
#define nPixel  30L
constexpr OUString USERITEM_NAME = u"UserItem"_ustr;
 
namespace {
    // helper class to deactivate UpdateMode, if needed, for the life time of an instance
    class DeactivateUpdateMode
    {
    public:
        explicit DeactivateUpdateMode( SfxSplitWindow& rSplitWindow )
            : mrSplitWindow( rSplitWindow )
            , mbUpdateMode( rSplitWindow.IsUpdateMode() )
        {
            if ( mbUpdateMode )
            {
                mrSplitWindow.SetUpdateMode( false );
            }
        }
 
        ~DeactivateUpdateMode()
        {
            if ( mbUpdateMode )
            {
                mrSplitWindow.SetUpdateMode( true );
            }
        }
 
    private:
        SfxSplitWindow& mrSplitWindow;
        const bool mbUpdateMode;
    };
}
 
class SfxEmptySplitWin_Impl : public SplitWindow
{
/*  [Description]
 
    The SfxEmptySplitWin_Impldow is an empty SplitWindow, that replaces the
    SfxSplitWindow AutoHide mode. It only serves as a placeholder to receive
    mouse moves and if possible blend in the true SplitWindow display.
*/
friend class SfxSplitWindow;
 
    VclPtr<SfxSplitWindow>  pOwner;
    bool                    bFadeIn;
    bool                    bAutoHide;
    bool                    bSplit;
    bool                    bEndAutoHide;
    Timer                   aTimer;
    Point                   aLastPos;
    sal_uInt16              nState;
 
public:
   explicit             SfxEmptySplitWin_Impl( SfxSplitWindow *pParent )
                            : SplitWindow( pParent->GetParent(), WinBits( WB_BORDER | WB_3DLOOK ) )
                            , pOwner( pParent )
                            , bFadeIn( false )
                            , bAutoHide( false )
                            , bSplit( false )
                            , bEndAutoHide( false )
                            , aTimer("sfx2 SfxEmptySplitWin_Impl aTimer")
                            , nState( 1 )
                        {
                            aTimer.SetInvokeHandler(
                                LINK(pOwner, SfxSplitWindow, TimerHdl ) );
                            aTimer.SetTimeout( 200 );
                            SetAlign( pOwner->GetAlign() );
                            Actualize();
                            ShowFadeInHideButton();
                        }
 
                        virtual ~SfxEmptySplitWin_Impl() override
                        { disposeOnce(); }
   virtual void         dispose() override
                        {
                            aTimer.Stop();
                            pOwner.clear();
                            SplitWindow::dispose();
                        }
 
    virtual void        FadeIn() override;
    void                Actualize();
};
 
void SfxEmptySplitWin_Impl::Actualize()
{
    Size aSize( pOwner->GetSizePixel() );
    switch ( pOwner->GetAlign() )
    {
        case WindowAlign::Left:
        case WindowAlign::Right:
            aSize.setWidth( GetFadeInSize() );
            break;
        case WindowAlign::Top:
        case WindowAlign::Bottom:
            aSize.setHeight( GetFadeInSize() );
            break;
    }
 
    SetSizePixel( aSize );
}
 
void SfxEmptySplitWin_Impl::FadeIn()
{
    if (!bAutoHide )
        bAutoHide = IsFadeNoButtonMode();
    pOwner->SetFadeIn_Impl( true );
    if ( bAutoHide )
    {
        // Set Timer to close; the caller has to ensure themselves that the
        // Window is not closed instantly (eg by setting the focus or a modal
        // mode.
        aLastPos = GetPointerPosPixel();
        aTimer.Start();
    }
    else
        pOwner->SaveConfig_Impl();
}
 
 
void SfxSplitWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( rMEvt.GetClicks() != 2 )
        SplitWindow::MouseButtonDown( rMEvt );
}
 
SfxSplitWindow::SfxSplitWindow( vcl::Window* pParent, SfxChildAlignment eAl,
        SfxWorkWindow *pW, bool bWithButtons )
 
/*  [Description]
 
    A SfxSplitWindow brings the recursive structure of the SV-SplitWindows to
    the outside by simulating a table-like structure with rows and columns
    (maximum recursion depth 2). Furthermore, it ensures the persistence of
    the arrangement of the SfxDockingWindows.
*/
 
:   SplitWindow ( pParent, WB_BORDER | WB_SIZEABLE | WB_3DLOOK | WB_HIDE ),
    eAlign(eAl),
    pWorkWin(pW),
    bPinned(true),
    pEmptyWin(nullptr),
    pActive(nullptr)
{
    if (bWithButtons)
    {
        ShowFadeOutButton();
    }
 
    // Set SV-Alignment
    WindowAlign eTbxAlign;
    switch ( eAlign )
    {
        case SfxChildAlignment::LEFT:
            eTbxAlign = WindowAlign::Left;
            break;
        case SfxChildAlignment::RIGHT:
            eTbxAlign = WindowAlign::Right;
            break;
        case SfxChildAlignment::TOP:
            eTbxAlign = WindowAlign::Top;
            break;
        case SfxChildAlignment::BOTTOM:
            eTbxAlign = WindowAlign::Bottom;
            bPinned = true;
            break;
        default:
            eTbxAlign = WindowAlign::Top;  // some sort of default...
            break;  // -Wall lots not handled...
    }
 
    SetAlign (eTbxAlign);
    pEmptyWin = VclPtr<SfxEmptySplitWin_Impl>::Create( this );
    if ( bPinned )
    {
        pEmptyWin->bFadeIn = true;
        pEmptyWin->nState = 2;
    }
 
    if ( bWithButtons )
    {
        //  Read Configuration
        const OUString aWindowId{ "SplitWindow" + OUString::number(static_cast<sal_Int32>(eTbxAlign)) };
        SvtViewOptions aWinOpt( EViewType::Window, aWindowId );
        OUString aWinData;
        Any aUserItem = aWinOpt.GetUserItem( USERITEM_NAME );
        OUString aTemp;
        if ( aUserItem >>= aTemp )
            aWinData = aTemp;
        if ( aWinData.startsWith("V") )
        {
            sal_Int32 nIdx{ 0 };
            pEmptyWin->nState = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 1, ',', nIdx )));
            if ( pEmptyWin->nState & 2 )
                pEmptyWin->bFadeIn = true;
            bPinned = true; // always assume pinned - floating mode not used anymore
 
            const sal_Int32 nCount{ o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx)) };
            for ( sal_Int32 n=0; n<nCount; ++n )
            {
                std::unique_ptr<SfxDock_Impl> pDock(new SfxDock_Impl);
                pDock->pWin = nullptr;
                pDock->bNewLine = false;
                pDock->bHide = true;
                pDock->nType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx)));
                if ( !pDock->nType )
                {
                    // could mean NewLine
                    pDock->nType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx)));
                    if ( !pDock->nType )
                    {
                        // Read error
                        break;
                    }
                    else
                        pDock->bNewLine = true;
                }
 
                maDockArr.insert(maDockArr.begin() + n, std::move(pDock));
            }
        }
    }
    else
    {
        bPinned = true;
        pEmptyWin->bFadeIn = true;
        pEmptyWin->nState = 2;
    }
}
 
 
SfxSplitWindow::~SfxSplitWindow()
{
    disposeOnce();
}
 
void SfxSplitWindow::dispose()
{
    SaveConfig_Impl();
 
    if ( pEmptyWin )
    {
        // Set pOwner to NULL, otherwise try to delete pEmptyWin once more. The
        // window that is just being docked is always deleted from the outside.
        pEmptyWin->pOwner = nullptr;
    }
    pEmptyWin.disposeAndClear();
 
    maDockArr.clear();
    pActive.clear();
    SplitWindow::dispose();
}
 
void SfxSplitWindow::SaveConfig_Impl()
{
    // Save configuration
    OUStringBuffer aWinData =
        "V"
        + OUString::number(static_cast<sal_Int32>(VERSION))
        + ","
        + OUString::number(static_cast<sal_Int32>(pEmptyWin->nState))
        + ",";
 
    sal_uInt16 nCount = 0;
    for ( auto const & rDock: maDockArr )
    {
        if ( rDock->bHide || rDock->pWin )
            nCount++;
    }
 
    aWinData.append(static_cast<sal_Int32>(nCount));
 
    for ( auto const & rDock: maDockArr )
    {
        if ( !rDock->bHide && !rDock->pWin )
            continue;
        if ( rDock->bNewLine )
            aWinData.append(",0");
        aWinData.append("," + OUString::number(static_cast<sal_Int32>(rDock->nType)));
    }
 
    const OUString aWindowId{ "SplitWindow" + OUString::number(static_cast<sal_Int32>(GetAlign())) };
    SvtViewOptions aWinOpt( EViewType::Window, aWindowId );
    aWinOpt.SetUserItem( USERITEM_NAME, Any( aWinData.makeStringAndClear() ) );
}
 
 
void SfxSplitWindow::StartSplit()
{
    tools::Long nSize = 0;
    Size aSize = GetSizePixel();
 
    if ( pEmptyWin )
    {
        pEmptyWin->bFadeIn = true;
        pEmptyWin->bSplit = true;
    }
 
    tools::Rectangle aRect = pWorkWin->GetFreeArea( !bPinned );
    switch ( GetAlign() )
    {
        case WindowAlign::Left:
        case WindowAlign::Right:
            nSize = aSize.Width() + aRect.GetWidth();
            break;
        case WindowAlign::Top:
        case WindowAlign::Bottom:
            nSize = aSize.Height() + aRect.GetHeight();
            break;
    }
 
    SetMaxSizePixel( nSize );
}
 
 
void SfxSplitWindow::SplitResize()
{
    if ( bPinned )
    {
        pWorkWin->ArrangeChildren_Impl();
        pWorkWin->ShowChildren_Impl();
    }
    else
        pWorkWin->ArrangeAutoHideWindows( this );
}
 
 
void SfxSplitWindow::Split()
{
    if ( pEmptyWin )
        pEmptyWin->bSplit = false;
 
    SplitWindow::Split();
 
    std::vector< std::pair< sal_uInt16, tools::Long > > aNewOrgSizes;
 
    sal_uInt16 nCount = maDockArr.size();
    for ( sal_uInt16 n=0; n<nCount; n++ )
    {
        const SfxDock_Impl& rD = *maDockArr[n];
        if ( rD.pWin )
        {
            const sal_uInt16 nId = rD.nType;
            const tools::Long nSize    = GetItemSize( nId, SplitWindowItemFlags::Fixed );
            const tools::Long nSetSize = GetItemSize( GetSet( nId ) );
            Size aSize;
 
            if ( IsHorizontal() )
            {
                aSize.setWidth( nSize );
                aSize.setHeight( nSetSize );
            }
            else
            {
                aSize.setWidth( nSetSize );
                aSize.setHeight( nSize );
            }
 
            rD.pWin->SetItemSize_Impl( aSize );
 
            aNewOrgSizes.emplace_back( nId, nSize );
        }
    }
 
    // workaround insufficiency of <SplitWindow> regarding dock layouting:
    // apply FIXED item size as 'original' item size to improve layouting of undock-dock-cycle of a window
    {
        DeactivateUpdateMode aDeactivateUpdateMode( *this );
        for (const std::pair< sal_uInt16, tools::Long > & rNewOrgSize : aNewOrgSizes)
        {
            SetItemSize( rNewOrgSize.first, rNewOrgSize.second );
        }
    }
 
    SaveConfig_Impl();
}
 
 
void SfxSplitWindow::InsertWindow( SfxDockingWindow* pDockWin, const Size& rSize)
 
/*
    To insert SfxDockingWindows just pass no position. The SfxSplitWindow
    searches the last marked one to the passed SfxDockingWindow or appends a
    new one at the end.
*/
{
    short nLine = -1;  // so that the first window cab set nline to 0
    sal_uInt16 nPos = 0;
    bool bNewLine = true;
    bool bSaveConfig = false;
    SfxDock_Impl *pFoundDock=nullptr;
    sal_uInt16 nCount = maDockArr.size();
    for ( sal_uInt16 n=0; n<nCount; n++ )
    {
        SfxDock_Impl& rDock = *maDockArr[n];
        if ( rDock.bNewLine )
        {
            // The window opens a new line
            if ( pFoundDock )
                // But after the just inserted window
                break;
 
            // New line
            nPos = 0;
            bNewLine = true;
        }
 
        if ( rDock.pWin )
        {
            // Does there exist a window now at this position
            if ( bNewLine && !pFoundDock )
            {
                // Not known until now in which real line it is located
                sal_uInt16 nL = 0;
                [[maybe_unused]] auto const ok = GetWindowPos( rDock.pWin, nL, nPos );
                assert(ok);
                nLine = static_cast<short>(nL);
            }
 
            if ( !pFoundDock )
            {
                // The window is located before the inserted one
                nPos++;
            }
 
            // Line is opened
            bNewLine = false;
            if ( pFoundDock )
                break;
        }
 
        if ( rDock.nType == pDockWin->GetType() )
        {
            DBG_ASSERT( !pFoundDock && !rDock.pWin, "Window already exists!");
            pFoundDock = &rDock;
            if ( !bNewLine )
                break;
            else
            {
                // A new line has been created but no window was found there;
                // continue searching for a window in this line in-order to set
                // bNewLine correctly. While doing so nline or nPos are not
                // to be changed!
                nLine++;
            }
        }
    }
 
    if ( !pFoundDock )
    {
        // Not found, insert at end
        pFoundDock = new SfxDock_Impl;
        pFoundDock->bHide = true;
        maDockArr.push_back( std::unique_ptr<SfxDock_Impl>(pFoundDock) );
        pFoundDock->nType = pDockWin->GetType();
        nLine++;
        nPos = 0;
        bNewLine = true;
        pFoundDock->bNewLine = bNewLine;
        bSaveConfig = true;
    }
 
    pFoundDock->pWin = pDockWin;
    pFoundDock->bHide = false;
    InsertWindow_Impl( pFoundDock, rSize, nLine, nPos, bNewLine );
    if ( bSaveConfig )
        SaveConfig_Impl();
}
 
 
void SfxSplitWindow::ReleaseWindow_Impl(SfxDockingWindow const *pDockWin, bool bSave)
{
//  The docking window is no longer stored in the internal data.
    sal_uInt16 nCount = maDockArr.size();
    for ( sal_uInt16 n=0; n<nCount; n++ )
    {
        const SfxDock_Impl& rDock = *maDockArr[n];
        if ( rDock.nType == pDockWin->GetType() )
        {
            if ( rDock.bNewLine && n<nCount-1 )
                maDockArr[n+1]->bNewLine = true;
 
            // Window has a position, this we forget
            maDockArr.erase(maDockArr.begin() + n);
            break;
        }
    }
 
    if ( bSave )
        SaveConfig_Impl();
}
 
 
void SfxSplitWindow::MoveWindow( SfxDockingWindow* pDockWin, const Size& rSize,
                        sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine)
 
/*  [Description]
 
    The docking window is moved within the SplitWindows.
*/
 
{
    sal_uInt16 nL = 0;
    sal_uInt16 nP = 0;
    [[maybe_unused]] auto const ok = GetWindowPos( pDockWin, nL, nP );
    assert(ok);
 
    if ( nLine > nL && GetItemCount( GetItemId( nL ) ) == 1 )
    {
        // If the last window is removed from its line, then everything slips
        // one line to the front!
        nLine--;
    }
    RemoveWindow( pDockWin );
    InsertWindow( pDockWin, rSize, nLine, nPos, bNewLine );
}
 
 
void SfxSplitWindow::InsertWindow( SfxDockingWindow* pDockWin, const Size& rSize,
                        sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine)
 
/*  [Description]
 
    The DockingWindow that is pushed on this SplitWindow and shall hold the
    given position and size.
*/
{
    ReleaseWindow_Impl( pDockWin, false );
    SfxDock_Impl *pDock = new SfxDock_Impl;
    pDock->bHide = false;
    pDock->nType = pDockWin->GetType();
    pDock->bNewLine = bNewLine;
    pDock->pWin = pDockWin;
 
    DBG_ASSERT( nPos==0 || !bNewLine, "Wrong Parameter!");
    if ( bNewLine )
        nPos = 0;
 
    // The window must be inserted before the first window so that it has the
    // same or a greater position than pDockWin.
    sal_uInt16 nCount = maDockArr.size();
    sal_uInt16 nLastWindowIdx(0);
 
    // If no window is found, a first window is inserted
    sal_uInt16 nInsertPos = 0;
    for ( sal_uInt16 n=0; n<nCount; n++ )
    {
        SfxDock_Impl& rD = *maDockArr[n];
 
        if (rD.pWin)
        {
            // A docked window has been found. If no suitable window behind
            // the desired insertion point s found, then insertion is done at
            // the end.
            nInsertPos = nCount;
            nLastWindowIdx = n;
            sal_uInt16 nL=0, nP=0;
            GetWindowPos( rD.pWin, nL, nP );
 
            if ( (nL == nLine && nP == nPos) || nL > nLine )
            {
                DBG_ASSERT( nL == nLine || bNewLine || nPos > 0, "Wrong Parameter!" );
                if ( nL == nLine && nPos == 0 && !bNewLine )
                {
                    DBG_ASSERT(rD.bNewLine, "No new line?");
 
                    // The position is pushed to nPos==0
                    rD.bNewLine = false;
                    pDock->bNewLine = true;
                }
 
                nInsertPos = n != 0 ? nLastWindowIdx + 1 : 0;    // ignore all non-windows after the last window
                break;
            }
        }
    }
    if (nCount != 0 && nInsertPos == nCount && nLastWindowIdx != nCount - 1)
    {
        nInsertPos = nLastWindowIdx + 1;    // ignore all non-windows after the last window
    }
 
    maDockArr.insert(maDockArr.begin() + nInsertPos, std::unique_ptr<SfxDock_Impl>(pDock));
    InsertWindow_Impl( pDock, rSize, nLine, nPos, bNewLine );
    SaveConfig_Impl();
}
 
 
void SfxSplitWindow::InsertWindow_Impl( SfxDock_Impl const * pDock,
                        const Size& rSize,
                        sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine)
 
/*  [Description]
 
    Adds a DockingWindow, and causes the recalculation of the size of
    the SplitWindows.
*/
 
{
    SfxDockingWindow* pDockWin = pDock->pWin;
 
    SplitWindowItemFlags nItemBits = SplitWindowItemFlags::NONE;
 
    tools::Long nWinSize, nSetSize;
    if ( IsHorizontal() )
    {
        nWinSize = rSize.Width();
        nSetSize = rSize.Height();
    }
    else
    {
        nSetSize = rSize.Width();
        nWinSize = rSize.Height();
    }
 
    std::unique_ptr<DeactivateUpdateMode> pDeactivateUpdateMode(new DeactivateUpdateMode( *this ));
 
    if ( bNewLine || nLine == GetItemCount() )
    {
        // An existing row should not be inserted, instead a new one
        // will be created
 
        sal_uInt16 nId = 1;
        for ( sal_uInt16 n=0; n<GetItemCount(); n++ )
        {
            if ( GetItemId(n) >= nId )
                nId = GetItemId(n)+1;
        }
 
        // Create a new nLine:th line
        SplitWindowItemFlags nBits = nItemBits;
        if ( GetAlign() == WindowAlign::Top || GetAlign() == WindowAlign::Bottom )
            nBits |= SplitWindowItemFlags::ColSet;
        InsertItem( nId, nSetSize, nLine, 0, nBits );
    }
 
    // Insert the window at line with the position nline. ItemWindowSize set to
    // "percentage" share since the SV then does the re-sizing as expected,
    // "pixel" actually only makes sense if also items with percentage or
    // relative sizes are present.
    nItemBits |= SplitWindowItemFlags::PercentSize;
    sal_uInt16 nSet = GetItemId( nLine );
    InsertItem( pDockWin->GetType(), pDockWin, nWinSize, nPos, nSet, nItemBits );
 
    // SplitWindows are once created in SFX and when inserting the first
    // DockingWindows is made visible.
    if ( GetItemCount() == 1 && GetItemCount( 1 ) == 1 )
    {
        // The Rearranging in WorkWindow and a Show() on the SplitWindow is
        // caused by SfxDockingwindow (->SfxWorkWindow::ConfigChild_Impl)
        if ( !bPinned && !IsFloatingMode() )
        {
            bPinned = true;
            bool bFadeIn = ( pEmptyWin->nState & 2 ) != 0;
            pEmptyWin->bFadeIn = false;
            SetPinned_Impl( false );
            pEmptyWin->Actualize();
            SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering empty Splitwindow" );
            pWorkWin->RegisterChild_Impl( *GetSplitWindow(), eAlign )->nVisible = SfxChildVisibility::VISIBLE;
            // tdf#113539 FadeIn will call ArrangeChildren_Impl() for us, and avoiding extra calls to that
            // can make a different to load times because it avoids extra accessibility calcs
            if ( bFadeIn )
                FadeIn();
            else
                pWorkWin->ArrangeChildren_Impl();
        }
        else
        {
            bool bFadeIn = ( pEmptyWin->nState & 2 ) != 0;
            pEmptyWin->bFadeIn = false;
            pEmptyWin->Actualize();
            if ( !bPinned || !pEmptyWin->bFadeIn )
            {
                SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering empty Splitwindow" );
            }
            else
            {
                SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering real Splitwindow" );
            }
            pWorkWin->RegisterChild_Impl( *GetSplitWindow(), eAlign )->nVisible = SfxChildVisibility::VISIBLE;
            // tdf#113539 FadeIn will call ArrangeChildren_Impl() for us, and avoiding extra calls to that
            // can make a different to load times because it avoids extra accessibility calcs
            if ( bFadeIn )
                FadeIn();
            else
                pWorkWin->ArrangeChildren_Impl();
        }
 
        pWorkWin->ShowChildren_Impl();
    }
 
    pDeactivateUpdateMode.reset();
 
    // workaround insufficiency of <SplitWindow> regarding dock layouting:
    // apply FIXED item size as 'original' item size to improve layouting of undock-dock-cycle of a window
    {
        std::vector< std::pair< sal_uInt16, tools::Long > > aNewOrgSizes;
        // get FIXED item sizes
        sal_uInt16 nCount = maDockArr.size();
        for ( sal_uInt16 n=0; n<nCount; ++n )
        {
            const SfxDock_Impl& rD = *maDockArr[n];
            if ( rD.pWin )
            {
                const sal_uInt16 nId = rD.nType;
                const tools::Long nSize    = GetItemSize( nId, SplitWindowItemFlags::Fixed );
                aNewOrgSizes.emplace_back( nId, nSize );
            }
        }
        // apply new item sizes
        DeactivateUpdateMode aDeactivateUpdateMode( *this );
        for (const std::pair< sal_uInt16, tools::Long > & rNewOrgSize : aNewOrgSizes)
        {
            SetItemSize( rNewOrgSize.first, rNewOrgSize.second );
        }
    }
}
 
 
void SfxSplitWindow::RemoveWindow( SfxDockingWindow const * pDockWin, bool bHide )
 
/*  [Description]
 
    Removes a DockingWindow. If it was the last one, then the SplitWindow is
    being hidden.
*/
{
    sal_uInt16 nSet = GetSet( pDockWin->GetType() );
 
    // SplitWindows are once created in SFX and is made invisible after
    // removing the last DockingWindows.
    if ( GetItemCount( nSet ) == 1 && GetItemCount() == 1 )
    {
        // The Rearranging in WorkWindow is caused by SfxDockingwindow
        Hide();
        pEmptyWin->aTimer.Stop();
        sal_uInt16 nRealState = pEmptyWin->nState;
        FadeOut_Impl();
        pEmptyWin->Hide();
#ifdef DBG_UTIL
        if ( !bPinned || !pEmptyWin->bFadeIn )
        {
            SAL_INFO("sfx", "SfxSplitWindow::RemoveWindow - releasing empty Splitwindow" );
        }
        else
        {
            SAL_INFO("sfx", "SfxSplitWindow::RemoveWindow - releasing real Splitwindow" );
        }
#endif
        pWorkWin->ReleaseChild_Impl( *GetSplitWindow() );
        pEmptyWin->nState = nRealState;
        pWorkWin->ArrangeAutoHideWindows( this );
    }
 
    sal_uInt16 nCount = maDockArr.size();
    for ( sal_uInt16 n=0; n<nCount; n++ )
    {
        SfxDock_Impl& rDock = *maDockArr[n];
        if ( rDock.nType == pDockWin->GetType() )
        {
            rDock.pWin = nullptr;
            rDock.bHide = bHide;
            break;
        }
    }
 
    // Remove Windows, and if it was the last of the line, then also remove
    // the line (line = itemset)
    DeactivateUpdateMode aDeactivateUpdateMode( *this );
 
    RemoveItem( pDockWin->GetType() );
 
    if ( nSet && !GetItemCount( nSet ) )
        RemoveItem( nSet );
};
 
 
bool SfxSplitWindow::GetWindowPos( const SfxDockingWindow* pWindow,
                                        sal_uInt16& rLine, sal_uInt16& rPos ) const
/*  [Description]
 
    Returns the ID of the item sets and items for the DockingWindow in
    the position passed on the old row / column-name.
*/
 
{
    sal_uInt16 nSet = GetSet ( pWindow->GetType() );
    if ( nSet == SPLITWINDOW_ITEM_NOTFOUND )
        return false;
 
    rPos  = GetItemPos( pWindow->GetType(), nSet );
    rLine = GetItemPos( nSet );
    return true;
}
 
 
bool SfxSplitWindow::GetWindowPos( const Point& rTestPos,
                                      sal_uInt16& rLine, sal_uInt16& rPos ) const
/*  [Description]
 
    Returns the ID of the item sets and items for the DockingWindow in
    the position passed on the old row / column-name.
*/
 
{
    sal_uInt16 nId = GetItemId( rTestPos );
    if ( nId == 0 )
        return false;
 
    sal_uInt16 nSet = GetSet ( nId );
    rPos  = GetItemPos( nId, nSet );
    rLine = GetItemPos( nSet );
    return true;
}
 
 
sal_uInt16 SfxSplitWindow::GetLineCount() const
 
/*  [Description]
 
    Returns the number of rows = number of sub-itemsets in the root set.
*/
{
    return GetItemCount();
}
 
 
tools::Long SfxSplitWindow::GetLineSize( sal_uInt16 nLine ) const
 
/*  [Description]
 
    Returns the Row Height of nline itemset.
*/
{
    sal_uInt16 nId = GetItemId( nLine );
    return GetItemSize( nId );
}
 
 
sal_uInt16 SfxSplitWindow::GetWindowCount( sal_uInt16 nLine ) const
 
/*  [Description]
 
    Returns the total number of windows
*/
{
    sal_uInt16 nId = GetItemId( nLine );
    return GetItemCount( nId );
}
 
 
sal_uInt16 SfxSplitWindow::GetWindowCount() const
 
/*  [Description]
 
    Returns the total number of windows
*/
{
    return GetItemCount();
}
 
 
IMPL_LINK( SfxSplitWindow, TimerHdl, Timer*, pTimer, void)
{
    if ( pTimer )
        pTimer->Stop();
 
    if ( CursorIsOverRect() || !pTimer )
    {
        // If the cursor is within the window, display the SplitWindow and set
        // up the timer for close
        pEmptyWin->bAutoHide = true;
        if ( !IsVisible() )
            pEmptyWin->FadeIn();
 
        pEmptyWin->aLastPos = GetPointerPosPixel();
        pEmptyWin->aTimer.Start();
    }
    else if ( pEmptyWin->bAutoHide )
    {
        if ( GetPointerPosPixel() != pEmptyWin->aLastPos )
        {
            // The mouse has moved within the running time of the timer, thus
            // do nothing
            pEmptyWin->aLastPos = GetPointerPosPixel();
            pEmptyWin->aTimer.Start();
            return;
        }
 
        // Especially for TF_AUTOSHOW_ON_MOUSEMOVE :
        // If the window is not visible, there is nothing to do
        // (user has simply moved the mouse over pEmptyWin)
        if ( IsVisible() )
        {
            pEmptyWin->bEndAutoHide = false;
            if ( !Application::IsInModalMode() &&
                  !vcl::IsInPopupMenuExecute() &&
                  !pEmptyWin->bSplit && !HasChildPathFocus( true ) )
            {
                // While a modal dialog or a popup menu is open or while the
                // Splitting is done, in any case, do not close. Even as long
                // as one of the Children has the focus, the window remains
                // open.
                pEmptyWin->bEndAutoHide = true;
            }
 
            if ( pEmptyWin->bEndAutoHide )
            {
               // As far as I am concerned this can be the end of AutoShow
               // But maybe some other SfxSplitWindow will remain open,
               // then all others remain open too.
                if ( !pWorkWin->IsAutoHideMode( this ) )
                {
                    FadeOut_Impl();
                    pWorkWin->ArrangeAutoHideWindows( this );
                }
                else
                {
                    pEmptyWin->aLastPos = GetPointerPosPixel();
                    pEmptyWin->aTimer.Start();
                }
            }
            else
            {
                pEmptyWin->aLastPos = GetPointerPosPixel();
                pEmptyWin->aTimer.Start();
            }
        }
    }
}
 
 
bool SfxSplitWindow::CursorIsOverRect() const
{
    bool bVisible = IsVisible();
 
    // Also, take the collapsed SplitWindow into account
    Point aPos = pEmptyWin->GetParent()->OutputToScreenPixel( pEmptyWin->GetPosPixel() );
    Size aSize = pEmptyWin->GetSizePixel();
 
    tools::Rectangle aRect( aPos, aSize );
 
    if ( bVisible )
    {
        Point aVisPos = GetPosPixel();
        Size aVisSize = GetSizePixel();
 
        // Extend with +/- a few pixels, otherwise it is too nervous
        aVisPos.AdjustX( -(nPixel) );
        aVisPos.AdjustY( -(nPixel) );
        aVisSize.AdjustWidth(2 * nPixel );
        aVisSize.AdjustHeight(2 * nPixel );
 
        tools::Rectangle aVisRect( aVisPos, aVisSize );
        aRect = aRect.GetUnion( aVisRect );
    }
 
    return aRect.Contains( OutputToScreenPixel( static_cast<vcl::Window*>(const_cast<SfxSplitWindow *>(this))->GetPointerPosPixel() ) );
}
 
 
SplitWindow* SfxSplitWindow::GetSplitWindow()
{
    if ( !bPinned || !pEmptyWin->bFadeIn )
        return pEmptyWin;
    return this;
}
 
 
bool SfxSplitWindow::IsFadeIn() const
{
    return pEmptyWin->bFadeIn;
}
 
bool SfxSplitWindow::IsAutoHide( bool bSelf ) const
{
    return bSelf ? pEmptyWin->bAutoHide && !pEmptyWin->bEndAutoHide : pEmptyWin->bAutoHide;
}
 
 
void SfxSplitWindow::SetPinned_Impl( bool bOn )
{
    if ( bPinned == bOn )
        return;
 
    bPinned = bOn;
    if ( GetItemCount() == 0 )
        return;
 
    if ( !bOn )
    {
        pEmptyWin->nState |= 1;
        if ( pEmptyWin->bFadeIn )
        {
            // Unregister replacement windows
            SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - releasing real Splitwindow" );
            pWorkWin->ReleaseChild_Impl( *this );
            Hide();
            pEmptyWin->Actualize();
            SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - registering empty Splitwindow" );
            pWorkWin->RegisterChild_Impl( *pEmptyWin, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
        }
 
        Point aPos( GetPosPixel() );
        aPos = GetParent()->OutputToScreenPixel( aPos );
        SetFloatingPos( aPos );
        SetFloatingMode( true );
        GetFloatingWindow()->SetOutputSizePixel( GetOutputSizePixel() );
 
        if ( pEmptyWin->bFadeIn )
            Show();
    }
    else
    {
        pEmptyWin->nState &= ~1;
        SetOutputSizePixel( GetFloatingWindow()->GetOutputSizePixel() );
        SetFloatingMode(false);
 
        if ( pEmptyWin->bFadeIn )
        {
            // Unregister replacement windows
            SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - releasing empty Splitwindow" );
            pWorkWin->ReleaseChild_Impl( *pEmptyWin );
            pEmptyWin->Hide();
            SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - registering real Splitwindow" );
            pWorkWin->RegisterChild_Impl( *this, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
        }
    }
}
 
void SfxSplitWindow::SetFadeIn_Impl( bool bOn )
{
    if ( bOn == pEmptyWin->bFadeIn )
        return;
 
    if ( GetItemCount() == 0 )
        return;
 
    pEmptyWin->bFadeIn = bOn;
    if ( bOn )
    {
        pEmptyWin->nState |= 2;
        if ( IsFloatingMode() )
        {
            // FloatingWindow is not visible, thus display it
            pWorkWin->ArrangeAutoHideWindows( this );
            Show();
        }
        else
        {
            SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - releasing empty Splitwindow" );
            pWorkWin->ReleaseChild_Impl( *pEmptyWin );
            pEmptyWin->Hide();
            SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - registering real Splitwindow" );
            pWorkWin->RegisterChild_Impl( *this, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
            pWorkWin->ArrangeChildren_Impl();
            pWorkWin->ShowChildren_Impl();
        }
    }
    else
    {
        pEmptyWin->bAutoHide = false;
        pEmptyWin->nState &= ~2;
        if ( !IsFloatingMode() )
        {
            // The window is not "floating", should be hidden
            SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - releasing real Splitwindow" );
            pWorkWin->ReleaseChild_Impl( *this );
            Hide();
            pEmptyWin->Actualize();
            SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - registering empty Splitwindow" );
            pWorkWin->RegisterChild_Impl( *pEmptyWin, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
            pWorkWin->ArrangeChildren_Impl();
            pWorkWin->ShowChildren_Impl();
            pWorkWin->ArrangeAutoHideWindows( this );
        }
        else
        {
            Hide();
            pWorkWin->ArrangeAutoHideWindows( this );
        }
    }
}
 
void SfxSplitWindow::FadeOut_Impl()
{
    if ( pEmptyWin->aTimer.IsActive() )
    {
        pEmptyWin->bAutoHide = false;
        pEmptyWin->aTimer.Stop();
    }
 
    SetFadeIn_Impl( false );
}
 
void SfxSplitWindow::FadeOut()
{
    FadeOut_Impl();
    SaveConfig_Impl();
}
 
void SfxSplitWindow::FadeIn()
{
    SetFadeIn_Impl( true );
}
 
void SfxSplitWindow::SetActiveWindow_Impl( SfxDockingWindow* pWin )
{
    pActive = pWin;
    pWorkWin->SetActiveChild_Impl( this );
}
 
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V1053 Calling the 'SetSizePixel' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'splitwin.cxx:113', 'splitwin.cxx:145', 'window.hxx:932'.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.