c2253f587b
Needed to eventual re-implementation of starmath's SmElementsControl
using IconView.
This required re-implementation of IconViewImpl, to layout entries
by iteration, because now it's impossible to find an entry position
just based on its index.
This coincidentally fixed some visual glitches in non-gtk IconView
implementation from commit 5813660e7b
Author Szymon Kłos <eszkadev@gmail.com>
Date Tue Feb 16 16:03:30 2016 +0100
icon view for RemoteFilesDialog
where any selected element could become first in row when scrolling.
SvTreeListBox::SetEntryHeight taking a SvTreeListEntry const* had to
be renamed to CalcEntryHeight, to avoid both virtual and non-virtual
overloads, additionally having different accessibility.
A TODO is implement separators in GtkInstanceIconView. I couldn't
find a GTK API for separators in IconView, so possibly a workaround
would be needed with some non-selectable narrow elements.
Change-Id: Ie8dc35d94049a1c48e4eb49697681ffbe93c17f4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135112
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
3141 lines
93 KiB
C++
3141 lines
93 KiB
C++
/* -*- 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/toolkit/treelistbox.hxx>
|
|
#include <vcl/toolkit/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/toolkit/treelistentry.hxx>
|
|
#include <vcl/toolkit/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_aScrBarBox(VclPtr<ScrollBarBox>::Create(pLBView))
|
|
, m_aFctSet(this, pLBView)
|
|
, mbForceMakeVisible (false)
|
|
, m_aEditIdle("SvImpLBox m_aEditIdle")
|
|
, m_aHorSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_HSCROLL))
|
|
, 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_pCursorOld = 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 = false;
|
|
}
|
|
|
|
SvImpLBox::~SvImpLBox()
|
|
{
|
|
m_aEditIdle.Stop();
|
|
StopUserEvent();
|
|
|
|
if ( osl_atomic_decrement(&s_nImageRefCount) == 0 )
|
|
{
|
|
delete s_pDefCollapsed;
|
|
s_pDefCollapsed = nullptr;
|
|
delete s_pDefExpanded;
|
|
s_pDefExpanded = nullptr;
|
|
}
|
|
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_pCursorOld = 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->GetOutDev()->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;
|
|
}
|
|
}
|
|
|
|
// handler for vertical scrollbar
|
|
|
|
IMPL_LINK( SvImpLBox, ScrollUpDownHdl, ScrollBar *, pScrollBar, void )
|
|
{
|
|
DBG_ASSERT(!m_bInVScrollHdl,"Scroll handler out-paces itself!");
|
|
tools::Long nDelta = pScrollBar->GetDelta();
|
|
if( !nDelta )
|
|
return;
|
|
|
|
// when only one row don't skip lines
|
|
if (pScrollBar->GetPageSize() == 1)
|
|
nDelta = nDelta > 0 ? 1 : -1;
|
|
|
|
m_nFlags &= ~LBoxFlags::Filling;
|
|
|
|
m_bInVScrollHdl = true;
|
|
|
|
if( m_pView->IsEditingActive() )
|
|
{
|
|
m_pView->EndEditing( true ); // Cancel
|
|
m_pView->PaintImmediately();
|
|
}
|
|
|
|
if( nDelta > 0 )
|
|
{
|
|
if( nDelta == 1 && pScrollBar->GetPageSize() > 1)
|
|
CursorDown();
|
|
else
|
|
PageDown( static_cast<sal_uInt16>(nDelta) );
|
|
}
|
|
else
|
|
{
|
|
nDelta *= -1;
|
|
if( nDelta == 1 && pScrollBar->GetPageSize() > 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;
|
|
tools::Long nEntryHeight = m_pView->GetEntryHeight();
|
|
ShowCursor( false );
|
|
m_pView->PaintImmediately();
|
|
m_pStartEntry = pPrevFirstToDraw;
|
|
tools::Rectangle aArea( GetVisibleArea() );
|
|
if (aArea.GetHeight() > nEntryHeight)
|
|
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() );
|
|
tools::Long nScroll = m_pView->GetEntryHeight() * static_cast<tools::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
|
|
{
|
|
tools::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;
|
|
|
|
tools::Long nDelta;
|
|
if( bPageUp )
|
|
nDelta = m_aVerSBar->GetPageSize();
|
|
else
|
|
nDelta = 1;
|
|
|
|
tools::Long nThumbPos = m_aVerSBar->GetThumbPos();
|
|
|
|
if( nThumbPos < nDelta )
|
|
nDelta = nThumbPos;
|
|
|
|
if( nDelta <= 0 )
|
|
return;
|
|
|
|
m_nFlags &= ~LBoxFlags::Filling;
|
|
|
|
m_aVerSBar->SetThumbPos( nThumbPos - nDelta );
|
|
if( bPageUp )
|
|
PageUp( static_cast<short>(nDelta) );
|
|
else
|
|
CursorUp();
|
|
}
|
|
|
|
|
|
void SvImpLBox::KeyDown( bool bPageDown )
|
|
{
|
|
if( !m_aVerSBar->IsVisible() )
|
|
return;
|
|
|
|
tools::Long nDelta;
|
|
if( bPageDown )
|
|
nDelta = m_aVerSBar->GetPageSize();
|
|
else
|
|
nDelta = 1;
|
|
|
|
tools::Long nThumbPos = m_aVerSBar->GetThumbPos();
|
|
tools::Long nVisibleSize = m_aVerSBar->GetVisibleSize();
|
|
tools::Long nRange = m_aVerSBar->GetRange().Len();
|
|
|
|
tools::Long nTmp = nThumbPos+nVisibleSize;
|
|
while( (nDelta > 0) && (nTmp+nDelta) >= nRange )
|
|
nDelta--;
|
|
|
|
if( nDelta <= 0 )
|
|
return;
|
|
|
|
m_nFlags &= ~LBoxFlags::Filling;
|
|
|
|
m_aVerSBar->SetThumbPos( nThumbPos+nDelta );
|
|
if( bPageDown )
|
|
PageDown( static_cast<short>(nDelta) );
|
|
else
|
|
CursorDown();
|
|
}
|
|
|
|
|
|
void SvImpLBox::InvalidateEntriesFrom( tools::Long nY ) const
|
|
{
|
|
if( !(m_nFlags & LBoxFlags::InPaint ))
|
|
{
|
|
tools::Rectangle aRect( GetVisibleArea() );
|
|
aRect.SetTop( nY );
|
|
m_pView->Invalidate( aRect );
|
|
}
|
|
}
|
|
|
|
void SvImpLBox::InvalidateEntry( tools::Long nY ) const
|
|
{
|
|
if( m_nFlags & LBoxFlags::InPaint )
|
|
return;
|
|
|
|
tools::Rectangle aRect( GetVisibleArea() );
|
|
tools::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() )
|
|
{
|
|
tools::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();
|
|
tools::Long nY = GetEntryLine( m_pCursor );
|
|
tools::Rectangle aRect = m_pView->GetFocusRect( m_pCursor, nY );
|
|
vcl::Region aOldClip( m_pView->GetOutDev()->GetClipRegion());
|
|
vcl::Region aClipRegion( GetClipRegionRect() );
|
|
m_pView->GetOutDev()->SetClipRegion( aClipRegion );
|
|
m_pView->ShowFocus( aRect );
|
|
m_pView->GetOutDev()->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->GetOutDev()->GetClipRegion());
|
|
vcl::Region aClipRegion( GetClipRegionRect() );
|
|
m_pView->GetOutDev()->SetClipRegion( aClipRegion );
|
|
m_pView->HideFocus();
|
|
m_pView->GetOutDev()->SetClipRegion( aOldClip );
|
|
}
|
|
else
|
|
{
|
|
tools::Long nY = GetEntryLine( m_pCursor );
|
|
tools::Rectangle aRect = m_pView->GetFocusRect( m_pCursor, nY );
|
|
vcl::Region aOldClip( m_pView->GetOutDev()->GetClipRegion());
|
|
vcl::Region aClipRegion( GetClipRegionRect() );
|
|
m_pView->GetOutDev()->SetClipRegion( aClipRegion );
|
|
m_pView->ShowFocus( aRect );
|
|
m_pView->GetOutDev()->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 )
|
|
{
|
|
tools::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( tools::Long nDelta )
|
|
{
|
|
if( !(m_nFlags & LBoxFlags::InResize) )
|
|
m_pView->PaintImmediately();
|
|
m_nFlags &= ~LBoxFlags::Filling;
|
|
ShowCursor( false );
|
|
|
|
// calculate new origin
|
|
tools::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, tools::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.Contains( 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;
|
|
tools::Long nY = rPoint.Y();
|
|
SvTreeListEntry* pEntry = nullptr;
|
|
tools::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();
|
|
|
|
tools::Long nRectHeight = rRect.GetHeight();
|
|
tools::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
|
|
|
|
tools::Long nY = nStartLine * nEntryHeight;
|
|
SvTreeListEntry* pEntry = m_pStartEntry;
|
|
while (nStartLine && pEntry)
|
|
{
|
|
pEntry = m_pView->NextVisible(pEntry);
|
|
nStartLine--;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (m_nStyle & (WB_HASLINES | WB_HASLINESATROOT))
|
|
DrawNet(rRenderContext);
|
|
|
|
m_nFlags &= ~LBoxFlags::DeselectAll;
|
|
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<tools::Long>(m_pView->GetVisiblePos( m_pStartEntry )) );
|
|
ShowCursor( true );
|
|
m_pView->Invalidate();
|
|
}
|
|
|
|
void SvImpLBox::ScrollToAbsPos( tools::Long nPos )
|
|
{
|
|
if( m_pView->GetVisibleCount() == 0 )
|
|
return;
|
|
tools::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;
|
|
}
|
|
}
|
|
|
|
tools::Long nEntryHeight = m_pView->GetEntryHeight();
|
|
tools::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));
|
|
tools::Long nY = 0;
|
|
nY -= (nOffs * nEntryHeight);
|
|
|
|
DBG_ASSERT(pFirstDynamicTab,"No Tree!");
|
|
|
|
rRenderContext.Push(vcl::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))
|
|
{
|
|
// draw vertical line
|
|
aPos1.setX(m_pView->GetTabPos(pEntry, pFirstDynamicTab) + m_nNodeBmpTabDistance +
|
|
rExpandedNodeBitmap.GetSizePixel().Width() / 2);
|
|
aPos1.setY(nY + nEntryHeight);
|
|
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) - (nEntryHeightDIV2 + 2));
|
|
rRenderContext.DrawLine(aPos1, aPos2);
|
|
}
|
|
// visible in control?
|
|
if (n >= nOffs && !m_pTree->IsAtRootDepth(pEntry))
|
|
{
|
|
// draw horizontal line
|
|
aPos1.setX(m_pView->GetTabPos(m_pView->GetParent(pEntry), pFirstDynamicTab)
|
|
+ m_nNodeBmpTabDistance
|
|
+ rExpandedNodeBitmap.GetSizePixel().Width() / 2);
|
|
aPos1.setY(nY + nEntryHeightDIV2);
|
|
aPos2 = aPos1;
|
|
aPos2.AdjustX(m_pView->GetIndent() / 2);
|
|
rRenderContext.DrawLine(aPos1, aPos2);
|
|
}
|
|
nY += nEntryHeight;
|
|
pEntry = m_pView->NextVisible(pEntry);
|
|
}
|
|
|
|
rRenderContext.Pop();
|
|
}
|
|
|
|
void SvImpLBox::PositionScrollBars( Size& rSize, sal_uInt16 nMask )
|
|
{
|
|
tools::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 )
|
|
{
|
|
tools::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;
|
|
tools::Long nMaxRight = aOSize.Width(); //GetOutputSize().Width();
|
|
Point aOrigin( m_pView->GetMapMode().GetOrigin() );
|
|
aOrigin.setX( aOrigin.X() * -1 );
|
|
nMaxRight += aOrigin.X() - 1;
|
|
tools::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
|
|
tools::Long nTemp = static_cast<tools::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() );
|
|
tools::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();
|
|
tools::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 );
|
|
}
|
|
}
|
|
|
|
tools::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 )
|
|
{
|
|
tools::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;
|
|
tools::Long nY = GetEntryLine( pEntry );
|
|
if( nY < 0 )
|
|
return false;
|
|
tools::Long nMax = m_nVisibleCount * m_pView->GetEntryHeight();
|
|
return nY < nMax;
|
|
}
|
|
|
|
|
|
tools::Long SvImpLBox::GetEntryLine(const SvTreeListEntry* pEntry) const
|
|
{
|
|
if(!m_pStartEntry )
|
|
return -1; // invisible position
|
|
|
|
tools::Long nFirstVisPos = m_pView->GetVisiblePos( m_pStartEntry );
|
|
tools::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 );
|
|
tools::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 )
|
|
{
|
|
tools::Long nOldThumbPos = m_aVerSBar->GetThumbPos();
|
|
sal_uLong nVisList = m_pView->GetVisibleCount();
|
|
m_aVerSBar->SetRange( Range(0, nVisList-1) );
|
|
tools::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?
|
|
tools::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) )
|
|
{
|
|
tools::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
|
|
tools::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 )
|
|
{
|
|
tools::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 );
|
|
tools::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, const SvTreeListEntry* pEntry ) const
|
|
{
|
|
if( !pEntry->HasChildren() && !pEntry->HasChildrenOnDemand() )
|
|
return false;
|
|
|
|
SvLBoxTab* pFirstDynamicTab = m_pView->GetFirstDynamicTab();
|
|
if( !pFirstDynamicTab )
|
|
return false;
|
|
|
|
tools::Long nMouseX = rPosPixel.X();
|
|
// convert to document coordinates
|
|
Point aOrigin( m_pView->GetMapMode().GetOrigin() );
|
|
nMouseX -= aOrigin.X();
|
|
|
|
tools::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;
|
|
|
|
tools::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 );
|
|
if( m_nFlags & LBoxFlags::StartEditTimer )
|
|
{
|
|
m_nFlags &= ~LBoxFlags::StartEditTimer;
|
|
m_aEditClickPos = rMEvt.GetPosPixel();
|
|
m_aEditIdle.Start();
|
|
}
|
|
|
|
if (m_pView->mbActivateOnSingleClick)
|
|
{
|
|
Point aPos(rMEvt.GetPosPixel());
|
|
SvTreeListEntry* pEntry = GetEntry(aPos);
|
|
// tdf#143245 ActivateOnSingleClick only
|
|
// if the 'up' is at the active entry
|
|
// typically selected by the 'down'
|
|
if (!pEntry || pEntry != m_pCursor)
|
|
return;
|
|
m_pView->DoubleClickHdl();
|
|
}
|
|
}
|
|
|
|
void SvImpLBox::MouseMove( const MouseEvent& rMEvt)
|
|
{
|
|
Point aPos = rMEvt.GetPosPixel();
|
|
SvTreeListEntry* pEntry = GetClickedEntry(aPos);
|
|
if ( MouseMoveCheckCtrl( rMEvt, pEntry ) || ( m_aSelEng.GetSelectionMode() == SelectionMode::NONE ) )
|
|
return;
|
|
|
|
m_aSelEng.SelMouseMove(rMEvt);
|
|
if (m_pView->mbHoverSelection)
|
|
{
|
|
if (aPos.X() < 0 || aPos.Y() < 0 || aPos.X() > m_aOutputSize.Width() || aPos.Y() > m_aOutputSize.Height())
|
|
pEntry = nullptr;
|
|
else
|
|
pEntry = GetEntry(aPos);
|
|
if (!pEntry)
|
|
m_pView->SelectAll(false);
|
|
else if (!m_pView->IsSelected(pEntry) && IsSelectable(pEntry))
|
|
{
|
|
m_pView->mbSelectingByHover = true;
|
|
m_pView->Select(pEntry);
|
|
m_pView->mbSelectingByHover = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
return;
|
|
|
|
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())
|
|
{
|
|
tools::Long nThumb = m_aHorSBar->GetThumbPos();
|
|
nThumb += m_aHorSBar->GetLineSize();
|
|
tools::Long nOldThumb = m_aHorSBar->GetThumbPos();
|
|
m_aHorSBar->SetThumbPos( nThumb );
|
|
nThumb = nOldThumb;
|
|
nThumb -= m_aHorSBar->GetThumbPos();
|
|
nThumb *= -1;
|
|
if( nThumb )
|
|
{
|
|
KeyLeftRight( nThumb );
|
|
}
|
|
}
|
|
else
|
|
bKeyUsed = false;
|
|
break;
|
|
}
|
|
|
|
case KEY_LEFT:
|
|
{
|
|
if (m_aHorSBar->IsVisible())
|
|
{
|
|
tools::Long nThumb = m_aHorSBar->GetThumbPos();
|
|
nThumb -= m_aHorSBar->GetLineSize();
|
|
tools::Long nOldThumb = m_aHorSBar->GetThumbPos();
|
|
m_aHorSBar->SetThumbPos( nThumb );
|
|
nThumb = nOldThumb;
|
|
nThumb -= m_aHorSBar->GetThumbPos();
|
|
if( nThumb )
|
|
{
|
|
KeyLeftRight( -nThumb );
|
|
}
|
|
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 )
|
|
{
|
|
// 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) )
|
|
{
|
|
tools::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;
|
|
m_pView->StartDrag( 0, m_aSelEng.GetMousePosPixel() );
|
|
}
|
|
|
|
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->GetOutDev());
|
|
#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;
|
|
}
|
|
|
|
const Point& rPos = rCEvt.GetMousePosPixel();
|
|
if( rPos.X() < m_aOutputSize.Width() && rPos.Y() < m_aOutputSize.Height() )
|
|
m_aSelEng.Command( rCEvt );
|
|
}
|
|
|
|
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->GetOutDev()->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().Contains( 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.Contains( 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::SetUpdateMode( bool bMode )
|
|
{
|
|
if( m_bUpdateMode != bMode )
|
|
{
|
|
m_bUpdateMode = bMode;
|
|
if( m_bUpdateMode )
|
|
UpdateAll( false );
|
|
}
|
|
}
|
|
|
|
void 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 )
|
|
return;
|
|
|
|
if( nLastItem < nLastTab )
|
|
nLastTab = nLastItem;
|
|
|
|
SvLBoxTab* pTab = m_pView->aTabs[ nLastTab ].get();
|
|
SvLBoxItem& rItem = pEntry->GetItem( nLastTab );
|
|
|
|
tools::Long nTabPos = m_pView->GetTabPos( pEntry, pTab );
|
|
|
|
tools::Long nMaxRight = GetOutputSize().Width();
|
|
Point aPos( m_pView->GetMapMode().GetOrigin() );
|
|
aPos.setX( aPos.X() * -1 ); // conversion document coordinates
|
|
nMaxRight = nMaxRight + aPos.X() - 1;
|
|
|
|
tools::Long nNextTab = nTabPos < nMaxRight ? nMaxRight : nMaxRight + 50;
|
|
tools::Long nTabWidth = nNextTab - nTabPos + 1;
|
|
auto nItemSize = rItem.GetWidth(m_pView,pEntry);
|
|
tools::Long nOffset = pTab->CalcOffset( nItemSize, nTabWidth );
|
|
|
|
tools::Long nRight = nTabPos + nOffset + nItemSize;
|
|
if( nRight > m_nMostRight )
|
|
{
|
|
m_nMostRight = nRight;
|
|
m_pMostRightEntry = pEntry;
|
|
}
|
|
}
|
|
|
|
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::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 ) const
|
|
{
|
|
if( pEntry )
|
|
{
|
|
SvViewDataEntry* pViewDataNewCur = m_pView->GetViewDataEntry(pEntry);
|
|
return (pViewDataNewCur == nullptr) || pViewDataNewCur->IsSelectable();
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|