summaryrefslogtreecommitdiffstats
path: root/vcl/source/treelist/svimpbox.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/treelist/svimpbox.cxx')
-rw-r--r--vcl/source/treelist/svimpbox.cxx3314
1 files changed, 3314 insertions, 0 deletions
diff --git a/vcl/source/treelist/svimpbox.cxx b/vcl/source/treelist/svimpbox.cxx
new file mode 100644
index 000000000..fcd1487e7
--- /dev/null
+++ b/vcl/source/treelist/svimpbox.cxx
@@ -0,0 +1,3314 @@
+/* -*- 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 <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/salnativewidgets.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandevent.hxx>
+
+#include <cstdlib>
+#include <memory>
+#include <stack>
+
+#include <vcl/treelistbox.hxx>
+#include <vcl/svlbitm.hxx>
+#include <tools/wintypes.hxx>
+#include <bitmaps.hlst>
+#include <svimpbox.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/debug.hxx>
+
+#include <vcl/treelistentry.hxx>
+#include <vcl/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)
+ : m_aHorSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_HSCROLL))
+ , m_aScrBarBox(VclPtr<ScrollBarBox>::Create(pLBView))
+ , m_aFctSet(this, pLBView)
+ , mbForceMakeVisible (false)
+ , m_aVerSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_VSCROLL))
+ , m_aOutputSize(0, 0)
+ , mbNoAutoCurEntry(false)
+ , m_aSelEng(pLBView, nullptr)
+ , m_nNextVerVisSize(0)
+{
+ osl_atomic_increment(&s_nImageRefCount);
+ m_pView = pLBView;
+ m_pTree = pLBTree;
+ m_aSelEng.SetFunctionSet( static_cast<FunctionSet*>(&m_aFctSet) );
+ m_aSelEng.ExpandSelectionOnMouseMove( false );
+ SetStyle( nWinStyle );
+ SetSelectionMode( SelectionMode::Single );
+ SetDragDropMode( DragDropMode::NONE );
+
+ m_aVerSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollUpDownHdl ) );
+ m_aHorSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollLeftRightHdl ) );
+ m_aHorSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
+ m_aVerSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
+ m_aVerSBar->SetRange( Range(0,0) );
+ m_aVerSBar->Hide();
+ m_aHorSBar->SetRange( Range(0,0) );
+ m_aHorSBar->SetPageSize( 24 ); // pixels
+ m_aHorSBar->SetLineSize( 8 ); // pixels
+
+ m_nHorSBarHeight = static_cast<short>(m_aHorSBar->GetSizePixel().Height());
+ m_nVerSBarWidth = static_cast<short>(m_aVerSBar->GetSizePixel().Width());
+
+ m_pStartEntry = nullptr;
+ m_pCursor = nullptr;
+ m_pAnchor = nullptr;
+ m_nVisibleCount = 0; // number of rows of data in control
+ m_nNodeBmpTabDistance = NODE_BMP_TABDIST_NOTVALID;
+ m_nNodeBmpWidth = 0;
+
+ // button animation in listbox
+ m_pActiveButton = nullptr;
+ m_pActiveEntry = nullptr;
+ m_pActiveTab = nullptr;
+
+ m_nFlags = LBoxFlags::NONE;
+
+ m_aEditIdle.SetPriority( TaskPriority::LOWEST );
+ m_aEditIdle.SetInvokeHandler( LINK(this,SvImpLBox,EditTimerCall) );
+
+ m_nMostRight = -1;
+ m_pMostRightEntry = nullptr;
+ m_nCurUserEvent = nullptr;
+
+ m_bUpdateMode = true;
+ m_bInVScrollHdl = false;
+ m_nFlags |= LBoxFlags::Filling;
+
+ m_bSubLstOpLR = m_bContextMenuHandling = false;
+}
+
+SvImpLBox::~SvImpLBox()
+{
+ m_aEditIdle.Stop();
+ StopUserEvent();
+
+ if ( osl_atomic_decrement(&s_nImageRefCount) == 0 )
+ {
+ DELETEZ(s_pDefCollapsed);
+ DELETEZ(s_pDefExpanded);
+ }
+ m_aVerSBar.disposeAndClear();
+ m_aHorSBar.disposeAndClear();
+ m_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( m_pView->pModel, "View and Model aren't valid!" );
+
+ sal_uInt16 nDepth = m_pView->pModel->GetDepth( pEntry );
+ // initialize vector if necessary
+ std::vector< short >::size_type nSize = m_aContextBmpWidthVector.size();
+ while ( nDepth > nSize )
+ {
+ m_aContextBmpWidthVector.resize( nSize + 1 );
+ m_aContextBmpWidthVector.at( nSize ) = nWidth;
+ ++nSize;
+ }
+ if( m_aContextBmpWidthVector.size() == nDepth )
+ {
+ m_aContextBmpWidthVector.resize( nDepth + 1 );
+ m_aContextBmpWidthVector.at( nDepth ) = 0;
+ }
+ short nContextBmpWidth = m_aContextBmpWidthVector[ nDepth ];
+ if( nContextBmpWidth < nWidth )
+ {
+ m_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 = m_pView->FirstChild( pEntry );
+ DBG_ASSERT( pChild, "The first child is invalid!" );
+ do
+ {
+ UpdateContextBmpWidthVectorFromMovedEntry( pChild );
+ pChild = m_pView->Next( pChild );
+ } while ( pChild );
+ }
+}
+
+void SvImpLBox::UpdateContextBmpWidthMax( SvTreeListEntry const * pEntry )
+{
+ sal_uInt16 nDepth = m_pView->pModel->GetDepth( pEntry );
+ if( m_aContextBmpWidthVector.empty() )
+ return;
+ short nWidth = m_aContextBmpWidthVector[ nDepth ];
+ if( nWidth != m_pView->nContextBmpWidthMax ) {
+ m_pView->nContextBmpWidthMax = nWidth;
+ m_nFlags |= LBoxFlags::IgnoreChangedTabs;
+ m_pView->SetTabs();
+ m_nFlags &= ~LBoxFlags::IgnoreChangedTabs;
+ }
+}
+
+void SvImpLBox::SetStyle( WinBits i_nWinStyle )
+{
+ m_nStyle = i_nWinStyle;
+ if ( ( m_nStyle & WB_SIMPLEMODE) && ( m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) )
+ m_aSelEng.AddAlways( true );
+}
+
+void SvImpLBox::SetNoAutoCurEntry( bool b )
+{
+ mbNoAutoCurEntry = b;
+}
+
+// don't touch the model any more
+void SvImpLBox::Clear()
+{
+ StopUserEvent();
+ m_pStartEntry = nullptr;
+ m_pAnchor = nullptr;
+
+ m_pActiveButton = nullptr;
+ m_pActiveEntry = nullptr;
+ m_pActiveTab = nullptr;
+
+ m_nMostRight = -1;
+ m_pMostRightEntry = nullptr;
+
+ // don't touch the cursor any more
+ if( m_pCursor )
+ {
+ if( m_pView->HasFocus() )
+ m_pView->HideFocus();
+ m_pCursor = nullptr;
+ }
+ m_aVerSBar->Hide();
+ m_aVerSBar->SetThumbPos( 0 );
+ Range aRange( 0, 0 );
+ m_aVerSBar->SetRange( aRange );
+ m_aOutputSize = m_pView->Control::GetOutputSizePixel();
+ m_aHorSBar->Hide();
+ m_aHorSBar->SetThumbPos( 0 );
+ MapMode aMapMode( m_pView->GetMapMode());
+ aMapMode.SetOrigin( Point(0,0) );
+ m_pView->Control::SetMapMode( aMapMode );
+ m_aHorSBar->SetRange( aRange );
+ m_aHorSBar->SetSizePixel(Size(m_aOutputSize.Width(),m_nHorSBarHeight));
+ m_pView->SetClipRegion();
+ if( GetUpdateMode() )
+ m_pView->Invalidate( GetVisibleArea() );
+ m_nFlags |= LBoxFlags::Filling;
+ if( !m_aHorSBar->IsVisible() && !m_aVerSBar->IsVisible() )
+ m_aScrBarBox->Hide();
+
+ m_aContextBmpWidthVector.clear();
+
+ CallEventListeners( VclEventId::ListboxItemRemoved );
+}
+
+// *********************************************************************
+// Paint, navigate, scroll
+// *********************************************************************
+
+IMPL_LINK_NOARG(SvImpLBox, EndScrollHdl, ScrollBar*, void)
+{
+ if( m_nFlags & LBoxFlags::EndScrollSetVisSize )
+ {
+ m_aVerSBar->SetVisibleSize( m_nNextVerVisSize );
+ m_nFlags &= ~LBoxFlags::EndScrollSetVisSize;
+ }
+ EndScroll();
+}
+
+// handler for vertical scrollbar
+
+IMPL_LINK( SvImpLBox, ScrollUpDownHdl, ScrollBar *, pScrollBar, void )
+{
+ DBG_ASSERT(!m_bInVScrollHdl,"Scroll handler out-paces itself!");
+ long nDelta = pScrollBar->GetDelta();
+ if( !nDelta )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+
+ m_bInVScrollHdl = true;
+
+ if( m_pView->IsEditingActive() )
+ {
+ m_pView->EndEditing( true ); // Cancel
+ m_pView->PaintImmediately();
+ }
+ 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) );
+ }
+ m_bInVScrollHdl = false;
+}
+
+
+void SvImpLBox::CursorDown()
+{
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pNextFirstToDraw = m_pView->NextVisible(m_pStartEntry);
+ if( pNextFirstToDraw )
+ {
+ m_nFlags &= ~LBoxFlags::Filling;
+ ShowCursor( false );
+ m_pView->PaintImmediately();
+ m_pStartEntry = pNextFirstToDraw;
+ tools::Rectangle aArea( GetVisibleArea() );
+ m_pView->Scroll( 0, -(m_pView->GetEntryHeight()), aArea, ScrollFlags::NoChildren );
+ m_pView->PaintImmediately();
+ ShowCursor( true );
+ m_pView->NotifyScrolled();
+ }
+}
+
+void SvImpLBox::CursorUp()
+{
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pPrevFirstToDraw = m_pView->PrevVisible(m_pStartEntry);
+ if( !pPrevFirstToDraw )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ long nEntryHeight = m_pView->GetEntryHeight();
+ ShowCursor( false );
+ m_pView->PaintImmediately();
+ m_pStartEntry = pPrevFirstToDraw;
+ tools::Rectangle aArea( GetVisibleArea() );
+ aArea.AdjustBottom( -nEntryHeight );
+ m_pView->Scroll( 0, nEntryHeight, aArea, ScrollFlags::NoChildren );
+ m_pView->PaintImmediately();
+ ShowCursor( true );
+ m_pView->NotifyScrolled();
+}
+
+void SvImpLBox::PageDown( sal_uInt16 nDelta )
+{
+ sal_uInt16 nRealDelta = nDelta;
+
+ if( !nDelta )
+ return;
+
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pNext = m_pView->NextVisible(m_pStartEntry, nRealDelta);
+ if( pNext == m_pStartEntry )
+ return;
+
+ ShowCursor( false );
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ m_pStartEntry = pNext;
+
+ if( nRealDelta >= m_nVisibleCount )
+ {
+ m_pView->Invalidate( GetVisibleArea() );
+ m_pView->PaintImmediately();
+ }
+ else
+ {
+ tools::Rectangle aArea( GetVisibleArea() );
+ long nScroll = m_pView->GetEntryHeight() * static_cast<long>(nRealDelta);
+ nScroll = -nScroll;
+ m_pView->PaintImmediately();
+ m_pView->Scroll( 0, nScroll, aArea, ScrollFlags::NoChildren );
+ m_pView->PaintImmediately();
+ m_pView->NotifyScrolled();
+ }
+
+ ShowCursor( true );
+}
+
+void SvImpLBox::PageUp( sal_uInt16 nDelta )
+{
+ sal_uInt16 nRealDelta = nDelta;
+ if( !nDelta )
+ return;
+
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pPrev = m_pView->PrevVisible(m_pStartEntry, nRealDelta);
+ if( pPrev == m_pStartEntry )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ ShowCursor( false );
+
+ m_pStartEntry = pPrev;
+ if( nRealDelta >= m_nVisibleCount )
+ {
+ m_pView->Invalidate( GetVisibleArea() );
+ m_pView->PaintImmediately();
+ }
+ else
+ {
+ long nEntryHeight = m_pView->GetEntryHeight();
+ tools::Rectangle aArea( GetVisibleArea() );
+ m_pView->PaintImmediately();
+ m_pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, ScrollFlags::NoChildren );
+ m_pView->PaintImmediately();
+ m_pView->NotifyScrolled();
+ }
+
+ ShowCursor( true );
+}
+
+void SvImpLBox::KeyUp( bool bPageUp )
+{
+ if( !m_aVerSBar->IsVisible() )
+ return;
+
+ long nDelta;
+ if( bPageUp )
+ nDelta = m_aVerSBar->GetPageSize();
+ else
+ nDelta = 1;
+
+ long nThumbPos = m_aVerSBar->GetThumbPos();
+
+ if( nThumbPos < nDelta )
+ nDelta = nThumbPos;
+
+ if( nDelta <= 0 )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ BeginScroll();
+
+ m_aVerSBar->SetThumbPos( nThumbPos - nDelta );
+ if( bPageUp )
+ PageUp( static_cast<short>(nDelta) );
+ else
+ CursorUp();
+
+ EndScroll();
+}
+
+
+void SvImpLBox::KeyDown( bool bPageDown )
+{
+ if( !m_aVerSBar->IsVisible() )
+ return;
+
+ long nDelta;
+ if( bPageDown )
+ nDelta = m_aVerSBar->GetPageSize();
+ else
+ nDelta = 1;
+
+ long nThumbPos = m_aVerSBar->GetThumbPos();
+ long nVisibleSize = m_aVerSBar->GetVisibleSize();
+ long nRange = m_aVerSBar->GetRange().Len();
+
+ long nTmp = nThumbPos+nVisibleSize;
+ while( (nDelta > 0) && (nTmp+nDelta) >= nRange )
+ nDelta--;
+
+ if( nDelta <= 0 )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ BeginScroll();
+
+ m_aVerSBar->SetThumbPos( nThumbPos+nDelta );
+ if( bPageDown )
+ PageDown( static_cast<short>(nDelta) );
+ else
+ CursorDown();
+
+ EndScroll();
+}
+
+
+void SvImpLBox::InvalidateEntriesFrom( long nY ) const
+{
+ if( !(m_nFlags & LBoxFlags::InPaint ))
+ {
+ tools::Rectangle aRect( GetVisibleArea() );
+ aRect.SetTop( nY );
+ m_pView->Invalidate( aRect );
+ }
+}
+
+void SvImpLBox::InvalidateEntry( long nY ) const
+{
+ if( !(m_nFlags & LBoxFlags::InPaint ))
+ {
+ tools::Rectangle aRect( GetVisibleArea() );
+ long nMaxBottom = aRect.Bottom();
+ aRect.SetTop( nY );
+ aRect.SetBottom( nY ); aRect.AdjustBottom(m_pView->GetEntryHeight() );
+ if( aRect.Top() > nMaxBottom )
+ return;
+ if( aRect.Bottom() > nMaxBottom )
+ aRect.SetBottom( nMaxBottom );
+ if (m_pView->SupportsDoubleBuffering())
+ // Perform full paint when flicker is to be avoided explicitly.
+ m_pView->Invalidate();
+ else
+ m_pView->Invalidate(aRect);
+ }
+}
+
+void SvImpLBox::InvalidateEntry( SvTreeListEntry* pEntry )
+{
+ if( GetUpdateMode() )
+ {
+ long nPrev = m_nMostRight;
+ SetMostRight( pEntry );
+ if( nPrev < m_nMostRight )
+ ShowVerSBar();
+ }
+ if( !(m_nFlags & LBoxFlags::InPaint ))
+ {
+ bool bHasFocusRect = false;
+ if( pEntry==m_pCursor && m_pView->HasFocus() )
+ {
+ bHasFocusRect = true;
+ ShowCursor( false );
+ }
+ InvalidateEntry( GetEntryLine( pEntry ) );
+ if( bHasFocusRect )
+ ShowCursor( true );
+ }
+}
+
+
+void SvImpLBox::RecalcFocusRect()
+{
+ if( m_pView->HasFocus() && m_pCursor )
+ {
+ m_pView->HideFocus();
+ long nY = GetEntryLine( m_pCursor );
+ tools::Rectangle aRect = m_pView->GetFocusRect( m_pCursor, nY );
+ vcl::Region aOldClip( m_pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ m_pView->SetClipRegion( aClipRegion );
+ m_pView->ShowFocus( aRect );
+ m_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= m_pView->GetViewDataEntry(pEntry);
+ if( pEntry &&
+ pEntry == m_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 = m_pView->NextVisible(pEntry);
+ pViewDataNewCur = pEntry ? m_pView->GetViewDataEntry(pEntry) : nullptr;
+ }
+
+ SvTreeListEntry* pOldCursor = m_pCursor;
+ if( m_pCursor && pEntry != m_pCursor )
+ {
+ m_pView->SetEntryFocus( m_pCursor, false );
+ if( m_bSimpleTravel )
+ m_pView->Select( m_pCursor, false );
+ m_pView->HideFocus();
+ }
+ m_pCursor = pEntry;
+ if( m_pCursor )
+ {
+ if (pViewDataNewCur)
+ pViewDataNewCur->SetFocus( true );
+ if(!bForceNoSelect && m_bSimpleTravel && !(m_nFlags & LBoxFlags::DeselectAll) && GetUpdateMode())
+ {
+ m_pView->Select( m_pCursor );
+ CallEventListeners( VclEventId::ListboxTreeFocus, m_pCursor );
+ }
+ // multiple selection: select in cursor move if we're not in
+ // Add mode (Ctrl-F8)
+ else if( GetUpdateMode() &&
+ m_pView->GetSelectionMode() == SelectionMode::Multiple &&
+ !(m_nFlags & LBoxFlags::DeselectAll) && !m_aSelEng.IsAddMode() &&
+ !bForceNoSelect )
+ {
+ m_pView->Select( m_pCursor );
+ CallEventListeners( VclEventId::ListboxTreeFocus, m_pCursor );
+ }
+ else
+ {
+ ShowCursor( true );
+ if (bForceNoSelect && GetUpdateMode())
+ {
+ CallEventListeners( VclEventId::ListboxTreeFocus, m_pCursor);
+ }
+ }
+
+ if( m_pAnchor )
+ {
+ DBG_ASSERT(m_aSelEng.GetSelectionMode() != SelectionMode::Single,"Mode?");
+ SetAnchorSelection( pOldCursor, m_pCursor );
+ }
+ }
+ m_nFlags &= ~LBoxFlags::DeselectAll;
+
+ m_pView->OnCurrentEntryChanged();
+}
+
+void SvImpLBox::ShowCursor( bool bShow )
+{
+ if( !bShow || !m_pCursor || !m_pView->HasFocus() )
+ {
+ vcl::Region aOldClip( m_pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ m_pView->SetClipRegion( aClipRegion );
+ m_pView->HideFocus();
+ m_pView->SetClipRegion( aOldClip );
+ }
+ else
+ {
+ long nY = GetEntryLine( m_pCursor );
+ tools::Rectangle aRect = m_pView->GetFocusRect( m_pCursor, nY );
+ vcl::Region aOldClip( m_pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ m_pView->SetClipRegion( aClipRegion );
+ m_pView->ShowFocus( aRect );
+ m_pView->SetClipRegion( aOldClip );
+ }
+}
+
+
+void SvImpLBox::UpdateAll( bool bInvalidateCompleteView )
+{
+ FindMostRight();
+ m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1 ) );
+ SyncVerThumb();
+ FillView();
+ ShowVerSBar();
+ if( m_bSimpleTravel && m_pCursor && m_pView->HasFocus() )
+ m_pView->Select( m_pCursor );
+ ShowCursor( true );
+ if( bInvalidateCompleteView )
+ m_pView->Invalidate();
+ else
+ m_pView->Invalidate( GetVisibleArea() );
+}
+
+IMPL_LINK( SvImpLBox, ScrollLeftRightHdl, ScrollBar *, pScrollBar, void )
+{
+ long nDelta = pScrollBar->GetDelta();
+ if( nDelta )
+ {
+ if( m_pView->IsEditingActive() )
+ {
+ m_pView->EndEditing( true ); // Cancel
+ m_pView->PaintImmediately();
+ }
+ m_pView->nFocusWidth = -1;
+ KeyLeftRight( nDelta );
+ }
+}
+
+void SvImpLBox::KeyLeftRight( long nDelta )
+{
+ if( !(m_nFlags & LBoxFlags::InResize) )
+ m_pView->PaintImmediately();
+ BeginScroll();
+ m_nFlags &= ~LBoxFlags::Filling;
+ ShowCursor( false );
+
+ // calculate new origin
+ long nPos = m_aHorSBar->GetThumbPos();
+ Point aOrigin( -nPos, 0 );
+
+ MapMode aMapMode( m_pView->GetMapMode() );
+ aMapMode.SetOrigin( aOrigin );
+ m_pView->SetMapMode( aMapMode );
+
+ if( !(m_nFlags & LBoxFlags::InResize) )
+ {
+ tools::Rectangle aRect( GetVisibleArea() );
+ m_pView->Scroll( -nDelta, 0, aRect, ScrollFlags::NoChildren );
+ }
+ else
+ m_pView->Invalidate();
+ RecalcFocusRect();
+ ShowCursor( true );
+ m_pView->NotifyScrolled();
+}
+
+
+// returns the last entry if position is just past the last entry
+SvTreeListEntry* SvImpLBox::GetClickedEntry( const Point& rPoint ) const
+{
+ DBG_ASSERT( m_pView->GetModel(), "SvImpLBox::GetClickedEntry: how can this ever happen? Please tell me (frank.schoenheit@sun.com) how to reproduce!" );
+ if ( !m_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( m_pView->GetEntryCount() == 0 || !m_pStartEntry || !m_pView->GetEntryHeight())
+ return nullptr;
+
+ sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / m_pView->GetEntryHeight() );
+ sal_uInt16 nTemp = nClickedEntry;
+ SvTreeListEntry* pEntry = m_pView->NextVisible(m_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( m_pView->GetFocusRect( pEntry, nLine ));
+ aRect.SetRight( GetOutputSize().Width() - m_pView->GetMapMode().GetOrigin().X() );
+
+ SvLBoxContextBmp* pBmp = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
+ aRect.AdjustLeft( -pBmp->GetWidth(m_pView,pEntry) );
+ aRect.AdjustLeft( -4 ); // a little tolerance
+
+ Point aPos( rPosPixel );
+ aPos -= m_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( (m_pView->GetEntryCount() == 0) || !m_pStartEntry ||
+ (rPoint.Y() > m_aOutputSize.Height())
+ || !m_pView->GetEntryHeight())
+ return nullptr;
+
+ sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / m_pView->GetEntryHeight() );
+ sal_uInt16 nTemp = nClickedEntry;
+ SvTreeListEntry* pEntry = m_pView->NextVisible(m_pStartEntry, nTemp);
+ if( nTemp != nClickedEntry )
+ pEntry = nullptr;
+ return pEntry;
+}
+
+
+SvTreeListEntry* SvImpLBox::MakePointVisible(const Point& rPoint)
+{
+ if( !m_pCursor )
+ return nullptr;
+ long nY = rPoint.Y();
+ SvTreeListEntry* pEntry = nullptr;
+ long nMax = m_aOutputSize.Height();
+ if( nY < 0 || nY >= nMax ) // aOutputSize.Height() )
+ {
+ if( nY < 0 )
+ pEntry = m_pView->PrevVisible(m_pCursor);
+ else
+ pEntry = m_pView->NextVisible(m_pCursor);
+
+ if( pEntry && pEntry != m_pCursor )
+ m_pView->SetEntryFocus( m_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 = m_pView->NextVisible(m_pStartEntry, nSteps);
+ }
+ if( pEntry )
+ {
+ if( pEntry != m_pCursor &&
+ m_aSelEng.GetSelectionMode() == SelectionMode::Single
+ )
+ m_pView->Select( m_pCursor, false );
+ }
+ }
+ return pEntry;
+}
+
+tools::Rectangle SvImpLBox::GetClipRegionRect() const
+{
+ Point aOrigin( m_pView->GetMapMode().GetOrigin() );
+ aOrigin.setX( aOrigin.X() * -1 ); // conversion document coordinates
+ tools::Rectangle aClipRect( aOrigin, m_aOutputSize );
+ aClipRect.AdjustBottom( 1 );
+ return aClipRect;
+}
+
+
+void SvImpLBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ if (!m_pView->GetVisibleCount())
+ return;
+
+ m_nFlags |= LBoxFlags::InPaint;
+
+ if (m_nFlags & LBoxFlags::Filling)
+ {
+ SvTreeListEntry* pFirst = m_pView->First();
+ if (pFirst != m_pStartEntry)
+ {
+ ShowCursor(false);
+ m_pStartEntry = m_pView->First();
+ m_aVerSBar->SetThumbPos( 0 );
+ StopUserEvent();
+ ShowCursor(true);
+ m_nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
+ reinterpret_cast<void*>(1));
+ return;
+ }
+ }
+
+ if (!m_pStartEntry)
+ {
+ m_pStartEntry = m_pView->First();
+ }
+
+ if (m_nNodeBmpTabDistance == NODE_BMP_TABDIST_NOTVALID)
+ SetNodeBmpTabDistance();
+
+ long nRectHeight = rRect.GetHeight();
+ long nEntryHeight = m_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 = m_pStartEntry;
+ while (nStartLine && pEntry)
+ {
+ pEntry = m_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 (!m_pCursor && !mbNoAutoCurEntry)
+ {
+ // do not select if multiselection or explicit set
+ bool bNotSelect = (m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
+ SetCursor(m_pStartEntry, bNotSelect);
+ }
+
+ for(sal_uInt16 n=0; n< nCount && pEntry; n++)
+ {
+ /*long nMaxRight=*/
+ m_pView->PaintEntry1(*pEntry, nY, rRenderContext );
+ nY += nEntryHeight;
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+
+ m_nFlags &= ~LBoxFlags::DeselectAll;
+ rRenderContext.SetClipRegion();
+ m_nFlags &= ~LBoxFlags::InPaint;
+}
+
+void SvImpLBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
+{
+ if( !pEntry )
+ return;
+
+ bool bInView = IsEntryInView( pEntry );
+
+ if( bInView && (!bMoveToTop || m_pStartEntry == pEntry) )
+ return; // is already visible
+
+ if( m_pStartEntry || mbForceMakeVisible )
+ m_nFlags &= ~LBoxFlags::Filling;
+ if( !bInView )
+ {
+ if( !m_pView->IsEntryVisible(pEntry) ) // Parent(s) collapsed?
+ {
+ SvTreeListEntry* pParent = m_pView->GetParent( pEntry );
+ while( pParent )
+ {
+ if( !m_pView->IsExpanded( pParent ) )
+ {
+ bool bRet = m_pView->Expand( pParent );
+ DBG_ASSERT(bRet,"Not expanded!");
+ }
+ pParent = m_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
+ }
+ }
+
+ m_pStartEntry = pEntry;
+ ShowCursor( false );
+ FillView();
+ m_aVerSBar->SetThumbPos( static_cast<long>(m_pView->GetVisiblePos( m_pStartEntry )) );
+ ShowCursor( true );
+ m_pView->Invalidate();
+}
+
+void SvImpLBox::ScrollToAbsPos( long nPos )
+{
+ if( m_pView->GetVisibleCount() == 0 )
+ return;
+ long nLastEntryPos = m_pView->GetAbsPos( m_pView->Last() );
+
+ if( nPos < 0 )
+ nPos = 0;
+ else if( nPos > nLastEntryPos )
+ nPos = nLastEntryPos;
+
+ SvTreeListEntry* pEntry = m_pView->GetEntryAtAbsPos( nPos );
+ if( !pEntry || pEntry == m_pStartEntry )
+ return;
+
+ if( m_pStartEntry || mbForceMakeVisible )
+ m_nFlags &= ~LBoxFlags::Filling;
+
+ if( m_pView->IsEntryVisible(pEntry) )
+ {
+ m_pStartEntry = pEntry;
+ ShowCursor( false );
+ m_aVerSBar->SetThumbPos( nPos );
+ ShowCursor( true );
+ if (GetUpdateMode())
+ m_pView->Invalidate();
+ }
+}
+
+void SvImpLBox::DrawNet(vcl::RenderContext& rRenderContext)
+{
+ if (m_pView->GetVisibleCount() < 2 && !m_pStartEntry->HasChildrenOnDemand() &&
+ !m_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 = m_pView->GetEntryHeight();
+ long nEntryHeightDIV2 = nEntryHeight / 2;
+ if( nEntryHeightDIV2 && !(nEntryHeight & 0x0001))
+ nEntryHeightDIV2--;
+
+ SvTreeListEntry* pChild;
+ SvTreeListEntry* pEntry = m_pStartEntry;
+
+ SvLBoxTab* pFirstDynamicTab = m_pView->GetFirstDynamicTab();
+ while (m_pTree->GetDepth( pEntry ) > 0)
+ {
+ pEntry = m_pView->GetParent(pEntry);
+ }
+ sal_uInt16 nOffs = static_cast<sal_uInt16>(m_pView->GetVisiblePos(m_pStartEntry) - m_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 = m_nVisibleCount + nOffs + 1;
+
+ const Image& rExpandedNodeBitmap = GetExpandedNodeBmp();
+
+ for (sal_uLong n=0; n< nMax && pEntry; n++)
+ {
+ if (m_pView->IsExpanded(pEntry))
+ {
+ aPos1.setX( m_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 (!m_pView->nContextBmpWidthMax)
+ aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
+
+ aPos1.setY( nY );
+ aPos1.AdjustY(nEntryHeightDIV2 );
+
+ pChild = m_pView->FirstChild( pEntry );
+ assert(pChild && "Child?");
+ pChild = pChild->LastSibling();
+ nDistance = static_cast<sal_uInt16>(m_pView->GetVisiblePos(pChild) - m_pView->GetVisiblePos(pEntry));
+ aPos2 = aPos1;
+ aPos2.AdjustY(nDistance * nEntryHeight );
+ rRenderContext.DrawLine(aPos1, aPos2);
+ }
+ // visible in control?
+ if (n >= nOffs && ((m_nStyle & WB_HASLINESATROOT) || !m_pTree->IsAtRootDepth(pEntry)))
+ {
+ // can we recycle aPos1?
+ if (!m_pView->IsExpanded(pEntry))
+ {
+ // nope
+ aPos1.setX( m_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 (!m_pView->nContextBmpWidthMax)
+ aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
+ aPos1.setY( nY );
+ aPos1.AdjustY(nEntryHeightDIV2 );
+ aPos2.setX( aPos1.X() );
+ }
+ aPos2.setY( aPos1.Y() );
+ aPos2.AdjustX( -(m_pView->GetIndent()) );
+ rRenderContext.DrawLine(aPos1, aPos2);
+ }
+ nY += nEntryHeight;
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if (m_nStyle & WB_HASLINESATROOT)
+ {
+ pEntry = m_pView->First();
+ aPos1.setX( m_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 (!m_pView->nContextBmpWidthMax)
+ aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
+ aPos1.AdjustX( -(m_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( m_nVerSBarWidth, rSize.Height() );
+ Size aHorSize( rSize.Width(), m_nHorSBarHeight );
+
+ if( nMask & 0x0001 )
+ aHorSize.AdjustWidth( -m_nVerSBarWidth );
+ if( nMask & 0x0002 )
+ aVerSize.AdjustHeight( -m_nHorSBarHeight );
+
+ aVerSize.AdjustHeight(2 * nOverlap );
+ Point aVerPos( rSize.Width() - aVerSize.Width() + nOverlap, -nOverlap );
+ m_aVerSBar->SetPosSizePixel( aVerPos, aVerSize );
+
+ aHorSize.AdjustWidth(2 * nOverlap );
+ Point aHorPos( -nOverlap, rSize.Height() - aHorSize.Height() + nOverlap );
+
+ m_aHorSBar->SetPosSizePixel( aHorPos, aHorSize );
+
+ if( nMask & 0x0001 )
+ rSize.setWidth( aVerPos.X() );
+ if( nMask & 0x0002 )
+ rSize.setHeight( aHorPos.Y() );
+
+ if( (nMask & (0x0001|0x0002)) == (0x0001|0x0002) )
+ m_aScrBarBox->Show();
+ else
+ m_aScrBarBox->Hide();
+}
+
+void SvImpLBox::AdjustScrollBars( Size& rSize )
+{
+ long nEntryHeight = m_pView->GetEntryHeight();
+ if( !nEntryHeight )
+ return;
+
+ sal_uInt16 nResult = 0;
+
+ Size aOSize( m_pView->Control::GetOutputSizePixel() );
+
+ const WinBits nWindowStyle = m_pView->GetStyle();
+ bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;
+ bool bHorBar = false;
+ long nMaxRight = aOSize.Width(); //GetOutputSize().Width();
+ Point aOrigin( m_pView->GetMapMode().GetOrigin() );
+ aOrigin.setX( aOrigin.X() * -1 );
+ nMaxRight += aOrigin.X() - 1;
+ long nVis = m_nMostRight - aOrigin.X();
+ if( (nWindowStyle & (WB_AUTOHSCROLL|WB_HSCROLL)) &&
+ (nVis < m_nMostRight || nMaxRight < m_nMostRight) )
+ {
+ bHorBar = true;
+ }
+
+ // number of entries that are not collapsed
+ sal_uLong nTotalCount = m_pView->GetVisibleCount();
+
+ // number of entries visible within the view
+ m_nVisibleCount = aOSize.Height() / nEntryHeight;
+
+ // do we need a vertical scrollbar?
+ if( bVerSBar || nTotalCount > m_nVisibleCount )
+ {
+ nResult = 1;
+ nMaxRight -= m_nVerSBarWidth;
+ if( !bHorBar )
+ {
+ if( (nWindowStyle & (WB_AUTOHSCROLL|WB_HSCROLL)) &&
+ (nVis < m_nMostRight || nMaxRight < m_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.
+ m_nVisibleCount = (aOSize.Height() - m_nHorSBarHeight) / nEntryHeight;
+ // we might actually need a vertical scrollbar now
+ if( !(nResult & 0x0001) &&
+ ((nTotalCount > m_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 );
+ m_aSelEng.SetVisibleArea( aRect );
+
+ // vertical scrollbar
+ long nTemp = static_cast<long>(m_nVisibleCount);
+ nTemp--;
+ if( nTemp != m_aVerSBar->GetVisibleSize() )
+ {
+ if( !m_bInVScrollHdl )
+ {
+ m_aVerSBar->SetPageSize( nTemp - 1 );
+ m_aVerSBar->SetVisibleSize( nTemp );
+ }
+ else
+ {
+ m_nFlags |= LBoxFlags::EndScrollSetVisSize;
+ m_nNextVerVisSize = nTemp;
+ }
+ }
+
+ // horizontal scrollbar
+ nTemp = m_aHorSBar->GetThumbPos();
+ m_aHorSBar->SetVisibleSize( aOSize.Width() );
+ long nNewThumbPos = m_aHorSBar->GetThumbPos();
+ Range aRange( m_aHorSBar->GetRange() );
+ if( aRange.Max() < m_nMostRight+25 )
+ {
+ aRange.Max() = m_nMostRight+25;
+ m_aHorSBar->SetRange( aRange );
+ }
+
+ if( nTemp != nNewThumbPos )
+ {
+ nTemp = nNewThumbPos - nTemp;
+ if( m_pView->IsEditingActive() )
+ {
+ m_pView->EndEditing( true ); // Cancel
+ m_pView->PaintImmediately();
+ }
+ m_pView->nFocusWidth = -1;
+ KeyLeftRight( nTemp );
+ }
+
+ if( nResult & 0x0001 )
+ m_aVerSBar->Show();
+ else
+ m_aVerSBar->Hide();
+
+ if( nResult & 0x0002 )
+ m_aHorSBar->Show();
+ else
+ {
+ m_aHorSBar->Hide();
+ }
+ rSize = aOSize;
+}
+
+void SvImpLBox::InitScrollBarBox()
+{
+ m_aScrBarBox->SetSizePixel( Size(m_nVerSBarWidth, m_nHorSBarHeight) );
+ Size aSize( m_pView->Control::GetOutputSizePixel() );
+ m_aScrBarBox->SetPosPixel( Point(aSize.Width()-m_nVerSBarWidth, aSize.Height()-m_nHorSBarHeight));
+}
+
+void SvImpLBox::Resize()
+{
+ m_aOutputSize = m_pView->Control::GetOutputSizePixel();
+ if( m_aOutputSize.IsEmpty() )
+ return;
+ m_nFlags |= LBoxFlags::InResize;
+ InitScrollBarBox();
+
+ if( m_pView->GetEntryHeight())
+ {
+ AdjustScrollBars( m_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( m_aHorSBar->IsVisible())
+ m_aHorSBar->Invalidate();
+ if( m_aVerSBar->IsVisible())
+ m_aVerSBar->Invalidate();
+ m_nFlags &= ~LBoxFlags::InResize;
+}
+
+void SvImpLBox::FillView()
+{
+ if( !m_pStartEntry )
+ {
+ sal_uLong nVisibleViewCount = m_pView->GetVisibleCount();
+ long nTempThumb = m_aVerSBar->GetThumbPos();
+ if( nTempThumb < 0 )
+ nTempThumb = 0;
+ else if( o3tl::make_unsigned(nTempThumb) >= nVisibleViewCount )
+ nTempThumb = nVisibleViewCount == 0 ? 0 : nVisibleViewCount - 1;
+ m_pStartEntry = m_pView->GetEntryAtVisPos(nTempThumb);
+ }
+ if( !m_pStartEntry )
+ return;
+
+ sal_uInt16 nLast = static_cast<sal_uInt16>(m_pView->GetVisiblePos(m_pView->LastVisible()));
+ sal_uInt16 nThumb = static_cast<sal_uInt16>(m_pView->GetVisiblePos( m_pStartEntry ));
+ sal_uLong nCurDispEntries = nLast-nThumb+1;
+ if( nCurDispEntries >= m_nVisibleCount )
+ return;
+
+ ShowCursor( false );
+ // fill window by moving the thumb up incrementally
+ bool bFound = false;
+ SvTreeListEntry* pTemp = m_pStartEntry;
+ while( nCurDispEntries < m_nVisibleCount && pTemp )
+ {
+ pTemp = m_pView->PrevVisible(m_pStartEntry);
+ if( pTemp )
+ {
+ nThumb--;
+ m_pStartEntry = pTemp;
+ nCurDispEntries++;
+ bFound = true;
+ }
+ }
+ if( bFound )
+ {
+ m_aVerSBar->SetThumbPos( nThumb );
+ ShowCursor( true ); // recalculate focus rectangle
+ m_pView->Invalidate();
+ }
+}
+
+
+void SvImpLBox::ShowVerSBar()
+{
+ bool bVerBar = ( m_pView->GetStyle() & WB_VSCROLL ) != 0;
+ sal_uLong nVis = 0;
+ if( !bVerBar )
+ nVis = m_pView->GetVisibleCount();
+ if( bVerBar || (m_nVisibleCount && nVis > static_cast<sal_uLong>(m_nVisibleCount-1)) )
+ {
+ if( !m_aVerSBar->IsVisible() )
+ {
+ m_pView->nFocusWidth = -1;
+ AdjustScrollBars( m_aOutputSize );
+ if( GetUpdateMode() )
+ m_aVerSBar->Invalidate();
+ }
+ }
+ else
+ {
+ if( m_aVerSBar->IsVisible() )
+ {
+ m_pView->nFocusWidth = -1;
+ AdjustScrollBars( m_aOutputSize );
+ }
+ }
+
+ long nMaxRight = GetOutputSize().Width();
+ Point aPos( m_pView->GetMapMode().GetOrigin() );
+ aPos.setX( aPos.X() * -1 ); // convert document coordinates
+ nMaxRight = nMaxRight + aPos.X() - 1;
+ if( nMaxRight < m_nMostRight )
+ {
+ if( !m_aHorSBar->IsVisible() )
+ {
+ m_pView->nFocusWidth = -1;
+ AdjustScrollBars( m_aOutputSize );
+ if( GetUpdateMode() )
+ m_aHorSBar->Invalidate();
+ }
+ else
+ {
+ Range aRange( m_aHorSBar->GetRange() );
+ if( aRange.Max() < m_nMostRight+25 )
+ {
+ aRange.Max() = m_nMostRight+25;
+ m_aHorSBar->SetRange( aRange );
+ }
+ else
+ {
+ m_pView->nFocusWidth = -1;
+ AdjustScrollBars( m_aOutputSize );
+ }
+ }
+ }
+ else
+ {
+ if( m_aHorSBar->IsVisible() )
+ {
+ m_pView->nFocusWidth = -1;
+ AdjustScrollBars( m_aOutputSize );
+ }
+ }
+}
+
+
+void SvImpLBox::SyncVerThumb()
+{
+ if( m_pStartEntry )
+ {
+ long nEntryPos = m_pView->GetVisiblePos( m_pStartEntry );
+ m_aVerSBar->SetThumbPos( nEntryPos );
+ }
+ else
+ m_aVerSBar->SetThumbPos( 0 );
+}
+
+bool SvImpLBox::IsEntryInView( SvTreeListEntry* pEntry ) const
+{
+ // parent collapsed
+ if( !m_pView->IsEntryVisible(pEntry) )
+ return false;
+ long nY = GetEntryLine( pEntry );
+ if( nY < 0 )
+ return false;
+ long nMax = m_nVisibleCount * m_pView->GetEntryHeight();
+ return nY < nMax;
+}
+
+
+long SvImpLBox::GetEntryLine(const SvTreeListEntry* pEntry) const
+{
+ if(!m_pStartEntry )
+ return -1; // invisible position
+
+ long nFirstVisPos = m_pView->GetVisiblePos( m_pStartEntry );
+ long nEntryVisPos = m_pView->GetVisiblePos( pEntry );
+ nFirstVisPos = nEntryVisPos - nFirstVisPos;
+ nFirstVisPos *= m_pView->GetEntryHeight();
+ return nFirstVisPos;
+}
+
+void SvImpLBox::SetEntryHeight()
+{
+ SetNodeBmpWidth( GetExpandedNodeBmp() );
+ SetNodeBmpWidth( GetCollapsedNodeBmp() );
+ if(!m_pView->HasViewData()) // are we within the Clear?
+ {
+ Size aSize = m_pView->Control::GetOutputSizePixel();
+ AdjustScrollBars( aSize );
+ }
+ else
+ {
+ Resize();
+ if( GetUpdateMode() )
+ m_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 );
+ }
+ m_aVerSBar->SetRange( Range(0, m_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( !m_pView->IsEntryVisible( pEntry ) )
+ return;
+
+ ShowCursor( false );
+
+ if( !m_pMostRightEntry || m_pTree->IsChild( pEntry,m_pMostRightEntry ) )
+ {
+ FindMostRight();
+ }
+
+ if( m_pStartEntry )
+ {
+ long nOldThumbPos = m_aVerSBar->GetThumbPos();
+ sal_uLong nVisList = m_pView->GetVisibleCount();
+ m_aVerSBar->SetRange( Range(0, nVisList-1) );
+ long nNewThumbPos = m_aVerSBar->GetThumbPos();
+ if( nNewThumbPos != nOldThumbPos )
+ {
+ m_pStartEntry = m_pView->First();
+ sal_uInt16 nDistance = static_cast<sal_uInt16>(nNewThumbPos);
+ if( nDistance )
+ m_pStartEntry = m_pView->NextVisible(m_pStartEntry, nDistance);
+ if( GetUpdateMode() )
+ m_pView->Invalidate();
+ }
+ else
+ SyncVerThumb();
+ ShowVerSBar();
+ }
+ // has the cursor been collapsed?
+ if( m_pTree->IsChild( pEntry, m_pCursor ) )
+ SetCursor( pEntry );
+ if( GetUpdateMode() )
+ ShowVerSBar();
+ ShowCursor( true );
+ if( GetUpdateMode() && m_pCursor )
+ m_pView->Select( m_pCursor );
+}
+
+void SvImpLBox::CollapsingEntry( SvTreeListEntry* pEntry )
+{
+ if( !m_pView->IsEntryVisible( pEntry ) || !m_pStartEntry )
+ return;
+
+ SelAllDestrAnch( false ); // deselect all
+
+ // is the collapsed cursor visible?
+ long nY = GetEntryLine( pEntry );
+ if( IsLineVisible(nY) )
+ {
+ if( GetUpdateMode() )
+ InvalidateEntriesFrom( nY );
+ }
+ else
+ {
+ if( m_pTree->IsChild(pEntry, m_pStartEntry) )
+ {
+ m_pStartEntry = pEntry;
+ if( GetUpdateMode() )
+ m_pView->Invalidate();
+ }
+ }
+}
+
+
+void SvImpLBox::SetNodeBmpWidth( const Image& rBmp )
+{
+ const Size aSize( rBmp.GetSizePixel() );
+ m_nNodeBmpWidth = aSize.Width();
+}
+
+void SvImpLBox::SetNodeBmpTabDistance()
+{
+ m_nNodeBmpTabDistance = -m_pView->GetIndent();
+ if( m_pView->nContextBmpWidthMax )
+ {
+ // only if the first dynamic tab is centered (we currently assume that)
+ Size aSize = GetExpandedNodeBmp().GetSizePixel();
+ m_nNodeBmpTabDistance -= aSize.Width() / 2;
+ }
+}
+
+
+// corrects the cursor when using SingleSelection
+
+void SvImpLBox::EntrySelected( SvTreeListEntry* pEntry, bool bSelect )
+{
+ if( m_nFlags & LBoxFlags::IgnoreSelect )
+ return;
+
+ m_nFlags &= ~LBoxFlags::DeselectAll;
+ if( bSelect &&
+ m_aSelEng.GetSelectionMode() == SelectionMode::Single &&
+ pEntry != m_pCursor )
+ {
+ SetCursor( pEntry );
+ DBG_ASSERT(m_pView->GetSelectionCount()==1,"selection count?");
+ }
+
+ if( GetUpdateMode() && m_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( !m_pView->IsEntryVisible( pEntry ) )
+ {
+ // if parent is collapsed => bye!
+ m_nFlags |= LBoxFlags::RemovedEntryInvisible;
+ return;
+ }
+
+ if( pEntry == m_pMostRightEntry || (
+ pEntry->HasChildren() && m_pView->IsExpanded(pEntry) &&
+ m_pTree->IsChild(pEntry, m_pMostRightEntry)))
+ {
+ m_nFlags |= LBoxFlags::RemovedRecalcMostRight;
+ }
+
+ SvTreeListEntry* pOldStartEntry = m_pStartEntry;
+
+ SvTreeListEntry* pParent = m_pView->GetModel()->GetParent(pEntry);
+
+ if (pParent && m_pView->GetModel()->GetChildList(pParent).size() == 1)
+ {
+ DBG_ASSERT( m_pView->IsExpanded( pParent ), "Parent not expanded");
+ pParent->SetFlags( pParent->GetFlags() | SvTLEntryFlags::NO_NODEBMP);
+ InvalidateEntry( pParent );
+ }
+
+ if( m_pCursor && m_pTree->IsChild( pEntry, m_pCursor) )
+ m_pCursor = pEntry;
+ if( m_pStartEntry && m_pTree->IsChild(pEntry,m_pStartEntry) )
+ m_pStartEntry = pEntry;
+
+ SvTreeListEntry* pTemp;
+ if( m_pCursor && m_pCursor == pEntry )
+ {
+ if( m_bSimpleTravel )
+ m_pView->Select( m_pCursor, false );
+ ShowCursor( false ); // focus rectangle gone
+ // NextSibling, because we also delete the children of the cursor
+ pTemp = m_pCursor->NextSibling();
+ if( !pTemp )
+ pTemp = m_pView->PrevVisible(m_pCursor);
+
+ SetCursor( pTemp, true );
+ }
+ if( m_pStartEntry && m_pStartEntry == pEntry )
+ {
+ pTemp = m_pStartEntry->NextSibling();
+ if( !pTemp )
+ pTemp = m_pView->PrevVisible(m_pStartEntry);
+ m_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( m_pStartEntry && (m_pStartEntry != pOldStartEntry || pEntry == m_pView->GetModel()->Last()) )
+ {
+ m_aVerSBar->SetThumbPos( m_pView->GetVisiblePos( m_pStartEntry ));
+ m_pView->Invalidate( GetVisibleArea() );
+ }
+ else
+ InvalidateEntriesFrom( GetEntryLine( pEntry ) );
+ }
+}
+
+void SvImpLBox::EntryRemoved()
+{
+ if( m_nFlags & LBoxFlags::RemovedEntryInvisible )
+ {
+ m_nFlags &= ~LBoxFlags::RemovedEntryInvisible;
+ return;
+ }
+ if( !m_pStartEntry )
+ m_pStartEntry = m_pTree->First();
+ if( !m_pCursor )
+ SetCursor( m_pStartEntry, true );
+
+ if( m_pCursor && (m_bSimpleTravel || !m_pView->GetSelectionCount() ))
+ m_pView->Select( m_pCursor );
+
+ if( GetUpdateMode())
+ {
+ if( m_nFlags & LBoxFlags::RemovedRecalcMostRight )
+ FindMostRight();
+ m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1 ) );
+ FillView();
+ if( m_pStartEntry )
+ // if something above the thumb was deleted
+ m_aVerSBar->SetThumbPos( m_pView->GetVisiblePos( m_pStartEntry) );
+
+ ShowVerSBar();
+ if( m_pCursor && m_pView->HasFocus() && !m_pView->IsSelected(m_pCursor) )
+ {
+ if( m_pView->GetSelectionCount() )
+ {
+ // is a neighboring entry selected?
+ SvTreeListEntry* pNextCursor = m_pView->PrevVisible( m_pCursor );
+ if( !pNextCursor || !m_pView->IsSelected( pNextCursor ))
+ pNextCursor = m_pView->NextVisible( m_pCursor );
+ if( !pNextCursor || !m_pView->IsSelected( pNextCursor ))
+ // no neighbor selected: use first selected
+ pNextCursor = m_pView->FirstSelected();
+ SetCursor( pNextCursor );
+ MakeVisible( m_pCursor );
+ }
+ else
+ m_pView->Select( m_pCursor );
+ }
+ ShowCursor( true );
+ }
+ m_nFlags &= ~LBoxFlags::RemovedRecalcMostRight;
+}
+
+
+void SvImpLBox::MovingEntry( SvTreeListEntry* pEntry )
+{
+ bool bDeselAll(m_nFlags & LBoxFlags::DeselectAll);
+ SelAllDestrAnch( false ); // DeselectAll();
+ if( !bDeselAll )
+ m_nFlags &= ~LBoxFlags::DeselectAll;
+
+ if( pEntry == m_pCursor )
+ ShowCursor( false );
+ if( IsEntryInView( pEntry ) )
+ m_pView->Invalidate();
+ if( pEntry != m_pStartEntry )
+ return;
+
+ SvTreeListEntry* pNew = nullptr;
+ if( !pEntry->HasChildren() )
+ {
+ pNew = m_pView->NextVisible(m_pStartEntry);
+ if( !pNew )
+ pNew = m_pView->PrevVisible(m_pStartEntry);
+ }
+ else
+ {
+ pNew = pEntry->NextSibling();
+ if( !pNew )
+ pNew = pEntry->PrevSibling();
+ }
+ m_pStartEntry = pNew;
+}
+
+void SvImpLBox::EntryMoved( SvTreeListEntry* pEntry )
+{
+ UpdateContextBmpWidthVectorFromMovedEntry( pEntry );
+
+ if ( !m_pStartEntry )
+ // this might happen if the only entry in the view is moved to its very same position
+ // #i97346#
+ m_pStartEntry = m_pView->First();
+
+ m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1));
+ sal_uInt16 nFirstPos = static_cast<sal_uInt16>(m_pTree->GetAbsPos( m_pStartEntry ));
+ sal_uInt16 nNewPos = static_cast<sal_uInt16>(m_pTree->GetAbsPos( pEntry ));
+ FindMostRight();
+ if( nNewPos < nFirstPos ) // HACK!
+ m_pStartEntry = pEntry;
+ SyncVerThumb();
+ if( pEntry == m_pCursor )
+ {
+ if( m_pView->IsEntryVisible( m_pCursor ) )
+ ShowCursor( true );
+ else
+ {
+ SvTreeListEntry* pParent = pEntry;
+ do {
+ pParent = m_pTree->GetParent( pParent );
+ }
+ while( !m_pView->IsEntryVisible( pParent ) );
+ SetCursor( pParent );
+ }
+ }
+ if( IsEntryInView( pEntry ) )
+ m_pView->Invalidate();
+}
+
+
+void SvImpLBox::EntryInserted( SvTreeListEntry* pEntry )
+{
+ if( !GetUpdateMode() )
+ return;
+
+ SvTreeListEntry* pParent = m_pTree->GetParent(pEntry);
+ if (pParent && m_pTree->GetChildList(pParent).size() == 1)
+ // draw plus sign
+ m_pTree->InvalidateEntry( pParent );
+
+ if( !m_pView->IsEntryVisible( pEntry ) )
+ return;
+ bool bDeselAll(m_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 -= m_pView->GetEntryHeight(); // because of lines
+ InvalidateEntriesFrom( nY );
+ }
+ else if( m_pStartEntry && nY < GetEntryLine(m_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>(m_pView->GetVisiblePos(m_pView->LastVisible()));
+ sal_uInt16 nThumb = static_cast<sal_uInt16>(m_pView->GetVisiblePos( m_pStartEntry ));
+ sal_uInt16 nCurDispEntries = nLast-nThumb+1;
+ if( nCurDispEntries < m_nVisibleCount )
+ {
+ // set at the next paint event
+ m_pStartEntry = nullptr;
+ SetCursor( nullptr );
+ m_pView->Invalidate();
+ }
+ }
+ else if( !m_pStartEntry )
+ m_pView->Invalidate();
+
+ SetMostRight( pEntry );
+ m_aVerSBar->SetRange( Range(0, m_pView->GetVisibleCount()-1));
+ SyncVerThumb(); // if something was inserted before the thumb
+ ShowVerSBar();
+ ShowCursor( true );
+ if( m_pStartEntry != m_pView->First() && (m_nFlags & LBoxFlags::Filling) )
+ m_pView->PaintImmediately();
+}
+
+
+// ********************************************************************
+// Event handler
+// ********************************************************************
+
+
+// ****** Control the control animation
+
+bool SvImpLBox::ButtonDownCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry* pEntry)
+{
+ SvLBoxItem* pItem = m_pView->GetItem(pEntry,rMEvt.GetPosPixel().X(),&m_pActiveTab);
+ if (pItem && pItem->GetType() == SvLBoxItemType::Button)
+ {
+ m_pActiveButton = static_cast<SvLBoxButton*>(pItem);
+ m_pActiveEntry = pEntry;
+ if( m_pCursor == m_pActiveEntry )
+ m_pView->HideFocus();
+ m_pView->CaptureMouse();
+ m_pActiveButton->SetStateHilighted( true );
+ InvalidateEntry(m_pActiveEntry);
+ return true;
+ }
+ else
+ m_pActiveButton = nullptr;
+ return false;
+}
+
+bool SvImpLBox::MouseMoveCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry const * pEntry)
+{
+ if( m_pActiveButton )
+ {
+ long nMouseX = rMEvt.GetPosPixel().X();
+ if( pEntry == m_pActiveEntry &&
+ m_pView->GetItem(m_pActiveEntry, nMouseX) == m_pActiveButton )
+ {
+ if( !m_pActiveButton->IsStateHilighted() )
+ {
+ m_pActiveButton->SetStateHilighted(true );
+ InvalidateEntry(m_pActiveEntry);
+ }
+ }
+ else
+ {
+ if( m_pActiveButton->IsStateHilighted() )
+ {
+ m_pActiveButton->SetStateHilighted(false );
+ InvalidateEntry(m_pActiveEntry);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SvImpLBox::ButtonUpCheckCtrl( const MouseEvent& rMEvt )
+{
+ if( m_pActiveButton )
+ {
+ m_pView->ReleaseMouse();
+ SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
+ m_pActiveButton->SetStateHilighted( false );
+ long nMouseX = rMEvt.GetPosPixel().X();
+ if (pEntry == m_pActiveEntry && m_pView->GetItem(m_pActiveEntry, nMouseX) == m_pActiveButton)
+ m_pActiveButton->ClickHdl(m_pActiveEntry);
+ InvalidateEntry(m_pActiveEntry);
+ if (m_pCursor == m_pActiveEntry)
+ ShowCursor(true);
+ m_pActiveButton = nullptr;
+ m_pActiveEntry = nullptr;
+ m_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 = m_pView->GetFirstDynamicTab();
+ if( !pFirstDynamicTab )
+ return false;
+
+ long nMouseX = rPosPixel.X();
+ // convert to document coordinates
+ Point aOrigin( m_pView->GetMapMode().GetOrigin() );
+ nMouseX -= aOrigin.X();
+
+ long nX = m_pView->GetTabPos( pEntry, pFirstDynamicTab);
+ nX += m_nNodeBmpTabDistance;
+ if( nMouseX < nX )
+ return false;
+ nX += m_nNodeBmpWidth;
+ return nMouseX <= nX;
+}
+
+// false == hit no node button
+bool SvImpLBox::ButtonDownCheckExpand( const MouseEvent& rMEvt, SvTreeListEntry* pEntry )
+{
+ bool bRet = false;
+
+ if ( m_pView->IsEditingActive() && pEntry == m_pView->pEdEntry )
+ // inplace editing -> nothing to do
+ bRet = true;
+ else if ( IsNodeButton( rMEvt.GetPosPixel(), pEntry ) )
+ {
+ if ( m_pView->IsExpanded( pEntry ) )
+ {
+ m_pView->EndEditing( true );
+ m_pView->Collapse( pEntry );
+ }
+ else
+ {
+ // you can expand an entry, which is in editing
+ m_pView->Expand( pEntry );
+ }
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void SvImpLBox::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( !rMEvt.IsLeft() && !rMEvt.IsRight())
+ return;
+
+ m_aEditIdle.Stop();
+ Point aPos( rMEvt.GetPosPixel());
+
+ if( aPos.X() > m_aOutputSize.Width() || aPos.Y() > m_aOutputSize.Height() )
+ return;
+
+ if( !m_pCursor )
+ m_pCursor = m_pStartEntry;
+ m_nFlags &= ~LBoxFlags::Filling;
+ m_pView->GrabFocus();
+ //fdo#82270 Grabbing focus can invalidate the entries, re-fetch
+ SvTreeListEntry* pEntry = GetEntry(aPos);
+ // the entry can still be invalid!
+ if( !pEntry || !m_pView->GetViewData( pEntry ))
+ return;
+
+ long nY = GetEntryLine( pEntry );
+ // Node-Button?
+ if( ButtonDownCheckExpand( rMEvt, pEntry ) )
+ return;
+
+ if( !EntryReallyHit(pEntry,aPos,nY))
+ return;
+
+ SvLBoxItem* pXItem = m_pView->GetItem( pEntry, aPos.X() );
+ if( pXItem )
+ {
+ SvLBoxTab* pXTab = m_pView->GetTab( pEntry, pXItem );
+ if ( !rMEvt.IsMod1() && !rMEvt.IsMod2() && rMEvt.IsLeft() && pXTab->IsEditable()
+ && pEntry == m_pView->FirstSelected() && nullptr == m_pView->NextSelected( pEntry ) )
+ // #i8234# FirstSelected() and NextSelected() ensures, that inplace editing is only triggered, when only one entry is selected
+ m_nFlags |= LBoxFlags::StartEditTimer;
+ if ( !m_pView->IsSelected( pEntry ) )
+ m_nFlags &= ~LBoxFlags::StartEditTimer;
+ }
+
+
+ if( (rMEvt.GetClicks() % 2) == 0)
+ {
+ m_nFlags &= ~LBoxFlags::StartEditTimer;
+ m_pView->pHdlEntry = pEntry;
+ if( !m_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 != m_pView->pHdlEntry )
+ {
+ // select anew & bye
+ if( !m_bSimpleTravel && !m_aSelEng.IsAlwaysAdding())
+ SelAllDestrAnch( false ); // DeselectAll();
+ SetCursor( pEntry );
+
+ return;
+ }
+ if( pEntry->HasChildren() || pEntry->HasChildrenOnDemand() )
+ {
+ if( m_pView->IsExpanded(pEntry) )
+ m_pView->Collapse( pEntry );
+ else
+ m_pView->Expand( pEntry );
+ if( pEntry == m_pCursor ) // only if Entryitem was clicked
+ // (Nodebutton is not an Entryitem!)
+ m_pView->Select( m_pCursor );
+ return;
+ }
+ }
+ }
+ else
+ {
+ // CheckButton? (TreeListBox: Check + Info)
+ if( ButtonDownCheckCtrl(rMEvt, pEntry) )
+ return;
+ // Inplace-Editing?
+ }
+ if ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE
+ && !rMEvt.IsRight() ) // tdf#128824
+ m_aSelEng.SelMouseButtonDown( rMEvt );
+}
+
+void SvImpLBox::MouseButtonUp( const MouseEvent& rMEvt)
+{
+ if ( !ButtonUpCheckCtrl( rMEvt ) && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
+ m_aSelEng.SelMouseButtonUp( rMEvt );
+ EndScroll();
+ if( m_nFlags & LBoxFlags::StartEditTimer )
+ {
+ m_nFlags &= ~LBoxFlags::StartEditTimer;
+ m_aEditClickPos = rMEvt.GetPosPixel();
+ m_aEditIdle.Start();
+ }
+
+ if (m_pView->mbActivateOnSingleClick)
+ m_pView->DoubleClickHdl();
+}
+
+void SvImpLBox::MouseMove( const MouseEvent& rMEvt)
+{
+ SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
+ if ( !MouseMoveCheckCtrl( rMEvt, pEntry ) && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
+ m_aSelEng.SelMouseMove( rMEvt );
+}
+
+void SvImpLBox::ExpandAll()
+{
+ sal_uInt16 nRefDepth = m_pTree->GetDepth(m_pCursor);
+ SvTreeListEntry* pCur = m_pTree->Next(m_pCursor);
+ while (pCur && m_pTree->GetDepth(pCur) > nRefDepth)
+ {
+ if (pCur->HasChildren() && !m_pView->IsExpanded(pCur))
+ m_pView->Expand(pCur);
+ pCur = m_pTree->Next(pCur);
+ }
+}
+
+void SvImpLBox::CollapseTo(SvTreeListEntry* pParentToCollapse)
+{
+ // collapse all parents until we get to the given parent to collapse
+ if (pParentToCollapse)
+ {
+ sal_uInt16 nRefDepth;
+ // special case explorer: if the root only has a single
+ // entry, don't collapse the root entry
+ if (m_pTree->GetChildList(nullptr).size() < 2)
+ {
+ nRefDepth = 1;
+ pParentToCollapse = m_pCursor;
+ while (m_pTree->GetParent(pParentToCollapse)
+ && m_pTree->GetDepth(m_pTree->GetParent(pParentToCollapse)) > 0)
+ {
+ pParentToCollapse = m_pTree->GetParent(pParentToCollapse);
+ }
+ }
+ else
+ nRefDepth = m_pTree->GetDepth(pParentToCollapse);
+
+ if (m_pView->IsExpanded(pParentToCollapse))
+ m_pView->Collapse(pParentToCollapse);
+ SvTreeListEntry* pCur = m_pTree->Next(pParentToCollapse);
+ while (pCur && m_pTree->GetDepth(pCur) > nRefDepth)
+ {
+ if (pCur->HasChildren() && m_pView->IsExpanded(pCur))
+ m_pView->Collapse(pCur);
+ pCur = m_pTree->Next(pCur);
+ }
+ }
+}
+
+bool SvImpLBox::KeyInput( const KeyEvent& rKEvt)
+{
+ m_aEditIdle.Stop();
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+ if( rKeyCode.IsMod2() )
+ return false; // don't evaluate Alt key
+
+ m_nFlags &= ~LBoxFlags::Filling;
+
+ if( !m_pCursor )
+ m_pCursor = m_pStartEntry;
+ if( !m_pCursor )
+ return false;
+
+ bool bKeyUsed = true;
+
+ sal_uInt16 nDelta = static_cast<sal_uInt16>(m_aVerSBar->GetPageSize());
+ sal_uInt16 aCode = rKeyCode.GetCode();
+
+ bool bShift = rKeyCode.IsShift();
+ bool bMod1 = rKeyCode.IsMod1();
+
+ SvTreeListEntry* pNewCursor;
+
+ switch( aCode )
+ {
+ case KEY_UP:
+ if( !IsEntryInView( m_pCursor ) )
+ MakeVisible( m_pCursor );
+
+ pNewCursor = m_pCursor;
+ do
+ {
+ pNewCursor = m_pView->PrevVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+
+ // 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 = m_pCursor;
+
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ if( !IsEntryInView( pNewCursor ) )
+ KeyUp( false );
+ break;
+
+ case KEY_DOWN:
+ if( !IsEntryInView( m_pCursor ) )
+ MakeVisible( m_pCursor );
+
+ pNewCursor = m_pCursor;
+ do
+ {
+ pNewCursor = m_pView->NextVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+
+ // 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 && m_pCursor )
+ pNewCursor = m_pCursor;
+
+ if( pNewCursor )
+ {
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ if( IsEntryInView( pNewCursor ) )
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ else
+ {
+ if( m_pCursor )
+ m_pView->Select( m_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( m_bSubLstOpLR )
+ {
+ // only try to expand if sublist is expandable,
+ // otherwise ignore the key press
+ if( IsExpandable() && !m_pView->IsExpanded( m_pCursor ) )
+ m_pView->Expand( m_pCursor );
+ }
+ else if (m_aHorSBar->IsVisible())
+ {
+ long nThumb = m_aHorSBar->GetThumbPos();
+ nThumb += m_aHorSBar->GetLineSize();
+ long nOldThumb = m_aHorSBar->GetThumbPos();
+ m_aHorSBar->SetThumbPos( nThumb );
+ nThumb = nOldThumb;
+ nThumb -= m_aHorSBar->GetThumbPos();
+ nThumb *= -1;
+ if( nThumb )
+ {
+ KeyLeftRight( nThumb );
+ EndScroll();
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+ }
+
+ case KEY_LEFT:
+ {
+ if (m_aHorSBar->IsVisible())
+ {
+ long nThumb = m_aHorSBar->GetThumbPos();
+ nThumb -= m_aHorSBar->GetLineSize();
+ long nOldThumb = m_aHorSBar->GetThumbPos();
+ m_aHorSBar->SetThumbPos( nThumb );
+ nThumb = nOldThumb;
+ nThumb -= m_aHorSBar->GetThumbPos();
+ if( nThumb )
+ {
+ KeyLeftRight( -nThumb );
+ EndScroll();
+ }
+ else if( m_bSubLstOpLR )
+ {
+ if( IsExpandable() && m_pView->IsExpanded( m_pCursor ) )
+ m_pView->Collapse( m_pCursor );
+ else
+ {
+ pNewCursor = m_pView->GetParent( m_pCursor );
+ if( pNewCursor )
+ SetCursor( pNewCursor );
+ }
+ }
+ }
+ else if( m_bSubLstOpLR )
+ {
+ if( IsExpandable() && m_pView->IsExpanded( m_pCursor ) )
+ m_pView->Collapse( m_pCursor );
+ else
+ {
+ pNewCursor = m_pView->GetParent( m_pCursor );
+ if( pNewCursor )
+ SetCursor( pNewCursor );
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+ }
+
+ case KEY_PAGEUP:
+ if( !bMod1 )
+ {
+ pNewCursor = m_pView->PrevVisible(m_pCursor, nDelta);
+
+ while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = m_pView->NextVisible(pNewCursor);
+ nDelta--;
+ }
+
+ if( nDelta )
+ {
+ DBG_ASSERT(pNewCursor && pNewCursor!=m_pCursor, "Cursor?");
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ if( IsEntryInView( pNewCursor ) )
+ SetCursor( pNewCursor );
+ else
+ {
+ SetCursor( pNewCursor );
+ KeyUp( true );
+ }
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_PAGEDOWN:
+ if( !bMod1 )
+ {
+ pNewCursor= m_pView->NextVisible(m_pCursor, nDelta);
+
+ while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = m_pView->PrevVisible(pNewCursor);
+ nDelta--;
+ }
+
+ if( nDelta && pNewCursor )
+ {
+ DBG_ASSERT(pNewCursor && pNewCursor!=m_pCursor, "Cursor?");
+ m_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 ( m_pView->GetSelectionMode() != SelectionMode::NONE )
+ {
+ if ( bMod1 )
+ {
+ if ( m_pView->GetSelectionMode() == SelectionMode::Multiple && !bShift )
+ // toggle selection
+ m_pView->Select( m_pCursor, !m_pView->IsSelected( m_pCursor ) );
+ }
+ else if ( !bShift /*&& !bMod1*/ )
+ {
+ if ( m_aSelEng.IsAddMode() )
+ {
+ // toggle selection
+ m_pView->Select( m_pCursor, !m_pView->IsSelected( m_pCursor ) );
+ }
+ else if ( !m_pView->IsSelected( m_pCursor ) )
+ {
+ SelAllDestrAnch( false );
+ m_pView->Select( m_pCursor );
+ }
+ else
+ bKeyUsed = false;
+ }
+ else
+ bKeyUsed = false;
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_RETURN:
+ bKeyUsed = !m_pView->DoubleClickHdl();
+ break;
+
+ case KEY_F2:
+ if( !bShift && !bMod1 )
+ {
+ m_aEditClickPos = Point( -1, -1 );
+ EditTimerCall( nullptr );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_F8:
+ if( bShift && m_pView->GetSelectionMode()==SelectionMode::Multiple &&
+ !(m_nStyle & WB_SIMPLEMODE))
+ {
+ if( m_aSelEng.IsAlwaysAdding() )
+ m_aSelEng.AddAlways( false );
+ else
+ m_aSelEng.AddAlways( true );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_ADD:
+ if (!m_pView->IsExpanded(m_pCursor))
+ m_pView->Expand(m_pCursor);
+ if (bMod1)
+ ExpandAll();
+ break;
+
+ case KEY_A:
+ if( bMod1 )
+ SelAllDestrAnch( true );
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_SUBTRACT:
+ if (m_pView->IsExpanded(m_pCursor))
+ m_pView->Collapse(m_pCursor);
+ if (bMod1)
+ CollapseTo(m_pTree->GetRootLevelParent(m_pCursor));
+ break;
+
+ case KEY_MULTIPLY:
+ if( bMod1 )
+ {
+ // only try to expand/collapse if sublist is expandable,
+ // otherwise ignore the key press
+ if( IsExpandable() )
+ {
+ if (!m_pView->IsAllExpanded(m_pCursor))
+ {
+ m_pView->Expand(m_pCursor);
+ ExpandAll();
+ }
+ else
+ CollapseTo(m_pCursor);
+ }
+ }
+ 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 = m_pView->GetModel()->First();
+
+ while( pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = m_pView->NextVisible(pNewCursor);
+ }
+
+ if( pNewCursor && pNewCursor != m_pCursor )
+ {
+// SelAllDestrAnch( false );
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor );
+ if( !IsEntryInView( pNewCursor ) )
+ MakeVisible( pNewCursor );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_END :
+ pNewCursor = m_pView->GetModel()->Last();
+
+ while( pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = m_pView->PrevVisible(pNewCursor);
+ }
+
+ if( pNewCursor && pNewCursor != m_pCursor)
+ {
+// SelAllDestrAnch( false );
+ m_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( m_pCursor )
+ {
+ m_pView->SetEntryFocus( m_pCursor, true );
+ ShowCursor( true );
+// auskommentiert wg. deselectall
+// if( bSimpleTravel && !pView->IsSelected(pCursor) )
+// pView->Select( pCursor, true );
+ }
+ if( m_nStyle & WB_HIDESELECTION )
+ {
+ SvTreeListEntry* pEntry = m_pView->FirstSelected();
+ while( pEntry )
+ {
+ InvalidateEntry( pEntry );
+ pEntry = m_pView->NextSelected( pEntry );
+ }
+ }
+}
+
+void SvImpLBox::LoseFocus()
+{
+ m_aEditIdle.Stop();
+ if( m_pCursor )
+ m_pView->SetEntryFocus( m_pCursor,false );
+ ShowCursor( false );
+
+ if( m_nStyle & WB_HIDESELECTION )
+ {
+ SvTreeListEntry* pEntry = m_pView ? m_pView->FirstSelected() : nullptr;
+ while( pEntry )
+ {
+ InvalidateEntry( pEntry );
+ pEntry = m_pView->NextSelected( pEntry );
+ }
+ }
+}
+
+
+// ********************************************************************
+// SelectionEngine
+// ********************************************************************
+
+void SvImpLBox::SelectEntry( SvTreeListEntry* pEntry, bool bSelect )
+{
+ m_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->m_pAnchor = pImp->m_pCursor;
+}
+
+void ImpLBSelEng::DestroyAnchor()
+{
+ pImp->m_pAnchor = nullptr;
+}
+
+void ImpLBSelEng::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor)
+{
+ SvTreeListEntry* pNewCursor = pImp->MakePointVisible( rPoint );
+ if( pNewCursor != pImp->m_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->m_nFlags &= ~LBoxFlags::DeselectAll;
+}
+
+// ***********************************************************************
+// Selection
+// ***********************************************************************
+
+void SvImpLBox::SetAnchorSelection(SvTreeListEntry* pOldCursor,SvTreeListEntry* pNewCursor)
+{
+ SvTreeListEntry* pEntry;
+ sal_uLong nAnchorVisPos = m_pView->GetVisiblePos( m_pAnchor );
+ sal_uLong nOldVisPos = m_pView->GetVisiblePos( pOldCursor );
+ sal_uLong nNewVisPos = m_pView->GetVisiblePos( pNewCursor );
+
+ if( nOldVisPos > nAnchorVisPos ||
+ ( nAnchorVisPos==nOldVisPos && nNewVisPos > nAnchorVisPos) )
+ {
+ if( nNewVisPos > nOldVisPos )
+ {
+ pEntry = pOldCursor;
+ while( pEntry && pEntry != pNewCursor )
+ {
+ m_pView->Select( pEntry );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ m_pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos < nAnchorVisPos )
+ {
+ pEntry = m_pAnchor;
+ while( pEntry && pEntry != pOldCursor )
+ {
+ m_pView->Select( pEntry, false );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ m_pView->Select( pEntry, false );
+
+ pEntry = pNewCursor;
+ while( pEntry && pEntry != m_pAnchor )
+ {
+ m_pView->Select( pEntry );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ m_pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos < nOldVisPos )
+ {
+ pEntry = m_pView->NextVisible(pNewCursor);
+ while( pEntry && pEntry != pOldCursor )
+ {
+ m_pView->Select( pEntry, false );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ m_pView->Select( pEntry, false );
+ return;
+ }
+ }
+ else
+ {
+ if( nNewVisPos < nOldVisPos ) // enlarge selection
+ {
+ pEntry = pNewCursor;
+ while( pEntry && pEntry != pOldCursor )
+ {
+ m_pView->Select( pEntry );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ m_pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos > nAnchorVisPos )
+ {
+ pEntry = pOldCursor;
+ while( pEntry && pEntry != m_pAnchor )
+ {
+ m_pView->Select( pEntry, false );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ m_pView->Select( pEntry, false );
+ pEntry = m_pAnchor;
+ while( pEntry && pEntry != pNewCursor )
+ {
+ m_pView->Select( pEntry );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ m_pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos > nOldVisPos )
+ {
+ pEntry = pOldCursor;
+ while( pEntry && pEntry != pNewCursor )
+ {
+ m_pView->Select( pEntry, false );
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+ return;
+ }
+ }
+}
+
+void SvImpLBox::SelAllDestrAnch(
+ bool bSelect, bool bDestroyAnchor, bool bSingleSelToo )
+{
+ SvTreeListEntry* pEntry;
+ m_nFlags &= ~LBoxFlags::DeselectAll;
+ if( bSelect && m_bSimpleTravel )
+ {
+ if( m_pCursor && !m_pView->IsSelected( m_pCursor ))
+ {
+ m_pView->Select( m_pCursor );
+ }
+ return;
+ }
+ if( !bSelect && m_pView->GetSelectionCount() == 0 )
+ {
+ if( m_bSimpleTravel && ( !GetUpdateMode() || !m_pCursor) )
+ m_nFlags |= LBoxFlags::DeselectAll;
+ return;
+ }
+ if( bSelect && m_pView->GetSelectionCount() == m_pView->GetEntryCount())
+ return;
+ if( !bSingleSelToo && m_bSimpleTravel )
+ return;
+
+ if( !bSelect && m_pView->GetSelectionCount()==1 && m_pCursor &&
+ m_pView->IsSelected( m_pCursor ))
+ {
+ m_pView->Select( m_pCursor, false );
+ if( bDestroyAnchor )
+ DestroyAnchor(); // delete anchor & reset SelectionEngine
+ else
+ m_pAnchor = nullptr; // always delete internal anchor
+ return;
+ }
+
+ if( m_bSimpleTravel && !m_pCursor && !GetUpdateMode() )
+ m_nFlags |= LBoxFlags::DeselectAll;
+
+ ShowCursor( false );
+ bool bUpdate = GetUpdateMode();
+
+ m_nFlags |= LBoxFlags::IgnoreSelect; // EntryInserted should not do anything
+ pEntry = m_pTree->First();
+ while( pEntry )
+ {
+ if( m_pView->Select( pEntry, bSelect ) )
+ {
+ if( bUpdate && m_pView->IsEntryVisible(pEntry) )
+ {
+ long nY = GetEntryLine( pEntry );
+ if( IsLineVisible( nY ) )
+ InvalidateEntry(pEntry);
+ }
+ }
+ pEntry = m_pTree->Next( pEntry );
+ }
+ m_nFlags &= ~LBoxFlags::IgnoreSelect;
+
+ if( bDestroyAnchor )
+ DestroyAnchor(); // delete anchor & reset SelectionEngine
+ else
+ m_pAnchor = nullptr; // always delete internal anchor
+ ShowCursor( true );
+}
+
+void SvImpLBox::SetSelectionMode( SelectionMode eSelMode )
+{
+ m_aSelEng.SetSelectionMode( eSelMode);
+ if( eSelMode == SelectionMode::Single )
+ m_bSimpleTravel = true;
+ else
+ m_bSimpleTravel = false;
+ if( (m_nStyle & WB_SIMPLEMODE) && (eSelMode == SelectionMode::Multiple) )
+ m_aSelEng.AddAlways( true );
+}
+
+// ***********************************************************************
+// Drag & Drop
+// ***********************************************************************
+
+void SvImpLBox::SetDragDropMode( DragDropMode eDDMode )
+{
+ if( eDDMode != DragDropMode::NONE )
+ {
+ m_aSelEng.ExpandSelectionOnMouseMove( false );
+ m_aSelEng.EnableDrag( true );
+ }
+ else
+ {
+ m_aSelEng.ExpandSelectionOnMouseMove();
+ m_aSelEng.EnableDrag( false );
+ }
+}
+
+void SvImpLBox::BeginDrag()
+{
+ m_nFlags &= ~LBoxFlags::Filling;
+ BeginScroll();
+ m_pView->StartDrag( 0, m_aSelEng.GetMousePosPixel() );
+ EndScroll();
+}
+
+void SvImpLBox::PaintDDCursor(SvTreeListEntry* pEntry, bool bShow)
+{
+ if (pEntry)
+ {
+
+ SvViewDataEntry* pViewData = m_pView->GetViewData(pEntry);
+ pViewData->SetDragTarget(bShow);
+#ifdef MACOSX
+ // in MacOS we need to draw directly (as we are synchronous) or no invalidation happens
+ m_pView->PaintEntry1(*pEntry, GetEntryLine(pEntry), *m_pView);
+#else
+ InvalidateEntry(pEntry);
+#endif
+ }
+}
+
+void SvImpLBox::Command( const CommandEvent& rCEvt )
+{
+ CommandEventId nCommand = rCEvt.GetCommand();
+
+ if( nCommand == CommandEventId::ContextMenu )
+ m_aEditIdle.Stop();
+
+ // scroll mouse event?
+ if (nCommand == CommandEventId::Wheel ||
+ nCommand == CommandEventId::StartAutoScroll ||
+ nCommand == CommandEventId::AutoScroll ||
+ nCommand == CommandEventId::Gesture)
+ {
+ if (m_pView->HandleScrollCommand(rCEvt, m_aHorSBar.get(), m_aVerSBar.get()))
+ return;
+ }
+
+ if( m_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 = m_pView->FirstSelected();
+ while( pSelected )
+ {
+ bClickedIsSelected |= ( pClickedEntry == pSelected );
+ pSelected = m_pView->NextSelected( pSelected );
+ }
+
+ // if the entry which the user clicked at is not selected
+ if( !bClickedIsSelected )
+ { // deselect all other and select the clicked one
+ m_pView->SelectAll( false );
+ m_pView->SetCursor( pClickedEntry );
+ }
+ }
+ else if( m_aSelEng.GetSelectionMode() == SelectionMode::Single )
+ {
+ bClickedIsFreePlace = true;
+ sal_Int32 nSelectedEntries = m_pView->GetSelectionCount();
+ SvTreeListEntry* pSelected = m_pView->FirstSelected();
+ for(sal_Int32 nSel = 0; nSel < nSelectedEntries; nSel++ )
+ {
+ aSelRestore.push(pSelected);
+ pSelected = m_pView->NextSelected( pSelected );
+ }
+ m_pView->SelectAll( false );
+ }
+ else
+ { // deselect all
+ m_pView->SelectAll( false );
+ }
+
+
+ }
+ else
+ { // key event (or at least no mouse event)
+ sal_Int32 nSelectionCount = m_pView->GetSelectionCount();
+
+ if( nSelectionCount )
+ { // now always take first visible as base for positioning the menu
+ SvTreeListEntry* pSelected = m_pView->FirstSelected();
+ while( pSelected )
+ {
+ if( IsEntryInView( pSelected ) )
+ break;
+
+ pSelected = m_pView->NextSelected( pSelected );
+ }
+
+ if( !pSelected )
+ {
+ // no one was visible
+ pSelected = m_pView->FirstSelected();
+ m_pView->MakeVisible( pSelected );
+ }
+
+ aPopupPos = m_pView->GetFocusRect( pSelected, m_pView->GetEntryPosition( pSelected ).Y() ).Center();
+ }
+ else
+ aPopupPos = Point( 0, 0 );
+ }
+
+ {
+ VclPtr<PopupMenu> pPopup = m_pView->CreateContextMenu();
+ if (pPopup)
+ {
+ // do action for selected entry in popup menu
+ sal_uInt16 nMenuAction = pPopup->Execute( m_pView, aPopupPos );
+ if ( nMenuAction )
+ m_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 < m_pView->GetEntryCount(); nEntry++)
+ if(pEntry == m_pView->GetEntry(nEntry))
+ {
+ bFound = true;
+ break;
+ }
+ if(bFound)
+ SetCurEntry( pEntry );
+ aSelRestore.pop();
+ }
+ }
+ }
+ else
+ {
+ const Point& rPos = rCEvt.GetMousePosPixel();
+ if( rPos.X() < m_aOutputSize.Width() && rPos.Y() < m_aOutputSize.Height() )
+ m_aSelEng.Command( rCEvt );
+ }
+}
+
+void SvImpLBox::BeginScroll()
+{
+ if( !(m_nFlags & LBoxFlags::InScrolling))
+ {
+ m_nFlags |= LBoxFlags::InScrolling;
+ }
+}
+
+void SvImpLBox::EndScroll()
+{
+ if( m_nFlags & LBoxFlags::InScrolling)
+ {
+ m_pView->NotifyEndScroll();
+ m_nFlags &= ~LBoxFlags::InScrolling;
+ }
+}
+
+
+tools::Rectangle SvImpLBox::GetVisibleArea() const
+{
+ Point aPos( m_pView->GetMapMode().GetOrigin() );
+ aPos.setX( aPos.X() * -1 );
+ tools::Rectangle aRect( aPos, m_aOutputSize );
+ return aRect;
+}
+
+void SvImpLBox::Invalidate()
+{
+ m_pView->SetClipRegion();
+}
+
+void SvImpLBox::SetCurEntry( SvTreeListEntry* pEntry )
+{
+ if ( ( m_aSelEng.GetSelectionMode() != SelectionMode::Single )
+ && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE )
+ )
+ SelAllDestrAnch( false );
+ if ( pEntry )
+ MakeVisible( pEntry );
+ SetCursor( pEntry );
+ if ( pEntry && ( m_aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
+ m_pView->Select( pEntry );
+}
+
+IMPL_LINK_NOARG(SvImpLBox, EditTimerCall, Timer *, void)
+{
+ if( !m_pView->IsInplaceEditingEnabled() )
+ return;
+
+ bool bIsMouseTriggered = m_aEditClickPos.X() >= 0;
+ if ( bIsMouseTriggered )
+ {
+ Point aCurrentMousePos = m_pView->GetPointerPosPixel();
+ if ( ( std::abs( aCurrentMousePos.X() - m_aEditClickPos.X() ) > 5 )
+ || ( std::abs( aCurrentMousePos.Y() - m_aEditClickPos.Y() ) > 5 )
+ )
+ {
+ return;
+ }
+ }
+
+ SvTreeListEntry* pEntry = GetCurEntry();
+ if( pEntry )
+ {
+ ShowCursor( false );
+ m_pView->ImplEditEntry( pEntry );
+ ShowCursor( true );
+ }
+}
+
+bool SvImpLBox::RequestHelp( const HelpEvent& rHEvt )
+{
+ if( rHEvt.GetMode() & HelpEventMode::QUICK )
+ {
+ Point aPos( m_pView->ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
+ if( !GetVisibleArea().IsInside( aPos ))
+ return false;
+
+ SvTreeListEntry* pEntry = GetEntry( aPos );
+ if( pEntry )
+ {
+ // recalculate text rectangle
+ SvLBoxTab* pTab;
+ SvLBoxItem* pItem = m_pView->GetItem( pEntry, aPos.X(), &pTab );
+ if (!pItem || pItem->GetType() != SvLBoxItemType::String)
+ return false;
+
+ aPos = GetEntryPosition( pEntry );
+ aPos.setX( m_pView->GetTabPos( pEntry, pTab ) ); //pTab->GetPos();
+ Size aSize(pItem->GetWidth(m_pView, pEntry), pItem->GetHeight(m_pView, pEntry));
+ SvLBoxTab* pNextTab = NextTab( pTab );
+ bool bItemClipped = false;
+ // is the item cut off by its right neighbor?
+ if( pNextTab && m_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 = m_pView->OutputToScreenPixel( aItemRect.TopLeft() );
+ aItemRect.SetLeft( aPt.X() );
+ aItemRect.SetTop( aPt.Y() );
+ aPt = m_pView->OutputToScreenPixel( aItemRect.BottomRight() );
+ aItemRect.SetRight( aPt.X() );
+ aItemRect.SetBottom( aPt.Y() );
+
+ Help::ShowQuickHelp( m_pView, aItemRect,
+ static_cast<SvLBoxString*>(pItem)->GetText(), QuickHelpFlags::Left | QuickHelpFlags::VCenter );
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SvLBoxTab* SvImpLBox::NextTab( SvLBoxTab const * pTab )
+{
+ sal_uInt16 nTabCount = m_pView->TabCount();
+ if( nTabCount <= 1 )
+ return nullptr;
+ for( int nTab=0; nTab < (nTabCount-1); nTab++)
+ {
+ if( m_pView->aTabs[nTab].get() == pTab )
+ return m_pView->aTabs[nTab+1].get();
+ }
+ return nullptr;
+}
+
+void SvImpLBox::EndSelection()
+{
+ DestroyAnchor();
+ m_nFlags &= ~LBoxFlags::StartEditTimer;
+}
+
+void SvImpLBox::SetUpdateMode( bool bMode )
+{
+ if( m_bUpdateMode != bMode )
+ {
+ m_bUpdateMode = bMode;
+ if( m_bUpdateMode )
+ UpdateAll( false );
+ }
+}
+
+bool SvImpLBox::SetMostRight( SvTreeListEntry* pEntry )
+{
+ if( m_pView->nTreeFlags & SvTreeFlags::RECALCTABS )
+ {
+ m_nFlags |= LBoxFlags::IgnoreChangedTabs;
+ m_pView->SetTabs();
+ m_nFlags &= ~LBoxFlags::IgnoreChangedTabs;
+ }
+
+ sal_uInt16 nLastTab = m_pView->aTabs.size() - 1;
+ sal_uInt16 nLastItem = pEntry->ItemCount() - 1;
+ if( !m_pView->aTabs.empty() && nLastItem != USHRT_MAX )
+ {
+ if( nLastItem < nLastTab )
+ nLastTab = nLastItem;
+
+ SvLBoxTab* pTab = m_pView->aTabs[ nLastTab ].get();
+ SvLBoxItem& rItem = pEntry->GetItem( nLastTab );
+
+ long nTabPos = m_pView->GetTabPos( pEntry, pTab );
+
+ long nMaxRight = GetOutputSize().Width();
+ Point aPos( m_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;
+ auto nItemSize = rItem.GetWidth(m_pView,pEntry);
+ long nOffset = pTab->CalcOffset( nItemSize, nTabWidth );
+
+ long nRight = nTabPos + nOffset + nItemSize;
+ if( nRight > m_nMostRight )
+ {
+ m_nMostRight = nRight;
+ m_pMostRightEntry = pEntry;
+ return true;
+ }
+ }
+ return false;
+}
+
+void SvImpLBox::FindMostRight()
+{
+ m_nMostRight = -1;
+ m_pMostRightEntry = nullptr;
+ if( !m_pView->GetModel() )
+ return;
+
+ SvTreeListEntry* pEntry = m_pView->FirstVisible();
+ while( pEntry )
+ {
+ SetMostRight( pEntry );
+ pEntry = m_pView->NextVisible( pEntry );
+ }
+}
+
+void SvImpLBox::FindMostRight( SvTreeListEntry* pParent )
+{
+ if( !pParent )
+ FindMostRight();
+ else
+ FindMostRight_Impl( pParent );
+}
+
+void SvImpLBox::FindMostRight_Impl( SvTreeListEntry* pParent )
+{
+ SvTreeListEntries& rList = m_pTree->GetChildList( pParent );
+
+ size_t nCount = rList.size();
+ for( size_t nCur = 0; nCur < nCount; nCur++ )
+ {
+ SvTreeListEntry* pChild = rList[nCur].get();
+ SetMostRight( pChild );
+ if( pChild->HasChildren() && m_pView->IsExpanded( pChild ))
+ FindMostRight_Impl( pChild );
+ }
+}
+
+void SvImpLBox::NotifyTabsChanged()
+{
+ if( GetUpdateMode() && !(m_nFlags & LBoxFlags::IgnoreChangedTabs ) &&
+ m_nCurUserEvent == nullptr )
+ {
+ m_nCurUserEvent = Application::PostUserEvent(LINK(this,SvImpLBox,MyUserEvent));
+ }
+}
+
+bool SvImpLBox::IsExpandable() const
+{
+ return m_pCursor->HasChildren() || m_pCursor->HasChildrenOnDemand();
+}
+
+IMPL_LINK(SvImpLBox, MyUserEvent, void*, pArg, void )
+{
+ m_nCurUserEvent = nullptr;
+ if( !pArg )
+ {
+ m_pView->Invalidate();
+ m_pView->PaintImmediately();
+ }
+ else
+ {
+ FindMostRight();
+ ShowVerSBar();
+ m_pView->Invalidate( GetVisibleArea() );
+ }
+}
+
+
+void SvImpLBox::StopUserEvent()
+{
+ if( m_nCurUserEvent != nullptr )
+ {
+ Application::RemoveUserEvent( m_nCurUserEvent );
+ m_nCurUserEvent = nullptr;
+ }
+}
+
+void SvImpLBox::ShowFocusRect( const SvTreeListEntry* pEntry )
+{
+ if( pEntry )
+ {
+ long nY = GetEntryLine(pEntry);
+ tools::Rectangle aRect = m_pView->GetFocusRect(pEntry, nY);
+ vcl::Region aOldClip( m_pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ m_pView->SetClipRegion( aClipRegion );
+ m_pView->ShowFocus( aRect );
+ m_pView->SetClipRegion( aOldClip );
+
+ }
+ else
+ {
+ m_pView->HideFocus();
+ }
+}
+
+
+void SvImpLBox::implInitDefaultNodeImages()
+{
+ if ( s_pDefCollapsed )
+ // assume that all or nothing is initialized
+ return;
+
+ s_pDefCollapsed = new Image(StockImage::Yes, RID_BMP_TREENODE_COLLAPSED);
+ s_pDefExpanded = new Image(StockImage::Yes, 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 ( m_pView )
+ m_pView->CallImplEventListeners( nEvent, pData);
+}
+
+
+bool SvImpLBox::IsSelectable( const SvTreeListEntry* pEntry )
+{
+ if( pEntry )
+ {
+ SvViewDataEntry* pViewDataNewCur = m_pView->GetViewDataEntry(pEntry);
+ return (pViewDataNewCur == nullptr) || pViewDataNewCur->IsSelectable();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */