1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "imivctl.hxx"
21 #include <sal/log.hxx>
23 IcnCursor_Impl::IcnCursor_Impl( SvxIconChoiceCtrl_Impl
* pOwner
)
33 IcnCursor_Impl::~IcnCursor_Impl()
37 sal_uInt16
IcnCursor_Impl::GetSortListPos( SvxIconChoiceCtrlEntryPtrVec
& rList
, tools::Long nValue
,
40 sal_uInt16 nCount
= rList
.size();
44 sal_uInt16 nCurPos
= 0;
45 tools::Long nPrevValue
= LONG_MIN
;
48 const tools::Rectangle
& rRect
= pView
->GetEntryBoundRect( rList
[nCurPos
] );
49 tools::Long nCurValue
;
51 nCurValue
= rRect
.Top();
53 nCurValue
= rRect
.Left();
54 if( nValue
>= nPrevValue
&& nValue
<= nCurValue
)
56 nPrevValue
= nCurValue
;
63 void IcnCursor_Impl::ImplCreate()
65 pView
->CheckBoundingRects();
66 DBG_ASSERT(xColumns
==nullptr&&xRows
==nullptr,"ImplCreate: Not cleared");
70 xColumns
.reset(new IconChoiceMap
);
71 xRows
.reset(new IconChoiceMap
);
73 size_t nCount
= pView
->maEntries
.size();
74 for( size_t nCur
= 0; nCur
< nCount
; nCur
++ )
76 SvxIconChoiceCtrlEntry
* pEntry
= pView
->maEntries
[ nCur
].get();
77 // const Rectangle& rRect = pView->GetEntryBoundRect( pEntry );
78 tools::Rectangle
rRect( pView
->CalcBmpRect( pEntry
) );
79 short nY
= static_cast<short>( ((rRect
.Top()+rRect
.Bottom())/2) / nDeltaHeight
);
80 short nX
= static_cast<short>( ((rRect
.Left()+rRect
.Right())/2) / nDeltaWidth
);
82 // capture rounding errors
84 nY
= sal::static_int_cast
< short >(nRows
- 1);
86 nX
= sal::static_int_cast
< short >(nCols
- 1);
88 SvxIconChoiceCtrlEntryPtrVec
& rColEntry
= (*xColumns
)[nX
];
89 sal_uInt16 nIns
= GetSortListPos( rColEntry
, rRect
.Top(), true );
90 rColEntry
.insert( rColEntry
.begin() + nIns
, pEntry
);
92 SvxIconChoiceCtrlEntryPtrVec
& rRowEntry
= (*xRows
)[nY
];
93 nIns
= GetSortListPos( rRowEntry
, rRect
.Left(), false );
94 rRowEntry
.insert( rRowEntry
.begin() + nIns
, pEntry
);
102 void IcnCursor_Impl::Clear()
114 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::SearchCol(sal_uInt16 nCol
, sal_uInt16 nTop
, sal_uInt16 nBottom
,
115 bool bDown
, bool bSimple
)
117 DBG_ASSERT(pCurEntry
, "SearchCol: No reference entry");
118 IconChoiceMap::iterator mapIt
= xColumns
->find( nCol
);
119 if ( mapIt
== xColumns
->end() )
121 SvxIconChoiceCtrlEntryPtrVec
const & rList
= mapIt
->second
;
122 const sal_uInt16 nCount
= rList
.size();
126 const tools::Rectangle
& rRefRect
= pView
->GetEntryBoundRect(pCurEntry
);
130 SvxIconChoiceCtrlEntryPtrVec::const_iterator it
= std::find( rList
.begin(), rList
.end(), pCurEntry
);
132 assert(it
!= rList
.end()); //Entry not in Col-List
133 if (it
== rList
.end())
138 while( ++it
!= rList
.end() )
140 SvxIconChoiceCtrlEntry
* pEntry
= *it
;
141 const tools::Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
142 if( rRect
.Top() > rRefRect
.Top() )
149 SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator
it2(it
);
150 while (it2
!= rList
.rend())
152 SvxIconChoiceCtrlEntry
* pEntry
= *it2
;
153 const tools::Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
154 if( rRect
.Top() < rRefRect
.Top() )
163 std::swap(nTop
, nBottom
);
165 tools::Long nMinDistance
= LONG_MAX
;
166 SvxIconChoiceCtrlEntry
* pResult
= nullptr;
167 for( sal_uInt16 nCur
= 0; nCur
< nCount
; nCur
++ )
169 SvxIconChoiceCtrlEntry
* pEntry
= rList
[ nCur
];
170 if( pEntry
!= pCurEntry
)
172 sal_uInt16 nY
= pEntry
->nY
;
173 if( nY
>= nTop
&& nY
<= nBottom
)
175 const tools::Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
176 tools::Long nDistance
= rRect
.Top() - rRefRect
.Top();
179 if( nDistance
&& nDistance
< nMinDistance
)
181 nMinDistance
= nDistance
;
190 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::SearchRow(sal_uInt16 nRow
, sal_uInt16 nLeft
, sal_uInt16 nRight
,
191 bool bRight
, bool bSimple
)
193 DBG_ASSERT(pCurEntry
,"SearchRow: No reference entry");
194 IconChoiceMap::iterator mapIt
= xRows
->find( nRow
);
195 if ( mapIt
== xRows
->end() )
197 SvxIconChoiceCtrlEntryPtrVec
const & rList
= mapIt
->second
;
198 const sal_uInt16 nCount
= rList
.size();
202 const tools::Rectangle
& rRefRect
= pView
->GetEntryBoundRect(pCurEntry
);
206 SvxIconChoiceCtrlEntryPtrVec::const_iterator it
= std::find( rList
.begin(), rList
.end(), pCurEntry
);
208 assert(it
!= rList
.end()); //Entry not in Row-List
209 if (it
== rList
.end())
214 while( ++it
!= rList
.end() )
216 SvxIconChoiceCtrlEntry
* pEntry
= *it
;
217 const tools::Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
218 if( rRect
.Left() > rRefRect
.Left() )
225 SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator
it2(it
);
226 while (it2
!= rList
.rend())
228 SvxIconChoiceCtrlEntry
* pEntry
= *it2
;
229 const tools::Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
230 if( rRect
.Left() < rRefRect
.Left() )
239 std::swap(nRight
, nLeft
);
241 tools::Long nMinDistance
= LONG_MAX
;
242 SvxIconChoiceCtrlEntry
* pResult
= nullptr;
243 for( sal_uInt16 nCur
= 0; nCur
< nCount
; nCur
++ )
245 SvxIconChoiceCtrlEntry
* pEntry
= rList
[ nCur
];
246 if( pEntry
!= pCurEntry
)
248 sal_uInt16 nX
= pEntry
->nX
;
249 if( nX
>= nLeft
&& nX
<= nRight
)
251 const tools::Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
252 tools::Long nDistance
= rRect
.Left() - rRefRect
.Left();
255 if( nDistance
&& nDistance
< nMinDistance
)
257 nMinDistance
= nDistance
;
268 Searches, starting from the passed value, the next entry to the left/to the
269 right. Example for bRight = sal_True:
274 S 1 1 1 ====> search direction
279 S : starting position
280 1 : first searched rectangle
281 a,b,c : 2nd, 3rd, 4th searched rectangle
284 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoLeftRight( SvxIconChoiceCtrlEntry
* pCtrlEntry
, bool bRight
)
286 SvxIconChoiceCtrlEntry
* pResult
;
287 pCurEntry
= pCtrlEntry
;
289 sal_uInt16 nY
= pCtrlEntry
->nY
;
290 sal_uInt16 nX
= pCtrlEntry
->nX
;
291 DBG_ASSERT(nY
< nRows
,"GoLeftRight:Bad column");
292 DBG_ASSERT(nX
< nCols
,"GoLeftRight:Bad row");
293 // neighbor in same row?
296 nY
, nX
, sal::static_int_cast
< sal_uInt16
>(nCols
-1), true, true );
298 pResult
= SearchRow( nY
, 0, nX
, false, true );
302 tools::Long nCurCol
= nX
;
304 tools::Long nColOffs
, nLastCol
;
313 nLastCol
= -1; // 0-1
316 sal_uInt16 nRowMin
= nY
;
317 sal_uInt16 nRowMax
= nY
;
320 SvxIconChoiceCtrlEntry
* pEntry
= SearchCol(static_cast<sal_uInt16
>(nCurCol
), nRowMin
, nRowMax
, true, false);
325 if( nRowMax
< (nRows
-1))
328 } while( nCurCol
!= nLastCol
);
332 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoPageUpDown( SvxIconChoiceCtrlEntry
* pStart
, bool bDown
)
334 if( pView
->IsAutoArrange() && !(pView
->nWinBits
& WB_ALIGN_TOP
) )
336 const tools::Long nPos
= static_cast<tools::Long
>(pView
->GetEntryListPos( pStart
));
337 tools::Long nEntriesInView
= pView
->aOutputSize
.Height() / pView
->nGridDY
;
339 ((pView
->aOutputSize
.Width()+(pView
->nGridDX
/2)) / pView
->nGridDX
);
340 tools::Long nNewPos
= nPos
;
343 nNewPos
+= nEntriesInView
;
344 if( nNewPos
>= static_cast<tools::Long
>(pView
->maEntries
.size()) )
345 nNewPos
= pView
->maEntries
.size() - 1;
349 nNewPos
-= nEntriesInView
;
353 if( nPos
!= nNewPos
)
354 return pView
->maEntries
[ static_cast<size_t>(nNewPos
) ].get();
357 tools::Long nOpt
= pView
->GetEntryBoundRect( pStart
).Top();
360 nOpt
+= pView
->aOutputSize
.Height();
361 nOpt
-= pView
->nGridDY
;
365 nOpt
-= pView
->aOutputSize
.Height();
366 nOpt
+= pView
->nGridDY
;
371 tools::Long nPrevErr
= LONG_MAX
;
373 SvxIconChoiceCtrlEntry
* pPrev
= pStart
;
374 SvxIconChoiceCtrlEntry
* pNext
= GoUpDown( pStart
, bDown
);
377 tools::Long nCur
= pView
->GetEntryBoundRect( pNext
).Top();
378 tools::Long nErr
= nOpt
- nCur
;
381 if( nErr
> nPrevErr
)
385 pNext
= GoUpDown( pNext
, bDown
);
387 if( pPrev
!= pStart
)
392 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoUpDown( SvxIconChoiceCtrlEntry
* pCtrlEntry
, bool bDown
)
394 if( pView
->IsAutoArrange() && !(pView
->nWinBits
& WB_ALIGN_TOP
) )
396 sal_uLong nPos
= pView
->GetEntryListPos( pCtrlEntry
);
397 if( bDown
&& nPos
< (pView
->maEntries
.size() - 1) )
398 return pView
->maEntries
[ nPos
+ 1 ].get();
399 else if( !bDown
&& nPos
> 0 )
400 return pView
->maEntries
[ nPos
- 1 ].get();
404 SvxIconChoiceCtrlEntry
* pResult
;
405 pCurEntry
= pCtrlEntry
;
407 sal_uInt16 nY
= pCtrlEntry
->nY
;
408 sal_uInt16 nX
= pCtrlEntry
->nX
;
409 DBG_ASSERT(nY
<nRows
,"GoUpDown:Bad column");
410 DBG_ASSERT(nX
<nCols
,"GoUpDown:Bad row");
412 // neighbor in same column?
415 nX
, nY
, sal::static_int_cast
< sal_uInt16
>(nRows
-1), true, true );
417 pResult
= SearchCol( nX
, 0, nY
, false, true );
421 tools::Long nCurRow
= nY
;
423 tools::Long nRowOffs
, nLastRow
;
432 nLastRow
= -1; // 0-1
435 sal_uInt16 nColMin
= nX
;
436 sal_uInt16 nColMax
= nX
;
439 SvxIconChoiceCtrlEntry
* pEntry
= SearchRow(static_cast<sal_uInt16
>(nCurRow
), nColMin
, nColMax
, true, false);
444 if( nColMax
< (nCols
-1))
447 } while( nCurRow
!= nLastRow
);
451 void IcnCursor_Impl::SetDeltas()
453 const Size
& rSize
= pView
->aVirtOutputSize
;
454 nCols
= rSize
.Width() / pView
->nGridDX
;
457 nRows
= rSize
.Height() / pView
->nGridDY
;
458 if( (nRows
* pView
->nGridDY
) < rSize
.Height() )
463 nDeltaWidth
= static_cast<short>(rSize
.Width() / nCols
);
464 nDeltaHeight
= static_cast<short>(rSize
.Height() / nRows
);
468 SAL_INFO("vcl", "SetDeltas:Bad height");
473 SAL_INFO("vcl", "SetDeltas:Bad width");
477 IcnGridMap_Impl::IcnGridMap_Impl(SvxIconChoiceCtrl_Impl
* pView
)
478 : _pView(pView
), _nGridCols(0), _nGridRows(0)
482 IcnGridMap_Impl::~IcnGridMap_Impl()
486 void IcnGridMap_Impl::Expand()
492 sal_uInt16 nNewGridRows
= _nGridRows
;
493 sal_uInt16 nNewGridCols
= _nGridCols
;
494 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
499 size_t nNewCellCount
= static_cast<size_t>(nNewGridRows
) * nNewGridCols
;
500 bool* pNewGridMap
= new bool[nNewCellCount
];
501 size_t nOldCellCount
= static_cast<size_t>(_nGridRows
) * _nGridCols
;
502 memcpy(pNewGridMap
, _pGridMap
.get(), nOldCellCount
* sizeof(bool));
503 memset(pNewGridMap
+ nOldCellCount
, 0, (nNewCellCount
-nOldCellCount
) * sizeof(bool));
504 _pGridMap
.reset( pNewGridMap
);
505 _nGridRows
= nNewGridRows
;
506 _nGridCols
= nNewGridCols
;
510 void IcnGridMap_Impl::Create_Impl()
512 DBG_ASSERT(!_pGridMap
,"Unnecessary call to IcnGridMap_Impl::Create_Impl()");
515 GetMinMapSize( _nGridCols
, _nGridRows
);
516 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
517 _nGridRows
+= 50; // avoid resize of gridmap too often
521 size_t nCellCount
= static_cast<size_t>(_nGridRows
) * _nGridCols
;
522 _pGridMap
.reset( new bool[nCellCount
] );
523 memset(_pGridMap
.get(), 0, nCellCount
* sizeof(bool));
525 const size_t nCount
= _pView
->maEntries
.size();
526 for( size_t nCur
=0; nCur
< nCount
; nCur
++ )
527 OccupyGrids( _pView
->maEntries
[ nCur
].get() );
530 void IcnGridMap_Impl::GetMinMapSize( sal_uInt16
& rDX
, sal_uInt16
& rDY
) const
533 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
535 // The view grows in vertical direction. Its max. width is _pView->nMaxVirtWidth
536 nX
= _pView
->nMaxVirtWidth
;
538 nX
= _pView
->pView
->GetOutputSizePixel().Width();
539 if( !(_pView
->nFlags
& IconChoiceFlags::Arranging
) )
540 nX
-= _pView
->nVerSBarWidth
;
542 nY
= _pView
->aVirtOutputSize
.Height();
546 // The view grows in horizontal direction. Its max. height is _pView->nMaxVirtHeight
547 nY
= _pView
->nMaxVirtHeight
;
549 nY
= _pView
->pView
->GetOutputSizePixel().Height();
550 if( !(_pView
->nFlags
& IconChoiceFlags::Arranging
) )
551 nY
-= _pView
->nHorSBarHeight
;
552 nX
= _pView
->aVirtOutputSize
.Width();
556 nX
= DEFAULT_MAX_VIRT_WIDTH
;
558 nY
= DEFAULT_MAX_VIRT_HEIGHT
;
560 tools::Long nDX
= nX
/ _pView
->nGridDX
;
561 tools::Long nDY
= nY
/ _pView
->nGridDY
;
568 rDX
= static_cast<sal_uInt16
>(nDX
);
569 rDY
= static_cast<sal_uInt16
>(nDY
);
572 GridId
IcnGridMap_Impl::GetGrid( sal_uInt16 nGridX
, sal_uInt16 nGridY
)
575 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
576 return nGridX
+ ( static_cast<GridId
>(nGridY
) * _nGridCols
);
578 return nGridY
+ ( static_cast<GridId
>(nGridX
) * _nGridRows
);
581 GridId
IcnGridMap_Impl::GetGrid( const Point
& rDocPos
)
585 tools::Long nX
= rDocPos
.X();
586 tools::Long nY
= rDocPos
.Y();
587 nX
-= LROFFS_WINBORDER
;
588 nY
-= TBOFFS_WINBORDER
;
589 nX
/= _pView
->nGridDX
;
590 nY
/= _pView
->nGridDY
;
591 if( nX
>= _nGridCols
)
595 if( nY
>= _nGridRows
)
599 GridId nId
= GetGrid( static_cast<sal_uInt16
>(nX
), static_cast<sal_uInt16
>(nY
) );
600 DBG_ASSERT(nId
<o3tl::make_unsigned(_nGridCols
*_nGridRows
),"GetGrid failed");
604 tools::Rectangle
IcnGridMap_Impl::GetGridRect( GridId nId
)
607 sal_uInt16 nGridX
, nGridY
;
608 GetGridCoord( nId
, nGridX
, nGridY
);
609 const tools::Long nLeft
= nGridX
* _pView
->nGridDX
+ LROFFS_WINBORDER
;
610 const tools::Long nTop
= nGridY
* _pView
->nGridDY
+ TBOFFS_WINBORDER
;
611 return tools::Rectangle(
613 nLeft
+ _pView
->nGridDX
,
614 nTop
+ _pView
->nGridDY
);
617 GridId
IcnGridMap_Impl::GetUnoccupiedGrid()
620 sal_uLong nStart
= 0;
621 bool bExpanded
= false;
625 const sal_uLong nCount
= static_cast<sal_uInt16
>(_nGridCols
* _nGridRows
);
626 for( sal_uLong nCur
= nStart
; nCur
< nCount
; nCur
++ )
628 if( !_pGridMap
[ nCur
] )
630 _pGridMap
[ nCur
] = true;
631 return static_cast<GridId
>(nCur
);
634 DBG_ASSERT(!bExpanded
,"ExpandGrid failed");
636 return 0; // prevent never ending loop
643 // An entry only means that there's a GridRect lying under its center. This
644 // variant is much faster than allocating via the bounding rectangle but can
645 // lead to small overlaps.
646 void IcnGridMap_Impl::OccupyGrids( const SvxIconChoiceCtrlEntry
* pEntry
)
648 if( !_pGridMap
|| !SvxIconChoiceCtrl_Impl::IsBoundingRectValid( pEntry
->aRect
))
650 OccupyGrid( GetGrid( pEntry
->aRect
.Center()) );
653 void IcnGridMap_Impl::Clear()
660 _aLastOccupiedGrid
.SetEmpty();
664 sal_uLong
IcnGridMap_Impl::GetGridCount( const Size
& rSizePixel
, sal_uInt16 nDX
, sal_uInt16 nDY
)
666 tools::Long ndx
= (rSizePixel
.Width() - LROFFS_WINBORDER
) / nDX
;
667 if( ndx
< 0 ) ndx
*= -1;
668 tools::Long ndy
= (rSizePixel
.Height() - TBOFFS_WINBORDER
) / nDY
;
669 if( ndy
< 0 ) ndy
*= -1;
670 return static_cast<sal_uLong
>(ndx
* ndy
);
673 void IcnGridMap_Impl::OutputSizeChanged()
678 sal_uInt16 nCols
, nRows
;
679 GetMinMapSize( nCols
, nRows
);
680 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
682 if( nCols
!= _nGridCols
)
684 else if( nRows
>= _nGridRows
)
689 if( nRows
!= _nGridRows
)
691 else if( nCols
>= _nGridCols
)
696 // Independently of the view's alignment (TOP or LEFT), the gridmap
697 // should contain the data in a continuous region, to make it possible
698 // to copy the whole block if the gridmap needs to be expanded.
699 void IcnGridMap_Impl::GetGridCoord( GridId nId
, sal_uInt16
& rGridX
, sal_uInt16
& rGridY
)
702 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
704 rGridX
= static_cast<sal_uInt16
>(nId
% _nGridCols
);
705 rGridY
= static_cast<sal_uInt16
>(nId
/ _nGridCols
);
709 rGridX
= static_cast<sal_uInt16
>(nId
/ _nGridRows
);
710 rGridY
= static_cast<sal_uInt16
>(nId
% _nGridRows
);
715 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */