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"
22 IcnCursor_Impl::IcnCursor_Impl( SvxIconChoiceCtrl_Impl
* pOwner
)
32 IcnCursor_Impl::~IcnCursor_Impl()
36 sal_uInt16
IcnCursor_Impl::GetSortListPos( SvxIconChoiceCtrlEntryPtrVec
& rList
, long nValue
,
39 sal_uInt16 nCount
= rList
.size();
43 sal_uInt16 nCurPos
= 0;
44 long nPrevValue
= LONG_MIN
;
47 const Rectangle
& rRect
= pView
->GetEntryBoundRect( rList
[nCurPos
] );
50 nCurValue
= rRect
.Top();
52 nCurValue
= rRect
.Left();
53 if( nValue
>= nPrevValue
&& nValue
<= nCurValue
)
54 return (sal_uInt16
)nCurPos
;
55 nPrevValue
= nCurValue
;
62 void IcnCursor_Impl::ImplCreate()
64 pView
->CheckBoundingRects();
65 DBG_ASSERT(xColumns
==0&&xRows
==0,"ImplCreate: Not cleared");
69 xColumns
.reset(new IconChoiceMap
);
70 xRows
.reset(new IconChoiceMap
);
72 size_t nCount
= pView
->aEntries
.size();
73 for( size_t nCur
= 0; nCur
< nCount
; nCur
++ )
75 SvxIconChoiceCtrlEntry
* pEntry
= pView
->aEntries
[ nCur
];
76 // const Rectangle& rRect = pView->GetEntryBoundRect( pEntry );
77 Rectangle
rRect( pView
->CalcBmpRect( pEntry
,0 ) );
78 short nY
= (short)( ((rRect
.Top()+rRect
.Bottom())/2) / nDeltaHeight
);
79 short nX
= (short)( ((rRect
.Left()+rRect
.Right())/2) / nDeltaWidth
);
81 // capture rounding errors
83 nY
= sal::static_int_cast
< short >(nRows
- 1);
85 nX
= sal::static_int_cast
< short >(nCols
- 1);
87 SvxIconChoiceCtrlEntryPtrVec
& rColEntry
= (*xColumns
)[nX
];
88 sal_uInt16 nIns
= GetSortListPos( rColEntry
, rRect
.Top(), true );
89 rColEntry
.insert( rColEntry
.begin() + nIns
, pEntry
);
91 SvxIconChoiceCtrlEntryPtrVec
& rRowEntry
= (*xRows
)[nY
];
92 nIns
= GetSortListPos( rRowEntry
, rRect
.Left(), false );
93 rRowEntry
.insert( rRowEntry
.begin() + nIns
, pEntry
);
103 void IcnCursor_Impl::Clear()
115 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::SearchCol(sal_uInt16 nCol
, sal_uInt16 nTop
, sal_uInt16 nBottom
,
116 sal_uInt16
, bool bDown
, bool bSimple
)
118 DBG_ASSERT(pCurEntry
, "SearchCol: No reference entry");
119 IconChoiceMap::iterator mapIt
= xColumns
->find( nCol
);
120 if ( mapIt
== xColumns
->end() )
122 SvxIconChoiceCtrlEntryPtrVec
const & rList
= mapIt
->second
;
123 const sal_uInt16 nCount
= rList
.size();
127 const Rectangle
& rRefRect
= pView
->GetEntryBoundRect(pCurEntry
);
131 SvxIconChoiceCtrlEntryPtrVec::const_iterator it
= std::find( rList
.begin(), rList
.end(), pCurEntry
);
133 assert(it
!= rList
.end()); //Entry not in Col-List
134 if (it
== rList
.end())
139 while( ++it
!= rList
.end() )
141 SvxIconChoiceCtrlEntry
* pEntry
= *it
;
142 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
143 if( rRect
.Top() > rRefRect
.Top() )
150 SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator
it2(it
);
151 while (it2
!= rList
.rend())
153 SvxIconChoiceCtrlEntry
* pEntry
= *it2
;
154 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
155 if( rRect
.Top() < rRefRect
.Top() )
165 sal_uInt16 nTemp
= nTop
;
169 long nMinDistance
= LONG_MAX
;
170 SvxIconChoiceCtrlEntry
* pResult
= 0;
171 for( sal_uInt16 nCur
= 0; nCur
< nCount
; nCur
++ )
173 SvxIconChoiceCtrlEntry
* pEntry
= rList
[ nCur
];
174 if( pEntry
!= pCurEntry
)
176 sal_uInt16 nY
= pEntry
->nY
;
177 if( nY
>= nTop
&& nY
<= nBottom
)
179 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
180 long nDistance
= rRect
.Top() - rRefRect
.Top();
183 if( nDistance
&& nDistance
< nMinDistance
)
185 nMinDistance
= nDistance
;
194 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::SearchRow(sal_uInt16 nRow
, sal_uInt16 nLeft
, sal_uInt16 nRight
,
195 sal_uInt16
, bool bRight
, bool bSimple
)
197 DBG_ASSERT(pCurEntry
,"SearchRow: No reference entry");
198 IconChoiceMap::iterator mapIt
= xRows
->find( nRow
);
199 if ( mapIt
== xRows
->end() )
201 SvxIconChoiceCtrlEntryPtrVec
const & rList
= mapIt
->second
;
202 const sal_uInt16 nCount
= rList
.size();
206 const Rectangle
& rRefRect
= pView
->GetEntryBoundRect(pCurEntry
);
210 SvxIconChoiceCtrlEntryPtrVec::const_iterator it
= std::find( rList
.begin(), rList
.end(), pCurEntry
);
212 assert(it
!= rList
.end()); //Entry not in Row-List
213 if (it
== rList
.end())
218 while( ++it
!= rList
.end() )
220 SvxIconChoiceCtrlEntry
* pEntry
= *it
;
221 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
222 if( rRect
.Left() > rRefRect
.Left() )
229 SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator
it2(it
);
230 while (it2
!= rList
.rend())
232 SvxIconChoiceCtrlEntry
* pEntry
= *it2
;
233 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
234 if( rRect
.Left() < rRefRect
.Left() )
244 sal_uInt16 nTemp
= nRight
;
248 long nMinDistance
= LONG_MAX
;
249 SvxIconChoiceCtrlEntry
* pResult
= 0;
250 for( sal_uInt16 nCur
= 0; nCur
< nCount
; nCur
++ )
252 SvxIconChoiceCtrlEntry
* pEntry
= rList
[ nCur
];
253 if( pEntry
!= pCurEntry
)
255 sal_uInt16 nX
= pEntry
->nX
;
256 if( nX
>= nLeft
&& nX
<= nRight
)
258 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
259 long nDistance
= rRect
.Left() - rRefRect
.Left();
262 if( nDistance
&& nDistance
< nMinDistance
)
264 nMinDistance
= nDistance
;
276 Searches, starting from the passed value, the next entry to the left/to the
277 right. Example for bRight = sal_True:
282 S 1 1 1 ====> search direction
287 S : starting position
288 1 : first searched rectangle
289 a,b,c : 2nd, 3rd, 4th searched rectangle
292 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoLeftRight( SvxIconChoiceCtrlEntry
* pCtrlEntry
, bool bRight
)
294 SvxIconChoiceCtrlEntry
* pResult
;
295 pCurEntry
= pCtrlEntry
;
297 sal_uInt16 nY
= pCtrlEntry
->nY
;
298 sal_uInt16 nX
= pCtrlEntry
->nX
;
299 DBG_ASSERT(nY
< nRows
,"GoLeftRight:Bad column");
300 DBG_ASSERT(nX
< nCols
,"GoLeftRight:Bad row");
301 // neighbor in same row?
304 nY
, nX
, sal::static_int_cast
< sal_uInt16
>(nCols
-1), nX
, true, true );
306 pResult
= SearchRow( nY
, nX
,0, nX
, false, true );
312 long nColOffs
, nLastCol
;
321 nLastCol
= -1; // 0-1
324 sal_uInt16 nRowMin
= nY
;
325 sal_uInt16 nRowMax
= nY
;
328 SvxIconChoiceCtrlEntry
* pEntry
= SearchCol((sal_uInt16
)nCurCol
,nRowMin
,nRowMax
,nY
,true, false);
333 if( nRowMax
< (nRows
-1))
336 } while( nCurCol
!= nLastCol
);
340 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoPageUpDown( SvxIconChoiceCtrlEntry
* pStart
, bool bDown
)
342 if( pView
->IsAutoArrange() && !(pView
->nWinBits
& WB_ALIGN_TOP
) )
344 const long nPos
= (long)pView
->GetEntryListPos( pStart
);
345 long nEntriesInView
= (pView
->aOutputSize
.Height() / pView
->nGridDY
);
347 ((pView
->aOutputSize
.Width()+(pView
->nGridDX
/2)) / pView
->nGridDX
);
351 nNewPos
+= nEntriesInView
;
352 if( nNewPos
>= (long)pView
->aEntries
.size() )
353 nNewPos
= pView
->aEntries
.size() - 1;
357 nNewPos
-= nEntriesInView
;
361 if( nPos
!= nNewPos
)
362 return pView
->aEntries
[ (size_t)nNewPos
];
365 long nOpt
= pView
->GetEntryBoundRect( pStart
).Top();
368 nOpt
+= pView
->aOutputSize
.Height();
369 nOpt
-= pView
->nGridDY
;
373 nOpt
-= pView
->aOutputSize
.Height();
374 nOpt
+= pView
->nGridDY
;
379 long nPrevErr
= LONG_MAX
;
381 SvxIconChoiceCtrlEntry
* pPrev
= pStart
;
382 SvxIconChoiceCtrlEntry
* pNext
= GoUpDown( pStart
, bDown
);
385 long nCur
= pView
->GetEntryBoundRect( pNext
).Top();
386 long nErr
= nOpt
- nCur
;
389 if( nErr
> nPrevErr
)
393 pNext
= GoUpDown( pNext
, bDown
);
395 if( pPrev
!= pStart
)
400 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoUpDown( SvxIconChoiceCtrlEntry
* pCtrlEntry
, bool bDown
)
402 if( pView
->IsAutoArrange() && !(pView
->nWinBits
& WB_ALIGN_TOP
) )
404 sal_uLong nPos
= pView
->GetEntryListPos( pCtrlEntry
);
405 if( bDown
&& nPos
< (pView
->aEntries
.size() - 1) )
406 return pView
->aEntries
[ nPos
+ 1 ];
407 else if( !bDown
&& nPos
> 0 )
408 return pView
->aEntries
[ nPos
- 1 ];
412 SvxIconChoiceCtrlEntry
* pResult
;
413 pCurEntry
= pCtrlEntry
;
415 sal_uInt16 nY
= pCtrlEntry
->nY
;
416 sal_uInt16 nX
= pCtrlEntry
->nX
;
417 DBG_ASSERT(nY
<nRows
,"GoUpDown:Bad column");
418 DBG_ASSERT(nX
<nCols
,"GoUpDown:Bad row");
420 // neighbor in same column?
423 nX
, nY
, sal::static_int_cast
< sal_uInt16
>(nRows
-1), nY
, true, true );
425 pResult
= SearchCol( nX
, nY
,0, nY
, false, true );
431 long nRowOffs
, nLastRow
;
440 nLastRow
= -1; // 0-1
443 sal_uInt16 nColMin
= nX
;
444 sal_uInt16 nColMax
= nX
;
447 SvxIconChoiceCtrlEntry
* pEntry
= SearchRow((sal_uInt16
)nCurRow
,nColMin
,nColMax
,nX
,true, false);
452 if( nColMax
< (nCols
-1))
455 } while( nCurRow
!= nLastRow
);
459 void IcnCursor_Impl::SetDeltas()
461 const Size
& rSize
= pView
->aVirtOutputSize
;
462 nCols
= rSize
.Width() / pView
->nGridDX
;
465 nRows
= rSize
.Height() / pView
->nGridDY
;
466 if( (nRows
* pView
->nGridDY
) < rSize
.Height() )
471 nDeltaWidth
= (short)(rSize
.Width() / nCols
);
472 nDeltaHeight
= (short)(rSize
.Height() / nRows
);
476 DBG_WARNING("SetDeltas:Bad height");
481 DBG_WARNING("SetDeltas:Bad width");
485 void IcnCursor_Impl::CreateGridAjustData( IconChoiceMap
& rLists
, SvxIconChoiceCtrlEntry
* pRefEntry
)
489 sal_uInt16 nGridRows
= (sal_uInt16
)(pView
->aVirtOutputSize
.Height() / pView
->nGridDY
);
490 nGridRows
++; // because we round down later!
494 const size_t nCount
= pView
->aEntries
.size();
495 for( size_t nCur
= 0; nCur
< nCount
; nCur
++ )
497 SvxIconChoiceCtrlEntry
* pEntry
= pView
->aEntries
[ nCur
];
498 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
499 short nY
= (short)( ((rRect
.Top()+rRect
.Bottom())/2) / pView
->nGridDY
);
500 sal_uInt16 nIns
= GetSortListPos( rLists
[nY
], rRect
.Left(), false );
501 rLists
[ nY
].insert( rLists
[ nY
].begin() + nIns
, pEntry
);
506 // build a horizontal "tube" in the RefEntry line
507 // STOP AND THINK: maybe use bounding rectangle because of overlaps?
508 Rectangle
rRefRect( pView
->CalcBmpRect( pRefEntry
) );
509 //const Rectangle& rRefRect = pView->GetEntryBoundRect( pRefEntry );
510 short nRefRow
= (short)( ((rRefRect
.Top()+rRefRect
.Bottom())/2) / pView
->nGridDY
);
511 SvxIconChoiceCtrlEntryPtrVec
& rRow
= rLists
[0];
512 size_t nCount
= pView
->aEntries
.size();
513 for( size_t nCur
= 0; nCur
< nCount
; nCur
++ )
515 SvxIconChoiceCtrlEntry
* pEntry
= pView
->aEntries
[ nCur
];
516 Rectangle
rRect( pView
->CalcBmpRect(pEntry
) );
517 //const Rectangle& rRect = pView->GetEntryBoundRect( pEntry );
518 short nY
= (short)( ((rRect
.Top()+rRect
.Bottom())/2) / pView
->nGridDY
);
521 sal_uInt16 nIns
= GetSortListPos( rRow
, rRect
.Left(), false );
522 rRow
.insert( rRow
.begin() + nIns
, pEntry
);
529 void IcnCursor_Impl::DestroyGridAdjustData( IconChoiceMap
& rLists
)
534 IcnGridMap_Impl::IcnGridMap_Impl(SvxIconChoiceCtrl_Impl
* pView
)
542 IcnGridMap_Impl::~IcnGridMap_Impl()
544 delete[] _pGridMap
, _pGridMap
=0;
547 void IcnGridMap_Impl::Expand()
553 sal_uInt16 nNewGridRows
= _nGridRows
;
554 sal_uInt16 nNewGridCols
= _nGridCols
;
555 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
560 size_t nNewCellCount
= static_cast<size_t>(nNewGridRows
) * nNewGridCols
;
561 bool* pNewGridMap
= new bool[nNewCellCount
];
562 memset(pNewGridMap
, 0, nNewCellCount
* sizeof(bool));
563 size_t nOldCellCount
= static_cast<size_t>(_nGridRows
) * _nGridCols
;
564 memcpy(pNewGridMap
, _pGridMap
, nOldCellCount
* sizeof(bool));
566 _pGridMap
= pNewGridMap
;
567 _nGridRows
= nNewGridRows
;
568 _nGridCols
= nNewGridCols
;
572 void IcnGridMap_Impl::Create_Impl()
574 DBG_ASSERT(!_pGridMap
,"Unnecessary call to IcnGridMap_Impl::Create_Impl()");
577 GetMinMapSize( _nGridCols
, _nGridRows
);
578 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
579 _nGridRows
+= 50; // avoid resize of gridmap too often
583 size_t nCellCount
= static_cast<size_t>(_nGridRows
) * _nGridCols
;
584 _pGridMap
= new bool[nCellCount
];
585 memset(_pGridMap
, 0, nCellCount
* sizeof(bool));
587 const size_t nCount
= _pView
->aEntries
.size();
588 for( size_t nCur
=0; nCur
< nCount
; nCur
++ )
589 OccupyGrids( _pView
->aEntries
[ nCur
] );
592 void IcnGridMap_Impl::GetMinMapSize( sal_uInt16
& rDX
, sal_uInt16
& rDY
) const
595 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
597 // The view grows in vertical direction. Its max. width is _pView->nMaxVirtWidth
598 nX
= _pView
->nMaxVirtWidth
;
600 nX
= _pView
->pView
->GetOutputSizePixel().Width();
601 if( !(_pView
->nFlags
& F_ARRANGING
) )
602 nX
-= _pView
->nVerSBarWidth
;
604 nY
= _pView
->aVirtOutputSize
.Height();
608 // The view grows in horizontal direction. Its max. height is _pView->nMaxVirtHeight
609 nY
= _pView
->nMaxVirtHeight
;
611 nY
= _pView
->pView
->GetOutputSizePixel().Height();
612 if( !(_pView
->nFlags
& F_ARRANGING
) )
613 nY
-= _pView
->nHorSBarHeight
;
614 nX
= _pView
->aVirtOutputSize
.Width();
618 nX
= DEFAULT_MAX_VIRT_WIDTH
;
620 nY
= DEFAULT_MAX_VIRT_HEIGHT
;
622 long nDX
= nX
/ _pView
->nGridDX
;
623 long nDY
= nY
/ _pView
->nGridDY
;
630 rDX
= (sal_uInt16
)nDX
;
631 rDY
= (sal_uInt16
)nDY
;
634 GridId
IcnGridMap_Impl::GetGrid( sal_uInt16 nGridX
, sal_uInt16 nGridY
)
637 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
638 return nGridX
+ ( static_cast<GridId
>(nGridY
) * _nGridCols
);
640 return nGridY
+ ( static_cast<GridId
>(nGridX
) * _nGridRows
);
643 GridId
IcnGridMap_Impl::GetGrid( const Point
& rDocPos
, bool* pbClipped
)
647 long nX
= rDocPos
.X();
648 long nY
= rDocPos
.Y();
649 nX
-= LROFFS_WINBORDER
;
650 nY
-= TBOFFS_WINBORDER
;
651 nX
/= _pView
->nGridDX
;
652 nY
/= _pView
->nGridDY
;
653 bool bClipped
= false;
654 if( nX
>= _nGridCols
)
659 if( nY
>= _nGridRows
)
664 GridId nId
= GetGrid( (sal_uInt16
)nX
, (sal_uInt16
)nY
);
666 *pbClipped
= bClipped
;
667 DBG_ASSERT(nId
<(sal_uLong
)(_nGridCols
*_nGridRows
),"GetGrid failed");
671 Rectangle
IcnGridMap_Impl::GetGridRect( GridId nId
)
674 sal_uInt16 nGridX
, nGridY
;
675 GetGridCoord( nId
, nGridX
, nGridY
);
676 const long nLeft
= nGridX
* _pView
->nGridDX
+ LROFFS_WINBORDER
;
677 const long nTop
= nGridY
* _pView
->nGridDY
+ TBOFFS_WINBORDER
;
680 nLeft
+ _pView
->nGridDX
,
681 nTop
+ _pView
->nGridDY
);
684 GridId
IcnGridMap_Impl::GetUnoccupiedGrid( bool bOccupyFound
)
687 sal_uLong nStart
= 0;
688 bool bExpanded
= false;
692 const sal_uLong nCount
= (sal_uInt16
)(_nGridCols
* _nGridRows
);
693 for( sal_uLong nCur
= nStart
; nCur
< nCount
; nCur
++ )
695 if( !_pGridMap
[ nCur
] )
698 _pGridMap
[ nCur
] = true;
702 DBG_ASSERT(!bExpanded
,"ExpandGrid failed");
704 return 0; // prevent never ending loop
711 // An entry only means that there's a GridRect lying under its center. This
712 // variant is much faster than allocating via the bounding rectangle but can
713 // lead to small overlaps.
714 void IcnGridMap_Impl::OccupyGrids( const SvxIconChoiceCtrlEntry
* pEntry
, bool bOccupy
)
716 if( !_pGridMap
|| !SvxIconChoiceCtrl_Impl::IsBoundingRectValid( pEntry
->aRect
))
718 OccupyGrid( GetGrid( pEntry
->aRect
.Center()), bOccupy
);
721 void IcnGridMap_Impl::Clear()
725 delete[] _pGridMap
, _pGridMap
=0;
728 _aLastOccupiedGrid
.SetEmpty();
732 sal_uLong
IcnGridMap_Impl::GetGridCount( const Size
& rSizePixel
, sal_uInt16 nDX
, sal_uInt16 nDY
)
734 long ndx
= (rSizePixel
.Width() - LROFFS_WINBORDER
) / nDX
;
735 if( ndx
< 0 ) ndx
*= -1;
736 long ndy
= (rSizePixel
.Height() - TBOFFS_WINBORDER
) / nDY
;
737 if( ndy
< 0 ) ndy
*= -1;
738 return (sal_uLong
)(ndx
* ndy
);
741 void IcnGridMap_Impl::OutputSizeChanged()
745 sal_uInt16 nCols
, nRows
;
746 GetMinMapSize( nCols
, nRows
);
747 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
749 if( nCols
!= _nGridCols
)
751 else if( nRows
>= _nGridRows
)
756 if( nRows
!= _nGridRows
)
758 else if( nCols
>= _nGridCols
)
764 // Independently of the view's alignment (TOP or LEFT), the gridmap
765 // should contain the data in a continuous region, to make it possible
766 // to copy the whole block if the gridmap needs to be expanded.
767 void IcnGridMap_Impl::GetGridCoord( GridId nId
, sal_uInt16
& rGridX
, sal_uInt16
& rGridY
)
770 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
772 rGridX
= (sal_uInt16
)(nId
% _nGridCols
);
773 rGridY
= (sal_uInt16
)(nId
/ _nGridCols
);
777 rGridX
= (sal_uInt16
)(nId
/ _nGridRows
);
778 rGridY
= (sal_uInt16
)(nId
% _nGridRows
);
784 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */