From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- vcl/source/control/imivctl2.cxx | 715 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 715 insertions(+) create mode 100644 vcl/source/control/imivctl2.cxx (limited to 'vcl/source/control/imivctl2.cxx') diff --git a/vcl/source/control/imivctl2.cxx b/vcl/source/control/imivctl2.cxx new file mode 100644 index 000000000..5b0d2d8ad --- /dev/null +++ b/vcl/source/control/imivctl2.cxx @@ -0,0 +1,715 @@ +/* -*- 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 "imivctl.hxx" +#include + +IcnCursor_Impl::IcnCursor_Impl( SvxIconChoiceCtrl_Impl* pOwner ) +{ + pView = pOwner; + pCurEntry = nullptr; + nDeltaWidth = 0; + nDeltaHeight= 0; + nCols = 0; + nRows = 0; +} + +IcnCursor_Impl::~IcnCursor_Impl() +{ +} + +sal_uInt16 IcnCursor_Impl::GetSortListPos( SvxIconChoiceCtrlEntryPtrVec& rList, tools::Long nValue, + bool bVertical ) +{ + sal_uInt16 nCount = rList.size(); + if( !nCount ) + return 0; + + sal_uInt16 nCurPos = 0; + tools::Long nPrevValue = LONG_MIN; + while( nCount ) + { + const tools::Rectangle& rRect = pView->GetEntryBoundRect( rList[nCurPos] ); + tools::Long nCurValue; + if( bVertical ) + nCurValue = rRect.Top(); + else + nCurValue = rRect.Left(); + if( nValue >= nPrevValue && nValue <= nCurValue ) + return nCurPos; + nPrevValue = nCurValue; + nCount--; + nCurPos++; + } + return rList.size(); +} + +void IcnCursor_Impl::ImplCreate() +{ + pView->CheckBoundingRects(); + DBG_ASSERT(xColumns==nullptr&&xRows==nullptr,"ImplCreate: Not cleared"); + + SetDeltas(); + + xColumns.reset(new IconChoiceMap); + xRows.reset(new IconChoiceMap); + + size_t nCount = pView->maEntries.size(); + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = pView->maEntries[ nCur ].get(); + // const Rectangle& rRect = pView->GetEntryBoundRect( pEntry ); + tools::Rectangle rRect( pView->CalcBmpRect( pEntry ) ); + short nY = static_cast( ((rRect.Top()+rRect.Bottom())/2) / nDeltaHeight ); + short nX = static_cast( ((rRect.Left()+rRect.Right())/2) / nDeltaWidth ); + + // capture rounding errors + if( nY >= nRows ) + nY = sal::static_int_cast< short >(nRows - 1); + if( nX >= nCols ) + nX = sal::static_int_cast< short >(nCols - 1); + + SvxIconChoiceCtrlEntryPtrVec& rColEntry = (*xColumns)[nX]; + sal_uInt16 nIns = GetSortListPos( rColEntry, rRect.Top(), true ); + rColEntry.insert( rColEntry.begin() + nIns, pEntry ); + + SvxIconChoiceCtrlEntryPtrVec& rRowEntry = (*xRows)[nY]; + nIns = GetSortListPos( rRowEntry, rRect.Left(), false ); + rRowEntry.insert( rRowEntry.begin() + nIns, pEntry ); + + pEntry->nX = nX; + pEntry->nY = nY; + } +} + + +void IcnCursor_Impl::Clear() +{ + if( xColumns ) + { + xColumns.reset(); + xRows.reset(); + pCurEntry = nullptr; + nDeltaWidth = 0; + nDeltaHeight = 0; + } +} + +SvxIconChoiceCtrlEntry* IcnCursor_Impl::SearchCol(sal_uInt16 nCol, sal_uInt16 nTop, sal_uInt16 nBottom, + bool bDown, bool bSimple ) +{ + DBG_ASSERT(pCurEntry, "SearchCol: No reference entry"); + IconChoiceMap::iterator mapIt = xColumns->find( nCol ); + if ( mapIt == xColumns->end() ) + return nullptr; + SvxIconChoiceCtrlEntryPtrVec const & rList = mapIt->second; + const sal_uInt16 nCount = rList.size(); + if( !nCount ) + return nullptr; + + const tools::Rectangle& rRefRect = pView->GetEntryBoundRect(pCurEntry); + + if( bSimple ) + { + SvxIconChoiceCtrlEntryPtrVec::const_iterator it = std::find( rList.begin(), rList.end(), pCurEntry ); + + assert(it != rList.end()); //Entry not in Col-List + if (it == rList.end()) + return nullptr; + + if( bDown ) + { + while( ++it != rList.end() ) + { + SvxIconChoiceCtrlEntry* pEntry = *it; + const tools::Rectangle& rRect = pView->GetEntryBoundRect( pEntry ); + if( rRect.Top() > rRefRect.Top() ) + return pEntry; + } + return nullptr; + } + else + { + SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator it2(it); + while (it2 != rList.rend()) + { + SvxIconChoiceCtrlEntry* pEntry = *it2; + const tools::Rectangle& rRect = pView->GetEntryBoundRect( pEntry ); + if( rRect.Top() < rRefRect.Top() ) + return pEntry; + ++it2; + } + return nullptr; + } + } + + if( nTop > nBottom ) + std::swap(nTop, nBottom); + + tools::Long nMinDistance = LONG_MAX; + SvxIconChoiceCtrlEntry* pResult = nullptr; + for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = rList[ nCur ]; + if( pEntry != pCurEntry ) + { + sal_uInt16 nY = pEntry->nY; + if( nY >= nTop && nY <= nBottom ) + { + const tools::Rectangle& rRect = pView->GetEntryBoundRect( pEntry ); + tools::Long nDistance = rRect.Top() - rRefRect.Top(); + if( nDistance < 0 ) + nDistance *= -1; + if( nDistance && nDistance < nMinDistance ) + { + nMinDistance = nDistance; + pResult = pEntry; + } + } + } + } + return pResult; +} + +SvxIconChoiceCtrlEntry* IcnCursor_Impl::SearchRow(sal_uInt16 nRow, sal_uInt16 nLeft, sal_uInt16 nRight, + bool bRight, bool bSimple ) +{ + DBG_ASSERT(pCurEntry,"SearchRow: No reference entry"); + IconChoiceMap::iterator mapIt = xRows->find( nRow ); + if ( mapIt == xRows->end() ) + return nullptr; + SvxIconChoiceCtrlEntryPtrVec const & rList = mapIt->second; + const sal_uInt16 nCount = rList.size(); + if( !nCount ) + return nullptr; + + const tools::Rectangle& rRefRect = pView->GetEntryBoundRect(pCurEntry); + + if( bSimple ) + { + SvxIconChoiceCtrlEntryPtrVec::const_iterator it = std::find( rList.begin(), rList.end(), pCurEntry ); + + assert(it != rList.end()); //Entry not in Row-List + if (it == rList.end()) + return nullptr; + + if( bRight ) + { + while( ++it != rList.end() ) + { + SvxIconChoiceCtrlEntry* pEntry = *it; + const tools::Rectangle& rRect = pView->GetEntryBoundRect( pEntry ); + if( rRect.Left() > rRefRect.Left() ) + return pEntry; + } + return nullptr; + } + else + { + SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator it2(it); + while (it2 != rList.rend()) + { + SvxIconChoiceCtrlEntry* pEntry = *it2; + const tools::Rectangle& rRect = pView->GetEntryBoundRect( pEntry ); + if( rRect.Left() < rRefRect.Left() ) + return pEntry; + ++it2; + } + return nullptr; + } + + } + if( nRight < nLeft ) + std::swap(nRight, nLeft); + + tools::Long nMinDistance = LONG_MAX; + SvxIconChoiceCtrlEntry* pResult = nullptr; + for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = rList[ nCur ]; + if( pEntry != pCurEntry ) + { + sal_uInt16 nX = pEntry->nX; + if( nX >= nLeft && nX <= nRight ) + { + const tools::Rectangle& rRect = pView->GetEntryBoundRect( pEntry ); + tools::Long nDistance = rRect.Left() - rRefRect.Left(); + if( nDistance < 0 ) + nDistance *= -1; + if( nDistance && nDistance < nMinDistance ) + { + nMinDistance = nDistance; + pResult = pEntry; + } + } + } + } + return pResult; +} + + +/* + Searches, starting from the passed value, the next entry to the left/to the + right. Example for bRight = sal_True: + + c + b c + a b c + S 1 1 1 ====> search direction + a b c + b c + c + + S : starting position + 1 : first searched rectangle + a,b,c : 2nd, 3rd, 4th searched rectangle +*/ + +SvxIconChoiceCtrlEntry* IcnCursor_Impl::GoLeftRight( SvxIconChoiceCtrlEntry* pCtrlEntry, bool bRight ) +{ + SvxIconChoiceCtrlEntry* pResult; + pCurEntry = pCtrlEntry; + Create(); + sal_uInt16 nY = pCtrlEntry->nY; + sal_uInt16 nX = pCtrlEntry->nX; + DBG_ASSERT(nY< nRows,"GoLeftRight:Bad column"); + DBG_ASSERT(nX< nCols,"GoLeftRight:Bad row"); + // neighbor in same row? + if( bRight ) + pResult = SearchRow( + nY, nX, sal::static_int_cast< sal_uInt16 >(nCols-1), true, true ); + else + pResult = SearchRow( nY, 0, nX, false, true ); + if( pResult ) + return pResult; + + tools::Long nCurCol = nX; + + tools::Long nColOffs, nLastCol; + if( bRight ) + { + nColOffs = 1; + nLastCol = nCols; + } + else + { + nColOffs = -1; + nLastCol = -1; // 0-1 + } + + sal_uInt16 nRowMin = nY; + sal_uInt16 nRowMax = nY; + do + { + SvxIconChoiceCtrlEntry* pEntry = SearchCol(static_cast(nCurCol), nRowMin, nRowMax, true, false); + if( pEntry ) + return pEntry; + if( nRowMin ) + nRowMin--; + if( nRowMax < (nRows-1)) + nRowMax++; + nCurCol += nColOffs; + } while( nCurCol != nLastCol ); + return nullptr; +} + +SvxIconChoiceCtrlEntry* IcnCursor_Impl::GoPageUpDown( SvxIconChoiceCtrlEntry* pStart, bool bDown) +{ + if( pView->IsAutoArrange() && !(pView->nWinBits & WB_ALIGN_TOP) ) + { + const tools::Long nPos = static_cast(pView->GetEntryListPos( pStart )); + tools::Long nEntriesInView = pView->aOutputSize.Height() / pView->nGridDY; + nEntriesInView *= + ((pView->aOutputSize.Width()+(pView->nGridDX/2)) / pView->nGridDX ); + tools::Long nNewPos = nPos; + if( bDown ) + { + nNewPos += nEntriesInView; + if( nNewPos >= static_cast(pView->maEntries.size()) ) + nNewPos = pView->maEntries.size() - 1; + } + else + { + nNewPos -= nEntriesInView; + if( nNewPos < 0 ) + nNewPos = 0; + } + if( nPos != nNewPos ) + return pView->maEntries[ static_cast(nNewPos) ].get(); + return nullptr; + } + tools::Long nOpt = pView->GetEntryBoundRect( pStart ).Top(); + if( bDown ) + { + nOpt += pView->aOutputSize.Height(); + nOpt -= pView->nGridDY; + } + else + { + nOpt -= pView->aOutputSize.Height(); + nOpt += pView->nGridDY; + } + if( nOpt < 0 ) + nOpt = 0; + + tools::Long nPrevErr = LONG_MAX; + + SvxIconChoiceCtrlEntry* pPrev = pStart; + SvxIconChoiceCtrlEntry* pNext = GoUpDown( pStart, bDown ); + while( pNext ) + { + tools::Long nCur = pView->GetEntryBoundRect( pNext ).Top(); + tools::Long nErr = nOpt - nCur; + if( nErr < 0 ) + nErr *= -1; + if( nErr > nPrevErr ) + return pPrev; + nPrevErr = nErr; + pPrev = pNext; + pNext = GoUpDown( pNext, bDown ); + } + if( pPrev != pStart ) + return pPrev; + return nullptr; +} + +SvxIconChoiceCtrlEntry* IcnCursor_Impl::GoUpDown( SvxIconChoiceCtrlEntry* pCtrlEntry, bool bDown) +{ + if( pView->IsAutoArrange() && !(pView->nWinBits & WB_ALIGN_TOP) ) + { + sal_uLong nPos = pView->GetEntryListPos( pCtrlEntry ); + if( bDown && nPos < (pView->maEntries.size() - 1) ) + return pView->maEntries[ nPos + 1 ].get(); + else if( !bDown && nPos > 0 ) + return pView->maEntries[ nPos - 1 ].get(); + return nullptr; + } + + SvxIconChoiceCtrlEntry* pResult; + pCurEntry = pCtrlEntry; + Create(); + sal_uInt16 nY = pCtrlEntry->nY; + sal_uInt16 nX = pCtrlEntry->nX; + DBG_ASSERT(nY(nRows-1), true, true ); + else + pResult = SearchCol( nX, 0, nY, false, true ); + if( pResult ) + return pResult; + + tools::Long nCurRow = nY; + + tools::Long nRowOffs, nLastRow; + if( bDown ) + { + nRowOffs = 1; + nLastRow = nRows; + } + else + { + nRowOffs = -1; + nLastRow = -1; // 0-1 + } + + sal_uInt16 nColMin = nX; + sal_uInt16 nColMax = nX; + do + { + SvxIconChoiceCtrlEntry* pEntry = SearchRow(static_cast(nCurRow), nColMin, nColMax, true, false); + if( pEntry ) + return pEntry; + if( nColMin ) + nColMin--; + if( nColMax < (nCols-1)) + nColMax++; + nCurRow += nRowOffs; + } while( nCurRow != nLastRow ); + return nullptr; +} + +void IcnCursor_Impl::SetDeltas() +{ + const Size& rSize = pView->aVirtOutputSize; + nCols = rSize.Width() / pView->nGridDX; + if( !nCols ) + nCols = 1; + nRows = rSize.Height() / pView->nGridDY; + if( (nRows * pView->nGridDY) < rSize.Height() ) + nRows++; + if( !nRows ) + nRows = 1; + + nDeltaWidth = static_cast(rSize.Width() / nCols); + nDeltaHeight = static_cast(rSize.Height() / nRows); + if( !nDeltaHeight ) + { + nDeltaHeight = 1; + SAL_INFO("vcl", "SetDeltas:Bad height"); + } + if( !nDeltaWidth ) + { + nDeltaWidth = 1; + SAL_INFO("vcl", "SetDeltas:Bad width"); + } +} + +IcnGridMap_Impl::IcnGridMap_Impl(SvxIconChoiceCtrl_Impl* pView) + : _pView(pView), _nGridCols(0), _nGridRows(0) +{ +} + +IcnGridMap_Impl::~IcnGridMap_Impl() +{ +} + +void IcnGridMap_Impl::Expand() +{ + if( !_pGridMap ) + Create_Impl(); + else + { + sal_uInt16 nNewGridRows = _nGridRows; + sal_uInt16 nNewGridCols = _nGridCols; + if( _pView->nWinBits & WB_ALIGN_TOP ) + nNewGridRows += 50; + else + nNewGridCols += 50; + + size_t nNewCellCount = static_cast(nNewGridRows) * nNewGridCols; + bool* pNewGridMap = new bool[nNewCellCount]; + size_t nOldCellCount = static_cast(_nGridRows) * _nGridCols; + memcpy(pNewGridMap, _pGridMap.get(), nOldCellCount * sizeof(bool)); + memset(pNewGridMap + nOldCellCount, 0, (nNewCellCount-nOldCellCount) * sizeof(bool)); + _pGridMap.reset( pNewGridMap ); + _nGridRows = nNewGridRows; + _nGridCols = nNewGridCols; + } +} + +void IcnGridMap_Impl::Create_Impl() +{ + DBG_ASSERT(!_pGridMap,"Unnecessary call to IcnGridMap_Impl::Create_Impl()"); + if( _pGridMap ) + return; + GetMinMapSize( _nGridCols, _nGridRows ); + if( _pView->nWinBits & WB_ALIGN_TOP ) + _nGridRows += 50; // avoid resize of gridmap too often + else + _nGridCols += 50; + + size_t nCellCount = static_cast(_nGridRows) * _nGridCols; + _pGridMap.reset( new bool[nCellCount] ); + memset(_pGridMap.get(), 0, nCellCount * sizeof(bool)); + + const size_t nCount = _pView->maEntries.size(); + for( size_t nCur=0; nCur < nCount; nCur++ ) + OccupyGrids( _pView->maEntries[ nCur ].get() ); +} + +void IcnGridMap_Impl::GetMinMapSize( sal_uInt16& rDX, sal_uInt16& rDY ) const +{ + tools::Long nX, nY; + if( _pView->nWinBits & WB_ALIGN_TOP ) + { + // The view grows in vertical direction. Its max. width is _pView->nMaxVirtWidth + nX = _pView->nMaxVirtWidth; + if( !nX ) + nX = _pView->pView->GetOutputSizePixel().Width(); + if( !(_pView->nFlags & IconChoiceFlags::Arranging) ) + nX -= _pView->nVerSBarWidth; + + nY = _pView->aVirtOutputSize.Height(); + } + else + { + // The view grows in horizontal direction. Its max. height is _pView->nMaxVirtHeight + nY = _pView->nMaxVirtHeight; + if( !nY ) + nY = _pView->pView->GetOutputSizePixel().Height(); + if( !(_pView->nFlags & IconChoiceFlags::Arranging) ) + nY -= _pView->nHorSBarHeight; + nX = _pView->aVirtOutputSize.Width(); + } + + if( !nX ) + nX = DEFAULT_MAX_VIRT_WIDTH; + if( !nY ) + nY = DEFAULT_MAX_VIRT_HEIGHT; + + tools::Long nDX = nX / _pView->nGridDX; + tools::Long nDY = nY / _pView->nGridDY; + + if( !nDX ) + nDX++; + if( !nDY ) + nDY++; + + rDX = static_cast(nDX); + rDY = static_cast(nDY); +} + +GridId IcnGridMap_Impl::GetGrid( sal_uInt16 nGridX, sal_uInt16 nGridY ) +{ + Create(); + if( _pView->nWinBits & WB_ALIGN_TOP ) + return nGridX + ( static_cast(nGridY) * _nGridCols ); + else + return nGridY + ( static_cast(nGridX) * _nGridRows ); +} + +GridId IcnGridMap_Impl::GetGrid( const Point& rDocPos ) +{ + Create(); + + tools::Long nX = rDocPos.X(); + tools::Long nY = rDocPos.Y(); + nX -= LROFFS_WINBORDER; + nY -= TBOFFS_WINBORDER; + nX /= _pView->nGridDX; + nY /= _pView->nGridDY; + if( nX >= _nGridCols ) + { + nX = _nGridCols - 1; + } + if( nY >= _nGridRows ) + { + nY = _nGridRows - 1; + } + GridId nId = GetGrid( static_cast(nX), static_cast(nY) ); + DBG_ASSERT(nId nGridDX+ LROFFS_WINBORDER; + const tools::Long nTop = nGridY * _pView->nGridDY + TBOFFS_WINBORDER; + return tools::Rectangle( + nLeft, nTop, + nLeft + _pView->nGridDX, + nTop + _pView->nGridDY ); +} + +GridId IcnGridMap_Impl::GetUnoccupiedGrid() +{ + Create(); + sal_uLong nStart = 0; + bool bExpanded = false; + + while( true ) + { + const sal_uLong nCount = static_cast(_nGridCols * _nGridRows); + for( sal_uLong nCur = nStart; nCur < nCount; nCur++ ) + { + if( !_pGridMap[ nCur ] ) + { + _pGridMap[ nCur ] = true; + return static_cast(nCur); + } + } + DBG_ASSERT(!bExpanded,"ExpandGrid failed"); + if( bExpanded ) + return 0; // prevent never ending loop + bExpanded = true; + Expand(); + nStart = nCount; + } +} + +// An entry only means that there's a GridRect lying under its center. This +// variant is much faster than allocating via the bounding rectangle but can +// lead to small overlaps. +void IcnGridMap_Impl::OccupyGrids( const SvxIconChoiceCtrlEntry* pEntry ) +{ + if( !_pGridMap || !SvxIconChoiceCtrl_Impl::IsBoundingRectValid( pEntry->aRect )) + return; + OccupyGrid( GetGrid( pEntry->aRect.Center()) ); +} + +void IcnGridMap_Impl::Clear() +{ + if( _pGridMap ) + { + _pGridMap.reset(); + _nGridRows = 0; + _nGridCols = 0; + _aLastOccupiedGrid.SetEmpty(); + } +} + +sal_uLong IcnGridMap_Impl::GetGridCount( const Size& rSizePixel, sal_uInt16 nDX, sal_uInt16 nDY) +{ + tools::Long ndx = (rSizePixel.Width() - LROFFS_WINBORDER) / nDX; + if( ndx < 0 ) ndx *= -1; + tools::Long ndy = (rSizePixel.Height() - TBOFFS_WINBORDER) / nDY; + if( ndy < 0 ) ndy *= -1; + return static_cast(ndx * ndy); +} + +void IcnGridMap_Impl::OutputSizeChanged() +{ + if( !_pGridMap ) + return; + + sal_uInt16 nCols, nRows; + GetMinMapSize( nCols, nRows ); + if( _pView->nWinBits & WB_ALIGN_TOP ) + { + if( nCols != _nGridCols ) + Clear(); + else if( nRows >= _nGridRows ) + Expand(); + } + else + { + if( nRows != _nGridRows ) + Clear(); + else if( nCols >= _nGridCols ) + Expand(); + } +} + +// Independently of the view's alignment (TOP or LEFT), the gridmap +// should contain the data in a continuous region, to make it possible +// to copy the whole block if the gridmap needs to be expanded. +void IcnGridMap_Impl::GetGridCoord( GridId nId, sal_uInt16& rGridX, sal_uInt16& rGridY ) +{ + Create(); + if( _pView->nWinBits & WB_ALIGN_TOP ) + { + rGridX = static_cast(nId % _nGridCols); + rGridY = static_cast(nId / _nGridCols); + } + else + { + rGridX = static_cast(nId / _nGridRows); + rGridY = static_cast(nId % _nGridRows); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3