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 const tools::Long nPos
= static_cast<tools::Long
>(pView
->GetEntryListPos( pStart
));
335 tools::Long nEntriesInView
= pView
->aOutputSize
.Height() / pView
->nGridDY
;
337 ((pView
->aOutputSize
.Width()+(pView
->nGridDX
/2)) / pView
->nGridDX
);
338 tools::Long nNewPos
= nPos
;
341 nNewPos
+= nEntriesInView
;
342 if( nNewPos
>= static_cast<tools::Long
>(pView
->maEntries
.size()) )
343 nNewPos
= pView
->maEntries
.size() - 1;
347 nNewPos
-= nEntriesInView
;
351 if( nPos
!= nNewPos
)
352 return pView
->maEntries
[ static_cast<size_t>(nNewPos
) ].get();
356 SvxIconChoiceCtrlEntry
* IcnCursor_Impl::GoUpDown( SvxIconChoiceCtrlEntry
* pCtrlEntry
, bool bDown
)
358 sal_uLong nPos
= pView
->GetEntryListPos( pCtrlEntry
);
359 if( bDown
&& nPos
< (pView
->maEntries
.size() - 1) )
360 return pView
->maEntries
[ nPos
+ 1 ].get();
361 else if( !bDown
&& nPos
> 0 )
362 return pView
->maEntries
[ nPos
- 1 ].get();
366 void IcnCursor_Impl::SetDeltas()
368 const Size
& rSize
= pView
->aVirtOutputSize
;
369 nCols
= rSize
.Width() / pView
->nGridDX
;
372 nRows
= rSize
.Height() / pView
->nGridDY
;
373 if( (nRows
* pView
->nGridDY
) < rSize
.Height() )
378 nDeltaWidth
= static_cast<short>(rSize
.Width() / nCols
);
379 nDeltaHeight
= static_cast<short>(rSize
.Height() / nRows
);
383 SAL_INFO("vcl", "SetDeltas:Bad height");
388 SAL_INFO("vcl", "SetDeltas:Bad width");
392 IcnGridMap_Impl::IcnGridMap_Impl(SvxIconChoiceCtrl_Impl
* pView
)
393 : _pView(pView
), _nGridCols(0), _nGridRows(0)
397 IcnGridMap_Impl::~IcnGridMap_Impl()
401 void IcnGridMap_Impl::Expand()
407 sal_uInt16 nNewGridRows
= _nGridRows
;
408 sal_uInt16 nNewGridCols
= _nGridCols
;
411 size_t nNewCellCount
= static_cast<size_t>(nNewGridRows
) * nNewGridCols
;
412 bool* pNewGridMap
= new bool[nNewCellCount
];
413 size_t nOldCellCount
= static_cast<size_t>(_nGridRows
) * _nGridCols
;
414 memcpy(pNewGridMap
, _pGridMap
.get(), nOldCellCount
* sizeof(bool));
415 memset(pNewGridMap
+ nOldCellCount
, 0, (nNewCellCount
-nOldCellCount
) * sizeof(bool));
416 _pGridMap
.reset( pNewGridMap
);
417 _nGridRows
= nNewGridRows
;
418 _nGridCols
= nNewGridCols
;
422 void IcnGridMap_Impl::Create_Impl()
424 DBG_ASSERT(!_pGridMap
,"Unnecessary call to IcnGridMap_Impl::Create_Impl()");
427 GetMinMapSize( _nGridCols
, _nGridRows
);
430 size_t nCellCount
= static_cast<size_t>(_nGridRows
) * _nGridCols
;
431 _pGridMap
.reset( new bool[nCellCount
] );
432 memset(_pGridMap
.get(), 0, nCellCount
* sizeof(bool));
434 const size_t nCount
= _pView
->maEntries
.size();
435 for( size_t nCur
=0; nCur
< nCount
; nCur
++ )
436 OccupyGrids( _pView
->maEntries
[ nCur
].get() );
439 void IcnGridMap_Impl::GetMinMapSize( sal_uInt16
& rDX
, sal_uInt16
& rDY
) const
441 // The view grows in horizontal direction. Its max. height is _pView->nMaxVirtHeight
442 tools::Long nY
= _pView
->nMaxVirtHeight
;
444 nY
= _pView
->pView
->GetOutputSizePixel().Height();
445 if( !(_pView
->nFlags
& IconChoiceFlags::Arranging
) )
446 nY
-= _pView
->nHorSBarHeight
;
448 tools::Long nX
= _pView
->aVirtOutputSize
.Width();
451 nX
= DEFAULT_MAX_VIRT_WIDTH
;
453 nY
= DEFAULT_MAX_VIRT_HEIGHT
;
455 tools::Long nDX
= nX
/ _pView
->nGridDX
;
456 tools::Long nDY
= nY
/ _pView
->nGridDY
;
463 rDX
= static_cast<sal_uInt16
>(nDX
);
464 rDY
= static_cast<sal_uInt16
>(nDY
);
467 GridId
IcnGridMap_Impl::GetGrid( sal_uInt16 nGridX
, sal_uInt16 nGridY
)
470 return nGridY
+ ( static_cast<GridId
>(nGridX
) * _nGridRows
);
473 GridId
IcnGridMap_Impl::GetGrid( const Point
& rDocPos
)
477 tools::Long nX
= rDocPos
.X();
478 tools::Long nY
= rDocPos
.Y();
479 nX
-= LROFFS_WINBORDER
;
480 nY
-= TBOFFS_WINBORDER
;
481 nX
/= _pView
->nGridDX
;
482 nY
/= _pView
->nGridDY
;
483 if( nX
>= _nGridCols
)
487 if( nY
>= _nGridRows
)
491 GridId nId
= GetGrid( static_cast<sal_uInt16
>(nX
), static_cast<sal_uInt16
>(nY
) );
492 DBG_ASSERT(nId
<o3tl::make_unsigned(_nGridCols
*_nGridRows
),"GetGrid failed");
496 tools::Rectangle
IcnGridMap_Impl::GetGridRect( GridId nId
)
499 sal_uInt16 nGridX
, nGridY
;
500 GetGridCoord( nId
, nGridX
, nGridY
);
501 const tools::Long nLeft
= nGridX
* _pView
->nGridDX
+ LROFFS_WINBORDER
;
502 const tools::Long nTop
= nGridY
* _pView
->nGridDY
+ TBOFFS_WINBORDER
;
503 return tools::Rectangle(
505 nLeft
+ _pView
->nGridDX
,
506 nTop
+ _pView
->nGridDY
);
509 GridId
IcnGridMap_Impl::GetUnoccupiedGrid()
512 sal_uLong nStart
= 0;
513 bool bExpanded
= false;
517 const sal_uLong nCount
= static_cast<sal_uInt16
>(_nGridCols
* _nGridRows
);
518 for( sal_uLong nCur
= nStart
; nCur
< nCount
; nCur
++ )
520 if( !_pGridMap
[ nCur
] )
522 _pGridMap
[ nCur
] = true;
523 return static_cast<GridId
>(nCur
);
526 DBG_ASSERT(!bExpanded
,"ExpandGrid failed");
528 return 0; // prevent never ending loop
535 // An entry only means that there's a GridRect lying under its center. This
536 // variant is much faster than allocating via the bounding rectangle but can
537 // lead to small overlaps.
538 void IcnGridMap_Impl::OccupyGrids( const SvxIconChoiceCtrlEntry
* pEntry
)
540 if( !_pGridMap
|| !SvxIconChoiceCtrl_Impl::IsBoundingRectValid( pEntry
->aRect
))
542 OccupyGrid( GetGrid( pEntry
->aRect
.Center()) );
545 void IcnGridMap_Impl::Clear()
552 _aLastOccupiedGrid
.SetEmpty();
556 sal_uLong
IcnGridMap_Impl::GetGridCount( const Size
& rSizePixel
, sal_uInt16 nDX
, sal_uInt16 nDY
)
558 tools::Long ndx
= (rSizePixel
.Width() - LROFFS_WINBORDER
) / nDX
;
559 if( ndx
< 0 ) ndx
*= -1;
560 tools::Long ndy
= (rSizePixel
.Height() - TBOFFS_WINBORDER
) / nDY
;
561 if( ndy
< 0 ) ndy
*= -1;
562 return static_cast<sal_uLong
>(ndx
* ndy
);
565 void IcnGridMap_Impl::OutputSizeChanged()
570 sal_uInt16 nCols
, nRows
;
571 GetMinMapSize( nCols
, nRows
);
572 if( nRows
!= _nGridRows
)
574 else if( nCols
>= _nGridCols
)
578 // the gridmap should contain the data in a continuous region, to make it possible
579 // to copy the whole block if the gridmap needs to be expanded.
580 void IcnGridMap_Impl::GetGridCoord( GridId nId
, sal_uInt16
& rGridX
, sal_uInt16
& rGridY
)
582 rGridX
= static_cast<sal_uInt16
>(nId
/ _nGridRows
);
583 rGridY
= static_cast<sal_uInt16
>(nId
% _nGridRows
);
587 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */