/* -*- 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 <vcl/svapp.hxx>
#include <vcl/salnativewidgets.hxx>
#include <vcl/help.hxx>
#include <vcl/settings.hxx>
#include <cstdlib>
#include <memory>
#include <stack>
#include <svtools/treelistbox.hxx>
#include <svtools/iconview.hxx>
#include <svtools/svlbitm.hxx>
#include <svimpbox.hxx>
#include <iconviewimpl.hxx>
#include <rtl/instance.hxx>
#include <tools/wintypes.hxx>
#include <bitmaps.hlst>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <svtools/treelistentry.hxx>
#include <svtools/viewdataentry.hxx>
// #i27063# (pl), #i32300# (pb) never access VCL after DeInitVCL - also no destructors
Image* SvImpLBox::s_pDefCollapsed = nullptr;
Image* SvImpLBox::s_pDefExpanded = nullptr;
oslInterlockedCount SvImpLBox::s_nImageRefCount = 0;
SvImpLBox::SvImpLBox( SvTreeListBox* pLBView, SvTreeList* pLBTree, WinBits nWinStyle)
: aHorSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_HSCROLL))
, aScrBarBox(VclPtr<ScrollBarBox>::Create(pLBView))
, aFctSet(this, pLBView)
, bAreChildrenTransient(true)
, mbForceMakeVisible (false)
, aVerSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_VSCROLL))
, aOutputSize(0, 0)
, mbNoAutoCurEntry(false)
, aSelEng(pLBView, nullptr)
, nNextVerVisSize(0)
{
osl_atomic_increment(&s_nImageRefCount);
pView = pLBView;
pTree = pLBTree;
aSelEng.SetFunctionSet( static_cast<FunctionSet*>(&aFctSet) );
aSelEng.ExpandSelectionOnMouseMove( false );
SetStyle( nWinStyle );
SetSelectionMode( SelectionMode::Single );
SetDragDropMode( DragDropMode::NONE );
aVerSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollUpDownHdl ) );
aHorSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollLeftRightHdl ) );
aHorSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
aVerSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
aVerSBar->SetRange( Range(0,0) );
aVerSBar->Hide();
aHorSBar->SetRange( Range(0,0) );
aHorSBar->SetPageSize( 24 ); // pixels
aHorSBar->SetLineSize( 8 ); // pixels
nHorSBarHeight = static_cast<short>(aHorSBar->GetSizePixel().Height());
nVerSBarWidth = static_cast<short>(aVerSBar->GetSizePixel().Width());
pStartEntry = nullptr;
pCursor = nullptr;
pAnchor = nullptr;
nVisibleCount = 0; // number of rows of data in control
nNodeBmpTabDistance = NODE_BMP_TABDIST_NOTVALID;
nNodeBmpWidth = 0;
bAsyncBeginDrag = false;
aAsyncBeginDragIdle.SetPriority( TaskPriority::HIGHEST );
aAsyncBeginDragIdle.SetInvokeHandler( LINK(this,SvImpLBox,BeginDragHdl));
// button animation in listbox
pActiveButton = nullptr;
pActiveEntry = nullptr;
pActiveTab = nullptr;
nFlags = LBoxFlags::NONE;
nCurTabPos = FIRST_ENTRY_TAB;
aEditIdle.SetPriority( TaskPriority::LOWEST );
aEditIdle.SetInvokeHandler( LINK(this,SvImpLBox,EditTimerCall) );
nMostRight = -1;
pMostRightEntry = nullptr;
nCurUserEvent = nullptr;
bUpdateMode = true;
bInVScrollHdl = false;
nFlags |= LBoxFlags::Filling;
bSubLstOpRet = bSubLstOpLR = bContextMenuHandling = bIsCellFocusEnabled = false;
bSubLstOpDblClick = true;
}
SvImpLBox::~SvImpLBox()
{
aEditIdle.Stop();
StopUserEvent();
if ( osl_atomic_decrement(&s_nImageRefCount) == 0 )
{
DELETEZ(s_pDefCollapsed);
DELETEZ(s_pDefExpanded);
}
aVerSBar.disposeAndClear();
aHorSBar.disposeAndClear();
aScrBarBox.disposeAndClear();
}
void SvImpLBox::UpdateStringSorter()
{
const css::lang::Locale& rNewLocale = Application::GetSettings().GetLanguageTag().getLocale();
if( m_pStringSorter )
{
// different Locale from the older one, drop it and force recreate
const css::lang::Locale &aLocale = m_pStringSorter->getLocale();
if( aLocale.Language != rNewLocale.Language ||
aLocale.Country != rNewLocale.Country ||
aLocale.Variant != rNewLocale.Variant )
m_pStringSorter.reset();
}
if( !m_pStringSorter )
{
m_pStringSorter.reset(new comphelper::string::NaturalStringSorter(
::comphelper::getProcessComponentContext(),
rNewLocale));
}
}
short SvImpLBox::UpdateContextBmpWidthVector( SvTreeListEntry const * pEntry, short nWidth )
{
DBG_ASSERT( pView->pModel, "View and Model aren't valid!" );
sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
// initialize vector if necessary
std::vector< short >::size_type nSize = aContextBmpWidthVector.size();
while ( nDepth > nSize )
{
aContextBmpWidthVector.resize( nSize + 1 );
aContextBmpWidthVector.at( nSize ) = nWidth;
++nSize;
}
if( aContextBmpWidthVector.size() == nDepth )
{
aContextBmpWidthVector.resize( nDepth + 1 );
aContextBmpWidthVector.at( nDepth ) = 0;
}
short nContextBmpWidth = aContextBmpWidthVector[ nDepth ];
if( nContextBmpWidth < nWidth )
{
aContextBmpWidthVector.at( nDepth ) = nWidth;
return nWidth;
}
else
return nContextBmpWidth;
}
void SvImpLBox::UpdateContextBmpWidthVectorFromMovedEntry( SvTreeListEntry* pEntry )
{
DBG_ASSERT( pEntry, "Moved Entry is invalid!" );
SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry->GetFirstItem(SvLBoxItemType::ContextBmp) );
short nExpWidth = static_cast<short>(pBmpItem->GetBitmap1().GetSizePixel().Width());
short nColWidth = static_cast<short>(pBmpItem->GetBitmap2().GetSizePixel().Width());
short nMax = std::max(nExpWidth, nColWidth);
UpdateContextBmpWidthVector( pEntry, nMax );
if( pEntry->HasChildren() ) // recursive call, whether expanded or not
{
SvTreeListEntry* pChild = pView->FirstChild( pEntry );
DBG_ASSERT( pChild, "The first child is invalid!" );
do
{
UpdateContextBmpWidthVectorFromMovedEntry( pChild );
pChild = pView->Next( pChild );
} while ( pChild );
}
}
void SvImpLBox::UpdateContextBmpWidthMax( SvTreeListEntry const * pEntry )
{
sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
if( aContextBmpWidthVector.size() < 1 )
return;
short nWidth = aContextBmpWidthVector[ nDepth ];
if( nWidth != pView->nContextBmpWidthMax ) {
pView->nContextBmpWidthMax = nWidth;
nFlags |= LBoxFlags::IgnoreChangedTabs;
pView->SetTabs();
nFlags &= ~LBoxFlags::IgnoreChangedTabs;
}
}
void SvImpLBox::CalcCellFocusRect( SvTreeListEntry const * pEntry, tools::Rectangle& rRect )
{
if ( !(pEntry && bIsCellFocusEnabled) )
return;
if ( nCurTabPos > FIRST_ENTRY_TAB )
{
SvLBoxItem& rItem = pCursor->GetItem( nCurTabPos );
rRect.SetLeft( pView->GetTab( pCursor, &rItem )->GetPos() );
}
if (pCursor->ItemCount() > static_cast<size_t>(nCurTabPos+1))
{
SvLBoxItem& rNextItem = pCursor->GetItem( nCurTabPos + 1 );
long nRight = pView->GetTab( pCursor, &rNextItem )->GetPos() - 1;
if ( nRight < rRect.Right() )
rRect.SetRight( nRight );
}
}
void SvImpLBox::SetStyle( WinBits i_nWinStyle )
{
m_nStyle = i_nWinStyle;
if ( ( m_nStyle & WB_SIMPLEMODE) && ( aSelEng.GetSelectionMode() == SelectionMode::Multiple ) )
aSelEng.AddAlways( true );
}
void SvImpLBox::SetNoAutoCurEntry( bool b )
{
mbNoAutoCurEntry = b;
}
// don't touch the model any more
void SvImpLBox::Clear()
{
StopUserEvent();
pStartEntry = nullptr;
pAnchor = nullptr;
pActiveButton = nullptr;
pActiveEntry = nullptr;
pActiveTab = nullptr;
nMostRight = -1;
pMostRightEntry = nullptr;
// don't touch the cursor any more
if( pCursor )
{
if( pView->HasFocus() )
pView->HideFocus();
pCursor = nullptr;
}
aVerSBar->Hide();
aVerSBar->SetThumbPos( 0 );
Range aRange( 0, 0 );
aVerSBar->SetRange( aRange );
aOutputSize = pView->Control::GetOutputSizePixel();
aHorSBar->Hide();
aHorSBar->SetThumbPos( 0 );
MapMode aMapMode( pView->GetMapMode());
aMapMode.SetOrigin( Point(0,0) );
pView->Control::SetMapMode( aMapMode );
aHorSBar->SetRange( aRange );
aHorSBar->SetSizePixel(Size(aOutputSize.Width(),nHorSBarHeight));
pView->SetClipRegion();
if( GetUpdateMode() )
pView->Invalidate( GetVisibleArea() );
nFlags |= LBoxFlags::Filling;
if( !aHorSBar->IsVisible() && !aVerSBar->IsVisible() )
aScrBarBox->Hide();
aContextBmpWidthVector.clear();
CallEventListeners( VclEventId::ListboxItemRemoved );
}
// *********************************************************************
// Paint, navigate, scroll
// *********************************************************************
IMPL_LINK_NOARG(SvImpLBox, EndScrollHdl, ScrollBar*, void)
{
if( nFlags & LBoxFlags::EndScrollSetVisSize )
{
aVerSBar->SetVisibleSize( nNextVerVisSize );
nFlags &= ~LBoxFlags::EndScrollSetVisSize;
}
EndScroll();
}
// handler for vertical scrollbar
IMPL_LINK( SvImpLBox, ScrollUpDownHdl, ScrollBar *, pScrollBar, void )
{
DBG_ASSERT(!bInVScrollHdl,"Scroll handler out-paces itself!");
long nDelta = pScrollBar->GetDelta();
if( !nDelta )
return;
nFlags &= (~LBoxFlags::Filling);
bInVScrollHdl = true;
if( pView->IsEditingActive() )
{
pView->EndEditing( true ); // Cancel
pView->Update();
}
BeginScroll();
if( nDelta > 0 )
{
if( nDelta == 1 )
CursorDown();
else
PageDown( static_cast<sal_uInt16>(nDelta) );
}
else
{
nDelta *= -1;
if( nDelta == 1 )
CursorUp();
else
PageUp( static_cast<sal_uInt16>(nDelta) );
}
bInVScrollHdl = false;
}
void SvImpLBox::CursorDown()
{
if (!pStartEntry)
return;
SvTreeListEntry* pNextFirstToDraw = pView->NextVisible(pStartEntry);
if( pNextFirstToDraw )
{
nFlags &= (~LBoxFlags::Filling);
ShowCursor( false );
pView->Update();
pStartEntry = pNextFirstToDraw;
tools::Rectangle aArea( GetVisibleArea() );
pView->Scroll( 0, -(pView->GetEntryHeight()), aArea, ScrollFlags::NoChildren );
pView->Update();
ShowCursor( true );
pView->NotifyScrolled();
}
}
void SvImpLBox::CursorUp()
{
if (!pStartEntry)
return;
SvTreeListEntry* pPrevFirstToDraw = pView->PrevVisible(pStartEntry);
if( !pPrevFirstToDraw )
return;
nFlags &= (~LBoxFlags::Filling);
long nEntryHeight = pView->GetEntryHeight();
ShowCursor( false );
pView->Update();
pStartEntry = pPrevFirstToDraw;
tools::Rectangle aArea( GetVisibleArea() );
aArea.AdjustBottom( -nEntryHeight );
pView->Scroll( 0, nEntryHeight, aArea, ScrollFlags::NoChildren );
pView->Update();
ShowCursor( true );
pView->NotifyScrolled();
}
void SvImpLBox::PageDown( sal_uInt16 nDelta )
{
sal_uInt16 nRealDelta = nDelta;
if( !nDelta )
return;
if (!pStartEntry)
return;
SvTreeListEntry* pNext = pView->NextVisible(pStartEntry, nRealDelta);
if( pNext == pStartEntry )
return;
ShowCursor( false );
nFlags &= (~LBoxFlags::Filling);
pView->Update();
pStartEntry = pNext;
if( nRealDelta >= nVisibleCount )
{
pView->Invalidate( GetVisibleArea() );
pView->Update();
}
else
{
tools::Rectangle aArea( GetVisibleArea() );
long nScroll = pView->GetEntryHeight() * static_cast<long>(nRealDelta);
nScroll = -nScroll;
pView->Update();
pView->Scroll( 0, nScroll, aArea, ScrollFlags::NoChildren );
pView->Update();
pView->NotifyScrolled();
}
ShowCursor( true );
}
void SvImpLBox::PageUp( sal_uInt16 nDelta )
{
sal_uInt16 nRealDelta = nDelta;
if( !nDelta )
return;
if (!pStartEntry)
return;
SvTreeListEntry* pPrev = pView->PrevVisible(pStartEntry, nRealDelta);
if( pPrev == pStartEntry )
return;
nFlags &= (~LBoxFlags::Filling);
ShowCursor( false );
pView->Update();
pStartEntry = pPrev;
if( nRealDelta >= nVisibleCount )
{
pView->Invalidate( GetVisibleArea() );
pView->Update();
}
else
{
long nEntryHeight = pView->GetEntryHeight();
tools::Rectangle aArea( GetVisibleArea() );
pView->Update();
pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, ScrollFlags::NoChildren );
pView->Update();
pView->NotifyScrolled();
}
ShowCursor( true );
}
void SvImpLBox::KeyUp( bool bPageUp )
{
if( !aVerSBar->IsVisible() )
return;
long nDelta;
if( bPageUp )
nDelta = aVerSBar->GetPageSize();
else
nDelta = 1;
long nThumbPos = aVerSBar->GetThumbPos();
if( nThumbPos < nDelta )
nDelta = nThumbPos;
if( nDelta <= 0 )
return;
nFlags &= (~LBoxFlags::Filling);
BeginScroll();
aVerSBar->SetThumbPos( nThumbPos - nDelta );
if( bPageUp )
PageUp( static_cast<short>(nDelta) );
else
CursorUp();
EndScroll();
}
void SvImpLBox::KeyDown( bool bPageDown )
{
if( !aVerSBar->IsVisible() )
return;
long nDelta;
if( bPageDown )
nDelta = aVerSBar->GetPageSize();
else
nDelta = 1;
long nThumbPos = aVerSBar->GetThumbPos();
long nVisibleSize = aVerSBar->GetVisibleSize();
long nRange = aVerSBar->GetRange().Len();
long nTmp = nThumbPos+nVisibleSize;
while( (nDelta > 0) && (nTmp+nDelta) >= nRange )
nDelta--;
if( nDelta <= 0 )
return;
nFlags &= (~LBoxFlags::Filling);
BeginScroll();
aVerSBar->SetThumbPos( nThumbPos+nDelta );
if( bPageDown )
PageDown( static_cast<short>(nDelta) );
else
CursorDown();
EndScroll();
}
void SvImpLBox::InvalidateEntriesFrom( long nY ) const
{
if( !(nFlags & LBoxFlags::InPaint ))
{
tools::Rectangle aRect( GetVisibleArea() );
aRect.SetTop( nY );
pView->Invalidate( aRect );
}
}
void SvImpLBox::InvalidateEntry( long nY ) const
{
if( !(nFlags & LBoxFlags::InPaint ))
{
tools::Rectangle aRect( GetVisibleArea() );
long nMaxBottom = aRect.Bottom();
aRect.SetTop( nY );
aRect.SetBottom( nY ); aRect.AdjustBottom(pView->GetEntryHeight() );
if( aRect.Top() > nMaxBottom )
return;
if( aRect.Bottom() > nMaxBottom )
aRect.SetBottom( nMaxBottom );
pView->Invalidate( aRect );
}
}
void SvImpLBox::InvalidateEntry( SvTreeListEntry* pEntry )
{
if( GetUpdateMode() )
{
long nPrev = nMostRight;
SetMostRight( pEntry );
if( nPrev < nMostRight )
ShowVerSBar();
}
if( !(nFlags & LBoxFlags::InPaint ))
{
bool bHasFocusRect = false;
if( pEntry==pCursor && pView->HasFocus() )
{
bHasFocusRect = true;
ShowCursor( false );
}
InvalidateEntry( GetEntryLine( pEntry ) );
if( bHasFocusRect )
ShowCursor( true );
}
}
void SvImpLBox::RecalcFocusRect()
{
if( pView->HasFocus() && pCursor )
{
pView->HideFocus();
long nY = GetEntryLine( pCursor );
tools::Rectangle aRect = pView->GetFocusRect( pCursor, nY );
CalcCellFocusRect( pCursor, aRect );
vcl::Region aOldClip( pView->GetClipRegion());
vcl::Region aClipRegion( GetClipRegionRect() );
pView->SetClipRegion( aClipRegion );
pView->ShowFocus( aRect );
pView->SetClipRegion( aOldClip );
}
}
// Sets cursor. When using SingleSelection, the selection is adjusted.
void SvImpLBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect )
{
SvViewDataEntry* pViewDataNewCur = nullptr;
if( pEntry )
pViewDataNewCur= pView->GetViewDataEntry(pEntry);
if( pEntry &&
pEntry == pCursor &&
pViewDataNewCur &&
pViewDataNewCur->HasFocus() &&
pViewDataNewCur->IsSelected())
{
return;
}
// if this cursor is not selectable, find first visible that is and use it
while( pEntry && pViewDataNewCur && !pViewDataNewCur->IsSelectable() )
{
pEntry = pView->NextVisible(pEntry);
pViewDataNewCur = pEntry ? pView->GetViewDataEntry(pEntry) : nullptr;
}
SvTreeListEntry* pOldCursor = pCursor;
if( pCursor && pEntry != pCursor )
{
pView->SetEntryFocus( pCursor, false );
if( bSimpleTravel )
pView->Select( pCursor, false );
pView->HideFocus();
}
pCursor = pEntry;
if( pCursor )
{
if (pViewDataNewCur)
pViewDataNewCur->SetFocus( true );
if(!bForceNoSelect && bSimpleTravel && !(nFlags & LBoxFlags::DeselectAll) && GetUpdateMode())
{
pView->Select( pCursor );
CallEventListeners( VclEventId::ListboxTreeFocus, pCursor );
}
// multiple selection: select in cursor move if we're not in
// Add mode (Ctrl-F8)
else if( GetUpdateMode() &&
pView->GetSelectionMode() == SelectionMode::Multiple &&
!(nFlags & LBoxFlags::DeselectAll) && !aSelEng.IsAddMode() &&
!bForceNoSelect )
{
pView->Select( pCursor );
CallEventListeners( VclEventId::ListboxTreeFocus, pCursor );
}
else
{
ShowCursor( true );
if (bForceNoSelect && GetUpdateMode())
{
CallEventListeners( VclEventId::ListboxTreeFocus, pCursor);
}
}
if( pAnchor )
{
DBG_ASSERT(aSelEng.GetSelectionMode() != SelectionMode::Single,"Mode?");
SetAnchorSelection( pOldCursor, pCursor );
}
}
nFlags &= (~LBoxFlags::DeselectAll);
pView->OnCurrentEntryChanged();
}
void SvImpLBox::ShowCursor( bool bShow )
{
if( !bShow || !pCursor || !pView->HasFocus() )
{
vcl::Region aOldClip( pView->GetClipRegion());
vcl::Region aClipRegion( GetClipRegionRect() );
pView->SetClipRegion( aClipRegion );
pView->HideFocus();
pView->SetClipRegion( aOldClip );
}
else
{
long nY = GetEntryLine( pCursor );
tools::Rectangle aRect = pView->GetFocusRect( pCursor, nY );
CalcCellFocusRect( pCursor, aRect );
vcl::Region aOldClip( pView->GetClipRegion());
vcl::Region aClipRegion( GetClipRegionRect() );
pView->SetClipRegion( aClipRegion );
pView->ShowFocus( aRect );
pView->SetClipRegion( aOldClip );
}
}
void SvImpLBox::UpdateAll( bool bInvalidateCompleteView )
{
FindMostRight(nullptr);
aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
SyncVerThumb();
FillView();
ShowVerSBar();
if( bSimpleTravel && pCursor && pView->HasFocus() )
pView->Select( pCursor );
ShowCursor( true );
if( bInvalidateCompleteView )
pView->Invalidate();
else
pView->Invalidate( GetVisibleArea() );
}
IMPL_LINK( SvImpLBox, ScrollLeftRightHdl, ScrollBar *, pScrollBar, void )
{
long nDelta = pScrollBar->GetDelta();
if( nDelta )
{
if( pView->IsEditingActive() )
{
pView->EndEditing( true ); // Cancel
pView->Update();
}
pView->nFocusWidth = -1;
KeyLeftRight( nDelta );
}
}
void SvImpLBox::KeyLeftRight( long nDelta )
{
if( !(nFlags & LBoxFlags::InResize) )
pView->Update();
BeginScroll();
nFlags &= (~LBoxFlags::Filling);
ShowCursor( false );
// calculate new origin
long nPos = aHorSBar->GetThumbPos();
Point aOrigin( -nPos, 0 );
MapMode aMapMode( pView->GetMapMode() );
aMapMode.SetOrigin( aOrigin );
pView->SetMapMode( aMapMode );
if( !(nFlags & LBoxFlags::InResize) )
{
tools::Rectangle aRect( GetVisibleArea() );
pView->Scroll( -nDelta, 0, aRect, ScrollFlags::NoChildren );
}
else
pView->Invalidate();
RecalcFocusRect();
ShowCursor( true );
pView->NotifyScrolled();
}
// returns the last entry if position is just past the last entry
SvTreeListEntry* SvImpLBox::GetClickedEntry( const Point& rPoint ) const
{
DBG_ASSERT( pView->GetModel(), "SvImpLBox::GetClickedEntry: how can this ever happen? Please tell me (frank.schoenheit@sun.com) how to reproduce!" );
if ( !pView->GetModel() )
// this is quite impossible. Nevertheless, stack traces from the crash reporter
// suggest it isn't. Okay, make it safe, and wait for somebody to reproduce it
// reliably :-\ ....
// #122359# / 2005-05-23 / frank.schoenheit@sun.com
return nullptr;
if( pView->GetEntryCount() == 0 || !pStartEntry || !pView->GetEntryHeight())
return nullptr;
sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / pView->GetEntryHeight() );
sal_uInt16 nTemp = nClickedEntry;
SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
return pEntry;
}
// checks if the entry was hit "the right way"
// (Focusrect+ ContextBitmap at TreeListBox)
bool SvImpLBox::EntryReallyHit(SvTreeListEntry* pEntry, const Point& rPosPixel, long nLine)
{
bool bRet;
// we are not too exact when it comes to "special" entries
// (with CheckButtons etc.)
if( pEntry->ItemCount() >= 3 )
return true;
tools::Rectangle aRect( pView->GetFocusRect( pEntry, nLine ));
aRect.SetRight( GetOutputSize().Width() - pView->GetMapMode().GetOrigin().X() );
SvLBoxContextBmp* pBmp = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
aRect.AdjustLeft( -(pBmp->GetSize(pView,pEntry).Width()) );
aRect.AdjustLeft( -4 ); // a little tolerance
Point aPos( rPosPixel );
aPos -= pView->GetMapMode().GetOrigin();
bRet = aRect.IsInside( aPos );
return bRet;
}
// returns 0 if position is just past the last entry
SvTreeListEntry* SvImpLBox::GetEntry( const Point& rPoint ) const
{
if( (pView->GetEntryCount() == 0) || !pStartEntry ||
(rPoint.Y() > aOutputSize.Height())
|| !pView->GetEntryHeight())
return nullptr;
sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / pView->GetEntryHeight() );
sal_uInt16 nTemp = nClickedEntry;
SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
if( nTemp != nClickedEntry )
pEntry = nullptr;
return pEntry;
}
SvTreeListEntry* SvImpLBox::MakePointVisible(const Point& rPoint)
{
if( !pCursor )
return nullptr;
long nY = rPoint.Y();
SvTreeListEntry* pEntry = nullptr;
long nMax = aOutputSize.Height();
if( nY < 0 || nY >= nMax ) // aOutputSize.Height() )
{
if( nY < 0 )
pEntry = pView->PrevVisible(pCursor);
else
pEntry = pView->NextVisible(pCursor);
if( pEntry && pEntry != pCursor )
pView->SetEntryFocus( pCursor, false );
if( nY < 0 )
KeyUp( false );
else
KeyDown( false );
}
else
{
pEntry = GetClickedEntry( rPoint );
if( !pEntry )
{
sal_uInt16 nSteps = 0xFFFF;
// TODO: LastVisible is not yet implemented!
pEntry = pView->NextVisible(pStartEntry, nSteps);
}
if( pEntry )
{
if( pEntry != pCursor &&
aSelEng.GetSelectionMode() == SelectionMode::Single
)
pView->Select( pCursor, false );
}
}
return pEntry;
}
tools::Rectangle SvImpLBox::GetClipRegionRect() const
{
Point aOrigin( pView->GetMapMode().GetOrigin() );
aOrigin.setX( aOrigin.X() * -1 ); // conversion document coordinates
tools::Rectangle aClipRect( aOrigin, aOutputSize );
aClipRect.AdjustBottom( 1 );
return aClipRect;
}
void SvImpLBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
if (!pView->GetVisibleCount())
return;
nFlags |= LBoxFlags::InPaint;
if (nFlags & LBoxFlags::Filling)
{
SvTreeListEntry* pFirst = pView->First();
if (pFirst != pStartEntry)
{
ShowCursor(false);
pStartEntry = pView->First();
aVerSBar->SetThumbPos( 0 );
StopUserEvent();
ShowCursor(true);
nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
reinterpret_cast<void*>(1));
return;
}
}
if (!pStartEntry)
{
pStartEntry = pView->First();
}
if (nNodeBmpTabDistance == NODE_BMP_TABDIST_NOTVALID)
SetNodeBmpTabDistance();
long nRectHeight = rRect.GetHeight();
long nEntryHeight = pView->GetEntryHeight();
// calculate area for the entries we want to draw
sal_uInt16 nStartLine = static_cast<sal_uInt16>(rRect.Top() / nEntryHeight);
sal_uInt16 nCount = static_cast<sal_uInt16>(nRectHeight / nEntryHeight);
nCount += 2; // don't miss a row
long nY = nStartLine * nEntryHeight;
SvTreeListEntry* pEntry = pStartEntry;
while (nStartLine && pEntry)
{
pEntry = pView->NextVisible(pEntry);
nStartLine--;
}
vcl::Region aClipRegion(GetClipRegionRect());
// first draw the lines, then clip them!
rRenderContext.SetClipRegion();
if (m_nStyle & (WB_HASLINES | WB_HASLINESATROOT))
DrawNet(rRenderContext);
rRenderContext.SetClipRegion(aClipRegion);
if (!pCursor && !mbNoAutoCurEntry)
{
// do not select if multiselection or explicit set
bool bNotSelect = (aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
SetCursor(pStartEntry, bNotSelect);
}
for(sal_uInt16 n=0; n< nCount && pEntry; n++)
{
/*long nMaxRight=*/
pView->PaintEntry1(*pEntry, nY, rRenderContext );
nY += nEntryHeight;
pEntry = pView->NextVisible(pEntry);
}
nFlags &= (~LBoxFlags::DeselectAll);
rRenderContext.SetClipRegion();
nFlags &= (~LBoxFlags::InPaint);
}
void SvImpLBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
{
if( !pEntry )
return;
bool bInView = IsEntryInView( pEntry );
if( bInView && (!bMoveToTop || pStartEntry == pEntry) )
return; // is already visible
if( pStartEntry || mbForceMakeVisible )
nFlags &= (~LBoxFlags::Filling);
if( !bInView )
{
if( !pView->IsEntryVisible(pEntry) ) // Parent(s) collapsed?
{
SvTreeListEntry* pParent = pView->GetParent( pEntry );
while( pParent )
{
if( !pView->IsExpanded( pParent ) )
{
bool bRet = pView->Expand( pParent );
DBG_ASSERT(bRet,"Not expanded!");
}
pParent = pView->GetParent( pParent );
}
// do the parent's children fit into the view or do we have to scroll?
if( IsEntryInView( pEntry ) && !bMoveToTop )
return; // no need to scroll
}
}
pStartEntry = pEntry;
ShowCursor( false );
FillView();
aVerSBar->SetThumbPos( static_cast<long>(pView->GetVisiblePos( pStartEntry )) );
ShowCursor( true );
pView->Invalidate();
}
void SvImpLBox::ScrollToAbsPos( long nPos )
{
if( pView->GetVisibleCount() == 0 )
return;
long nLastEntryPos = pView->GetAbsPos( pView->Last() );
if( nPos < 0 )
nPos = 0;
else if( nPos > nLastEntryPos )
nPos = nLastEntryPos;
SvTreeListEntry* pEntry = pView->GetEntryAtAbsPos( nPos );
if( !pEntry || pEntry == pStartEntry )
return;
if( pStartEntry || mbForceMakeVisible )
nFlags &= (~LBoxFlags::Filling);
if( pView->IsEntryVisible(pEntry) )
{
pStartEntry = pEntry;
ShowCursor( false );
aVerSBar->SetThumbPos( nPos );
ShowCursor( true );
if (GetUpdateMode())
pView->Invalidate();
}
}
void SvImpLBox::DrawNet(vcl::RenderContext& rRenderContext)
{
if (pView->GetVisibleCount() < 2 && !pStartEntry->HasChildrenOnDemand() &&
!pStartEntry->HasChildren())
{
return;
}
// for platforms that don't have nets, DrawNativeControl does nothing and returns true
// so that SvImpLBox::DrawNet() doesn't draw anything either
if (rRenderContext.IsNativeControlSupported(ControlType::ListNet, ControlPart::Entire))
{
ImplControlValue aControlValue;
if (rRenderContext.DrawNativeControl(ControlType::ListNet, ControlPart::Entire,
tools::Rectangle(), ControlState::ENABLED, aControlValue, OUString()))
{
return;
}
}
long nEntryHeight = pView->GetEntryHeight();
long nEntryHeightDIV2 = nEntryHeight / 2;
if( nEntryHeightDIV2 && !(nEntryHeight & 0x0001))
nEntryHeightDIV2--;
SvTreeListEntry* pChild;
SvTreeListEntry* pEntry = pStartEntry;
SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
while (pTree->GetDepth( pEntry ) > 0)
{
pEntry = pView->GetParent(pEntry);
}
sal_uInt16 nOffs = static_cast<sal_uInt16>(pView->GetVisiblePos(pStartEntry) - pView->GetVisiblePos(pEntry));
long nY = 0;
nY -= (nOffs * nEntryHeight);
DBG_ASSERT(pFirstDynamicTab,"No Tree!");
rRenderContext.Push(PushFlags::LINECOLOR);
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
Color aCol = rStyleSettings.GetFaceColor();
if (aCol.IsRGBEqual(rRenderContext.GetBackground().GetColor()))
aCol = rStyleSettings.GetShadowColor();
rRenderContext.SetLineColor(aCol);
Point aPos1, aPos2;
sal_uInt16 nDistance;
sal_uLong nMax = nVisibleCount + nOffs + 1;
const Image& rExpandedNodeBitmap = GetExpandedNodeBmp();
for (sal_uLong n=0; n< nMax && pEntry; n++)
{
if (pView->IsExpanded(pEntry))
{
aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
// if it is not a context bitmap, go a little to the right below the
// first text (node bitmap, too)
if (!pView->nContextBmpWidthMax)
aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
aPos1.setY( nY );
aPos1.AdjustY(nEntryHeightDIV2 );
pChild = pView->FirstChild( pEntry );
assert(pChild && "Child?");
pChild = pChild->LastSibling();
nDistance = static_cast<sal_uInt16>(pView->GetVisiblePos(pChild) - pView->GetVisiblePos(pEntry));
aPos2 = aPos1;
aPos2.AdjustY(nDistance * nEntryHeight );
rRenderContext.DrawLine(aPos1, aPos2);
}
// visible in control?
if (n >= nOffs && ((m_nStyle & WB_HASLINESATROOT) || !pTree->IsAtRootDepth(pEntry)))
{
// can we recycle aPos1?
if (!pView->IsExpanded(pEntry))
{
// nope
aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
// if it is not a context bitmap, go a little to the right below
// the first text (node bitmap, too)
if (!pView->nContextBmpWidthMax)
aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
aPos1.setY( nY );
aPos1.AdjustY(nEntryHeightDIV2 );
aPos2.setX( aPos1.X() );
}
aPos2.setY( aPos1.Y() );
aPos2.AdjustX( -(pView->GetIndent()) );
rRenderContext.DrawLine(aPos1, aPos2);
}
nY += nEntryHeight;
pEntry = pView->NextVisible(pEntry);
}
if (m_nStyle & WB_HASLINESATROOT)
{
pEntry = pView->First();
aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
// if it is not a context bitmap, go a little to the right below the
// first text (node bitmap, too)
if (!pView->nContextBmpWidthMax)
aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
aPos1.AdjustX( -(pView->GetIndent()) );
aPos1.setY( GetEntryLine( pEntry ) );
aPos1.AdjustY(nEntryHeightDIV2 );
pChild = pEntry->LastSibling();
aPos2.setX( aPos1.X() );
aPos2.setY( GetEntryLine( pChild ) );
aPos2.AdjustY(nEntryHeightDIV2 );
rRenderContext.DrawLine(aPos1, aPos2);
}
rRenderContext.Pop();
}
void SvImpLBox::PositionScrollBars( Size& rSize, sal_uInt16 nMask )
{
long nOverlap = 0;
Size aVerSize( nVerSBarWidth, rSize.Height() );
Size aHorSize( rSize.Width(), nHorSBarHeight );
if( nMask & 0x0001 )
aHorSize.AdjustWidth( -(nVerSBarWidth) );
if( nMask & 0x0002 )
aVerSize.AdjustHeight( -(nHorSBarHeight) );
aVerSize.AdjustHeight(2 * nOverlap );
Point aVerPos( rSize.Width() - aVerSize.Width() + nOverlap, -nOverlap );
aVerSBar->SetPosSizePixel( aVerPos, aVerSize );
aHorSize.AdjustWidth(2 * nOverlap );
Point aHorPos( -nOverlap, rSize.Height() - aHorSize.Height() + nOverlap );
aHorSBar->SetPosSizePixel( aHorPos, aHorSize );
if( nMask & 0x0001 )
rSize.setWidth( aVerPos.X() );
if( nMask & 0x0002 )
rSize.setHeight( aHorPos.Y() );
if( (nMask & (0x0001|0x0002)) == (0x0001|0x0002) )
aScrBarBox->Show();
else
aScrBarBox->Hide();
}
void SvImpLBox::AdjustScrollBars( Size& rSize )
{
long nEntryHeight = pView->GetEntryHeight();
if( !nEntryHeight )
return;
sal_uInt16 nResult = 0;
Size aOSize( pView->Control::GetOutputSizePixel() );
const WinBits nWindowStyle = pView->GetStyle();
bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;
bool bHorBar = false;
long nMaxRight = aOSize.Width(); //GetOutputSize().Width();
Point aOrigin( pView->GetMapMode().GetOrigin() );
aOrigin.setX( aOrigin.X() * -1 );
nMaxRight += aOrigin.X() - 1;
long nVis = nMostRight - aOrigin.X();
if( (nWindowStyle & WB_HSCROLL) &&
(nVis < nMostRight || nMaxRight < nMostRight) )
{
bHorBar = true;
}
// number of entries that are not collapsed
sal_uLong nTotalCount = pView->GetVisibleCount();
// number of entries visible within the view
nVisibleCount = aOSize.Height() / nEntryHeight;
// do we need a vertical scrollbar?
if( bVerSBar || nTotalCount > nVisibleCount )
{
nResult = 1;
nMaxRight -= nVerSBarWidth;
if( !bHorBar )
{
if( (nWindowStyle & WB_HSCROLL) &&
(nVis < nMostRight || nMaxRight < nMostRight) )
bHorBar = true;
}
}
// do we need a horizontal scrollbar?
if( bHorBar )
{
nResult |= 0x0002;
// the number of entries visible within the view has to be recalculated
// because the horizontal scrollbar is now visible.
nVisibleCount = (aOSize.Height() - nHorSBarHeight) / nEntryHeight;
// we might actually need a vertical scrollbar now
if( !(nResult & 0x0001) &&
((nTotalCount > nVisibleCount) || bVerSBar) )
{
nResult = 3;
}
}
PositionScrollBars( aOSize, nResult );
// adapt Range, VisibleRange etc.
// refresh output size, in case we have to scroll
tools::Rectangle aRect;
aRect.SetSize( aOSize );
aSelEng.SetVisibleArea( aRect );
// vertical scrollbar
long nTemp = static_cast<long>(nVisibleCount);
nTemp--;
if( nTemp != aVerSBar->GetVisibleSize() )
{
if( !bInVScrollHdl )
{
aVerSBar->SetPageSize( nTemp - 1 );
aVerSBar->SetVisibleSize( nTemp );
}
else
{
nFlags |= LBoxFlags::EndScrollSetVisSize;
nNextVerVisSize = nTemp;
}
}
// horizontal scrollbar
nTemp = aHorSBar->GetThumbPos();
aHorSBar->SetVisibleSize( aOSize.Width() );
long nNewThumbPos = aHorSBar->GetThumbPos();
Range aRange( aHorSBar->GetRange() );
if( aRange.Max() < nMostRight+25 )
{
aRange.Max() = nMostRight+25;
aHorSBar->SetRange( aRange );
}
if( nTemp != nNewThumbPos )
{
nTemp = nNewThumbPos - nTemp;
if( pView->IsEditingActive() )
{
pView->EndEditing( true ); // Cancel
pView->Update();
}
pView->nFocusWidth = -1;
KeyLeftRight( nTemp );
}
if( nResult & 0x0001 )
aVerSBar->Show();
else
aVerSBar->Hide();
if( nResult & 0x0002 )
aHorSBar->Show();
else
{
aHorSBar->Hide();
}
rSize = aOSize;
}
void SvImpLBox::InitScrollBarBox()
{
aScrBarBox->SetSizePixel( Size(nVerSBarWidth, nHorSBarHeight) );
Size aSize( pView->Control::GetOutputSizePixel() );
aScrBarBox->SetPosPixel( Point(aSize.Width()-nVerSBarWidth, aSize.Height()-nHorSBarHeight));
}
void SvImpLBox::Resize()
{
aOutputSize = pView->Control::GetOutputSizePixel();
if( aOutputSize.Width() <= 0 || aOutputSize.Height() <= 0 )
return;
nFlags |= LBoxFlags::InResize;
InitScrollBarBox();
if( pView->GetEntryHeight())
{
AdjustScrollBars( aOutputSize );
UpdateAll(false);
}
// HACK, as in floating and docked windows the scrollbars might not be drawn
// correctly/not be drawn at all after resizing!
if( aHorSBar->IsVisible())
aHorSBar->Invalidate();
if( aVerSBar->IsVisible())
aVerSBar->Invalidate();
nFlags &= ~LBoxFlags::InResize;
}
void SvImpLBox::FillView()
{
if( !pStartEntry )
{
sal_uLong nVisibleViewCount = pView->GetVisibleCount();
long nTempThumb = aVerSBar->GetThumbPos();
if( nTempThumb < 0 )
nTempThumb = 0;
else if( static_cast<unsigned long>(nTempThumb) >= nVisibleViewCount )
nTempThumb = nVisibleViewCount == 0 ? 0 : nVisibleViewCount - 1;
pStartEntry = pView->GetEntryAtVisPos(nTempThumb);
}
if( !pStartEntry )
return;
sal_uInt16 nLast = static_cast<sal_uInt16>(pView->GetVisiblePos(pView->LastVisible()));
sal_uInt16 nThumb = static_cast<sal_uInt16>(pView->GetVisiblePos( pStartEntry ));
sal_uLong nCurDispEntries = nLast-nThumb+1;
if( nCurDispEntries >= nVisibleCount )
return;
ShowCursor( false );
// fill window by moving the thumb up incrementally
bool bFound = false;
SvTreeListEntry* pTemp = pStartEntry;
while( nCurDispEntries < nVisibleCount && pTemp )
{
pTemp = pView->PrevVisible(pStartEntry);
if( pTemp )
{
nThumb--;
pStartEntry = pTemp;
nCurDispEntries++;
bFound = true;
}
}
if( bFound )
{
aVerSBar->SetThumbPos( nThumb );
ShowCursor( true ); // recalculate focus rectangle
pView->Invalidate();
}
}
void SvImpLBox::ShowVerSBar()
{
bool bVerBar = ( pView->GetStyle() & WB_VSCROLL ) != 0;
sal_uLong nVis = 0;
if( !bVerBar )
nVis = pView->GetVisibleCount();
if( bVerBar || (nVisibleCount && nVis > static_cast<sal_uLong>(nVisibleCount-1)) )
{
if( !aVerSBar->IsVisible() )
{
pView->nFocusWidth = -1;
AdjustScrollBars( aOutputSize );
if( GetUpdateMode() )
aVerSBar->Update();
}
}
else
{
if( aVerSBar->IsVisible() )
{
pView->nFocusWidth = -1;
AdjustScrollBars( aOutputSize );
}
}
long nMaxRight = GetOutputSize().Width();
Point aPos( pView->GetMapMode().GetOrigin() );
aPos.setX( aPos.X() * -1 ); // convert document coordinates
nMaxRight = nMaxRight + aPos.X() - 1;
if( nMaxRight < nMostRight )
{
if( !aHorSBar->IsVisible() )
{
pView->nFocusWidth = -1;
AdjustScrollBars( aOutputSize );
if( GetUpdateMode() )
aHorSBar->Update();
}
else
{
Range aRange( aHorSBar->GetRange() );
if( aRange.Max() < nMostRight+25 )
{
aRange.Max() = nMostRight+25;
aHorSBar->SetRange( aRange );
}
else
{
pView->nFocusWidth = -1;
AdjustScrollBars( aOutputSize );
}
}
}
else
{
if( aHorSBar->IsVisible() )
{
pView->nFocusWidth = -1;
AdjustScrollBars( aOutputSize );
}
}
}
void SvImpLBox::SyncVerThumb()
{
if( pStartEntry )
{
long nEntryPos = pView->GetVisiblePos( pStartEntry );
aVerSBar->SetThumbPos( nEntryPos );
}
else
aVerSBar->SetThumbPos( 0 );
}
bool SvImpLBox::IsEntryInView( SvTreeListEntry* pEntry ) const
{
// parent collapsed
if( !pView->IsEntryVisible(pEntry) )
return false;
long nY = GetEntryLine( pEntry );
if( nY < 0 )
return false;
long nMax = nVisibleCount * pView->GetEntryHeight();
return nY < nMax;
}
long SvImpLBox::GetEntryLine( SvTreeListEntry* pEntry ) const
{
if(!pStartEntry )
return -1; // invisible position
long nFirstVisPos = pView->GetVisiblePos( pStartEntry );
long nEntryVisPos = pView->GetVisiblePos( pEntry );
nFirstVisPos = nEntryVisPos - nFirstVisPos;
nFirstVisPos *= pView->GetEntryHeight();
return nFirstVisPos;
}
void SvImpLBox::SetEntryHeight()
{
SetNodeBmpWidth( GetExpandedNodeBmp() );
SetNodeBmpWidth( GetCollapsedNodeBmp() );
if(!pView->HasViewData()) // are we within the Clear?
{
Size aSize = pView->Control::GetOutputSizePixel();
AdjustScrollBars( aSize );
}
else
{
Resize();
if( GetUpdateMode() )
pView->Invalidate();
}
}
// ***********************************************************************
// Callback Functions
// ***********************************************************************
void SvImpLBox::EntryExpanded( SvTreeListEntry* pEntry )
{
// SelAllDestrAnch( false, true ); //DeselectAll();
if( !GetUpdateMode() )
return;
ShowCursor( false );
long nY = GetEntryLine( pEntry );
if( IsLineVisible(nY) )
{
InvalidateEntriesFrom( nY );
FindMostRight( pEntry, nullptr );
}
aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
// if we expanded before the thumb, the thumb's position has to be
// corrected
SyncVerThumb();
ShowVerSBar();
ShowCursor( true );
}
void SvImpLBox::EntryCollapsed( SvTreeListEntry* pEntry )
{
if( !pView->IsEntryVisible( pEntry ) )
return;
ShowCursor( false );
if( !pMostRightEntry || pTree->IsChild( pEntry,pMostRightEntry ) )
{
FindMostRight(nullptr);
}
if( pStartEntry )
{
long nOldThumbPos = aVerSBar->GetThumbPos();
sal_uLong nVisList = pView->GetVisibleCount();
aVerSBar->SetRange( Range(0, nVisList-1) );
long nNewThumbPos = aVerSBar->GetThumbPos();
if( nNewThumbPos != nOldThumbPos )
{
pStartEntry = pView->First();
sal_uInt16 nDistance = static_cast<sal_uInt16>(nNewThumbPos);
if( nDistance )
pStartEntry = pView->NextVisible(pStartEntry, nDistance);
if( GetUpdateMode() )
pView->Invalidate();
}
else
SyncVerThumb();
ShowVerSBar();
}
// has the cursor been collapsed?
if( pTree->IsChild( pEntry, pCursor ) )
SetCursor( pEntry );
if( GetUpdateMode() )
ShowVerSBar();
ShowCursor( true );
if( GetUpdateMode() && pCursor )
pView->Select( pCursor );
}
void SvImpLBox::CollapsingEntry( SvTreeListEntry* pEntry )
{
if( !pView->IsEntryVisible( pEntry ) || !pStartEntry )
return;
SelAllDestrAnch( false ); // deselect all
// is the collapsed cursor visible?
long nY = GetEntryLine( pEntry );
if( IsLineVisible(nY) )
{
if( GetUpdateMode() )
InvalidateEntriesFrom( nY );
}
else
{
if( pTree->IsChild(pEntry, pStartEntry) )
{
pStartEntry = pEntry;
if( GetUpdateMode() )
pView->Invalidate();
}
}
}
void SvImpLBox::SetNodeBmpWidth( const Image& rBmp )
{
const Size aSize( rBmp.GetSizePixel() );
nNodeBmpWidth = aSize.Width();
}
void SvImpLBox::SetNodeBmpTabDistance()
{
nNodeBmpTabDistance = -pView->GetIndent();
if( pView->nContextBmpWidthMax )
{
// only if the first dynamic tab is centered (we currently assume that)
Size aSize = GetExpandedNodeBmp().GetSizePixel();
nNodeBmpTabDistance -= aSize.Width() / 2;
}
}
// corrects the cursor when using SingleSelection
void SvImpLBox::EntrySelected( SvTreeListEntry* pEntry, bool bSelect )
{
if( nFlags & LBoxFlags::IgnoreSelect )
return;
nFlags &= (~LBoxFlags::DeselectAll);
if( bSelect &&
aSelEng.GetSelectionMode() == SelectionMode::Single &&
pEntry != pCursor )
{
SetCursor( pEntry );
DBG_ASSERT(pView->GetSelectionCount()==1,"selection count?");
}
if( GetUpdateMode() && pView->IsEntryVisible(pEntry) )
{
long nY = GetEntryLine( pEntry );
if( IsLineVisible( nY ) )
{
ShowCursor(false);
InvalidateEntry(pEntry);
ShowCursor(true);
}
}
}
void SvImpLBox::RemovingEntry( SvTreeListEntry* pEntry )
{
CallEventListeners( VclEventId::ListboxItemRemoved , pEntry );
DestroyAnchor();
if( !pView->IsEntryVisible( pEntry ) )
{
// if parent is collapsed => bye!
nFlags |= LBoxFlags::RemovedEntryInvisible;
return;
}
if( pEntry == pMostRightEntry || (
pEntry->HasChildren() && pView->IsExpanded(pEntry) &&
pTree->IsChild(pEntry, pMostRightEntry)))
{
nFlags |= LBoxFlags::RemovedRecalcMostRight;
}
SvTreeListEntry* pOldStartEntry = pStartEntry;
SvTreeListEntry* pParent = pView->GetModel()->GetParent(pEntry);
if (pParent && pView->GetModel()->GetChildList(pParent).size() == 1)
{
DBG_ASSERT( pView->IsExpanded( pParent ), "Parent not expanded");
pParent->SetFlags( pParent->GetFlags() | SvTLEntryFlags::NO_NODEBMP);
InvalidateEntry( pParent );
}
if( pCursor && pTree->IsChild( pEntry, pCursor) )
pCursor = pEntry;
if( pStartEntry && pTree->IsChild(pEntry,pStartEntry) )
pStartEntry = pEntry;
SvTreeListEntry* pTemp;
if( pCursor && pCursor == pEntry )
{
if( bSimpleTravel )
pView->Select( pCursor, false );
ShowCursor( false ); // focus rectangle gone
// NextSibling, because we also delete the children of the cursor
pTemp = pCursor->NextSibling();
if( !pTemp )
pTemp = pView->PrevVisible(pCursor);
SetCursor( pTemp, true );
}
if( pStartEntry && pStartEntry == pEntry )
{
pTemp = pStartEntry->NextSibling();
if( !pTemp )
pTemp = pView->PrevVisible(pStartEntry);
pStartEntry = pTemp;
}
if( GetUpdateMode())
{
// if it is the last one, we have to invalidate it, so the lines are
// drawn correctly (in this case they're deleted)
if( pStartEntry && (pStartEntry != pOldStartEntry || pEntry == pView->GetModel()->Last()) )
{
aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry ));
pView->Invalidate( GetVisibleArea() );
}
else
InvalidateEntriesFrom( GetEntryLine( pEntry ) );
}
}
void SvImpLBox::EntryRemoved()
{
if( nFlags & LBoxFlags::RemovedEntryInvisible )
{
nFlags &= (~LBoxFlags::RemovedEntryInvisible);
return;
}
if( !pStartEntry )
pStartEntry = pTree->First();
if( !pCursor )
SetCursor( pStartEntry, true );
if( pCursor && (bSimpleTravel || !pView->GetSelectionCount() ))
pView->Select( pCursor );
if( GetUpdateMode())
{
if( nFlags & LBoxFlags::RemovedRecalcMostRight )
FindMostRight(nullptr);
aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
FillView();
if( pStartEntry )
// if something above the thumb was deleted
aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry) );
ShowVerSBar();
if( pCursor && pView->HasFocus() && !pView->IsSelected(pCursor) )
{
if( pView->GetSelectionCount() )
{
// is a neighboring entry selected?
SvTreeListEntry* pNextCursor = pView->PrevVisible( pCursor );
if( !pNextCursor || !pView->IsSelected( pNextCursor ))
pNextCursor = pView->NextVisible( pCursor );
if( !pNextCursor || !pView->IsSelected( pNextCursor ))
// no neighbor selected: use first selected
pNextCursor = pView->FirstSelected();
SetCursor( pNextCursor );
MakeVisible( pCursor );
}
else
pView->Select( pCursor );
}
ShowCursor( true );
}
nFlags &= (~LBoxFlags::RemovedRecalcMostRight);
}
void SvImpLBox::MovingEntry( SvTreeListEntry* pEntry )
{
bool bDeselAll(nFlags & LBoxFlags::DeselectAll);
SelAllDestrAnch( false ); // DeselectAll();
if( !bDeselAll )
nFlags &= (~LBoxFlags::DeselectAll);
if( pEntry == pCursor )
ShowCursor( false );
if( IsEntryInView( pEntry ) )
pView->Invalidate();
if( pEntry != pStartEntry )
return;
SvTreeListEntry* pNew = nullptr;
if( !pEntry->HasChildren() )
{
pNew = pView->NextVisible(pStartEntry);
if( !pNew )
pNew = pView->PrevVisible(pStartEntry);
}
else
{
pNew = pEntry->NextSibling();
if( !pNew )
pNew = pEntry->PrevSibling();
}
pStartEntry = pNew;
}
void SvImpLBox::EntryMoved( SvTreeListEntry* pEntry )
{
UpdateContextBmpWidthVectorFromMovedEntry( pEntry );
if ( !pStartEntry )
// this might happen if the only entry in the view is moved to its very same position
// #i97346#
pStartEntry = pView->First();
aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
sal_uInt16 nFirstPos = static_cast<sal_uInt16>(pTree->GetAbsPos( pStartEntry ));
sal_uInt16 nNewPos = static_cast<sal_uInt16>(pTree->GetAbsPos( pEntry ));
FindMostRight(nullptr);
if( nNewPos < nFirstPos ) // HACK!
pStartEntry = pEntry;
SyncVerThumb();
if( pEntry == pCursor )
{
if( pView->IsEntryVisible( pCursor ) )
ShowCursor( true );
else
{
SvTreeListEntry* pParent = pEntry;
do {
pParent = pTree->GetParent( pParent );
}
while( !pView->IsEntryVisible( pParent ) );
SetCursor( pParent );
}
}
if( IsEntryInView( pEntry ) )
pView->Invalidate();
}
void SvImpLBox::EntryInserted( SvTreeListEntry* pEntry )
{
if( !GetUpdateMode() )
return;
SvTreeListEntry* pParent = pTree->GetParent(pEntry);
if (pParent && pTree->GetChildList(pParent).size() == 1)
// draw plus sign
pTree->InvalidateEntry( pParent );
if( !pView->IsEntryVisible( pEntry ) )
return;
bool bDeselAll(nFlags & LBoxFlags::DeselectAll);
if( bDeselAll )
SelAllDestrAnch( false );
else
DestroyAnchor();
// nFlags &= (~LBoxFlags::DeselectAll);
// ShowCursor( false ); // if cursor is moved lower
long nY = GetEntryLine( pEntry );
bool bEntryVisible = IsLineVisible( nY );
if( bEntryVisible )
{
ShowCursor( false ); // if cursor is moved lower
nY -= pView->GetEntryHeight(); // because of lines
InvalidateEntriesFrom( nY );
}
else if( pStartEntry && nY < GetEntryLine(pStartEntry) )
{
// Check if the view is filled completely. If not, then adjust
// pStartEntry and the Cursor (automatic scrolling).
sal_uInt16 nLast = static_cast<sal_uInt16>(pView->GetVisiblePos(pView->LastVisible()));
sal_uInt16 nThumb = static_cast<sal_uInt16>(pView->GetVisiblePos( pStartEntry ));
sal_uInt16 nCurDispEntries = nLast-nThumb+1;
if( nCurDispEntries < nVisibleCount )
{
// set at the next paint event
pStartEntry = nullptr;
SetCursor( nullptr );
pView->Invalidate();
}
}
else if( !pStartEntry )
pView->Invalidate();
SetMostRight( pEntry );
aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
SyncVerThumb(); // if something was inserted before the thumb
ShowVerSBar();
ShowCursor( true );
if( pStartEntry != pView->First() && (nFlags & LBoxFlags::Filling) )
pView->Update();
}
// ********************************************************************
// Event handler
// ********************************************************************
// ****** Control the control animation
bool SvImpLBox::ButtonDownCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry* pEntry)
{
SvLBoxItem* pItem = pView->GetItem(pEntry,rMEvt.GetPosPixel().X(),&pActiveTab);
if (pItem && pItem->GetType() == SvLBoxItemType::Button)
{
pActiveButton = static_cast<SvLBoxButton*>(pItem);
pActiveEntry = pEntry;
if( pCursor == pActiveEntry )
pView->HideFocus();
pView->CaptureMouse();
pActiveButton->SetStateHilighted( true );
InvalidateEntry(pActiveEntry);
return true;
}
else
pActiveButton = nullptr;
return false;
}
bool SvImpLBox::MouseMoveCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry const * pEntry)
{
if( pActiveButton )
{
long nMouseX = rMEvt.GetPosPixel().X();
if( pEntry == pActiveEntry &&
pView->GetItem(pActiveEntry, nMouseX) == pActiveButton )
{
if( !pActiveButton->IsStateHilighted() )
{
pActiveButton->SetStateHilighted(true );
InvalidateEntry(pActiveEntry);
}
}
else
{
if( pActiveButton->IsStateHilighted() )
{
pActiveButton->SetStateHilighted(false );
InvalidateEntry(pActiveEntry);
}
}
return true;
}
return false;
}
bool SvImpLBox::ButtonUpCheckCtrl( const MouseEvent& rMEvt )
{
if( pActiveButton )
{
pView->ReleaseMouse();
SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
pActiveButton->SetStateHilighted( false );
long nMouseX = rMEvt.GetPosPixel().X();
if (pEntry == pActiveEntry && pView->GetItem(pActiveEntry, nMouseX) == pActiveButton)
pActiveButton->ClickHdl(pActiveEntry);
InvalidateEntry(pActiveEntry);
if (pCursor == pActiveEntry)
ShowCursor(true);
pActiveButton = nullptr;
pActiveEntry = nullptr;
pActiveTab = nullptr;
return true;
}
return false;
}
// ******* Control plus/minus button for expanding/collapsing
// false == no expand/collapse button hit
bool SvImpLBox::IsNodeButton( const Point& rPosPixel, SvTreeListEntry* pEntry ) const
{
if( !pEntry->HasChildren() && !pEntry->HasChildrenOnDemand() )
return false;
SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
if( !pFirstDynamicTab )
return false;
long nMouseX = rPosPixel.X();
// convert to document coordinates
Point aOrigin( pView->GetMapMode().GetOrigin() );
nMouseX -= aOrigin.X();
long nX = pView->GetTabPos( pEntry, pFirstDynamicTab);
nX += nNodeBmpTabDistance;
if( nMouseX < nX )
return false;
nX += nNodeBmpWidth;
return nMouseX <= nX;
}
// false == hit no node button
bool SvImpLBox::ButtonDownCheckExpand( const MouseEvent& rMEvt, SvTreeListEntry* pEntry )
{
bool bRet = false;
if ( pView->IsEditingActive() && pEntry == pView->pEdEntry )
// inplace editing -> nothing to do
bRet = true;
else if ( IsNodeButton( rMEvt.GetPosPixel(), pEntry ) )
{
if ( pView->IsExpanded( pEntry ) )
{
pView->EndEditing( true );
pView->Collapse( pEntry );
}
else
{
// you can expand an entry, which is in editing
pView->Expand( pEntry );
}
bRet = true;
}
return bRet;
}
void SvImpLBox::MouseButtonDown( const MouseEvent& rMEvt )
{
if ( !rMEvt.IsLeft() && !rMEvt.IsRight())
return;
aEditIdle.Stop();
Point aPos( rMEvt.GetPosPixel());
if( aPos.X() > aOutputSize.Width() || aPos.Y() > aOutputSize.Height() )
return;
SvTreeListEntry* pEntry = GetEntry( aPos );
if ( pEntry != pCursor )
// new entry selected -> reset current tab position to first tab
nCurTabPos = FIRST_ENTRY_TAB;
nFlags &= (~LBoxFlags::Filling);
pView->GrabFocus();
//fdo#82270 Grabbing focus can invalidate the entries, re-fetch
pEntry = GetEntry(aPos);
// the entry can still be invalid!
if( !pEntry || !pView->GetViewData( pEntry ))
return;
long nY = GetEntryLine( pEntry );
// Node-Button?
if( ButtonDownCheckExpand( rMEvt, pEntry ) )
return;
if( !EntryReallyHit(pEntry,aPos,nY))
return;
SvLBoxItem* pXItem = pView->GetItem( pEntry, aPos.X() );
if( pXItem )
{
SvLBoxTab* pXTab = pView->GetTab( pEntry, pXItem );
if ( !rMEvt.IsMod1() && !rMEvt.IsMod2() && rMEvt.IsLeft() && pXTab->IsEditable()
&& pEntry == pView->FirstSelected() && nullptr == pView->NextSelected( pEntry ) )
// #i8234# FirstSelected() and NextSelected() ensures, that inplace editing is only triggered, when only one entry is selected
nFlags |= LBoxFlags::StartEditTimer;
if ( !pView->IsSelected( pEntry ) )
nFlags &= ~LBoxFlags::StartEditTimer;
}
if( (rMEvt.GetClicks() % 2) == 0 )
{
nFlags &= (~LBoxFlags::StartEditTimer);
pView->pHdlEntry = pEntry;
if( !pView->DoubleClickHdl() )
{
// Handler signals nothing to be done anymore, bail out, 'this' may
// even be dead and destroyed.
return;
}
else
{
// if the entry was deleted within the handler
pEntry = GetClickedEntry( aPos );
if( !pEntry )
return;
if( pEntry != pView->pHdlEntry )
{
// select anew & bye
if( !bSimpleTravel && !aSelEng.IsAlwaysAdding())
SelAllDestrAnch( false ); // DeselectAll();
SetCursor( pEntry );
return;
}
if( pEntry->HasChildren() || pEntry->HasChildrenOnDemand() )
{
if( bSubLstOpDblClick )
{
if( pView->IsExpanded(pEntry) )
pView->Collapse( pEntry );
else
pView->Expand( pEntry );
}
if( pEntry == pCursor ) // only if Entryitem was clicked
// (Nodebutton is not an Entryitem!)
pView->Select( pCursor );
return;
}
}
}
else
{
// CheckButton? (TreeListBox: Check + Info)
if( ButtonDownCheckCtrl(rMEvt, pEntry) )
return;
// Inplace-Editing?
}
if ( aSelEng.GetSelectionMode() != SelectionMode::NONE )
aSelEng.SelMouseButtonDown( rMEvt );
}
void SvImpLBox::MouseButtonUp( const MouseEvent& rMEvt)
{
if ( !ButtonUpCheckCtrl( rMEvt ) && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
aSelEng.SelMouseButtonUp( rMEvt );
EndScroll();
if( nFlags & LBoxFlags::StartEditTimer )
{
nFlags &= (~LBoxFlags::StartEditTimer);
aEditClickPos = rMEvt.GetPosPixel();
aEditIdle.Start();
}
}
void SvImpLBox::MouseMove( const MouseEvent& rMEvt)
{
SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
if ( !MouseMoveCheckCtrl( rMEvt, pEntry ) && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
aSelEng.SelMouseMove( rMEvt );
}
bool SvImpLBox::KeyInput( const KeyEvent& rKEvt)
{
aEditIdle.Stop();
const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
if( rKeyCode.IsMod2() )
return false; // don't evaluate Alt key
nFlags &= (~LBoxFlags::Filling);
if( !pCursor )
pCursor = pStartEntry;
if( !pCursor )
return false;
bool bKeyUsed = true;
sal_uInt16 nDelta = static_cast<sal_uInt16>(aVerSBar->GetPageSize());
sal_uInt16 aCode = rKeyCode.GetCode();
bool bShift = rKeyCode.IsShift();
bool bMod1 = rKeyCode.IsMod1();
SvTreeListEntry* pNewCursor;
const WinBits nWindowStyle = pView->GetStyle();
switch( aCode )
{
case KEY_UP:
if( !IsEntryInView( pCursor ) )
MakeVisible( pCursor );
pNewCursor = pCursor;
do
{
pNewCursor = pView->PrevVisible(pNewCursor);
} while( pNewCursor && !IsSelectable(pNewCursor) );
if ( pNewCursor )
// new entry selected -> reset current tab position to first tab
nCurTabPos = FIRST_ENTRY_TAB;
// if there is no next entry, take the current one
// this ensures that in case of _one_ entry in the list, this entry is selected when pressing
// the cursor key
if (!pNewCursor)
pNewCursor = pCursor;
aSelEng.CursorPosChanging( bShift, bMod1 );
SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
if( !IsEntryInView( pNewCursor ) )
KeyUp( false );
break;
case KEY_DOWN:
if( !IsEntryInView( pCursor ) )
MakeVisible( pCursor );
pNewCursor = pCursor;
do
{
pNewCursor = pView->NextVisible(pNewCursor);
} while( pNewCursor && !IsSelectable(pNewCursor) );
if ( pNewCursor )
// new entry selected -> reset current tab position to first tab
nCurTabPos = FIRST_ENTRY_TAB;
// if there is no next entry, take the current one
// this ensures that in case of _one_ entry in the list, this entry is selected when pressing
// the cursor key
// 06.09.20001 - 83416 - frank.schoenheit@sun.com
if ( !pNewCursor && pCursor )
pNewCursor = pCursor;
if( pNewCursor )
{
aSelEng.CursorPosChanging( bShift, bMod1 );
if( IsEntryInView( pNewCursor ) )
SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
else
{
if( pCursor )
pView->Select( pCursor, false );
KeyDown( false );
SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
}
}
else
KeyDown( false ); // because scrollbar range might still
// allow scrolling
break;
case KEY_RIGHT:
{
if( bSubLstOpLR )
{
// only try to expand if sublist is expandable,
// otherwise ignore the key press
if( IsExpandable() && !pView->IsExpanded( pCursor ) )
pView->Expand( pCursor );
}
else if ( bIsCellFocusEnabled && pCursor )
{
if ( nCurTabPos < ( pView->TabCount() - 1 /*!2*/ ) )
{
++nCurTabPos;
ShowCursor( true );
CallEventListeners( VclEventId::ListboxSelect, pCursor );
}
}
else if( nWindowStyle & WB_HSCROLL )
{
long nThumb = aHorSBar->GetThumbPos();
nThumb += aHorSBar->GetLineSize();
long nOldThumb = aHorSBar->GetThumbPos();
aHorSBar->SetThumbPos( nThumb );
nThumb = nOldThumb;
nThumb -= aHorSBar->GetThumbPos();
nThumb *= -1;
if( nThumb )
{
KeyLeftRight( nThumb );
EndScroll();
}
}
else
bKeyUsed = false;
break;
}
case KEY_LEFT:
{
if ( bIsCellFocusEnabled && pCursor )
{
if ( nCurTabPos > FIRST_ENTRY_TAB )
{
--nCurTabPos;
ShowCursor( true );
CallEventListeners( VclEventId::ListboxSelect, pCursor );
}
}
else if ( nWindowStyle & WB_HSCROLL )
{
long nThumb = aHorSBar->GetThumbPos();
nThumb -= aHorSBar->GetLineSize();
long nOldThumb = aHorSBar->GetThumbPos();
aHorSBar->SetThumbPos( nThumb );
nThumb = nOldThumb;
nThumb -= aHorSBar->GetThumbPos();
if( nThumb )
{
KeyLeftRight( -nThumb );
EndScroll();
}
else if( bSubLstOpLR )
{
if( IsExpandable() && pView->IsExpanded( pCursor ) )
pView->Collapse( pCursor );
else
{
pNewCursor = pView->GetParent( pCursor );
if( pNewCursor )
SetCursor( pNewCursor );
}
}
}
else if( bSubLstOpLR )
{
if( IsExpandable() && pView->IsExpanded( pCursor ) )
pView->Collapse( pCursor );
else
{
pNewCursor = pView->GetParent( pCursor );
if( pNewCursor )
SetCursor( pNewCursor );
}
}
else
bKeyUsed = false;
break;
}
case KEY_PAGEUP:
if( !bMod1 )
{
pNewCursor = pView->PrevVisible(pCursor, nDelta);
while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
{
pNewCursor = pView->NextVisible(pNewCursor);
nDelta--;
}
if( nDelta )
{
DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
aSelEng.CursorPosChanging( bShift, bMod1 );
if( IsEntryInView( pNewCursor ) )
SetCursor( pNewCursor );
else
{
SetCursor( pNewCursor );
KeyUp( true );
}
}
}
else
bKeyUsed = false;
break;
case KEY_PAGEDOWN:
if( !bMod1 )
{
pNewCursor= pView->NextVisible(pCursor, nDelta);
while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
{
pNewCursor = pView->PrevVisible(pNewCursor);
nDelta--;
}
if( nDelta && pNewCursor )
{
DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
aSelEng.CursorPosChanging( bShift, bMod1 );
if( IsEntryInView( pNewCursor ) )
SetCursor( pNewCursor );
else
{
SetCursor( pNewCursor );
KeyDown( true );
}
}
else
KeyDown( false ); // see also: KEY_DOWN
}
else
bKeyUsed = false;
break;
case KEY_SPACE:
if ( pView->GetSelectionMode() != SelectionMode::NONE )
{
if ( bMod1 )
{
if ( pView->GetSelectionMode() == SelectionMode::Multiple && !bShift )
// toggle selection
pView->Select( pCursor, !pView->IsSelected( pCursor ) );
}
else if ( !bShift /*&& !bMod1*/ )
{
if ( aSelEng.IsAddMode() )
{
// toggle selection
pView->Select( pCursor, !pView->IsSelected( pCursor ) );
}
else if ( !pView->IsSelected( pCursor ) )
{
SelAllDestrAnch( false );
pView->Select( pCursor );
}
else
bKeyUsed = false;
}
else
bKeyUsed = false;
}
else
bKeyUsed = false;
break;
case KEY_RETURN:
if( bSubLstOpRet && IsExpandable() )
{
if( pView->IsExpanded( pCursor ) )
pView->Collapse( pCursor );
else
pView->Expand( pCursor );
}
else
bKeyUsed = false;
break;
case KEY_F2:
if( !bShift && !bMod1 )
{
aEditClickPos = Point( -1, -1 );
EditTimerCall( nullptr );
}
else
bKeyUsed = false;
break;
case KEY_F8:
if( bShift && pView->GetSelectionMode()==SelectionMode::Multiple &&
!(m_nStyle & WB_SIMPLEMODE))
{
if( aSelEng.IsAlwaysAdding() )
aSelEng.AddAlways( false );
else
aSelEng.AddAlways( true );
}
else
bKeyUsed = false;
break;
case KEY_ADD:
if( pCursor )
{
if( !pView->IsExpanded(pCursor))
pView->Expand( pCursor );
if( bMod1 )
{
sal_uInt16 nRefDepth = pTree->GetDepth( pCursor );
SvTreeListEntry* pCur = pTree->Next( pCursor );
while( pCur && pTree->GetDepth(pCur) > nRefDepth )
{
if( pCur->HasChildren() && !pView->IsExpanded(pCur))
pView->Expand( pCur );
pCur = pTree->Next( pCur );
}
}
}
else
bKeyUsed = false;
break;
case KEY_A:
if( bMod1 )
SelAllDestrAnch( true );
else
bKeyUsed = false;
break;
case KEY_SUBTRACT:
if( pCursor )
{
if( pView->IsExpanded(pCursor))
pView->Collapse( pCursor );
if( bMod1 )
{
// collapse all parents until we get to the root
SvTreeListEntry* pParentToCollapse = pTree->GetRootLevelParent(pCursor);
if( pParentToCollapse )
{
sal_uInt16 nRefDepth;
// special case explorer: if the root only has a single
// entry, don't collapse the root entry
if (pTree->GetChildList(nullptr).size() < 2)
{
nRefDepth = 1;
pParentToCollapse = pCursor;
while( pTree->GetParent(pParentToCollapse) &&
pTree->GetDepth( pTree->GetParent(pParentToCollapse)) > 0)
{
pParentToCollapse = pTree->GetParent(pParentToCollapse);
}
}
else
nRefDepth = 0;
if( pView->IsExpanded(pParentToCollapse) )
pView->Collapse( pParentToCollapse );
SvTreeListEntry* pCur = pTree->Next( pParentToCollapse );
while( pCur && pTree->GetDepth(pCur) > nRefDepth )
{
if( pCur->HasChildren() && pView->IsExpanded(pCur) )
pView->Collapse( pCur );
pCur = pTree->Next( pCur );
}
}
}
}
else
bKeyUsed = false;
break;
case KEY_DIVIDE :
if( bMod1 )
SelAllDestrAnch( true );
else
bKeyUsed = false;
break;
case KEY_COMMA :
if( bMod1 )
SelAllDestrAnch( false );
else
bKeyUsed = false;
break;
case KEY_HOME :
pNewCursor = pView->GetModel()->First();
while( pNewCursor && !IsSelectable(pNewCursor) )
{
pNewCursor = pView->NextVisible(pNewCursor);
}
if( pNewCursor && pNewCursor != pCursor )
{
// SelAllDestrAnch( false );
aSelEng.CursorPosChanging( bShift, bMod1 );
SetCursor( pNewCursor );
if( !IsEntryInView( pNewCursor ) )
MakeVisible( pNewCursor );
}
else
bKeyUsed = false;
break;
case KEY_END :
pNewCursor = pView->GetModel()->Last();
while( pNewCursor && !IsSelectable(pNewCursor) )
{
pNewCursor = pView->PrevVisible(pNewCursor);
}
if( pNewCursor && pNewCursor != pCursor)
{
// SelAllDestrAnch( false );
aSelEng.CursorPosChanging( bShift, bMod1 );
SetCursor( pNewCursor );
if( !IsEntryInView( pNewCursor ) )
MakeVisible( pNewCursor );
}
else
bKeyUsed = false;
break;
case KEY_ESCAPE:
case KEY_TAB:
case KEY_DELETE:
case KEY_BACKSPACE:
// must not be handled because this quits dialogs and does other magic things...
// if there are other single keys which should not be handled, they can be added here
bKeyUsed = false;
break;
default:
// is there any reason why we should eat the events here? The only place where this is called
// is from SvTreeListBox::KeyInput. If we set bKeyUsed to true here, then the key input
// is just silenced. However, we want SvLBox::KeyInput to get a chance, to do the QuickSelection
// handling.
// (The old code here which intentionally set bKeyUsed to sal_True said this was because of "quick search"
// handling, but actually there was no quick search handling anymore. We just re-implemented it.)
// #i31275# / 2009-06-16 / frank.schoenheit@sun.com
bKeyUsed = false;
break;
}
return bKeyUsed;
}
void SvImpLBox::GetFocus()
{
if( pCursor )
{
pView->SetEntryFocus( pCursor, true );
ShowCursor( true );
// auskommentiert wg. deselectall
// if( bSimpleTravel && !pView->IsSelected(pCursor) )
// pView->Select( pCursor, true );
}
if( m_nStyle & WB_HIDESELECTION )
{
SvTreeListEntry* pEntry = pView->FirstSelected();
while( pEntry )
{
InvalidateEntry( pEntry );
pEntry = pView->NextSelected( pEntry );
}
}
}
void SvImpLBox::LoseFocus()
{
aEditIdle.Stop();
if( pCursor )
pView->SetEntryFocus( pCursor,false );
ShowCursor( false );
if( m_nStyle & WB_HIDESELECTION )
{
SvTreeListEntry* pEntry = pView ? pView->FirstSelected() : nullptr;
while( pEntry )
{
InvalidateEntry( pEntry );
pEntry = pView->NextSelected( pEntry );
}
}
}
// ********************************************************************
// SelectionEngine
// ********************************************************************
void SvImpLBox::SelectEntry( SvTreeListEntry* pEntry, bool bSelect )
{
pView->Select( pEntry, bSelect );
}
ImpLBSelEng::ImpLBSelEng( SvImpLBox* pImpl, SvTreeListBox* pV )
{
pImp = pImpl;
pView = pV;
}
ImpLBSelEng::~ImpLBSelEng()
{
}
void ImpLBSelEng::BeginDrag()
{
pImp->BeginDrag();
}
void ImpLBSelEng::CreateAnchor()
{
pImp->pAnchor = pImp->pCursor;
}
void ImpLBSelEng::DestroyAnchor()
{
pImp->pAnchor = nullptr;
}
void ImpLBSelEng::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor)
{
SvTreeListEntry* pNewCursor = pImp->MakePointVisible( rPoint );
if( pNewCursor != pImp->pCursor )
pImp->BeginScroll();
if( pNewCursor )
{
// at SimpleTravel, the SetCursor is selected and the select handler is
// called
//if( !bDontSelectAtCursor && !pImp->bSimpleTravel )
// pImp->SelectEntry( pNewCursor, true );
pImp->SetCursor( pNewCursor, bDontSelectAtCursor );
}
}
bool ImpLBSelEng::IsSelectionAtPoint( const Point& rPoint )
{
SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
if( pEntry )
return pView->IsSelected(pEntry);
return false;
}
void ImpLBSelEng::DeselectAtPoint( const Point& rPoint )
{
SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
if( !pEntry )
return;
pImp->SelectEntry( pEntry, false );
}
void ImpLBSelEng::DeselectAll()
{
pImp->SelAllDestrAnch( false, false ); // don't reset SelectionEngine!
pImp->nFlags &= (~LBoxFlags::DeselectAll);
}
// ***********************************************************************
// Selection
// ***********************************************************************
void SvImpLBox::SetAnchorSelection(SvTreeListEntry* pOldCursor,SvTreeListEntry* pNewCursor)
{
SvTreeListEntry* pEntry;
sal_uLong nAnchorVisPos = pView->GetVisiblePos( pAnchor );
sal_uLong nOldVisPos = pView->GetVisiblePos( pOldCursor );
sal_uLong nNewVisPos = pView->GetVisiblePos( pNewCursor );
if( nOldVisPos > nAnchorVisPos ||
( nAnchorVisPos==nOldVisPos && nNewVisPos > nAnchorVisPos) )
{
if( nNewVisPos > nOldVisPos )
{
pEntry = pOldCursor;
while( pEntry && pEntry != pNewCursor )
{
pView->Select( pEntry );
pEntry = pView->NextVisible(pEntry);
}
if( pEntry )
pView->Select( pEntry );
return;
}
if( nNewVisPos < nAnchorVisPos )
{
pEntry = pAnchor;
while( pEntry && pEntry != pOldCursor )
{
pView->Select( pEntry, false );
pEntry = pView->NextVisible(pEntry);
}
if( pEntry )
pView->Select( pEntry, false );
pEntry = pNewCursor;
while( pEntry && pEntry != pAnchor )
{
pView->Select( pEntry );
pEntry = pView->NextVisible(pEntry);
}
if( pEntry )
pView->Select( pEntry );
return;
}
if( nNewVisPos < nOldVisPos )
{
pEntry = pNewCursor;
pEntry = pView->NextVisible(pEntry);
while( pEntry && pEntry != pOldCursor )
{
pView->Select( pEntry, false );
pEntry = pView->NextVisible(pEntry);
}
if( pEntry )
pView->Select( pEntry, false );
return;
}
}
else
{
if( nNewVisPos < nOldVisPos ) // enlarge selection
{
pEntry = pNewCursor;
while( pEntry && pEntry != pOldCursor )
{
pView->Select( pEntry );
pEntry = pView->NextVisible(pEntry);
}
if( pEntry )
pView->Select( pEntry );
return;
}
if( nNewVisPos > nAnchorVisPos )
{
pEntry = pOldCursor;
while( pEntry && pEntry != pAnchor )
{
pView->Select( pEntry, false );
pEntry = pView->NextVisible(pEntry);
}
if( pEntry )
pView->Select( pEntry, false );
pEntry = pAnchor;
while( pEntry && pEntry != pNewCursor )
{
pView->Select( pEntry );
pEntry = pView->NextVisible(pEntry);
}
if( pEntry )
pView->Select( pEntry );
return;
}
if( nNewVisPos > nOldVisPos )
{
pEntry = pOldCursor;
while( pEntry && pEntry != pNewCursor )
{
pView->Select( pEntry, false );
pEntry = pView->NextVisible(pEntry);
}
return;
}
}
}
void SvImpLBox::SelAllDestrAnch(
bool bSelect, bool bDestroyAnchor, bool bSingleSelToo )
{
SvTreeListEntry* pEntry;
nFlags &= (~LBoxFlags::DeselectAll);
if( bSelect && bSimpleTravel )
{
if( pCursor && !pView->IsSelected( pCursor ))
{
pView->Select( pCursor );
}
return;
}
if( !bSelect && pView->GetSelectionCount() == 0 )
{
if( bSimpleTravel && ( !GetUpdateMode() || !pCursor) )
nFlags |= LBoxFlags::DeselectAll;
return;
}
if( bSelect && pView->GetSelectionCount() == pView->GetEntryCount())
return;
if( !bSingleSelToo && bSimpleTravel )
return;
if( !bSelect && pView->GetSelectionCount()==1 && pCursor &&
pView->IsSelected( pCursor ))
{
pView->Select( pCursor, false );
if( bDestroyAnchor )
DestroyAnchor(); // delete anchor & reset SelectionEngine
else
pAnchor = nullptr; // always delete internal anchor
return;
}
if( bSimpleTravel && !pCursor && !GetUpdateMode() )
nFlags |= LBoxFlags::DeselectAll;
ShowCursor( false );
bool bUpdate = GetUpdateMode();
nFlags |= LBoxFlags::IgnoreSelect; // EntryInserted should not do anything
pEntry = pTree->First();
while( pEntry )
{
if( pView->Select( pEntry, bSelect ) )
{
if( bUpdate && pView->IsEntryVisible(pEntry) )
{
long nY = GetEntryLine( pEntry );
if( IsLineVisible( nY ) )
InvalidateEntry(pEntry);
}
}
pEntry = pTree->Next( pEntry );
}
nFlags &= ~LBoxFlags::IgnoreSelect;
if( bDestroyAnchor )
DestroyAnchor(); // delete anchor & reset SelectionEngine
else
pAnchor = nullptr; // always delete internal anchor
ShowCursor( true );
}
void SvImpLBox::SetSelectionMode( SelectionMode eSelMode )
{
aSelEng.SetSelectionMode( eSelMode);
if( eSelMode == SelectionMode::Single )
bSimpleTravel = true;
else
bSimpleTravel = false;
if( (m_nStyle & WB_SIMPLEMODE) && (eSelMode == SelectionMode::Multiple) )
aSelEng.AddAlways( true );
}
// ***********************************************************************
// Drag & Drop
// ***********************************************************************
void SvImpLBox::SetDragDropMode( DragDropMode eDDMode )
{
if( eDDMode != DragDropMode::NONE && eDDMode != DragDropMode::APP_DROP )
{
aSelEng.ExpandSelectionOnMouseMove( false );
aSelEng.EnableDrag( true );
}
else
{
aSelEng.ExpandSelectionOnMouseMove();
aSelEng.EnableDrag( false );
}
}
void SvImpLBox::BeginDrag()
{
nFlags &= (~LBoxFlags::Filling);
if( !bAsyncBeginDrag )
{
BeginScroll();
pView->StartDrag( 0, aSelEng.GetMousePosPixel() );
EndScroll();
}
else
{
aAsyncBeginDragPos = aSelEng.GetMousePosPixel();
aAsyncBeginDragIdle.Start();
}
}
IMPL_LINK_NOARG(SvImpLBox, BeginDragHdl, Timer *, void)
{
pView->StartDrag( 0, aAsyncBeginDragPos );
}
void SvImpLBox::PaintDDCursor( SvTreeListEntry* pInsertionPos )
{
long nY;
if( pInsertionPos )
{
nY = GetEntryLine( pInsertionPos );
nY += pView->GetEntryHeight();
}
else
nY = 1;
RasterOp eOldOp = pView->GetRasterOp();
pView->SetRasterOp( RasterOp::Invert );
Color aOldLineColor = pView->GetLineColor();
pView->SetLineColor( COL_BLACK );
pView->DrawLine( Point( 0, nY ), Point( aOutputSize.Width(), nY ) );
pView->SetLineColor( aOldLineColor );
pView->SetRasterOp( eOldOp );
}
void SvImpLBox::Command( const CommandEvent& rCEvt )
{
CommandEventId nCommand = rCEvt.GetCommand();
if( nCommand == CommandEventId::ContextMenu )
aEditIdle.Stop();
// scroll mouse event?
if( ( ( nCommand == CommandEventId::Wheel ) || ( nCommand == CommandEventId::StartAutoScroll ) || ( nCommand == CommandEventId::AutoScroll ) )
&& pView->HandleScrollCommand( rCEvt, aHorSBar.get(), aVerSBar.get() ) )
return;
if( bContextMenuHandling && nCommand == CommandEventId::ContextMenu )
{
Point aPopupPos;
bool bClickedIsFreePlace = false;
std::stack<SvTreeListEntry*> aSelRestore;
if( rCEvt.IsMouseEvent() )
{ // change selection, if mouse position doesn't fit to selection
aPopupPos = rCEvt.GetMousePosPixel();
SvTreeListEntry* pClickedEntry = GetEntry( aPopupPos );
if( pClickedEntry )
{ // mouse in non empty area
bool bClickedIsSelected = false;
// collect the currently selected entries
SvTreeListEntry* pSelected = pView->FirstSelected();
while( pSelected )
{
bClickedIsSelected |= ( pClickedEntry == pSelected );
pSelected = pView->NextSelected( pSelected );
}
// if the entry which the user clicked at is not selected
if( !bClickedIsSelected )
{ // deselect all other and select the clicked one
pView->SelectAll( false );
pView->SetCursor( pClickedEntry );
}
}
else if( aSelEng.GetSelectionMode() == SelectionMode::Single )
{
bClickedIsFreePlace = true;
sal_Int32 nSelectedEntries = pView->GetSelectionCount();
SvTreeListEntry* pSelected = pView->FirstSelected();
for(sal_Int32 nSel = 0; nSel < nSelectedEntries; nSel++ )
{
aSelRestore.push(pSelected);
pSelected = pView->NextSelected( pSelected );
}
pView->SelectAll( false );
}
else
{ // deselect all
pView->SelectAll( false );
}
}
else
{ // key event (or at least no mouse event)
sal_Int32 nSelectionCount = pView->GetSelectionCount();
if( nSelectionCount )
{ // now always take first visible as base for positioning the menu
SvTreeListEntry* pSelected = pView->FirstSelected();
while( pSelected )
{
if( IsEntryInView( pSelected ) )
break;
pSelected = pView->NextSelected( pSelected );
}
if( !pSelected )
{
// no one was visible
pSelected = pView->FirstSelected();
pView->MakeVisible( pSelected );
}
aPopupPos = pView->GetFocusRect( pSelected, pView->GetEntryPosition( pSelected ).Y() ).Center();
}
else
aPopupPos = Point( 0, 0 );
}
{
VclPtr<PopupMenu> pPopup = pView->CreateContextMenu();
if (pPopup)
{
// do action for selected entry in popup menu
sal_uInt16 nMenuAction = pPopup->Execute( pView, aPopupPos );
if ( nMenuAction )
pView->ExecuteContextMenuAction( nMenuAction );
pPopup.disposeAndClear();
}
}
if( bClickedIsFreePlace )
{
while(!aSelRestore.empty())
{
SvTreeListEntry* pEntry = aSelRestore.top();
//#i19717# the entry is maybe already deleted
bool bFound = false;
for(sal_uLong nEntry = 0; nEntry < pView->GetEntryCount(); nEntry++)
if(pEntry == pView->GetEntry(nEntry))
{
bFound = true;
break;
}
if(bFound)
SetCurEntry( pEntry );
aSelRestore.pop();
}
}
}
else
{
const Point& rPos = rCEvt.GetMousePosPixel();
if( rPos.X() < aOutputSize.Width() && rPos.Y() < aOutputSize.Height() )
aSelEng.Command( rCEvt );
}
}
void SvImpLBox::BeginScroll()
{
if( !(nFlags & LBoxFlags::InScrolling))
{
nFlags |= LBoxFlags::InScrolling;
}
}
void SvImpLBox::EndScroll()
{
if( nFlags & LBoxFlags::InScrolling)
{
pView->NotifyEndScroll();
nFlags &= (~LBoxFlags::InScrolling);
}
}
tools::Rectangle SvImpLBox::GetVisibleArea() const
{
Point aPos( pView->GetMapMode().GetOrigin() );
aPos.setX( aPos.X() * -1 );
tools::Rectangle aRect( aPos, aOutputSize );
return aRect;
}
void SvImpLBox::Invalidate()
{
pView->SetClipRegion();
}
void SvImpLBox::SetCurEntry( SvTreeListEntry* pEntry )
{
if ( ( aSelEng.GetSelectionMode() != SelectionMode::Single )
&& ( aSelEng.GetSelectionMode() != SelectionMode::NONE )
)
SelAllDestrAnch( false );
if ( pEntry )
MakeVisible( pEntry );
SetCursor( pEntry );
if ( pEntry && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
pView->Select( pEntry );
}
IMPL_LINK_NOARG(SvImpLBox, EditTimerCall, Timer *, void)
{
if( !pView->IsInplaceEditingEnabled() )
return;
bool bIsMouseTriggered = aEditClickPos.X() >= 0;
if ( bIsMouseTriggered )
{
Point aCurrentMousePos = pView->GetPointerPosPixel();
if ( ( std::abs( aCurrentMousePos.X() - aEditClickPos.X() ) > 5 )
|| ( std::abs( aCurrentMousePos.Y() - aEditClickPos.Y() ) > 5 )
)
{
return;
}
}
SvTreeListEntry* pEntry = GetCurEntry();
if( pEntry )
{
ShowCursor( false );
pView->ImplEditEntry( pEntry );
ShowCursor( true );
}
}
bool SvImpLBox::RequestHelp( const HelpEvent& rHEvt )
{
if( rHEvt.GetMode() & HelpEventMode::QUICK )
{
Point aPos( pView->ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
if( !GetVisibleArea().IsInside( aPos ))
return false;
SvTreeListEntry* pEntry = GetEntry( aPos );
if( pEntry )
{
// recalculate text rectangle
SvLBoxTab* pTab;
SvLBoxItem* pItem = pView->GetItem( pEntry, aPos.X(), &pTab );
if (!pItem || pItem->GetType() != SvLBoxItemType::String)
return false;
aPos = GetEntryPosition( pEntry );
aPos.setX( pView->GetTabPos( pEntry, pTab ) ); //pTab->GetPos();
Size aSize( pItem->GetSize( pView, pEntry ) );
SvLBoxTab* pNextTab = NextTab( pTab );
bool bItemClipped = false;
// is the item cut off by its right neighbor?
if( pNextTab && pView->GetTabPos(pEntry,pNextTab) < aPos.X()+aSize.Width() )
{
aSize.setWidth( pNextTab->GetPos() - pTab->GetPos() );
bItemClipped = true;
}
tools::Rectangle aItemRect( aPos, aSize );
tools::Rectangle aViewRect( GetVisibleArea() );
if( bItemClipped || !aViewRect.IsInside( aItemRect ) )
{
// clip the right edge of the item at the edge of the view
//if( aItemRect.Right() > aViewRect.Right() )
// aItemRect.Right() = aViewRect.Right();
Point aPt = pView->OutputToScreenPixel( aItemRect.TopLeft() );
aItemRect.SetLeft( aPt.X() );
aItemRect.SetTop( aPt.Y() );
aPt = pView->OutputToScreenPixel( aItemRect.BottomRight() );
aItemRect.SetRight( aPt.X() );
aItemRect.SetBottom( aPt.Y() );
Help::ShowQuickHelp( pView, aItemRect,
static_cast<SvLBoxString*>(pItem)->GetText(), QuickHelpFlags::Left | QuickHelpFlags::VCenter );
return true;
}
}
}
return false;
}
SvLBoxTab* SvImpLBox::NextTab( SvLBoxTab const * pTab )
{
sal_uInt16 nTabCount = pView->TabCount();
if( nTabCount <= 1 )
return nullptr;
for( int nTab=0; nTab < (nTabCount-1); nTab++)
{
if( pView->aTabs[nTab]==pTab )
return pView->aTabs[nTab+1];
}
return nullptr;
}
void SvImpLBox::EndSelection()
{
DestroyAnchor();
nFlags &= ~LBoxFlags::StartEditTimer;
}
void SvImpLBox::SetUpdateMode( bool bMode )
{
if( bUpdateMode != bMode )
{
bUpdateMode = bMode;
if( bUpdateMode )
UpdateAll( false );
}
}
bool SvImpLBox::SetMostRight( SvTreeListEntry* pEntry )
{
if( pView->nTreeFlags & SvTreeFlags::RECALCTABS )
{
nFlags |= LBoxFlags::IgnoreChangedTabs;
pView->SetTabs();
nFlags &= ~LBoxFlags::IgnoreChangedTabs;
}
sal_uInt16 nLastTab = pView->aTabs.size() - 1;
sal_uInt16 nLastItem = pEntry->ItemCount() - 1;
if( !pView->aTabs.empty() && nLastItem != USHRT_MAX )
{
if( nLastItem < nLastTab )
nLastTab = nLastItem;
SvLBoxTab* pTab = pView->aTabs[ nLastTab ];
SvLBoxItem& rItem = pEntry->GetItem( nLastTab );
long nTabPos = pView->GetTabPos( pEntry, pTab );
long nMaxRight = GetOutputSize().Width();
Point aPos( pView->GetMapMode().GetOrigin() );
aPos.setX( aPos.X() * -1 ); // conversion document coordinates
nMaxRight = nMaxRight + aPos.X() - 1;
long nNextTab = nTabPos < nMaxRight ? nMaxRight : nMaxRight + 50;
long nTabWidth = nNextTab - nTabPos + 1;
long nItemSize = rItem.GetSize(pView,pEntry).Width();
long nOffset = pTab->CalcOffset( nItemSize, nTabWidth );
long nRight = nTabPos + nOffset + nItemSize;
if( nRight > nMostRight )
{
nMostRight = nRight;
pMostRightEntry = pEntry;
return true;
}
}
return false;
}
void SvImpLBox::FindMostRight( SvTreeListEntry const * pEntryToIgnore )
{
nMostRight = -1;
pMostRightEntry = nullptr;
if( !pView->GetModel() )
return;
SvTreeListEntry* pEntry = pView->FirstVisible();
while( pEntry )
{
if( pEntry != pEntryToIgnore )
SetMostRight( pEntry );
pEntry = pView->NextVisible( pEntry );
}
}
void SvImpLBox::FindMostRight( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
{
if( !pParent )
FindMostRight( pEntryToIgnore );
else
FindMostRight_Impl( pParent, pEntryToIgnore );
}
void SvImpLBox::FindMostRight_Impl( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
{
SvTreeListEntries& rList = pTree->GetChildList( pParent );
size_t nCount = rList.size();
for( size_t nCur = 0; nCur < nCount; nCur++ )
{
SvTreeListEntry* pChild = rList[nCur].get();
if( pChild != pEntryToIgnore )
{
SetMostRight( pChild );
if( pChild->HasChildren() && pView->IsExpanded( pChild ))
FindMostRight_Impl( pChild, pEntryToIgnore );
}
}
}
void SvImpLBox::NotifyTabsChanged()
{
if( GetUpdateMode() && !(nFlags & LBoxFlags::IgnoreChangedTabs ) &&
nCurUserEvent == nullptr )
{
nCurUserEvent = Application::PostUserEvent(LINK(this,SvImpLBox,MyUserEvent));
}
}
bool SvImpLBox::IsExpandable() const
{
return pCursor->HasChildren() || pCursor->HasChildrenOnDemand();
}
IMPL_LINK(SvImpLBox, MyUserEvent, void*, pArg, void )
{
nCurUserEvent = nullptr;
if( !pArg )
{
pView->Invalidate();
pView->Update();
}
else
{
FindMostRight( nullptr );
ShowVerSBar();
pView->Invalidate( GetVisibleArea() );
}
}
void SvImpLBox::StopUserEvent()
{
if( nCurUserEvent != nullptr )
{
Application::RemoveUserEvent( nCurUserEvent );
nCurUserEvent = nullptr;
}
}
void SvImpLBox::ShowFocusRect( const SvTreeListEntry* pEntry )
{
if( pEntry )
{
long nY = GetEntryLine( const_cast<SvTreeListEntry*>(pEntry) );
tools::Rectangle aRect = pView->GetFocusRect( const_cast<SvTreeListEntry*>(pEntry), nY );
vcl::Region aOldClip( pView->GetClipRegion());
vcl::Region aClipRegion( GetClipRegionRect() );
pView->SetClipRegion( aClipRegion );
pView->ShowFocus( aRect );
pView->SetClipRegion( aOldClip );
}
else
{
pView->HideFocus();
}
}
void SvImpLBox::implInitDefaultNodeImages()
{
if ( s_pDefCollapsed )
// assume that all or nothing is initialized
return;
s_pDefCollapsed = new Image(BitmapEx(RID_BMP_TREENODE_COLLAPSED));
s_pDefExpanded = new Image(BitmapEx(RID_BMP_TREENODE_EXPANDED));
}
const Image& SvImpLBox::GetDefaultExpandedNodeImage( )
{
implInitDefaultNodeImages();
return *s_pDefExpanded;
}
const Image& SvImpLBox::GetDefaultCollapsedNodeImage( )
{
implInitDefaultNodeImages();
return *s_pDefCollapsed;
}
void SvImpLBox::CallEventListeners( VclEventId nEvent, void* pData )
{
if ( pView )
pView->CallImplEventListeners( nEvent, pData);
}
bool SvImpLBox::SetCurrentTabPos( sal_uInt16 _nNewPos )
{
bool bRet = false;
if ( pView && _nNewPos < ( pView->TabCount() - 2 ) )
{
nCurTabPos = _nNewPos;
ShowCursor( true );
bRet = true;
}
return bRet;
}
bool SvImpLBox::IsSelectable( const SvTreeListEntry* pEntry )
{
if( pEntry )
{
SvViewDataEntry* pViewDataNewCur = pView->GetViewDataEntry(pEntry);
return (pViewDataNewCur == nullptr) || pViewDataNewCur->IsSelectable();
}
else
{
return false;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V560 A part of conditional expression is always true: pCursor.
↑ V560 A part of conditional expression is always true: pCursor.
↑ V547 Expression 'pCursor' is always true.
↑ V547 Expression 'pCursor' is always true.