/* -*- 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 <ViewsWindow.hxx>
#include <ScrollHelper.hxx>
#include <UndoActions.hxx>
#include <ReportWindow.hxx>
#include <DesignView.hxx>
#include <svtools/colorcfg.hxx>
#include <ReportController.hxx>
#include <UITools.hxx>
#include <RptDef.hxx>
#include <strings.hrc>
#include <SectionView.hxx>
#include <ReportSection.hxx>
#include <strings.hxx>
#include <rptui_slotid.hrc>
#include <dlgedclip.hxx>
#include <RptObject.hxx>
#include <EndMarker.hxx>
#include <sal/log.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <core_resource.hxx>
#include <svx/svdundo.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <algorithm>
#include <cstdlib>
#include <numeric>
 
namespace rptui
{
#define DEFAUL_MOVE_SIZE    100
 
using namespace ::com::sun::star;
 
static bool lcl_getNewRectSize(const tools::Rectangle& _aObjRect,tools::Long& _nXMov, tools::Long& _nYMov,SdrObject const * _pObj,SdrView const * _pView, ControlModification _nControlModification)
{
    bool bMoveAllowed = _nXMov != 0 || _nYMov != 0;
    if ( bMoveAllowed )
    {
        tools::Rectangle aNewRect = _aObjRect;
        SdrObject* pOverlappedObj = nullptr;
        do
        {
            aNewRect = _aObjRect;
            switch(_nControlModification)
            {
                case ControlModification::HEIGHT_GREATEST:
                case ControlModification::WIDTH_GREATEST:
                    aNewRect.setWidth(_nXMov);
                    aNewRect.setHeight(_nYMov);
                    break;
                default:
                    aNewRect.Move(_nXMov,_nYMov);
                    break;
            }
            if (dynamic_cast<OUnoObject const *>(_pObj) != nullptr || dynamic_cast<OOle2Obj const *>(_pObj) != nullptr)
            {
                pOverlappedObj = isOver(aNewRect,*_pObj->getSdrPageFromSdrObject(),*_pView,true,_pObj);
                if ( pOverlappedObj && _pObj != pOverlappedObj )
                {
                    tools::Rectangle aOverlappingRect = pOverlappedObj->GetSnapRect();
                    sal_Int32 nXTemp = _nXMov;
                    sal_Int32 nYTemp = _nYMov;
                    switch(_nControlModification)
                    {
                        case ControlModification::LEFT:
                            nXTemp += aOverlappingRect.Right() - aNewRect.Left();
                            bMoveAllowed = _nXMov != nXTemp;
                            break;
                        case ControlModification::RIGHT:
                            nXTemp += aOverlappingRect.Left() - aNewRect.Right();
                            bMoveAllowed = _nXMov != nXTemp;
                            break;
                        case ControlModification::TOP:
                            nYTemp += aOverlappingRect.Bottom() - aNewRect.Top();
                            bMoveAllowed = _nYMov != nYTemp;
                            break;
                        case ControlModification::BOTTOM:
                            nYTemp += aOverlappingRect.Top() - aNewRect.Bottom();
                            bMoveAllowed = _nYMov != nYTemp;
                            break;
                        case ControlModification::CENTER_HORIZONTAL:
                            if ( _aObjRect.Left() < aOverlappingRect.Left() )
                                nXTemp += aOverlappingRect.Left() - aNewRect.Left() - aNewRect.getOpenWidth();
                            else
                                nXTemp += aOverlappingRect.Right() - aNewRect.Left();
                            bMoveAllowed = _nXMov != nXTemp;
                            break;
                        case ControlModification::CENTER_VERTICAL:
                            if ( _aObjRect.Top() < aOverlappingRect.Top() )
                                nYTemp += aOverlappingRect.Top() - aNewRect.Top() - aNewRect.getOpenHeight();
                            else
                                nYTemp += aOverlappingRect.Bottom() - aNewRect.Top();
                            bMoveAllowed = _nYMov != nYTemp;
                            break;
                        case ControlModification::HEIGHT_GREATEST:
                        case ControlModification::WIDTH_GREATEST:
                            {
                                tools::Rectangle aIntersectionRect = aNewRect.GetIntersection(aOverlappingRect);
                                if ( !aIntersectionRect.IsEmpty() )
                                {
                                    if ( _nControlModification == ControlModification::WIDTH_GREATEST )
                                    {
                                        if ( aNewRect.Left() < aIntersectionRect.Left() )
                                        {
                                            aNewRect.SetRight( aIntersectionRect.Left() );
                                        }
                                        else if ( aNewRect.Left() < aIntersectionRect.Right() )
                                        {
                                            aNewRect.SetLeft( aIntersectionRect.Right() );
                                        }
                                    }
                                    else if ( _nControlModification == ControlModification::HEIGHT_GREATEST )
                                    {
                                        if ( aNewRect.Top() < aIntersectionRect.Top() )
                                        {
                                            aNewRect.SetBottom( aIntersectionRect.Top() );
                                        }
                                        else if ( aNewRect.Top() < aIntersectionRect.Bottom() )
                                        {
                                            aNewRect.SetTop( aIntersectionRect.Bottom() );
                                        }
                                    }
                                    nYTemp = aNewRect.getOpenHeight();
                                    bMoveAllowed = _nYMov != nYTemp;
                                    nXTemp = aNewRect.getOpenWidth();
                                    bMoveAllowed = bMoveAllowed && _nXMov != nXTemp;
                                }
                            }
                            break;
                        default:
                            break;
                    }
 
                    _nXMov = nXTemp;
                    _nYMov = nYTemp;
                }
                else
                    pOverlappedObj = nullptr;
            }
        }
        while ( pOverlappedObj && bMoveAllowed );
    }
    return bMoveAllowed;
}
 
OViewsWindow::OViewsWindow( OReportWindow* _pReportWindow)
    : Window( _pReportWindow,WB_DIALOGCONTROL)
    , m_pParent(_pReportWindow)
    , m_bInUnmark(false)
{
    SetPaintTransparent(true);
    SetMapMode(MapMode(MapUnit::Map100thMM));
    m_aColorConfig.AddListener(this);
    ImplInitSettings();
}
 
OViewsWindow::~OViewsWindow()
{
    disposeOnce();
}
 
void OViewsWindow::dispose()
{
    m_aColorConfig.RemoveListener(this);
    for (auto& rxSection : m_aSections)
        rxSection.disposeAndClear();
    m_aSections.clear();
    m_pParent.clear();
    vcl::Window::dispose();
}
 
void OViewsWindow::impl_resizeSectionWindow(OSectionWindow& _rSectionWindow,Point& _rStartPoint,bool _bSet)
{
    const uno::Reference< report::XSection> xSection = _rSectionWindow.getReportSection().getSection();
 
    Size aSectionSize = _rSectionWindow.LogicToPixel( Size( 0,xSection->getHeight() ) );
    aSectionSize.setWidth( getView()->GetTotalWidth() );
 
    const sal_Int32 nMinHeight = _rSectionWindow.getStartMarker().getMinHeight();
    if ( _rSectionWindow.getStartMarker().isCollapsed() || nMinHeight > aSectionSize.Height() )
    {
        aSectionSize.setHeight( nMinHeight );
    }
    aSectionSize.AdjustHeight(static_cast<tools::Long>(StyleSettings::GetSplitSize() * static_cast<double>(_rSectionWindow.GetMapMode().GetScaleY())) );
 
    if ( _bSet )
        _rSectionWindow.SetPosSizePixel(_rStartPoint,aSectionSize);
 
    _rStartPoint.AdjustY(aSectionSize.Height() );
}
 
 
void OViewsWindow::resize(const OSectionWindow& _rSectionWindow)
{
    bool bSet = false;
    Point aStartPoint;
    for (VclPtr<OSectionWindow> const & pSectionWindow : m_aSections)
    {
        if ( pSectionWindow == &_rSectionWindow )
        {
            aStartPoint = pSectionWindow->GetPosPixel();
            bSet = true;
        }
 
        if ( bSet )
        {
            impl_resizeSectionWindow(*pSectionWindow,aStartPoint,bSet);
            static const InvalidateFlags nIn = InvalidateFlags::Update | InvalidateFlags::Transparent;
            pSectionWindow->getStartMarker().Invalidate( nIn ); // InvalidateFlags::NoErase |InvalidateFlags::NoChildren| InvalidateFlags::Transparent
            pSectionWindow->getEndMarker().Invalidate( nIn );
        }
    }
    m_pParent->notifySizeChanged();
}
 
void OViewsWindow::Resize()
{
    Window::Resize();
    if ( !m_aSections.empty() )
    {
        const Point aOffset(m_pParent->getThumbPos());
        Point aStartPoint(0,-aOffset.Y());
        for (VclPtr<OSectionWindow> const & pSectionWindow : m_aSections)
        {
            impl_resizeSectionWindow(*pSectionWindow,aStartPoint,true);
        }
    }
}
 
void OViewsWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    Window::Paint(rRenderContext, rRect);
 
    rRenderContext.SetBackground();
    rRenderContext.SetFillColor(Application::GetSettings().GetStyleSettings().GetDialogColor());
    rRenderContext.SetTextFillColor(Application::GetSettings().GetStyleSettings().GetDialogColor());
 
    Size aOut(GetOutputSizePixel());
    tools::Long nStartWidth = tools::Long(REPORT_STARTMARKER_WIDTH * rRenderContext.GetMapMode().GetScaleX());
 
    aOut.AdjustWidth( -nStartWidth );
    aOut = rRenderContext.PixelToLogic(aOut);
 
    tools::Rectangle aRect(rRenderContext.PixelToLogic(Point(nStartWidth,0)), aOut);
    Wallpaper aWall(m_aColorConfig.GetColorValue(::svtools::APPBACKGROUND).nColor);
    rRenderContext.DrawWallpaper(aRect, aWall);
}
 
void OViewsWindow::ImplInitSettings()
{
    EnableChildTransparentMode();
}
 
void OViewsWindow::DataChanged( const DataChangedEvent& rDCEvt )
{
    Window::DataChanged( rDCEvt );
 
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
         (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
    {
        ImplInitSettings();
        Invalidate();
    }
}
 
void OViewsWindow::addSection(const uno::Reference< report::XSection >& _xSection,const OUString& _sColorEntry,sal_uInt16 _nPosition)
{
    VclPtrInstance<OSectionWindow> pSectionWindow(this,_xSection,_sColorEntry);
    m_aSections.insert(getIteratorAtPos(_nPosition) , TSectionsMap::value_type(pSectionWindow));
    m_pParent->setMarked(&pSectionWindow->getReportSection().getSectionView(),m_aSections.size() == 1);
    Resize();
}
 
void OViewsWindow::removeSection(sal_uInt16 _nPosition)
{
    if ( _nPosition < m_aSections.size() )
    {
        TSectionsMap::iterator aPos = getIteratorAtPos(_nPosition);
        TSectionsMap::const_iterator aNew = getIteratorAtPos(_nPosition == 0 ? _nPosition+1: _nPosition - 1);
 
        m_pParent->getReportView()->UpdatePropertyBrowserDelayed((*aNew)->getReportSection().getSectionView());
 
        aPos->disposeAndClear();
        m_aSections.erase(aPos);
        Resize();
    }
}
 
void OViewsWindow::toggleGrid(bool _bVisible)
{
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [_bVisible] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getReportSection().SetGridVisible(_bVisible);
        });
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getReportSection().Window::Invalidate(InvalidateFlags::NoErase);
        });
}
 
sal_Int32 OViewsWindow::getTotalHeight() const
{
    return std::accumulate(m_aSections.begin(), m_aSections.end(), sal_Int32(0),
        [](const sal_Int32 nHeight, const VclPtr<OSectionWindow>& rxSection) { return nHeight + rxSection->GetSizePixel().Height(); });
}
 
sal_uInt16 OViewsWindow::getSectionCount() const
{
    return static_cast<sal_uInt16>(m_aSections.size());
}
 
void OViewsWindow::SetInsertObj( SdrObjKind eObj,const OUString& _sShapeType )
{
    for (const auto& rxSection : m_aSections)
        rxSection->getReportSection().getSectionView().SetCurrentObj( eObj, SdrInventor::ReportDesign );
 
    m_sShapeType = _sShapeType;
}
 
 
void OViewsWindow::SetMode( DlgEdMode eNewMode )
{
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [&eNewMode] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getReportSection().SetMode(eNewMode);
        });
}
 
bool OViewsWindow::HasSelection() const
{
    return std::any_of(m_aSections.begin(), m_aSections.end(),
        [](const VclPtr<OSectionWindow>& rxSection) { return rxSection->getReportSection().getSectionView().GetMarkedObjectList().GetMarkCount() != 0; });
}
 
void OViewsWindow::Delete()
{
    m_bInUnmark = true;
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getReportSection().Delete();
        });
    m_bInUnmark = false;
}
 
void OViewsWindow::Copy()
{
    uno::Sequence< beans::NamedValue > aAllreadyCopiedObjects;
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [&aAllreadyCopiedObjects] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getReportSection().Copy(aAllreadyCopiedObjects);
        });
 
    rtl::Reference<OReportExchange> pCopy = new OReportExchange(aAllreadyCopiedObjects);
    pCopy->CopyToClipboard(this);
}
 
void OViewsWindow::Paste()
{
    TransferableDataHelper aTransferData(TransferableDataHelper::CreateFromSystemClipboard(this));
    OReportExchange::TSectionElements aCopies = OReportExchange::extractCopies(aTransferData);
    if ( aCopies.getLength() > 1 )
        ::std::for_each(m_aSections.begin(),m_aSections.end(),
            [&aCopies] (const TSectionsMap::value_type& sectionPtr) {
                sectionPtr->getReportSection().Paste(aCopies);
            });
    else
    {
        OSectionWindow*  pMarkedSection = getMarkedSection();
        if ( pMarkedSection )
            pMarkedSection->getReportSection().Paste(aCopies,true);
    }
}
 
OSectionWindow* OViewsWindow::getSectionWindow(const uno::Reference< report::XSection>& _xSection) const
{
    OSL_ENSURE(_xSection.is(),"Section is NULL!");
 
    OSectionWindow* pSectionWindow = nullptr;
    for (VclPtr<OSectionWindow> const & p : m_aSections)
    {
        if (p->getReportSection().getSection() == _xSection)
        {
            pSectionWindow = p.get();
            break;
        }
    }
 
    return pSectionWindow;
}
 
 
OSectionWindow* OViewsWindow::getMarkedSection(NearSectionAccess nsa) const
{
    OSectionWindow* pRet = nullptr;
    TSectionsMap::const_iterator aIter = m_aSections.begin();
    TSectionsMap::const_iterator aEnd = m_aSections.end();
    sal_uInt32 nCurrentPosition = 0;
    for (; aIter != aEnd ; ++aIter)
    {
        if ( (*aIter)->getStartMarker().isMarked() )
        {
            if (nsa == CURRENT)
            {
                pRet = aIter->get();
                break;
            }
            else if ( nsa == PREVIOUS )
            {
                if (nCurrentPosition > 0)
                {
                    pRet = (--aIter)->get();
                    if (pRet == nullptr)
                    {
                        pRet = m_aSections.begin()->get();
                    }
                }
                else
                {
                    // if we are out of bounds return the first one
                    pRet = m_aSections.begin()->get();
                }
                break;
            }
            else if ( nsa == POST )
            {
                sal_uInt32 nSize = m_aSections.size();
                if ((nCurrentPosition + 1) < nSize)
                {
                    pRet = (++aIter)->get();
                    if (pRet == nullptr)
                    {
                        pRet = (--aEnd)->get();
                    }
                }
                else
                {
                    // if we are out of bounds return the last one
                    pRet = (--aEnd)->get();
                }
                break;
            }
        }
        ++nCurrentPosition;
    }
 
    return pRet;
}
 
void OViewsWindow::markSection(const sal_uInt16 _nPos)
{
    if ( _nPos < m_aSections.size() )
        m_pParent->setMarked(m_aSections[_nPos]->getReportSection().getSection(),true);
}
 
bool OViewsWindow::IsPasteAllowed() const
{
    TransferableDataHelper aTransferData( TransferableDataHelper::CreateFromSystemClipboard( const_cast< OViewsWindow* >( this ) ) );
    return aTransferData.HasFormat(OReportExchange::getDescriptorFormatId());
}
 
void OViewsWindow::SelectAll(const SdrObjKind _nObjectType)
{
    m_bInUnmark = true;
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [&_nObjectType] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getReportSection().SelectAll(_nObjectType);
        });
    m_bInUnmark = false;
}
 
void OViewsWindow::unmarkAllObjects(OSectionView const * _pSectionView)
{
    if ( m_bInUnmark )
        return;
 
    m_bInUnmark = true;
    for (const auto& rxSection : m_aSections)
    {
        if ( &rxSection->getReportSection().getSectionView() != _pSectionView )
        {
            rxSection->getReportSection().deactivateOle();
            rxSection->getReportSection().getSectionView().UnmarkAllObj();
        }
    }
    m_bInUnmark = false;
}
 
void OViewsWindow::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints)
{
    ImplInitSettings();
    Invalidate();
}
 
void OViewsWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( rMEvt.IsLeft() )
    {
        GrabFocus();
        const uno::Sequence< beans::PropertyValue> aArgs;
        getView()->getReportView()->getController().executeChecked(SID_SELECT_REPORT,aArgs);
    }
    Window::MouseButtonDown(rMEvt);
}
 
void OViewsWindow::showRuler(bool _bShow)
{
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [_bShow] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getStartMarker().showRuler(_bShow);
        });
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getStartMarker().Window::Invalidate(InvalidateFlags::NoErase);
        });
}
 
void OViewsWindow::MouseButtonUp( const MouseEvent& rMEvt )
{
    if ( !rMEvt.IsLeft() )
        return;
 
    auto aIter = std::find_if(m_aSections.begin(), m_aSections.end(),
        [](const VclPtr<OSectionWindow>& rxSection) { return rxSection->getReportSection().getSectionView().GetMarkedObjectList().GetMarkCount() != 0; });
    if (aIter != m_aSections.end())
    {
        (*aIter)->getReportSection().MouseButtonUp(rMEvt);
    }
 
    // remove special insert mode
    for (const auto& rxSection : m_aSections)
    {
        rxSection->getReportSection().getPage()->resetSpecialMode();
    }
}
 
bool OViewsWindow::handleKeyEvent(const KeyEvent& _rEvent)
{
    bool bRet = false;
    for (const auto& rxSection : m_aSections)
    {
        if ( rxSection->getStartMarker().isMarked() )
        {
            bRet = rxSection->getReportSection().handleKeyEvent(_rEvent);
        }
    }
    return bRet;
}
 
OViewsWindow::TSectionsMap::iterator OViewsWindow::getIteratorAtPos(sal_uInt16 _nPos)
{
    TSectionsMap::iterator aRet = m_aSections.end();
    if ( _nPos < m_aSections.size() )
        aRet = m_aSections.begin() + _nPos;
    return aRet;
}
 
void OViewsWindow::setMarked(OSectionView const * _pSectionView, bool _bMark)
{
    OSL_ENSURE(_pSectionView != nullptr,"SectionView is NULL!");
    if ( _pSectionView )
        setMarked(_pSectionView->getReportSection()->getSection(),_bMark);
}
 
void OViewsWindow::setMarked(const uno::Reference< report::XSection>& _xSection, bool _bMark)
{
    for (const auto& rxSection : m_aSections)
    {
        if ( rxSection->getReportSection().getSection() != _xSection )
        {
            rxSection->setMarked(false);
        }
        else if ( rxSection->getStartMarker().isMarked() != _bMark )
        {
            rxSection->setMarked(_bMark);
        }
    }
}
 
void OViewsWindow::setMarked(const uno::Sequence< uno::Reference< report::XReportComponent> >& _aShapes, bool _bMark)
{
    bool bFirst = true;
    for(const uno::Reference< report::XReportComponent>& rShape : _aShapes)
    {
        const uno::Reference< report::XSection> xSection = rShape->getSection();
        if ( xSection.is() )
        {
            if ( bFirst )
            {
                bFirst = false;
                m_pParent->setMarked(xSection,_bMark);
            }
            OSectionWindow* pSectionWindow = getSectionWindow(xSection);
            if ( pSectionWindow )
            {
                SdrObject* pObject = SdrObject::getSdrObjectFromXShape( rShape );
                OSL_ENSURE( pObject, "OViewsWindow::setMarked: no SdrObject for the shape!" );
                if ( pObject )
                    pSectionWindow->getReportSection().getSectionView().MarkObj( pObject, pSectionWindow->getReportSection().getSectionView().GetSdrPageView(), !_bMark );
            }
        }
    }
}
 
void OViewsWindow::collectRectangles(TRectangleMap& _rSortRectangles)
{
    for (const auto& rxSection : m_aSections)
    {
        OSectionView& rView = rxSection->getReportSection().getSectionView();
        if ( rView.GetMarkedObjectList().GetMarkCount() != 0 )
        {
            rView.GetMarkedObjectList().ForceSort();
            const size_t nCount = rView.GetMarkedObjectList().GetMarkCount();
            for (size_t i=0; i < nCount; ++i)
            {
                const SdrMark* pM = rView.GetMarkedObjectList().GetMark(i);
                SdrObject* pObj = pM->GetMarkedSdrObj();
                tools::Rectangle aObjRect(pObj->GetSnapRect());
                _rSortRectangles.emplace(aObjRect,TRectangleMap::mapped_type(pObj,&rView));
            }
        }
    }
}
 
void OViewsWindow::collectBoundResizeRect(const TRectangleMap& _rSortRectangles, ControlModification _nControlModification,bool _bAlignAtSection, tools::Rectangle& _rBound, tools::Rectangle& _rResize)
{
    bool bOnlyOnce = false;
    for (const auto& [aObjRect, rObjViewPair] : _rSortRectangles)
    {
        if ( _rResize.IsEmpty() )
            _rResize = aObjRect;
        switch(_nControlModification)
        {
            case ControlModification::WIDTH_SMALLEST:
                if ( _rResize.getOpenWidth() > aObjRect.getOpenWidth() )
                    _rResize = aObjRect;
                break;
            case ControlModification::HEIGHT_SMALLEST:
                if ( _rResize.getOpenHeight() > aObjRect.getOpenHeight() )
                    _rResize = aObjRect;
                break;
            case ControlModification::WIDTH_GREATEST:
                if ( _rResize.getOpenWidth() < aObjRect.getOpenWidth() )
                    _rResize = aObjRect;
                break;
            case ControlModification::HEIGHT_GREATEST:
                if ( _rResize.getOpenHeight() < aObjRect.getOpenHeight() )
                    _rResize = aObjRect;
                break;
            default: break;
        }
 
        SdrObjTransformInfoRec aInfo;
        const SdrObject* pObj = rObjViewPair.first;
        pObj->TakeObjInfo(aInfo);
        bool bHasFixed = !aInfo.bMoveAllowed || pObj->IsMoveProtect();
        if ( bHasFixed )
            _rBound.Union(aObjRect);
        else
        {
            if ( _bAlignAtSection || _rSortRectangles.size() == 1 )
            { // align single object at the page
                if ( ! bOnlyOnce )
                {
                    bOnlyOnce = true;
                    OReportSection* pReportSection = rObjViewPair.second->getReportSection();
                    const uno::Reference< report::XSection>& xSection = pReportSection->getSection();
                    try
                    {
                        uno::Reference<report::XReportDefinition> xReportDefinition = xSection->getReportDefinition();
                        _rBound.Union(tools::Rectangle(getStyleProperty<sal_Int32>(xReportDefinition,PROPERTY_LEFTMARGIN),0,
                                            getStyleProperty<awt::Size>(xReportDefinition,PROPERTY_PAPERSIZE).Width  - getStyleProperty<sal_Int32>(xReportDefinition,PROPERTY_RIGHTMARGIN),
                                            xSection->getHeight()));
                    }
                    catch(const uno::Exception &){}
                }
            }
            else
            {
                _rBound.Union(rObjViewPair.second->GetMarkedObjRect());
            }
        }
    }
}
 
void OViewsWindow::alignMarkedObjects(ControlModification _nControlModification, bool _bAlignAtSection)
{
    if ( _nControlModification == ControlModification::NONE )
        return;
 
    Point aRefPoint;
    RectangleLess::CompareMode eCompareMode = RectangleLess::POS_LEFT;
    switch (_nControlModification)
    {
        case ControlModification::TOP   : eCompareMode = RectangleLess::POS_UPPER; break;
        case ControlModification::BOTTOM: eCompareMode = RectangleLess::POS_DOWN; break;
        case ControlModification::LEFT  : eCompareMode = RectangleLess::POS_LEFT; break;
        case ControlModification::RIGHT : eCompareMode = RectangleLess::POS_RIGHT; break;
        case ControlModification::CENTER_HORIZONTAL :
        case ControlModification::CENTER_VERTICAL :
            {
                eCompareMode = (ControlModification::CENTER_VERTICAL == _nControlModification) ?  RectangleLess::POS_CENTER_VERTICAL :  RectangleLess::POS_CENTER_HORIZONTAL;
                uno::Reference<report::XSection> xSection = (*m_aSections.begin())->getReportSection().getSection();
                uno::Reference<report::XReportDefinition> xReportDefinition = xSection->getReportDefinition();
                aRefPoint = tools::Rectangle(getStyleProperty<sal_Int32>(xReportDefinition,PROPERTY_LEFTMARGIN),0,
                                        getStyleProperty<awt::Size>(xReportDefinition,PROPERTY_PAPERSIZE).Width  - getStyleProperty<sal_Int32>(xReportDefinition,PROPERTY_RIGHTMARGIN),
                                        xSection->getHeight()).Center();
            }
            break;
        default: break;
    }
    RectangleLess aCompare(eCompareMode,aRefPoint);
    TRectangleMap aSortRectangles(aCompare);
    collectRectangles(aSortRectangles);
 
    tools::Rectangle aBound;
    tools::Rectangle aResize;
    collectBoundResizeRect(aSortRectangles,_nControlModification,_bAlignAtSection,aBound,aResize);
 
    bool bMove = true;
 
    auto aGetFun = ::std::mem_fn(&tools::Rectangle::Bottom);
    auto aSetFun = ::std::mem_fn(&tools::Rectangle::SetBottom);
    auto aRefFun = ::std::mem_fn(&tools::Rectangle::Top);
    TRectangleMap::const_iterator aRectIter = aSortRectangles.begin();
    TRectangleMap::const_iterator aRectEnd = aSortRectangles.end();
    for (;aRectIter != aRectEnd ; ++aRectIter)
    {
        tools::Rectangle aObjRect = aRectIter->first;
        SdrObject* pObj = aRectIter->second.first;
        SdrView* pView = aRectIter->second.second;
        Point aCenter(aBound.Center());
        SdrObjTransformInfoRec aInfo;
        pObj->TakeObjInfo(aInfo);
        if (aInfo.bMoveAllowed && !pObj->IsMoveProtect())
        {
            tools::Long nXMov = 0;
            tools::Long nYMov = 0;
            tools::Long* pValue = &nXMov;
            switch(_nControlModification)
            {
                case ControlModification::TOP   :
                    aGetFun  = ::std::mem_fn(&tools::Rectangle::Top);
                    aSetFun  = ::std::mem_fn(&tools::Rectangle::SetTop);
                    aRefFun  = ::std::mem_fn(&tools::Rectangle::Bottom);
                    pValue = &nYMov;
                    break;
                case ControlModification::BOTTOM:
                    // defaults are already set
                    pValue = &nYMov;
                    break;
                case ControlModification::CENTER_VERTICAL:
                    nYMov = aCenter.Y() - aObjRect.Center().Y();
                    pValue = &nYMov;
                    bMove = false;
                    break;
                case ControlModification::RIGHT :
                    aGetFun  = ::std::mem_fn(&tools::Rectangle::Right);
                    aSetFun  = ::std::mem_fn(&tools::Rectangle::SetRight);
                    aRefFun  = ::std::mem_fn(&tools::Rectangle::Left);
                    break;
                case ControlModification::CENTER_HORIZONTAL:
                    nXMov = aCenter.X() - aObjRect.Center().X();
                    bMove = false;
                    break;
                case ControlModification::LEFT  :
                    aGetFun  = ::std::mem_fn(&tools::Rectangle::Left);
                    aSetFun  = ::std::mem_fn(&tools::Rectangle::SetLeft);
                    aRefFun  = ::std::mem_fn(&tools::Rectangle::Right);
                    break;
                default:
                    bMove = false;
                    break;
            }
            if ( bMove )
            {
                tools::Rectangle aTest = aObjRect;
                aSetFun(&aTest, aGetFun(&aBound));
                TRectangleMap::const_iterator aInterSectRectIter = aSortRectangles.begin();
                for (; aInterSectRectIter != aRectIter; ++aInterSectRectIter)
                {
                    if ( pView == aInterSectRectIter->second.second && (dynamic_cast<OUnoObject*>(aInterSectRectIter->second.first) || dynamic_cast<OOle2Obj*>(aInterSectRectIter->second.first)))
                    {
                        SdrObject* pPreviousObj = aInterSectRectIter->second.first;
                        tools::Rectangle aIntersectRect = aTest.GetIntersection( pPreviousObj->GetSnapRect());
                        if ( !aIntersectRect.IsEmpty() && (aIntersectRect.Left() != aIntersectRect.Right() && aIntersectRect.Top() != aIntersectRect.Bottom() ) )
                        {
                            *pValue = aRefFun(&aIntersectRect) - aGetFun(&aObjRect);
                            break;
                        }
                    }
                }
                if ( aInterSectRectIter == aRectIter )
                    *pValue = aGetFun(&aBound) - aGetFun(&aObjRect);
            }
 
            if ( lcl_getNewRectSize(aObjRect,nXMov,nYMov,pObj,pView,_nControlModification) )
            {
                const Size aSize(nXMov,nYMov);
                pView->AddUndo(pView->GetModel().GetSdrUndoFactory().CreateUndoMoveObject(*pObj,aSize));
                pObj->Move(aSize);
                aObjRect =   pObj->GetSnapRect();
            }
 
            // resizing control
            if ( !aResize.IsEmpty() && aObjRect != aResize )
            {
                nXMov = aResize.getOpenWidth();
                nYMov = aResize.getOpenHeight();
                switch(_nControlModification)
                {
                    case ControlModification::WIDTH_GREATEST:
                    case ControlModification::HEIGHT_GREATEST:
                        if ( _nControlModification == ControlModification::HEIGHT_GREATEST )
                            nXMov = aObjRect.getOpenWidth();
                        else if ( _nControlModification == ControlModification::WIDTH_GREATEST )
                            nYMov = aObjRect.getOpenHeight();
                        lcl_getNewRectSize(aObjRect,nXMov,nYMov,pObj,pView,_nControlModification);
                        [[fallthrough]];
                    case ControlModification::WIDTH_SMALLEST:
                    case ControlModification::HEIGHT_SMALLEST:
                        pView->AddUndo( pView->GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
                        {
                            OObjectBase* pObjBase = dynamic_cast<OObjectBase*>(pObj);
                            OSL_ENSURE(pObjBase,"Where comes this object from?");
                            if ( pObjBase )
                            {
                                if ( _nControlModification == ControlModification::WIDTH_SMALLEST || _nControlModification == ControlModification::WIDTH_GREATEST )
                                    pObjBase->getReportComponent()->setSize(awt::Size(nXMov,aObjRect.getOpenHeight()));
                                else if ( _nControlModification == ControlModification::HEIGHT_GREATEST || _nControlModification == ControlModification::HEIGHT_SMALLEST )
                                    pObjBase->getReportComponent()->setSize(awt::Size(aObjRect.getOpenWidth(),nYMov));
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        pView->AdjustMarkHdl();
    }
}
 
void OViewsWindow::createDefault()
{
    OSectionWindow* pMarkedSection = getMarkedSection();
    if ( pMarkedSection )
        pMarkedSection->getReportSection().createDefault(m_sShapeType);
}
 
void OViewsWindow::setGridSnap(bool bOn)
{
    for (const auto& rxSection : m_aSections)
    {
        rxSection->getReportSection().getSectionView().SetGridSnap(bOn);
        rxSection->getReportSection().Invalidate();
    }
}
 
void OViewsWindow::setDragStripes(bool bOn)
{
    for (const auto& rxSection : m_aSections)
        rxSection->getReportSection().getSectionView().SetDragStripes(bOn);
}
 
sal_uInt16 OViewsWindow::getPosition(const OSectionWindow* _pSectionWindow) const
{
    auto aIter = std::find_if(m_aSections.begin(), m_aSections.end(),
        [&_pSectionWindow](const VclPtr<OSectionWindow>& rxSection) { return _pSectionWindow == rxSection.get(); });
    return static_cast<sal_uInt16>(std::distance(m_aSections.begin(), aIter));
}
 
OSectionWindow* OViewsWindow::getSectionWindow(const sal_uInt16 _nPos) const
{
    OSectionWindow* aReturn = nullptr;
 
    if ( _nPos < m_aSections.size() )
        aReturn = m_aSections[_nPos].get();
 
    return aReturn;
}
 
namespace
{
    enum SectionViewAction
    {
        eEndDragObj,
        eEndAction,
        eForceToAnotherPage,
        eBreakAction
    };
    class ApplySectionViewAction
    {
    private:
        SectionViewAction   m_eAction;
 
    public:
        explicit ApplySectionViewAction()
            : m_eAction(eEndDragObj)
        {
        }
 
        explicit ApplySectionViewAction(SectionViewAction _eAction)
            : m_eAction(_eAction)
        {
        }
 
        void operator() ( const OViewsWindow::TSectionsMap::value_type& _rhs )
        {
            OSectionView& rView( _rhs->getReportSection().getSectionView() );
            switch ( m_eAction )
            {
            case eEndDragObj:
                rView.EndDragObj();
                break;
            case eEndAction:
                if ( rView.IsAction() )
                    rView.EndAction (      );
                break;
            case eForceToAnotherPage:
                rView.ForceMarkedToAnotherPage();
                break;
            case eBreakAction:
                if ( rView.IsAction() )
                    rView.BrkAction (      );
                break;
                // default:
 
            }
        }
    };
}
 
void OViewsWindow::BrkAction()
{
    EndDragObj_removeInvisibleObjects();
    ::std::for_each( m_aSections.begin(), m_aSections.end(), ApplySectionViewAction(eBreakAction) );
}
 
void OViewsWindow::BegDragObj_createInvisibleObjectAtPosition(const tools::Rectangle& _aRect, const OSectionView& _rSection)
{
    Point aNewPos(0,0);
 
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        rReportSection.getPage()->setSpecialMode();
        OSectionView& rView = rReportSection.getSectionView();
 
        if ( &rView != &_rSection )
        {
            rtl::Reference<SdrObject> pNewObj = new SdrUnoObj(
                rView.getSdrModelFromSdrView(),
                u"com.sun.star.form.component.FixedText"_ustr);
 
            pNewObj->SetLogicRect(_aRect);
            pNewObj->Move(Size(0, aNewPos.Y()));
            bool bChanged = rView.GetModel().IsChanged();
            rReportSection.getPage()->InsertObject(pNewObj.get());
            rView.GetModel().SetChanged(bChanged);
            m_aBegDragTempList.push_back(pNewObj.get());
 
            rView.MarkObj( pNewObj.get(), rView.GetSdrPageView() );
        }
        const tools::Long nSectionHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
        aNewPos.AdjustY( -nSectionHeight );
    }
}
 
void OViewsWindow::BegDragObj(const Point& _aPnt, SdrHdl* _pHdl,const OSectionView* _pSection)
{
    SAL_INFO(
        "reportdesign", "Clickpoint X:" << _aPnt.X() << " Y:" << _aPnt.Y());
 
    m_aBegDragTempList.clear();
 
    // Calculate the absolute clickpoint in the views
    Point aAbsolutePnt = _aPnt;
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        OSectionView* pView = &rReportSection.getSectionView();
        if (pView == _pSection)
            break;
        const tools::Long nSectionHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
        aAbsolutePnt.AdjustY(nSectionHeight );
    }
    m_aDragDelta = Point(SAL_MAX_INT32, SAL_MAX_INT32);
    SAL_INFO(
        "reportdesign",
        "Absolute X:" << aAbsolutePnt.X() << " Y:" << aAbsolutePnt.Y());
 
    // Create drag lines over all viewable Views
    // Therefore we need to identify the marked objects
    // and create temporary objects on all other views at the same position
    // relative to its occurrence.
 
    int nViewCount = 0;
    Point aNewObjPos(0,0);
    Point aLeftTop(SAL_MAX_INT32, SAL_MAX_INT32);
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
 
        OSectionView& rView = rReportSection.getSectionView();
 
        if ( rView.GetMarkedObjectList().GetMarkCount() != 0 )
        {
            const size_t nCount = rView.GetMarkedObjectList().GetMarkCount();
            for (size_t i=0; i < nCount; ++i)
            {
                const SdrMark* pM = rView.GetMarkedObjectList().GetMark(i);
                SdrObject* pObj = pM->GetMarkedSdrObj();
                if (::std::find(m_aBegDragTempList.begin(),m_aBegDragTempList.end(),pObj) == m_aBegDragTempList.end())
                {
                    tools::Rectangle aRect( pObj->GetCurrentBoundRect() );
                    aRect.Move(0, aNewObjPos.Y());
 
                    aLeftTop.setX( ::std::min( aRect.Left(), aLeftTop.X() ) );
                    aLeftTop.setY( ::std::min( aRect.Top(), aLeftTop.Y() ) );
 
                    SAL_INFO(
                        "reportdesign",
                        "createInvisible X:" << aRect.Left() << " Y:"
                            << aRect.Top() << " on View #" << nViewCount);
 
                    BegDragObj_createInvisibleObjectAtPosition(aRect, rView);
                    // BegDragObj_createInvisibleObjectAtPosition will call
                    //   SdrMarkView::MarkObj
                    //   -> SdrMarkView::AdjustMarkHdl
                    //   -> SdrDragView::SetMarkHandles
                    //   -> SdrMarkView::SetMarkHandles
                    // which will recreate the SdrHdl objects.
                    _pHdl = _pSection->PickHandle(_aPnt);
                }
            }
        }
        ++nViewCount;
        tools::Rectangle aClipRect = rView.GetWorkArea();
        aClipRect.SetTop( -aNewObjPos.Y() );
        rView.SetWorkArea( aClipRect );
 
        const tools::Long nSectionHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
        aNewObjPos.AdjustY(nSectionHeight );
    }
 
    const sal_Int32 nDeltaX = std::abs(aLeftTop.X() - aAbsolutePnt.X());
    const sal_Int32 nDeltaY = std::abs(aLeftTop.Y() - aAbsolutePnt.Y());
    m_aDragDelta.setX( nDeltaX );
    m_aDragDelta.setY( nDeltaY );
 
    Point aNewPos = aAbsolutePnt;
 
    const short nDrgLog = static_cast<short>(PixelToLogic(Size(3,0)).Width());
    nViewCount = 0;
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
 
        SdrHdl* pHdl = _pHdl;
        if ( pHdl )
        {
            if ( &rReportSection.getSectionView() != _pSection )
            {
                const SdrHdlList& rHdlList = rReportSection.getSectionView().GetHdlList();
                pHdl = rHdlList.GetHdl(_pHdl->GetKind());
            }
        }
        SAL_INFO(
            "reportdesign",
            "X:" << aNewPos.X() << " Y:" << aNewPos.Y() << " on View#"
                << nViewCount++);
        rReportSection.getSectionView().BegDragObj(aNewPos, nullptr, pHdl, nDrgLog);
 
        const tools::Long nSectionHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
        aNewPos.AdjustY( -nSectionHeight );
    }
}
 
 
void OViewsWindow::ForceMarkedToAnotherPage()
{
    ::std::for_each( m_aSections.begin(), m_aSections.end(), ApplySectionViewAction(eForceToAnotherPage ) );
}
 
void OViewsWindow::BegMarkObj(const Point& _aPnt,const OSectionView* _pSection)
{
    bool bAdd = true;
    Point aNewPos = _aPnt;
 
    tools::Long nLastSectionHeight = 0;
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        if ( &rReportSection.getSectionView() == _pSection )
        {
            bAdd = false;
            aNewPos = _aPnt; // 2,2
        }
        else if ( bAdd )
        {
            const tools::Long nSectionHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
            aNewPos.AdjustY(nSectionHeight );
        }
        else
        {
            aNewPos.AdjustY( -nLastSectionHeight );
        }
        rReportSection.getSectionView().BegMarkObj ( aNewPos );
        nLastSectionHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
    }
}
 
OSectionView* OViewsWindow::getSectionRelativeToPosition(const OSectionView* _pSection,Point& _rPnt)
{
    OSectionView* pSection = nullptr;
    TSectionsMap::const_iterator aIter = m_aSections.begin();
    const TSectionsMap::const_iterator aEnd = m_aSections.end();
    aIter = std::find_if(aIter, aEnd, [&_pSection](const VclPtr<OSectionWindow>& rxSection) {
        return &rxSection->getReportSection().getSectionView() == _pSection; });
    sal_Int32 nCount = static_cast<sal_Int32>(std::distance(m_aSections.cbegin(), aIter));
    OSL_ENSURE(aIter != aEnd,"This can never happen!");
    if ( _rPnt.Y() < 0 )
    {
        if ( nCount )
            --aIter;
        for (; nCount && (_rPnt.Y() < 0); --nCount)
        {
            OReportSection& rReportSection = (*aIter)->getReportSection();
            const sal_Int32 nHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
            _rPnt.AdjustY(nHeight );
            if ( (nCount -1) > 0 && (_rPnt.Y() < 0) )
                --aIter;
        }
        if ( nCount == 0 )
            pSection = &(*m_aSections.begin())->getReportSection().getSectionView();
        else
            pSection = &(*aIter)->getReportSection().getSectionView();
    }
    else
    {
        for (; aIter != aEnd; ++aIter)
        {
            OReportSection& rReportSection = (*aIter)->getReportSection();
            const tools::Long nHeight = rReportSection.PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
            if ( (_rPnt.Y() - nHeight) < 0  )
                break;
            _rPnt.AdjustY( -nHeight );
        }
        if ( aIter != aEnd )
            pSection = &(*aIter)->getReportSection().getSectionView();
        else
            pSection = &(*(aEnd-1))->getReportSection().getSectionView();
    }
 
    return pSection;
}
 
void OViewsWindow::EndDragObj_removeInvisibleObjects()
{
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        rReportSection.getPage()->resetSpecialMode();
    }
}
 
void OViewsWindow::EndDragObj(bool _bControlKeyPressed, const OSectionView* _pSection, const Point& _aPnt)
{
    const OUString sUndoAction = RptResId(RID_STR_UNDO_CHANGEPOSITION);
    const UndoContext aUndoContext( getView()->getReportView()->getController().getUndoManager(), sUndoAction );
 
    Point aNewPos = _aPnt;
    OSectionView* pInSection = getSectionRelativeToPosition(_pSection, aNewPos);
    if (!_bControlKeyPressed &&
        _pSection && !_pSection->IsDragResize() && /* Not in resize mode */
        _pSection != pInSection)
    {
        EndDragObj_removeInvisibleObjects();
 
        // we need to manipulate the current clickpoint, we subtract the old delta from BeginDrag
        aNewPos -= m_aDragDelta;
 
        uno::Sequence< beans::NamedValue > aAllreadyCopiedObjects;
        for (const auto& rxSection : m_aSections)
        {
            OReportSection& rReportSection = rxSection->getReportSection();
            if ( pInSection != &rReportSection.getSectionView() )
            {
                rReportSection.getSectionView().BrkAction();
                rReportSection.Copy(aAllreadyCopiedObjects,true);
            }
            else
                pInSection->EndDragObj();
        }
 
        if ( aAllreadyCopiedObjects.hasElements() )
        {
            try
            {
                uno::Reference<report::XReportDefinition> xReportDefinition = getView()->getReportView()->getController().getReportDefinition();
                const sal_Int32 nLeftMargin  = getStyleProperty<sal_Int32>(xReportDefinition,PROPERTY_LEFTMARGIN);
                const sal_Int32 nRightMargin = getStyleProperty<sal_Int32>(xReportDefinition,PROPERTY_RIGHTMARGIN);
                const sal_Int32 nPaperWidth  = getStyleProperty<awt::Size>(xReportDefinition,PROPERTY_PAPERSIZE).Width;
 
                if ( aNewPos.X() < nLeftMargin )
                    aNewPos.setX( nLeftMargin );
                if ( aNewPos.Y() < 0 )
                    aNewPos.setY( 0 );
 
                Point aPrevious;
                for (beans::NamedValue const& namedVal : aAllreadyCopiedObjects)
                {
                    uno::Sequence< uno::Reference<report::XReportComponent> > aClones;
                    namedVal.Value >>= aClones;
                    uno::Reference<report::XReportComponent>* pColIter = aClones.getArray();
                    const uno::Reference<report::XReportComponent>* pColEnd = pColIter + aClones.getLength();
 
                    // move the cloned Components to new positions
                    for (; pColIter != pColEnd; ++pColIter)
                    {
                        uno::Reference< report::XReportComponent> xRC(*pColIter);
                        aPrevious = VCLUnoHelper::ConvertToVCLPoint(xRC->getPosition());
                        awt::Size aSize = xRC->getSize();
 
                        if ( aNewPos.X() < nLeftMargin )
                        {
                            aNewPos.setX( nLeftMargin );
                        }
                        else if ( (aNewPos.X() + aSize.Width) > (nPaperWidth - nRightMargin) )
                        {
                            aNewPos.setX( nPaperWidth - nRightMargin - aSize.Width );
                        }
                        if ( aNewPos.Y() < 0 )
                        {
                            aNewPos.setY( 0 );
                        }
                        if ( aNewPos.X() < 0 )
                        {
                            aSize.Width += aNewPos.X();
                            aNewPos.setX( 0 );
                            xRC->setSize(aSize);
                        }
                        xRC->setPosition(VCLUnoHelper::ConvertToAWTPoint(aNewPos));
                        if ( (pColIter+1) != pColEnd )
                        {
                            // bring aNewPos to the position of the next object
                            uno::Reference< report::XReportComponent> xRCNext = *(pColIter + 1);
                            Point aNextPosition
                                = VCLUnoHelper::ConvertToVCLPoint(xRCNext->getPosition());
                            aNewPos += aNextPosition - aPrevious;
                        }
                    }
                }
            }
            catch(uno::Exception&)
            {
            }
            pInSection->getReportSection()->Paste(aAllreadyCopiedObjects,true);
        }
    }
    else
    {
        ::std::for_each( m_aSections.begin(), m_aSections.end(), ApplySectionViewAction() );
        EndDragObj_removeInvisibleObjects();
    }
    m_aDragDelta = Point(SAL_MAX_INT32, SAL_MAX_INT32);
}
 
void OViewsWindow::EndAction()
{
    ::std::for_each( m_aSections.begin(), m_aSections.end(), ApplySectionViewAction(eEndAction) );
}
 
void OViewsWindow::MovAction(const Point& _aPnt,const OSectionView* _pSection, bool _bControlKeySet)
{
    Point aRealMousePos = _aPnt;
    Point aCurrentSectionPos;
    SAL_INFO(
        "reportdesign",
        "X:" << aRealMousePos.X() << " Y:" << aRealMousePos.Y());
 
    Point aHdlPos;
    SdrHdl* pHdl = _pSection->GetDragHdl();
    if ( pHdl )
    {
        aHdlPos = pHdl->GetPos();
    }
 
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        if ( &rReportSection.getSectionView() == _pSection )
            break;
        const tools::Long nSectionHeight = rxSection->PixelToLogic(rReportSection.GetOutputSizePixel()).Height();
        aCurrentSectionPos.AdjustY(nSectionHeight );
    }
    aRealMousePos += aCurrentSectionPos;
 
    // If control key is pressed the work area is limited to the section with the current selection.
    Point aPosForWorkArea(0,0);
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        OSectionView& rView = rReportSection.getSectionView();
        const tools::Long nSectionHeight = rxSection->PixelToLogic(rxSection->GetOutputSizePixel()).Height();
 
        if (_bControlKeySet)
        {
            tools::Rectangle aClipRect = rView.GetWorkArea();
            aClipRect.SetTop( aCurrentSectionPos.Y() - aPosForWorkArea.Y() );
            aClipRect.SetBottom( aClipRect.Top() + nSectionHeight );
            rView.SetWorkArea( aClipRect );
        }
        else
        {
            tools::Rectangle aClipRect = rView.GetWorkArea();
            aClipRect.SetTop( -aPosForWorkArea.Y() );
            rView.SetWorkArea( aClipRect );
        }
        aPosForWorkArea.AdjustY(nSectionHeight );
    }
 
 
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        SdrHdl* pCurrentHdl = rReportSection.getSectionView().GetDragHdl();
        if ( pCurrentHdl && aRealMousePos.Y() > 0 )
            aRealMousePos = _aPnt + pCurrentHdl->GetPos() - aHdlPos;
        rReportSection.getSectionView().MovAction ( aRealMousePos );
        const tools::Long nSectionHeight = rxSection->PixelToLogic(rxSection->GetOutputSizePixel()).Height();
        aRealMousePos.AdjustY( -nSectionHeight );
    }
}
 
bool OViewsWindow::IsAction() const
{
    return std::any_of(m_aSections.begin(), m_aSections.end(),
        [](const VclPtr<OSectionWindow>& rxSection) { return rxSection->getReportSection().getSectionView().IsAction(); });
}
 
bool OViewsWindow::IsDragObj() const
{
    return std::any_of(m_aSections.begin(), m_aSections.end(),
        [](const VclPtr<OSectionWindow>& rxSection) { return rxSection->getReportSection().getSectionView().IsAction(); });
}
 
sal_uInt32 OViewsWindow::getMarkedObjectCount() const
{
    return std::accumulate(m_aSections.begin(), m_aSections.end(), sal_uInt32(0),
        [](const sal_uInt32 nCount, const VclPtr<OSectionWindow>& rxSection) {
            return nCount + static_cast<sal_uInt32>(rxSection->getReportSection().getSectionView().GetMarkedObjectList().GetMarkCount()); });
}
 
void OViewsWindow::handleKey(const vcl::KeyCode& _rCode)
{
    const sal_uInt16 nCode = _rCode.GetCode();
    if ( _rCode.IsMod1() )
    {
        // scroll page
        OScrollWindowHelper* pScrollWindow = getView()->getScrollWindow();
        ScrollAdaptor& rScrollBar = ( nCode == KEY_LEFT || nCode == KEY_RIGHT ) ? pScrollWindow->GetHScroll() : pScrollWindow->GetVScroll();
        if ( rScrollBar.IsVisible() )
        {
            auto nCurrentPos = rScrollBar.GetThumbPos();
            auto nLineSize = rScrollBar.GetLineSize();
            rScrollBar.DoScroll(( nCode == KEY_RIGHT || nCode == KEY_UP ) ? (nCurrentPos - nLineSize) : (nCurrentPos + nLineSize));
        }
        return;
    }
 
    for (const auto& rxSection : m_aSections)
    {
        OReportSection& rReportSection = rxSection->getReportSection();
        tools::Long nX = 0;
        tools::Long nY = 0;
 
        if ( nCode == KEY_UP )
            nY = -1;
        else if ( nCode == KEY_DOWN )
            nY =  1;
        else if ( nCode == KEY_LEFT )
            nX = -1;
        else if ( nCode == KEY_RIGHT )
            nX =  1;
 
        if ( rReportSection.getSectionView().GetMarkedObjectList().GetMarkCount() != 0 )
        {
            if ( _rCode.IsMod2() )
            {
                // move in 1 pixel distance
                const Size aPixelSize = rReportSection.PixelToLogic( Size( 1, 1 ) );
                nX *= aPixelSize.Width();
                nY *= aPixelSize.Height();
            }
            else
            {
                // move in 1 mm distance
                nX *= DEFAUL_MOVE_SIZE;
                nY *= DEFAUL_MOVE_SIZE;
            }
 
            OSectionView& rView = rReportSection.getSectionView();
            const SdrHdlList& rHdlList = rView.GetHdlList();
            SdrHdl* pHdl = rHdlList.GetFocusHdl();
 
            if ( pHdl == nullptr )
            {
                // no handle selected
                if ( rView.IsMoveAllowed() )
                {
                    // restrict movement to work area
                    tools::Rectangle rWorkArea = rView.GetWorkArea();
                    rWorkArea.AdjustRight( 1 );
 
                    if ( !rWorkArea.IsEmpty() )
                    {
                        if ( rWorkArea.Top() < 0 )
                            rWorkArea.SetTop( 0 );
                        tools::Rectangle aMarkRect( rView.GetMarkedObjRect() );
                        aMarkRect.Move( nX, nY );
 
                        if ( !rWorkArea.Contains( aMarkRect ) )
                        {
                            if ( aMarkRect.Left() < rWorkArea.Left() )
                                nX += rWorkArea.Left() - aMarkRect.Left();
 
                            if ( aMarkRect.Right() > rWorkArea.Right() )
                                nX -= aMarkRect.Right() - rWorkArea.Right();
 
                            if ( aMarkRect.Top() < rWorkArea.Top() )
                                nY += rWorkArea.Top() - aMarkRect.Top();
 
                            if ( aMarkRect.Bottom() > rWorkArea.Bottom() )
                                nY -= aMarkRect.Bottom() - rWorkArea.Bottom();
                        }
                        bool bCheck = false;
                        const SdrMarkList& rMarkList = rView.GetMarkedObjectList();
                        for (size_t i = 0; !bCheck && i < rMarkList.GetMarkCount(); ++i )
                        {
                            SdrMark* pMark = rMarkList.GetMark(i);
                            bCheck = dynamic_cast<OUnoObject*>(pMark->GetMarkedSdrObj()) != nullptr|| dynamic_cast<OOle2Obj*>(pMark->GetMarkedSdrObj());
                        }
 
 
                        if ( bCheck )
                        {
                            SdrObject* pOverlapped = isOver(aMarkRect,*rReportSection.getPage(),rView);
                            if ( pOverlapped )
                            {
                                do
                                {
                                    tools::Rectangle aOver = pOverlapped->GetLastBoundRect();
                                    Point aPos;
                                    if ( nCode == KEY_UP )
                                    {
                                        aPos.setX( aMarkRect.Left() );
                                        aPos.setY( aOver.Top() - aMarkRect.getOpenHeight() );
                                        nY += (aPos.Y() - aMarkRect.Top());
                                    }
                                    else if ( nCode == KEY_DOWN )
                                    {
                                        aPos.setX( aMarkRect.Left() );
                                        aPos.setY( aOver.Bottom() );
                                        nY += (aPos.Y() - aMarkRect.Top());
                                    }
                                    else if ( nCode == KEY_LEFT )
                                    {
                                        aPos.setX( aOver.Left() - aMarkRect.getOpenWidth() );
                                        aPos.setY( aMarkRect.Top() );
                                        nX += (aPos.X() - aMarkRect.Left());
                                    }
                                    else if ( nCode == KEY_RIGHT )
                                    {
                                        aPos.setX( aOver.Right() );
                                        aPos.setY( aMarkRect.Top() );
                                        nX += (aPos.X() - aMarkRect.Left());
                                    }
 
                                    aMarkRect.SetPos(aPos);
                                    if ( !rWorkArea.Contains( aMarkRect ) )
                                    {
                                        break;
                                    }
                                    pOverlapped = isOver(aMarkRect,*rReportSection.getPage(),rView);
                                }
                                while(pOverlapped != nullptr);
                                if (pOverlapped != nullptr)
                                    break;
                            }
                        }
                    }
 
                    if ( nX != 0 || nY != 0 )
                    {
                        rView.MoveAllMarked( Size( nX, nY ) );
                        rView.MakeVisible( rView.GetAllMarkedRect(), rReportSection);
                    }
                }
            }
            else // pHdl != nullptr
            {
                // move the handle
                if (nX || nY)
                {
                    const Point aStartPoint( pHdl->GetPos() );
                    const Point aEndPoint( pHdl->GetPos() + Point( nX, nY ) );
                    const SdrDragStat& rDragStat = rView.GetDragStat();
 
                    // start dragging
                    rView.BegDragObj( aStartPoint, nullptr, pHdl, 0 );
 
                    if ( rView.IsDragObj() )
                    {
                        const bool bWasNoSnap = rDragStat.IsNoSnap();
                        const bool bWasSnapEnabled = rView.IsSnapEnabled();
 
                        // switch snapping off
                        if ( !bWasNoSnap )
                            const_cast<SdrDragStat&>(rDragStat).SetNoSnap();
                        if ( bWasSnapEnabled )
                            rView.SetSnapEnabled( false );
 
                        tools::Rectangle aNewRect;
                        bool bCheck = false;
                        const SdrMarkList& rMarkList = rView.GetMarkedObjectList();
                        for (size_t i = 0; !bCheck && i < rMarkList.GetMarkCount(); ++i )
                        {
                            SdrMark* pMark = rMarkList.GetMark(i);
                            bCheck = dynamic_cast<OUnoObject*>(pMark->GetMarkedSdrObj()) != nullptr || dynamic_cast<OOle2Obj*>(pMark->GetMarkedSdrObj()) != nullptr;
                            if ( bCheck )
                                aNewRect.Union(pMark->GetMarkedSdrObj()->GetLastBoundRect());
                        }
 
                        switch(pHdl->GetKind())
                        {
                            case SdrHdlKind::Left:
                            case SdrHdlKind::UpperLeft:
                            case SdrHdlKind::LowerLeft:
                            case SdrHdlKind::Upper:
                                aNewRect.AdjustLeft(nX );
                                aNewRect.AdjustTop(nY );
                                break;
                            case SdrHdlKind::UpperRight:
                            case SdrHdlKind::Right:
                            case SdrHdlKind::LowerRight:
                            case SdrHdlKind::Lower:
                                aNewRect.setWidth(aNewRect.getOpenWidth() + nX);
                                aNewRect.setHeight(aNewRect.getOpenHeight() + nY);
                                break;
                            default:
                                break;
                        }
                        if ( !(bCheck && isOver(aNewRect,*rReportSection.getPage(),rView)) )
                            rView.MovAction(aEndPoint);
                        rView.EndDragObj();
 
                        // restore snap
                        if ( !bWasNoSnap )
                            const_cast<SdrDragStat&>(rDragStat).SetNoSnap( bWasNoSnap );
                        if ( bWasSnapEnabled )
                            rView.SetSnapEnabled( bWasSnapEnabled );
                    }
 
                    // make moved handle visible
                    const tools::Rectangle aVisRect( aEndPoint - Point( DEFAUL_MOVE_SIZE, DEFAUL_MOVE_SIZE ), Size( 200, 200 ) );
                    rView.MakeVisible( aVisRect, rReportSection);
                }
            }
            rView.AdjustMarkHdl();
        }
    }
}
 
void OViewsWindow::stopScrollTimer()
{
    ::std::for_each(m_aSections.begin(),m_aSections.end(),
        [] (const TSectionsMap::value_type& sectionPtr) {
            sectionPtr->getReportSection().stopScrollTimer();
        });
}
 
void OViewsWindow::fillCollapsedSections(::std::vector<sal_uInt16>& _rCollapsedPositions) const
{
    sal_uInt16 i = 0;
    for (const auto& rxSection : m_aSections)
    {
        if ( rxSection->getStartMarker().isCollapsed() )
            _rCollapsedPositions.push_back(i);
        ++i;
    }
}
 
void OViewsWindow::collapseSections(const uno::Sequence< beans::PropertyValue>& _aCollapsedSections)
{
    for (const beans::PropertyValue& rSection : _aCollapsedSections)
    {
        sal_uInt16 nPos = sal_uInt16(-1);
        if ( (rSection.Value >>= nPos) && nPos < m_aSections.size() )
        {
            m_aSections[nPos]->setCollapsed(true);
        }
    }
}
 
void OViewsWindow::zoom(const Fraction& _aZoom)
{
    const MapMode& aMapMode = GetMapMode();
 
    Fraction aStartWidth(tools::Long(REPORT_STARTMARKER_WIDTH));
    if ( _aZoom < aMapMode.GetScaleX() )
        aStartWidth *= aMapMode.GetScaleX();
    else
        aStartWidth *= _aZoom;
 
    setZoomFactor(_aZoom,*this);
 
    for (const auto& rxSection : m_aSections)
    {
        rxSection->zoom(_aZoom);
    }
 
    Resize();
 
    Size aOut = GetOutputSizePixel();
    aOut.setWidth( tools::Long(aStartWidth) );
    aOut = PixelToLogic(aOut);
 
    tools::Rectangle aRect(PixelToLogic(Point(0,0)),aOut);
    Invalidate(aRect, InvalidateFlags::NoChildren);
}
 
void OViewsWindow::scrollChildren(const Point& _aThumbPos)
{
    const Point aPos(PixelToLogic(_aThumbPos));
    {
        MapMode aMapMode = GetMapMode();
        const Point aOld = aMapMode.GetOrigin();
        aMapMode.SetOrigin(m_pParent->GetMapMode().GetOrigin());
 
        const Point aPosY(m_pParent->PixelToLogic(_aThumbPos,aMapMode));
 
        aMapMode.SetOrigin( Point(aOld.X() , - aPosY.Y()));
        SetMapMode( aMapMode );
        Scroll(0, -( aOld.Y() + aPosY.Y()),ScrollFlags::Children);
    }
 
    for (const auto& rxSection : m_aSections)
    {
        rxSection->scrollChildren(aPos.X());
    }
}
 
void OViewsWindow::fillControlModelSelection(::std::vector< uno::Reference< uno::XInterface > >& _rSelection) const
{
    for (const auto& rxSection : m_aSections)
    {
        rxSection->getReportSection().fillControlModelSelection(_rSelection);
    }
}
 
} // rptui
 
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

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

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

V568 It's odd that the argument of sizeof() operator is the expression.

V783 Dereferencing of the invalid iterator '-- aIter' might take place.

V560 A part of conditional expression is always true.