    /* -*- 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 <tools/time.hxx>
    #include <sal/log.hxx>

    #include <brdwin.hxx>
    #include <svdata.hxx>
    #include <salframe.hxx>
    #include <window.h>

    #include <vcl/event.hxx>
    #include <vcl/floatwin.hxx>
    #include <vcl/dockwin.hxx>
    #include <vcl/toolbox.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/timer.hxx>
    #include <vcl/idle.hxx>
    #include <vcl/lineinfo.hxx>
    #include <vcl/unowrap.hxx>
    #include <vcl/settings.hxx>

    #define DOCKWIN_FLOATSTYLES         (WB_SIZEABLE | WB_MOVEABLE | WB_CLOSEABLE | WB_STANDALONE | WB_ROLLABLE )

    class ImplDockFloatWin2 : public FloatingWindow
    {
    private:
        ImplDockingWindowWrapper*  mpDockWin;
        sal_uInt64      mnLastTicks;
        Timer           m_aDockTimer;
        Timer           m_aEndDockTimer;
        Point           maDockPos;
        tools::Rectangle       maDockRect;
        bool            mbInMove;
        ImplSVEvent *   mnLastUserEvent;

        DECL_LINK(DockingHdl, void *, void);
        DECL_LINK(DockTimerHdl, Timer *, void);
        DECL_LINK(EndDockTimerHdl, Timer *, void);
    public:
        ImplDockFloatWin2( vcl::Window* pParent, WinBits nWinBits,
                          ImplDockingWindowWrapper* pDockingWin );
        virtual ~ImplDockFloatWin2() override;
        virtual void dispose() override;

        virtual void    Move() override;
        virtual void    Resize() override;
        virtual void    TitleButtonClick( TitleButton nButton ) override;
        virtual void    Resizing( Size& rSize ) override;
        virtual bool    Close() override;
    };

    ImplDockFloatWin2::ImplDockFloatWin2( vcl::Window* pParent, WinBits nWinBits,
                                        ImplDockingWindowWrapper* pDockingWin ) :
            FloatingWindow( pParent, nWinBits ),
            mpDockWin( pDockingWin ),
            mnLastTicks( tools::Time::GetSystemTicks() ),
            mbInMove( false ),
            mnLastUserEvent( nullptr )
    {
        // copy state of DockingWindow
        if ( pDockingWin )
        {
            SetSettings( pDockingWin->GetWindow()->GetSettings() );
            Enable( pDockingWin->GetWindow()->IsEnabled(), false );
            EnableInput( pDockingWin->GetWindow()->IsInputEnabled(), false );
            AlwaysEnableInput( pDockingWin->GetWindow()->IsAlwaysEnableInput(), false );
            EnableAlwaysOnTop( pDockingWin->GetWindow()->IsAlwaysOnTopEnabled() );
            SetActivateMode( pDockingWin->GetWindow()->GetActivateMode() );
        }

        SetBackground( GetSettings().GetStyleSettings().GetFaceColor() );

        m_aDockTimer.SetInvokeHandler( LINK( this, ImplDockFloatWin2, DockTimerHdl ) );
        m_aDockTimer.SetPriority( TaskPriority::HIGH_IDLE );
        m_aDockTimer.SetTimeout( 50 );
        m_aDockTimer.SetDebugName( "vcl::ImplDockFloatWin2 m_aDockTimer" );

        m_aEndDockTimer.SetInvokeHandler( LINK( this, ImplDockFloatWin2, EndDockTimerHdl ) );
        m_aEndDockTimer.SetPriority( TaskPriority::HIGH_IDLE );
        m_aEndDockTimer.SetTimeout( 50 );
        m_aEndDockTimer.SetDebugName( "vcl::ImplDockFloatWin2 m_aEndDockTimer" );
    }

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

    void ImplDockFloatWin2::dispose()
    {
        if( mnLastUserEvent )
            Application::RemoveUserEvent( mnLastUserEvent );
        FloatingWindow::dispose();
    }

    IMPL_LINK_NOARG(ImplDockFloatWin2, DockTimerHdl, Timer *, void)
    {
        SAL_WARN_IF( !mpDockWin->IsFloatingMode(), "vcl", "docktimer called but not floating" );

        PointerState aState = GetPointerState();

        if( aState.mnState & KEY_MOD1 )
        {
            // i43499 CTRL disables docking now
            mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
            if( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) )
                m_aDockTimer.Start();
        }
        else if( ! ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) )
        {
            mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
            mpDockWin->EndDocking( maDockRect, false );
        }
        else
        {
            mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Big | ShowTrackFlags::TrackWindow );
            m_aDockTimer.Start();
        }
    }

    IMPL_LINK_NOARG(ImplDockFloatWin2, EndDockTimerHdl, Timer *, void)
    {
        SAL_WARN_IF( !mpDockWin->IsFloatingMode(), "vcl", "enddocktimer called but not floating" );

        PointerState aState = GetPointerState();
        if( ! ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) )
        {
            mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
            mpDockWin->EndDocking( maDockRect, true );
        }
        else
            m_aEndDockTimer.Start();
    }

    IMPL_LINK_NOARG(ImplDockFloatWin2, DockingHdl, void*, void)
    {
        // called during move of a floating window
        mnLastUserEvent = nullptr;

        vcl::Window *pDockingArea = mpDockWin->GetWindow()->GetParent();
        PointerState aState = pDockingArea->GetPointerState();

        bool bRealMove = true;
        if( GetStyle() & WB_OWNERDRAWDECORATION )
        {
            // for windows with ownerdraw decoration
            // we allow docking only when the window was moved
            // by dragging its caption
            // and ignore move request due to resizing
            vcl::Window *pBorder = GetWindow( GetWindowType::Border );
            if( pBorder != this )
            {
                tools::Rectangle aBorderRect( Point(), pBorder->GetSizePixel() );
                sal_Int32 nLeft, nTop, nRight, nBottom;
                GetBorder( nLeft, nTop, nRight, nBottom );
                // limit borderrect to the caption part only and without the resizing borders
                aBorderRect.SetBottom( aBorderRect.Top() + nTop );
                aBorderRect.AdjustLeft(nLeft );
                aBorderRect.AdjustRight( -nRight );

                PointerState aBorderState = pBorder->GetPointerState();
                bRealMove = aBorderRect.IsInside( aBorderState.maPos );
            }
        }

        if( mpDockWin->GetWindow()->IsVisible() &&
            (tools::Time::GetSystemTicks() - mnLastTicks > 500) &&
            ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) &&
            !(aState.mnState & KEY_MOD1) && // i43499 CTRL disables docking now
            bRealMove )
        {
            maDockPos = pDockingArea->OutputToScreenPixel( pDockingArea->AbsoluteScreenToOutputPixel( OutputToAbsoluteScreenPixel( Point() ) ) );
            maDockRect = tools::Rectangle( maDockPos, mpDockWin->GetSizePixel() );

            // mouse pos in screen pixels
            Point aMousePos = pDockingArea->OutputToScreenPixel( aState.maPos );

            if( ! mpDockWin->IsDocking() )
                mpDockWin->StartDocking( aMousePos, maDockRect );

            bool bFloatMode = mpDockWin->Docking( aMousePos, maDockRect );

            if( ! bFloatMode )
            {
                // indicates that the window could be docked at maDockRect
                maDockRect.SetPos( mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ScreenToOutputPixel(
                     maDockRect.TopLeft() ) );
                mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Big | ShowTrackFlags::TrackWindow );
                m_aEndDockTimer.Stop();
                m_aDockTimer.Invoke();
            }
            else
            {
                mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
                m_aDockTimer.Stop();
                m_aEndDockTimer.Invoke();
            }
        }
        mbInMove = false;
    }

    void ImplDockFloatWin2::Move()
    {
        if( mbInMove )
            return;

        mbInMove = true;
        FloatingWindow::Move();
        mpDockWin->GetWindow()->Move();

        /*
         *  note: the window should only dock if KEY_MOD1 is pressed
         *  and the user releases all mouse buttons. The real problem here
         *  is that we don't get mouse events (at least not on X)
         *  if the mouse is on the decoration. So we have to start an
         *  awkward timer based process that polls the modifier/buttons
         *  to see whether they are in the right condition shortly after the
         *  last Move message.
         */
        if( ! mnLastUserEvent )
            mnLastUserEvent = Application::PostUserEvent( LINK( this, ImplDockFloatWin2, DockingHdl ), nullptr, true );
    }

    void ImplDockFloatWin2::Resize()
    {
        // forwarding of resize only required if we have no borderwindow ( GetWindow() then returns 'this' )
        if( GetWindow( GetWindowType::Border ) == this )
        {
            FloatingWindow::Resize();
            Size aSize( GetSizePixel() );
            mpDockWin->GetWindow()->ImplPosSizeWindow( 0, 0, aSize.Width(), aSize.Height(), PosSizeFlags::PosSize ); // TODO: is this needed ???
        }
    }

    void ImplDockFloatWin2::TitleButtonClick( TitleButton nButton )
    {
        FloatingWindow::TitleButtonClick( nButton );
        mpDockWin->TitleButtonClick( nButton );
    }

    void ImplDockFloatWin2::Resizing( Size& rSize )
    {
        FloatingWindow::Resizing( rSize );
        mpDockWin->Resizing( rSize );
    }

    bool ImplDockFloatWin2::Close()
    {
        return true;
    }

    DockingManager::DockingManager()
    {
    }

    DockingManager::~DockingManager()
    {
    }

    ImplDockingWindowWrapper* DockingManager::GetDockingWindowWrapper( const vcl::Window *pWindow )
    {
        for( auto& xWrapper : mvDockingWindows )
        {
            if( xWrapper->mpDockingWindow == pWindow )
                return xWrapper.get();
        }
        return nullptr;
    }

    bool DockingManager::IsDockable( const vcl::Window *pWindow )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );

        /*
        if( pWindow->HasDockingHandler() )
            return true;
        */
        return (pWrapper != nullptr);
    }

    bool DockingManager::IsFloating( const vcl::Window *pWindow )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            return pWrapper->IsFloatingMode();
        else
            return false;
    }

    bool DockingManager::IsLocked( const vcl::Window *pWindow )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        return pWrapper && pWrapper->IsLocked();
    }

    void DockingManager::Lock( const vcl::Window *pWindow )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            pWrapper->Lock();
    }

    void DockingManager::Unlock( const vcl::Window *pWindow )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            pWrapper->Unlock();
    }

    void DockingManager::SetFloatingMode( const vcl::Window *pWindow, bool bFloating )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            pWrapper->SetFloatingMode( bFloating );
    }

    void DockingManager::StartPopupMode( const vcl::Window *pWindow, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            pWrapper->StartPopupMode( rRect, nFlags );
    }

    void DockingManager::StartPopupMode( ToolBox *pParentToolBox, const vcl::Window *pWindow, FloatWinPopupFlags nFlags )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            pWrapper->StartPopupMode( pParentToolBox, nFlags );
    }

    void DockingManager::StartPopupMode( ToolBox *pParentToolBox, const vcl::Window *pWindow )
    {
        StartPopupMode( pParentToolBox, pWindow, FloatWinPopupFlags::AllowTearOff         |
                        FloatWinPopupFlags::AllMouseButtonClose  |
                        FloatWinPopupFlags::NoMouseUpClose );
    }

    bool DockingManager::IsInPopupMode( const vcl::Window *pWindow )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        return pWrapper && pWrapper->IsInPopupMode();
    }

    void DockingManager::EndPopupMode( const vcl::Window *pWin )
    {
        ImplDockingWindowWrapper *pWrapper = GetDockingWindowWrapper( pWin );
        if( pWrapper && pWrapper->GetFloatingWindow() && pWrapper->GetFloatingWindow()->IsInPopupMode() )
            pWrapper->GetFloatingWindow()->EndPopupMode();
    }

    void DockingManager::AddWindow( const vcl::Window *pWindow )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            return;
        mvDockingWindows.emplace_back( new ImplDockingWindowWrapper( pWindow ) );
    }

    void DockingManager::RemoveWindow( const vcl::Window *pWindow )
    {
        for( auto it = mvDockingWindows.begin(); it != mvDockingWindows.end(); ++it )
        {
            if( (*it)->mpDockingWindow == pWindow )
            {
                mvDockingWindows.erase( it );
                break;
            }
        }
    }

    void DockingManager::SetPosSizePixel( vcl::Window const *pWindow, long nX, long nY,
                                        long nWidth, long nHeight,
                                        PosSizeFlags nFlags )
    {
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            pWrapper->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
    }

    tools::Rectangle DockingManager::GetPosSizePixel( const vcl::Window *pWindow )
    {
        tools::Rectangle aRect;
        ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
        if( pWrapper )
            aRect = tools::Rectangle( pWrapper->GetPosPixel(), pWrapper->GetSizePixel() );

        return aRect;
    }

    class ImplPopupFloatWin : public FloatingWindow
    {
    private:
        bool mbToolBox;

    public:
        ImplPopupFloatWin( vcl::Window* pParent, bool bToolBox );
        virtual ~ImplPopupFloatWin() override;
        virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
    };

    ImplPopupFloatWin::ImplPopupFloatWin( vcl::Window* pParent, bool bToolBox ) :
        FloatingWindow( pParent, bToolBox ? WB_BORDER | WB_POPUP | WB_SYSTEMWINDOW | WB_NOSHADOW : WB_STDPOPUP ),
        mbToolBox( bToolBox )
    {
        if ( bToolBox )
        {
            // indicate window type, required for accessibility
            // which should not see this window as a toplevel window
            mpWindowImpl->mbToolbarFloatingWindow = true;
        }
    }

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

    css::uno::Reference< css::accessibility::XAccessible > ImplPopupFloatWin::CreateAccessible()
    {
        if ( !mbToolBox )
            return FloatingWindow::CreateAccessible();

        // switch off direct accessibility support for this window

        // this is to avoid appearance of this window as standalone window in the accessibility hierarchy
        // as this window is only used as a helper for subtoolbars that are not teared-off, the parent toolbar
        // has to provide accessibility support (as implemented in the toolkit)
        // so the contained toolbar should appear as child of the corresponding toolbar item of the parent toolbar
        return css::uno::Reference< css::accessibility::XAccessible >();
    }

    ImplDockingWindowWrapper::ImplDockingWindowWrapper( const vcl::Window *pWindow )
        : mpDockingWindow(const_cast<vcl::Window*>(pWindow))
        , mpFloatWin(nullptr)
        , mpOldBorderWin(nullptr)
        , mpParent(pWindow->GetParent())
        , maMaxOutSize( SHRT_MAX, SHRT_MAX )
        , mnTrackX(0)
        , mnTrackY(0)
        , mnTrackWidth(0)
        , mnTrackHeight(0)
        , mnDockLeft(0)
        , mnDockTop(0)
        , mnDockRight(0)
        , mnDockBottom(0)
        , mnFloatBits(WB_BORDER | WB_CLOSEABLE | WB_SIZEABLE | (pWindow->GetStyle() & DOCKWIN_FLOATSTYLES))
        , mbDockCanceled(false)
        , mbDocking(false)
        , mbLastFloatMode(false)
        , mbRollUp(false)
        , mbDockBtn(false)
        , mbHideBtn(false)
        // must be enabled in Window::Notify to prevent permanent docking during mouse move
        , mbStartDockingEnabled(false)
        , mbLocked(false)
    {
        DockingWindow *pDockWin = dynamic_cast< DockingWindow* > ( mpDockingWindow.get() );
        if( pDockWin )
            mnFloatBits = pDockWin->GetFloatStyle();
    }

    ImplDockingWindowWrapper::~ImplDockingWindowWrapper()
    {
        if ( IsFloatingMode() )
        {
            GetWindow()->Show( false, ShowFlags::NoFocusChange );
            SetFloatingMode(false);
        }
    }

    void ImplDockingWindowWrapper::ImplStartDocking( const Point& rPos )
    {
        if( !mbStartDockingEnabled )
            return;

        maMouseOff      = rPos;
        maMouseStart    = maMouseOff;
        mbDocking       = true;
        mbLastFloatMode = IsFloatingMode();

        // calculate FloatingBorder
        VclPtr<FloatingWindow> pWin;
        if ( mpFloatWin )
            pWin = mpFloatWin;
        else
            pWin = VclPtr<ImplDockFloatWin2>::Create( mpParent, mnFloatBits, nullptr );
        pWin->GetBorder( mnDockLeft, mnDockTop, mnDockRight, mnDockBottom );
        if ( !mpFloatWin )
            pWin.disposeAndClear();

        Point   aPos    = GetWindow()->ImplOutputToFrame( Point() );
        Size    aSize   = GetWindow()->GetOutputSizePixel();
        mnTrackX        = aPos.X();
        mnTrackY        = aPos.Y();
        mnTrackWidth    = aSize.Width();
        mnTrackHeight   = aSize.Height();

        if ( mbLastFloatMode )
        {
            maMouseOff.AdjustX(mnDockLeft );
            maMouseOff.AdjustY(mnDockTop );
            mnTrackX        -= mnDockLeft;
            mnTrackY        -= mnDockTop;
            mnTrackWidth    += mnDockLeft+mnDockRight;
            mnTrackHeight   += mnDockTop+mnDockBottom;
        }

        vcl::Window *pDockingArea = GetWindow()->GetParent();
        vcl::Window::PointerState aState = pDockingArea->GetPointerState();

        // mouse pos in screen pixels
        Point aMousePos = pDockingArea->OutputToScreenPixel( aState.maPos );
        Point aDockPos = pDockingArea->AbsoluteScreenToOutputPixel( GetWindow()->OutputToAbsoluteScreenPixel( GetWindow()->GetPosPixel() ) );
        tools::Rectangle aDockRect( aDockPos, GetWindow()->GetSizePixel() );
        StartDocking( aMousePos, aDockRect );

        GetWindow()->ImplUpdateAll();
        GetWindow()->ImplGetFrameWindow()->ImplUpdateAll();

        GetWindow()->StartTracking( StartTrackingFlags::KeyMod );
    }

    void ImplDockingWindowWrapper::Tracking( const TrackingEvent& rTEvt )
    {
        // used during docking of a currently docked window
        if ( mbDocking )
        {
            if ( rTEvt.IsTrackingEnded() )
            {
                mbDocking = false;
                GetWindow()->HideTracking();
                if ( rTEvt.IsTrackingCanceled() )
                {
                    mbDockCanceled = true;
                    EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
                    mbDockCanceled = false;
                }
                else
                    EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
            }
            // Docking only upon non-synthetic MouseEvents
            else if ( !rTEvt.GetMouseEvent().IsSynthetic() || rTEvt.GetMouseEvent().IsModifierChanged() )
            {
                Point   aMousePos = rTEvt.GetMouseEvent().GetPosPixel();
                Point   aFrameMousePos = GetWindow()->ImplOutputToFrame( aMousePos );
                Size    aFrameSize = GetWindow()->ImplGetFrameWindow()->GetOutputSizePixel();
                if ( aFrameMousePos.X() < 0 )
                    aFrameMousePos.setX( 0 );
                if ( aFrameMousePos.Y() < 0 )
                    aFrameMousePos.setY( 0 );
                if ( aFrameMousePos.X() > aFrameSize.Width()-1 )
                    aFrameMousePos.setX( aFrameSize.Width()-1 );
                if ( aFrameMousePos.Y() > aFrameSize.Height()-1 )
                    aFrameMousePos.setY( aFrameSize.Height()-1 );
                aMousePos = GetWindow()->ImplFrameToOutput( aFrameMousePos );
                aMousePos.AdjustX( -(maMouseOff.X()) );
                aMousePos.AdjustY( -(maMouseOff.Y()) );
                Point aPos = GetWindow()->ImplOutputToFrame( aMousePos );
                tools::Rectangle aTrackRect( aPos, Size( mnTrackWidth, mnTrackHeight ) );
                tools::Rectangle aCompRect = aTrackRect;
                aPos.AdjustX(maMouseOff.X() );
                aPos.AdjustY(maMouseOff.Y() );

                bool bFloatMode = Docking( aPos, aTrackRect );

                if ( mbLastFloatMode != bFloatMode )
                {
                    if ( bFloatMode )
                    {
                        aTrackRect.AdjustLeft( -(mnDockLeft) );
                        aTrackRect.AdjustTop( -(mnDockTop) );
                        aTrackRect.AdjustRight(mnDockRight );
                        aTrackRect.AdjustBottom(mnDockBottom );
                    }
                    else
                    {
                        if ( aCompRect == aTrackRect )
                        {
                            aTrackRect.AdjustLeft(mnDockLeft );
                            aTrackRect.AdjustTop(mnDockTop );
                            aTrackRect.AdjustRight( -(mnDockRight) );
                            aTrackRect.AdjustBottom( -(mnDockBottom) );
                        }
                    }
                    mbLastFloatMode = bFloatMode;
                }

                ShowTrackFlags nTrackStyle;
                if ( bFloatMode )
                    nTrackStyle = ShowTrackFlags::Object;
                else
                    nTrackStyle = ShowTrackFlags::Big;
                tools::Rectangle aShowTrackRect = aTrackRect;
                aShowTrackRect.SetPos( GetWindow()->ImplFrameToOutput( aShowTrackRect.TopLeft() ) );

                GetWindow()->ShowTracking( aShowTrackRect, nTrackStyle );

                // calculate mouse offset again, as the rectangle was changed
                maMouseOff.setX( aPos.X() - aTrackRect.Left() );
                maMouseOff.setY( aPos.Y() - aTrackRect.Top() );

                mnTrackX        = aTrackRect.Left();
                mnTrackY        = aTrackRect.Top();
                mnTrackWidth    = aTrackRect.GetWidth();
                mnTrackHeight   = aTrackRect.GetHeight();
            }
        }
    }

    void ImplDockingWindowWrapper::StartDocking( const Point& rPoint, tools::Rectangle const & rRect )
    {
        DockingData data( rPoint, rRect, IsFloatingMode() );

        GetWindow()->CallEventListeners( VclEventId::WindowStartDocking, &data );
        mbDocking = true;
    }

    bool ImplDockingWindowWrapper::Docking( const Point& rPoint, tools::Rectangle& rRect )
    {
        DockingData data( rPoint, rRect, IsFloatingMode() );

        GetWindow()->CallEventListeners( VclEventId::WindowDocking, &data );
        rRect = data.maTrackRect;
        return data.mbFloating;
    }

    void ImplDockingWindowWrapper::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
    {
        tools::Rectangle aRect( rRect );

        bool bOrigDockCanceled = mbDockCanceled;
        if (bFloatMode && !StyleSettings::GetDockingFloatsSupported())
            mbDockCanceled = true;

        if ( !IsDockingCanceled() )
        {
            bool bShow = false;
            if ( bFloatMode != IsFloatingMode() )
            {
                GetWindow()->Show( false, ShowFlags::NoFocusChange );
                SetFloatingMode( bFloatMode );
                bShow = true;
                if ( bFloatMode )
                {
                    // #i44800# always use outputsize - as in all other places
                    mpFloatWin->SetOutputSizePixel( aRect.GetSize() );
                    mpFloatWin->SetPosPixel( aRect.TopLeft() );
                }
            }
            if ( !bFloatMode )
            {
                Point aPos = aRect.TopLeft();
                aPos = GetWindow()->GetParent()->ScreenToOutputPixel( aPos );
                GetWindow()->SetPosSizePixel( aPos, aRect.GetSize() );
            }

            if ( bShow )
                GetWindow()->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
        }

        EndDockingData data( aRect, IsFloatingMode(), IsDockingCanceled() );
        GetWindow()->CallEventListeners( VclEventId::WindowEndDocking, &data );

        mbDocking = false;

        // must be enabled in Window::Notify to prevent permanent docking during mouse move
        mbStartDockingEnabled = false;

        mbDockCanceled = bOrigDockCanceled;
    }

    bool ImplDockingWindowWrapper::PrepareToggleFloatingMode()
    {
        bool bFloating = true;
        GetWindow()->CallEventListeners( VclEventId::WindowPrepareToggleFloating, &bFloating );
        return bFloating;
    }

    void ImplDockingWindowWrapper::ToggleFloatingMode()
    {
        // notify dockingwindow/toolbox
        // note: this must be done *before* notifying the
        //       listeners to have the toolbox in the proper state
        if( GetWindow()->IsDockingWindow() )
            static_cast<DockingWindow*>(GetWindow())->ToggleFloatingMode();

        // now notify listeners
        GetWindow()->CallEventListeners( VclEventId::WindowToggleFloating );

        // must be enabled in Window::Notify to prevent permanent docking during mouse move
        mbStartDockingEnabled = false;
    }

    void ImplDockingWindowWrapper::TitleButtonClick( TitleButton nType )
    {
        if( nType == TitleButton::Menu )
        {
            ToolBox *pToolBox = dynamic_cast< ToolBox* >( GetWindow() );
            if( pToolBox )
            {
                pToolBox->ExecuteCustomMenu();
            }
        }
        if( nType == TitleButton::Docking )
        {
            SetFloatingMode( !IsFloatingMode() );
        }
    }

    void ImplDockingWindowWrapper::Resizing( Size& rSize )
    {
        // TODO: add virtual Resizing() to class Window, so we can get rid of class DockingWindow
        DockingWindow *pDockingWindow = dynamic_cast< DockingWindow* >( GetWindow() );
        if( pDockingWindow )
            pDockingWindow->Resizing( rSize );
    }

    void ImplDockingWindowWrapper::ShowTitleButton( TitleButton nButton, bool bVisible )
    {
        if ( mpFloatWin )
            mpFloatWin->ShowTitleButton( nButton, bVisible );
        else
        {
            if ( nButton == TitleButton::Docking )
                mbDockBtn = bVisible;
            else // if ( nButton == TitleButton::Hide )
                mbHideBtn = bVisible;
        }
    }

    void ImplDockingWindowWrapper::ImplPreparePopupMode()
    {
        GetWindow()->Show( false, ShowFlags::NoFocusChange );

        // prepare reparenting
        vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent );
        mpOldBorderWin = GetWindow()->GetWindow( GetWindowType::Border );
        if( mpOldBorderWin.get() == GetWindow() )
            mpOldBorderWin = nullptr;  // no border window found

        // the new parent for popup mode
        VclPtrInstance<ImplPopupFloatWin> pWin( mpParent, GetWindow()->GetType() == WindowType::TOOLBOX );
        pWin->SetPopupModeEndHdl( LINK( this, ImplDockingWindowWrapper, PopupModeEnd ) );

        // At least for DockingWindow, GetText() has a side effect of setting deferred
        // properties. This must be done before setting the border window (see below),
        // so that the border width will end up in mpWindowImpl->mnBorderWidth, not in
        // the border window (See DockingWindow::setPosSizeOnContainee() and
        // DockingWindow::GetOptimalSize()).
        pWin->SetText( GetWindow()->GetText() );
        pWin->SetOutputSizePixel( GetWindow()->GetSizePixel() );

        GetWindow()->mpWindowImpl->mpBorderWindow  = nullptr;
        GetWindow()->mpWindowImpl->mnLeftBorder    = 0;
        GetWindow()->mpWindowImpl->mnTopBorder     = 0;
        GetWindow()->mpWindowImpl->mnRightBorder   = 0;
        GetWindow()->mpWindowImpl->mnBottomBorder  = 0;

        // reparent borderwindow and window
        if ( mpOldBorderWin )
            mpOldBorderWin->SetParent( pWin );
        GetWindow()->SetParent( pWin );

        // correct border window pointers
        GetWindow()->mpWindowImpl->mpBorderWindow = pWin;
        pWin->mpWindowImpl->mpClientWindow = GetWindow();
        GetWindow()->mpWindowImpl->mpRealParent = pRealParent;

        // set mpFloatWin not until all window positioning is done !!!
        // (SetPosPixel etc. check for valid mpFloatWin pointer)
        mpFloatWin = pWin;
    }

    void ImplDockingWindowWrapper::StartPopupMode( ToolBox *pParentToolBox, FloatWinPopupFlags nFlags )
    {
        // do nothing if window is floating
        if( IsFloatingMode() )
            return;

        ImplPreparePopupMode();

        // don't allow tearoff, if globally disabled
        if( !StyleSettings::GetDockingFloatsSupported() ||
            ( GetWindow()->GetType() == WindowType::TOOLBOX && ToolBox::AlwaysLocked() ) )
            nFlags &= ~FloatWinPopupFlags::AllowTearOff;

        // if the subtoolbar was opened via keyboard make sure that key events
        // will go into subtoolbar
        if( pParentToolBox->IsKeyEvent() )
            nFlags |= FloatWinPopupFlags::GrabFocus;

        mpFloatWin->StartPopupMode( pParentToolBox, nFlags );
        GetWindow()->Show();

        if( pParentToolBox->IsKeyEvent() )
        {
            // send HOME key to subtoolbar in order to select first item
            KeyEvent aEvent( 0, vcl::KeyCode( KEY_HOME ) );
            GetWindow()->KeyInput(aEvent);
        }
    }

    void ImplDockingWindowWrapper::StartPopupMode( const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
    {
        // do nothing if window is floating
        if( IsFloatingMode() )
            return;

        ImplPreparePopupMode();
        mpFloatWin->StartPopupMode( rRect, nFlags );
        GetWindow()->Show();
    }

    IMPL_LINK_NOARG(ImplDockingWindowWrapper, PopupModeEnd, FloatingWindow*, void)
    {
        GetWindow()->Show( false, ShowFlags::NoFocusChange );

        // set parameter for handler before destroying floating window
        EndPopupModeData aData( mpFloatWin->GetWindow( GetWindowType::Border )->GetPosPixel(), mpFloatWin->IsPopupModeTearOff() );

        // before deleting change parent back, so we can delete the floating window alone
        vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent );
        GetWindow()->mpWindowImpl->mpBorderWindow = nullptr;
        if ( mpOldBorderWin )
        {
            GetWindow()->SetParent( mpOldBorderWin );
            static_cast<ImplBorderWindow*>(mpOldBorderWin.get())->GetBorder(
                GetWindow()->mpWindowImpl->mnLeftBorder, GetWindow()->mpWindowImpl->mnTopBorder,
                GetWindow()->mpWindowImpl->mnRightBorder, GetWindow()->mpWindowImpl->mnBottomBorder );
            mpOldBorderWin->Resize();
        }
        GetWindow()->mpWindowImpl->mpBorderWindow = mpOldBorderWin;
        GetWindow()->SetParent( pRealParent );
        GetWindow()->mpWindowImpl->mpRealParent = pRealParent;

        mpFloatWin.disposeAndClear();

        // call handler - which will destroy the window and thus the wrapper as well !
        GetWindow()->CallEventListeners( VclEventId::WindowEndPopupMode, &aData );
    }

    bool ImplDockingWindowWrapper::IsInPopupMode() const
    {
        if( GetFloatingWindow() )
            return GetFloatingWindow()->IsInPopupMode();
        else
            return false;
    }

    void ImplDockingWindowWrapper::SetFloatingMode( bool bFloatMode )
    {
        // do nothing if window is docked and locked
        if( !IsFloatingMode() && IsLocked() )
            return;

        if ( IsFloatingMode() != bFloatMode )
        {
            if ( PrepareToggleFloatingMode() )
            {
                bool bVisible = GetWindow()->IsVisible();

                if ( bFloatMode )
                {
                    GetWindow()->Show( false, ShowFlags::NoFocusChange );

                    maDockPos = GetWindow()->GetPosPixel();

                    vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent );
                    mpOldBorderWin = GetWindow()->GetWindow( GetWindowType::Border );
                    if( mpOldBorderWin == mpDockingWindow )
                        mpOldBorderWin = nullptr;  // no border window found

                    VclPtrInstance<ImplDockFloatWin2> pWin(
                                             mpParent,
                                             mnFloatBits & ( WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE ) ?
                                              mnFloatBits | WB_SYSTEMWINDOW
                                              | WB_OWNERDRAWDECORATION
                                              : mnFloatBits,
                                             this );

                    // At least for DockingWindow, GetText() has a side effect of setting deferred
                    // properties. This must be done before setting the border window (see below),
                    // so that the border width will end up in mpWindowImpl->mnBorderWidth, not in
                    // the border window (See DockingWindow::setPosSizeOnContainee() and
                    // DockingWindow::GetOptimalSize()).
                    pWin->SetText( GetWindow()->GetText() );

                    GetWindow()->mpWindowImpl->mpBorderWindow  = nullptr;
                    GetWindow()->mpWindowImpl->mnLeftBorder    = 0;
                    GetWindow()->mpWindowImpl->mnTopBorder     = 0;
                    GetWindow()->mpWindowImpl->mnRightBorder   = 0;
                    GetWindow()->mpWindowImpl->mnBottomBorder  = 0;

                    // if the parent gets destroyed, we also have to reset the parent of the BorderWindow
                    if ( mpOldBorderWin )
                        mpOldBorderWin->SetParent( pWin );
                    GetWindow()->SetParent( pWin );
                    pWin->SetPosPixel( Point() );

                    GetWindow()->mpWindowImpl->mpBorderWindow = pWin;
                    pWin->mpWindowImpl->mpClientWindow = mpDockingWindow;
                    GetWindow()->mpWindowImpl->mpRealParent = pRealParent;

                    pWin->SetOutputSizePixel( GetWindow()->GetSizePixel() );
                    pWin->SetPosPixel( maFloatPos );
                    // pass on DockingData to FloatingWindow
                    pWin->ShowTitleButton( TitleButton::Docking, mbDockBtn );
                    pWin->ShowTitleButton( TitleButton::Hide, mbHideBtn );
                    if ( mbRollUp )
                        pWin->RollUp();
                    else
                        pWin->RollDown();
                    pWin->SetRollUpOutputSizePixel( maRollUpOutSize );
                    pWin->SetMinOutputSizePixel( maMinOutSize );
                    pWin->SetMaxOutputSizePixel( maMaxOutSize );

                    mpFloatWin      = pWin;

                    if ( bVisible )
                        GetWindow()->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );

                    ToggleFloatingMode();
                }
                else
                {
                    GetWindow()->Show( false, ShowFlags::NoFocusChange );

                    // store FloatingData in FloatingWindow
                    maFloatPos      = mpFloatWin->GetPosPixel();
                    mbDockBtn       = mpFloatWin->IsTitleButtonVisible( TitleButton::Docking );
                    mbHideBtn       = mpFloatWin->IsTitleButtonVisible( TitleButton::Hide );
                    mbRollUp        = mpFloatWin->IsRollUp();
                    maRollUpOutSize = mpFloatWin->GetRollUpOutputSizePixel();
                    maMinOutSize    = mpFloatWin->GetMinOutputSizePixel();
                    maMaxOutSize    = mpFloatWin->GetMaxOutputSizePixel();

                    vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent ); //mpWindowImpl->mpRealParent;
                    GetWindow()->mpWindowImpl->mpBorderWindow = nullptr;
                    if ( mpOldBorderWin )
                    {
                        GetWindow()->SetParent( mpOldBorderWin );
                        static_cast<ImplBorderWindow*>(mpOldBorderWin.get())->GetBorder(
                            GetWindow()->mpWindowImpl->mnLeftBorder, GetWindow()->mpWindowImpl->mnTopBorder,
                            GetWindow()->mpWindowImpl->mnRightBorder, GetWindow()->mpWindowImpl->mnBottomBorder );
                        mpOldBorderWin->Resize();
                    }
                    GetWindow()->mpWindowImpl->mpBorderWindow = mpOldBorderWin;
                    GetWindow()->SetParent( pRealParent );
                    GetWindow()->mpWindowImpl->mpRealParent = pRealParent;

                    mpFloatWin.disposeAndClear();
                    GetWindow()->SetPosPixel( maDockPos );

                    if ( bVisible )
                        GetWindow()->Show();

                    ToggleFloatingMode();

                }
            }
        }
    }

    void ImplDockingWindowWrapper::SetFloatStyle( WinBits nStyle )
    {
        mnFloatBits = nStyle;
    }

    void ImplDockingWindowWrapper::setPosSizePixel( long nX, long nY,
                                         long nWidth, long nHeight,
                                         PosSizeFlags nFlags )
    {
        if ( mpFloatWin )
            mpFloatWin->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
        else
            GetWindow()->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
    }

    Point ImplDockingWindowWrapper::GetPosPixel() const
    {
        if ( mpFloatWin )
            return mpFloatWin->GetPosPixel();
        else
            return mpDockingWindow->GetPosPixel();
    }

    Size ImplDockingWindowWrapper::GetSizePixel() const
    {
        if ( mpFloatWin )
            return mpFloatWin->GetSizePixel();
        else
            return mpDockingWindow->GetSizePixel();
    }

    // old inlines from DockingWindow

    void ImplDockingWindowWrapper::SetMinOutputSizePixel( const Size& rSize )
    {
        if ( mpFloatWin )
            mpFloatWin->SetMinOutputSizePixel( rSize );
        maMinOutSize = rSize;
    }

    void ImplDockingWindowWrapper::SetMaxOutputSizePixel( const Size& rSize )
    {
        if ( mpFloatWin )
            mpFloatWin->SetMaxOutputSizePixel( rSize );
        maMaxOutSize = rSize;
    }

    bool ImplDockingWindowWrapper::IsFloatingMode() const
    {
        return (mpFloatWin != nullptr);
    }

    void    ImplDockingWindowWrapper::SetDragArea( const tools::Rectangle& rRect )
    {
        maDragArea = rRect;
    }

    void ImplDockingWindowWrapper::Lock()
    {
        mbLocked = true;
        // only toolbars support locking
        ToolBox *pToolBox = dynamic_cast< ToolBox * >( GetWindow() );
        if( pToolBox )
            pToolBox->Lock( mbLocked );
    }

    void ImplDockingWindowWrapper::Unlock()
    {
        mbLocked = false;
        // only toolbars support locking
        ToolBox *pToolBox = dynamic_cast< ToolBox * >( GetWindow() );
        if( pToolBox )
            pToolBox->Lock( mbLocked );
    }

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

↑ V1023 A pointer without owner is added to the 'mvDockingWindows' container by the 'emplace_back' method. A memory leak will occur in case of an exception.