bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / control / imivctl1.cxx
blob770f8770a0ca2984a25b63efe86245d426e29bad
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <limits.h>
22 #include <osl/diagnose.h>
23 #include <tools/debug.hxx>
24 #include <vcl/wall.hxx>
25 #include <vcl/help.hxx>
26 #include <vcl/decoview.hxx>
27 #include <vcl/svapp.hxx>
28 #include <tools/poly.hxx>
29 #include <vcl/lineinfo.hxx>
30 #include <vcl/i18nhelp.hxx>
31 #include <vcl/mnemonic.hxx>
32 #include <vcl/settings.hxx>
33 #include <vcl/commandevent.hxx>
35 #include <vcl/ivctrl.hxx>
36 #include "imivctl.hxx"
38 #include <algorithm>
39 #include <memory>
40 #include <vcl/idle.hxx>
42 static constexpr auto DRAWTEXT_FLAGS_ICON =
43 DrawTextFlags::Center | DrawTextFlags::Top | DrawTextFlags::EndEllipsis |
44 DrawTextFlags::Clip | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak | DrawTextFlags::Mnemonic;
46 #define DRAWTEXT_FLAGS_SMALLICON (DrawTextFlags::Left|DrawTextFlags::EndEllipsis|DrawTextFlags::Clip)
48 #define EVENTID_SHOW_CURSOR (reinterpret_cast<void*>(1))
49 #define EVENTID_ADJUST_SCROLLBARS (reinterpret_cast<void*>(2))
51 SvxIconChoiceCtrl_Impl::SvxIconChoiceCtrl_Impl(
52 SvtIconChoiceCtrl* pCurView,
53 WinBits nWinStyle
54 ) :
55 aVerSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_VSCROLL) ),
56 aHorSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_HSCROLL) ),
57 aScrBarBox( VclPtr<ScrollBarBox>::Create(pCurView) ),
58 aAutoArrangeIdle ( "svtools contnr SvxIconChoiceCtrl_Impl AutoArrange" ),
59 aDocRectChangedIdle ( "svtools contnr SvxIconChoiceCtrl_Impl DocRectChanged" ),
60 aVisRectChangedIdle ( "svtools contnr SvxIconChoiceCtrl_Impl VisRectChanged" ),
61 aCallSelectHdlIdle ( "svtools contnr SvxIconChoiceCtrl_Impl CallSelectHdl" ),
62 aImageSize( 32 * pCurView->GetDPIScaleFactor(), 32 * pCurView->GetDPIScaleFactor())
64 bChooseWithCursor = false;
65 pEntryPaintDev = nullptr;
66 pCurHighlightFrame = nullptr;
67 pAnchor = nullptr;
68 pHdlEntry = nullptr;
69 pHead = nullptr;
70 pCursor = nullptr;
71 bUpdateMode = true;
72 bHighlightFramePressed = false;
73 eSelectionMode = SelectionMode::Multiple;
74 pView = pCurView;
75 ePositionMode = SvxIconChoiceCtrlPositionMode::Free;
76 SetStyle( nWinStyle );
77 nFlags = IconChoiceFlags::NONE;
78 nUserEventAdjustScrBars = nullptr;
79 nMaxVirtWidth = DEFAULT_MAX_VIRT_WIDTH;
80 nMaxVirtHeight = DEFAULT_MAX_VIRT_HEIGHT;
81 pDDDev = nullptr;
82 pDDBufDev = nullptr;
83 pDDTempDev = nullptr;
84 eTextMode = SvxIconChoiceCtrlTextMode::Short;
85 pImpCursor.reset( new IcnCursor_Impl( this ) );
86 pGridMap.reset( new IcnGridMap_Impl( this ) );
88 aVerSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollUpDownHdl ) );
89 aHorSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl ) );
91 nHorSBarHeight = aHorSBar->GetSizePixel().Height();
92 nVerSBarWidth = aVerSBar->GetSizePixel().Width();
94 aAutoArrangeIdle.SetPriority( TaskPriority::HIGH_IDLE );
95 aAutoArrangeIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,AutoArrangeHdl));
96 aAutoArrangeIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aAutoArrangeIdle" );
98 aCallSelectHdlIdle.SetPriority( TaskPriority::LOWEST );
99 aCallSelectHdlIdle.SetInvokeHandler( LINK(this,SvxIconChoiceCtrl_Impl,CallSelectHdlHdl));
100 aCallSelectHdlIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aCallSelectHdlIdle" );
102 aDocRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE );
103 aDocRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,DocRectChangedHdl));
104 aDocRectChangedIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aDocRectChangedIdle" );
106 aVisRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE );
107 aVisRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,VisRectChangedHdl));
108 aVisRectChangedIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aVisRectChangedIdle" );
110 Clear( true );
111 Size gridSize(100,70);
112 if(pView->GetDPIScaleFactor() > 1)
114 gridSize.setHeight( gridSize.Height() * ( pView->GetDPIScaleFactor()) );
116 SetGrid(gridSize);
119 SvxIconChoiceCtrl_Impl::~SvxIconChoiceCtrl_Impl()
121 Clear(false);
122 CancelUserEvents();
123 pImpCursor.reset();
124 pGridMap.reset();
125 pDDDev.disposeAndClear();
126 pDDBufDev.disposeAndClear();
127 pDDTempDev.disposeAndClear();
128 pEntryPaintDev.disposeAndClear();
129 ClearSelectedRectList();
130 ClearColumnList();
131 aVerSBar.disposeAndClear();
132 aHorSBar.disposeAndClear();
133 aScrBarBox.disposeAndClear();
136 void SvxIconChoiceCtrl_Impl::Clear( bool bInCtor )
138 nSelectionCount = 0;
139 pCurHighlightFrame = nullptr;
140 CancelUserEvents();
141 ShowCursor( false );
142 bBoundRectsDirty = false;
143 nMaxBoundHeight = 0;
145 pCursor = nullptr;
146 if( !bInCtor )
148 pImpCursor->Clear();
149 pGridMap->Clear();
150 aVirtOutputSize.setWidth( 0 );
151 aVirtOutputSize.setHeight( 0 );
152 Size aSize( pView->GetOutputSizePixel() );
153 nMaxVirtWidth = aSize.Width() - nVerSBarWidth;
154 if( nMaxVirtWidth <= 0 )
155 nMaxVirtWidth = DEFAULT_MAX_VIRT_WIDTH;
156 nMaxVirtHeight = aSize.Height() - nHorSBarHeight;
157 if( nMaxVirtHeight <= 0 )
158 nMaxVirtHeight = DEFAULT_MAX_VIRT_HEIGHT;
159 maZOrderList.clear();
160 SetOrigin( Point() );
161 if( bUpdateMode )
162 pView->Invalidate(InvalidateFlags::NoChildren);
164 AdjustScrollBars();
165 maEntries.clear();
166 DocRectChanged();
167 VisRectChanged();
170 void SvxIconChoiceCtrl_Impl::SetStyle( WinBits nWinStyle )
172 nWinBits = nWinStyle;
173 nCurTextDrawFlags = DRAWTEXT_FLAGS_ICON;
174 if( nWinBits & (WB_SMALLICON | WB_DETAILS) )
175 nCurTextDrawFlags = DRAWTEXT_FLAGS_SMALLICON;
176 if( nWinBits & WB_NOSELECTION )
177 eSelectionMode = SelectionMode::NONE;
178 if( !(nWinStyle & (WB_ALIGN_TOP | WB_ALIGN_LEFT)))
179 nWinBits |= WB_ALIGN_LEFT;
180 if( nWinStyle & WB_DETAILS )
182 if (!m_pColumns)
183 SetColumn( 0, SvxIconChoiceCtrlColumnInfo() );
187 IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollUpDownHdl, ScrollBar*, pScrollBar, void )
189 // arrow up: delta=-1; arrow down: delta=+1
190 Scroll( 0, pScrollBar->GetDelta() );
193 IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl, ScrollBar*, pScrollBar, void )
195 // arrow left: delta=-1; arrow right: delta=+1
196 Scroll( pScrollBar->GetDelta(), 0 );
199 void SvxIconChoiceCtrl_Impl::FontModified()
201 pDDDev.disposeAndClear();
202 pDDBufDev.disposeAndClear();
203 pDDTempDev.disposeAndClear();
204 pEntryPaintDev.disposeAndClear();
205 SetDefaultTextSize();
206 ShowCursor( false );
207 ShowCursor( true );
210 void SvxIconChoiceCtrl_Impl::InsertEntry( std::unique_ptr<SvxIconChoiceCtrlEntry> pEntry1, size_t nPos)
212 auto pEntry = pEntry1.get();
214 if ( nPos < maEntries.size() ) {
215 maEntries.insert( maEntries.begin() + nPos, std::move(pEntry1) );
216 } else {
217 maEntries.push_back( std::move(pEntry1) );
220 if( pHead )
221 pEntry->SetBacklink( pHead->pblink );
223 if( (nFlags & IconChoiceFlags::EntryListPosValid) && nPos >= maEntries.size() - 1 )
224 pEntry->nPos = maEntries.size() - 1;
225 else
226 nFlags &= ~IconChoiceFlags::EntryListPosValid;
228 maZOrderList.push_back( pEntry );
229 pImpCursor->Clear();
231 // If the UpdateMode is true, don't set all bounding rectangles to
232 // 'to be checked', but only the bounding rectangle of the new entry.
233 // Thus, don't call InvalidateBoundingRect!
234 pEntry->aRect.SetRight( LONG_MAX );
235 if( bUpdateMode )
237 FindBoundingRect( pEntry );
238 tools::Rectangle aOutputArea( GetOutputRect() );
239 pGridMap->OccupyGrids( pEntry );
240 if( !aOutputArea.IsOver( pEntry->aRect ) )
241 return; // is invisible
242 pView->Invalidate( pEntry->aRect );
244 else
245 InvalidateBoundingRect( pEntry->aRect );
248 void SvxIconChoiceCtrl_Impl::RemoveEntry(size_t nPos)
250 pImpCursor->Clear();
251 maEntries.erase(maEntries.begin() + nPos);
252 RecalcAllBoundingRectsSmart();
255 void SvxIconChoiceCtrl_Impl::CreateAutoMnemonics( MnemonicGenerator* _pGenerator )
257 std::unique_ptr< MnemonicGenerator > pAutoDeleteOwnGenerator;
258 if ( !_pGenerator )
260 _pGenerator = new MnemonicGenerator;
261 pAutoDeleteOwnGenerator.reset( _pGenerator );
264 sal_uLong nEntryCount = GetEntryCount();
265 sal_uLong i;
267 // insert texts in generator
268 for( i = 0; i < nEntryCount; ++i )
270 DBG_ASSERT( GetEntry( i ), "-SvxIconChoiceCtrl_Impl::CreateAutoMnemonics(): more expected than provided!" );
272 _pGenerator->RegisterMnemonic( GetEntry( i )->GetText() );
275 // exchange texts with generated mnemonics
276 for( i = 0; i < nEntryCount; ++i )
278 SvxIconChoiceCtrlEntry* pEntry = GetEntry( i );
279 OUString aTxt = pEntry->GetText();
281 OUString aNewText = _pGenerator->CreateMnemonic( aTxt );
282 if( aNewText != aTxt )
283 pEntry->SetText( aNewText );
287 tools::Rectangle SvxIconChoiceCtrl_Impl::GetOutputRect() const
289 Point aOrigin( pView->GetMapMode().GetOrigin() );
290 aOrigin *= -1;
291 return tools::Rectangle( aOrigin, aOutputSize );
294 void SvxIconChoiceCtrl_Impl::SetListPositions()
296 if( nFlags & IconChoiceFlags::EntryListPosValid )
297 return;
299 size_t nCount = maEntries.size();
300 for( size_t nCur = 0; nCur < nCount; nCur++ )
302 maEntries[ nCur ]->nPos = nCur;
304 nFlags |= IconChoiceFlags::EntryListPosValid;
307 void SvxIconChoiceCtrl_Impl::SelectEntry( SvxIconChoiceCtrlEntry* pEntry, bool bSelect,
308 bool bAdd )
310 if( eSelectionMode == SelectionMode::NONE )
311 return;
313 if( !bAdd )
315 if ( !( nFlags & IconChoiceFlags::ClearingSelection ) )
317 nFlags |= IconChoiceFlags::ClearingSelection;
318 DeselectAllBut( pEntry );
319 nFlags &= ~IconChoiceFlags::ClearingSelection;
322 if( pEntry->IsSelected() == bSelect )
323 return;
325 pHdlEntry = pEntry;
326 SvxIconViewFlags nEntryFlags = pEntry->GetFlags();
327 if( bSelect )
329 nEntryFlags |= SvxIconViewFlags::SELECTED;
330 pEntry->AssignFlags( nEntryFlags );
331 nSelectionCount++;
332 CallSelectHandler();
334 else
336 nEntryFlags &= ~SvxIconViewFlags::SELECTED;
337 pEntry->AssignFlags( nEntryFlags );
338 nSelectionCount--;
339 CallSelectHandler();
341 EntrySelected( pEntry, bSelect );
344 void SvxIconChoiceCtrl_Impl::EntrySelected(SvxIconChoiceCtrlEntry* pEntry, bool bSelect)
346 // When using SingleSelection, make sure that the cursor is always placed
347 // over the (only) selected entry. (But only if a cursor exists.)
348 if (bSelect && pCursor &&
349 eSelectionMode == SelectionMode::Single &&
350 pEntry != pCursor)
352 SetCursor(pEntry);
355 // Not when dragging though, else the loop in SelectRect doesn't work
356 // correctly!
357 if (!(nFlags & IconChoiceFlags::SelectingRect))
358 ToTop(pEntry);
359 if (bUpdateMode)
361 if (pEntry == pCursor)
362 ShowCursor(false);
363 pView->Invalidate(CalcFocusRect(pEntry));
364 if (pEntry == pCursor)
365 ShowCursor(true);
368 // #i101012# emit vcl event LISTBOX_SELECT only in case that the given entry is selected.
369 if (bSelect)
371 CallEventListeners(VclEventId::ListboxSelect, pEntry);
375 void SvxIconChoiceCtrl_Impl::ResetVirtSize()
377 aVirtOutputSize.setWidth( 0 );
378 aVirtOutputSize.setHeight( 0 );
379 const size_t nCount = maEntries.size();
380 for( size_t nCur = 0; nCur < nCount; nCur++ )
382 SvxIconChoiceCtrlEntry* pCur = maEntries[ nCur ].get();
383 pCur->ClearFlags( SvxIconViewFlags::POS_MOVED );
384 if( pCur->IsPosLocked() )
386 // adapt (among others) VirtSize
387 if( !IsBoundingRectValid( pCur->aRect ) )
388 FindBoundingRect( pCur );
389 else
390 AdjustVirtSize( pCur->aRect );
392 else
393 InvalidateBoundingRect( pCur->aRect );
396 if( !(nWinBits & (WB_NOVSCROLL | WB_NOHSCROLL)) )
398 Size aRealOutputSize( pView->GetOutputSizePixel() );
399 if( aVirtOutputSize.Width() < aRealOutputSize.Width() ||
400 aVirtOutputSize.Height() < aRealOutputSize.Height() )
402 sal_uLong nGridCount = IcnGridMap_Impl::GetGridCount(
403 aRealOutputSize, static_cast<sal_uInt16>(nGridDX), static_cast<sal_uInt16>(nGridDY) );
404 if( nGridCount < nCount )
406 if( nWinBits & WB_ALIGN_TOP )
407 nMaxVirtWidth = aRealOutputSize.Width() - nVerSBarWidth;
408 else // WB_ALIGN_LEFT
409 nMaxVirtHeight = aRealOutputSize.Height() - nHorSBarHeight;
414 pImpCursor->Clear();
415 pGridMap->Clear();
416 VisRectChanged();
419 void SvxIconChoiceCtrl_Impl::AdjustVirtSize( const tools::Rectangle& rRect )
421 long nHeightOffs = 0;
422 long nWidthOffs = 0;
424 if( aVirtOutputSize.Width() < (rRect.Right()+LROFFS_WINBORDER) )
425 nWidthOffs = (rRect.Right()+LROFFS_WINBORDER) - aVirtOutputSize.Width();
427 if( aVirtOutputSize.Height() < (rRect.Bottom()+TBOFFS_WINBORDER) )
428 nHeightOffs = (rRect.Bottom()+TBOFFS_WINBORDER) - aVirtOutputSize.Height();
430 if( !(nWidthOffs || nHeightOffs) )
431 return;
433 Range aRange;
434 aVirtOutputSize.AdjustWidth(nWidthOffs );
435 aRange.Max() = aVirtOutputSize.Width();
436 aHorSBar->SetRange( aRange );
438 aVirtOutputSize.AdjustHeight(nHeightOffs );
439 aRange.Max() = aVirtOutputSize.Height();
440 aVerSBar->SetRange( aRange );
442 pImpCursor->Clear();
443 pGridMap->OutputSizeChanged();
444 AdjustScrollBars();
445 DocRectChanged();
448 void SvxIconChoiceCtrl_Impl::InitPredecessors()
450 DBG_ASSERT(!pHead,"SvxIconChoiceCtrl_Impl::InitPredecessors() >> Already initialized");
451 size_t nCount = maEntries.size();
452 if( nCount )
454 SvxIconChoiceCtrlEntry* pPrev = maEntries[ 0 ].get();
455 for( size_t nCur = 1; nCur <= nCount; nCur++ )
457 pPrev->ClearFlags( SvxIconViewFlags::POS_LOCKED | SvxIconViewFlags::POS_MOVED );
459 SvxIconChoiceCtrlEntry* pNext;
460 if( nCur == nCount )
461 pNext = maEntries[ 0 ].get();
462 else
463 pNext = maEntries[ nCur ].get();
464 pPrev->pflink = pNext;
465 pNext->pblink = pPrev;
466 pPrev = pNext;
468 pHead = maEntries[ 0 ].get();
470 else
471 pHead = nullptr;
474 void SvxIconChoiceCtrl_Impl::ClearPredecessors()
476 if( pHead )
478 size_t nCount = maEntries.size();
479 for( size_t nCur = 0; nCur < nCount; nCur++ )
481 SvxIconChoiceCtrlEntry* pCur = maEntries[ nCur ].get();
482 pCur->pflink = nullptr;
483 pCur->pblink = nullptr;
485 pHead = nullptr;
489 void SvxIconChoiceCtrl_Impl::Arrange( bool bKeepPredecessors, long nSetMaxVirtWidth, long nSetMaxVirtHeight )
491 if ( nSetMaxVirtWidth != 0 )
492 nMaxVirtWidth = nSetMaxVirtWidth;
493 else
494 nMaxVirtWidth = aOutputSize.Width();
496 if ( nSetMaxVirtHeight != 0 )
497 nMaxVirtHeight = nSetMaxVirtHeight;
498 else
499 nMaxVirtHeight = aOutputSize.Height();
501 ImpArrange( bKeepPredecessors );
504 void SvxIconChoiceCtrl_Impl::ImpArrange( bool bKeepPredecessors )
506 static Point aEmptyPoint;
508 bool bOldUpdate = bUpdateMode;
509 tools::Rectangle aCurOutputArea( GetOutputRect() );
510 if( (nWinBits & WB_SMART_ARRANGE) && aCurOutputArea.TopLeft() != aEmptyPoint )
511 bUpdateMode = false;
512 aAutoArrangeIdle.Stop();
513 nFlags |= IconChoiceFlags::Arranging;
514 ShowCursor( false );
515 ResetVirtSize();
516 if( !bKeepPredecessors )
517 ClearPredecessors();
518 bBoundRectsDirty = false;
519 SetOrigin( Point() );
520 VisRectChanged();
521 RecalcAllBoundingRectsSmart();
522 // TODO: the invalidation in the detail view should be more intelligent
523 //if( !(nWinBits & WB_DETAILS ))
524 pView->Invalidate( InvalidateFlags::NoChildren );
525 nFlags &= ~IconChoiceFlags::Arranging;
526 if( (nWinBits & WB_SMART_ARRANGE) && aCurOutputArea.TopLeft() != aEmptyPoint )
528 MakeVisible( aCurOutputArea );
529 SetUpdateMode( bOldUpdate );
531 ShowCursor( true );
534 void SvxIconChoiceCtrl_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
536 #if defined(OV_DRAWGRID)
537 Color aOldColor (rRenderContext.GetLineColor());
538 Color aCOL_BLACK);
539 rRenderContext.SetLineColor( aColor );
540 Point aOffs(rRenderContext.GetMapMode().GetOrigin());
541 Size aXSize(GetOutputSizePixel());
543 Point aStart(LROFFS_WINBORDER, 0);
544 Point aEnd(LROFFS_WINBORDER, aXSize.Height());
545 aStart -= aOffs;
546 aEnd -= aOffs;
547 rRenderContext.DrawLine(aStart, aEnd);
550 Point aStart(0, TBOFFS_WINBORDER);
551 Point aEnd(aXSize.Width(), TBOFFS_WINBORDER);
552 aStart -= aOffs;
553 aEnd -= aOffs;
554 rRenderContext.DrawLine(aStart, aEnd);
557 for (long nDX = nGridDX; nDX <= aXSize.Width(); nDX += nGridDX)
559 Point aStart( nDX+LROFFS_WINBORDER, 0 );
560 Point aEnd( nDX+LROFFS_WINBORDER, aXSize.Height());
561 aStart -= aOffs;
562 aEnd -= aOffs;
563 rRenderContext.DrawLine(aStart, aEnd);
565 for (long nDY = nGridDY; nDY <= aXSize.Height(); nDY += nGridDY)
567 Point aStart(0, nDY + TBOFFS_WINBORDER);
568 Point aEnd(aXSize.Width(), nDY + TBOFFS_WINBORDER);
569 aStart -= aOffs;
570 aEnd -= aOffs;
571 rRenderContext.DrawLine(aStart, aEnd);
573 rRenderContext.SetLineColor(aOldColor);
574 #endif
576 if (!maEntries.size())
577 return;
578 if (!pCursor)
580 // set cursor to item with focus-flag
581 bool bfound = false;
582 for (sal_Int32 i = 0; i < pView->GetEntryCount() && !bfound; i++)
584 SvxIconChoiceCtrlEntry* pEntry = pView->GetEntry(i);
585 if (pEntry->IsFocused())
587 pCursor = pEntry;
588 bfound = true;
592 if (!bfound)
593 pCursor = maEntries[ 0 ].get();
596 size_t nCount = maZOrderList.size();
597 if (!nCount)
598 return;
600 rRenderContext.Push(PushFlags::CLIPREGION);
601 rRenderContext.SetClipRegion(vcl::Region(rRect));
603 std::vector< SvxIconChoiceCtrlEntry* > aNewZOrderList;
604 std::vector< SvxIconChoiceCtrlEntry* > aPaintedEntries;
606 size_t nPos = 0;
607 while(nCount)
609 SvxIconChoiceCtrlEntry* pEntry = maZOrderList[nPos];
610 const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry);
611 if (rRect.IsOver(rBoundRect))
613 PaintEntry(pEntry, rBoundRect.TopLeft(), rRenderContext);
614 // set entries to Top if they are being repainted
615 aPaintedEntries.push_back(pEntry);
617 else
618 aNewZOrderList.push_back(pEntry);
620 nCount--;
621 nPos++;
623 maZOrderList = std::move( aNewZOrderList );
624 maZOrderList.insert(maZOrderList.end(), aPaintedEntries.begin(), aPaintedEntries.end());
626 rRenderContext.Pop();
629 void SvxIconChoiceCtrl_Impl::RepaintSelectedEntries()
631 const size_t nCount = maZOrderList.size();
632 if (!nCount)
633 return;
635 tools::Rectangle aOutRect(GetOutputRect());
636 for (size_t nCur = 0; nCur < nCount; nCur++)
638 SvxIconChoiceCtrlEntry* pEntry = maZOrderList[nCur];
639 if (pEntry->GetFlags() & SvxIconViewFlags::SELECTED)
641 const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry);
642 if (aOutRect.IsOver(rBoundRect))
643 pView->Invalidate(rBoundRect);
648 void SvxIconChoiceCtrl_Impl::InitScrollBarBox()
650 aScrBarBox->SetSizePixel( Size(nVerSBarWidth-1, nHorSBarHeight-1) );
651 Size aSize( pView->GetOutputSizePixel() );
652 aScrBarBox->SetPosPixel( Point(aSize.Width()-nVerSBarWidth+1, aSize.Height()-nHorSBarHeight+1));
655 bool SvxIconChoiceCtrl_Impl::MouseButtonDown( const MouseEvent& rMEvt)
657 bool bHandled = true;
658 bHighlightFramePressed = false;
659 bool bGotFocus = (!pView->HasFocus() && !(nWinBits & WB_NOPOINTERFOCUS));
660 if( !(nWinBits & WB_NOPOINTERFOCUS) )
661 pView->GrabFocus();
663 Point aDocPos( rMEvt.GetPosPixel() );
664 if(aDocPos.X()>=aOutputSize.Width() || aDocPos.Y()>=aOutputSize.Height())
665 return false;
666 ToDocPos( aDocPos );
667 SvxIconChoiceCtrlEntry* pEntry = GetEntry( aDocPos, true );
668 if( pEntry )
669 MakeEntryVisible( pEntry, false );
671 if( rMEvt.IsShift() && eSelectionMode != SelectionMode::Single )
673 if( pEntry )
674 SetCursor_Impl( pCursor, pEntry, rMEvt.IsMod1(), rMEvt.IsShift() );
675 return true;
678 if( pAnchor && (rMEvt.IsShift() || rMEvt.IsMod1())) // keyboard selection?
680 DBG_ASSERT(eSelectionMode != SelectionMode::Single,"Invalid selection mode");
681 if( rMEvt.IsMod1() )
682 nFlags |= IconChoiceFlags::AddMode;
684 if( rMEvt.IsShift() )
686 tools::Rectangle aRect( GetEntryBoundRect( pAnchor ));
687 if( pEntry )
688 aRect.Union( GetEntryBoundRect( pEntry ) );
689 else
691 tools::Rectangle aTempRect( aDocPos, Size(1,1));
692 aRect.Union( aTempRect );
694 aCurSelectionRect = aRect;
695 SelectRect( aRect, bool(nFlags & IconChoiceFlags::AddMode), &aSelectedRectList );
697 else if( rMEvt.IsMod1() )
699 AddSelectedRect( aCurSelectionRect );
700 pAnchor = nullptr;
701 aCurSelectionRect.SetPos( aDocPos );
704 if( !pEntry && !(nWinBits & WB_NODRAGSELECTION))
705 pView->StartTracking( StartTrackingFlags::ScrollRepeat );
706 return true;
708 else
710 if( !pEntry )
712 if( eSelectionMode == SelectionMode::Multiple )
714 if( !rMEvt.IsMod1() ) // Ctrl
716 if( !bGotFocus )
718 SetNoSelection();
719 ClearSelectedRectList();
722 else
723 nFlags |= IconChoiceFlags::AddMode;
724 aCurSelectionRect.SetPos( aDocPos );
725 pView->StartTracking( StartTrackingFlags::ScrollRepeat );
727 else
728 bHandled = false;
729 return bHandled;
732 bool bSelected = pEntry->IsSelected();
734 if( rMEvt.GetClicks() == 2 )
736 DeselectAllBut( pEntry );
737 SelectEntry( pEntry, true, false );
738 pHdlEntry = pEntry;
739 pView->ClickIcon();
741 else
743 // Inplace-Editing ?
744 if( rMEvt.IsMod2() ) // Alt?
747 else if( eSelectionMode == SelectionMode::Single )
749 DeselectAllBut( pEntry );
750 SetCursor( pEntry );
752 else if( eSelectionMode == SelectionMode::NONE )
754 if( rMEvt.IsLeft() && (nWinBits & WB_HIGHLIGHTFRAME) )
756 pCurHighlightFrame = nullptr; // force repaint of frame
757 bHighlightFramePressed = true;
758 SetEntryHighlightFrame( pEntry, true );
761 else
763 if( !rMEvt.GetModifier() && rMEvt.IsLeft() )
765 if( !bSelected )
767 DeselectAllBut( pEntry );
768 SetCursor( pEntry );
769 SelectEntry( pEntry, true, false );
771 else
773 // deselect only in the Up, if the Move happened via D&D!
774 nFlags |= IconChoiceFlags::DownDeselect;
777 else if( rMEvt.IsMod1() )
778 nFlags |= IconChoiceFlags::DownCtrl;
781 return bHandled;
784 bool SvxIconChoiceCtrl_Impl::MouseButtonUp( const MouseEvent& rMEvt )
786 bool bHandled = false;
787 if( rMEvt.IsRight() && (nFlags & (IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect) ))
789 nFlags &= ~IconChoiceFlags(IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect);
790 bHandled = true;
793 Point aDocPos( rMEvt.GetPosPixel() );
794 ToDocPos( aDocPos );
795 SvxIconChoiceCtrlEntry* pDocEntry = GetEntry( aDocPos );
796 if( pDocEntry )
798 if( nFlags & IconChoiceFlags::DownCtrl )
800 // Ctrl & MultiSelection
801 ToggleSelection( pDocEntry );
802 SetCursor( pDocEntry );
803 bHandled = true;
805 else if( nFlags & IconChoiceFlags::DownDeselect )
807 DeselectAllBut( pDocEntry );
808 SetCursor( pDocEntry );
809 SelectEntry( pDocEntry, true, false );
810 bHandled = true;
814 nFlags &= ~IconChoiceFlags(IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect);
816 if((nWinBits & WB_HIGHLIGHTFRAME) && bHighlightFramePressed && pCurHighlightFrame)
818 bHandled = true;
819 SvxIconChoiceCtrlEntry* pEntry = pCurHighlightFrame;
820 pCurHighlightFrame = nullptr; // force repaint of frame
821 bHighlightFramePressed = false;
822 SetEntryHighlightFrame( pEntry, true );
824 pHdlEntry = pCurHighlightFrame;
825 pView->ClickIcon();
827 // set focus on Icon
828 SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
829 SetCursor_Impl( pOldCursor, pHdlEntry, false, false );
831 pHdlEntry = nullptr;
833 return bHandled;
836 bool SvxIconChoiceCtrl_Impl::MouseMove( const MouseEvent& rMEvt )
838 const Point aDocPos( pView->PixelToLogic(rMEvt.GetPosPixel()) );
840 if( pView->IsTracking() )
841 return false;
842 else if( nWinBits & WB_HIGHLIGHTFRAME )
844 SvxIconChoiceCtrlEntry* pEntry = GetEntry( aDocPos, true );
845 SetEntryHighlightFrame( pEntry, false );
847 else
848 return false;
849 return true;
852 void SvxIconChoiceCtrl_Impl::SetCursor_Impl( SvxIconChoiceCtrlEntry* pOldCursor,
853 SvxIconChoiceCtrlEntry* pNewCursor, bool bMod1, bool bShift )
855 if( !pNewCursor )
856 return;
858 SvxIconChoiceCtrlEntry* pFilterEntry = nullptr;
859 bool bDeselectAll = false;
860 if( eSelectionMode != SelectionMode::Single )
862 if( !bMod1 && !bShift )
863 bDeselectAll = true;
864 else if( bShift && !bMod1 && !pAnchor )
866 bDeselectAll = true;
867 pFilterEntry = pOldCursor;
870 if( bDeselectAll )
871 DeselectAllBut( pFilterEntry );
872 ShowCursor( false );
873 MakeEntryVisible( pNewCursor );
874 SetCursor( pNewCursor );
875 if( bMod1 && !bShift )
877 if( pAnchor )
879 AddSelectedRect( pAnchor, pOldCursor );
880 pAnchor = nullptr;
883 else if( bShift )
885 if( !pAnchor )
886 pAnchor = pOldCursor;
887 if ( nWinBits & WB_ALIGN_LEFT )
888 SelectRange( pAnchor, pNewCursor, bool(nFlags & IconChoiceFlags::AddMode) );
889 else
890 SelectRect(pAnchor,pNewCursor, bool(nFlags & IconChoiceFlags::AddMode), &aSelectedRectList);
892 else
894 SelectEntry( pCursor, true, false );
895 aCurSelectionRect = GetEntryBoundRect( pCursor );
896 CallEventListeners( VclEventId::ListboxSelect, pCursor );
900 bool SvxIconChoiceCtrl_Impl::KeyInput( const KeyEvent& rKEvt )
902 bool bMod2 = rKEvt.GetKeyCode().IsMod2();
903 sal_Unicode cChar = rKEvt.GetCharCode();
904 sal_uLong nPos = sal_uLong(-1);
905 if ( bMod2 && cChar && IsMnemonicChar( cChar, nPos ) )
907 // shortcut is clicked
908 SvxIconChoiceCtrlEntry* pNewCursor = GetEntry( nPos );
909 SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
910 if ( pNewCursor != pOldCursor )
911 SetCursor_Impl( pOldCursor, pNewCursor, false, false );
912 return true;
915 if ( bMod2 )
916 // no actions with <ALT>
917 return false;
919 bool bKeyUsed = true;
920 bool bMod1 = rKEvt.GetKeyCode().IsMod1();
921 bool bShift = rKEvt.GetKeyCode().IsShift();
923 if( eSelectionMode == SelectionMode::Single || eSelectionMode == SelectionMode::NONE)
925 bShift = false;
926 bMod1 = false;
929 if( bMod1 )
930 nFlags |= IconChoiceFlags::AddMode;
932 SvxIconChoiceCtrlEntry* pNewCursor;
933 SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
935 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
936 switch( nCode )
938 case KEY_UP:
939 case KEY_PAGEUP:
940 if( pCursor )
942 MakeEntryVisible( pCursor );
943 if( nCode == KEY_UP )
944 pNewCursor = pImpCursor->GoUpDown(pCursor,false);
945 else
946 pNewCursor = pImpCursor->GoPageUpDown(pCursor,false);
947 SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
948 if( !pNewCursor )
950 tools::Rectangle aRect( GetEntryBoundRect( pCursor ) );
951 if( aRect.Top())
953 aRect.AdjustBottom( -(aRect.Top()) );
954 aRect.SetTop( 0 );
955 MakeVisible( aRect );
959 if ( bChooseWithCursor && pNewCursor != nullptr )
961 pHdlEntry = pNewCursor;//GetCurEntry();
962 pCurHighlightFrame = pHdlEntry;
963 pView->ClickIcon();
964 pCurHighlightFrame = nullptr;
967 break;
969 case KEY_DOWN:
970 case KEY_PAGEDOWN:
971 if( pCursor )
973 if( nCode == KEY_DOWN )
974 pNewCursor=pImpCursor->GoUpDown( pCursor,true );
975 else
976 pNewCursor=pImpCursor->GoPageUpDown( pCursor,true );
977 SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
979 if ( bChooseWithCursor && pNewCursor != nullptr)
981 pHdlEntry = pNewCursor;//GetCurEntry();
982 pCurHighlightFrame = pHdlEntry;
983 pView->ClickIcon();
984 pCurHighlightFrame = nullptr;
987 break;
989 case KEY_RIGHT:
990 if( pCursor )
992 pNewCursor=pImpCursor->GoLeftRight(pCursor,true );
993 SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
995 break;
997 case KEY_LEFT:
998 if( pCursor )
1000 MakeEntryVisible( pCursor );
1001 pNewCursor = pImpCursor->GoLeftRight(pCursor,false );
1002 SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
1003 if( !pNewCursor )
1005 tools::Rectangle aRect( GetEntryBoundRect(pCursor));
1006 if( aRect.Left() )
1008 aRect.AdjustRight( -(aRect.Left()) );
1009 aRect.SetLeft( 0 );
1010 MakeVisible( aRect );
1014 break;
1016 case KEY_F2:
1017 if( bMod1 || bShift )
1018 bKeyUsed = false;
1019 break;
1021 case KEY_F8:
1022 if( rKEvt.GetKeyCode().IsShift() )
1024 if( nFlags & IconChoiceFlags::AddMode )
1025 nFlags &= ~IconChoiceFlags::AddMode;
1026 else
1027 nFlags |= IconChoiceFlags::AddMode;
1029 else
1030 bKeyUsed = false;
1031 break;
1033 case KEY_SPACE:
1034 if( pCursor && eSelectionMode != SelectionMode::Single )
1036 if( !bMod1 )
1038 //SelectAll( false );
1039 SetNoSelection();
1040 ClearSelectedRectList();
1042 // click Icon with spacebar
1043 SetEntryHighlightFrame( GetCurEntry(), true );
1044 pView->ClickIcon();
1045 pHdlEntry = pCurHighlightFrame;
1046 pCurHighlightFrame=nullptr;
1048 else
1049 ToggleSelection( pCursor );
1051 break;
1053 #ifdef DBG_UTIL
1054 case KEY_F10:
1055 if( rKEvt.GetKeyCode().IsShift() )
1057 if( pCursor )
1058 pView->SetEntryTextMode( SvxIconChoiceCtrlTextMode::Full, pCursor );
1060 if( rKEvt.GetKeyCode().IsMod1() )
1062 if( pCursor )
1063 pView->SetEntryTextMode( SvxIconChoiceCtrlTextMode::Short, pCursor );
1065 break;
1066 #endif
1068 case KEY_ADD:
1069 case KEY_DIVIDE :
1070 case KEY_A:
1071 if( bMod1 && (eSelectionMode != SelectionMode::Single))
1072 SelectAll();
1073 else
1074 bKeyUsed = false;
1075 break;
1077 case KEY_SUBTRACT:
1078 case KEY_COMMA :
1079 if( bMod1 )
1080 SetNoSelection();
1081 else
1082 bKeyUsed = false;
1083 break;
1085 case KEY_RETURN:
1086 if( !bMod1 )
1087 bKeyUsed = false;
1088 break;
1090 case KEY_END:
1091 if( pCursor )
1093 pNewCursor = maEntries.back().get();
1094 SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
1096 break;
1098 case KEY_HOME:
1099 if( pCursor )
1101 pNewCursor = maEntries[ 0 ].get();
1102 SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift );
1104 break;
1106 default:
1107 bKeyUsed = false;
1110 return bKeyUsed;
1113 // recalculate TopLeft of scrollbars (but not their sizes!)
1114 void SvxIconChoiceCtrl_Impl::PositionScrollBars( long nRealWidth, long nRealHeight )
1116 // horizontal scrollbar
1117 Point aPos( 0, nRealHeight );
1118 aPos.AdjustY( -nHorSBarHeight );
1120 if( aHorSBar->GetPosPixel() != aPos )
1121 aHorSBar->SetPosPixel( aPos );
1123 // vertical scrollbar
1124 aPos.setX( nRealWidth ); aPos.setY( 0 );
1125 aPos.AdjustX( -nVerSBarWidth );
1126 aPos.AdjustX( 1 );
1127 aPos.AdjustY( -1 );
1129 if( aVerSBar->GetPosPixel() != aPos )
1130 aVerSBar->SetPosPixel( aPos );
1133 void SvxIconChoiceCtrl_Impl::AdjustScrollBars()
1135 long nVirtHeight = aVirtOutputSize.Height();
1136 long nVirtWidth = aVirtOutputSize.Width();
1138 Size aOSize( pView->Control::GetOutputSizePixel() );
1139 long nRealHeight = aOSize.Height();
1140 long nRealWidth = aOSize.Width();
1142 PositionScrollBars( nRealWidth, nRealHeight );
1144 const MapMode& rMapMode = pView->GetMapMode();
1145 Point aOrigin( rMapMode.GetOrigin() );
1147 long nVisibleWidth;
1148 if( nRealWidth > nVirtWidth )
1149 nVisibleWidth = nVirtWidth + aOrigin.X();
1150 else
1151 nVisibleWidth = nRealWidth;
1153 long nVisibleHeight;
1154 if( nRealHeight > nVirtHeight )
1155 nVisibleHeight = nVirtHeight + aOrigin.Y();
1156 else
1157 nVisibleHeight = nRealHeight;
1159 bool bVerSBar = ( nWinBits & WB_VSCROLL ) != 0;
1160 bool bHorSBar = ( nWinBits & WB_HSCROLL ) != 0;
1161 bool bNoVerSBar = ( nWinBits & WB_NOVSCROLL ) != 0;
1162 bool bNoHorSBar = ( nWinBits & WB_NOHSCROLL ) != 0;
1164 sal_uInt16 nResult = 0;
1165 if( nVirtHeight )
1167 // activate vertical scrollbar?
1168 if( !bNoVerSBar && (bVerSBar || ( nVirtHeight > nVisibleHeight)) )
1170 nResult = 0x0001;
1171 nRealWidth -= nVerSBarWidth;
1173 if( nRealWidth > nVirtWidth )
1174 nVisibleWidth = nVirtWidth + aOrigin.X();
1175 else
1176 nVisibleWidth = nRealWidth;
1178 // activate horizontal scrollbar?
1179 if( !bNoHorSBar && (bHorSBar || (nVirtWidth > nVisibleWidth)) )
1181 nResult |= 0x0002;
1182 nRealHeight -= nHorSBarHeight;
1184 if( nRealHeight > nVirtHeight )
1185 nVisibleHeight = nVirtHeight + aOrigin.Y();
1186 else
1187 nVisibleHeight = nRealHeight;
1189 // do we need a vertical scrollbar after all?
1190 if( !(nResult & 0x0001) && // only if not already there
1191 ( !bNoVerSBar && ((nVirtHeight > nVisibleHeight) || bVerSBar)) )
1193 nResult = 3; // both turned on
1194 nRealWidth -= nVerSBarWidth;
1196 if( nRealWidth > nVirtWidth )
1197 nVisibleWidth = nVirtWidth + aOrigin.X();
1198 else
1199 nVisibleWidth = nRealWidth;
1204 // size vertical scrollbar
1205 long nThumb = aVerSBar->GetThumbPos();
1206 Size aSize( nVerSBarWidth, nRealHeight );
1207 aSize.AdjustHeight(2 );
1208 if( aSize != aVerSBar->GetSizePixel() )
1209 aVerSBar->SetSizePixel( aSize );
1210 aVerSBar->SetVisibleSize( nVisibleHeight );
1211 aVerSBar->SetPageSize( GetScrollBarPageSize( nVisibleHeight ));
1213 if( nResult & 0x0001 )
1215 aVerSBar->SetThumbPos( nThumb );
1216 aVerSBar->Show();
1218 else
1220 aVerSBar->SetThumbPos( 0 );
1221 aVerSBar->Hide();
1224 // size horizontal scrollbar
1225 nThumb = aHorSBar->GetThumbPos();
1226 aSize.setWidth( nRealWidth );
1227 aSize.setHeight( nHorSBarHeight );
1228 aSize.AdjustWidth( 1 );
1229 if( nResult & 0x0001 ) // vertical scrollbar?
1231 aSize.AdjustWidth( 1 );
1232 nRealWidth++;
1234 if( aSize != aHorSBar->GetSizePixel() )
1235 aHorSBar->SetSizePixel( aSize );
1236 aHorSBar->SetVisibleSize( nVisibleWidth );
1237 aHorSBar->SetPageSize( GetScrollBarPageSize(nVisibleWidth ));
1238 if( nResult & 0x0002 )
1240 aHorSBar->SetThumbPos( nThumb );
1241 aHorSBar->Show();
1243 else
1245 aHorSBar->SetThumbPos( 0 );
1246 aHorSBar->Hide();
1249 aOutputSize.setWidth( nRealWidth );
1250 if( nResult & 0x0002 ) // horizontal scrollbar ?
1251 nRealHeight++; // because lower border is clipped
1252 aOutputSize.setHeight( nRealHeight );
1254 if( (nResult & (0x0001|0x0002)) == (0x0001|0x0002) )
1255 aScrBarBox->Show();
1256 else
1257 aScrBarBox->Hide();
1260 void SvxIconChoiceCtrl_Impl::Resize()
1262 InitScrollBarBox();
1263 aOutputSize = pView->GetOutputSizePixel();
1264 pImpCursor->Clear();
1265 pGridMap->OutputSizeChanged();
1267 const Size& rSize = pView->Control::GetOutputSizePixel();
1268 PositionScrollBars( rSize.Width(), rSize.Height() );
1269 // The scrollbars are shown/hidden asynchronously, so derived classes can
1270 // do an Arrange during Resize, without the scrollbars suddenly turning
1271 // on and off again.
1272 // If an event is already underway, we don't need to send a new one, at least
1273 // as long as there is only one event type.
1274 if ( ! nUserEventAdjustScrBars )
1275 nUserEventAdjustScrBars =
1276 Application::PostUserEvent( LINK( this, SvxIconChoiceCtrl_Impl, UserEventHdl),
1277 EVENTID_ADJUST_SCROLLBARS);
1279 VisRectChanged();
1282 bool SvxIconChoiceCtrl_Impl::CheckHorScrollBar()
1284 if( maZOrderList.empty() || !aHorSBar->IsVisible() )
1285 return false;
1286 const MapMode& rMapMode = pView->GetMapMode();
1287 Point aOrigin( rMapMode.GetOrigin() );
1288 if(!( nWinBits & WB_HSCROLL) && !aOrigin.X() )
1290 long nWidth = aOutputSize.Width();
1291 const size_t nCount = maZOrderList.size();
1292 long nMostRight = 0;
1293 for( size_t nCur = 0; nCur < nCount; nCur++ )
1295 SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ];
1296 long nRight = GetEntryBoundRect(pEntry).Right();
1297 if( nRight > nWidth )
1298 return false;
1299 if( nRight > nMostRight )
1300 nMostRight = nRight;
1302 aHorSBar->Hide();
1303 aOutputSize.AdjustHeight(nHorSBarHeight );
1304 aVirtOutputSize.setWidth( nMostRight );
1305 aHorSBar->SetThumbPos( 0 );
1306 Range aRange;
1307 aRange.Max() = nMostRight - 1;
1308 aHorSBar->SetRange( aRange );
1309 if( aVerSBar->IsVisible() )
1311 Size aSize( aVerSBar->GetSizePixel());
1312 aSize.AdjustHeight(nHorSBarHeight );
1313 aVerSBar->SetSizePixel( aSize );
1315 return true;
1317 return false;
1320 bool SvxIconChoiceCtrl_Impl::CheckVerScrollBar()
1322 if( maZOrderList.empty() || !aVerSBar->IsVisible() )
1323 return false;
1324 const MapMode& rMapMode = pView->GetMapMode();
1325 Point aOrigin( rMapMode.GetOrigin() );
1326 if(!( nWinBits & WB_VSCROLL) && !aOrigin.Y() )
1328 long nDeepest = 0;
1329 long nHeight = aOutputSize.Height();
1330 const size_t nCount = maZOrderList.size();
1331 for( size_t nCur = 0; nCur < nCount; nCur++ )
1333 SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ];
1334 long nBottom = GetEntryBoundRect(pEntry).Bottom();
1335 if( nBottom > nHeight )
1336 return false;
1337 if( nBottom > nDeepest )
1338 nDeepest = nBottom;
1340 aVerSBar->Hide();
1341 aOutputSize.AdjustWidth(nVerSBarWidth );
1342 aVirtOutputSize.setHeight( nDeepest );
1343 aVerSBar->SetThumbPos( 0 );
1344 Range aRange;
1345 aRange.Max() = nDeepest - 1;
1346 aVerSBar->SetRange( aRange );
1347 if( aHorSBar->IsVisible() )
1349 Size aSize( aHorSBar->GetSizePixel());
1350 aSize.AdjustWidth(nVerSBarWidth );
1351 aHorSBar->SetSizePixel( aSize );
1353 return true;
1355 return false;
1359 // hides scrollbars if they're unnecessary
1360 void SvxIconChoiceCtrl_Impl::CheckScrollBars()
1362 CheckVerScrollBar();
1363 if( CheckHorScrollBar() )
1364 CheckVerScrollBar();
1365 if( aVerSBar->IsVisible() && aHorSBar->IsVisible() )
1366 aScrBarBox->Show();
1367 else
1368 aScrBarBox->Hide();
1372 void SvxIconChoiceCtrl_Impl::GetFocus()
1374 RepaintSelectedEntries();
1375 if( pCursor )
1377 pCursor->SetFlags( SvxIconViewFlags::FOCUSED );
1378 ShowCursor( true );
1382 void SvxIconChoiceCtrl_Impl::LoseFocus()
1384 if( pCursor )
1385 pCursor->ClearFlags( SvxIconViewFlags::FOCUSED );
1386 ShowCursor( false );
1388 // HideFocus ();
1389 // pView->Invalidate ( aFocus.aRect );
1391 RepaintSelectedEntries();
1394 void SvxIconChoiceCtrl_Impl::SetUpdateMode( bool bUpdate )
1396 if( bUpdate != bUpdateMode )
1398 bUpdateMode = bUpdate;
1399 if( bUpdate )
1401 AdjustScrollBars();
1402 pImpCursor->Clear();
1403 pGridMap->Clear();
1404 pView->Invalidate(InvalidateFlags::NoChildren);
1409 // priorities of the emphasis: bSelected
1410 void SvxIconChoiceCtrl_Impl::PaintEmphasis(const tools::Rectangle& rTextRect, bool bSelected,
1411 vcl::RenderContext& rRenderContext)
1413 Color aOldFillColor(rRenderContext.GetFillColor());
1415 bool bSolidTextRect = false;
1417 if (!bSelected)
1419 const Color& rFillColor = rRenderContext.GetFont().GetFillColor();
1420 rRenderContext.SetFillColor(rFillColor);
1421 if (rFillColor != COL_TRANSPARENT)
1422 bSolidTextRect = true;
1425 // draw text rectangle
1426 if (bSolidTextRect)
1428 rRenderContext.DrawRect(rTextRect);
1431 rRenderContext.SetFillColor(aOldFillColor);
1435 void SvxIconChoiceCtrl_Impl::PaintItem(const tools::Rectangle& rRect,
1436 IcnViewFieldType eItem, SvxIconChoiceCtrlEntry* pEntry, sal_uInt16 nPaintFlags,
1437 vcl::RenderContext& rRenderContext )
1439 if (eItem == IcnViewFieldType::Text)
1441 OUString aText = SvtIconChoiceCtrl::GetEntryText(pEntry);
1443 rRenderContext.DrawText(rRect, aText, nCurTextDrawFlags);
1445 if (pEntry->IsFocused())
1447 tools::Rectangle aRect (CalcFocusRect(pEntry));
1448 ShowFocus(aRect);
1449 DrawFocusRect(rRenderContext);
1452 else
1454 Point aPos(rRect.TopLeft());
1455 if (nPaintFlags & PAINTFLAG_HOR_CENTERED)
1456 aPos.AdjustX((rRect.GetWidth() - aImageSize.Width()) / 2 );
1457 if (nPaintFlags & PAINTFLAG_VER_CENTERED)
1458 aPos.AdjustY((rRect.GetHeight() - aImageSize.Height()) / 2 );
1459 SvtIconChoiceCtrl::DrawEntryImage(pEntry, aPos, rRenderContext);
1463 void SvxIconChoiceCtrl_Impl::PaintEntry(SvxIconChoiceCtrlEntry* pEntry, const Point& rPos, vcl::RenderContext& rRenderContext)
1465 bool bSelected = false;
1467 if (eSelectionMode != SelectionMode::NONE)
1468 bSelected = pEntry->IsSelected();
1470 rRenderContext.Push(PushFlags::FONT | PushFlags::TEXTCOLOR);
1472 OUString aEntryText(SvtIconChoiceCtrl::GetEntryText(pEntry));
1473 tools::Rectangle aTextRect(CalcTextRect(pEntry, &rPos, &aEntryText));
1474 tools::Rectangle aBmpRect(CalcBmpRect(pEntry, &rPos));
1476 bool bShowSelection = (bSelected && (eSelectionMode != SelectionMode::NONE));
1478 bool bActiveSelection = (0 != (nWinBits & WB_NOHIDESELECTION)) || pView->HasFocus();
1480 if (bShowSelection)
1482 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1483 vcl::Font aNewFont(rRenderContext.GetFont());
1485 // font fill colors that are attributed "hard" need corresponding "hard"
1486 // attributed highlight colors
1487 if ((nWinBits & WB_NOHIDESELECTION) || pView->HasFocus())
1488 aNewFont.SetFillColor(rSettings.GetHighlightColor());
1489 else
1490 aNewFont.SetFillColor(rSettings.GetDeactiveColor());
1492 Color aWinCol = rSettings.GetWindowTextColor();
1493 if (!bActiveSelection && rSettings.GetFaceColor().IsBright() == aWinCol.IsBright())
1494 aNewFont.SetColor(rSettings.GetWindowTextColor());
1495 else
1496 aNewFont.SetColor(rSettings.GetHighlightTextColor());
1498 rRenderContext.SetFont(aNewFont);
1500 rRenderContext.SetFillColor(rRenderContext.GetBackground().GetColor());
1501 rRenderContext.DrawRect(CalcFocusRect(pEntry));
1502 rRenderContext.SetFillColor();
1505 bool bResetClipRegion = false;
1506 if (!rRenderContext.IsClipRegion() && (aVerSBar->IsVisible() || aHorSBar->IsVisible()))
1508 tools::Rectangle aOutputArea(GetOutputRect());
1509 if (aOutputArea.IsOver(aTextRect) || aOutputArea.IsOver(aBmpRect))
1511 rRenderContext.SetClipRegion(vcl::Region(aOutputArea));
1512 bResetClipRegion = true;
1516 bool bLargeIconMode = WB_ICON == ( nWinBits & VIEWMODE_MASK );
1517 sal_uInt16 nBmpPaintFlags = PAINTFLAG_VER_CENTERED;
1518 if (bLargeIconMode)
1519 nBmpPaintFlags |= PAINTFLAG_HOR_CENTERED;
1520 sal_uInt16 nTextPaintFlags = bLargeIconMode ? PAINTFLAG_HOR_CENTERED : PAINTFLAG_VER_CENTERED;
1522 PaintEmphasis(aTextRect, bSelected, rRenderContext);
1524 if ( bShowSelection )
1525 vcl::RenderTools::DrawSelectionBackground(rRenderContext, *pView, CalcFocusRect(pEntry),
1526 bActiveSelection ? 1 : 2, false, true, false);
1529 PaintItem(aBmpRect, IcnViewFieldType::Image, pEntry, nBmpPaintFlags, rRenderContext);
1531 PaintItem(aTextRect, IcnViewFieldType::Text, pEntry, nTextPaintFlags, rRenderContext);
1533 // draw highlight frame
1534 if (pEntry == pCurHighlightFrame)
1535 DrawHighlightFrame(rRenderContext, CalcFocusRect(pEntry));
1537 rRenderContext.Pop();
1538 if (bResetClipRegion)
1539 rRenderContext.SetClipRegion();
1542 void SvxIconChoiceCtrl_Impl::SetEntryPos( SvxIconChoiceCtrlEntry* pEntry, const Point& rPos )
1544 ShowCursor( false );
1545 tools::Rectangle aBoundRect( GetEntryBoundRect( pEntry ));
1546 pView->Invalidate( aBoundRect );
1547 ToTop( pEntry );
1548 if( !IsAutoArrange() )
1550 bool bAdjustVirtSize = false;
1551 if( rPos != aBoundRect.TopLeft() )
1553 Point aGridOffs(
1554 pEntry->aGridRect.TopLeft() - pEntry->aRect.TopLeft() );
1555 pImpCursor->Clear();
1556 pGridMap->Clear();
1557 aBoundRect.SetPos( rPos );
1558 pEntry->aRect = aBoundRect;
1559 pEntry->aGridRect.SetPos( rPos + aGridOffs );
1560 bAdjustVirtSize = true;
1562 if( bAdjustVirtSize )
1563 AdjustVirtSize( pEntry->aRect );
1565 pView->Invalidate( pEntry->aRect );
1566 pGridMap->OccupyGrids( pEntry );
1568 else
1570 SvxIconChoiceCtrlEntry* pPrev = FindEntryPredecessor( pEntry, rPos );
1571 SetEntryPredecessor( pEntry, pPrev );
1572 aAutoArrangeIdle.Start();
1574 ShowCursor( true );
1577 void SvxIconChoiceCtrl_Impl::SetNoSelection()
1579 // block recursive calls via SelectEntry
1580 if( !(nFlags & IconChoiceFlags::ClearingSelection ))
1582 nFlags |= IconChoiceFlags::ClearingSelection;
1583 DeselectAllBut( nullptr );
1584 nFlags &= ~IconChoiceFlags::ClearingSelection;
1588 SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetEntry( const Point& rDocPos, bool bHit )
1590 CheckBoundingRects();
1591 // search through z-order list from the end
1592 size_t nCount = maZOrderList.size();
1593 while( nCount )
1595 nCount--;
1596 SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCount ];
1597 if( pEntry->aRect.IsInside( rDocPos ) )
1599 if( bHit )
1601 tools::Rectangle aRect = CalcBmpRect( pEntry );
1602 aRect.AdjustTop( -3 );
1603 aRect.AdjustBottom(3 );
1604 aRect.AdjustLeft( -3 );
1605 aRect.AdjustRight(3 );
1606 if( aRect.IsInside( rDocPos ) )
1607 return pEntry;
1608 aRect = CalcTextRect( pEntry );
1609 if( aRect.IsInside( rDocPos ) )
1610 return pEntry;
1612 else
1613 return pEntry;
1616 return nullptr;
1619 void SvxIconChoiceCtrl_Impl::MakeEntryVisible( SvxIconChoiceCtrlEntry* pEntry, bool bBound )
1621 if ( bBound )
1623 const tools::Rectangle& rRect = GetEntryBoundRect( pEntry );
1624 MakeVisible( rRect );
1626 else
1628 tools::Rectangle aRect = CalcBmpRect( pEntry );
1629 aRect.Union( CalcTextRect( pEntry ) );
1630 aRect.AdjustTop(TBOFFS_BOUND );
1631 aRect.AdjustBottom(TBOFFS_BOUND );
1632 aRect.AdjustLeft(LROFFS_BOUND );
1633 aRect.AdjustRight(LROFFS_BOUND );
1634 MakeVisible( aRect );
1638 const tools::Rectangle& SvxIconChoiceCtrl_Impl::GetEntryBoundRect( SvxIconChoiceCtrlEntry* pEntry )
1640 if( !IsBoundingRectValid( pEntry->aRect ))
1641 FindBoundingRect( pEntry );
1642 return pEntry->aRect;
1645 tools::Rectangle SvxIconChoiceCtrl_Impl::CalcBmpRect( SvxIconChoiceCtrlEntry* pEntry, const Point* pPos )
1647 tools::Rectangle aBound = GetEntryBoundRect( pEntry );
1648 if( pPos )
1649 aBound.SetPos( *pPos );
1650 Point aPos( aBound.TopLeft() );
1652 switch( nWinBits & VIEWMODE_MASK )
1654 case WB_ICON:
1656 aPos.AdjustX(( aBound.GetWidth() - aImageSize.Width() ) / 2 );
1657 return tools::Rectangle( aPos, aImageSize );
1660 case WB_SMALLICON:
1661 case WB_DETAILS:
1662 aPos.AdjustY(( aBound.GetHeight() - aImageSize.Height() ) / 2 );
1663 //TODO: determine horizontal distance to bounding rectangle
1664 return tools::Rectangle( aPos, aImageSize );
1666 default:
1667 OSL_FAIL("IconView: Viewmode not set");
1668 return aBound;
1672 tools::Rectangle SvxIconChoiceCtrl_Impl::CalcTextRect( SvxIconChoiceCtrlEntry* pEntry,
1673 const Point* pEntryPos, const OUString* pStr )
1675 OUString aEntryText;
1676 if( !pStr )
1677 aEntryText = SvtIconChoiceCtrl::GetEntryText( pEntry );
1678 else
1679 aEntryText = *pStr;
1681 const tools::Rectangle aMaxTextRect( CalcMaxTextRect( pEntry ) );
1682 tools::Rectangle aBound( GetEntryBoundRect( pEntry ) );
1683 if( pEntryPos )
1684 aBound.SetPos( *pEntryPos );
1686 tools::Rectangle aTextRect = pView->GetTextRect( aMaxTextRect, aEntryText, nCurTextDrawFlags );
1688 Size aTextSize( aTextRect.GetSize() );
1690 Point aPos( aBound.TopLeft() );
1691 long nBoundWidth = aBound.GetWidth();
1692 long nBoundHeight = aBound.GetHeight();
1694 switch( nWinBits & VIEWMODE_MASK )
1696 case WB_ICON:
1697 aPos.AdjustY(aImageSize.Height() );
1698 aPos.AdjustY(VER_DIST_BMP_STRING );
1699 aPos.AdjustX((nBoundWidth - aTextSize.Width()) / 2 );
1700 break;
1702 case WB_SMALLICON:
1703 case WB_DETAILS:
1704 aPos.AdjustX(aImageSize.Width() );
1705 aPos.AdjustX(HOR_DIST_BMP_STRING );
1706 aPos.AdjustY((nBoundHeight - aTextSize.Height()) / 2 );
1707 break;
1709 return tools::Rectangle( aPos, aTextSize );
1713 long SvxIconChoiceCtrl_Impl::CalcBoundingWidth() const
1715 long nStringWidth = GetItemSize( IcnViewFieldType::Text ).Width();
1716 long nWidth = 0;
1718 switch( nWinBits & VIEWMODE_MASK )
1720 case WB_ICON:
1721 nWidth = std::max( nStringWidth, aImageSize.Width() );
1722 break;
1724 case WB_SMALLICON:
1725 case WB_DETAILS:
1726 nWidth = aImageSize.Width();
1727 nWidth += HOR_DIST_BMP_STRING;
1728 nWidth += nStringWidth;
1729 break;
1731 return nWidth;
1734 long SvxIconChoiceCtrl_Impl::CalcBoundingHeight() const
1736 long nStringHeight = GetItemSize(IcnViewFieldType::Text).Height();
1737 long nHeight = 0;
1739 switch( nWinBits & VIEWMODE_MASK )
1741 case WB_ICON:
1742 nHeight = aImageSize.Height();
1743 nHeight += VER_DIST_BMP_STRING;
1744 nHeight += nStringHeight;
1745 break;
1747 case WB_SMALLICON:
1748 case WB_DETAILS:
1749 nHeight = std::max( aImageSize.Height(), nStringHeight );
1750 break;
1752 if( nHeight > nMaxBoundHeight )
1754 const_cast<SvxIconChoiceCtrl_Impl*>(this)->nMaxBoundHeight = nHeight;
1755 const_cast<SvxIconChoiceCtrl_Impl*>(this)->aHorSBar->SetLineSize( GetScrollBarLineSize() );
1756 const_cast<SvxIconChoiceCtrl_Impl*>(this)->aVerSBar->SetLineSize( GetScrollBarLineSize() );
1758 return nHeight;
1761 Size SvxIconChoiceCtrl_Impl::CalcBoundingSize() const
1763 return Size( CalcBoundingWidth(), CalcBoundingHeight() );
1766 void SvxIconChoiceCtrl_Impl::RecalcAllBoundingRectsSmart()
1768 nMaxBoundHeight = 0;
1769 maZOrderList.clear();
1770 size_t nCur;
1771 SvxIconChoiceCtrlEntry* pEntry;
1772 const size_t nCount = maEntries.size();
1774 if( !IsAutoArrange() || !pHead )
1776 for( nCur = 0; nCur < nCount; nCur++ )
1778 pEntry = maEntries[ nCur ].get();
1779 if( IsBoundingRectValid( pEntry->aRect ))
1781 Size aBoundSize( pEntry->aRect.GetSize() );
1782 if( aBoundSize.Height() > nMaxBoundHeight )
1783 nMaxBoundHeight = aBoundSize.Height();
1785 else
1786 FindBoundingRect( pEntry );
1787 maZOrderList.push_back( pEntry );
1790 else
1792 nCur = 0;
1793 pEntry = pHead;
1794 while( nCur != nCount )
1796 DBG_ASSERT(pEntry->pflink&&pEntry->pblink,"SvxIconChoiceCtrl_Impl::RecalcAllBoundingRect > Bad link(s)");
1797 if( IsBoundingRectValid( pEntry->aRect ))
1799 Size aBoundSize( pEntry->aRect.GetSize() );
1800 if( aBoundSize.Height() > nMaxBoundHeight )
1801 nMaxBoundHeight = aBoundSize.Height();
1803 else
1804 FindBoundingRect( pEntry );
1805 maZOrderList.push_back( pEntry );
1806 pEntry = pEntry->pflink;
1807 nCur++;
1810 AdjustScrollBars();
1813 void SvxIconChoiceCtrl_Impl::FindBoundingRect( SvxIconChoiceCtrlEntry* pEntry )
1815 DBG_ASSERT(!pEntry->IsPosLocked(),"Locked entry pos in FindBoundingRect");
1816 if( pEntry->IsPosLocked() && IsBoundingRectValid( pEntry->aRect) )
1818 AdjustVirtSize( pEntry->aRect );
1819 return;
1821 Size aSize( CalcBoundingSize() );
1822 Point aPos(pGridMap->GetGridRect(pGridMap->GetUnoccupiedGrid()).TopLeft());
1823 SetBoundingRect_Impl( pEntry, aPos, aSize );
1826 void SvxIconChoiceCtrl_Impl::SetBoundingRect_Impl( SvxIconChoiceCtrlEntry* pEntry, const Point& rPos,
1827 const Size& /*rBoundingSize*/ )
1829 tools::Rectangle aGridRect( rPos, Size(nGridDX, nGridDY) );
1830 pEntry->aGridRect = aGridRect;
1831 Center( pEntry );
1832 AdjustVirtSize( pEntry->aRect );
1833 pGridMap->OccupyGrids( pEntry );
1837 void SvxIconChoiceCtrl_Impl::SetCursor( SvxIconChoiceCtrlEntry* pEntry )
1839 if( pEntry == pCursor )
1841 if( pCursor && eSelectionMode == SelectionMode::Single &&
1842 !pCursor->IsSelected() )
1843 SelectEntry( pCursor, true );
1844 return;
1846 ShowCursor( false );
1847 SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
1848 pCursor = pEntry;
1849 if( pOldCursor )
1851 pOldCursor->ClearFlags( SvxIconViewFlags::FOCUSED );
1852 if( eSelectionMode == SelectionMode::Single )
1853 SelectEntry( pOldCursor, false ); // deselect old cursor
1855 if( pCursor )
1857 ToTop( pCursor );
1858 pCursor->SetFlags( SvxIconViewFlags::FOCUSED );
1859 if( eSelectionMode == SelectionMode::Single )
1860 SelectEntry( pCursor, true );
1861 ShowCursor( true );
1866 void SvxIconChoiceCtrl_Impl::ShowCursor( bool bShow )
1868 if( !pCursor || !bShow || !pView->HasFocus() )
1870 pView->HideFocus();
1871 return;
1873 tools::Rectangle aRect ( CalcFocusRect( pCursor ) );
1874 /*pView->*/ShowFocus( aRect );
1878 void SvxIconChoiceCtrl_Impl::HideDDIcon()
1880 pView->Update();
1881 if( pDDDev )
1883 Size aSize( pDDDev->GetOutputSizePixel() );
1884 // restore pView
1885 pView->DrawOutDev( Point(), aSize, Point(), aSize, *pDDDev );
1887 pDDBufDev = pDDDev;
1888 pDDDev = nullptr;
1891 bool SvxIconChoiceCtrl_Impl::HandleScrollCommand( const CommandEvent& rCmd )
1893 tools::Rectangle aDocRect( Point(), aVirtOutputSize );
1894 tools::Rectangle aVisRect( GetOutputRect() );
1895 if( aVisRect.IsInside( aDocRect ))
1896 return false;
1897 Size aDocSize( aDocRect.GetSize() );
1898 Size aVisSize( aVisRect.GetSize() );
1899 bool bHor = aDocSize.Width() > aVisSize.Width();
1900 bool bVer = aDocSize.Height() > aVisSize.Height();
1902 long nScrollDX = 0, nScrollDY = 0;
1904 switch( rCmd.GetCommand() )
1906 case CommandEventId::StartAutoScroll:
1908 pView->EndTracking();
1909 StartAutoScrollFlags nScrollFlags = StartAutoScrollFlags::NONE;
1910 if( bHor )
1911 nScrollFlags |= StartAutoScrollFlags::Horz;
1912 if( bVer )
1913 nScrollFlags |= StartAutoScrollFlags::Vert;
1914 if( nScrollFlags != StartAutoScrollFlags::NONE )
1916 pView->StartAutoScroll( nScrollFlags );
1917 return true;
1920 break;
1922 case CommandEventId::Wheel:
1924 const CommandWheelData* pData = rCmd.GetWheelData();
1925 if( pData && (CommandWheelMode::SCROLL == pData->GetMode()) && !pData->IsHorz() )
1927 sal_uLong nScrollLines = pData->GetScrollLines();
1928 if( nScrollLines == COMMAND_WHEEL_PAGESCROLL )
1930 nScrollDY = GetScrollBarPageSize( aVisSize.Width() );
1931 if( pData->GetDelta() < 0 )
1932 nScrollDY *= -1;
1934 else
1936 nScrollDY = pData->GetNotchDelta() * static_cast<long>(nScrollLines);
1937 nScrollDY *= GetScrollBarLineSize();
1941 break;
1943 case CommandEventId::AutoScroll:
1945 const CommandScrollData* pData = rCmd.GetAutoScrollData();
1946 if( pData )
1948 nScrollDX = pData->GetDeltaX() * GetScrollBarLineSize();
1949 nScrollDY = pData->GetDeltaY() * GetScrollBarLineSize();
1952 break;
1954 default: break;
1957 if( nScrollDX || nScrollDY )
1959 aVisRect.AdjustTop( -nScrollDY );
1960 aVisRect.AdjustBottom( -nScrollDY );
1961 aVisRect.AdjustLeft( -nScrollDX );
1962 aVisRect.AdjustRight( -nScrollDX );
1963 MakeVisible( aVisRect );
1964 return true;
1966 return false;
1970 void SvxIconChoiceCtrl_Impl::Command( const CommandEvent& rCEvt )
1972 // scroll mouse event?
1973 if( (rCEvt.GetCommand() == CommandEventId::Wheel) ||
1974 (rCEvt.GetCommand() == CommandEventId::StartAutoScroll) ||
1975 (rCEvt.GetCommand() == CommandEventId::AutoScroll) )
1977 if( HandleScrollCommand( rCEvt ) )
1978 return;
1982 void SvxIconChoiceCtrl_Impl::ToTop( SvxIconChoiceCtrlEntry* pEntry )
1984 if( maZOrderList.empty() || pEntry == maZOrderList.back())
1985 return;
1987 auto it = std::find(maZOrderList.begin(), maZOrderList.end(), pEntry);
1988 if (it != maZOrderList.end())
1990 maZOrderList.erase( it );
1991 maZOrderList.push_back( pEntry );
1995 void SvxIconChoiceCtrl_Impl::ClipAtVirtOutRect( tools::Rectangle& rRect ) const
1997 if( rRect.Bottom() >= aVirtOutputSize.Height() )
1998 rRect.SetBottom( aVirtOutputSize.Height() - 1 );
1999 if( rRect.Right() >= aVirtOutputSize.Width() )
2000 rRect.SetRight( aVirtOutputSize.Width() - 1 );
2001 if( rRect.Top() < 0 )
2002 rRect.SetTop( 0 );
2003 if( rRect.Left() < 0 )
2004 rRect.SetLeft( 0 );
2007 // rRect: area of the document (in document coordinates) that we want to make
2008 // visible
2009 // bScrBar == true: rectangle was calculated because of a scrollbar event
2011 void SvxIconChoiceCtrl_Impl::MakeVisible( const tools::Rectangle& rRect, bool bScrBar )
2013 tools::Rectangle aVirtRect( rRect );
2014 ClipAtVirtOutRect( aVirtRect );
2015 Point aOrigin( pView->GetMapMode().GetOrigin() );
2016 // convert to document coordinate
2017 aOrigin *= -1;
2018 tools::Rectangle aOutputArea( GetOutputRect() );
2019 if( aOutputArea.IsInside( aVirtRect ) )
2020 return; // is already visible
2022 long nDy;
2023 if( aVirtRect.Top() < aOutputArea.Top() )
2025 // scroll up (nDy < 0)
2026 nDy = aVirtRect.Top() - aOutputArea.Top();
2028 else if( aVirtRect.Bottom() > aOutputArea.Bottom() )
2030 // scroll down (nDy > 0)
2031 nDy = aVirtRect.Bottom() - aOutputArea.Bottom();
2033 else
2034 nDy = 0;
2036 long nDx;
2037 if( aVirtRect.Left() < aOutputArea.Left() )
2039 // scroll to the left (nDx < 0)
2040 nDx = aVirtRect.Left() - aOutputArea.Left();
2042 else if( aVirtRect.Right() > aOutputArea.Right() )
2044 // scroll to the right (nDx > 0)
2045 nDx = aVirtRect.Right() - aOutputArea.Right();
2047 else
2048 nDx = 0;
2050 aOrigin.AdjustX(nDx );
2051 aOrigin.AdjustY(nDy );
2052 aOutputArea.SetPos( aOrigin );
2053 if( GetUpdateMode() )
2055 HideDDIcon();
2056 pView->Update();
2057 ShowCursor( false );
2060 // invert origin for SV (so we can scroll/paint using document coordinates)
2061 aOrigin *= -1;
2062 SetOrigin( aOrigin );
2064 bool bScrollable = pView->GetBackground().IsScrollable();
2066 if( bScrollable && GetUpdateMode() )
2068 // scroll in reverse direction!
2069 pView->Control::Scroll( -nDx, -nDy, aOutputArea,
2070 ScrollFlags::NoChildren | ScrollFlags::UseClipRegion | ScrollFlags::Clip );
2072 else
2073 pView->Invalidate(InvalidateFlags::NoChildren);
2075 if( aHorSBar->IsVisible() || aVerSBar->IsVisible() )
2077 if( !bScrBar )
2079 aOrigin *= -1;
2080 // correct thumbs
2081 if(aHorSBar->IsVisible() && aHorSBar->GetThumbPos() != aOrigin.X())
2082 aHorSBar->SetThumbPos( aOrigin.X() );
2083 if(aVerSBar->IsVisible() && aVerSBar->GetThumbPos() != aOrigin.Y())
2084 aVerSBar->SetThumbPos( aOrigin.Y() );
2088 if( GetUpdateMode() )
2089 ShowCursor( true );
2091 // check if we still need scrollbars
2092 CheckScrollBars();
2093 if( bScrollable && GetUpdateMode() )
2094 pView->Update();
2096 // If the requested area can not be made completely visible, the
2097 // Vis-Rect-Changed handler is called in any case. This case may occur e.g.
2098 // if only few pixels of the lower border are invisible, but a scrollbar has
2099 // a larger line size.
2100 VisRectChanged();
2103 sal_Int32 SvxIconChoiceCtrl_Impl::GetSelectionCount() const
2105 if( (nWinBits & WB_HIGHLIGHTFRAME) && pCurHighlightFrame )
2106 return 1;
2107 return nSelectionCount;
2110 void SvxIconChoiceCtrl_Impl::ToggleSelection( SvxIconChoiceCtrlEntry* pEntry )
2112 bool bSel;
2113 bSel = !pEntry->IsSelected();
2114 SelectEntry( pEntry, bSel, true );
2117 void SvxIconChoiceCtrl_Impl::DeselectAllBut( SvxIconChoiceCtrlEntry const * pThisEntryNot )
2119 ClearSelectedRectList();
2121 // TODO: work through z-order list, if necessary!
2123 size_t nCount = maEntries.size();
2124 for( size_t nCur = 0; nCur < nCount; nCur++ )
2126 SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
2127 if( pEntry != pThisEntryNot && pEntry->IsSelected() )
2128 SelectEntry( pEntry, false, true );
2130 pAnchor = nullptr;
2131 nFlags &= ~IconChoiceFlags::AddMode;
2134 Size SvxIconChoiceCtrl_Impl::GetMinGrid() const
2136 Size aMinSize( aImageSize );
2137 aMinSize.AdjustWidth(2 * LROFFS_BOUND );
2138 aMinSize.AdjustHeight(TBOFFS_BOUND ); // single offset is enough (FileDlg)
2139 OUString const aStrDummy( "XXX" );
2140 Size aTextSize( pView->GetTextWidth( aStrDummy ), pView->GetTextHeight() );
2141 if( nWinBits & WB_ICON )
2143 aMinSize.AdjustHeight(VER_DIST_BMP_STRING );
2144 aMinSize.AdjustHeight(aTextSize.Height() );
2146 else
2148 aMinSize.AdjustWidth(HOR_DIST_BMP_STRING );
2149 aMinSize.AdjustWidth(aTextSize.Width() );
2151 return aMinSize;
2154 void SvxIconChoiceCtrl_Impl::SetGrid( const Size& rSize )
2156 Size aSize( rSize );
2157 Size aMinSize( GetMinGrid() );
2158 if( aSize.Width() < aMinSize.Width() )
2159 aSize.setWidth( aMinSize.Width() );
2160 if( aSize.Height() < aMinSize.Height() )
2161 aSize.setHeight( aMinSize.Height() );
2163 nGridDX = aSize.Width();
2164 // HACK: Detail mode is not yet fully implemented, this workaround makes it
2165 // fly with a single column
2166 if( nWinBits & WB_DETAILS )
2168 const SvxIconChoiceCtrlColumnInfo* pCol = GetColumn( 0 );
2169 if( pCol )
2170 const_cast<SvxIconChoiceCtrlColumnInfo*>(pCol)->SetWidth( nGridDX );
2172 nGridDY = aSize.Height();
2173 SetDefaultTextSize();
2176 // Calculates the maximum size that the text rectangle may use within its
2177 // bounding rectangle. In WB_ICON mode with SvxIconChoiceCtrlTextMode::Full, Bottom is set to
2178 // LONG_MAX.
2180 tools::Rectangle SvxIconChoiceCtrl_Impl::CalcMaxTextRect( const SvxIconChoiceCtrlEntry* pEntry ) const
2182 tools::Rectangle aBoundRect;
2183 // avoid infinite recursion: don't calculate the bounding rectangle here
2184 if( IsBoundingRectValid( pEntry->aRect ) )
2185 aBoundRect = pEntry->aRect;
2186 else
2187 aBoundRect = pEntry->aGridRect;
2189 tools::Rectangle aBmpRect( const_cast<SvxIconChoiceCtrl_Impl*>(this)->CalcBmpRect(
2190 const_cast<SvxIconChoiceCtrlEntry*>(pEntry) ) );
2191 if( nWinBits & WB_ICON )
2193 aBoundRect.SetTop( aBmpRect.Bottom() );
2194 aBoundRect.AdjustTop(VER_DIST_BMP_STRING );
2195 if( aBoundRect.Top() > aBoundRect.Bottom())
2196 aBoundRect.SetTop( aBoundRect.Bottom() );
2197 aBoundRect.AdjustLeft(LROFFS_BOUND );
2198 aBoundRect.AdjustLeft( 1 );
2199 aBoundRect.AdjustRight( -(LROFFS_BOUND) );
2200 aBoundRect.AdjustRight( -1 );
2201 if( aBoundRect.Left() > aBoundRect.Right())
2202 aBoundRect.SetLeft( aBoundRect.Right() );
2203 if( pEntry->GetTextMode() == SvxIconChoiceCtrlTextMode::Full )
2204 aBoundRect.SetBottom( LONG_MAX );
2206 else
2208 aBoundRect.SetLeft( aBmpRect.Right() );
2209 aBoundRect.AdjustLeft(HOR_DIST_BMP_STRING );
2210 aBoundRect.AdjustRight( -(LROFFS_BOUND) );
2211 if( aBoundRect.Left() > aBoundRect.Right() )
2212 aBoundRect.SetLeft( aBoundRect.Right() );
2213 long nHeight = aBoundRect.GetSize().Height();
2214 nHeight = nHeight - aDefaultTextSize.Height();
2215 nHeight /= 2;
2216 aBoundRect.AdjustTop(nHeight );
2217 aBoundRect.AdjustBottom( -nHeight );
2219 return aBoundRect;
2222 void SvxIconChoiceCtrl_Impl::SetDefaultTextSize()
2224 long nDY = nGridDY;
2225 nDY -= aImageSize.Height();
2226 nDY -= VER_DIST_BMP_STRING;
2227 nDY -= 2 * TBOFFS_BOUND;
2228 if (nDY <= 0)
2229 nDY = 2;
2231 long nDX = nGridDX;
2232 nDX -= 2 * LROFFS_BOUND;
2233 nDX -= 2;
2234 if (nDX <= 0)
2235 nDX = 2;
2237 long nHeight = pView->GetTextHeight();
2238 if (nDY < nHeight)
2239 nDY = nHeight;
2240 if(pView->GetDPIScaleFactor() > 1)
2242 nDY*=2;
2244 aDefaultTextSize = Size(nDX, nDY);
2248 void SvxIconChoiceCtrl_Impl::Center( SvxIconChoiceCtrlEntry* pEntry ) const
2250 pEntry->aRect = pEntry->aGridRect;
2251 Size aSize( CalcBoundingSize() );
2252 if( nWinBits & WB_ICON )
2254 // center horizontally
2255 long nBorder = pEntry->aGridRect.GetWidth() - aSize.Width();
2256 pEntry->aRect.AdjustLeft(nBorder / 2 );
2257 pEntry->aRect.AdjustRight( -(nBorder / 2) );
2259 // center vertically
2260 pEntry->aRect.SetBottom( pEntry->aRect.Top() + aSize.Height() );
2264 // The deltas are the offsets by which the view is moved on the document.
2265 // left, up: offsets < 0
2266 // right, down: offsets > 0
2267 void SvxIconChoiceCtrl_Impl::Scroll( long nDeltaX, long nDeltaY )
2269 const MapMode& rMapMode = pView->GetMapMode();
2270 Point aOrigin( rMapMode.GetOrigin() );
2271 // convert to document coordinate
2272 aOrigin *= -1;
2273 aOrigin.AdjustY(nDeltaY );
2274 aOrigin.AdjustX(nDeltaX );
2275 tools::Rectangle aRect( aOrigin, aOutputSize );
2276 MakeVisible( aRect, true/*bScrollBar*/ );
2280 const Size& SvxIconChoiceCtrl_Impl::GetItemSize( IcnViewFieldType eItem ) const
2282 if (eItem == IcnViewFieldType::Text)
2283 return aDefaultTextSize;
2284 return aImageSize; // IcnViewFieldType::Image
2287 tools::Rectangle SvxIconChoiceCtrl_Impl::CalcFocusRect( SvxIconChoiceCtrlEntry* pEntry )
2289 tools::Rectangle aTextRect( CalcTextRect( pEntry ) );
2290 tools::Rectangle aBoundRect( GetEntryBoundRect( pEntry ) );
2291 return tools::Rectangle(
2292 aBoundRect.Left(), aBoundRect.Top() - 1, aBoundRect.Right() - 1,
2293 aTextRect.Bottom() + 1);
2296 // the hot spot is the inner 50% of the rectangle
2297 static tools::Rectangle GetHotSpot( const tools::Rectangle& rRect )
2299 tools::Rectangle aResult( rRect );
2300 aResult.Justify();
2301 Size aSize( rRect.GetSize() );
2302 long nDelta = aSize.Width() / 4;
2303 aResult.AdjustLeft(nDelta );
2304 aResult.AdjustRight( -nDelta );
2305 nDelta = aSize.Height() / 4;
2306 aResult.AdjustTop(nDelta );
2307 aResult.AdjustBottom( -nDelta );
2308 return aResult;
2311 void SvxIconChoiceCtrl_Impl::SelectRect( SvxIconChoiceCtrlEntry* pEntry1, SvxIconChoiceCtrlEntry* pEntry2,
2312 bool bAdd, std::vector<tools::Rectangle>* pOtherRects )
2314 DBG_ASSERT(pEntry1 && pEntry2,"SelectEntry: Invalid Entry-Ptr");
2315 tools::Rectangle aRect( GetEntryBoundRect( pEntry1 ) );
2316 aRect.Union( GetEntryBoundRect( pEntry2 ) );
2317 SelectRect( aRect, bAdd, pOtherRects );
2320 void SvxIconChoiceCtrl_Impl::SelectRect( const tools::Rectangle& rRect, bool bAdd,
2321 std::vector<tools::Rectangle>* pOtherRects )
2323 aCurSelectionRect = rRect;
2324 if( maZOrderList.empty() )
2325 return;
2327 // set flag, so ToTop won't be called in Select
2328 bool bAlreadySelectingRect(nFlags & IconChoiceFlags::SelectingRect);
2329 nFlags |= IconChoiceFlags::SelectingRect;
2331 CheckBoundingRects();
2332 pView->Update();
2333 const size_t nCount = maZOrderList.size();
2335 tools::Rectangle aRect( rRect );
2336 aRect.Justify();
2337 bool bCalcOverlap = (bAdd && pOtherRects && !pOtherRects->empty());
2339 bool bResetClipRegion = false;
2340 if( !pView->IsClipRegion() )
2342 bResetClipRegion = true;
2343 pView->SetClipRegion(vcl::Region(GetOutputRect()));
2346 for( size_t nPos = 0; nPos < nCount; nPos++ )
2348 SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nPos ];
2350 if( !IsBoundingRectValid( pEntry->aRect ))
2351 FindBoundingRect( pEntry );
2352 tools::Rectangle aBoundRect( GetHotSpot( pEntry->aRect ) );
2353 bool bSelected = pEntry->IsSelected();
2355 bool bOverlaps;
2356 if( bCalcOverlap )
2357 bOverlaps = IsOver( pOtherRects, aBoundRect );
2358 else
2359 bOverlaps = false;
2360 bool bOver = aRect.IsOver( aBoundRect );
2362 if( bOver && !bOverlaps )
2364 // is inside the new selection rectangle and outside of any old one
2365 // => select
2366 if( !bSelected )
2367 SelectEntry( pEntry, true, true );
2369 else if( !bAdd )
2371 // is outside of the selection rectangle
2372 // => deselect
2373 if( bSelected )
2374 SelectEntry( pEntry, false, true );
2376 else if (bOverlaps)
2378 // The entry is inside an old (=>span multiple rectangles with Ctrl)
2379 // selection rectangle.
2381 // There is still a bug here! The selection status of an entry in a
2382 // previous rectangle has to be restored, if it was touched by the
2383 // current selection rectangle but is not inside it any more.
2384 // For simplicity's sake, let's assume that all entries in the old
2385 // rectangles were correctly selected. It is wrong to just deselect
2386 // the intersection.
2387 // Possible solution: remember a snapshot of the selection before
2388 // spanning the rectangle.
2389 if( aBoundRect.IsOver( rRect))
2391 // deselect intersection between old rectangles and current rectangle
2392 if( bSelected )
2393 SelectEntry( pEntry, false, true );
2395 else
2397 // select entry of an old rectangle
2398 if( !bSelected )
2399 SelectEntry( pEntry, true, true );
2402 else if( !bOver && bSelected )
2404 // this entry is completely outside the rectangle => deselect it
2405 SelectEntry( pEntry, false, true );
2409 if( !bAlreadySelectingRect )
2410 nFlags &= ~IconChoiceFlags::SelectingRect;
2412 pView->Update();
2413 if( bResetClipRegion )
2414 pView->SetClipRegion();
2417 void SvxIconChoiceCtrl_Impl::SelectRange(
2418 SvxIconChoiceCtrlEntry const * pStart,
2419 SvxIconChoiceCtrlEntry const * pEnd,
2420 bool bAdd )
2422 sal_uLong nFront = GetEntryListPos( pStart );
2423 sal_uLong nBack = GetEntryListPos( pEnd );
2424 sal_uLong nFirst = std::min( nFront, nBack );
2425 sal_uLong nLast = std::max( nFront, nBack );
2426 sal_uLong i;
2427 SvxIconChoiceCtrlEntry* pEntry;
2429 if ( ! bAdd )
2431 // deselect everything before the first entry if not in
2432 // adding mode
2433 for ( i=0; i<nFirst; i++ )
2435 pEntry = GetEntry( i );
2436 if( pEntry->IsSelected() )
2437 SelectEntry( pEntry, false, true );
2441 // select everything between nFirst and nLast
2442 for ( i=nFirst; i<=nLast; i++ )
2444 pEntry = GetEntry( i );
2445 if( ! pEntry->IsSelected() )
2446 SelectEntry( pEntry, true, true );
2449 if ( ! bAdd )
2451 // deselect everything behind the last entry if not in
2452 // adding mode
2453 sal_uLong nEnd = GetEntryCount();
2454 for ( ; i<nEnd; i++ )
2456 pEntry = GetEntry( i );
2457 if( pEntry->IsSelected() )
2458 SelectEntry( pEntry, false, true );
2463 bool SvxIconChoiceCtrl_Impl::IsOver( std::vector<tools::Rectangle>* pRectList, const tools::Rectangle& rBoundRect )
2465 const sal_uInt16 nCount = pRectList->size();
2466 for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
2468 tools::Rectangle& rRect = (*pRectList)[ nCur ];
2469 if( rBoundRect.IsOver( rRect ))
2470 return true;
2472 return false;
2475 void SvxIconChoiceCtrl_Impl::AddSelectedRect( SvxIconChoiceCtrlEntry* pEntry1,
2476 SvxIconChoiceCtrlEntry* pEntry2 )
2478 DBG_ASSERT(pEntry1 && pEntry2,"SelectEntry: Invalid Entry-Ptr");
2479 tools::Rectangle aRect( GetEntryBoundRect( pEntry1 ) );
2480 aRect.Union( GetEntryBoundRect( pEntry2 ) );
2481 AddSelectedRect( aRect );
2484 void SvxIconChoiceCtrl_Impl::AddSelectedRect( const tools::Rectangle& rRect )
2486 tools::Rectangle newRect = rRect;
2487 newRect.Justify();
2488 aSelectedRectList.push_back( newRect );
2491 void SvxIconChoiceCtrl_Impl::ClearSelectedRectList()
2493 aSelectedRectList.clear();
2496 IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, AutoArrangeHdl, Timer *, void)
2498 aAutoArrangeIdle.Stop();
2499 Arrange( IsAutoArrange(), 0, 0 );
2502 IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, VisRectChangedHdl, Timer *, void)
2504 aVisRectChangedIdle.Stop();
2507 IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, DocRectChangedHdl, Timer *, void)
2509 aDocRectChangedIdle.Stop();
2512 #ifdef DBG_UTIL
2513 void SvxIconChoiceCtrl_Impl::SetEntryTextMode( SvxIconChoiceCtrlTextMode eMode, SvxIconChoiceCtrlEntry* pEntry )
2515 if( !pEntry )
2517 if( eTextMode != eMode )
2519 eTextMode = eMode;
2520 Arrange( true, 0, 0 );
2523 else
2525 if( pEntry->eTextMode != eMode )
2527 pEntry->eTextMode = eMode;
2528 InvalidateEntry( pEntry );
2529 pView->Invalidate( GetEntryBoundRect( pEntry ) );
2530 AdjustVirtSize( pEntry->aRect );
2534 #endif
2536 // Draw my own focusrect, because the focusrect of the outputdevice has got the inverted color
2537 // of the background. But what will we see, if the backgroundcolor is gray ? - We will see
2538 // a gray focusrect on a gray background !!!
2540 void SvxIconChoiceCtrl_Impl::ShowFocus ( tools::Rectangle const & rRect )
2542 Color aBkgColor(pView->GetBackground().GetColor());
2543 Color aPenColor;
2544 sal_uInt16 nColor = ( aBkgColor.GetRed() + aBkgColor.GetGreen() + aBkgColor.GetBlue() ) / 3;
2545 if (nColor > 128)
2546 aPenColor = COL_BLACK;
2547 else
2548 aPenColor = COL_WHITE;
2550 aFocus.aPenColor = aPenColor;
2551 aFocus.aRect = rRect;
2554 void SvxIconChoiceCtrl_Impl::DrawFocusRect(vcl::RenderContext& rRenderContext)
2556 rRenderContext.SetLineColor(aFocus.aPenColor);
2557 rRenderContext.SetFillColor();
2558 tools::Polygon aPolygon (aFocus.aRect);
2560 LineInfo aLineInfo(LineStyle::Dash);
2562 aLineInfo.SetDashLen(1);
2563 aLineInfo.SetDotLen(1);
2564 aLineInfo.SetDistance(1);
2565 aLineInfo.SetDotCount(1);
2567 rRenderContext.DrawPolyLine(aPolygon, aLineInfo);
2570 bool SvxIconChoiceCtrl_Impl::IsMnemonicChar( sal_Unicode cChar, sal_uLong& rPos ) const
2572 bool bRet = false;
2573 const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
2574 size_t nEntryCount = GetEntryCount();
2575 for ( size_t i = 0; i < nEntryCount; ++i )
2577 if ( rI18nHelper.MatchMnemonic( GetEntry( i )->GetText(), cChar ) )
2579 bRet = true;
2580 rPos = i;
2581 break;
2585 return bRet;
2589 IMPL_LINK(SvxIconChoiceCtrl_Impl, UserEventHdl, void*, nId, void )
2591 if( nId == EVENTID_ADJUST_SCROLLBARS )
2593 nUserEventAdjustScrBars = nullptr;
2594 AdjustScrollBars();
2596 else if( nId == EVENTID_SHOW_CURSOR )
2598 ShowCursor( true );
2602 void SvxIconChoiceCtrl_Impl::CancelUserEvents()
2604 if( nUserEventAdjustScrBars )
2606 Application::RemoveUserEvent( nUserEventAdjustScrBars );
2607 nUserEventAdjustScrBars = nullptr;
2611 void SvxIconChoiceCtrl_Impl::InvalidateEntry( SvxIconChoiceCtrlEntry* pEntry )
2613 if( pEntry == pCursor )
2614 ShowCursor( false );
2615 pView->Invalidate( pEntry->aRect );
2616 Center( pEntry );
2617 pView->Invalidate( pEntry->aRect );
2618 if( pEntry == pCursor )
2619 ShowCursor( true );
2622 SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetFirstSelectedEntry() const
2624 if( !GetSelectionCount() )
2625 return nullptr;
2627 if( (nWinBits & WB_HIGHLIGHTFRAME) && (eSelectionMode == SelectionMode::NONE) )
2629 return pCurHighlightFrame;
2632 size_t nCount = maEntries.size();
2633 if( !pHead )
2635 for( size_t nCur = 0; nCur < nCount; nCur++ )
2637 SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
2638 if( pEntry->IsSelected() )
2640 return pEntry;
2644 else
2646 SvxIconChoiceCtrlEntry* pEntry = pHead;
2647 while( nCount-- )
2649 if( pEntry->IsSelected() )
2651 return pEntry;
2653 pEntry = pEntry->pflink;
2654 if( nCount && pEntry == pHead )
2656 OSL_FAIL("SvxIconChoiceCtrl_Impl::GetFirstSelectedEntry > infinite loop!");
2657 return nullptr;
2661 return nullptr;
2664 void SvxIconChoiceCtrl_Impl::SelectAll()
2666 size_t nCount = maEntries.size();
2667 for( size_t nCur = 0; nCur < nCount; nCur++ )
2669 SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
2670 SelectEntry( pEntry, true/*bSelect*/, true );
2672 nFlags &= ~IconChoiceFlags::AddMode;
2673 pAnchor = nullptr;
2679 sal_Int32 SvxIconChoiceCtrl_Impl::GetEntryListPos( SvxIconChoiceCtrlEntry const * pEntry ) const
2681 if( !(nFlags & IconChoiceFlags::EntryListPosValid ))
2682 const_cast<SvxIconChoiceCtrl_Impl*>(this)->SetListPositions();
2683 return pEntry->nPos;
2686 void SvxIconChoiceCtrl_Impl::InitSettings()
2688 const StyleSettings& rStyleSettings = pView->GetSettings().GetStyleSettings();
2690 // unit (from settings) is Point
2691 vcl::Font aFont( rStyleSettings.GetFieldFont() );
2692 aFont.SetColor( rStyleSettings.GetWindowTextColor() );
2693 pView->SetPointFont( aFont );
2694 SetDefaultTextSize();
2696 pView->SetTextColor( rStyleSettings.GetFieldTextColor() );
2697 pView->SetTextFillColor();
2699 pView->SetBackground( rStyleSettings.GetFieldColor());
2701 long nScrBarSize = rStyleSettings.GetScrollBarSize();
2702 if( nScrBarSize == nHorSBarHeight && nScrBarSize == nVerSBarWidth )
2703 return;
2705 nHorSBarHeight = nScrBarSize;
2706 Size aSize( aHorSBar->GetSizePixel() );
2707 aSize.setHeight( nScrBarSize );
2708 aHorSBar->Hide();
2709 aHorSBar->SetSizePixel( aSize );
2711 nVerSBarWidth = nScrBarSize;
2712 aSize = aVerSBar->GetSizePixel();
2713 aSize.setWidth( nScrBarSize );
2714 aVerSBar->Hide();
2715 aVerSBar->SetSizePixel( aSize );
2717 Size aOSize( pView->Control::GetOutputSizePixel() );
2718 PositionScrollBars( aOSize.Width(), aOSize.Height() );
2719 AdjustScrollBars();
2722 void SvxIconChoiceCtrl_Impl::SetPositionMode( SvxIconChoiceCtrlPositionMode eMode )
2724 if( eMode == ePositionMode )
2725 return;
2727 SvxIconChoiceCtrlPositionMode eOldMode = ePositionMode;
2728 ePositionMode = eMode;
2729 size_t nCount = maEntries.size();
2731 if( eOldMode == SvxIconChoiceCtrlPositionMode::AutoArrange )
2733 // when positioning moved entries "hard", there are problems with
2734 // unwanted overlaps, as these entries aren't taken into account in
2735 // Arrange.
2736 if( maEntries.size() )
2737 aAutoArrangeIdle.Start();
2738 return;
2741 if( ePositionMode == SvxIconChoiceCtrlPositionMode::AutoArrange )
2743 for( size_t nCur = 0; nCur < nCount; nCur++ )
2745 SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
2746 if( pEntry->GetFlags() & SvxIconViewFlags(SvxIconViewFlags::POS_LOCKED | SvxIconViewFlags::POS_MOVED))
2747 SetEntryPos(pEntry, GetEntryBoundRect( pEntry ).TopLeft());
2750 if( maEntries.size() )
2751 aAutoArrangeIdle.Start();
2755 void SvxIconChoiceCtrl_Impl::SetEntryPredecessor( SvxIconChoiceCtrlEntry* pEntry,
2756 SvxIconChoiceCtrlEntry* pPredecessor )
2758 if( !IsAutoArrange() )
2759 return;
2761 if( pEntry == pPredecessor )
2762 return;
2764 sal_uLong nPos1 = GetEntryListPos( pEntry );
2765 if( !pHead )
2767 if( pPredecessor )
2769 sal_uLong nPos2 = GetEntryListPos( pPredecessor );
2770 if( nPos1 == (nPos2 + 1) )
2771 return; // is already the predecessor
2773 else if( !nPos1 )
2774 return;
2776 InitPredecessors();
2779 if( !pPredecessor && pHead == pEntry )
2780 return; // is already the first one
2782 bool bSetHead = false;
2783 if( !pPredecessor )
2785 bSetHead = true;
2786 pPredecessor = pHead->pblink;
2788 if( pEntry == pHead )
2790 pHead = pHead->pflink;
2791 bSetHead = false;
2793 if( pEntry != pPredecessor )
2795 pEntry->Unlink();
2796 pEntry->SetBacklink( pPredecessor );
2798 if( bSetHead )
2799 pHead = pEntry;
2800 aAutoArrangeIdle.Start();
2803 SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::FindEntryPredecessor( SvxIconChoiceCtrlEntry* pEntry,
2804 const Point& rPosTopLeft )
2806 Point aPos( rPosTopLeft ); //TopLeft
2807 tools::Rectangle aCenterRect( CalcBmpRect( pEntry, &aPos ));
2808 Point aNewPos( aCenterRect.Center() );
2809 sal_uLong nGrid = GetPredecessorGrid( aNewPos );
2810 size_t nCount = maEntries.size();
2811 if( nGrid == ULONG_MAX )
2812 return nullptr;
2813 if( nGrid >= nCount )
2814 nGrid = nCount - 1;
2815 if( !pHead )
2816 return maEntries[ nGrid ].get();
2818 SvxIconChoiceCtrlEntry* pCur = pHead; // Grid 0
2819 // TODO: go through list from the end if nGrid > nCount/2
2820 for( sal_uLong nCur = 0; nCur < nGrid; nCur++ )
2821 pCur = pCur->pflink;
2823 return pCur;
2826 sal_uLong SvxIconChoiceCtrl_Impl::GetPredecessorGrid( const Point& rPos) const
2828 Point aPos( rPos );
2829 aPos.AdjustX( -(LROFFS_WINBORDER) );
2830 aPos.AdjustY( -(TBOFFS_WINBORDER) );
2831 long nMaxCol = aVirtOutputSize.Width() / nGridDX;
2832 if( nMaxCol )
2833 nMaxCol--;
2834 long nGridX = aPos.X() / nGridDX;
2835 if( nGridX > nMaxCol )
2836 nGridX = nMaxCol;
2837 long nGridY = aPos.Y() / nGridDY;
2838 long nGridsX = aOutputSize.Width() / nGridDX;
2839 sal_uLong nGrid = (nGridY * nGridsX) + nGridX;
2840 long nMiddle = (nGridX * nGridDX) + (nGridDX / 2);
2841 if( rPos.X() < nMiddle )
2843 if( !nGrid )
2844 nGrid = ULONG_MAX;
2845 else
2846 nGrid--;
2848 return nGrid;
2851 bool SvxIconChoiceCtrl_Impl::RequestHelp( const HelpEvent& rHEvt )
2853 if ( !(rHEvt.GetMode() & HelpEventMode::QUICK ) )
2854 return false;
2856 Point aPos( pView->ScreenToOutputPixel(rHEvt.GetMousePosPixel() ) );
2857 aPos -= pView->GetMapMode().GetOrigin();
2858 SvxIconChoiceCtrlEntry* pEntry = GetEntry( aPos, true );
2860 if ( !pEntry )
2861 return false;
2863 OUString sQuickHelpText = pEntry->GetQuickHelpText();
2864 OUString aEntryText( SvtIconChoiceCtrl::GetEntryText( pEntry ) );
2865 tools::Rectangle aTextRect( CalcTextRect( pEntry, nullptr, &aEntryText ) );
2866 if ( ( !aTextRect.IsInside( aPos ) || aEntryText.isEmpty() ) && sQuickHelpText.isEmpty() )
2867 return false;
2869 tools::Rectangle aOptTextRect( aTextRect );
2870 aOptTextRect.SetBottom( LONG_MAX );
2871 DrawTextFlags nNewFlags = nCurTextDrawFlags;
2872 nNewFlags &= ~DrawTextFlags( DrawTextFlags::Clip | DrawTextFlags::EndEllipsis );
2873 aOptTextRect = pView->GetTextRect( aOptTextRect, aEntryText, nNewFlags );
2874 if ( aOptTextRect != aTextRect || !sQuickHelpText.isEmpty() )
2876 //aTextRect.Right() = aTextRect.Left() + aRealSize.Width() + 4;
2877 Point aPt( aOptTextRect.TopLeft() );
2878 aPt += pView->GetMapMode().GetOrigin();
2879 aPt = pView->OutputToScreenPixel( aPt );
2880 // subtract border of tooltip help
2881 aPt.AdjustY( -1 );
2882 aPt.AdjustX( -3 );
2883 aOptTextRect.SetPos( aPt );
2884 OUString sHelpText;
2885 if ( !sQuickHelpText.isEmpty() )
2886 sHelpText = sQuickHelpText;
2887 else
2888 sHelpText = aEntryText;
2889 Help::ShowQuickHelp( static_cast<vcl::Window*>(pView), aOptTextRect, sHelpText, QuickHelpFlags::Left | QuickHelpFlags::VCenter );
2892 return true;
2895 void SvxIconChoiceCtrl_Impl::ClearColumnList()
2897 m_pColumns.reset();
2900 void SvxIconChoiceCtrl_Impl::SetColumn( sal_uInt16 nIndex, const SvxIconChoiceCtrlColumnInfo& rInfo)
2902 if (!m_pColumns)
2903 m_pColumns.reset(new SvxIconChoiceCtrlColumnInfoMap);
2905 SvxIconChoiceCtrlColumnInfo* pInfo = new SvxIconChoiceCtrlColumnInfo( rInfo );
2906 m_pColumns->insert(std::make_pair(nIndex, std::unique_ptr<SvxIconChoiceCtrlColumnInfo>(pInfo)));
2908 // HACK: Detail mode is not yet fully implemented, this workaround makes it
2909 // fly with a single column
2910 if( !nIndex && (nWinBits & WB_DETAILS) )
2911 nGridDX = pInfo->GetWidth();
2913 if( GetUpdateMode() )
2914 Arrange( IsAutoArrange(), 0, 0 );
2917 const SvxIconChoiceCtrlColumnInfo* SvxIconChoiceCtrl_Impl::GetColumn( sal_uInt16 nIndex ) const
2919 if (!m_pColumns)
2920 return nullptr;
2921 auto const it = m_pColumns->find( nIndex );
2922 if (it == m_pColumns->end())
2923 return nullptr;
2924 return it->second.get();
2927 void SvxIconChoiceCtrl_Impl::DrawHighlightFrame(vcl::RenderContext& rRenderContext, const tools::Rectangle& rBmpRect)
2929 tools::Rectangle aBmpRect(rBmpRect);
2930 long nBorder = 2;
2931 if (aImageSize.Width() < 32)
2932 nBorder = 1;
2933 aBmpRect.AdjustRight(nBorder );
2934 aBmpRect.AdjustLeft( -nBorder );
2935 aBmpRect.AdjustBottom(nBorder );
2936 aBmpRect.AdjustTop( -nBorder );
2938 DecorationView aDecoView(&rRenderContext);
2939 DrawHighlightFrameStyle nDecoFlags;
2940 if (bHighlightFramePressed)
2941 nDecoFlags = DrawHighlightFrameStyle::In;
2942 else
2943 nDecoFlags = DrawHighlightFrameStyle::Out;
2944 aDecoView.DrawHighlightFrame(aBmpRect, nDecoFlags);
2947 void SvxIconChoiceCtrl_Impl::SetEntryHighlightFrame( SvxIconChoiceCtrlEntry* pEntry,
2948 bool bKeepHighlightFlags )
2950 if( pEntry == pCurHighlightFrame )
2951 return;
2953 if( !bKeepHighlightFlags )
2954 bHighlightFramePressed = false;
2956 if (pCurHighlightFrame)
2958 tools::Rectangle aInvalidationRect(GetEntryBoundRect(pCurHighlightFrame));
2959 aInvalidationRect.expand(5);
2960 pCurHighlightFrame = nullptr;
2961 pView->Invalidate(aInvalidationRect);
2964 pCurHighlightFrame = pEntry;
2965 if (pEntry)
2967 tools::Rectangle aInvalidationRect(GetEntryBoundRect(pEntry));
2968 aInvalidationRect.expand(5);
2969 pView->Invalidate(aInvalidationRect);
2973 void SvxIconChoiceCtrl_Impl::CallSelectHandler()
2975 // When single-click mode is active, the selection handler should be called
2976 // synchronously, as the selection is automatically taken away once the
2977 // mouse cursor doesn't touch the object any more. Else, we might run into
2978 // missing calls to Select if the object is selected from a mouse movement,
2979 // because when starting the timer, the mouse cursor might have already left
2980 // the object.
2981 // In special cases (=>SfxFileDialog!), synchronous calls can be forced via
2982 // WB_NOASYNCSELECTHDL.
2983 if( nWinBits & (WB_NOASYNCSELECTHDL | WB_HIGHLIGHTFRAME) )
2985 pHdlEntry = nullptr;
2986 pView->ClickIcon();
2987 //pView->Select();
2989 else
2990 aCallSelectHdlIdle.Start();
2993 IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, CallSelectHdlHdl, Timer *, void)
2995 pHdlEntry = nullptr;
2996 pView->ClickIcon();
2997 //pView->Select();
3000 void SvxIconChoiceCtrl_Impl::SetOrigin( const Point& rPos )
3002 MapMode aMapMode( pView->GetMapMode() );
3003 aMapMode.SetOrigin( rPos );
3004 pView->SetMapMode( aMapMode );
3007 void SvxIconChoiceCtrl_Impl::CallEventListeners( VclEventId nEvent, void* pData )
3009 pView->CallImplEventListeners( nEvent, pData );
3013 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */