diff options
Diffstat (limited to 'vcl/source/control/imivctl1.cxx')
-rw-r--r-- | vcl/source/control/imivctl1.cxx | 2980 |
1 files changed, 2980 insertions, 0 deletions
diff --git a/vcl/source/control/imivctl1.cxx b/vcl/source/control/imivctl1.cxx new file mode 100644 index 000000000..4efb7596a --- /dev/null +++ b/vcl/source/control/imivctl1.cxx @@ -0,0 +1,2980 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <limits.h> +#include <osl/diagnose.h> +#include <tools/debug.hxx> +#include <vcl/wall.hxx> +#include <vcl/help.hxx> +#include <vcl/decoview.hxx> +#include <vcl/svapp.hxx> +#include <tools/poly.hxx> +#include <vcl/lineinfo.hxx> +#include <vcl/i18nhelp.hxx> +#include <vcl/mnemonic.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> + +#include <vcl/ivctrl.hxx> +#include "imivctl.hxx" + +#include <algorithm> +#include <memory> +#include <vcl/idle.hxx> + +static constexpr auto DRAWTEXT_FLAGS_ICON = + DrawTextFlags::Center | DrawTextFlags::Top | DrawTextFlags::EndEllipsis | + DrawTextFlags::Clip | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak | DrawTextFlags::Mnemonic; + +#define DRAWTEXT_FLAGS_SMALLICON (DrawTextFlags::Left|DrawTextFlags::EndEllipsis|DrawTextFlags::Clip) + +#define EVENTID_SHOW_CURSOR (reinterpret_cast<void*>(1)) +#define EVENTID_ADJUST_SCROLLBARS (reinterpret_cast<void*>(2)) + +SvxIconChoiceCtrl_Impl::SvxIconChoiceCtrl_Impl( + SvtIconChoiceCtrl* pCurView, + WinBits nWinStyle +) : + bChooseWithCursor(false), + aVerSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_VSCROLL) ), + aHorSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_HSCROLL) ), + aScrBarBox( VclPtr<ScrollBarBox>::Create(pCurView) ), + aAutoArrangeIdle ( "svtools contnr SvxIconChoiceCtrl_Impl AutoArrange" ), + aDocRectChangedIdle ( "svtools contnr SvxIconChoiceCtrl_Impl DocRectChanged" ), + aVisRectChangedIdle ( "svtools contnr SvxIconChoiceCtrl_Impl VisRectChanged" ), + aCallSelectHdlIdle ( "svtools contnr SvxIconChoiceCtrl_Impl CallSelectHdl" ), + aImageSize( 32 * pCurView->GetDPIScaleFactor(), 32 * pCurView->GetDPIScaleFactor()), + pView(pCurView), nMaxVirtWidth(DEFAULT_MAX_VIRT_WIDTH), nMaxVirtHeight(DEFAULT_MAX_VIRT_HEIGHT), + nFlags(IconChoiceFlags::NONE), nUserEventAdjustScrBars(nullptr), + pCurHighlightFrame(nullptr), bHighlightFramePressed(false), pHead(nullptr), pCursor(nullptr), + pHdlEntry(nullptr), + pAnchor(nullptr), eTextMode(SvxIconChoiceCtrlTextMode::Short), + eSelectionMode(SelectionMode::Multiple), ePositionMode(SvxIconChoiceCtrlPositionMode::Free), + bUpdateMode(true) +{ + SetStyle( nWinStyle ); + pImpCursor.reset( new IcnCursor_Impl( this ) ); + pGridMap.reset( new IcnGridMap_Impl( this ) ); + + aVerSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollUpDownHdl ) ); + aHorSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl ) ); + + nHorSBarHeight = aHorSBar->GetSizePixel().Height(); + nVerSBarWidth = aVerSBar->GetSizePixel().Width(); + + aAutoArrangeIdle.SetPriority( TaskPriority::HIGH_IDLE ); + aAutoArrangeIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,AutoArrangeHdl)); + aAutoArrangeIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aAutoArrangeIdle" ); + + aCallSelectHdlIdle.SetPriority( TaskPriority::LOWEST ); + aCallSelectHdlIdle.SetInvokeHandler( LINK(this,SvxIconChoiceCtrl_Impl,CallSelectHdlHdl)); + aCallSelectHdlIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aCallSelectHdlIdle" ); + + aDocRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE ); + aDocRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,DocRectChangedHdl)); + aDocRectChangedIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aDocRectChangedIdle" ); + + aVisRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE ); + aVisRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,VisRectChangedHdl)); + aVisRectChangedIdle.SetDebugName( "svtools::SvxIconChoiceCtrl_Impl aVisRectChangedIdle" ); + + Clear( true ); + Size gridSize(100,70); + if(pView->GetDPIScaleFactor() > 1) + { + gridSize.setHeight( gridSize.Height() * ( pView->GetDPIScaleFactor()) ); + } + SetGrid(gridSize); +} + +SvxIconChoiceCtrl_Impl::~SvxIconChoiceCtrl_Impl() +{ + Clear(false); + CancelUserEvents(); + pImpCursor.reset(); + pGridMap.reset(); + ClearSelectedRectList(); + m_pColumns.reset(); + aVerSBar.disposeAndClear(); + aHorSBar.disposeAndClear(); + aScrBarBox.disposeAndClear(); +} + +void SvxIconChoiceCtrl_Impl::Clear( bool bInCtor ) +{ + nSelectionCount = 0; + pCurHighlightFrame = nullptr; + CancelUserEvents(); + ShowCursor( false ); + bBoundRectsDirty = false; + nMaxBoundHeight = 0; + + pCursor = nullptr; + if( !bInCtor ) + { + pImpCursor->Clear(); + pGridMap->Clear(); + aVirtOutputSize.setWidth( 0 ); + aVirtOutputSize.setHeight( 0 ); + Size aSize( pView->GetOutputSizePixel() ); + nMaxVirtWidth = aSize.Width() - nVerSBarWidth; + if( nMaxVirtWidth <= 0 ) + nMaxVirtWidth = DEFAULT_MAX_VIRT_WIDTH; + nMaxVirtHeight = aSize.Height() - nHorSBarHeight; + if( nMaxVirtHeight <= 0 ) + nMaxVirtHeight = DEFAULT_MAX_VIRT_HEIGHT; + maZOrderList.clear(); + SetOrigin( Point() ); + if( bUpdateMode ) + pView->Invalidate(InvalidateFlags::NoChildren); + } + AdjustScrollBars(); + maEntries.clear(); + DocRectChanged(); + VisRectChanged(); +} + +void SvxIconChoiceCtrl_Impl::SetStyle( WinBits nWinStyle ) +{ + nWinBits = nWinStyle; + nCurTextDrawFlags = DRAWTEXT_FLAGS_ICON; + if( nWinBits & (WB_SMALLICON | WB_DETAILS) ) + nCurTextDrawFlags = DRAWTEXT_FLAGS_SMALLICON; + if( nWinBits & WB_NOSELECTION ) + eSelectionMode = SelectionMode::NONE; + if( !(nWinStyle & (WB_ALIGN_TOP | WB_ALIGN_LEFT))) + nWinBits |= WB_ALIGN_LEFT; + if( nWinStyle & WB_DETAILS ) + { + if (!m_pColumns) + SetColumn( 0, SvxIconChoiceCtrlColumnInfo() ); + } +} + +IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollUpDownHdl, ScrollBar*, pScrollBar, void ) +{ + // arrow up: delta=-1; arrow down: delta=+1 + Scroll( 0, pScrollBar->GetDelta() ); +} + +IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl, ScrollBar*, pScrollBar, void ) +{ + // arrow left: delta=-1; arrow right: delta=+1 + Scroll( pScrollBar->GetDelta(), 0 ); +} + +void SvxIconChoiceCtrl_Impl::FontModified() +{ + SetDefaultTextSize(); + ShowCursor( false ); + ShowCursor( true ); +} + +void SvxIconChoiceCtrl_Impl::InsertEntry( std::unique_ptr<SvxIconChoiceCtrlEntry> pEntry1, size_t nPos) +{ + auto pEntry = pEntry1.get(); + + if ( nPos < maEntries.size() ) { + maEntries.insert( maEntries.begin() + nPos, std::move(pEntry1) ); + } else { + maEntries.push_back( std::move(pEntry1) ); + } + + if( pHead ) + pEntry->SetBacklink( pHead->pblink ); + + if( (nFlags & IconChoiceFlags::EntryListPosValid) && nPos >= maEntries.size() - 1 ) + pEntry->nPos = maEntries.size() - 1; + else + nFlags &= ~IconChoiceFlags::EntryListPosValid; + + maZOrderList.push_back( pEntry ); + pImpCursor->Clear(); + + // If the UpdateMode is true, don't set all bounding rectangles to + // 'to be checked', but only the bounding rectangle of the new entry. + // Thus, don't call InvalidateBoundingRect! + pEntry->aRect.SetRight( LONG_MAX ); + if( bUpdateMode ) + { + FindBoundingRect( pEntry ); + tools::Rectangle aOutputArea( GetOutputRect() ); + pGridMap->OccupyGrids( pEntry ); + if( !aOutputArea.IsOver( pEntry->aRect ) ) + return; // is invisible + pView->Invalidate( pEntry->aRect ); + } + else + InvalidateBoundingRect( pEntry->aRect ); +} + +void SvxIconChoiceCtrl_Impl::RemoveEntry(size_t nPos) +{ + pImpCursor->Clear(); + maEntries.erase(maEntries.begin() + nPos); + RecalcAllBoundingRectsSmart(); +} + +void SvxIconChoiceCtrl_Impl::CreateAutoMnemonics( MnemonicGenerator* _pGenerator ) +{ + std::unique_ptr< MnemonicGenerator > pAutoDeleteOwnGenerator; + if ( !_pGenerator ) + { + _pGenerator = new MnemonicGenerator; + pAutoDeleteOwnGenerator.reset( _pGenerator ); + } + + sal_uLong nEntryCount = GetEntryCount(); + sal_uLong i; + + // insert texts in generator + for( i = 0; i < nEntryCount; ++i ) + { + DBG_ASSERT( GetEntry( i ), "-SvxIconChoiceCtrl_Impl::CreateAutoMnemonics(): more expected than provided!" ); + + _pGenerator->RegisterMnemonic( GetEntry( i )->GetText() ); + } + + // exchange texts with generated mnemonics + for( i = 0; i < nEntryCount; ++i ) + { + SvxIconChoiceCtrlEntry* pEntry = GetEntry( i ); + OUString aTxt = pEntry->GetText(); + + OUString aNewText = _pGenerator->CreateMnemonic( aTxt ); + if( aNewText != aTxt ) + pEntry->SetText( aNewText ); + } +} + +tools::Rectangle SvxIconChoiceCtrl_Impl::GetOutputRect() const +{ + Point aOrigin( pView->GetMapMode().GetOrigin() ); + aOrigin *= -1; + return tools::Rectangle( aOrigin, aOutputSize ); +} + +void SvxIconChoiceCtrl_Impl::SetListPositions() +{ + if( nFlags & IconChoiceFlags::EntryListPosValid ) + return; + + size_t nCount = maEntries.size(); + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + maEntries[ nCur ]->nPos = nCur; + } + nFlags |= IconChoiceFlags::EntryListPosValid; +} + +void SvxIconChoiceCtrl_Impl::SelectEntry( SvxIconChoiceCtrlEntry* pEntry, bool bSelect, + bool bAdd ) +{ + if( eSelectionMode == SelectionMode::NONE ) + return; + + if( !bAdd ) + { + if ( !( nFlags & IconChoiceFlags::ClearingSelection ) ) + { + nFlags |= IconChoiceFlags::ClearingSelection; + DeselectAllBut( pEntry ); + nFlags &= ~IconChoiceFlags::ClearingSelection; + } + } + if( pEntry->IsSelected() == bSelect ) + return; + + pHdlEntry = pEntry; + SvxIconViewFlags nEntryFlags = pEntry->GetFlags(); + if( bSelect ) + { + nEntryFlags |= SvxIconViewFlags::SELECTED; + pEntry->AssignFlags( nEntryFlags ); + nSelectionCount++; + CallSelectHandler(); + } + else + { + nEntryFlags &= ~SvxIconViewFlags::SELECTED; + pEntry->AssignFlags( nEntryFlags ); + nSelectionCount--; + CallSelectHandler(); + } + EntrySelected( pEntry, bSelect ); +} + +void SvxIconChoiceCtrl_Impl::EntrySelected(SvxIconChoiceCtrlEntry* pEntry, bool bSelect) +{ + // When using SingleSelection, make sure that the cursor is always placed + // over the (only) selected entry. (But only if a cursor exists.) + if (bSelect && pCursor && + eSelectionMode == SelectionMode::Single && + pEntry != pCursor) + { + SetCursor(pEntry); + } + + // Not when dragging though, else the loop in SelectRect doesn't work + // correctly! + if (!(nFlags & IconChoiceFlags::SelectingRect)) + ToTop(pEntry); + if (bUpdateMode) + { + if (pEntry == pCursor) + ShowCursor(false); + pView->Invalidate(CalcFocusRect(pEntry)); + if (pEntry == pCursor) + ShowCursor(true); + } + + // #i101012# emit vcl event LISTBOX_SELECT only in case that the given entry is selected. + if (bSelect) + { + CallEventListeners(VclEventId::ListboxSelect, pEntry); + } +} + +void SvxIconChoiceCtrl_Impl::ResetVirtSize() +{ + aVirtOutputSize.setWidth( 0 ); + aVirtOutputSize.setHeight( 0 ); + const size_t nCount = maEntries.size(); + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pCur = maEntries[ nCur ].get(); + pCur->ClearFlags( SvxIconViewFlags::POS_MOVED ); + if( pCur->IsPosLocked() ) + { + // adapt (among others) VirtSize + if( !IsBoundingRectValid( pCur->aRect ) ) + FindBoundingRect( pCur ); + else + AdjustVirtSize( pCur->aRect ); + } + else + InvalidateBoundingRect( pCur->aRect ); + } + + if( !(nWinBits & (WB_NOVSCROLL | WB_NOHSCROLL)) ) + { + Size aRealOutputSize( pView->GetOutputSizePixel() ); + if( aVirtOutputSize.Width() < aRealOutputSize.Width() || + aVirtOutputSize.Height() < aRealOutputSize.Height() ) + { + sal_uLong nGridCount = IcnGridMap_Impl::GetGridCount( + aRealOutputSize, static_cast<sal_uInt16>(nGridDX), static_cast<sal_uInt16>(nGridDY) ); + if( nGridCount < nCount ) + { + if( nWinBits & WB_ALIGN_TOP ) + nMaxVirtWidth = aRealOutputSize.Width() - nVerSBarWidth; + else // WB_ALIGN_LEFT + nMaxVirtHeight = aRealOutputSize.Height() - nHorSBarHeight; + } + } + } + + pImpCursor->Clear(); + pGridMap->Clear(); + VisRectChanged(); +} + +void SvxIconChoiceCtrl_Impl::AdjustVirtSize( const tools::Rectangle& rRect ) +{ + long nHeightOffs = 0; + long nWidthOffs = 0; + + if( aVirtOutputSize.Width() < (rRect.Right()+LROFFS_WINBORDER) ) + nWidthOffs = (rRect.Right()+LROFFS_WINBORDER) - aVirtOutputSize.Width(); + + if( aVirtOutputSize.Height() < (rRect.Bottom()+TBOFFS_WINBORDER) ) + nHeightOffs = (rRect.Bottom()+TBOFFS_WINBORDER) - aVirtOutputSize.Height(); + + if( !(nWidthOffs || nHeightOffs) ) + return; + + Range aRange; + aVirtOutputSize.AdjustWidth(nWidthOffs ); + aRange.Max() = aVirtOutputSize.Width(); + aHorSBar->SetRange( aRange ); + + aVirtOutputSize.AdjustHeight(nHeightOffs ); + aRange.Max() = aVirtOutputSize.Height(); + aVerSBar->SetRange( aRange ); + + pImpCursor->Clear(); + pGridMap->OutputSizeChanged(); + AdjustScrollBars(); + DocRectChanged(); +} + +void SvxIconChoiceCtrl_Impl::InitPredecessors() +{ + DBG_ASSERT(!pHead,"SvxIconChoiceCtrl_Impl::InitPredecessors() >> Already initialized"); + size_t nCount = maEntries.size(); + if( nCount ) + { + SvxIconChoiceCtrlEntry* pPrev = maEntries[ 0 ].get(); + for( size_t nCur = 1; nCur <= nCount; nCur++ ) + { + pPrev->ClearFlags( SvxIconViewFlags::POS_LOCKED | SvxIconViewFlags::POS_MOVED ); + + SvxIconChoiceCtrlEntry* pNext; + if( nCur == nCount ) + pNext = maEntries[ 0 ].get(); + else + pNext = maEntries[ nCur ].get(); + pPrev->pflink = pNext; + pNext->pblink = pPrev; + pPrev = pNext; + } + pHead = maEntries[ 0 ].get(); + } + else + pHead = nullptr; +} + +void SvxIconChoiceCtrl_Impl::ClearPredecessors() +{ + if( pHead ) + { + size_t nCount = maEntries.size(); + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pCur = maEntries[ nCur ].get(); + pCur->pflink = nullptr; + pCur->pblink = nullptr; + } + pHead = nullptr; + } +} + +void SvxIconChoiceCtrl_Impl::Arrange( bool bKeepPredecessors, long nSetMaxVirtWidth, long nSetMaxVirtHeight ) +{ + if ( nSetMaxVirtWidth != 0 ) + nMaxVirtWidth = nSetMaxVirtWidth; + else + nMaxVirtWidth = aOutputSize.Width(); + + if ( nSetMaxVirtHeight != 0 ) + nMaxVirtHeight = nSetMaxVirtHeight; + else + nMaxVirtHeight = aOutputSize.Height(); + + ImpArrange( bKeepPredecessors ); +} + +void SvxIconChoiceCtrl_Impl::ImpArrange( bool bKeepPredecessors ) +{ + static Point aEmptyPoint; + + bool bOldUpdate = bUpdateMode; + tools::Rectangle aCurOutputArea( GetOutputRect() ); + if( (nWinBits & WB_SMART_ARRANGE) && aCurOutputArea.TopLeft() != aEmptyPoint ) + bUpdateMode = false; + aAutoArrangeIdle.Stop(); + nFlags |= IconChoiceFlags::Arranging; + ShowCursor( false ); + ResetVirtSize(); + if( !bKeepPredecessors ) + ClearPredecessors(); + bBoundRectsDirty = false; + SetOrigin( Point() ); + VisRectChanged(); + RecalcAllBoundingRectsSmart(); + // TODO: the invalidation in the detail view should be more intelligent + //if( !(nWinBits & WB_DETAILS )) + pView->Invalidate( InvalidateFlags::NoChildren ); + nFlags &= ~IconChoiceFlags::Arranging; + if( (nWinBits & WB_SMART_ARRANGE) && aCurOutputArea.TopLeft() != aEmptyPoint ) + { + MakeVisible( aCurOutputArea ); + SetUpdateMode( bOldUpdate ); + } + ShowCursor( true ); +} + +void SvxIconChoiceCtrl_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ +#if defined(OV_DRAWGRID) + Color aOldColor (rRenderContext.GetLineColor()); + Color aCOL_BLACK); + rRenderContext.SetLineColor( aColor ); + Point aOffs(rRenderContext.GetMapMode().GetOrigin()); + Size aXSize(GetOutputSizePixel()); + { + Point aStart(LROFFS_WINBORDER, 0); + Point aEnd(LROFFS_WINBORDER, aXSize.Height()); + aStart -= aOffs; + aEnd -= aOffs; + rRenderContext.DrawLine(aStart, aEnd); + } + { + Point aStart(0, TBOFFS_WINBORDER); + Point aEnd(aXSize.Width(), TBOFFS_WINBORDER); + aStart -= aOffs; + aEnd -= aOffs; + rRenderContext.DrawLine(aStart, aEnd); + } + + for (long nDX = nGridDX; nDX <= aXSize.Width(); nDX += nGridDX) + { + Point aStart( nDX+LROFFS_WINBORDER, 0 ); + Point aEnd( nDX+LROFFS_WINBORDER, aXSize.Height()); + aStart -= aOffs; + aEnd -= aOffs; + rRenderContext.DrawLine(aStart, aEnd); + } + for (long nDY = nGridDY; nDY <= aXSize.Height(); nDY += nGridDY) + { + Point aStart(0, nDY + TBOFFS_WINBORDER); + Point aEnd(aXSize.Width(), nDY + TBOFFS_WINBORDER); + aStart -= aOffs; + aEnd -= aOffs; + rRenderContext.DrawLine(aStart, aEnd); + } + rRenderContext.SetLineColor(aOldColor); +#endif + + if (!maEntries.size()) + return; + if (!pCursor) + { + // set cursor to item with focus-flag + bool bfound = false; + for (sal_Int32 i = 0; i < pView->GetEntryCount() && !bfound; i++) + { + SvxIconChoiceCtrlEntry* pEntry = pView->GetEntry(i); + if (pEntry->IsFocused()) + { + pCursor = pEntry; + bfound = true; + } + } + + if (!bfound) + pCursor = maEntries[ 0 ].get(); + } + + size_t nCount = maZOrderList.size(); + if (!nCount) + return; + + rRenderContext.Push(PushFlags::CLIPREGION); + rRenderContext.SetClipRegion(vcl::Region(rRect)); + + std::vector< SvxIconChoiceCtrlEntry* > aNewZOrderList; + std::vector< SvxIconChoiceCtrlEntry* > aPaintedEntries; + + size_t nPos = 0; + while(nCount) + { + SvxIconChoiceCtrlEntry* pEntry = maZOrderList[nPos]; + const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry); + if (rRect.IsOver(rBoundRect)) + { + PaintEntry(pEntry, rBoundRect.TopLeft(), rRenderContext); + // set entries to Top if they are being repainted + aPaintedEntries.push_back(pEntry); + } + else + aNewZOrderList.push_back(pEntry); + + nCount--; + nPos++; + } + maZOrderList = std::move( aNewZOrderList ); + maZOrderList.insert(maZOrderList.end(), aPaintedEntries.begin(), aPaintedEntries.end()); + + rRenderContext.Pop(); +} + +void SvxIconChoiceCtrl_Impl::RepaintSelectedEntries() +{ + const size_t nCount = maZOrderList.size(); + if (!nCount) + return; + + tools::Rectangle aOutRect(GetOutputRect()); + for (size_t nCur = 0; nCur < nCount; nCur++) + { + SvxIconChoiceCtrlEntry* pEntry = maZOrderList[nCur]; + if (pEntry->GetFlags() & SvxIconViewFlags::SELECTED) + { + const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry); + if (aOutRect.IsOver(rBoundRect)) + pView->Invalidate(rBoundRect); + } + } +} + +void SvxIconChoiceCtrl_Impl::InitScrollBarBox() +{ + aScrBarBox->SetSizePixel( Size(nVerSBarWidth-1, nHorSBarHeight-1) ); + Size aSize( pView->GetOutputSizePixel() ); + aScrBarBox->SetPosPixel( Point(aSize.Width()-nVerSBarWidth+1, aSize.Height()-nHorSBarHeight+1)); +} + +bool SvxIconChoiceCtrl_Impl::MouseButtonDown( const MouseEvent& rMEvt) +{ + bool bHandled = true; + bHighlightFramePressed = false; + bool bGotFocus = (!pView->HasFocus() && !(nWinBits & WB_NOPOINTERFOCUS)); + if( !(nWinBits & WB_NOPOINTERFOCUS) ) + pView->GrabFocus(); + + Point aDocPos( rMEvt.GetPosPixel() ); + if(aDocPos.X()>=aOutputSize.Width() || aDocPos.Y()>=aOutputSize.Height()) + return false; + ToDocPos( aDocPos ); + SvxIconChoiceCtrlEntry* pEntry = GetEntry( aDocPos, true ); + if( pEntry ) + MakeEntryVisible( pEntry, false ); + + if( rMEvt.IsShift() && eSelectionMode != SelectionMode::Single ) + { + if( pEntry ) + SetCursor_Impl( pCursor, pEntry, rMEvt.IsMod1(), rMEvt.IsShift() ); + return true; + } + + if( pAnchor && (rMEvt.IsShift() || rMEvt.IsMod1())) // keyboard selection? + { + DBG_ASSERT(eSelectionMode != SelectionMode::Single,"Invalid selection mode"); + if( rMEvt.IsMod1() ) + nFlags |= IconChoiceFlags::AddMode; + + if( rMEvt.IsShift() ) + { + tools::Rectangle aRect( GetEntryBoundRect( pAnchor )); + if( pEntry ) + aRect.Union( GetEntryBoundRect( pEntry ) ); + else + { + tools::Rectangle aTempRect( aDocPos, Size(1,1)); + aRect.Union( aTempRect ); + } + aCurSelectionRect = aRect; + SelectRect( aRect, bool(nFlags & IconChoiceFlags::AddMode), &aSelectedRectList ); + } + else if( rMEvt.IsMod1() ) + { + AddSelectedRect( aCurSelectionRect ); + pAnchor = nullptr; + aCurSelectionRect.SetPos( aDocPos ); + } + + if( !pEntry && !(nWinBits & WB_NODRAGSELECTION)) + pView->StartTracking( StartTrackingFlags::ScrollRepeat ); + return true; + } + else + { + if( !pEntry ) + { + if( eSelectionMode == SelectionMode::Multiple ) + { + if( !rMEvt.IsMod1() ) // Ctrl + { + if( !bGotFocus ) + { + SetNoSelection(); + ClearSelectedRectList(); + } + } + else + nFlags |= IconChoiceFlags::AddMode; + aCurSelectionRect.SetPos( aDocPos ); + pView->StartTracking( StartTrackingFlags::ScrollRepeat ); + } + else + bHandled = false; + return bHandled; + } + } + bool bSelected = pEntry->IsSelected(); + + if( rMEvt.GetClicks() == 2 ) + { + DeselectAllBut( pEntry ); + SelectEntry( pEntry, true, false ); + pHdlEntry = pEntry; + pView->ClickIcon(); + } + else + { + // Inplace-Editing ? + if( rMEvt.IsMod2() ) // Alt? + { + } + else if( eSelectionMode == SelectionMode::Single ) + { + DeselectAllBut( pEntry ); + SetCursor( pEntry ); + } + else if( eSelectionMode == SelectionMode::NONE ) + { + if( rMEvt.IsLeft() && (nWinBits & WB_HIGHLIGHTFRAME) ) + { + pCurHighlightFrame = nullptr; // force repaint of frame + bHighlightFramePressed = true; + SetEntryHighlightFrame( pEntry, true ); + } + } + else + { + if( !rMEvt.GetModifier() && rMEvt.IsLeft() ) + { + if( !bSelected ) + { + DeselectAllBut( pEntry ); + SetCursor( pEntry ); + SelectEntry( pEntry, true, false ); + } + else + { + // deselect only in the Up, if the Move happened via D&D! + nFlags |= IconChoiceFlags::DownDeselect; + } + } + else if( rMEvt.IsMod1() ) + nFlags |= IconChoiceFlags::DownCtrl; + } + } + return bHandled; +} + +bool SvxIconChoiceCtrl_Impl::MouseButtonUp( const MouseEvent& rMEvt ) +{ + bool bHandled = false; + if( rMEvt.IsRight() && (nFlags & (IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect) )) + { + nFlags &= ~IconChoiceFlags(IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect); + bHandled = true; + } + + Point aDocPos( rMEvt.GetPosPixel() ); + ToDocPos( aDocPos ); + SvxIconChoiceCtrlEntry* pDocEntry = GetEntry( aDocPos ); + if( pDocEntry ) + { + if( nFlags & IconChoiceFlags::DownCtrl ) + { + // Ctrl & MultiSelection + ToggleSelection( pDocEntry ); + SetCursor( pDocEntry ); + bHandled = true; + } + else if( nFlags & IconChoiceFlags::DownDeselect ) + { + DeselectAllBut( pDocEntry ); + SetCursor( pDocEntry ); + SelectEntry( pDocEntry, true, false ); + bHandled = true; + } + } + + nFlags &= ~IconChoiceFlags(IconChoiceFlags::DownCtrl | IconChoiceFlags::DownDeselect); + + if((nWinBits & WB_HIGHLIGHTFRAME) && bHighlightFramePressed && pCurHighlightFrame) + { + bHandled = true; + SvxIconChoiceCtrlEntry* pEntry = pCurHighlightFrame; + pCurHighlightFrame = nullptr; // force repaint of frame + bHighlightFramePressed = false; + SetEntryHighlightFrame( pEntry, true ); + + pHdlEntry = pCurHighlightFrame; + pView->ClickIcon(); + + // set focus on Icon + SvxIconChoiceCtrlEntry* pOldCursor = pCursor; + SetCursor_Impl( pOldCursor, pHdlEntry, false, false ); + + pHdlEntry = nullptr; + } + return bHandled; +} + +bool SvxIconChoiceCtrl_Impl::MouseMove( const MouseEvent& rMEvt ) +{ + const Point aDocPos( pView->PixelToLogic(rMEvt.GetPosPixel()) ); + + if( pView->IsTracking() ) + return false; + else if( nWinBits & WB_HIGHLIGHTFRAME ) + { + SvxIconChoiceCtrlEntry* pEntry = GetEntry( aDocPos, true ); + SetEntryHighlightFrame( pEntry, false ); + } + else + return false; + return true; +} + +void SvxIconChoiceCtrl_Impl::SetCursor_Impl( SvxIconChoiceCtrlEntry* pOldCursor, + SvxIconChoiceCtrlEntry* pNewCursor, bool bMod1, bool bShift ) +{ + if( !pNewCursor ) + return; + + SvxIconChoiceCtrlEntry* pFilterEntry = nullptr; + bool bDeselectAll = false; + if( eSelectionMode != SelectionMode::Single ) + { + if( !bMod1 && !bShift ) + bDeselectAll = true; + else if( bShift && !bMod1 && !pAnchor ) + { + bDeselectAll = true; + pFilterEntry = pOldCursor; + } + } + if( bDeselectAll ) + DeselectAllBut( pFilterEntry ); + ShowCursor( false ); + MakeEntryVisible( pNewCursor ); + SetCursor( pNewCursor ); + if( bMod1 && !bShift ) + { + if( pAnchor ) + { + AddSelectedRect( pAnchor, pOldCursor ); + pAnchor = nullptr; + } + } + else if( bShift ) + { + if( !pAnchor ) + pAnchor = pOldCursor; + if ( nWinBits & WB_ALIGN_LEFT ) + SelectRange( pAnchor, pNewCursor, bool(nFlags & IconChoiceFlags::AddMode) ); + else + SelectRect(pAnchor,pNewCursor, bool(nFlags & IconChoiceFlags::AddMode), &aSelectedRectList); + } + else + { + SelectEntry( pCursor, true, false ); + aCurSelectionRect = GetEntryBoundRect( pCursor ); + CallEventListeners( VclEventId::ListboxSelect, pCursor ); + } +} + +bool SvxIconChoiceCtrl_Impl::KeyInput( const KeyEvent& rKEvt ) +{ + bool bMod2 = rKEvt.GetKeyCode().IsMod2(); + sal_Unicode cChar = rKEvt.GetCharCode(); + sal_uLong nPos = sal_uLong(-1); + if ( bMod2 && cChar && IsMnemonicChar( cChar, nPos ) ) + { + // shortcut is clicked + SvxIconChoiceCtrlEntry* pNewCursor = GetEntry( nPos ); + SvxIconChoiceCtrlEntry* pOldCursor = pCursor; + if ( pNewCursor != pOldCursor ) + SetCursor_Impl( pOldCursor, pNewCursor, false, false ); + return true; + } + + if ( bMod2 ) + // no actions with <ALT> + return false; + + bool bKeyUsed = true; + bool bMod1 = rKEvt.GetKeyCode().IsMod1(); + bool bShift = rKEvt.GetKeyCode().IsShift(); + + if( eSelectionMode == SelectionMode::Single || eSelectionMode == SelectionMode::NONE) + { + bShift = false; + bMod1 = false; + } + + if( bMod1 ) + nFlags |= IconChoiceFlags::AddMode; + + SvxIconChoiceCtrlEntry* pNewCursor; + SvxIconChoiceCtrlEntry* pOldCursor = pCursor; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch( nCode ) + { + case KEY_UP: + case KEY_PAGEUP: + if( pCursor ) + { + MakeEntryVisible( pCursor ); + if( nCode == KEY_UP ) + pNewCursor = pImpCursor->GoUpDown(pCursor,false); + else + pNewCursor = pImpCursor->GoPageUpDown(pCursor,false); + SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift ); + if( !pNewCursor ) + { + tools::Rectangle aRect( GetEntryBoundRect( pCursor ) ); + if( aRect.Top()) + { + aRect.AdjustBottom( -(aRect.Top()) ); + aRect.SetTop( 0 ); + MakeVisible( aRect ); + } + } + + if ( bChooseWithCursor && pNewCursor != nullptr ) + { + pHdlEntry = pNewCursor;//GetCurEntry(); + pCurHighlightFrame = pHdlEntry; + pView->ClickIcon(); + pCurHighlightFrame = nullptr; + } + } + break; + + case KEY_DOWN: + case KEY_PAGEDOWN: + if( pCursor ) + { + if( nCode == KEY_DOWN ) + pNewCursor=pImpCursor->GoUpDown( pCursor,true ); + else + pNewCursor=pImpCursor->GoPageUpDown( pCursor,true ); + SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift ); + + if ( bChooseWithCursor && pNewCursor != nullptr) + { + pHdlEntry = pNewCursor;//GetCurEntry(); + pCurHighlightFrame = pHdlEntry; + pView->ClickIcon(); + pCurHighlightFrame = nullptr; + } + } + break; + + case KEY_RIGHT: + if( pCursor ) + { + pNewCursor=pImpCursor->GoLeftRight(pCursor,true ); + SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift ); + } + break; + + case KEY_LEFT: + if( pCursor ) + { + MakeEntryVisible( pCursor ); + pNewCursor = pImpCursor->GoLeftRight(pCursor,false ); + SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift ); + if( !pNewCursor ) + { + tools::Rectangle aRect( GetEntryBoundRect(pCursor)); + if( aRect.Left() ) + { + aRect.AdjustRight( -(aRect.Left()) ); + aRect.SetLeft( 0 ); + MakeVisible( aRect ); + } + } + } + break; + + case KEY_F2: + if( bMod1 || bShift ) + bKeyUsed = false; + break; + + case KEY_F8: + if( rKEvt.GetKeyCode().IsShift() ) + { + if( nFlags & IconChoiceFlags::AddMode ) + nFlags &= ~IconChoiceFlags::AddMode; + else + nFlags |= IconChoiceFlags::AddMode; + } + else + bKeyUsed = false; + break; + + case KEY_SPACE: + if( pCursor && eSelectionMode != SelectionMode::Single ) + { + if( !bMod1 ) + { + //SelectAll( false ); + SetNoSelection(); + ClearSelectedRectList(); + + // click Icon with spacebar + SetEntryHighlightFrame( GetCurEntry(), true ); + pView->ClickIcon(); + pHdlEntry = pCurHighlightFrame; + pCurHighlightFrame=nullptr; + } + else + ToggleSelection( pCursor ); + } + break; + +#ifdef DBG_UTIL + case KEY_F10: + if( rKEvt.GetKeyCode().IsShift() ) + { + if( pCursor ) + pView->SetEntryTextMode( SvxIconChoiceCtrlTextMode::Full, pCursor ); + } + if( rKEvt.GetKeyCode().IsMod1() ) + { + if( pCursor ) + pView->SetEntryTextMode( SvxIconChoiceCtrlTextMode::Short, pCursor ); + } + break; +#endif + + case KEY_ADD: + case KEY_DIVIDE : + case KEY_A: + if( bMod1 && (eSelectionMode != SelectionMode::Single)) + SelectAll(); + else + bKeyUsed = false; + break; + + case KEY_SUBTRACT: + case KEY_COMMA : + if( bMod1 ) + SetNoSelection(); + else + bKeyUsed = false; + break; + + case KEY_RETURN: + if( !bMod1 ) + bKeyUsed = false; + break; + + case KEY_END: + if( pCursor ) + { + pNewCursor = maEntries.back().get(); + SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift ); + } + break; + + case KEY_HOME: + if( pCursor ) + { + pNewCursor = maEntries[ 0 ].get(); + SetCursor_Impl( pOldCursor, pNewCursor, bMod1, bShift ); + } + break; + + default: + bKeyUsed = false; + + } + return bKeyUsed; +} + +// recalculate TopLeft of scrollbars (but not their sizes!) +void SvxIconChoiceCtrl_Impl::PositionScrollBars( long nRealWidth, long nRealHeight ) +{ + // horizontal scrollbar + Point aPos( 0, nRealHeight ); + aPos.AdjustY( -nHorSBarHeight ); + + if( aHorSBar->GetPosPixel() != aPos ) + aHorSBar->SetPosPixel( aPos ); + + // vertical scrollbar + aPos.setX( nRealWidth ); aPos.setY( 0 ); + aPos.AdjustX( -nVerSBarWidth ); + aPos.AdjustX( 1 ); + aPos.AdjustY( -1 ); + + if( aVerSBar->GetPosPixel() != aPos ) + aVerSBar->SetPosPixel( aPos ); +} + +void SvxIconChoiceCtrl_Impl::AdjustScrollBars() +{ + long nVirtHeight = aVirtOutputSize.Height(); + long nVirtWidth = aVirtOutputSize.Width(); + + Size aOSize( pView->Control::GetOutputSizePixel() ); + long nRealHeight = aOSize.Height(); + long nRealWidth = aOSize.Width(); + + PositionScrollBars( nRealWidth, nRealHeight ); + + const MapMode& rMapMode = pView->GetMapMode(); + Point aOrigin( rMapMode.GetOrigin() ); + + long nVisibleWidth; + if( nRealWidth > nVirtWidth ) + nVisibleWidth = nVirtWidth + aOrigin.X(); + else + nVisibleWidth = nRealWidth; + + long nVisibleHeight; + if( nRealHeight > nVirtHeight ) + nVisibleHeight = nVirtHeight + aOrigin.Y(); + else + nVisibleHeight = nRealHeight; + + bool bVerSBar = ( nWinBits & WB_VSCROLL ) != 0; + bool bHorSBar = ( nWinBits & WB_HSCROLL ) != 0; + bool bNoVerSBar = ( nWinBits & WB_NOVSCROLL ) != 0; + bool bNoHorSBar = ( nWinBits & WB_NOHSCROLL ) != 0; + + sal_uInt16 nResult = 0; + if( nVirtHeight ) + { + // activate vertical scrollbar? + if( !bNoVerSBar && (bVerSBar || ( nVirtHeight > nVisibleHeight)) ) + { + nResult = 0x0001; + nRealWidth -= nVerSBarWidth; + + if( nRealWidth > nVirtWidth ) + nVisibleWidth = nVirtWidth + aOrigin.X(); + else + nVisibleWidth = nRealWidth; + } + // activate horizontal scrollbar? + if( !bNoHorSBar && (bHorSBar || (nVirtWidth > nVisibleWidth)) ) + { + nResult |= 0x0002; + nRealHeight -= nHorSBarHeight; + + if( nRealHeight > nVirtHeight ) + nVisibleHeight = nVirtHeight + aOrigin.Y(); + else + nVisibleHeight = nRealHeight; + + // do we need a vertical scrollbar after all? + if( !(nResult & 0x0001) && // only if not already there + ( !bNoVerSBar && ((nVirtHeight > nVisibleHeight) || bVerSBar)) ) + { + nResult = 3; // both turned on + nRealWidth -= nVerSBarWidth; + + if( nRealWidth > nVirtWidth ) + nVisibleWidth = nVirtWidth + aOrigin.X(); + else + nVisibleWidth = nRealWidth; + } + } + } + + // size vertical scrollbar + long nThumb = aVerSBar->GetThumbPos(); + Size aSize( nVerSBarWidth, nRealHeight ); + aSize.AdjustHeight(2 ); + if( aSize != aVerSBar->GetSizePixel() ) + aVerSBar->SetSizePixel( aSize ); + aVerSBar->SetVisibleSize( nVisibleHeight ); + aVerSBar->SetPageSize( GetScrollBarPageSize( nVisibleHeight )); + + if( nResult & 0x0001 ) + { + aVerSBar->SetThumbPos( nThumb ); + aVerSBar->Show(); + } + else + { + aVerSBar->SetThumbPos( 0 ); + aVerSBar->Hide(); + } + + // size horizontal scrollbar + nThumb = aHorSBar->GetThumbPos(); + aSize.setWidth( nRealWidth ); + aSize.setHeight( nHorSBarHeight ); + aSize.AdjustWidth( 1 ); + if( nResult & 0x0001 ) // vertical scrollbar? + { + aSize.AdjustWidth( 1 ); + nRealWidth++; + } + if( aSize != aHorSBar->GetSizePixel() ) + aHorSBar->SetSizePixel( aSize ); + aHorSBar->SetVisibleSize( nVisibleWidth ); + aHorSBar->SetPageSize( GetScrollBarPageSize(nVisibleWidth )); + if( nResult & 0x0002 ) + { + aHorSBar->SetThumbPos( nThumb ); + aHorSBar->Show(); + } + else + { + aHorSBar->SetThumbPos( 0 ); + aHorSBar->Hide(); + } + + aOutputSize.setWidth( nRealWidth ); + if( nResult & 0x0002 ) // horizontal scrollbar ? + nRealHeight++; // because lower border is clipped + aOutputSize.setHeight( nRealHeight ); + + if( (nResult & (0x0001|0x0002)) == (0x0001|0x0002) ) + aScrBarBox->Show(); + else + aScrBarBox->Hide(); +} + +void SvxIconChoiceCtrl_Impl::Resize() +{ + InitScrollBarBox(); + aOutputSize = pView->GetOutputSizePixel(); + pImpCursor->Clear(); + pGridMap->OutputSizeChanged(); + + const Size& rSize = pView->Control::GetOutputSizePixel(); + PositionScrollBars( rSize.Width(), rSize.Height() ); + // The scrollbars are shown/hidden asynchronously, so derived classes can + // do an Arrange during Resize, without the scrollbars suddenly turning + // on and off again. + // If an event is already underway, we don't need to send a new one, at least + // as long as there is only one event type. + if ( ! nUserEventAdjustScrBars ) + nUserEventAdjustScrBars = + Application::PostUserEvent( LINK( this, SvxIconChoiceCtrl_Impl, UserEventHdl), + EVENTID_ADJUST_SCROLLBARS); + + VisRectChanged(); +} + +bool SvxIconChoiceCtrl_Impl::CheckHorScrollBar() +{ + if( maZOrderList.empty() || !aHorSBar->IsVisible() ) + return false; + const MapMode& rMapMode = pView->GetMapMode(); + Point aOrigin( rMapMode.GetOrigin() ); + if(!( nWinBits & WB_HSCROLL) && !aOrigin.X() ) + { + long nWidth = aOutputSize.Width(); + const size_t nCount = maZOrderList.size(); + long nMostRight = 0; + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ]; + long nRight = GetEntryBoundRect(pEntry).Right(); + if( nRight > nWidth ) + return false; + if( nRight > nMostRight ) + nMostRight = nRight; + } + aHorSBar->Hide(); + aOutputSize.AdjustHeight(nHorSBarHeight ); + aVirtOutputSize.setWidth( nMostRight ); + aHorSBar->SetThumbPos( 0 ); + Range aRange; + aRange.Max() = nMostRight - 1; + aHorSBar->SetRange( aRange ); + if( aVerSBar->IsVisible() ) + { + Size aSize( aVerSBar->GetSizePixel()); + aSize.AdjustHeight(nHorSBarHeight ); + aVerSBar->SetSizePixel( aSize ); + } + return true; + } + return false; +} + +bool SvxIconChoiceCtrl_Impl::CheckVerScrollBar() +{ + if( maZOrderList.empty() || !aVerSBar->IsVisible() ) + return false; + const MapMode& rMapMode = pView->GetMapMode(); + Point aOrigin( rMapMode.GetOrigin() ); + if(!( nWinBits & WB_VSCROLL) && !aOrigin.Y() ) + { + long nDeepest = 0; + long nHeight = aOutputSize.Height(); + const size_t nCount = maZOrderList.size(); + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ]; + long nBottom = GetEntryBoundRect(pEntry).Bottom(); + if( nBottom > nHeight ) + return false; + if( nBottom > nDeepest ) + nDeepest = nBottom; + } + aVerSBar->Hide(); + aOutputSize.AdjustWidth(nVerSBarWidth ); + aVirtOutputSize.setHeight( nDeepest ); + aVerSBar->SetThumbPos( 0 ); + Range aRange; + aRange.Max() = nDeepest - 1; + aVerSBar->SetRange( aRange ); + if( aHorSBar->IsVisible() ) + { + Size aSize( aHorSBar->GetSizePixel()); + aSize.AdjustWidth(nVerSBarWidth ); + aHorSBar->SetSizePixel( aSize ); + } + return true; + } + return false; +} + + +// hides scrollbars if they're unnecessary +void SvxIconChoiceCtrl_Impl::CheckScrollBars() +{ + CheckVerScrollBar(); + if( CheckHorScrollBar() ) + CheckVerScrollBar(); + if( aVerSBar->IsVisible() && aHorSBar->IsVisible() ) + aScrBarBox->Show(); + else + aScrBarBox->Hide(); +} + + +void SvxIconChoiceCtrl_Impl::GetFocus() +{ + RepaintSelectedEntries(); + if( pCursor ) + { + pCursor->SetFlags( SvxIconViewFlags::FOCUSED ); + ShowCursor( true ); + } +} + +void SvxIconChoiceCtrl_Impl::LoseFocus() +{ + if( pCursor ) + pCursor->ClearFlags( SvxIconViewFlags::FOCUSED ); + ShowCursor( false ); + +// HideFocus (); +// pView->Invalidate ( aFocus.aRect ); + + RepaintSelectedEntries(); +} + +void SvxIconChoiceCtrl_Impl::SetUpdateMode( bool bUpdate ) +{ + if( bUpdate != bUpdateMode ) + { + bUpdateMode = bUpdate; + if( bUpdate ) + { + AdjustScrollBars(); + pImpCursor->Clear(); + pGridMap->Clear(); + pView->Invalidate(InvalidateFlags::NoChildren); + } + } +} + +// priorities of the emphasis: bSelected +void SvxIconChoiceCtrl_Impl::PaintEmphasis(const tools::Rectangle& rTextRect, bool bSelected, + vcl::RenderContext& rRenderContext) +{ + Color aOldFillColor(rRenderContext.GetFillColor()); + + bool bSolidTextRect = false; + + if (!bSelected) + { + const Color& rFillColor = rRenderContext.GetFont().GetFillColor(); + rRenderContext.SetFillColor(rFillColor); + if (rFillColor != COL_TRANSPARENT) + bSolidTextRect = true; + } + + // draw text rectangle + if (bSolidTextRect) + { + rRenderContext.DrawRect(rTextRect); + } + + rRenderContext.SetFillColor(aOldFillColor); +} + + +void SvxIconChoiceCtrl_Impl::PaintItem(const tools::Rectangle& rRect, + IcnViewFieldType eItem, SvxIconChoiceCtrlEntry* pEntry, sal_uInt16 nPaintFlags, + vcl::RenderContext& rRenderContext ) +{ + if (eItem == IcnViewFieldType::Text) + { + OUString aText = SvtIconChoiceCtrl::GetEntryText(pEntry); + + rRenderContext.DrawText(rRect, aText, nCurTextDrawFlags); + + if (pEntry->IsFocused()) + { + tools::Rectangle aRect (CalcFocusRect(pEntry)); + ShowFocus(aRect); + DrawFocusRect(rRenderContext); + } + } + else + { + Point aPos(rRect.TopLeft()); + if (nPaintFlags & PAINTFLAG_HOR_CENTERED) + aPos.AdjustX((rRect.GetWidth() - aImageSize.Width()) / 2 ); + if (nPaintFlags & PAINTFLAG_VER_CENTERED) + aPos.AdjustY((rRect.GetHeight() - aImageSize.Height()) / 2 ); + SvtIconChoiceCtrl::DrawEntryImage(pEntry, aPos, rRenderContext); + } +} + +void SvxIconChoiceCtrl_Impl::PaintEntry(SvxIconChoiceCtrlEntry* pEntry, const Point& rPos, vcl::RenderContext& rRenderContext) +{ + bool bSelected = false; + + if (eSelectionMode != SelectionMode::NONE) + bSelected = pEntry->IsSelected(); + + rRenderContext.Push(PushFlags::FONT | PushFlags::TEXTCOLOR); + + OUString aEntryText(SvtIconChoiceCtrl::GetEntryText(pEntry)); + tools::Rectangle aTextRect(CalcTextRect(pEntry, &rPos, &aEntryText)); + tools::Rectangle aBmpRect(CalcBmpRect(pEntry, &rPos)); + + bool bShowSelection = (bSelected && (eSelectionMode != SelectionMode::NONE)); + + bool bActiveSelection = (0 != (nWinBits & WB_NOHIDESELECTION)) || pView->HasFocus(); + + if (bShowSelection) + { + const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings(); + vcl::Font aNewFont(rRenderContext.GetFont()); + + // font fill colors that are attributed "hard" need corresponding "hard" + // attributed highlight colors + if ((nWinBits & WB_NOHIDESELECTION) || pView->HasFocus()) + aNewFont.SetFillColor(rSettings.GetHighlightColor()); + else + aNewFont.SetFillColor(rSettings.GetDeactiveColor()); + + Color aWinCol = rSettings.GetWindowTextColor(); + if (!bActiveSelection && rSettings.GetFaceColor().IsBright() == aWinCol.IsBright()) + aNewFont.SetColor(rSettings.GetWindowTextColor()); + else + aNewFont.SetColor(rSettings.GetHighlightTextColor()); + + rRenderContext.SetFont(aNewFont); + + rRenderContext.SetFillColor(rRenderContext.GetBackground().GetColor()); + rRenderContext.DrawRect(CalcFocusRect(pEntry)); + rRenderContext.SetFillColor(); + } + + bool bResetClipRegion = false; + if (!rRenderContext.IsClipRegion() && (aVerSBar->IsVisible() || aHorSBar->IsVisible())) + { + tools::Rectangle aOutputArea(GetOutputRect()); + if (aOutputArea.IsOver(aTextRect) || aOutputArea.IsOver(aBmpRect)) + { + rRenderContext.SetClipRegion(vcl::Region(aOutputArea)); + bResetClipRegion = true; + } + } + + bool bLargeIconMode = WB_ICON == ( nWinBits & VIEWMODE_MASK ); + sal_uInt16 nBmpPaintFlags = PAINTFLAG_VER_CENTERED; + if (bLargeIconMode) + nBmpPaintFlags |= PAINTFLAG_HOR_CENTERED; + sal_uInt16 nTextPaintFlags = bLargeIconMode ? PAINTFLAG_HOR_CENTERED : PAINTFLAG_VER_CENTERED; + + PaintEmphasis(aTextRect, bSelected, rRenderContext); + + if ( bShowSelection ) + vcl::RenderTools::DrawSelectionBackground(rRenderContext, *pView, CalcFocusRect(pEntry), + bActiveSelection ? 1 : 2, false, true, false); + + + PaintItem(aBmpRect, IcnViewFieldType::Image, pEntry, nBmpPaintFlags, rRenderContext); + + PaintItem(aTextRect, IcnViewFieldType::Text, pEntry, nTextPaintFlags, rRenderContext); + + // draw highlight frame + if (pEntry == pCurHighlightFrame) + DrawHighlightFrame(rRenderContext, CalcFocusRect(pEntry)); + + rRenderContext.Pop(); + if (bResetClipRegion) + rRenderContext.SetClipRegion(); +} + +void SvxIconChoiceCtrl_Impl::SetEntryPos( SvxIconChoiceCtrlEntry* pEntry, const Point& rPos ) +{ + ShowCursor( false ); + tools::Rectangle aBoundRect( GetEntryBoundRect( pEntry )); + pView->Invalidate( aBoundRect ); + ToTop( pEntry ); + if( !IsAutoArrange() ) + { + bool bAdjustVirtSize = false; + if( rPos != aBoundRect.TopLeft() ) + { + Point aGridOffs( + pEntry->aGridRect.TopLeft() - pEntry->aRect.TopLeft() ); + pImpCursor->Clear(); + pGridMap->Clear(); + aBoundRect.SetPos( rPos ); + pEntry->aRect = aBoundRect; + pEntry->aGridRect.SetPos( rPos + aGridOffs ); + bAdjustVirtSize = true; + } + if( bAdjustVirtSize ) + AdjustVirtSize( pEntry->aRect ); + + pView->Invalidate( pEntry->aRect ); + pGridMap->OccupyGrids( pEntry ); + } + else + { + SvxIconChoiceCtrlEntry* pPrev = FindEntryPredecessor( pEntry, rPos ); + SetEntryPredecessor( pEntry, pPrev ); + aAutoArrangeIdle.Start(); + } + ShowCursor( true ); +} + +void SvxIconChoiceCtrl_Impl::SetNoSelection() +{ + // block recursive calls via SelectEntry + if( !(nFlags & IconChoiceFlags::ClearingSelection )) + { + nFlags |= IconChoiceFlags::ClearingSelection; + DeselectAllBut( nullptr ); + nFlags &= ~IconChoiceFlags::ClearingSelection; + } +} + +SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetEntry( const Point& rDocPos, bool bHit ) +{ + CheckBoundingRects(); + // search through z-order list from the end + size_t nCount = maZOrderList.size(); + while( nCount ) + { + nCount--; + SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCount ]; + if( pEntry->aRect.IsInside( rDocPos ) ) + { + if( bHit ) + { + tools::Rectangle aRect = CalcBmpRect( pEntry ); + aRect.AdjustTop( -3 ); + aRect.AdjustBottom(3 ); + aRect.AdjustLeft( -3 ); + aRect.AdjustRight(3 ); + if( aRect.IsInside( rDocPos ) ) + return pEntry; + aRect = CalcTextRect( pEntry ); + if( aRect.IsInside( rDocPos ) ) + return pEntry; + } + else + return pEntry; + } + } + return nullptr; +} + +void SvxIconChoiceCtrl_Impl::MakeEntryVisible( SvxIconChoiceCtrlEntry* pEntry, bool bBound ) +{ + if ( bBound ) + { + const tools::Rectangle& rRect = GetEntryBoundRect( pEntry ); + MakeVisible( rRect ); + } + else + { + tools::Rectangle aRect = CalcBmpRect( pEntry ); + aRect.Union( CalcTextRect( pEntry ) ); + aRect.AdjustTop(TBOFFS_BOUND ); + aRect.AdjustBottom(TBOFFS_BOUND ); + aRect.AdjustLeft(LROFFS_BOUND ); + aRect.AdjustRight(LROFFS_BOUND ); + MakeVisible( aRect ); + } +} + +const tools::Rectangle& SvxIconChoiceCtrl_Impl::GetEntryBoundRect( SvxIconChoiceCtrlEntry* pEntry ) +{ + if( !IsBoundingRectValid( pEntry->aRect )) + FindBoundingRect( pEntry ); + return pEntry->aRect; +} + +tools::Rectangle SvxIconChoiceCtrl_Impl::CalcBmpRect( SvxIconChoiceCtrlEntry* pEntry, const Point* pPos ) +{ + tools::Rectangle aBound = GetEntryBoundRect( pEntry ); + if( pPos ) + aBound.SetPos( *pPos ); + Point aPos( aBound.TopLeft() ); + + switch( nWinBits & VIEWMODE_MASK ) + { + case WB_ICON: + { + aPos.AdjustX(( aBound.GetWidth() - aImageSize.Width() ) / 2 ); + return tools::Rectangle( aPos, aImageSize ); + } + + case WB_SMALLICON: + case WB_DETAILS: + aPos.AdjustY(( aBound.GetHeight() - aImageSize.Height() ) / 2 ); + //TODO: determine horizontal distance to bounding rectangle + return tools::Rectangle( aPos, aImageSize ); + + default: + OSL_FAIL("IconView: Viewmode not set"); + return aBound; + } +} + +tools::Rectangle SvxIconChoiceCtrl_Impl::CalcTextRect( SvxIconChoiceCtrlEntry* pEntry, + const Point* pEntryPos, const OUString* pStr ) +{ + OUString aEntryText; + if( !pStr ) + aEntryText = SvtIconChoiceCtrl::GetEntryText( pEntry ); + else + aEntryText = *pStr; + + const tools::Rectangle aMaxTextRect( CalcMaxTextRect( pEntry ) ); + tools::Rectangle aBound( GetEntryBoundRect( pEntry ) ); + if( pEntryPos ) + aBound.SetPos( *pEntryPos ); + + tools::Rectangle aTextRect = pView->GetTextRect( aMaxTextRect, aEntryText, nCurTextDrawFlags ); + + Size aTextSize( aTextRect.GetSize() ); + + Point aPos( aBound.TopLeft() ); + long nBoundWidth = aBound.GetWidth(); + long nBoundHeight = aBound.GetHeight(); + + switch( nWinBits & VIEWMODE_MASK ) + { + case WB_ICON: + aPos.AdjustY(aImageSize.Height() ); + aPos.AdjustY(VER_DIST_BMP_STRING ); + aPos.AdjustX((nBoundWidth - aTextSize.Width()) / 2 ); + break; + + case WB_SMALLICON: + case WB_DETAILS: + aPos.AdjustX(aImageSize.Width() ); + aPos.AdjustX(HOR_DIST_BMP_STRING ); + aPos.AdjustY((nBoundHeight - aTextSize.Height()) / 2 ); + break; + } + return tools::Rectangle( aPos, aTextSize ); +} + + +long SvxIconChoiceCtrl_Impl::CalcBoundingWidth() const +{ + long nStringWidth = GetItemSize( IcnViewFieldType::Text ).Width(); + long nWidth = 0; + + switch( nWinBits & VIEWMODE_MASK ) + { + case WB_ICON: + nWidth = std::max( nStringWidth, aImageSize.Width() ); + break; + + case WB_SMALLICON: + case WB_DETAILS: + nWidth = aImageSize.Width(); + nWidth += HOR_DIST_BMP_STRING; + nWidth += nStringWidth; + break; + } + return nWidth; +} + +long SvxIconChoiceCtrl_Impl::CalcBoundingHeight() const +{ + long nStringHeight = GetItemSize(IcnViewFieldType::Text).Height(); + long nHeight = 0; + + switch( nWinBits & VIEWMODE_MASK ) + { + case WB_ICON: + nHeight = aImageSize.Height(); + nHeight += VER_DIST_BMP_STRING; + nHeight += nStringHeight; + break; + + case WB_SMALLICON: + case WB_DETAILS: + nHeight = std::max( aImageSize.Height(), nStringHeight ); + break; + } + if( nHeight > nMaxBoundHeight ) + { + const_cast<SvxIconChoiceCtrl_Impl*>(this)->nMaxBoundHeight = nHeight; + const_cast<SvxIconChoiceCtrl_Impl*>(this)->aHorSBar->SetLineSize( GetScrollBarLineSize() ); + const_cast<SvxIconChoiceCtrl_Impl*>(this)->aVerSBar->SetLineSize( GetScrollBarLineSize() ); + } + return nHeight; +} + +Size SvxIconChoiceCtrl_Impl::CalcBoundingSize() const +{ + return Size( CalcBoundingWidth(), CalcBoundingHeight() ); +} + +void SvxIconChoiceCtrl_Impl::RecalcAllBoundingRectsSmart() +{ + nMaxBoundHeight = 0; + maZOrderList.clear(); + size_t nCur; + SvxIconChoiceCtrlEntry* pEntry; + const size_t nCount = maEntries.size(); + + if( !IsAutoArrange() || !pHead ) + { + for( nCur = 0; nCur < nCount; nCur++ ) + { + pEntry = maEntries[ nCur ].get(); + if( IsBoundingRectValid( pEntry->aRect )) + { + Size aBoundSize( pEntry->aRect.GetSize() ); + if( aBoundSize.Height() > nMaxBoundHeight ) + nMaxBoundHeight = aBoundSize.Height(); + } + else + FindBoundingRect( pEntry ); + maZOrderList.push_back( pEntry ); + } + } + else + { + nCur = 0; + pEntry = pHead; + while( nCur != nCount ) + { + DBG_ASSERT(pEntry->pflink&&pEntry->pblink,"SvxIconChoiceCtrl_Impl::RecalcAllBoundingRect > Bad link(s)"); + if( IsBoundingRectValid( pEntry->aRect )) + { + Size aBoundSize( pEntry->aRect.GetSize() ); + if( aBoundSize.Height() > nMaxBoundHeight ) + nMaxBoundHeight = aBoundSize.Height(); + } + else + FindBoundingRect( pEntry ); + maZOrderList.push_back( pEntry ); + pEntry = pEntry->pflink; + nCur++; + } + } + AdjustScrollBars(); +} + +void SvxIconChoiceCtrl_Impl::FindBoundingRect( SvxIconChoiceCtrlEntry* pEntry ) +{ + DBG_ASSERT(!pEntry->IsPosLocked(),"Locked entry pos in FindBoundingRect"); + if( pEntry->IsPosLocked() && IsBoundingRectValid( pEntry->aRect) ) + { + AdjustVirtSize( pEntry->aRect ); + return; + } + Size aSize( CalcBoundingSize() ); + Point aPos(pGridMap->GetGridRect(pGridMap->GetUnoccupiedGrid()).TopLeft()); + SetBoundingRect_Impl( pEntry, aPos, aSize ); +} + +void SvxIconChoiceCtrl_Impl::SetBoundingRect_Impl( SvxIconChoiceCtrlEntry* pEntry, const Point& rPos, + const Size& /*rBoundingSize*/ ) +{ + tools::Rectangle aGridRect( rPos, Size(nGridDX, nGridDY) ); + pEntry->aGridRect = aGridRect; + Center( pEntry ); + AdjustVirtSize( pEntry->aRect ); + pGridMap->OccupyGrids( pEntry ); +} + + +void SvxIconChoiceCtrl_Impl::SetCursor( SvxIconChoiceCtrlEntry* pEntry ) +{ + if( pEntry == pCursor ) + { + if( pCursor && eSelectionMode == SelectionMode::Single && + !pCursor->IsSelected() ) + SelectEntry( pCursor, true ); + return; + } + ShowCursor( false ); + SvxIconChoiceCtrlEntry* pOldCursor = pCursor; + pCursor = pEntry; + if( pOldCursor ) + { + pOldCursor->ClearFlags( SvxIconViewFlags::FOCUSED ); + if( eSelectionMode == SelectionMode::Single ) + SelectEntry( pOldCursor, false ); // deselect old cursor + } + if( pCursor ) + { + ToTop( pCursor ); + pCursor->SetFlags( SvxIconViewFlags::FOCUSED ); + if( eSelectionMode == SelectionMode::Single ) + SelectEntry( pCursor, true ); + ShowCursor( true ); + } +} + + +void SvxIconChoiceCtrl_Impl::ShowCursor( bool bShow ) +{ + if( !pCursor || !bShow || !pView->HasFocus() ) + { + pView->HideFocus(); + return; + } + tools::Rectangle aRect ( CalcFocusRect( pCursor ) ); + /*pView->*/ShowFocus( aRect ); +} + + +void SvxIconChoiceCtrl_Impl::HideDDIcon() +{ + pView->PaintImmediately(); +} + +bool SvxIconChoiceCtrl_Impl::HandleScrollCommand( const CommandEvent& rCmd ) +{ + tools::Rectangle aDocRect( Point(), aVirtOutputSize ); + tools::Rectangle aVisRect( GetOutputRect() ); + if( aVisRect.IsInside( aDocRect )) + return false; + Size aDocSize( aDocRect.GetSize() ); + Size aVisSize( aVisRect.GetSize() ); + bool bHor = aDocSize.Width() > aVisSize.Width(); + bool bVer = aDocSize.Height() > aVisSize.Height(); + + long nScrollDX = 0, nScrollDY = 0; + + switch( rCmd.GetCommand() ) + { + case CommandEventId::StartAutoScroll: + { + pView->EndTracking(); + StartAutoScrollFlags nScrollFlags = StartAutoScrollFlags::NONE; + if( bHor ) + nScrollFlags |= StartAutoScrollFlags::Horz; + if( bVer ) + nScrollFlags |= StartAutoScrollFlags::Vert; + if( nScrollFlags != StartAutoScrollFlags::NONE ) + { + pView->StartAutoScroll( nScrollFlags ); + return true; + } + } + break; + + case CommandEventId::Wheel: + { + const CommandWheelData* pData = rCmd.GetWheelData(); + if( pData && (CommandWheelMode::SCROLL == pData->GetMode()) && !pData->IsHorz() ) + { + sal_uLong nScrollLines = pData->GetScrollLines(); + if( nScrollLines == COMMAND_WHEEL_PAGESCROLL ) + { + nScrollDY = GetScrollBarPageSize( aVisSize.Width() ); + if( pData->GetDelta() < 0 ) + nScrollDY *= -1; + } + else + { + nScrollDY = pData->GetNotchDelta() * static_cast<long>(nScrollLines); + nScrollDY *= GetScrollBarLineSize(); + } + } + } + break; + + case CommandEventId::AutoScroll: + { + const CommandScrollData* pData = rCmd.GetAutoScrollData(); + if( pData ) + { + nScrollDX = pData->GetDeltaX() * GetScrollBarLineSize(); + nScrollDY = pData->GetDeltaY() * GetScrollBarLineSize(); + } + } + break; + + default: break; + } + + if( nScrollDX || nScrollDY ) + { + aVisRect.AdjustTop( -nScrollDY ); + aVisRect.AdjustBottom( -nScrollDY ); + aVisRect.AdjustLeft( -nScrollDX ); + aVisRect.AdjustRight( -nScrollDX ); + MakeVisible( aVisRect ); + return true; + } + return false; +} + + +void SvxIconChoiceCtrl_Impl::Command( const CommandEvent& rCEvt ) +{ + // scroll mouse event? + if( (rCEvt.GetCommand() == CommandEventId::Wheel) || + (rCEvt.GetCommand() == CommandEventId::StartAutoScroll) || + (rCEvt.GetCommand() == CommandEventId::AutoScroll) ) + { + if( HandleScrollCommand( rCEvt ) ) + return; + } +} + +void SvxIconChoiceCtrl_Impl::ToTop( SvxIconChoiceCtrlEntry* pEntry ) +{ + if( maZOrderList.empty() || pEntry == maZOrderList.back()) + return; + + auto it = std::find(maZOrderList.begin(), maZOrderList.end(), pEntry); + if (it != maZOrderList.end()) + { + maZOrderList.erase( it ); + maZOrderList.push_back( pEntry ); + } +} + +void SvxIconChoiceCtrl_Impl::ClipAtVirtOutRect( tools::Rectangle& rRect ) const +{ + if( rRect.Bottom() >= aVirtOutputSize.Height() ) + rRect.SetBottom( aVirtOutputSize.Height() - 1 ); + if( rRect.Right() >= aVirtOutputSize.Width() ) + rRect.SetRight( aVirtOutputSize.Width() - 1 ); + if( rRect.Top() < 0 ) + rRect.SetTop( 0 ); + if( rRect.Left() < 0 ) + rRect.SetLeft( 0 ); +} + +// rRect: area of the document (in document coordinates) that we want to make +// visible +// bScrBar == true: rectangle was calculated because of a scrollbar event + +void SvxIconChoiceCtrl_Impl::MakeVisible( const tools::Rectangle& rRect, bool bScrBar ) +{ + tools::Rectangle aVirtRect( rRect ); + ClipAtVirtOutRect( aVirtRect ); + Point aOrigin( pView->GetMapMode().GetOrigin() ); + // convert to document coordinate + aOrigin *= -1; + tools::Rectangle aOutputArea( GetOutputRect() ); + if( aOutputArea.IsInside( aVirtRect ) ) + return; // is already visible + + long nDy; + if( aVirtRect.Top() < aOutputArea.Top() ) + { + // scroll up (nDy < 0) + nDy = aVirtRect.Top() - aOutputArea.Top(); + } + else if( aVirtRect.Bottom() > aOutputArea.Bottom() ) + { + // scroll down (nDy > 0) + nDy = aVirtRect.Bottom() - aOutputArea.Bottom(); + } + else + nDy = 0; + + long nDx; + if( aVirtRect.Left() < aOutputArea.Left() ) + { + // scroll to the left (nDx < 0) + nDx = aVirtRect.Left() - aOutputArea.Left(); + } + else if( aVirtRect.Right() > aOutputArea.Right() ) + { + // scroll to the right (nDx > 0) + nDx = aVirtRect.Right() - aOutputArea.Right(); + } + else + nDx = 0; + + aOrigin.AdjustX(nDx ); + aOrigin.AdjustY(nDy ); + aOutputArea.SetPos( aOrigin ); + if( GetUpdateMode() ) + { + HideDDIcon(); + pView->PaintImmediately(); + ShowCursor( false ); + } + + // invert origin for SV (so we can scroll/paint using document coordinates) + aOrigin *= -1; + SetOrigin( aOrigin ); + + bool bScrollable = pView->GetBackground().IsScrollable(); + + if( bScrollable && GetUpdateMode() ) + { + // scroll in reverse direction! + pView->Control::Scroll( -nDx, -nDy, aOutputArea, + ScrollFlags::NoChildren | ScrollFlags::UseClipRegion | ScrollFlags::Clip ); + } + else + pView->Invalidate(InvalidateFlags::NoChildren); + + if( aHorSBar->IsVisible() || aVerSBar->IsVisible() ) + { + if( !bScrBar ) + { + aOrigin *= -1; + // correct thumbs + if(aHorSBar->IsVisible() && aHorSBar->GetThumbPos() != aOrigin.X()) + aHorSBar->SetThumbPos( aOrigin.X() ); + if(aVerSBar->IsVisible() && aVerSBar->GetThumbPos() != aOrigin.Y()) + aVerSBar->SetThumbPos( aOrigin.Y() ); + } + } + + if( GetUpdateMode() ) + ShowCursor( true ); + + // check if we still need scrollbars + CheckScrollBars(); + if( bScrollable && GetUpdateMode() ) + pView->PaintImmediately(); + + // If the requested area can not be made completely visible, the + // Vis-Rect-Changed handler is called in any case. This case may occur e.g. + // if only few pixels of the lower border are invisible, but a scrollbar has + // a larger line size. + VisRectChanged(); +} + +sal_Int32 SvxIconChoiceCtrl_Impl::GetSelectionCount() const +{ + if( (nWinBits & WB_HIGHLIGHTFRAME) && pCurHighlightFrame ) + return 1; + return nSelectionCount; +} + +void SvxIconChoiceCtrl_Impl::ToggleSelection( SvxIconChoiceCtrlEntry* pEntry ) +{ + bool bSel; + bSel = !pEntry->IsSelected(); + SelectEntry( pEntry, bSel, true ); +} + +void SvxIconChoiceCtrl_Impl::DeselectAllBut( SvxIconChoiceCtrlEntry const * pThisEntryNot ) +{ + ClearSelectedRectList(); + + // TODO: work through z-order list, if necessary! + + size_t nCount = maEntries.size(); + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get(); + if( pEntry != pThisEntryNot && pEntry->IsSelected() ) + SelectEntry( pEntry, false, true ); + } + pAnchor = nullptr; + nFlags &= ~IconChoiceFlags::AddMode; +} + +Size SvxIconChoiceCtrl_Impl::GetMinGrid() const +{ + Size aMinSize( aImageSize ); + aMinSize.AdjustWidth(2 * LROFFS_BOUND ); + aMinSize.AdjustHeight(TBOFFS_BOUND ); // single offset is enough (FileDlg) + OUString const aStrDummy( "XXX" ); + Size aTextSize( pView->GetTextWidth( aStrDummy ), pView->GetTextHeight() ); + if( nWinBits & WB_ICON ) + { + aMinSize.AdjustHeight(VER_DIST_BMP_STRING ); + aMinSize.AdjustHeight(aTextSize.Height() ); + } + else + { + aMinSize.AdjustWidth(HOR_DIST_BMP_STRING ); + aMinSize.AdjustWidth(aTextSize.Width() ); + } + return aMinSize; +} + +void SvxIconChoiceCtrl_Impl::SetGrid( const Size& rSize ) +{ + Size aSize( rSize ); + Size aMinSize( GetMinGrid() ); + if( aSize.Width() < aMinSize.Width() ) + aSize.setWidth( aMinSize.Width() ); + if( aSize.Height() < aMinSize.Height() ) + aSize.setHeight( aMinSize.Height() ); + + nGridDX = aSize.Width(); + // HACK: Detail mode is not yet fully implemented, this workaround makes it + // fly with a single column + if( nWinBits & WB_DETAILS ) + { + const SvxIconChoiceCtrlColumnInfo* pCol = GetColumn( 0 ); + if( pCol ) + const_cast<SvxIconChoiceCtrlColumnInfo*>(pCol)->SetWidth( nGridDX ); + } + nGridDY = aSize.Height(); + SetDefaultTextSize(); +} + +// Calculates the maximum size that the text rectangle may use within its +// bounding rectangle. In WB_ICON mode with SvxIconChoiceCtrlTextMode::Full, Bottom is set to +// LONG_MAX. + +tools::Rectangle SvxIconChoiceCtrl_Impl::CalcMaxTextRect( const SvxIconChoiceCtrlEntry* pEntry ) const +{ + tools::Rectangle aBoundRect; + // avoid infinite recursion: don't calculate the bounding rectangle here + if( IsBoundingRectValid( pEntry->aRect ) ) + aBoundRect = pEntry->aRect; + else + aBoundRect = pEntry->aGridRect; + + tools::Rectangle aBmpRect( const_cast<SvxIconChoiceCtrl_Impl*>(this)->CalcBmpRect( + const_cast<SvxIconChoiceCtrlEntry*>(pEntry) ) ); + if( nWinBits & WB_ICON ) + { + aBoundRect.SetTop( aBmpRect.Bottom() ); + aBoundRect.AdjustTop(VER_DIST_BMP_STRING ); + if( aBoundRect.Top() > aBoundRect.Bottom()) + aBoundRect.SetTop( aBoundRect.Bottom() ); + aBoundRect.AdjustLeft(LROFFS_BOUND ); + aBoundRect.AdjustLeft( 1 ); + aBoundRect.AdjustRight( -(LROFFS_BOUND) ); + aBoundRect.AdjustRight( -1 ); + if( aBoundRect.Left() > aBoundRect.Right()) + aBoundRect.SetLeft( aBoundRect.Right() ); + if( pEntry->GetTextMode() == SvxIconChoiceCtrlTextMode::Full ) + aBoundRect.SetBottom( LONG_MAX ); + } + else + { + aBoundRect.SetLeft( aBmpRect.Right() ); + aBoundRect.AdjustLeft(HOR_DIST_BMP_STRING ); + aBoundRect.AdjustRight( -(LROFFS_BOUND) ); + if( aBoundRect.Left() > aBoundRect.Right() ) + aBoundRect.SetLeft( aBoundRect.Right() ); + long nHeight = aBoundRect.GetSize().Height(); + nHeight = nHeight - aDefaultTextSize.Height(); + nHeight /= 2; + aBoundRect.AdjustTop(nHeight ); + aBoundRect.AdjustBottom( -nHeight ); + } + return aBoundRect; +} + +void SvxIconChoiceCtrl_Impl::SetDefaultTextSize() +{ + long nDY = nGridDY; + nDY -= aImageSize.Height(); + nDY -= VER_DIST_BMP_STRING; + nDY -= 2 * TBOFFS_BOUND; + if (nDY <= 0) + nDY = 2; + + long nDX = nGridDX; + nDX -= 2 * LROFFS_BOUND; + nDX -= 2; + if (nDX <= 0) + nDX = 2; + + long nHeight = pView->GetTextHeight(); + if (nDY < nHeight) + nDY = nHeight; + if(pView->GetDPIScaleFactor() > 1) + { + nDY*=2; + } + aDefaultTextSize = Size(nDX, nDY); +} + + +void SvxIconChoiceCtrl_Impl::Center( SvxIconChoiceCtrlEntry* pEntry ) const +{ + pEntry->aRect = pEntry->aGridRect; + Size aSize( CalcBoundingSize() ); + if( nWinBits & WB_ICON ) + { + // center horizontally + long nBorder = pEntry->aGridRect.GetWidth() - aSize.Width(); + pEntry->aRect.AdjustLeft(nBorder / 2 ); + pEntry->aRect.AdjustRight( -(nBorder / 2) ); + } + // center vertically + pEntry->aRect.SetBottom( pEntry->aRect.Top() + aSize.Height() ); +} + + +// The deltas are the offsets by which the view is moved on the document. +// left, up: offsets < 0 +// right, down: offsets > 0 +void SvxIconChoiceCtrl_Impl::Scroll( long nDeltaX, long nDeltaY ) +{ + const MapMode& rMapMode = pView->GetMapMode(); + Point aOrigin( rMapMode.GetOrigin() ); + // convert to document coordinate + aOrigin *= -1; + aOrigin.AdjustY(nDeltaY ); + aOrigin.AdjustX(nDeltaX ); + tools::Rectangle aRect( aOrigin, aOutputSize ); + MakeVisible( aRect, true/*bScrollBar*/ ); +} + + +const Size& SvxIconChoiceCtrl_Impl::GetItemSize( IcnViewFieldType eItem ) const +{ + if (eItem == IcnViewFieldType::Text) + return aDefaultTextSize; + return aImageSize; // IcnViewFieldType::Image +} + +tools::Rectangle SvxIconChoiceCtrl_Impl::CalcFocusRect( SvxIconChoiceCtrlEntry* pEntry ) +{ + tools::Rectangle aTextRect( CalcTextRect( pEntry ) ); + tools::Rectangle aBoundRect( GetEntryBoundRect( pEntry ) ); + return tools::Rectangle( + aBoundRect.Left(), aBoundRect.Top() - 1, aBoundRect.Right() - 1, + aTextRect.Bottom() + 1); +} + +// the hot spot is the inner 50% of the rectangle +static tools::Rectangle GetHotSpot( const tools::Rectangle& rRect ) +{ + tools::Rectangle aResult( rRect ); + aResult.Justify(); + Size aSize( rRect.GetSize() ); + long nDelta = aSize.Width() / 4; + aResult.AdjustLeft(nDelta ); + aResult.AdjustRight( -nDelta ); + nDelta = aSize.Height() / 4; + aResult.AdjustTop(nDelta ); + aResult.AdjustBottom( -nDelta ); + return aResult; +} + +void SvxIconChoiceCtrl_Impl::SelectRect( SvxIconChoiceCtrlEntry* pEntry1, SvxIconChoiceCtrlEntry* pEntry2, + bool bAdd, std::vector<tools::Rectangle>* pOtherRects ) +{ + DBG_ASSERT(pEntry1 && pEntry2,"SelectEntry: Invalid Entry-Ptr"); + tools::Rectangle aRect( GetEntryBoundRect( pEntry1 ) ); + aRect.Union( GetEntryBoundRect( pEntry2 ) ); + SelectRect( aRect, bAdd, pOtherRects ); +} + +void SvxIconChoiceCtrl_Impl::SelectRect( const tools::Rectangle& rRect, bool bAdd, + std::vector<tools::Rectangle>* pOtherRects ) +{ + aCurSelectionRect = rRect; + if( maZOrderList.empty() ) + return; + + // set flag, so ToTop won't be called in Select + bool bAlreadySelectingRect(nFlags & IconChoiceFlags::SelectingRect); + nFlags |= IconChoiceFlags::SelectingRect; + + CheckBoundingRects(); + pView->PaintImmediately(); + const size_t nCount = maZOrderList.size(); + + tools::Rectangle aRect( rRect ); + aRect.Justify(); + bool bCalcOverlap = (bAdd && pOtherRects && !pOtherRects->empty()); + + bool bResetClipRegion = false; + if( !pView->IsClipRegion() ) + { + bResetClipRegion = true; + pView->SetClipRegion(vcl::Region(GetOutputRect())); + } + + for( size_t nPos = 0; nPos < nCount; nPos++ ) + { + SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nPos ]; + + if( !IsBoundingRectValid( pEntry->aRect )) + FindBoundingRect( pEntry ); + tools::Rectangle aBoundRect( GetHotSpot( pEntry->aRect ) ); + bool bSelected = pEntry->IsSelected(); + + bool bOverlaps; + if( bCalcOverlap ) + bOverlaps = IsOver( pOtherRects, aBoundRect ); + else + bOverlaps = false; + bool bOver = aRect.IsOver( aBoundRect ); + + if( bOver && !bOverlaps ) + { + // is inside the new selection rectangle and outside of any old one + // => select + if( !bSelected ) + SelectEntry( pEntry, true, true ); + } + else if( !bAdd ) + { + // is outside of the selection rectangle + // => deselect + if( bSelected ) + SelectEntry( pEntry, false, true ); + } + else if (bOverlaps) + { + // The entry is inside an old (=>span multiple rectangles with Ctrl) + // selection rectangle. + + // There is still a bug here! The selection status of an entry in a + // previous rectangle has to be restored, if it was touched by the + // current selection rectangle but is not inside it any more. + // For simplicity's sake, let's assume that all entries in the old + // rectangles were correctly selected. It is wrong to just deselect + // the intersection. + // Possible solution: remember a snapshot of the selection before + // spanning the rectangle. + if( aBoundRect.IsOver( rRect)) + { + // deselect intersection between old rectangles and current rectangle + if( bSelected ) + SelectEntry( pEntry, false, true ); + } + else + { + // select entry of an old rectangle + if( !bSelected ) + SelectEntry( pEntry, true, true ); + } + } + else if( !bOver && bSelected ) + { + // this entry is completely outside the rectangle => deselect it + SelectEntry( pEntry, false, true ); + } + } + + if( !bAlreadySelectingRect ) + nFlags &= ~IconChoiceFlags::SelectingRect; + + pView->PaintImmediately(); + if( bResetClipRegion ) + pView->SetClipRegion(); +} + +void SvxIconChoiceCtrl_Impl::SelectRange( + SvxIconChoiceCtrlEntry const * pStart, + SvxIconChoiceCtrlEntry const * pEnd, + bool bAdd ) +{ + sal_uLong nFront = GetEntryListPos( pStart ); + sal_uLong nBack = GetEntryListPos( pEnd ); + sal_uLong nFirst = std::min( nFront, nBack ); + sal_uLong nLast = std::max( nFront, nBack ); + sal_uLong i; + SvxIconChoiceCtrlEntry* pEntry; + + if ( ! bAdd ) + { + // deselect everything before the first entry if not in + // adding mode + for ( i=0; i<nFirst; i++ ) + { + pEntry = GetEntry( i ); + if( pEntry->IsSelected() ) + SelectEntry( pEntry, false, true ); + } + } + + // select everything between nFirst and nLast + for ( i=nFirst; i<=nLast; i++ ) + { + pEntry = GetEntry( i ); + if( ! pEntry->IsSelected() ) + SelectEntry( pEntry, true, true ); + } + + if ( ! bAdd ) + { + // deselect everything behind the last entry if not in + // adding mode + sal_uLong nEnd = GetEntryCount(); + for ( ; i<nEnd; i++ ) + { + pEntry = GetEntry( i ); + if( pEntry->IsSelected() ) + SelectEntry( pEntry, false, true ); + } + } +} + +bool SvxIconChoiceCtrl_Impl::IsOver( std::vector<tools::Rectangle>* pRectList, const tools::Rectangle& rBoundRect ) +{ + const sal_uInt16 nCount = pRectList->size(); + for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ ) + { + tools::Rectangle& rRect = (*pRectList)[ nCur ]; + if( rBoundRect.IsOver( rRect )) + return true; + } + return false; +} + +void SvxIconChoiceCtrl_Impl::AddSelectedRect( SvxIconChoiceCtrlEntry* pEntry1, + SvxIconChoiceCtrlEntry* pEntry2 ) +{ + DBG_ASSERT(pEntry1 && pEntry2,"SelectEntry: Invalid Entry-Ptr"); + tools::Rectangle aRect( GetEntryBoundRect( pEntry1 ) ); + aRect.Union( GetEntryBoundRect( pEntry2 ) ); + AddSelectedRect( aRect ); +} + +void SvxIconChoiceCtrl_Impl::AddSelectedRect( const tools::Rectangle& rRect ) +{ + tools::Rectangle newRect = rRect; + newRect.Justify(); + aSelectedRectList.push_back( newRect ); +} + +void SvxIconChoiceCtrl_Impl::ClearSelectedRectList() +{ + aSelectedRectList.clear(); +} + +IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, AutoArrangeHdl, Timer *, void) +{ + aAutoArrangeIdle.Stop(); + Arrange( IsAutoArrange(), 0, 0 ); +} + +IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, VisRectChangedHdl, Timer *, void) +{ + aVisRectChangedIdle.Stop(); +} + +IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, DocRectChangedHdl, Timer *, void) +{ + aDocRectChangedIdle.Stop(); +} + +#ifdef DBG_UTIL +void SvxIconChoiceCtrl_Impl::SetEntryTextMode( SvxIconChoiceCtrlTextMode eMode, SvxIconChoiceCtrlEntry* pEntry ) +{ + if( !pEntry ) + { + if( eTextMode != eMode ) + { + eTextMode = eMode; + Arrange( true, 0, 0 ); + } + } + else + { + if( pEntry->eTextMode != eMode ) + { + pEntry->eTextMode = eMode; + InvalidateEntry( pEntry ); + pView->Invalidate( GetEntryBoundRect( pEntry ) ); + AdjustVirtSize( pEntry->aRect ); + } + } +} +#endif + +// Draw my own focusrect, because the focusrect of the outputdevice has got the inverted color +// of the background. But what will we see, if the backgroundcolor is gray ? - We will see +// a gray focusrect on a gray background !!! + +void SvxIconChoiceCtrl_Impl::ShowFocus ( tools::Rectangle const & rRect ) +{ + Color aBkgColor(pView->GetBackground().GetColor()); + Color aPenColor; + sal_uInt16 nColor = ( aBkgColor.GetRed() + aBkgColor.GetGreen() + aBkgColor.GetBlue() ) / 3; + if (nColor > 128) + aPenColor = COL_BLACK; + else + aPenColor = COL_WHITE; + + aFocus.aPenColor = aPenColor; + aFocus.aRect = rRect; +} + +void SvxIconChoiceCtrl_Impl::DrawFocusRect(vcl::RenderContext& rRenderContext) +{ + rRenderContext.SetLineColor(aFocus.aPenColor); + rRenderContext.SetFillColor(); + tools::Polygon aPolygon (aFocus.aRect); + + LineInfo aLineInfo(LineStyle::Dash); + + aLineInfo.SetDashLen(1); + aLineInfo.SetDotLen(1); + aLineInfo.SetDistance(1); + aLineInfo.SetDotCount(1); + + rRenderContext.DrawPolyLine(aPolygon, aLineInfo); +} + +bool SvxIconChoiceCtrl_Impl::IsMnemonicChar( sal_Unicode cChar, sal_uLong& rPos ) const +{ + bool bRet = false; + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + size_t nEntryCount = GetEntryCount(); + for ( size_t i = 0; i < nEntryCount; ++i ) + { + if ( rI18nHelper.MatchMnemonic( GetEntry( i )->GetText(), cChar ) ) + { + bRet = true; + rPos = i; + break; + } + } + + return bRet; +} + + +IMPL_LINK(SvxIconChoiceCtrl_Impl, UserEventHdl, void*, nId, void ) +{ + if( nId == EVENTID_ADJUST_SCROLLBARS ) + { + nUserEventAdjustScrBars = nullptr; + AdjustScrollBars(); + } + else if( nId == EVENTID_SHOW_CURSOR ) + { + ShowCursor( true ); + } +} + +void SvxIconChoiceCtrl_Impl::CancelUserEvents() +{ + if( nUserEventAdjustScrBars ) + { + Application::RemoveUserEvent( nUserEventAdjustScrBars ); + nUserEventAdjustScrBars = nullptr; + } +} + +void SvxIconChoiceCtrl_Impl::InvalidateEntry( SvxIconChoiceCtrlEntry* pEntry ) +{ + if( pEntry == pCursor ) + ShowCursor( false ); + pView->Invalidate( pEntry->aRect ); + Center( pEntry ); + pView->Invalidate( pEntry->aRect ); + if( pEntry == pCursor ) + ShowCursor( true ); +} + +SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetFirstSelectedEntry() const +{ + if( !GetSelectionCount() ) + return nullptr; + + if( (nWinBits & WB_HIGHLIGHTFRAME) && (eSelectionMode == SelectionMode::NONE) ) + { + return pCurHighlightFrame; + } + + size_t nCount = maEntries.size(); + if( !pHead ) + { + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get(); + if( pEntry->IsSelected() ) + { + return pEntry; + } + } + } + else + { + SvxIconChoiceCtrlEntry* pEntry = pHead; + while( nCount-- ) + { + if( pEntry->IsSelected() ) + { + return pEntry; + } + pEntry = pEntry->pflink; + if( nCount && pEntry == pHead ) + { + OSL_FAIL("SvxIconChoiceCtrl_Impl::GetFirstSelectedEntry > infinite loop!"); + return nullptr; + } + } + } + return nullptr; +} + +void SvxIconChoiceCtrl_Impl::SelectAll() +{ + size_t nCount = maEntries.size(); + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get(); + SelectEntry( pEntry, true/*bSelect*/, true ); + } + nFlags &= ~IconChoiceFlags::AddMode; + pAnchor = nullptr; +} + + + + +sal_Int32 SvxIconChoiceCtrl_Impl::GetEntryListPos( SvxIconChoiceCtrlEntry const * pEntry ) const +{ + if( !(nFlags & IconChoiceFlags::EntryListPosValid )) + const_cast<SvxIconChoiceCtrl_Impl*>(this)->SetListPositions(); + return pEntry->nPos; +} + +void SvxIconChoiceCtrl_Impl::InitSettings() +{ + const StyleSettings& rStyleSettings = pView->GetSettings().GetStyleSettings(); + + // unit (from settings) is Point + vcl::Font aFont( rStyleSettings.GetFieldFont() ); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + pView->SetPointFont( aFont ); + SetDefaultTextSize(); + + pView->SetTextColor( rStyleSettings.GetFieldTextColor() ); + pView->SetTextFillColor(); + + pView->SetBackground( rStyleSettings.GetFieldColor()); + + long nScrBarSize = rStyleSettings.GetScrollBarSize(); + if( nScrBarSize == nHorSBarHeight && nScrBarSize == nVerSBarWidth ) + return; + + nHorSBarHeight = nScrBarSize; + Size aSize( aHorSBar->GetSizePixel() ); + aSize.setHeight( nScrBarSize ); + aHorSBar->Hide(); + aHorSBar->SetSizePixel( aSize ); + + nVerSBarWidth = nScrBarSize; + aSize = aVerSBar->GetSizePixel(); + aSize.setWidth( nScrBarSize ); + aVerSBar->Hide(); + aVerSBar->SetSizePixel( aSize ); + + Size aOSize( pView->Control::GetOutputSizePixel() ); + PositionScrollBars( aOSize.Width(), aOSize.Height() ); + AdjustScrollBars(); +} + +void SvxIconChoiceCtrl_Impl::SetPositionMode( SvxIconChoiceCtrlPositionMode eMode ) +{ + if( eMode == ePositionMode ) + return; + + SvxIconChoiceCtrlPositionMode eOldMode = ePositionMode; + ePositionMode = eMode; + size_t nCount = maEntries.size(); + + if( eOldMode == SvxIconChoiceCtrlPositionMode::AutoArrange ) + { + // when positioning moved entries "hard", there are problems with + // unwanted overlaps, as these entries aren't taken into account in + // Arrange. + if( maEntries.size() ) + aAutoArrangeIdle.Start(); + return; + } + + if( ePositionMode == SvxIconChoiceCtrlPositionMode::AutoArrange ) + { + for( size_t nCur = 0; nCur < nCount; nCur++ ) + { + SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get(); + if( pEntry->GetFlags() & SvxIconViewFlags(SvxIconViewFlags::POS_LOCKED | SvxIconViewFlags::POS_MOVED)) + SetEntryPos(pEntry, GetEntryBoundRect( pEntry ).TopLeft()); + } + + if( maEntries.size() ) + aAutoArrangeIdle.Start(); + } +} + +void SvxIconChoiceCtrl_Impl::SetEntryPredecessor( SvxIconChoiceCtrlEntry* pEntry, + SvxIconChoiceCtrlEntry* pPredecessor ) +{ + if( !IsAutoArrange() ) + return; + + if( pEntry == pPredecessor ) + return; + + sal_uLong nPos1 = GetEntryListPos( pEntry ); + if( !pHead ) + { + if( pPredecessor ) + { + sal_uLong nPos2 = GetEntryListPos( pPredecessor ); + if( nPos1 == (nPos2 + 1) ) + return; // is already the predecessor + } + else if( !nPos1 ) + return; + + InitPredecessors(); + } + + if( !pPredecessor && pHead == pEntry ) + return; // is already the first one + + bool bSetHead = false; + if( !pPredecessor ) + { + bSetHead = true; + pPredecessor = pHead->pblink; + } + if( pEntry == pHead ) + { + pHead = pHead->pflink; + bSetHead = false; + } + if( pEntry != pPredecessor ) + { + pEntry->Unlink(); + pEntry->SetBacklink( pPredecessor ); + } + if( bSetHead ) + pHead = pEntry; + aAutoArrangeIdle.Start(); +} + +SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::FindEntryPredecessor( SvxIconChoiceCtrlEntry* pEntry, + const Point& rPosTopLeft ) +{ + Point aPos( rPosTopLeft ); //TopLeft + tools::Rectangle aCenterRect( CalcBmpRect( pEntry, &aPos )); + Point aNewPos( aCenterRect.Center() ); + sal_uLong nGrid = GetPredecessorGrid( aNewPos ); + size_t nCount = maEntries.size(); + if( nGrid == ULONG_MAX ) + return nullptr; + if( nGrid >= nCount ) + nGrid = nCount - 1; + if( !pHead ) + return maEntries[ nGrid ].get(); + + SvxIconChoiceCtrlEntry* pCur = pHead; // Grid 0 + // TODO: go through list from the end if nGrid > nCount/2 + for( sal_uLong nCur = 0; nCur < nGrid; nCur++ ) + pCur = pCur->pflink; + + return pCur; +} + +sal_uLong SvxIconChoiceCtrl_Impl::GetPredecessorGrid( const Point& rPos) const +{ + Point aPos( rPos ); + aPos.AdjustX( -(LROFFS_WINBORDER) ); + aPos.AdjustY( -(TBOFFS_WINBORDER) ); + long nMaxCol = aVirtOutputSize.Width() / nGridDX; + if( nMaxCol ) + nMaxCol--; + long nGridX = aPos.X() / nGridDX; + if( nGridX > nMaxCol ) + nGridX = nMaxCol; + long nGridY = aPos.Y() / nGridDY; + long nGridsX = aOutputSize.Width() / nGridDX; + sal_uLong nGrid = (nGridY * nGridsX) + nGridX; + long nMiddle = (nGridX * nGridDX) + (nGridDX / 2); + if( rPos.X() < nMiddle ) + { + if( !nGrid ) + nGrid = ULONG_MAX; + else + nGrid--; + } + return nGrid; +} + +bool SvxIconChoiceCtrl_Impl::RequestHelp( const HelpEvent& rHEvt ) +{ + if ( !(rHEvt.GetMode() & HelpEventMode::QUICK ) ) + return false; + + Point aPos( pView->ScreenToOutputPixel(rHEvt.GetMousePosPixel() ) ); + aPos -= pView->GetMapMode().GetOrigin(); + SvxIconChoiceCtrlEntry* pEntry = GetEntry( aPos, true ); + + if ( !pEntry ) + return false; + + OUString sQuickHelpText = pEntry->GetQuickHelpText(); + OUString aEntryText( SvtIconChoiceCtrl::GetEntryText( pEntry ) ); + tools::Rectangle aTextRect( CalcTextRect( pEntry, nullptr, &aEntryText ) ); + if ( ( !aTextRect.IsInside( aPos ) || aEntryText.isEmpty() ) && sQuickHelpText.isEmpty() ) + return false; + + tools::Rectangle aOptTextRect( aTextRect ); + aOptTextRect.SetBottom( LONG_MAX ); + DrawTextFlags nNewFlags = nCurTextDrawFlags; + nNewFlags &= ~DrawTextFlags( DrawTextFlags::Clip | DrawTextFlags::EndEllipsis ); + aOptTextRect = pView->GetTextRect( aOptTextRect, aEntryText, nNewFlags ); + if ( aOptTextRect != aTextRect || !sQuickHelpText.isEmpty() ) + { + //aTextRect.Right() = aTextRect.Left() + aRealSize.Width() + 4; + Point aPt( aOptTextRect.TopLeft() ); + aPt += pView->GetMapMode().GetOrigin(); + aPt = pView->OutputToScreenPixel( aPt ); + // subtract border of tooltip help + aPt.AdjustY( -1 ); + aPt.AdjustX( -3 ); + aOptTextRect.SetPos( aPt ); + OUString sHelpText; + if ( !sQuickHelpText.isEmpty() ) + sHelpText = sQuickHelpText; + else + sHelpText = aEntryText; + Help::ShowQuickHelp( static_cast<vcl::Window*>(pView), aOptTextRect, sHelpText, QuickHelpFlags::Left | QuickHelpFlags::VCenter ); + } + + return true; +} + +void SvxIconChoiceCtrl_Impl::SetColumn( sal_uInt16 nIndex, const SvxIconChoiceCtrlColumnInfo& rInfo) +{ + if (!m_pColumns) + m_pColumns.reset(new SvxIconChoiceCtrlColumnInfoMap); + + SvxIconChoiceCtrlColumnInfo* pInfo = new SvxIconChoiceCtrlColumnInfo( rInfo ); + m_pColumns->insert(std::make_pair(nIndex, std::unique_ptr<SvxIconChoiceCtrlColumnInfo>(pInfo))); + + // HACK: Detail mode is not yet fully implemented, this workaround makes it + // fly with a single column + if( !nIndex && (nWinBits & WB_DETAILS) ) + nGridDX = pInfo->GetWidth(); + + if( GetUpdateMode() ) + Arrange( IsAutoArrange(), 0, 0 ); +} + +const SvxIconChoiceCtrlColumnInfo* SvxIconChoiceCtrl_Impl::GetColumn( sal_uInt16 nIndex ) const +{ + if (!m_pColumns) + return nullptr; + auto const it = m_pColumns->find( nIndex ); + if (it == m_pColumns->end()) + return nullptr; + return it->second.get(); +} + +void SvxIconChoiceCtrl_Impl::DrawHighlightFrame(vcl::RenderContext& rRenderContext, const tools::Rectangle& rBmpRect) +{ + tools::Rectangle aBmpRect(rBmpRect); + long nBorder = 2; + if (aImageSize.Width() < 32) + nBorder = 1; + aBmpRect.AdjustRight(nBorder ); + aBmpRect.AdjustLeft( -nBorder ); + aBmpRect.AdjustBottom(nBorder ); + aBmpRect.AdjustTop( -nBorder ); + + DecorationView aDecoView(&rRenderContext); + DrawHighlightFrameStyle nDecoFlags; + if (bHighlightFramePressed) + nDecoFlags = DrawHighlightFrameStyle::In; + else + nDecoFlags = DrawHighlightFrameStyle::Out; + aDecoView.DrawHighlightFrame(aBmpRect, nDecoFlags); +} + +void SvxIconChoiceCtrl_Impl::SetEntryHighlightFrame( SvxIconChoiceCtrlEntry* pEntry, + bool bKeepHighlightFlags ) +{ + if( pEntry == pCurHighlightFrame ) + return; + + if( !bKeepHighlightFlags ) + bHighlightFramePressed = false; + + if (pCurHighlightFrame) + { + tools::Rectangle aInvalidationRect(GetEntryBoundRect(pCurHighlightFrame)); + aInvalidationRect.expand(5); + pCurHighlightFrame = nullptr; + pView->Invalidate(aInvalidationRect); + } + + pCurHighlightFrame = pEntry; + if (pEntry) + { + tools::Rectangle aInvalidationRect(GetEntryBoundRect(pEntry)); + aInvalidationRect.expand(5); + pView->Invalidate(aInvalidationRect); + } +} + +void SvxIconChoiceCtrl_Impl::CallSelectHandler() +{ + // When single-click mode is active, the selection handler should be called + // synchronously, as the selection is automatically taken away once the + // mouse cursor doesn't touch the object any more. Else, we might run into + // missing calls to Select if the object is selected from a mouse movement, + // because when starting the timer, the mouse cursor might have already left + // the object. + // In special cases (=>SfxFileDialog!), synchronous calls can be forced via + // WB_NOASYNCSELECTHDL. + if( nWinBits & (WB_NOASYNCSELECTHDL | WB_HIGHLIGHTFRAME) ) + { + pHdlEntry = nullptr; + pView->ClickIcon(); + //pView->Select(); + } + else + aCallSelectHdlIdle.Start(); +} + +IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, CallSelectHdlHdl, Timer *, void) +{ + pHdlEntry = nullptr; + pView->ClickIcon(); + //pView->Select(); +} + +void SvxIconChoiceCtrl_Impl::SetOrigin( const Point& rPos ) +{ + MapMode aMapMode( pView->GetMapMode() ); + aMapMode.SetOrigin( rPos ); + pView->SetMapMode( aMapMode ); +} + +void SvxIconChoiceCtrl_Impl::CallEventListeners( VclEventId nEvent, void* pData ) +{ + pView->CallImplEventListeners( nEvent, pData ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |