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(pColumns
==0&&pRows
==0,"ImplCreate: Not cleared");
69 pColumns
.reset(new IconChoiceMap
);
70 pRows
.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
= (*pColumns
)[nX
];
88 sal_uInt16 nIns
= GetSortListPos( rColEntry
, rRect
.Top(), sal_True
);
89 rColEntry
.insert( rColEntry
.begin() + nIns
, pEntry
);
91 SvxIconChoiceCtrlEntryPtrVec
& rRowEntry
= (*pRows
)[nY
];
92 nIns
= GetSortListPos( rRowEntry
, rRect
.Left(), sal_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
= pColumns
->find( nCol
);
120 if ( mapIt
== pColumns
->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
);
132 DBG_ASSERT( it
!= rList
.end(), "Entry not in Col-List" );
135 while( ++it
!= rList
.end() )
137 SvxIconChoiceCtrlEntry
* pEntry
= *it
;
138 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
139 if( rRect
.Top() > rRefRect
.Top() )
146 SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator
it2(it
);
147 while (it2
!= rList
.rend())
149 SvxIconChoiceCtrlEntry
* pEntry
= *it2
;
150 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
151 if( rRect
.Top() < rRefRect
.Top() )
161 sal_uInt16 nTemp
= nTop
;
165 long nMinDistance
= LONG_MAX
;
166 SvxIconChoiceCtrlEntry
* pResult
= 0;
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 Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
176 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 sal_uInt16
, bool bRight
, bool bSimple
)
193 DBG_ASSERT(pCurEntry
,"SearchRow: No reference entry");
194 IconChoiceMap::iterator mapIt
= pRows
->find( nRow
);
195 if ( mapIt
== pRows
->end() )
197 SvxIconChoiceCtrlEntryPtrVec
const & rList
= mapIt
->second
;
198 const sal_uInt16 nCount
= rList
.size();
202 const Rectangle
& rRefRect
= pView
->GetEntryBoundRect(pCurEntry
);
206 SvxIconChoiceCtrlEntryPtrVec::const_iterator it
= std::find( rList
.begin(), rList
.end(), pCurEntry
);
207 DBG_ASSERT( it
!= rList
.end(), "Entry not in Row-List" );
210 while( ++it
!= rList
.end() )
212 SvxIconChoiceCtrlEntry
* pEntry
= *it
;
213 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
214 if( rRect
.Left() > rRefRect
.Left() )
221 SvxIconChoiceCtrlEntryPtrVec::const_reverse_iterator
it2(it
);
222 while (it2
!= rList
.rend())
224 SvxIconChoiceCtrlEntry
* pEntry
= *it2
;
225 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
226 if( rRect
.Left() < rRefRect
.Left() )
236 sal_uInt16 nTemp
= nRight
;
240 long nMinDistance
= LONG_MAX
;
241 SvxIconChoiceCtrlEntry
* pResult
= 0;
242 for( sal_uInt16 nCur
= 0; nCur
< nCount
; nCur
++ )
244 SvxIconChoiceCtrlEntry
* pEntry
= rList
[ nCur
];
245 if( pEntry
!= pCurEntry
)
247 sal_uInt16 nX
= pEntry
->nX
;
248 if( nX
>= nLeft
&& nX
<= nRight
)
250 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
251 long nDistance
= rRect
.Left() - rRefRect
.Left();
254 if( nDistance
&& nDistance
< nMinDistance
)
256 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
, sal_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), nX
, sal_True
, sal_True
);
298 pResult
= SearchRow( nY
, nX
,0, nX
, sal_False
, sal_True
);
304 long nColOffs
, nLastCol
;
313 nLastCol
= -1; // 0-1
316 sal_uInt16 nRowMin
= nY
;
317 sal_uInt16 nRowMax
= nY
;
320 SvxIconChoiceCtrlEntry
* pEntry
= SearchCol((sal_uInt16
)nCurCol
,nRowMin
,nRowMax
,nY
,sal_True
, sal_False
);
325 if( nRowMax
< (nRows
-1))
328 } while( nCurCol
!= nLastCol
);
332 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoPageUpDown( SvxIconChoiceCtrlEntry
* pStart
, sal_Bool bDown
)
334 if( pView
->IsAutoArrange() && !(pView
->nWinBits
& WB_ALIGN_TOP
) )
336 const long nPos
= (long)pView
->GetEntryListPos( pStart
);
337 long nEntriesInView
= (pView
->aOutputSize
.Height() / pView
->nGridDY
);
339 ((pView
->aOutputSize
.Width()+(pView
->nGridDX
/2)) / pView
->nGridDX
);
343 nNewPos
+= nEntriesInView
;
344 if( nNewPos
>= (long)pView
->aEntries
.size() )
345 nNewPos
= pView
->aEntries
.size() - 1;
349 nNewPos
-= nEntriesInView
;
353 if( nPos
!= nNewPos
)
354 return pView
->aEntries
[ (size_t)nNewPos
];
357 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 long nPrevErr
= LONG_MAX
;
373 SvxIconChoiceCtrlEntry
* pPrev
= pStart
;
374 SvxIconChoiceCtrlEntry
* pNext
= GoUpDown( pStart
, bDown
);
377 long nCur
= pView
->GetEntryBoundRect( pNext
).Top();
378 long nErr
= nOpt
- nCur
;
381 if( nErr
> nPrevErr
)
385 pNext
= GoUpDown( pNext
, bDown
);
387 if( pPrev
!= pStart
)
392 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoUpDown( SvxIconChoiceCtrlEntry
* pCtrlEntry
, sal_Bool bDown
)
394 if( pView
->IsAutoArrange() && !(pView
->nWinBits
& WB_ALIGN_TOP
) )
396 sal_uLong nPos
= pView
->GetEntryListPos( pCtrlEntry
);
397 if( bDown
&& nPos
< (pView
->aEntries
.size() - 1) )
398 return pView
->aEntries
[ nPos
+ 1 ];
399 else if( !bDown
&& nPos
> 0 )
400 return pView
->aEntries
[ nPos
- 1 ];
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), nY
, sal_True
, sal_True
);
417 pResult
= SearchCol( nX
, nY
,0, nY
, sal_False
, sal_True
);
423 long nRowOffs
, nLastRow
;
432 nLastRow
= -1; // 0-1
435 sal_uInt16 nColMin
= nX
;
436 sal_uInt16 nColMax
= nX
;
439 SvxIconChoiceCtrlEntry
* pEntry
= SearchRow((sal_uInt16
)nCurRow
,nColMin
,nColMax
,nX
,sal_True
, sal_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
= (short)(rSize
.Width() / nCols
);
464 nDeltaHeight
= (short)(rSize
.Height() / nRows
);
468 DBG_WARNING("SetDeltas:Bad height");
473 DBG_WARNING("SetDeltas:Bad width");
477 void IcnCursor_Impl::CreateGridAjustData( IconChoiceMap
& rLists
, SvxIconChoiceCtrlEntry
* pRefEntry
)
481 sal_uInt16 nGridRows
= (sal_uInt16
)(pView
->aVirtOutputSize
.Height() / pView
->nGridDY
);
482 nGridRows
++; // because we round down later!
486 const size_t nCount
= pView
->aEntries
.size();
487 for( size_t nCur
= 0; nCur
< nCount
; nCur
++ )
489 SvxIconChoiceCtrlEntry
* pEntry
= pView
->aEntries
[ nCur
];
490 const Rectangle
& rRect
= pView
->GetEntryBoundRect( pEntry
);
491 short nY
= (short)( ((rRect
.Top()+rRect
.Bottom())/2) / pView
->nGridDY
);
492 sal_uInt16 nIns
= GetSortListPos( rLists
[nY
], rRect
.Left(), sal_False
);
493 rLists
[ nY
].insert( rLists
[ nY
].begin() + nIns
, pEntry
);
498 // build a horizontal "tube" in the RefEntry line
499 // STOP AND THINK: maybe use bounding rectangle because of overlaps?
500 Rectangle
rRefRect( pView
->CalcBmpRect( pRefEntry
) );
501 //const Rectangle& rRefRect = pView->GetEntryBoundRect( pRefEntry );
502 short nRefRow
= (short)( ((rRefRect
.Top()+rRefRect
.Bottom())/2) / pView
->nGridDY
);
503 SvxIconChoiceCtrlEntryPtrVec
& rRow
= rLists
[0];
504 size_t nCount
= pView
->aEntries
.size();
505 for( size_t nCur
= 0; nCur
< nCount
; nCur
++ )
507 SvxIconChoiceCtrlEntry
* pEntry
= pView
->aEntries
[ nCur
];
508 Rectangle
rRect( pView
->CalcBmpRect(pEntry
) );
509 //const Rectangle& rRect = pView->GetEntryBoundRect( pEntry );
510 short nY
= (short)( ((rRect
.Top()+rRect
.Bottom())/2) / pView
->nGridDY
);
513 sal_uInt16 nIns
= GetSortListPos( rRow
, rRect
.Left(), sal_False
);
514 rRow
.insert( rRow
.begin() + nIns
, pEntry
);
521 void IcnCursor_Impl::DestroyGridAdjustData( IconChoiceMap
& rLists
)
526 IcnGridMap_Impl::IcnGridMap_Impl(SvxIconChoiceCtrl_Impl
* pView
)
534 IcnGridMap_Impl::~IcnGridMap_Impl()
536 delete[] _pGridMap
, _pGridMap
=0;
539 void IcnGridMap_Impl::Expand()
545 sal_uInt16 nNewGridRows
= _nGridRows
;
546 sal_uInt16 nNewGridCols
= _nGridCols
;
547 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
552 sal_Bool
* pNewGridMap
= new sal_Bool
[nNewGridRows
*nNewGridCols
];
553 memset( pNewGridMap
, 0, nNewGridRows
* nNewGridCols
* sizeof(sal_Bool
) );
554 memcpy( pNewGridMap
, _pGridMap
, _nGridRows
* _nGridCols
* sizeof(sal_Bool
) );
556 _pGridMap
= pNewGridMap
;
557 _nGridRows
= nNewGridRows
;
558 _nGridCols
= nNewGridCols
;
562 void IcnGridMap_Impl::Create_Impl()
564 DBG_ASSERT(!_pGridMap
,"Unnecessary call to IcnGridMap_Impl::Create_Impl()");
567 GetMinMapSize( _nGridCols
, _nGridRows
);
568 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
569 _nGridRows
+= 50; // avoid resize of gridmap too often
573 _pGridMap
= new sal_Bool
[ _nGridRows
* _nGridCols
];
574 memset( (void*)_pGridMap
, 0, _nGridRows
* _nGridCols
);
576 const size_t nCount
= _pView
->aEntries
.size();
577 for( size_t nCur
=0; nCur
< nCount
; nCur
++ )
578 OccupyGrids( _pView
->aEntries
[ nCur
] );
581 void IcnGridMap_Impl::GetMinMapSize( sal_uInt16
& rDX
, sal_uInt16
& rDY
) const
584 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
586 // The view grows in vertical direction. Its max. width is _pView->nMaxVirtWidth
587 nX
= _pView
->nMaxVirtWidth
;
589 nX
= _pView
->pView
->GetOutputSizePixel().Width();
590 if( !(_pView
->nFlags
& F_ARRANGING
) )
591 nX
-= _pView
->nVerSBarWidth
;
593 nY
= _pView
->aVirtOutputSize
.Height();
597 // The view grows in horizontal direction. Its max. height is _pView->nMaxVirtHeight
598 nY
= _pView
->nMaxVirtHeight
;
600 nY
= _pView
->pView
->GetOutputSizePixel().Height();
601 if( !(_pView
->nFlags
& F_ARRANGING
) )
602 nY
-= _pView
->nHorSBarHeight
;
603 nX
= _pView
->aVirtOutputSize
.Width();
607 nX
= DEFAULT_MAX_VIRT_WIDTH
;
609 nY
= DEFAULT_MAX_VIRT_HEIGHT
;
611 long nDX
= nX
/ _pView
->nGridDX
;
612 long nDY
= nY
/ _pView
->nGridDY
;
619 rDX
= (sal_uInt16
)nDX
;
620 rDY
= (sal_uInt16
)nDY
;
623 GridId
IcnGridMap_Impl::GetGrid( sal_uInt16 nGridX
, sal_uInt16 nGridY
)
626 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
627 return nGridX
+ ( nGridY
* _nGridCols
);
629 return nGridY
+ ( nGridX
* _nGridRows
);
632 GridId
IcnGridMap_Impl::GetGrid( const Point
& rDocPos
, sal_Bool
* pbClipped
)
636 long nX
= rDocPos
.X();
637 long nY
= rDocPos
.Y();
638 nX
-= LROFFS_WINBORDER
;
639 nY
-= TBOFFS_WINBORDER
;
640 nX
/= _pView
->nGridDX
;
641 nY
/= _pView
->nGridDY
;
642 sal_Bool bClipped
= sal_False
;
643 if( nX
>= _nGridCols
)
648 if( nY
>= _nGridRows
)
653 GridId nId
= GetGrid( (sal_uInt16
)nX
, (sal_uInt16
)nY
);
655 *pbClipped
= bClipped
;
656 DBG_ASSERT(nId
<(sal_uLong
)(_nGridCols
*_nGridRows
),"GetGrid failed");
660 Rectangle
IcnGridMap_Impl::GetGridRect( GridId nId
)
663 sal_uInt16 nGridX
, nGridY
;
664 GetGridCoord( nId
, nGridX
, nGridY
);
665 const long nLeft
= nGridX
* _pView
->nGridDX
+ LROFFS_WINBORDER
;
666 const long nTop
= nGridY
* _pView
->nGridDY
+ TBOFFS_WINBORDER
;
669 nLeft
+ _pView
->nGridDX
,
670 nTop
+ _pView
->nGridDY
);
673 GridId
IcnGridMap_Impl::GetUnoccupiedGrid( sal_Bool bOccupyFound
)
676 sal_uLong nStart
= 0;
677 sal_Bool bExpanded
= sal_False
;
681 const sal_uLong nCount
= (sal_uInt16
)(_nGridCols
* _nGridRows
);
682 for( sal_uLong nCur
= nStart
; nCur
< nCount
; nCur
++ )
684 if( !_pGridMap
[ nCur
] )
687 _pGridMap
[ nCur
] = sal_True
;
691 DBG_ASSERT(!bExpanded
,"ExpandGrid failed");
693 return 0; // prevent never ending loop
694 bExpanded
= sal_True
;
700 // An entry only means that there's a GridRect lying under its center. This
701 // variant is much faster than allocating via the bounding rectangle but can
702 // lead to small overlaps.
703 void IcnGridMap_Impl::OccupyGrids( const SvxIconChoiceCtrlEntry
* pEntry
, sal_Bool bOccupy
)
705 if( !_pGridMap
|| !_pView
->IsBoundingRectValid( pEntry
->aRect
))
707 OccupyGrid( GetGrid( pEntry
->aRect
.Center()), bOccupy
);
710 void IcnGridMap_Impl::Clear()
714 delete[] _pGridMap
, _pGridMap
=0;
717 _aLastOccupiedGrid
.SetEmpty();
721 sal_uLong
IcnGridMap_Impl::GetGridCount( const Size
& rSizePixel
, sal_uInt16 nDX
, sal_uInt16 nDY
)
723 long ndx
= (rSizePixel
.Width() - LROFFS_WINBORDER
) / nDX
;
724 if( ndx
< 0 ) ndx
*= -1;
725 long ndy
= (rSizePixel
.Height() - TBOFFS_WINBORDER
) / nDY
;
726 if( ndy
< 0 ) ndy
*= -1;
727 return (sal_uLong
)(ndx
* ndy
);
730 void IcnGridMap_Impl::OutputSizeChanged()
734 sal_uInt16 nCols
, nRows
;
735 GetMinMapSize( nCols
, nRows
);
736 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
738 if( nCols
!= _nGridCols
)
740 else if( nRows
>= _nGridRows
)
745 if( nRows
!= _nGridRows
)
747 else if( nCols
>= _nGridCols
)
753 // Independently of the view's alignment (TOP or LEFT), the gridmap
754 // should contain the data in a continuous region, to make it possible
755 // to copy the whole block if the gridmap needs to be expanded.
756 void IcnGridMap_Impl::GetGridCoord( GridId nId
, sal_uInt16
& rGridX
, sal_uInt16
& rGridY
)
759 if( _pView
->nWinBits
& WB_ALIGN_TOP
)
761 rGridX
= (sal_uInt16
)(nId
% _nGridCols
);
762 rGridY
= (sal_uInt16
)(nId
/ _nGridCols
);
766 rGridX
= (sal_uInt16
)(nId
/ _nGridRows
);
767 rGridY
= (sal_uInt16
)(nId
% _nGridRows
);
773 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */