summaryrefslogtreecommitdiffstats
path: root/vcl
diff options
context:
space:
mode:
Diffstat (limited to 'vcl')
-rw-r--r--vcl/Library_vcl.mk13
-rw-r--r--vcl/inc/bitmaps.hlst3
-rw-r--r--vcl/source/treelist/imap.cxx991
-rw-r--r--vcl/source/treelist/imap2.cxx532
-rw-r--r--vcl/source/treelist/imap3.cxx87
-rw-r--r--vcl/source/treelist/inetimg.cxx135
-rw-r--r--vcl/source/treelist/svimpbox.cxx3387
-rw-r--r--vcl/source/treelist/svlbitm.cxx461
-rw-r--r--vcl/source/treelist/transfer.cxx2177
-rw-r--r--vcl/source/treelist/transfer2.cxx472
-rw-r--r--vcl/source/treelist/treelist.cxx1630
-rw-r--r--vcl/source/treelist/treelistbox.cxx3666
-rw-r--r--vcl/source/treelist/treelistentry.cxx241
-rw-r--r--vcl/source/treelist/uiobject.cxx170
-rw-r--r--vcl/source/treelist/viewdataentry.cxx102
-rw-r--r--vcl/source/window/accessibility.cxx2
-rw-r--r--vcl/source/window/builder.cxx7
17 files changed, 14071 insertions, 5 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 6b124c7c22eb..bf2422ed7d67 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -229,6 +229,19 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/outdev/vclreferencebase \
vcl/source/outdev/nativecontrols \
vcl/source/outdev/map \
+ vcl/source/treelist/imap \
+ vcl/source/treelist/imap2 \
+ vcl/source/treelist/imap3 \
+ vcl/source/treelist/inetimg \
+ vcl/source/treelist/transfer \
+ vcl/source/treelist/transfer2 \
+ vcl/source/treelist/viewdataentry \
+ vcl/source/treelist/treelist \
+ vcl/source/treelist/treelistbox \
+ vcl/source/treelist/treelistentry \
+ vcl/source/treelist/svimpbox \
+ vcl/source/treelist/svlbitm \
+ vcl/source/treelist/uiobject \
vcl/source/gdi/alpha \
vcl/source/gdi/animate \
vcl/source/gdi/bitmap3 \
diff --git a/vcl/inc/bitmaps.hlst b/vcl/inc/bitmaps.hlst
index d71840e3f3ef..a45f8019b69f 100644
--- a/vcl/inc/bitmaps.hlst
+++ b/vcl/inc/bitmaps.hlst
@@ -143,6 +143,9 @@
#define IMG_ERROR "dbaccess/res/exerror.png"
#define IMG_INFO "dbaccess/res/exinfo.png"
+#define RID_BMP_TREENODE_COLLAPSED "res/plus.png"
+#define RID_BMP_TREENODE_EXPANDED "res/minus.png"
+
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/vcl/source/treelist/imap.cxx b/vcl/source/treelist/imap.cxx
new file mode 100644
index 000000000000..bcd69ad5a6fb
--- /dev/null
+++ b/vcl/source/treelist/imap.cxx
@@ -0,0 +1,991 @@
+/* -*- 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 <tools/urlobj.hxx>
+#include <tools/fract.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/window.hxx>
+#include <o3tl/numeric.hxx>
+#include <svl/urihelper.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/imapcirc.hxx>
+#include <vcl/imaprect.hxx>
+#include <vcl/imappoly.hxx>
+
+#include <string.h>
+#include <math.h>
+#include <memory>
+#include <sal/log.hxx>
+
+
+#define SCALEPOINT(aPT,aFracX,aFracY) (aPT).setX(long((aPT).X()*aFracX)); \
+ (aPT).setY(long((aPT).Y()*aFracY));
+
+
+/******************************************************************************/
+
+sal_uInt16 IMapObject::nActualTextEncoding = sal_uInt16(RTL_TEXTENCODING_DONTKNOW);
+
+/******************************************************************************/
+
+
+IMapObject::IMapObject()
+ : bActive( false )
+ , nReadVersion( 0 )
+{
+}
+
+IMapObject::IMapObject( const OUString& rURL, const OUString& rAltText, const OUString& rDesc,
+ const OUString& rTarget, const OUString& rName, bool bURLActive )
+: aURL( rURL )
+, aAltText( rAltText )
+, aDesc( rDesc )
+, aTarget( rTarget )
+, aName( rName )
+, bActive( bURLActive )
+, nReadVersion( 0 )
+{
+}
+
+
+void IMapObject::Write( SvStream& rOStm ) const
+{
+ const rtl_TextEncoding eEncoding = osl_getThreadTextEncoding();
+
+ rOStm.WriteUInt16( GetType() );
+ rOStm.WriteUInt16( IMAP_OBJ_VERSION );
+ rOStm.WriteUInt16( eEncoding );
+
+ const OString aRelURL = OUStringToOString(
+ URIHelper::simpleNormalizedMakeRelative("", aURL), eEncoding);
+ write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm, aRelURL);
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aAltText, eEncoding);
+ rOStm.WriteBool( bActive );
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aTarget, eEncoding);
+
+ std::unique_ptr<IMapCompat> pCompat(new IMapCompat( rOStm, StreamMode::WRITE ));
+
+ WriteIMapObject( rOStm );
+ aEventList.Write( rOStm ); // V4
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aName, eEncoding); // V5
+}
+
+
+/******************************************************************************
+|*
+|* Binary import
+|*
+\******************************************************************************/
+
+void IMapObject::Read( SvStream& rIStm )
+{
+ rtl_TextEncoding nTextEncoding;
+
+ // read on type and version
+ rIStm.SeekRel( 2 );
+ rIStm.ReadUInt16( nReadVersion );
+ rIStm.ReadUInt16( nTextEncoding );
+ aURL = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);
+ aAltText = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);
+ rIStm.ReadCharAsBool( bActive );
+ aTarget = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);
+
+ // make URL absolute
+ aURL = URIHelper::SmartRel2Abs( INetURLObject(""), aURL, URIHelper::GetMaybeFileHdl(), true, false, INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::Unambiguous );
+ std::unique_ptr<IMapCompat> pCompat(new IMapCompat( rIStm, StreamMode::READ ));
+
+ ReadIMapObject( rIStm );
+
+ // from version 4 onwards we read a eventlist
+ if ( nReadVersion >= 0x0004 )
+ {
+ aEventList.Read(rIStm);
+
+ // from version 5 onwards an objectname could be available
+ if ( nReadVersion >= 0x0005 )
+ aName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, nTextEncoding);
+ }
+}
+
+bool IMapObject::IsEqual( const IMapObject& rEqObj )
+{
+ return ( ( aURL == rEqObj.aURL ) &&
+ ( aAltText == rEqObj.aAltText ) &&
+ ( aDesc == rEqObj.aDesc ) &&
+ ( aTarget == rEqObj.aTarget ) &&
+ ( aName == rEqObj.aName ) &&
+ ( bActive == rEqObj.bActive ) );
+}
+
+IMapRectangleObject::IMapRectangleObject( const tools::Rectangle& rRect,
+ const OUString& rURL,
+ const OUString& rAltText,
+ const OUString& rDesc,
+ const OUString& rTarget,
+ const OUString& rName,
+ bool bURLActive,
+ bool bPixelCoords ) :
+ IMapObject ( rURL, rAltText, rDesc, rTarget, rName, bURLActive )
+{
+ ImpConstruct( rRect, bPixelCoords );
+}
+
+void IMapRectangleObject::ImpConstruct( const tools::Rectangle& rRect, bool bPixel )
+{
+ if ( bPixel )
+ aRect = Application::GetDefaultDevice()->PixelToLogic( rRect, MapMode( MapUnit::Map100thMM ) );
+ else
+ aRect = rRect;
+}
+
+
+/******************************************************************************
+|*
+|* Binary export
+|*
+\******************************************************************************/
+
+void IMapRectangleObject::WriteIMapObject( SvStream& rOStm ) const
+{
+ WriteRectangle( rOStm, aRect );
+}
+
+
+/******************************************************************************
+|*
+|* Binary import
+|*
+\******************************************************************************/
+
+void IMapRectangleObject::ReadIMapObject( SvStream& rIStm )
+{
+ ReadRectangle( rIStm, aRect );
+}
+
+
+/******************************************************************************
+|*
+|* return type
+|*
+\******************************************************************************/
+
+sal_uInt16 IMapRectangleObject::GetType() const
+{
+ return IMAP_OBJ_RECTANGLE;
+}
+
+
+/******************************************************************************
+|*
+|* Hit test
+|*
+\******************************************************************************/
+
+bool IMapRectangleObject::IsHit( const Point& rPoint ) const
+{
+ return aRect.IsInside( rPoint );
+}
+
+tools::Rectangle IMapRectangleObject::GetRectangle( bool bPixelCoords ) const
+{
+ tools::Rectangle aNewRect;
+
+ if ( bPixelCoords )
+ aNewRect = Application::GetDefaultDevice()->LogicToPixel( aRect, MapMode( MapUnit::Map100thMM ) );
+ else
+ aNewRect = aRect;
+
+ return aNewRect;
+}
+
+void IMapRectangleObject::Scale( const Fraction& rFracX, const Fraction& rFracY )
+{
+ Point aTL( aRect.TopLeft() );
+ Point aBR( aRect.BottomRight() );
+
+ if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
+ {
+ SCALEPOINT( aTL, rFracX, rFracY );
+ SCALEPOINT( aBR, rFracX, rFracY );
+ }
+
+ aRect = tools::Rectangle( aTL, aBR );
+}
+
+bool IMapRectangleObject::IsEqual( const IMapRectangleObject& rEqObj )
+{
+ return ( IMapObject::IsEqual( rEqObj ) && ( aRect == rEqObj.aRect ) );
+}
+
+IMapCircleObject::IMapCircleObject( const Point& rCenter, sal_uLong nCircleRadius,
+ const OUString& rURL,
+ const OUString& rAltText,
+ const OUString& rDesc,
+ const OUString& rTarget,
+ const OUString& rName,
+ bool bURLActive,
+ bool bPixelCoords ) :
+ IMapObject ( rURL, rAltText, rDesc, rTarget, rName, bURLActive )
+{
+ ImpConstruct( rCenter, nCircleRadius, bPixelCoords );
+}
+
+void IMapCircleObject::ImpConstruct( const Point& rCenter, sal_uLong nRad, bool bPixel )
+{
+ if ( bPixel )
+ {
+ MapMode aMap100( MapUnit::Map100thMM );
+
+ aCenter = Application::GetDefaultDevice()->PixelToLogic( rCenter, aMap100 );
+ nRadius = Application::GetDefaultDevice()->PixelToLogic( Size( nRad, 0 ), aMap100 ).Width();
+ }
+ else
+ {
+ aCenter = rCenter;
+ nRadius = nRad;
+ }
+}
+
+
+/******************************************************************************
+|*
+|* Binary export
+|*
+\******************************************************************************/
+
+void IMapCircleObject::WriteIMapObject( SvStream& rOStm ) const
+{
+ sal_uInt32 nTmp = nRadius;
+
+ WritePair( rOStm, aCenter );
+ rOStm.WriteUInt32( nTmp );
+}
+
+
+/******************************************************************************
+|*
+|* Binary import
+|*
+\******************************************************************************/
+
+void IMapCircleObject::ReadIMapObject( SvStream& rIStm )
+{
+ sal_uInt32 nTmp;
+
+ ReadPair( rIStm, aCenter );
+ rIStm.ReadUInt32( nTmp );
+
+ nRadius = nTmp;
+}
+
+
+/******************************************************************************
+|*
+|* return type
+|*
+\******************************************************************************/
+
+sal_uInt16 IMapCircleObject::GetType() const
+{
+ return IMAP_OBJ_CIRCLE;
+}
+
+
+/******************************************************************************
+|*
+|* Hit-Test
+|*
+\******************************************************************************/
+
+bool IMapCircleObject::IsHit( const Point& rPoint ) const
+{
+ const Point aPoint( aCenter - rPoint );
+ bool bRet = false;
+
+ if ( static_cast<sal_Int32>(sqrt( static_cast<double>(aPoint.X()) * aPoint.X() +
+ aPoint.Y() * aPoint.Y() )) <= nRadius )
+ {
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+Point IMapCircleObject::GetCenter( bool bPixelCoords ) const
+{
+ Point aNewPoint;
+
+ if ( bPixelCoords )
+ aNewPoint = Application::GetDefaultDevice()->LogicToPixel( aCenter, MapMode( MapUnit::Map100thMM ) );
+ else
+ aNewPoint = aCenter;
+
+ return aNewPoint;
+}
+
+sal_uLong IMapCircleObject::GetRadius( bool bPixelCoords ) const
+{
+ sal_uLong nNewRadius;
+
+ if ( bPixelCoords )
+ nNewRadius = Application::GetDefaultDevice()->LogicToPixel( Size( nRadius, 0 ), MapMode( MapUnit::Map100thMM ) ).Width();
+ else
+ nNewRadius = nRadius;
+
+ return nNewRadius;
+}
+
+void IMapCircleObject::Scale( const Fraction& rFracX, const Fraction& rFracY )
+{
+ Fraction aAverage( rFracX );
+
+ aAverage += rFracY;
+ aAverage *= Fraction( 1, 2 );
+
+ if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
+ {
+ SCALEPOINT( aCenter, rFracX, rFracY );
+ }
+
+ if (!aAverage.GetDenominator())
+ throw o3tl::divide_by_zero();
+
+ nRadius = double(nRadius * aAverage);
+}
+
+bool IMapCircleObject::IsEqual( const IMapCircleObject& rEqObj )
+{
+ return ( IMapObject::IsEqual( rEqObj ) &&
+ ( aCenter == rEqObj.aCenter ) &&
+ ( nRadius == rEqObj.nRadius ) );
+}
+
+IMapPolygonObject::IMapPolygonObject( const tools::Polygon& rPoly,
+ const OUString& rURL,
+ const OUString& rAltText,
+ const OUString& rDesc,
+ const OUString& rTarget,
+ const OUString& rName,
+ bool bURLActive,
+ bool bPixelCoords ) :
+ IMapObject ( rURL, rAltText, rDesc, rTarget, rName, bURLActive ),
+ bEllipse ( false )
+{
+ ImpConstruct( rPoly, bPixelCoords );
+}
+
+void IMapPolygonObject::ImpConstruct( const tools::Polygon& rPoly, bool bPixel )
+{
+ if ( bPixel )
+ aPoly = Application::GetDefaultDevice()->PixelToLogic( rPoly, MapMode( MapUnit::Map100thMM ) );
+ else
+ aPoly = rPoly;
+}
+
+
+/******************************************************************************
+|*
+|* Binary export
+|*
+\******************************************************************************/
+
+void IMapPolygonObject::WriteIMapObject( SvStream& rOStm ) const
+{
+ WritePolygon( rOStm, aPoly );
+ rOStm.WriteBool( bEllipse ); // >= Version 2
+ WriteRectangle( rOStm, aEllipse ); // >= Version 2
+}
+
+
+/******************************************************************************
+|*
+|* Binary import
+|*
+\******************************************************************************/
+
+void IMapPolygonObject::ReadIMapObject( SvStream& rIStm )
+{
+ ReadPolygon( rIStm, aPoly );
+
+ // Version >= 2 has additional ellipses information
+ if ( nReadVersion >= 2 )
+ {
+ rIStm.ReadCharAsBool( bEllipse );
+ ReadRectangle( rIStm, aEllipse );
+ }
+}
+
+
+/******************************************************************************
+|*
+|* return type
+|*
+\******************************************************************************/
+
+sal_uInt16 IMapPolygonObject::GetType() const
+{
+ return IMAP_OBJ_POLYGON;
+}
+
+
+/******************************************************************************
+|*
+|* hit test
+|*
+\******************************************************************************/
+
+bool IMapPolygonObject::IsHit( const Point& rPoint ) const
+{
+ return aPoly.IsInside( rPoint );
+}
+
+tools::Polygon IMapPolygonObject::GetPolygon( bool bPixelCoords ) const
+{
+ tools::Polygon aNewPoly;
+
+ if ( bPixelCoords )
+ aNewPoly = Application::GetDefaultDevice()->LogicToPixel( aPoly, MapMode( MapUnit::Map100thMM ) );
+ else
+ aNewPoly = aPoly;
+
+ return aNewPoly;
+}
+
+void IMapPolygonObject::SetExtraEllipse( const tools::Rectangle& rEllipse )
+{
+ if ( aPoly.GetSize() )
+ {
+ bEllipse = true;
+ aEllipse = rEllipse;
+ }
+}
+
+void IMapPolygonObject::Scale( const Fraction& rFracX, const Fraction& rFracY )
+{
+ sal_uInt16 nCount = aPoly.GetSize();
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ Point aScaledPt( aPoly[ i ] );
+
+ if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
+ {
+ SCALEPOINT( aScaledPt, rFracX, rFracY );
+ }
+
+ aPoly[ i ] = aScaledPt;
+ }
+
+ if ( !bEllipse )
+ return;
+
+ Point aTL( aEllipse.TopLeft() );
+ Point aBR( aEllipse.BottomRight() );
+
+ if ( rFracX.GetDenominator() && rFracY.GetDenominator() )
+ {
+ SCALEPOINT( aTL, rFracX, rFracY );
+ SCALEPOINT( aBR, rFracX, rFracY );
+ }
+
+ aEllipse = tools::Rectangle( aTL, aBR );
+}
+
+bool IMapPolygonObject::IsEqual( const IMapPolygonObject& rEqObj )
+{
+ bool bRet = false;
+
+ if ( IMapObject::IsEqual( rEqObj ) )
+ {
+ const tools::Polygon& rEqPoly = rEqObj.aPoly;
+ const sal_uInt16 nCount = aPoly.GetSize();
+ const sal_uInt16 nEqCount = rEqPoly.GetSize();
+
+ if ( nCount == nEqCount )
+ {
+ bool bDifferent = false;
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ if ( aPoly[ i ] != rEqPoly[ i ] )
+ {
+ bDifferent = true;
+ break;
+ }
+ }
+
+ if ( !bDifferent )
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+/******************************************************************************
+|*
+|* Ctor
+|*
+\******************************************************************************/
+
+ImageMap::ImageMap( const OUString& rName )
+: aName( rName )
+{
+}
+
+
+/******************************************************************************
+|*
+|* Copy-Ctor
+|*
+\******************************************************************************/
+
+ImageMap::ImageMap( const ImageMap& rImageMap )
+{
+
+ size_t nCount = rImageMap.GetIMapObjectCount();
+
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ IMapObject* pCopyObj = rImageMap.GetIMapObject( i );
+
+ switch( pCopyObj->GetType() )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ maList.emplace_back( new IMapRectangleObject( *static_cast<IMapRectangleObject*>( pCopyObj ) ) );
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ maList.emplace_back( new IMapCircleObject( *static_cast<IMapCircleObject*>( pCopyObj ) ) );
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ maList.emplace_back( new IMapPolygonObject( *static_cast<IMapPolygonObject*>( pCopyObj ) ) );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ aName = rImageMap.aName;
+}
+
+
+/******************************************************************************
+|*
+|* Dtor
+|*
+\******************************************************************************/
+
+ImageMap::~ImageMap()
+{
+}
+
+
+/******************************************************************************
+|*
+|* release internal memory
+|*
+\******************************************************************************/
+
+void ImageMap::ClearImageMap()
+{
+ maList.clear();
+
+ aName.clear();
+}
+
+
+/******************************************************************************
+|*
+|* assignment operator
+|*
+\******************************************************************************/
+
+ImageMap& ImageMap::operator=( const ImageMap& rImageMap )
+{
+ if (this != &rImageMap)
+ {
+ size_t nCount = rImageMap.GetIMapObjectCount();
+
+ ClearImageMap();
+
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ IMapObject* pCopyObj = rImageMap.GetIMapObject( i );
+
+ switch( pCopyObj->GetType() )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ maList.emplace_back( new IMapRectangleObject( *static_cast<IMapRectangleObject*>(pCopyObj) ) );
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ maList.emplace_back( new IMapCircleObject( *static_cast<IMapCircleObject*>(pCopyObj) ) );
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ maList.emplace_back( new IMapPolygonObject( *static_cast<IMapPolygonObject*>(pCopyObj) ) );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ aName = rImageMap.aName;
+ }
+ return *this;
+}
+
+
+/******************************************************************************
+|*
+|* compare operator I
+|*
+\******************************************************************************/
+
+bool ImageMap::operator==( const ImageMap& rImageMap )
+{
+ const size_t nCount = maList.size();
+ const size_t nEqCount = rImageMap.GetIMapObjectCount();
+ bool bRet = false;
+
+ if ( nCount == nEqCount )
+ {
+ bool bDifferent = ( aName != rImageMap.aName );
+
+ for ( size_t i = 0; ( i < nCount ) && !bDifferent; i++ )
+ {
+ IMapObject* pObj = maList[ i ].get();
+ IMapObject* pEqObj = rImageMap.GetIMapObject( i );
+
+ if ( pObj->GetType() == pEqObj->GetType() )
+ {
+ switch( pObj->GetType() )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ {
+ if ( ! static_cast<IMapRectangleObject*>(pObj)->IsEqual( *static_cast<IMapRectangleObject*>(pEqObj) ) )
+ bDifferent = true;
+ }
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ {
+ if ( ! static_cast<IMapCircleObject*>(pObj)->IsEqual( *static_cast<IMapCircleObject*>(pEqObj) ) )
+ bDifferent = true;
+ }
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ {
+ if ( ! static_cast<IMapPolygonObject*>(pObj)->IsEqual( *static_cast<IMapPolygonObject*>(pEqObj) ) )
+ bDifferent = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ bDifferent = true;
+ }
+
+ if ( !bDifferent )
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+
+/******************************************************************************
+|*
+|* compare operator II
+|*
+\******************************************************************************/
+
+bool ImageMap::operator!=( const ImageMap& rImageMap )
+{
+ return !( *this == rImageMap );
+}
+
+
+/******************************************************************************
+|*
+|* insert new object
+|*
+\******************************************************************************/
+
+void ImageMap::InsertIMapObject( const IMapObject& rIMapObject )
+{
+ switch( rIMapObject.GetType() )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ maList.emplace_back( new IMapRectangleObject( static_cast<const IMapRectangleObject&>( rIMapObject ) ) );
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ maList.emplace_back( new IMapCircleObject( static_cast<const IMapCircleObject&>( rIMapObject ) ) );
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ maList.emplace_back( new IMapPolygonObject( static_cast<const IMapPolygonObject&>( rIMapObject ) ) );
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ImageMap::InsertIMapObject( std::unique_ptr<IMapObject> pNewObject )
+{
+ maList.emplace_back( std::move(pNewObject) );
+}
+
+/******************************************************************************
+|*
+|* hit test
+|*
+\******************************************************************************/
+
+IMapObject* ImageMap::GetHitIMapObject( const Size& rTotalSize,
+ const Size& rDisplaySize,
+ const Point& rRelHitPoint,
+ sal_uLong nFlags )
+{
+ Point aRelPoint( rTotalSize.Width() * rRelHitPoint.X() / rDisplaySize.Width(),
+ rTotalSize.Height() * rRelHitPoint.Y() / rDisplaySize.Height() );
+
+ // transform point to check before checking if flags to mirror etc. are set,
+ if ( nFlags )
+ {
+ if ( nFlags & IMAP_MIRROR_HORZ )
+ aRelPoint.setX( rTotalSize.Width() - aRelPoint.X() );
+
+ if ( nFlags & IMAP_MIRROR_VERT )
+ aRelPoint.setY( rTotalSize.Height() - aRelPoint.Y() );
+ }
+
+ // walk over all objects and execute HitTest
+ IMapObject* pObj = nullptr;
+ for(auto& i : maList) {
+ if ( i->IsHit( aRelPoint ) ) {
+ pObj = i.get();
+ break;
+ }
+ }
+
+ return( pObj ? ( pObj->IsActive() ? pObj : nullptr ) : nullptr );
+}
+
+void ImageMap::Scale( const Fraction& rFracX, const Fraction& rFracY )
+{
+ size_t nCount = maList.size();
+
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ IMapObject* pObj = maList[ i ].get();
+
+ switch( pObj->GetType() )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ static_cast<IMapRectangleObject*>( pObj )->Scale( rFracX, rFracY );
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ static_cast<IMapCircleObject*>( pObj )->Scale( rFracX, rFracY );
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ static_cast<IMapPolygonObject*>( pObj )->Scale( rFracX, rFracY );
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/******************************************************************************
+|*
+|* sequentially write objects
+|*
+\******************************************************************************/
+
+void ImageMap::ImpWriteImageMap( SvStream& rOStm ) const
+{
+ size_t nCount = maList.size();
+
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ auto& pObj = maList[ i ];
+ pObj->Write( rOStm );
+ }
+}
+
+
+/******************************************************************************
+|*
+|* sequentially read objects
+|*
+\******************************************************************************/
+
+void ImageMap::ImpReadImageMap( SvStream& rIStm, size_t nCount )
+{
+ const size_t nMinRecordSize = 12; //circle, three 32bit numbers
+ const size_t nMaxRecords = rIStm.remainingSize() / nMinRecordSize;
+
+ if (nCount > nMaxRecords)
+ {
+ SAL_WARN("svtools.misc", "Parsing error: " << nMaxRecords << " max possible entries, but " <<
+ nCount << " claimed, truncating");
+ nCount = nMaxRecords;
+ }
+
+ // read new objects
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ sal_uInt16 nType;
+
+ rIStm.ReadUInt16( nType );
+ rIStm.SeekRel( -2 );
+
+ switch( nType )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ {
+ IMapRectangleObject* pObj = new IMapRectangleObject;
+ pObj->Read( rIStm );
+ maList.emplace_back( pObj );
+ }
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ {
+ IMapCircleObject* pObj = new IMapCircleObject;
+ pObj->Read( rIStm );
+ maList.emplace_back( pObj );
+ }
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ {
+ IMapPolygonObject* pObj = new IMapPolygonObject;
+ pObj->Read( rIStm );
+ maList.emplace_back( pObj );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/******************************************************************************
+|*
+|* store binary
+|*
+\******************************************************************************/
+
+void ImageMap::Write( SvStream& rOStm ) const
+{
+ IMapCompat* pCompat;
+ OUString aImageName( GetName() );
+ SvStreamEndian nOldFormat = rOStm.GetEndian();
+ sal_uInt16 nCount = static_cast<sal_uInt16>(GetIMapObjectCount());
+ const rtl_TextEncoding eEncoding = osl_getThreadTextEncoding(); //vomit!
+
+ rOStm.SetEndian( SvStreamEndian::LITTLE );
+
+ // write MagicCode
+ rOStm.WriteCharPtr( IMAPMAGIC );
+ rOStm.WriteUInt16( IMAGE_MAP_VERSION );
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aImageName, eEncoding);
+ write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm, OString()); //dummy
+ rOStm.WriteUInt16( nCount );
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aImageName, eEncoding);
+
+ pCompat = new IMapCompat( rOStm, StreamMode::WRITE );
+
+ // here one can insert in newer versions
+
+ delete pCompat;
+
+ ImpWriteImageMap( rOStm );
+
+ rOStm.SetEndian( nOldFormat );
+}
+
+
+/******************************************************************************
+|*
+|* load binary
+|*
+\******************************************************************************/
+
+void ImageMap::Read( SvStream& rIStm )
+{
+ char cMagic[6];
+ SvStreamEndian nOldFormat = rIStm.GetEndian();
+ sal_uInt16 nCount;
+
+ rIStm.SetEndian( SvStreamEndian::LITTLE );
+ rIStm.ReadBytes(cMagic, sizeof(cMagic));
+
+ if ( !memcmp( cMagic, IMAPMAGIC, sizeof( cMagic ) ) )
+ {
+ IMapCompat* pCompat;
+
+ // delete old content
+ ClearImageMap();
+
+ // read on version
+ rIStm.SeekRel( 2 );
+
+ aName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, osl_getThreadTextEncoding());
+ read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm); // Dummy
+ rIStm.ReadUInt16( nCount );
+ read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm); // Dummy
+
+ pCompat = new IMapCompat( rIStm, StreamMode::READ );
+
+ // here one can read in newer versions
+
+ delete pCompat;
+ ImpReadImageMap( rIStm, nCount );
+
+ }
+ else
+ rIStm.SetError( SVSTREAM_GENERALERROR );
+
+ rIStm.SetEndian( nOldFormat );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/imap2.cxx b/vcl/source/treelist/imap2.cxx
new file mode 100644
index 000000000000..d2bcc37a8520
--- /dev/null
+++ b/vcl/source/treelist/imap2.cxx
@@ -0,0 +1,532 @@
+/* -*- 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 <comphelper/string.hxx>
+#include <string.h>
+#include <rtl/strbuf.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/wrkwin.hxx>
+#include <sot/formats.hxx>
+
+#include <svl/urihelper.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/imaprect.hxx>
+#include <vcl/imapcirc.hxx>
+#include <vcl/imappoly.hxx>
+
+#include <math.h>
+
+#define NOTEOL(c) ((c)!='\0')
+
+void IMapObject::AppendCERNCoords(OStringBuffer& rBuf, const Point& rPoint100)
+{
+ const Point aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MapUnit::Map100thMM ) ) );
+
+ rBuf.append('(');
+ rBuf.append(static_cast<sal_Int32>(aPixPt.X()));
+ rBuf.append(',');
+ rBuf.append(static_cast<sal_Int32>(aPixPt.Y()));
+ rBuf.append(") ");
+}
+
+void IMapObject::AppendNCSACoords(OStringBuffer& rBuf, const Point& rPoint100)
+{
+ const Point aPixPt( Application::GetDefaultDevice()->LogicToPixel( rPoint100, MapMode( MapUnit::Map100thMM ) ) );
+
+ rBuf.append(static_cast<sal_Int32>(aPixPt.X()));
+ rBuf.append(',');
+ rBuf.append(static_cast<sal_Int32>(aPixPt.Y()));
+ rBuf.append(' ');
+}
+
+void IMapObject::AppendCERNURL(OStringBuffer& rBuf) const
+{
+ rBuf.append(OUStringToOString(URIHelper::simpleNormalizedMakeRelative("", aURL), osl_getThreadTextEncoding()));
+}
+
+void IMapObject::AppendNCSAURL(OStringBuffer& rBuf) const
+{
+ rBuf.append(OUStringToOString(URIHelper::simpleNormalizedMakeRelative("", aURL), osl_getThreadTextEncoding()));
+ rBuf.append(' ');
+}
+
+void IMapRectangleObject::WriteCERN( SvStream& rOStm ) const
+{
+ OStringBuffer aStrBuf("rectangle ");
+
+ AppendCERNCoords(aStrBuf, aRect.TopLeft());
+ AppendCERNCoords(aStrBuf, aRect.BottomRight());
+ AppendCERNURL(aStrBuf);
+
+ rOStm.WriteLine(aStrBuf.makeStringAndClear());
+}
+
+void IMapRectangleObject::WriteNCSA( SvStream& rOStm ) const
+{
+ OStringBuffer aStrBuf("rect ");
+
+ AppendNCSAURL(aStrBuf);
+ AppendNCSACoords(aStrBuf, aRect.TopLeft());
+ AppendNCSACoords(aStrBuf, aRect.BottomRight());
+
+ rOStm.WriteLine(aStrBuf.makeStringAndClear());
+}
+
+void IMapCircleObject::WriteCERN( SvStream& rOStm ) const
+{
+ OStringBuffer aStrBuf("circle ");
+
+ AppendCERNCoords(aStrBuf, aCenter);
+ aStrBuf.append(nRadius);
+ aStrBuf.append(' ');
+ AppendCERNURL(aStrBuf);
+
+ rOStm.WriteLine(aStrBuf.makeStringAndClear());
+}
+
+void IMapCircleObject::WriteNCSA( SvStream& rOStm ) const
+{
+ OStringBuffer aStrBuf("circle ");
+
+ AppendNCSAURL(aStrBuf);
+ AppendNCSACoords(aStrBuf, aCenter);
+ AppendNCSACoords(aStrBuf, aCenter + Point(nRadius, 0));
+
+ rOStm.WriteLine(aStrBuf.makeStringAndClear());
+}
+
+void IMapPolygonObject::WriteCERN( SvStream& rOStm ) const
+{
+ OStringBuffer aStrBuf("polygon ");
+ const sal_uInt16 nCount = aPoly.GetSize();
+
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ AppendCERNCoords(aStrBuf, aPoly[i]);
+
+ AppendCERNURL(aStrBuf);
+
+ rOStm.WriteLine(aStrBuf.makeStringAndClear());
+}
+
+void IMapPolygonObject::WriteNCSA( SvStream& rOStm ) const
+{
+ OStringBuffer aStrBuf("poly ");
+ const sal_uInt16 nCount = std::min( aPoly.GetSize(), sal_uInt16(100) );
+
+ AppendNCSAURL(aStrBuf);
+
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ AppendNCSACoords(aStrBuf, aPoly[i]);
+
+ rOStm.WriteLine(aStrBuf.makeStringAndClear());
+}
+
+void ImageMap::Write( SvStream& rOStm, sal_uLong nFormat ) const
+{
+ switch( nFormat )
+ {
+ case IMAP_FORMAT_BIN : Write( rOStm ); break;
+ case IMAP_FORMAT_CERN : ImpWriteCERN( rOStm ); break;
+ case IMAP_FORMAT_NCSA : ImpWriteNCSA( rOStm ); break;
+
+ default:
+ break;
+ }
+}
+
+void ImageMap::ImpWriteCERN( SvStream& rOStm ) const
+{
+ size_t nCount = maList.size();
+
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ IMapObject* pObj = maList[ i ].get();
+
+ switch( pObj->GetType() )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ static_cast<IMapRectangleObject*>( pObj )->WriteCERN( rOStm );
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ static_cast<IMapCircleObject*>( pObj )->WriteCERN( rOStm );
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ static_cast<IMapPolygonObject*>( pObj )->WriteCERN( rOStm );
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void ImageMap::ImpWriteNCSA( SvStream& rOStm ) const
+{
+ size_t nCount = maList.size();
+
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ IMapObject* pObj = maList[ i ].get();
+
+ switch( pObj->GetType() )
+ {
+ case IMAP_OBJ_RECTANGLE:
+ static_cast<IMapRectangleObject*>( pObj )->WriteNCSA( rOStm );
+ break;
+
+ case IMAP_OBJ_CIRCLE:
+ static_cast<IMapCircleObject*>( pObj )->WriteNCSA( rOStm );
+ break;
+
+ case IMAP_OBJ_POLYGON:
+ static_cast<IMapPolygonObject*>( pObj )->WriteNCSA( rOStm );
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+sal_uLong ImageMap::Read( SvStream& rIStm, sal_uLong nFormat )
+{
+ sal_uLong nRet = IMAP_ERR_FORMAT;
+
+ if ( nFormat == IMAP_FORMAT_DETECT )
+ nFormat = ImpDetectFormat( rIStm );
+
+ switch ( nFormat )
+ {
+ case IMAP_FORMAT_BIN : Read( rIStm ); break;
+ case IMAP_FORMAT_CERN : ImpReadCERN( rIStm ); break;
+ case IMAP_FORMAT_NCSA : ImpReadNCSA( rIStm ); break;
+
+ default:
+ break;
+ }
+
+ if ( !rIStm.GetError() )
+ nRet = IMAP_ERR_OK;
+
+ return nRet;
+}
+
+void ImageMap::ImpReadCERN( SvStream& rIStm )
+{
+ // delete old content
+ ClearImageMap();
+
+ OString aStr;
+ while ( rIStm.ReadLine( aStr ) )
+ ImpReadCERNLine( aStr );
+}
+
+void ImageMap::ImpReadCERNLine( const OString& rLine )
+{
+ OString aStr = comphelper::string::stripStart(rLine, ' ');
+ aStr = comphelper::string::stripStart(aStr, '\t');
+ aStr = aStr.replaceAll(";", "");
+ aStr = aStr.toAsciiLowerCase();
+
+ const char* pStr = aStr.getStr();
+ char cChar = *pStr++;
+
+ // find instruction
+ OStringBuffer aBuf;
+ while( ( cChar >= 'a' ) && ( cChar <= 'z' ) && NOTEOL( cChar ) )
+ {
+ aBuf.append(cChar);
+ cChar = *pStr++;
+ }
+ OString aToken = aBuf.makeStringAndClear();
+
+ if ( !(NOTEOL( cChar )) )
+ return;
+
+ if ( ( aToken == "rectangle" ) || ( aToken == "rect" ) )
+ {
+ const Point aTopLeft( ImpReadCERNCoords( &pStr ) );
+ const Point aBottomRight( ImpReadCERNCoords( &pStr ) );
+ const OUString aURL( ImpReadCERNURL( &pStr ) );
+ const tools::Rectangle aRect( aTopLeft, aBottomRight );
+
+ maList.emplace_back( new IMapRectangleObject( aRect, aURL, OUString(), OUString(), OUString(), OUString() ) );
+ }
+ else if ( ( aToken == "circle" ) || ( aToken == "circ" ) )
+ {
+ const Point aCenter( ImpReadCERNCoords( &pStr ) );
+ const long nRadius = ImpReadCERNRadius( &pStr );
+ const OUString aURL( ImpReadCERNURL( &pStr ) );
+
+ maList.emplace_back( new IMapCircleObject( aCenter, nRadius, aURL, OUString(), OUString(), OUString(), OUString() ) );
+ }
+ else if ( ( aToken == "polygon" ) || ( aToken == "poly" ) )
+ {
+ const sal_uInt16 nCount = comphelper::string::getTokenCount(aStr, '(') - 1;
+ tools::Polygon aPoly( nCount );
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ aPoly[ i ] = ImpReadCERNCoords( &pStr );
+
+ const OUString aURL = ImpReadCERNURL( &pStr );
+
+ maList.emplace_back( new IMapPolygonObject( aPoly, aURL, OUString(), OUString(), OUString(), OUString() ) );
+ }
+}
+
+Point ImageMap::ImpReadCERNCoords( const char** ppStr )
+{
+ OUStringBuffer aStrX;
+ OUStringBuffer aStrY;
+ Point aPt;
+ char cChar = *(*ppStr)++;
+
+ while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
+ cChar = *(*ppStr)++;
+
+ if ( NOTEOL( cChar ) )
+ {
+ while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
+ {
+ aStrX.append( cChar );
+ cChar = *(*ppStr)++;
+ }
+
+ if ( NOTEOL( cChar ) )
+ {
+ while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
+ cChar = *(*ppStr)++;
+
+ while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
+ {
+ aStrY.append( cChar );
+ cChar = *(*ppStr)++;
+ }
+
+ if ( NOTEOL( cChar ) )
+ while( NOTEOL( cChar ) && ( cChar != ')' ) )
+ cChar = *(*ppStr)++;
+
+ aPt = Point( aStrX.makeStringAndClear().toInt32(), aStrY.makeStringAndClear().toInt32() );
+ }
+ }
+
+ return aPt;
+}
+
+long ImageMap::ImpReadCERNRadius( const char** ppStr )
+{
+ OUStringBuffer aStr;
+ char cChar = *(*ppStr)++;
+
+ while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
+ cChar = *(*ppStr)++;
+
+ if ( NOTEOL( cChar ) )
+ {
+ while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
+ {
+ aStr.append( cChar );
+ cChar = *(*ppStr)++;
+ }
+ }
+
+ return aStr.makeStringAndClear().toInt32();
+}
+
+OUString ImageMap::ImpReadCERNURL( const char** ppStr )
+{
+ OUString aStr(OUString::createFromAscii(*ppStr));
+
+ aStr = comphelper::string::stripStart(aStr, ' ');
+ aStr = comphelper::string::stripStart(aStr, '\t');
+ aStr = comphelper::string::stripEnd(aStr, ' ');
+ aStr = comphelper::string::stripEnd(aStr, '\t');
+
+ return INetURLObject::GetAbsURL( "", aStr );
+}
+
+void ImageMap::ImpReadNCSA( SvStream& rIStm )
+{
+ // delete old content
+ ClearImageMap();
+
+ OString aStr;
+ while ( rIStm.ReadLine( aStr ) )
+ ImpReadNCSALine( aStr );
+}
+
+void ImageMap::ImpReadNCSALine( const OString& rLine )
+{
+ OString aStr = comphelper::string::stripStart(rLine, ' ');
+ aStr = comphelper::string::stripStart(aStr, '\t');
+ aStr = aStr.replaceAll(";", "");
+ aStr = aStr.toAsciiLowerCase();
+
+ const char* pStr = aStr.getStr();
+ char cChar = *pStr++;
+
+ // find instruction
+ OStringBuffer aBuf;
+ while( ( cChar >= 'a' ) && ( cChar <= 'z' ) && NOTEOL( cChar ) )
+ {
+ aBuf.append(cChar);
+ cChar = *pStr++;
+ }
+ OString aToken = aBuf.makeStringAndClear();
+
+ if ( !(NOTEOL( cChar )) )
+ return;
+
+ if ( aToken == "rect" )
+ {
+ const OUString aURL( ImpReadNCSAURL( &pStr ) );
+ const Point aTopLeft( ImpReadNCSACoords( &pStr ) );
+ const Point aBottomRight( ImpReadNCSACoords( &pStr ) );
+ const tools::Rectangle aRect( aTopLeft, aBottomRight );
+
+ maList.emplace_back( new IMapRectangleObject( aRect, aURL, OUString(), OUString(), OUString(), OUString() ) );
+ }
+ else if ( aToken == "circle" )
+ {
+ const OUString aURL( ImpReadNCSAURL( &pStr ) );
+ const Point aCenter( ImpReadNCSACoords( &pStr ) );
+ const Point aDX( aCenter - ImpReadNCSACoords( &pStr ) );
+ long nRadius = static_cast<long>(sqrt( static_cast<double>(aDX.X()) * aDX.X() +
+ static_cast<double>(aDX.Y()) * aDX.Y() ));
+
+ maList.emplace_back( new IMapCircleObject( aCenter, nRadius, aURL, OUString(), OUString(), OUString(), OUString() ) );
+ }
+ else if ( aToken == "poly" )
+ {
+ const sal_uInt16 nCount = comphelper::string::getTokenCount(aStr, ',') - 1;
+ const OUString aURL( ImpReadNCSAURL( &pStr ) );
+ tools::Polygon aPoly( nCount );
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ aPoly[ i ] = ImpReadNCSACoords( &pStr );
+
+ maList.emplace_back( new IMapPolygonObject( aPoly, aURL, OUString(), OUString(), OUString(), OUString() ) );
+ }
+}
+
+OUString ImageMap::ImpReadNCSAURL( const char** ppStr )
+{
+ OUStringBuffer aStr;
+ char cChar = *(*ppStr)++;
+
+ while( NOTEOL( cChar ) && ( ( cChar == ' ' ) || ( cChar == '\t' ) ) )
+ cChar = *(*ppStr)++;
+
+ if ( NOTEOL( cChar ) )
+ {
+ while( NOTEOL( cChar ) && ( cChar != ' ' ) && ( cChar != '\t' ) )
+ {
+ aStr.append( cChar );
+ cChar = *(*ppStr)++;
+ }
+ }
+
+ return INetURLObject::GetAbsURL( "", aStr.makeStringAndClear() );
+}
+
+Point ImageMap::ImpReadNCSACoords( const char** ppStr )
+{
+ OUStringBuffer aStrX;
+ OUStringBuffer aStrY;
+ Point aPt;
+ char cChar = *(*ppStr)++;
+
+ while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
+ cChar = *(*ppStr)++;
+
+ if ( NOTEOL( cChar ) )
+ {
+ while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
+ {
+ aStrX.append( cChar );
+ cChar = *(*ppStr)++;
+ }
+
+ if ( NOTEOL( cChar ) )
+ {
+ while( NOTEOL( cChar ) && ( ( cChar < '0' ) || ( cChar > '9' ) ) )
+ cChar = *(*ppStr)++;
+
+ while( NOTEOL( cChar ) && ( cChar >= '0' ) && ( cChar <= '9' ) )
+ {
+ aStrY.append( cChar );
+ cChar = *(*ppStr)++;
+ }
+
+ aPt = Point( aStrX.makeStringAndClear().toInt32(), aStrY.makeStringAndClear().toInt32() );
+ }
+ }
+
+ return aPt;
+}
+
+sal_uLong ImageMap::ImpDetectFormat( SvStream& rIStm )
+{
+ sal_uInt64 nPos = rIStm.Tell();
+ sal_uLong nRet = IMAP_FORMAT_BIN;
+ char cMagic[6];
+
+ rIStm.ReadBytes(cMagic, sizeof(cMagic));
+
+ // if we do not have an internal formats
+ // we check the format
+ if ( memcmp( cMagic, IMAPMAGIC, sizeof( cMagic ) ) )
+ {
+ long nCount = 128;
+
+ rIStm.Seek( nPos );
+ OString aStr;
+ while ( rIStm.ReadLine( aStr ) && nCount-- )
+ {
+ aStr = aStr.toAsciiLowerCase();
+
+ if ( (aStr.indexOf("rect") != -1) ||
+ (aStr.indexOf("circ") != -1) ||
+ (aStr.indexOf("poly") != -1) )
+ {
+ if ( ( aStr.indexOf('(') != -1 ) &&
+ ( aStr.indexOf(')') != -1 ) )
+ {
+ nRet = IMAP_FORMAT_CERN;
+ }
+ else
+ nRet = IMAP_FORMAT_NCSA;
+
+ break;
+ }
+ }
+ }
+
+ rIStm.Seek( nPos );
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/imap3.cxx b/vcl/source/treelist/imap3.cxx
new file mode 100644
index 000000000000..32c6523e4cf6
--- /dev/null
+++ b/vcl/source/treelist/imap3.cxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/imap.hxx>
+
+#include <tools/debug.hxx>
+
+
+/******************************************************************************
+|*
+|* Ctor
+|*
+\******************************************************************************/
+
+IMapCompat::IMapCompat( SvStream& rStm, const StreamMode nStreamMode )
+ : pRWStm(&rStm)
+ , nCompatPos(0)
+ , nTotalSize(0)
+ , nStmMode(nStreamMode)
+{
+ DBG_ASSERT( nStreamMode == StreamMode::READ || nStreamMode == StreamMode::WRITE, "Wrong Mode!" );
+
+ if ( pRWStm->GetError() )
+ return;
+
+ if ( nStmMode == StreamMode::WRITE )
+ {
+ nCompatPos = pRWStm->Tell();
+ pRWStm->SeekRel( 4 );
+ nTotalSize = nCompatPos + 4;
+ }
+ else
+ {
+ sal_uInt32 nTotalSizeTmp;
+ pRWStm->ReadUInt32( nTotalSizeTmp );
+ nTotalSize = nTotalSizeTmp;
+ nCompatPos = pRWStm->Tell();
+ }
+}
+
+
+/******************************************************************************
+|*
+|* Dtor
+|*
+\******************************************************************************/
+
+IMapCompat::~IMapCompat()
+{
+ if ( pRWStm->GetError() )
+ return;
+
+ if ( nStmMode == StreamMode::WRITE )
+ {
+ const sal_uInt64 nEndPos = pRWStm->Tell();
+
+ pRWStm->Seek( nCompatPos );
+ pRWStm->WriteUInt32( nEndPos - nTotalSize );
+ pRWStm->Seek( nEndPos );
+ }
+ else
+ {
+ const sal_uInt64 nReadSize = pRWStm->Tell() - nCompatPos;
+
+ if ( nTotalSize > nReadSize )
+ pRWStm->SeekRel( nTotalSize - nReadSize );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/inetimg.cxx b/vcl/source/treelist/inetimg.cxx
new file mode 100644
index 000000000000..fe549a94ce3a
--- /dev/null
+++ b/vcl/source/treelist/inetimg.cxx
@@ -0,0 +1,135 @@
+/* -*- 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 <osl/thread.h>
+#include <sot/formats.hxx>
+#include <tools/stream.hxx>
+
+#include <vcl/inetimg.hxx>
+
+static const sal_Unicode TOKEN_SEPARATOR = '\001';
+
+void INetImage::Write( SvStream& rOStm, SotClipboardFormatId nFormat ) const
+{
+ switch( nFormat )
+ {
+ case SotClipboardFormatId::INET_IMAGE:
+ {
+ OUString sString(
+ aImageURL + OUStringLiteral1(TOKEN_SEPARATOR) + aTargetURL
+ + OUStringLiteral1(TOKEN_SEPARATOR) + aTargetFrame
+ + OUStringLiteral1(TOKEN_SEPARATOR) /* + aAlternateText */
+ + OUStringLiteral1(TOKEN_SEPARATOR)
+ + OUString::number(aSizePixel.Width())
+ + OUStringLiteral1(TOKEN_SEPARATOR)
+ + OUString::number(aSizePixel.Height()));
+
+ OString sOut(OUStringToOString(sString,
+ RTL_TEXTENCODING_UTF8));
+
+ rOStm.WriteBytes(sOut.getStr(), sOut.getLength());
+ static const sal_Char aEndChar[2] = { 0 };
+ rOStm.WriteBytes(aEndChar, sizeof(aEndChar));
+ }
+ break;
+
+ case SotClipboardFormatId::NETSCAPE_IMAGE:
+ break;
+ default: break;
+ }
+}
+
+bool INetImage::Read( SvStream& rIStm, SotClipboardFormatId nFormat )
+{
+ bool bRet = false;
+ switch( nFormat )
+ {
+ case SotClipboardFormatId::INET_IMAGE:
+ {
+ OUString sINetImg = read_zeroTerminated_uInt8s_ToOUString(rIStm, RTL_TEXTENCODING_UTF8);
+ sal_Int32 nStart = 0;
+ aImageURL = sINetImg.getToken( 0, TOKEN_SEPARATOR, nStart );
+ aTargetURL = sINetImg.getToken( 0, TOKEN_SEPARATOR, nStart );
+ aTargetFrame = sINetImg.getToken( 0, TOKEN_SEPARATOR, nStart );
+ /*aAlternateText =*/ sINetImg.getToken( 0, TOKEN_SEPARATOR, nStart );
+ aSizePixel.setWidth( sINetImg.getToken( 0, TOKEN_SEPARATOR,
+ nStart ).toInt32() );
+ aSizePixel.setHeight( sINetImg.getToken( 0, TOKEN_SEPARATOR,
+ nStart ).toInt32() );
+ bRet = !sINetImg.isEmpty();
+ }
+ break;
+
+ case SotClipboardFormatId::NETSCAPE_IMAGE:
+ {
+/*
+ --> structure size MUST - alignment of 4!
+ int iSize; // size of all data, including variable length strings
+ sal_Bool bIsMap; // For server side maps
+ sal_Int32 iWidth; // Fixed size data correspond to fields in LO_ImageDataStruct
+ sal_Int32 iHeight; // and EDT_ImageData
+ sal_Int32 iHSpace;
+ sal_Int32 iVSpace;
+ sal_Int32 iBorder;
+ int iLowResOffset; // Offsets into string_data. If 0, string is NULL (not used)
+ int iAltOffset; // (alternate text?)
+ int iAnchorOffset; // HREF in image
+ int iExtraHTML_Offset; // Extra HTML (stored in CImageElement)
+ sal_Char pImageURL[1]; // Append all variable-length strings starting here
+*/
+ rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding();
+ sal_Int32 nVal, nAnchorOffset, nAltOffset;
+ sal_uInt64 nFilePos;
+
+ nFilePos = rIStm.Tell();
+ // skip over iSize (int), bIsMao ( sal_Bool ) alignment of 4 !!!!
+ rIStm.SeekRel( 8 );
+ rIStm.ReadInt32( nVal ); aSizePixel.setWidth( nVal );
+ rIStm.ReadInt32( nVal ); aSizePixel.setHeight( nVal );
+ // skip over iHSpace, iVSpace, iBorder, iLowResOffset
+ rIStm.SeekRel( 3 * sizeof( sal_Int32 ) + sizeof( int ) );
+ rIStm.ReadInt32( nAltOffset );
+ rIStm.ReadInt32( nAnchorOffset );
+ // skip over iExtraHTML_Offset
+ rIStm.SeekRel( sizeof( int ) );
+
+ aImageURL = read_zeroTerminated_uInt8s_ToOUString(rIStm, eSysCSet);
+ if( nAltOffset )
+ {
+ rIStm.Seek( nFilePos + nAltOffset );
+ /*aAlternateText =*/ read_zeroTerminated_uInt8s_ToOUString(rIStm, eSysCSet);
+ }
+
+ if( nAnchorOffset )
+ {
+ rIStm.Seek( nFilePos + nAnchorOffset );
+ aTargetURL = read_zeroTerminated_uInt8s_ToOUString(rIStm, eSysCSet);
+ }
+ else if( !aTargetURL.isEmpty() )
+ aTargetURL.clear();
+
+ bRet = ERRCODE_NONE == rIStm.GetError();
+ }
+ break;
+ default: break;
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/svimpbox.cxx b/vcl/source/treelist/svimpbox.cxx
new file mode 100644
index 000000000000..5b37c1b6960c
--- /dev/null
+++ b/vcl/source/treelist/svimpbox.cxx
@@ -0,0 +1,3387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <vcl/salnativewidgets.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+
+#include <cstdlib>
+#include <memory>
+#include <stack>
+
+#include <vcl/treelistbox.hxx>
+#include <vcl/svlbitm.hxx>
+#include <vcl/svimpbox.hxx>
+#include <rtl/instance.hxx>
+#include <tools/wintypes.hxx>
+#include <bitmaps.hlst>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.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)
+ : aHorSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_HSCROLL))
+ , aScrBarBox(VclPtr<ScrollBarBox>::Create(pLBView))
+ , aFctSet(this, pLBView)
+ , bAreChildrenTransient(true)
+ , mbForceMakeVisible (false)
+ , aVerSBar(VclPtr<ScrollBar>::Create(pLBView, WB_DRAG | WB_VSCROLL))
+ , aOutputSize(0, 0)
+ , mbNoAutoCurEntry(false)
+ , aSelEng(pLBView, nullptr)
+ , nNextVerVisSize(0)
+{
+ osl_atomic_increment(&s_nImageRefCount);
+ pView = pLBView;
+ pTree = pLBTree;
+ aSelEng.SetFunctionSet( static_cast<FunctionSet*>(&aFctSet) );
+ aSelEng.ExpandSelectionOnMouseMove( false );
+ SetStyle( nWinStyle );
+ SetSelectionMode( SelectionMode::Single );
+ SetDragDropMode( DragDropMode::NONE );
+
+ aVerSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollUpDownHdl ) );
+ aHorSBar->SetScrollHdl( LINK( this, SvImpLBox, ScrollLeftRightHdl ) );
+ aHorSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
+ aVerSBar->SetEndScrollHdl( LINK( this, SvImpLBox, EndScrollHdl ) );
+ aVerSBar->SetRange( Range(0,0) );
+ aVerSBar->Hide();
+ aHorSBar->SetRange( Range(0,0) );
+ aHorSBar->SetPageSize( 24 ); // pixels
+ aHorSBar->SetLineSize( 8 ); // pixels
+
+ nHorSBarHeight = static_cast<short>(aHorSBar->GetSizePixel().Height());
+ nVerSBarWidth = static_cast<short>(aVerSBar->GetSizePixel().Width());
+
+ pStartEntry = nullptr;
+ pCursor = nullptr;
+ pAnchor = nullptr;
+ nVisibleCount = 0; // number of rows of data in control
+ nNodeBmpTabDistance = NODE_BMP_TABDIST_NOTVALID;
+ nNodeBmpWidth = 0;
+
+ bAsyncBeginDrag = false;
+ aAsyncBeginDragIdle.SetPriority( TaskPriority::HIGHEST );
+ aAsyncBeginDragIdle.SetInvokeHandler( LINK(this,SvImpLBox,BeginDragHdl));
+ // button animation in listbox
+ pActiveButton = nullptr;
+ pActiveEntry = nullptr;
+ pActiveTab = nullptr;
+
+ nFlags = LBoxFlags::NONE;
+ nCurTabPos = FIRST_ENTRY_TAB;
+
+ aEditIdle.SetPriority( TaskPriority::LOWEST );
+ aEditIdle.SetInvokeHandler( LINK(this,SvImpLBox,EditTimerCall) );
+
+ nMostRight = -1;
+ pMostRightEntry = nullptr;
+ nCurUserEvent = nullptr;
+
+ bUpdateMode = true;
+ bInVScrollHdl = false;
+ nFlags |= LBoxFlags::Filling;
+
+ bSubLstOpRet = bSubLstOpLR = bContextMenuHandling = bIsCellFocusEnabled = false;
+ bSubLstOpDblClick = true;
+}
+
+SvImpLBox::~SvImpLBox()
+{
+ aEditIdle.Stop();
+ StopUserEvent();
+
+ if ( osl_atomic_decrement(&s_nImageRefCount) == 0 )
+ {
+ DELETEZ(s_pDefCollapsed);
+ DELETEZ(s_pDefExpanded);
+ }
+ aVerSBar.disposeAndClear();
+ aHorSBar.disposeAndClear();
+ aScrBarBox.disposeAndClear();
+}
+
+void SvImpLBox::UpdateStringSorter()
+{
+ const css::lang::Locale& rNewLocale = Application::GetSettings().GetLanguageTag().getLocale();
+
+ if( m_pStringSorter )
+ {
+ // different Locale from the older one, drop it and force recreate
+ const css::lang::Locale &aLocale = m_pStringSorter->getLocale();
+ if( aLocale.Language != rNewLocale.Language ||
+ aLocale.Country != rNewLocale.Country ||
+ aLocale.Variant != rNewLocale.Variant )
+ m_pStringSorter.reset();
+ }
+
+ if( !m_pStringSorter )
+ {
+ m_pStringSorter.reset(new comphelper::string::NaturalStringSorter(
+ ::comphelper::getProcessComponentContext(),
+ rNewLocale));
+ }
+}
+
+short SvImpLBox::UpdateContextBmpWidthVector( SvTreeListEntry const * pEntry, short nWidth )
+{
+ DBG_ASSERT( pView->pModel, "View and Model aren't valid!" );
+
+ sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
+ // initialize vector if necessary
+ std::vector< short >::size_type nSize = aContextBmpWidthVector.size();
+ while ( nDepth > nSize )
+ {
+ aContextBmpWidthVector.resize( nSize + 1 );
+ aContextBmpWidthVector.at( nSize ) = nWidth;
+ ++nSize;
+ }
+ if( aContextBmpWidthVector.size() == nDepth )
+ {
+ aContextBmpWidthVector.resize( nDepth + 1 );
+ aContextBmpWidthVector.at( nDepth ) = 0;
+ }
+ short nContextBmpWidth = aContextBmpWidthVector[ nDepth ];
+ if( nContextBmpWidth < nWidth )
+ {
+ aContextBmpWidthVector.at( nDepth ) = nWidth;
+ return nWidth;
+ }
+ else
+ return nContextBmpWidth;
+}
+
+void SvImpLBox::UpdateContextBmpWidthVectorFromMovedEntry( SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT( pEntry, "Moved Entry is invalid!" );
+
+ SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry->GetFirstItem(SvLBoxItemType::ContextBmp) );
+ short nExpWidth = static_cast<short>(pBmpItem->GetBitmap1().GetSizePixel().Width());
+ short nColWidth = static_cast<short>(pBmpItem->GetBitmap2().GetSizePixel().Width());
+ short nMax = std::max(nExpWidth, nColWidth);
+ UpdateContextBmpWidthVector( pEntry, nMax );
+
+ if( pEntry->HasChildren() ) // recursive call, whether expanded or not
+ {
+ SvTreeListEntry* pChild = pView->FirstChild( pEntry );
+ DBG_ASSERT( pChild, "The first child is invalid!" );
+ do
+ {
+ UpdateContextBmpWidthVectorFromMovedEntry( pChild );
+ pChild = pView->Next( pChild );
+ } while ( pChild );
+ }
+}
+
+void SvImpLBox::UpdateContextBmpWidthMax( SvTreeListEntry const * pEntry )
+{
+ sal_uInt16 nDepth = pView->pModel->GetDepth( pEntry );
+ if( aContextBmpWidthVector.empty() )
+ return;
+ short nWidth = aContextBmpWidthVector[ nDepth ];
+ if( nWidth != pView->nContextBmpWidthMax ) {
+ pView->nContextBmpWidthMax = nWidth;
+ nFlags |= LBoxFlags::IgnoreChangedTabs;
+ pView->SetTabs();
+ nFlags &= ~LBoxFlags::IgnoreChangedTabs;
+ }
+}
+
+void SvImpLBox::CalcCellFocusRect( SvTreeListEntry const * pEntry, tools::Rectangle& rRect )
+{
+ if ( !(pEntry && bIsCellFocusEnabled) )
+ return;
+
+ if ( nCurTabPos > FIRST_ENTRY_TAB )
+ {
+ SvLBoxItem& rItem = pCursor->GetItem( nCurTabPos );
+ rRect.SetLeft( pView->GetTab( pCursor, &rItem )->GetPos() );
+ }
+ if (pCursor->ItemCount() > static_cast<size_t>(nCurTabPos+1))
+ {
+ SvLBoxItem& rNextItem = pCursor->GetItem( nCurTabPos + 1 );
+ long nRight = pView->GetTab( pCursor, &rNextItem )->GetPos() - 1;
+ if ( nRight < rRect.Right() )
+ rRect.SetRight( nRight );
+ }
+}
+
+void SvImpLBox::SetStyle( WinBits i_nWinStyle )
+{
+ m_nStyle = i_nWinStyle;
+ if ( ( m_nStyle & WB_SIMPLEMODE) && ( aSelEng.GetSelectionMode() == SelectionMode::Multiple ) )
+ aSelEng.AddAlways( true );
+}
+
+void SvImpLBox::SetNoAutoCurEntry( bool b )
+{
+ mbNoAutoCurEntry = b;
+}
+
+// don't touch the model any more
+void SvImpLBox::Clear()
+{
+ StopUserEvent();
+ pStartEntry = nullptr;
+ pAnchor = nullptr;
+
+ pActiveButton = nullptr;
+ pActiveEntry = nullptr;
+ pActiveTab = nullptr;
+
+ nMostRight = -1;
+ pMostRightEntry = nullptr;
+
+ // don't touch the cursor any more
+ if( pCursor )
+ {
+ if( pView->HasFocus() )
+ pView->HideFocus();
+ pCursor = nullptr;
+ }
+ aVerSBar->Hide();
+ aVerSBar->SetThumbPos( 0 );
+ Range aRange( 0, 0 );
+ aVerSBar->SetRange( aRange );
+ aOutputSize = pView->Control::GetOutputSizePixel();
+ aHorSBar->Hide();
+ aHorSBar->SetThumbPos( 0 );
+ MapMode aMapMode( pView->GetMapMode());
+ aMapMode.SetOrigin( Point(0,0) );
+ pView->Control::SetMapMode( aMapMode );
+ aHorSBar->SetRange( aRange );
+ aHorSBar->SetSizePixel(Size(aOutputSize.Width(),nHorSBarHeight));
+ pView->SetClipRegion();
+ if( GetUpdateMode() )
+ pView->Invalidate( GetVisibleArea() );
+ nFlags |= LBoxFlags::Filling;
+ if( !aHorSBar->IsVisible() && !aVerSBar->IsVisible() )
+ aScrBarBox->Hide();
+
+ aContextBmpWidthVector.clear();
+
+ CallEventListeners( VclEventId::ListboxItemRemoved );
+}
+
+// *********************************************************************
+// Paint, navigate, scroll
+// *********************************************************************
+
+IMPL_LINK_NOARG(SvImpLBox, EndScrollHdl, ScrollBar*, void)
+{
+ if( nFlags & LBoxFlags::EndScrollSetVisSize )
+ {
+ aVerSBar->SetVisibleSize( nNextVerVisSize );
+ nFlags &= ~LBoxFlags::EndScrollSetVisSize;
+ }
+ EndScroll();
+}
+
+// handler for vertical scrollbar
+
+IMPL_LINK( SvImpLBox, ScrollUpDownHdl, ScrollBar *, pScrollBar, void )
+{
+ DBG_ASSERT(!bInVScrollHdl,"Scroll handler out-paces itself!");
+ long nDelta = pScrollBar->GetDelta();
+ if( !nDelta )
+ return;
+
+ nFlags &= (~LBoxFlags::Filling);
+
+ bInVScrollHdl = true;
+
+ if( pView->IsEditingActive() )
+ {
+ pView->EndEditing( true ); // Cancel
+ pView->Update();
+ }
+ BeginScroll();
+
+ if( nDelta > 0 )
+ {
+ if( nDelta == 1 )
+ CursorDown();
+ else
+ PageDown( static_cast<sal_uInt16>(nDelta) );
+ }
+ else
+ {
+ nDelta *= -1;
+ if( nDelta == 1 )
+ CursorUp();
+ else
+ PageUp( static_cast<sal_uInt16>(nDelta) );
+ }
+ bInVScrollHdl = false;
+}
+
+
+void SvImpLBox::CursorDown()
+{
+ if (!pStartEntry)
+ return;
+
+ SvTreeListEntry* pNextFirstToDraw = pView->NextVisible(pStartEntry);
+ if( pNextFirstToDraw )
+ {
+ nFlags &= (~LBoxFlags::Filling);
+ ShowCursor( false );
+ pView->Update();
+ pStartEntry = pNextFirstToDraw;
+ tools::Rectangle aArea( GetVisibleArea() );
+ pView->Scroll( 0, -(pView->GetEntryHeight()), aArea, ScrollFlags::NoChildren );
+ pView->Update();
+ ShowCursor( true );
+ pView->NotifyScrolled();
+ }
+}
+
+void SvImpLBox::CursorUp()
+{
+ if (!pStartEntry)
+ return;
+
+ SvTreeListEntry* pPrevFirstToDraw = pView->PrevVisible(pStartEntry);
+ if( !pPrevFirstToDraw )
+ return;
+
+ nFlags &= (~LBoxFlags::Filling);
+ long nEntryHeight = pView->GetEntryHeight();
+ ShowCursor( false );
+ pView->Update();
+ pStartEntry = pPrevFirstToDraw;
+ tools::Rectangle aArea( GetVisibleArea() );
+ aArea.AdjustBottom( -nEntryHeight );
+ pView->Scroll( 0, nEntryHeight, aArea, ScrollFlags::NoChildren );
+ pView->Update();
+ ShowCursor( true );
+ pView->NotifyScrolled();
+}
+
+void SvImpLBox::PageDown( sal_uInt16 nDelta )
+{
+ sal_uInt16 nRealDelta = nDelta;
+
+ if( !nDelta )
+ return;
+
+ if (!pStartEntry)
+ return;
+
+ SvTreeListEntry* pNext = pView->NextVisible(pStartEntry, nRealDelta);
+ if( pNext == pStartEntry )
+ return;
+
+ ShowCursor( false );
+
+ nFlags &= (~LBoxFlags::Filling);
+ pView->Update();
+ pStartEntry = pNext;
+
+ if( nRealDelta >= nVisibleCount )
+ {
+ pView->Invalidate( GetVisibleArea() );
+ pView->Update();
+ }
+ else
+ {
+ tools::Rectangle aArea( GetVisibleArea() );
+ long nScroll = pView->GetEntryHeight() * static_cast<long>(nRealDelta);
+ nScroll = -nScroll;
+ pView->Update();
+ pView->Scroll( 0, nScroll, aArea, ScrollFlags::NoChildren );
+ pView->Update();
+ pView->NotifyScrolled();
+ }
+
+ ShowCursor( true );
+}
+
+void SvImpLBox::PageUp( sal_uInt16 nDelta )
+{
+ sal_uInt16 nRealDelta = nDelta;
+ if( !nDelta )
+ return;
+
+ if (!pStartEntry)
+ return;
+
+ SvTreeListEntry* pPrev = pView->PrevVisible(pStartEntry, nRealDelta);
+ if( pPrev == pStartEntry )
+ return;
+
+ nFlags &= (~LBoxFlags::Filling);
+ ShowCursor( false );
+
+ pView->Update();
+ pStartEntry = pPrev;
+ if( nRealDelta >= nVisibleCount )
+ {
+ pView->Invalidate( GetVisibleArea() );
+ pView->Update();
+ }
+ else
+ {
+ long nEntryHeight = pView->GetEntryHeight();
+ tools::Rectangle aArea( GetVisibleArea() );
+ pView->Update();
+ pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, ScrollFlags::NoChildren );
+ pView->Update();
+ pView->NotifyScrolled();
+ }
+
+ ShowCursor( true );
+}
+
+void SvImpLBox::KeyUp( bool bPageUp )
+{
+ if( !aVerSBar->IsVisible() )
+ return;
+
+ long nDelta;
+ if( bPageUp )
+ nDelta = aVerSBar->GetPageSize();
+ else
+ nDelta = 1;
+
+ long nThumbPos = aVerSBar->GetThumbPos();
+
+ if( nThumbPos < nDelta )
+ nDelta = nThumbPos;
+
+ if( nDelta <= 0 )
+ return;
+
+ nFlags &= (~LBoxFlags::Filling);
+ BeginScroll();
+
+ aVerSBar->SetThumbPos( nThumbPos - nDelta );
+ if( bPageUp )
+ PageUp( static_cast<short>(nDelta) );
+ else
+ CursorUp();
+
+ EndScroll();
+}
+
+
+void SvImpLBox::KeyDown( bool bPageDown )
+{
+ if( !aVerSBar->IsVisible() )
+ return;
+
+ long nDelta;
+ if( bPageDown )
+ nDelta = aVerSBar->GetPageSize();
+ else
+ nDelta = 1;
+
+ long nThumbPos = aVerSBar->GetThumbPos();
+ long nVisibleSize = aVerSBar->GetVisibleSize();
+ long nRange = aVerSBar->GetRange().Len();
+
+ long nTmp = nThumbPos+nVisibleSize;
+ while( (nDelta > 0) && (nTmp+nDelta) >= nRange )
+ nDelta--;
+
+ if( nDelta <= 0 )
+ return;
+
+ nFlags &= (~LBoxFlags::Filling);
+ BeginScroll();
+
+ aVerSBar->SetThumbPos( nThumbPos+nDelta );
+ if( bPageDown )
+ PageDown( static_cast<short>(nDelta) );
+ else
+ CursorDown();
+
+ EndScroll();
+}
+
+
+void SvImpLBox::InvalidateEntriesFrom( long nY ) const
+{
+ if( !(nFlags & LBoxFlags::InPaint ))
+ {
+ tools::Rectangle aRect( GetVisibleArea() );
+ aRect.SetTop( nY );
+ pView->Invalidate( aRect );
+ }
+}
+
+void SvImpLBox::InvalidateEntry( long nY ) const
+{
+ if( !(nFlags & LBoxFlags::InPaint ))
+ {
+ tools::Rectangle aRect( GetVisibleArea() );
+ long nMaxBottom = aRect.Bottom();
+ aRect.SetTop( nY );
+ aRect.SetBottom( nY ); aRect.AdjustBottom(pView->GetEntryHeight() );
+ if( aRect.Top() > nMaxBottom )
+ return;
+ if( aRect.Bottom() > nMaxBottom )
+ aRect.SetBottom( nMaxBottom );
+ pView->Invalidate( aRect );
+ }
+}
+
+void SvImpLBox::InvalidateEntry( SvTreeListEntry* pEntry )
+{
+ if( GetUpdateMode() )
+ {
+ long nPrev = nMostRight;
+ SetMostRight( pEntry );
+ if( nPrev < nMostRight )
+ ShowVerSBar();
+ }
+ if( !(nFlags & LBoxFlags::InPaint ))
+ {
+ bool bHasFocusRect = false;
+ if( pEntry==pCursor && pView->HasFocus() )
+ {
+ bHasFocusRect = true;
+ ShowCursor( false );
+ }
+ InvalidateEntry( GetEntryLine( pEntry ) );
+ if( bHasFocusRect )
+ ShowCursor( true );
+ }
+}
+
+
+void SvImpLBox::RecalcFocusRect()
+{
+ if( pView->HasFocus() && pCursor )
+ {
+ pView->HideFocus();
+ long nY = GetEntryLine( pCursor );
+ tools::Rectangle aRect = pView->GetFocusRect( pCursor, nY );
+ CalcCellFocusRect( pCursor, aRect );
+ vcl::Region aOldClip( pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ pView->SetClipRegion( aClipRegion );
+ pView->ShowFocus( aRect );
+ pView->SetClipRegion( aOldClip );
+ }
+}
+
+
+// Sets cursor. When using SingleSelection, the selection is adjusted.
+
+
+void SvImpLBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect )
+{
+ SvViewDataEntry* pViewDataNewCur = nullptr;
+ if( pEntry )
+ pViewDataNewCur= pView->GetViewDataEntry(pEntry);
+ if( pEntry &&
+ pEntry == pCursor &&
+ pViewDataNewCur &&
+ pViewDataNewCur->HasFocus() &&
+ pViewDataNewCur->IsSelected())
+ {
+ return;
+ }
+
+ // if this cursor is not selectable, find first visible that is and use it
+ while( pEntry && pViewDataNewCur && !pViewDataNewCur->IsSelectable() )
+ {
+ pEntry = pView->NextVisible(pEntry);
+ pViewDataNewCur = pEntry ? pView->GetViewDataEntry(pEntry) : nullptr;
+ }
+
+ SvTreeListEntry* pOldCursor = pCursor;
+ if( pCursor && pEntry != pCursor )
+ {
+ pView->SetEntryFocus( pCursor, false );
+ if( bSimpleTravel )
+ pView->Select( pCursor, false );
+ pView->HideFocus();
+ }
+ pCursor = pEntry;
+ if( pCursor )
+ {
+ if (pViewDataNewCur)
+ pViewDataNewCur->SetFocus( true );
+ if(!bForceNoSelect && bSimpleTravel && !(nFlags & LBoxFlags::DeselectAll) && GetUpdateMode())
+ {
+ pView->Select( pCursor );
+ CallEventListeners( VclEventId::ListboxTreeFocus, pCursor );
+ }
+ // multiple selection: select in cursor move if we're not in
+ // Add mode (Ctrl-F8)
+ else if( GetUpdateMode() &&
+ pView->GetSelectionMode() == SelectionMode::Multiple &&
+ !(nFlags & LBoxFlags::DeselectAll) && !aSelEng.IsAddMode() &&
+ !bForceNoSelect )
+ {
+ pView->Select( pCursor );
+ CallEventListeners( VclEventId::ListboxTreeFocus, pCursor );
+ }
+ else
+ {
+ ShowCursor( true );
+ if (bForceNoSelect && GetUpdateMode())
+ {
+ CallEventListeners( VclEventId::ListboxTreeFocus, pCursor);
+ }
+ }
+
+ if( pAnchor )
+ {
+ DBG_ASSERT(aSelEng.GetSelectionMode() != SelectionMode::Single,"Mode?");
+ SetAnchorSelection( pOldCursor, pCursor );
+ }
+ }
+ nFlags &= (~LBoxFlags::DeselectAll);
+
+ pView->OnCurrentEntryChanged();
+}
+
+void SvImpLBox::ShowCursor( bool bShow )
+{
+ if( !bShow || !pCursor || !pView->HasFocus() )
+ {
+ vcl::Region aOldClip( pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ pView->SetClipRegion( aClipRegion );
+ pView->HideFocus();
+ pView->SetClipRegion( aOldClip );
+ }
+ else
+ {
+ long nY = GetEntryLine( pCursor );
+ tools::Rectangle aRect = pView->GetFocusRect( pCursor, nY );
+ CalcCellFocusRect( pCursor, aRect );
+ vcl::Region aOldClip( pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ pView->SetClipRegion( aClipRegion );
+ pView->ShowFocus( aRect );
+ pView->SetClipRegion( aOldClip );
+ }
+}
+
+
+void SvImpLBox::UpdateAll( bool bInvalidateCompleteView )
+{
+ FindMostRight(nullptr);
+ aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
+ SyncVerThumb();
+ FillView();
+ ShowVerSBar();
+ if( bSimpleTravel && pCursor && pView->HasFocus() )
+ pView->Select( pCursor );
+ ShowCursor( true );
+ if( bInvalidateCompleteView )
+ pView->Invalidate();
+ else
+ pView->Invalidate( GetVisibleArea() );
+}
+
+IMPL_LINK( SvImpLBox, ScrollLeftRightHdl, ScrollBar *, pScrollBar, void )
+{
+ long nDelta = pScrollBar->GetDelta();
+ if( nDelta )
+ {
+ if( pView->IsEditingActive() )
+ {
+ pView->EndEditing( true ); // Cancel
+ pView->Update();
+ }
+ pView->nFocusWidth = -1;
+ KeyLeftRight( nDelta );
+ }
+}
+
+void SvImpLBox::KeyLeftRight( long nDelta )
+{
+ if( !(nFlags & LBoxFlags::InResize) )
+ pView->Update();
+ BeginScroll();
+ nFlags &= (~LBoxFlags::Filling);
+ ShowCursor( false );
+
+ // calculate new origin
+ long nPos = aHorSBar->GetThumbPos();
+ Point aOrigin( -nPos, 0 );
+
+ MapMode aMapMode( pView->GetMapMode() );
+ aMapMode.SetOrigin( aOrigin );
+ pView->SetMapMode( aMapMode );
+
+ if( !(nFlags & LBoxFlags::InResize) )
+ {
+ tools::Rectangle aRect( GetVisibleArea() );
+ pView->Scroll( -nDelta, 0, aRect, ScrollFlags::NoChildren );
+ }
+ else
+ pView->Invalidate();
+ RecalcFocusRect();
+ ShowCursor( true );
+ pView->NotifyScrolled();
+}
+
+
+// returns the last entry if position is just past the last entry
+SvTreeListEntry* SvImpLBox::GetClickedEntry( const Point& rPoint ) const
+{
+ DBG_ASSERT( pView->GetModel(), "SvImpLBox::GetClickedEntry: how can this ever happen? Please tell me (frank.schoenheit@sun.com) how to reproduce!" );
+ if ( !pView->GetModel() )
+ // this is quite impossible. Nevertheless, stack traces from the crash reporter
+ // suggest it isn't. Okay, make it safe, and wait for somebody to reproduce it
+ // reliably :-\ ....
+ // #122359# / 2005-05-23 / frank.schoenheit@sun.com
+ return nullptr;
+ if( pView->GetEntryCount() == 0 || !pStartEntry || !pView->GetEntryHeight())
+ return nullptr;
+
+ sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / pView->GetEntryHeight() );
+ sal_uInt16 nTemp = nClickedEntry;
+ SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
+ return pEntry;
+}
+
+
+// checks if the entry was hit "the right way"
+// (Focusrect+ ContextBitmap at TreeListBox)
+
+bool SvImpLBox::EntryReallyHit(SvTreeListEntry* pEntry, const Point& rPosPixel, long nLine)
+{
+ bool bRet;
+ // we are not too exact when it comes to "special" entries
+ // (with CheckButtons etc.)
+ if( pEntry->ItemCount() >= 3 )
+ return true;
+
+ tools::Rectangle aRect( pView->GetFocusRect( pEntry, nLine ));
+ aRect.SetRight( GetOutputSize().Width() - pView->GetMapMode().GetOrigin().X() );
+
+ SvLBoxContextBmp* pBmp = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
+ aRect.AdjustLeft( -(pBmp->GetSize(pView,pEntry).Width()) );
+ aRect.AdjustLeft( -4 ); // a little tolerance
+
+ Point aPos( rPosPixel );
+ aPos -= pView->GetMapMode().GetOrigin();
+ bRet = aRect.IsInside( aPos );
+ return bRet;
+}
+
+
+// returns 0 if position is just past the last entry
+SvTreeListEntry* SvImpLBox::GetEntry( const Point& rPoint ) const
+{
+ if( (pView->GetEntryCount() == 0) || !pStartEntry ||
+ (rPoint.Y() > aOutputSize.Height())
+ || !pView->GetEntryHeight())
+ return nullptr;
+
+ sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / pView->GetEntryHeight() );
+ sal_uInt16 nTemp = nClickedEntry;
+ SvTreeListEntry* pEntry = pView->NextVisible(pStartEntry, nTemp);
+ if( nTemp != nClickedEntry )
+ pEntry = nullptr;
+ return pEntry;
+}
+
+
+SvTreeListEntry* SvImpLBox::MakePointVisible(const Point& rPoint)
+{
+ if( !pCursor )
+ return nullptr;
+ long nY = rPoint.Y();
+ SvTreeListEntry* pEntry = nullptr;
+ long nMax = aOutputSize.Height();
+ if( nY < 0 || nY >= nMax ) // aOutputSize.Height() )
+ {
+ if( nY < 0 )
+ pEntry = pView->PrevVisible(pCursor);
+ else
+ pEntry = pView->NextVisible(pCursor);
+
+ if( pEntry && pEntry != pCursor )
+ pView->SetEntryFocus( pCursor, false );
+
+ if( nY < 0 )
+ KeyUp( false );
+ else
+ KeyDown( false );
+ }
+ else
+ {
+ pEntry = GetClickedEntry( rPoint );
+ if( !pEntry )
+ {
+ sal_uInt16 nSteps = 0xFFFF;
+ // TODO: LastVisible is not yet implemented!
+ pEntry = pView->NextVisible(pStartEntry, nSteps);
+ }
+ if( pEntry )
+ {
+ if( pEntry != pCursor &&
+ aSelEng.GetSelectionMode() == SelectionMode::Single
+ )
+ pView->Select( pCursor, false );
+ }
+ }
+ return pEntry;
+}
+
+tools::Rectangle SvImpLBox::GetClipRegionRect() const
+{
+ Point aOrigin( pView->GetMapMode().GetOrigin() );
+ aOrigin.setX( aOrigin.X() * -1 ); // conversion document coordinates
+ tools::Rectangle aClipRect( aOrigin, aOutputSize );
+ aClipRect.AdjustBottom( 1 );
+ return aClipRect;
+}
+
+
+void SvImpLBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ if (!pView->GetVisibleCount())
+ return;
+
+ nFlags |= LBoxFlags::InPaint;
+
+ if (nFlags & LBoxFlags::Filling)
+ {
+ SvTreeListEntry* pFirst = pView->First();
+ if (pFirst != pStartEntry)
+ {
+ ShowCursor(false);
+ pStartEntry = pView->First();
+ aVerSBar->SetThumbPos( 0 );
+ StopUserEvent();
+ ShowCursor(true);
+ nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
+ reinterpret_cast<void*>(1));
+ return;
+ }
+ }
+
+ if (!pStartEntry)
+ {
+ pStartEntry = pView->First();
+ }
+
+ if (nNodeBmpTabDistance == NODE_BMP_TABDIST_NOTVALID)
+ SetNodeBmpTabDistance();
+
+ long nRectHeight = rRect.GetHeight();
+ long nEntryHeight = pView->GetEntryHeight();
+
+ // calculate area for the entries we want to draw
+ sal_uInt16 nStartLine = static_cast<sal_uInt16>(rRect.Top() / nEntryHeight);
+ sal_uInt16 nCount = static_cast<sal_uInt16>(nRectHeight / nEntryHeight);
+ nCount += 2; // don't miss a row
+
+ long nY = nStartLine * nEntryHeight;
+ SvTreeListEntry* pEntry = pStartEntry;
+ while (nStartLine && pEntry)
+ {
+ pEntry = pView->NextVisible(pEntry);
+ nStartLine--;
+ }
+
+ vcl::Region aClipRegion(GetClipRegionRect());
+
+ // first draw the lines, then clip them!
+ rRenderContext.SetClipRegion();
+ if (m_nStyle & (WB_HASLINES | WB_HASLINESATROOT))
+ DrawNet(rRenderContext);
+
+ rRenderContext.SetClipRegion(aClipRegion);
+
+ if (!pCursor && !mbNoAutoCurEntry)
+ {
+ // do not select if multiselection or explicit set
+ bool bNotSelect = (aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
+ SetCursor(pStartEntry, bNotSelect);
+ }
+
+ for(sal_uInt16 n=0; n< nCount && pEntry; n++)
+ {
+ /*long nMaxRight=*/
+ pView->PaintEntry1(*pEntry, nY, rRenderContext );
+ nY += nEntryHeight;
+ pEntry = pView->NextVisible(pEntry);
+ }
+
+ nFlags &= (~LBoxFlags::DeselectAll);
+ rRenderContext.SetClipRegion();
+ nFlags &= (~LBoxFlags::InPaint);
+}
+
+void SvImpLBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
+{
+ if( !pEntry )
+ return;
+
+ bool bInView = IsEntryInView( pEntry );
+
+ if( bInView && (!bMoveToTop || pStartEntry == pEntry) )
+ return; // is already visible
+
+ if( pStartEntry || mbForceMakeVisible )
+ nFlags &= (~LBoxFlags::Filling);
+ if( !bInView )
+ {
+ if( !pView->IsEntryVisible(pEntry) ) // Parent(s) collapsed?
+ {
+ SvTreeListEntry* pParent = pView->GetParent( pEntry );
+ while( pParent )
+ {
+ if( !pView->IsExpanded( pParent ) )
+ {
+ bool bRet = pView->Expand( pParent );
+ DBG_ASSERT(bRet,"Not expanded!");
+ }
+ pParent = pView->GetParent( pParent );
+ }
+ // do the parent's children fit into the view or do we have to scroll?
+ if( IsEntryInView( pEntry ) && !bMoveToTop )
+ return; // no need to scroll
+ }
+ }
+
+ pStartEntry = pEntry;
+ ShowCursor( false );
+ FillView();
+ aVerSBar->SetThumbPos( static_cast<long>(pView->GetVisiblePos( pStartEntry )) );
+ ShowCursor( true );
+ pView->Invalidate();
+}
+
+void SvImpLBox::ScrollToAbsPos( long nPos )
+{
+ if( pView->GetVisibleCount() == 0 )
+ return;
+ long nLastEntryPos = pView->GetAbsPos( pView->Last() );
+
+ if( nPos < 0 )
+ nPos = 0;
+ else if( nPos > nLastEntryPos )
+ nPos = nLastEntryPos;
+
+ SvTreeListEntry* pEntry = pView->GetEntryAtAbsPos( nPos );
+ if( !pEntry || pEntry == pStartEntry )
+ return;
+
+ if( pStartEntry || mbForceMakeVisible )
+ nFlags &= (~LBoxFlags::Filling);
+
+ if( pView->IsEntryVisible(pEntry) )
+ {
+ pStartEntry = pEntry;
+ ShowCursor( false );
+ aVerSBar->SetThumbPos( nPos );
+ ShowCursor( true );
+ if (GetUpdateMode())
+ pView->Invalidate();
+ }
+}
+
+void SvImpLBox::DrawNet(vcl::RenderContext& rRenderContext)
+{
+ if (pView->GetVisibleCount() < 2 && !pStartEntry->HasChildrenOnDemand() &&
+ !pStartEntry->HasChildren())
+ {
+ return;
+ }
+
+ // for platforms that don't have nets, DrawNativeControl does nothing and returns true
+ // so that SvImpLBox::DrawNet() doesn't draw anything either
+ if (rRenderContext.IsNativeControlSupported(ControlType::ListNet, ControlPart::Entire))
+ {
+ ImplControlValue aControlValue;
+ if (rRenderContext.DrawNativeControl(ControlType::ListNet, ControlPart::Entire,
+ tools::Rectangle(), ControlState::ENABLED, aControlValue, OUString()))
+ {
+ return;
+ }
+ }
+
+ long nEntryHeight = pView->GetEntryHeight();
+ long nEntryHeightDIV2 = nEntryHeight / 2;
+ if( nEntryHeightDIV2 && !(nEntryHeight & 0x0001))
+ nEntryHeightDIV2--;
+
+ SvTreeListEntry* pChild;
+ SvTreeListEntry* pEntry = pStartEntry;
+
+ SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
+ while (pTree->GetDepth( pEntry ) > 0)
+ {
+ pEntry = pView->GetParent(pEntry);
+ }
+ sal_uInt16 nOffs = static_cast<sal_uInt16>(pView->GetVisiblePos(pStartEntry) - pView->GetVisiblePos(pEntry));
+ long nY = 0;
+ nY -= (nOffs * nEntryHeight);
+
+ DBG_ASSERT(pFirstDynamicTab,"No Tree!");
+
+ rRenderContext.Push(PushFlags::LINECOLOR);
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ Color aCol = rStyleSettings.GetFaceColor();
+
+ if (aCol.IsRGBEqual(rRenderContext.GetBackground().GetColor()))
+ aCol = rStyleSettings.GetShadowColor();
+ rRenderContext.SetLineColor(aCol);
+ Point aPos1, aPos2;
+ sal_uInt16 nDistance;
+ sal_uLong nMax = nVisibleCount + nOffs + 1;
+
+ const Image& rExpandedNodeBitmap = GetExpandedNodeBmp();
+
+ for (sal_uLong n=0; n< nMax && pEntry; n++)
+ {
+ if (pView->IsExpanded(pEntry))
+ {
+ aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
+ // if it is not a context bitmap, go a little to the right below the
+ // first text (node bitmap, too)
+ if (!pView->nContextBmpWidthMax)
+ aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
+
+ aPos1.setY( nY );
+ aPos1.AdjustY(nEntryHeightDIV2 );
+
+ pChild = pView->FirstChild( pEntry );
+ assert(pChild && "Child?");
+ pChild = pChild->LastSibling();
+ nDistance = static_cast<sal_uInt16>(pView->GetVisiblePos(pChild) - pView->GetVisiblePos(pEntry));
+ aPos2 = aPos1;
+ aPos2.AdjustY(nDistance * nEntryHeight );
+ rRenderContext.DrawLine(aPos1, aPos2);
+ }
+ // visible in control?
+ if (n >= nOffs && ((m_nStyle & WB_HASLINESATROOT) || !pTree->IsAtRootDepth(pEntry)))
+ {
+ // can we recycle aPos1?
+ if (!pView->IsExpanded(pEntry))
+ {
+ // nope
+ aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
+ // if it is not a context bitmap, go a little to the right below
+ // the first text (node bitmap, too)
+ if (!pView->nContextBmpWidthMax)
+ aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
+ aPos1.setY( nY );
+ aPos1.AdjustY(nEntryHeightDIV2 );
+ aPos2.setX( aPos1.X() );
+ }
+ aPos2.setY( aPos1.Y() );
+ aPos2.AdjustX( -(pView->GetIndent()) );
+ rRenderContext.DrawLine(aPos1, aPos2);
+ }
+ nY += nEntryHeight;
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if (m_nStyle & WB_HASLINESATROOT)
+ {
+ pEntry = pView->First();
+ aPos1.setX( pView->GetTabPos(pEntry, pFirstDynamicTab) );
+ // if it is not a context bitmap, go a little to the right below the
+ // first text (node bitmap, too)
+ if (!pView->nContextBmpWidthMax)
+ aPos1.AdjustX(rExpandedNodeBitmap.GetSizePixel().Width() / 2 );
+ aPos1.AdjustX( -(pView->GetIndent()) );
+ aPos1.setY( GetEntryLine( pEntry ) );
+ aPos1.AdjustY(nEntryHeightDIV2 );
+ pChild = pEntry->LastSibling();
+ aPos2.setX( aPos1.X() );
+ aPos2.setY( GetEntryLine( pChild ) );
+ aPos2.AdjustY(nEntryHeightDIV2 );
+ rRenderContext.DrawLine(aPos1, aPos2);
+ }
+ rRenderContext.Pop();
+}
+
+void SvImpLBox::PositionScrollBars( Size& rSize, sal_uInt16 nMask )
+{
+ long nOverlap = 0;
+
+ Size aVerSize( nVerSBarWidth, rSize.Height() );
+ Size aHorSize( rSize.Width(), nHorSBarHeight );
+
+ if( nMask & 0x0001 )
+ aHorSize.AdjustWidth( -(nVerSBarWidth) );
+ if( nMask & 0x0002 )
+ aVerSize.AdjustHeight( -(nHorSBarHeight) );
+
+ aVerSize.AdjustHeight(2 * nOverlap );
+ Point aVerPos( rSize.Width() - aVerSize.Width() + nOverlap, -nOverlap );
+ aVerSBar->SetPosSizePixel( aVerPos, aVerSize );
+
+ aHorSize.AdjustWidth(2 * nOverlap );
+ Point aHorPos( -nOverlap, rSize.Height() - aHorSize.Height() + nOverlap );
+
+ aHorSBar->SetPosSizePixel( aHorPos, aHorSize );
+
+ if( nMask & 0x0001 )
+ rSize.setWidth( aVerPos.X() );
+ if( nMask & 0x0002 )
+ rSize.setHeight( aHorPos.Y() );
+
+ if( (nMask & (0x0001|0x0002)) == (0x0001|0x0002) )
+ aScrBarBox->Show();
+ else
+ aScrBarBox->Hide();
+}
+
+void SvImpLBox::AdjustScrollBars( Size& rSize )
+{
+ long nEntryHeight = pView->GetEntryHeight();
+ if( !nEntryHeight )
+ return;
+
+ sal_uInt16 nResult = 0;
+
+ Size aOSize( pView->Control::GetOutputSizePixel() );
+
+ const WinBits nWindowStyle = pView->GetStyle();
+ bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;
+ bool bHorBar = false;
+ long nMaxRight = aOSize.Width(); //GetOutputSize().Width();
+ Point aOrigin( pView->GetMapMode().GetOrigin() );
+ aOrigin.setX( aOrigin.X() * -1 );
+ nMaxRight += aOrigin.X() - 1;
+ long nVis = nMostRight - aOrigin.X();
+ if( (nWindowStyle & WB_HSCROLL) &&
+ (nVis < nMostRight || nMaxRight < nMostRight) )
+ {
+ bHorBar = true;
+ }
+
+ // number of entries that are not collapsed
+ sal_uLong nTotalCount = pView->GetVisibleCount();
+
+ // number of entries visible within the view
+ nVisibleCount = aOSize.Height() / nEntryHeight;
+
+ // do we need a vertical scrollbar?
+ if( bVerSBar || nTotalCount > nVisibleCount )
+ {
+ nResult = 1;
+ nMaxRight -= nVerSBarWidth;
+ if( !bHorBar )
+ {
+ if( (nWindowStyle & WB_HSCROLL) &&
+ (nVis < nMostRight || nMaxRight < nMostRight) )
+ bHorBar = true;
+ }
+ }
+
+ // do we need a horizontal scrollbar?
+ if( bHorBar )
+ {
+ nResult |= 0x0002;
+ // the number of entries visible within the view has to be recalculated
+ // because the horizontal scrollbar is now visible.
+ nVisibleCount = (aOSize.Height() - nHorSBarHeight) / nEntryHeight;
+ // we might actually need a vertical scrollbar now
+ if( !(nResult & 0x0001) &&
+ ((nTotalCount > nVisibleCount) || bVerSBar) )
+ {
+ nResult = 3;
+ }
+ }
+
+ PositionScrollBars( aOSize, nResult );
+
+ // adapt Range, VisibleRange etc.
+
+ // refresh output size, in case we have to scroll
+ tools::Rectangle aRect;
+ aRect.SetSize( aOSize );
+ aSelEng.SetVisibleArea( aRect );
+
+ // vertical scrollbar
+ long nTemp = static_cast<long>(nVisibleCount);
+ nTemp--;
+ if( nTemp != aVerSBar->GetVisibleSize() )
+ {
+ if( !bInVScrollHdl )
+ {
+ aVerSBar->SetPageSize( nTemp - 1 );
+ aVerSBar->SetVisibleSize( nTemp );
+ }
+ else
+ {
+ nFlags |= LBoxFlags::EndScrollSetVisSize;
+ nNextVerVisSize = nTemp;
+ }
+ }
+
+ // horizontal scrollbar
+ nTemp = aHorSBar->GetThumbPos();
+ aHorSBar->SetVisibleSize( aOSize.Width() );
+ long nNewThumbPos = aHorSBar->GetThumbPos();
+ Range aRange( aHorSBar->GetRange() );
+ if( aRange.Max() < nMostRight+25 )
+ {
+ aRange.Max() = nMostRight+25;
+ aHorSBar->SetRange( aRange );
+ }
+
+ if( nTemp != nNewThumbPos )
+ {
+ nTemp = nNewThumbPos - nTemp;
+ if( pView->IsEditingActive() )
+ {
+ pView->EndEditing( true ); // Cancel
+ pView->Update();
+ }
+ pView->nFocusWidth = -1;
+ KeyLeftRight( nTemp );
+ }
+
+ if( nResult & 0x0001 )
+ aVerSBar->Show();
+ else
+ aVerSBar->Hide();
+
+ if( nResult & 0x0002 )
+ aHorSBar->Show();
+ else
+ {
+ aHorSBar->Hide();
+ }
+ rSize = aOSize;
+}
+
+void SvImpLBox::InitScrollBarBox()
+{
+ aScrBarBox->SetSizePixel( Size(nVerSBarWidth, nHorSBarHeight) );
+ Size aSize( pView->Control::GetOutputSizePixel() );
+ aScrBarBox->SetPosPixel( Point(aSize.Width()-nVerSBarWidth, aSize.Height()-nHorSBarHeight));
+}
+
+void SvImpLBox::Resize()
+{
+ aOutputSize = pView->Control::GetOutputSizePixel();
+ if( aOutputSize.Width() <= 0 || aOutputSize.Height() <= 0 )
+ return;
+ nFlags |= LBoxFlags::InResize;
+ InitScrollBarBox();
+
+ if( pView->GetEntryHeight())
+ {
+ AdjustScrollBars( aOutputSize );
+ UpdateAll(false);
+ }
+ // HACK, as in floating and docked windows the scrollbars might not be drawn
+ // correctly/not be drawn at all after resizing!
+ if( aHorSBar->IsVisible())
+ aHorSBar->Invalidate();
+ if( aVerSBar->IsVisible())
+ aVerSBar->Invalidate();
+ nFlags &= ~LBoxFlags::InResize;
+}
+
+void SvImpLBox::FillView()
+{
+ if( !pStartEntry )
+ {
+ sal_uLong nVisibleViewCount = pView->GetVisibleCount();
+ long nTempThumb = aVerSBar->GetThumbPos();
+ if( nTempThumb < 0 )
+ nTempThumb = 0;
+ else if( static_cast<unsigned long>(nTempThumb) >= nVisibleViewCount )
+ nTempThumb = nVisibleViewCount == 0 ? 0 : nVisibleViewCount - 1;
+ pStartEntry = pView->GetEntryAtVisPos(nTempThumb);
+ }
+ if( !pStartEntry )
+ return;
+
+ sal_uInt16 nLast = static_cast<sal_uInt16>(pView->GetVisiblePos(pView->LastVisible()));
+ sal_uInt16 nThumb = static_cast<sal_uInt16>(pView->GetVisiblePos( pStartEntry ));
+ sal_uLong nCurDispEntries = nLast-nThumb+1;
+ if( nCurDispEntries >= nVisibleCount )
+ return;
+
+ ShowCursor( false );
+ // fill window by moving the thumb up incrementally
+ bool bFound = false;
+ SvTreeListEntry* pTemp = pStartEntry;
+ while( nCurDispEntries < nVisibleCount && pTemp )
+ {
+ pTemp = pView->PrevVisible(pStartEntry);
+ if( pTemp )
+ {
+ nThumb--;
+ pStartEntry = pTemp;
+ nCurDispEntries++;
+ bFound = true;
+ }
+ }
+ if( bFound )
+ {
+ aVerSBar->SetThumbPos( nThumb );
+ ShowCursor( true ); // recalculate focus rectangle
+ pView->Invalidate();
+ }
+}
+
+
+void SvImpLBox::ShowVerSBar()
+{
+ bool bVerBar = ( pView->GetStyle() & WB_VSCROLL ) != 0;
+ sal_uLong nVis = 0;
+ if( !bVerBar )
+ nVis = pView->GetVisibleCount();
+ if( bVerBar || (nVisibleCount && nVis > static_cast<sal_uLong>(nVisibleCount-1)) )
+ {
+ if( !aVerSBar->IsVisible() )
+ {
+ pView->nFocusWidth = -1;
+ AdjustScrollBars( aOutputSize );
+ if( GetUpdateMode() )
+ aVerSBar->Update();
+ }
+ }
+ else
+ {
+ if( aVerSBar->IsVisible() )
+ {
+ pView->nFocusWidth = -1;
+ AdjustScrollBars( aOutputSize );
+ }
+ }
+
+ long nMaxRight = GetOutputSize().Width();
+ Point aPos( pView->GetMapMode().GetOrigin() );
+ aPos.setX( aPos.X() * -1 ); // convert document coordinates
+ nMaxRight = nMaxRight + aPos.X() - 1;
+ if( nMaxRight < nMostRight )
+ {
+ if( !aHorSBar->IsVisible() )
+ {
+ pView->nFocusWidth = -1;
+ AdjustScrollBars( aOutputSize );
+ if( GetUpdateMode() )
+ aHorSBar->Update();
+ }
+ else
+ {
+ Range aRange( aHorSBar->GetRange() );
+ if( aRange.Max() < nMostRight+25 )
+ {
+ aRange.Max() = nMostRight+25;
+ aHorSBar->SetRange( aRange );
+ }
+ else
+ {
+ pView->nFocusWidth = -1;
+ AdjustScrollBars( aOutputSize );
+ }
+ }
+ }
+ else
+ {
+ if( aHorSBar->IsVisible() )
+ {
+ pView->nFocusWidth = -1;
+ AdjustScrollBars( aOutputSize );
+ }
+ }
+}
+
+
+void SvImpLBox::SyncVerThumb()
+{
+ if( pStartEntry )
+ {
+ long nEntryPos = pView->GetVisiblePos( pStartEntry );
+ aVerSBar->SetThumbPos( nEntryPos );
+ }
+ else
+ aVerSBar->SetThumbPos( 0 );
+}
+
+bool SvImpLBox::IsEntryInView( SvTreeListEntry* pEntry ) const
+{
+ // parent collapsed
+ if( !pView->IsEntryVisible(pEntry) )
+ return false;
+ long nY = GetEntryLine( pEntry );
+ if( nY < 0 )
+ return false;
+ long nMax = nVisibleCount * pView->GetEntryHeight();
+ return nY < nMax;
+}
+
+
+long SvImpLBox::GetEntryLine( SvTreeListEntry* pEntry ) const
+{
+ if(!pStartEntry )
+ return -1; // invisible position
+
+ long nFirstVisPos = pView->GetVisiblePos( pStartEntry );
+ long nEntryVisPos = pView->GetVisiblePos( pEntry );
+ nFirstVisPos = nEntryVisPos - nFirstVisPos;
+ nFirstVisPos *= pView->GetEntryHeight();
+ return nFirstVisPos;
+}
+
+void SvImpLBox::SetEntryHeight()
+{
+ SetNodeBmpWidth( GetExpandedNodeBmp() );
+ SetNodeBmpWidth( GetCollapsedNodeBmp() );
+ if(!pView->HasViewData()) // are we within the Clear?
+ {
+ Size aSize = pView->Control::GetOutputSizePixel();
+ AdjustScrollBars( aSize );
+ }
+ else
+ {
+ Resize();
+ if( GetUpdateMode() )
+ pView->Invalidate();
+ }
+}
+
+
+// ***********************************************************************
+// Callback Functions
+// ***********************************************************************
+
+void SvImpLBox::EntryExpanded( SvTreeListEntry* pEntry )
+{
+ // SelAllDestrAnch( false, true ); //DeselectAll();
+ if( !GetUpdateMode() )
+ return;
+
+ ShowCursor( false );
+ long nY = GetEntryLine( pEntry );
+ if( IsLineVisible(nY) )
+ {
+ InvalidateEntriesFrom( nY );
+ FindMostRight( pEntry, nullptr );
+ }
+ aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
+ // if we expanded before the thumb, the thumb's position has to be
+ // corrected
+ SyncVerThumb();
+ ShowVerSBar();
+ ShowCursor( true );
+}
+
+void SvImpLBox::EntryCollapsed( SvTreeListEntry* pEntry )
+{
+ if( !pView->IsEntryVisible( pEntry ) )
+ return;
+
+ ShowCursor( false );
+
+ if( !pMostRightEntry || pTree->IsChild( pEntry,pMostRightEntry ) )
+ {
+ FindMostRight(nullptr);
+ }
+
+ if( pStartEntry )
+ {
+ long nOldThumbPos = aVerSBar->GetThumbPos();
+ sal_uLong nVisList = pView->GetVisibleCount();
+ aVerSBar->SetRange( Range(0, nVisList-1) );
+ long nNewThumbPos = aVerSBar->GetThumbPos();
+ if( nNewThumbPos != nOldThumbPos )
+ {
+ pStartEntry = pView->First();
+ sal_uInt16 nDistance = static_cast<sal_uInt16>(nNewThumbPos);
+ if( nDistance )
+ pStartEntry = pView->NextVisible(pStartEntry, nDistance);
+ if( GetUpdateMode() )
+ pView->Invalidate();
+ }
+ else
+ SyncVerThumb();
+ ShowVerSBar();
+ }
+ // has the cursor been collapsed?
+ if( pTree->IsChild( pEntry, pCursor ) )
+ SetCursor( pEntry );
+ if( GetUpdateMode() )
+ ShowVerSBar();
+ ShowCursor( true );
+ if( GetUpdateMode() && pCursor )
+ pView->Select( pCursor );
+}
+
+void SvImpLBox::CollapsingEntry( SvTreeListEntry* pEntry )
+{
+ if( !pView->IsEntryVisible( pEntry ) || !pStartEntry )
+ return;
+
+ SelAllDestrAnch( false ); // deselect all
+
+ // is the collapsed cursor visible?
+ long nY = GetEntryLine( pEntry );
+ if( IsLineVisible(nY) )
+ {
+ if( GetUpdateMode() )
+ InvalidateEntriesFrom( nY );
+ }
+ else
+ {
+ if( pTree->IsChild(pEntry, pStartEntry) )
+ {
+ pStartEntry = pEntry;
+ if( GetUpdateMode() )
+ pView->Invalidate();
+ }
+ }
+}
+
+
+void SvImpLBox::SetNodeBmpWidth( const Image& rBmp )
+{
+ const Size aSize( rBmp.GetSizePixel() );
+ nNodeBmpWidth = aSize.Width();
+}
+
+void SvImpLBox::SetNodeBmpTabDistance()
+{
+ nNodeBmpTabDistance = -pView->GetIndent();
+ if( pView->nContextBmpWidthMax )
+ {
+ // only if the first dynamic tab is centered (we currently assume that)
+ Size aSize = GetExpandedNodeBmp().GetSizePixel();
+ nNodeBmpTabDistance -= aSize.Width() / 2;
+ }
+}
+
+
+// corrects the cursor when using SingleSelection
+
+void SvImpLBox::EntrySelected( SvTreeListEntry* pEntry, bool bSelect )
+{
+ if( nFlags & LBoxFlags::IgnoreSelect )
+ return;
+
+ nFlags &= (~LBoxFlags::DeselectAll);
+ if( bSelect &&
+ aSelEng.GetSelectionMode() == SelectionMode::Single &&
+ pEntry != pCursor )
+ {
+ SetCursor( pEntry );
+ DBG_ASSERT(pView->GetSelectionCount()==1,"selection count?");
+ }
+
+ if( GetUpdateMode() && pView->IsEntryVisible(pEntry) )
+ {
+ long nY = GetEntryLine( pEntry );
+ if( IsLineVisible( nY ) )
+ {
+ ShowCursor(false);
+ InvalidateEntry(pEntry);
+ ShowCursor(true);
+ }
+ }
+}
+
+
+void SvImpLBox::RemovingEntry( SvTreeListEntry* pEntry )
+{
+ CallEventListeners( VclEventId::ListboxItemRemoved , pEntry );
+
+ DestroyAnchor();
+
+ if( !pView->IsEntryVisible( pEntry ) )
+ {
+ // if parent is collapsed => bye!
+ nFlags |= LBoxFlags::RemovedEntryInvisible;
+ return;
+ }
+
+ if( pEntry == pMostRightEntry || (
+ pEntry->HasChildren() && pView->IsExpanded(pEntry) &&
+ pTree->IsChild(pEntry, pMostRightEntry)))
+ {
+ nFlags |= LBoxFlags::RemovedRecalcMostRight;
+ }
+
+ SvTreeListEntry* pOldStartEntry = pStartEntry;
+
+ SvTreeListEntry* pParent = pView->GetModel()->GetParent(pEntry);
+
+ if (pParent && pView->GetModel()->GetChildList(pParent).size() == 1)
+ {
+ DBG_ASSERT( pView->IsExpanded( pParent ), "Parent not expanded");
+ pParent->SetFlags( pParent->GetFlags() | SvTLEntryFlags::NO_NODEBMP);
+ InvalidateEntry( pParent );
+ }
+
+ if( pCursor && pTree->IsChild( pEntry, pCursor) )
+ pCursor = pEntry;
+ if( pStartEntry && pTree->IsChild(pEntry,pStartEntry) )
+ pStartEntry = pEntry;
+
+ SvTreeListEntry* pTemp;
+ if( pCursor && pCursor == pEntry )
+ {
+ if( bSimpleTravel )
+ pView->Select( pCursor, false );
+ ShowCursor( false ); // focus rectangle gone
+ // NextSibling, because we also delete the children of the cursor
+ pTemp = pCursor->NextSibling();
+ if( !pTemp )
+ pTemp = pView->PrevVisible(pCursor);
+
+ SetCursor( pTemp, true );
+ }
+ if( pStartEntry && pStartEntry == pEntry )
+ {
+ pTemp = pStartEntry->NextSibling();
+ if( !pTemp )
+ pTemp = pView->PrevVisible(pStartEntry);
+ pStartEntry = pTemp;
+ }
+ if( GetUpdateMode())
+ {
+ // if it is the last one, we have to invalidate it, so the lines are
+ // drawn correctly (in this case they're deleted)
+ if( pStartEntry && (pStartEntry != pOldStartEntry || pEntry == pView->GetModel()->Last()) )
+ {
+ aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry ));
+ pView->Invalidate( GetVisibleArea() );
+ }
+ else
+ InvalidateEntriesFrom( GetEntryLine( pEntry ) );
+ }
+}
+
+void SvImpLBox::EntryRemoved()
+{
+ if( nFlags & LBoxFlags::RemovedEntryInvisible )
+ {
+ nFlags &= (~LBoxFlags::RemovedEntryInvisible);
+ return;
+ }
+ if( !pStartEntry )
+ pStartEntry = pTree->First();
+ if( !pCursor )
+ SetCursor( pStartEntry, true );
+
+ if( pCursor && (bSimpleTravel || !pView->GetSelectionCount() ))
+ pView->Select( pCursor );
+
+ if( GetUpdateMode())
+ {
+ if( nFlags & LBoxFlags::RemovedRecalcMostRight )
+ FindMostRight(nullptr);
+ aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1 ) );
+ FillView();
+ if( pStartEntry )
+ // if something above the thumb was deleted
+ aVerSBar->SetThumbPos( pView->GetVisiblePos( pStartEntry) );
+
+ ShowVerSBar();
+ if( pCursor && pView->HasFocus() && !pView->IsSelected(pCursor) )
+ {
+ if( pView->GetSelectionCount() )
+ {
+ // is a neighboring entry selected?
+ SvTreeListEntry* pNextCursor = pView->PrevVisible( pCursor );
+ if( !pNextCursor || !pView->IsSelected( pNextCursor ))
+ pNextCursor = pView->NextVisible( pCursor );
+ if( !pNextCursor || !pView->IsSelected( pNextCursor ))
+ // no neighbor selected: use first selected
+ pNextCursor = pView->FirstSelected();
+ SetCursor( pNextCursor );
+ MakeVisible( pCursor );
+ }
+ else
+ pView->Select( pCursor );
+ }
+ ShowCursor( true );
+ }
+ nFlags &= (~LBoxFlags::RemovedRecalcMostRight);
+}
+
+
+void SvImpLBox::MovingEntry( SvTreeListEntry* pEntry )
+{
+ bool bDeselAll(nFlags & LBoxFlags::DeselectAll);
+ SelAllDestrAnch( false ); // DeselectAll();
+ if( !bDeselAll )
+ nFlags &= (~LBoxFlags::DeselectAll);
+
+ if( pEntry == pCursor )
+ ShowCursor( false );
+ if( IsEntryInView( pEntry ) )
+ pView->Invalidate();
+ if( pEntry != pStartEntry )
+ return;
+
+ SvTreeListEntry* pNew = nullptr;
+ if( !pEntry->HasChildren() )
+ {
+ pNew = pView->NextVisible(pStartEntry);
+ if( !pNew )
+ pNew = pView->PrevVisible(pStartEntry);
+ }
+ else
+ {
+ pNew = pEntry->NextSibling();
+ if( !pNew )
+ pNew = pEntry->PrevSibling();
+ }
+ pStartEntry = pNew;
+}
+
+void SvImpLBox::EntryMoved( SvTreeListEntry* pEntry )
+{
+ UpdateContextBmpWidthVectorFromMovedEntry( pEntry );
+
+ if ( !pStartEntry )
+ // this might happen if the only entry in the view is moved to its very same position
+ // #i97346#
+ pStartEntry = pView->First();
+
+ aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
+ sal_uInt16 nFirstPos = static_cast<sal_uInt16>(pTree->GetAbsPos( pStartEntry ));
+ sal_uInt16 nNewPos = static_cast<sal_uInt16>(pTree->GetAbsPos( pEntry ));
+ FindMostRight(nullptr);
+ if( nNewPos < nFirstPos ) // HACK!
+ pStartEntry = pEntry;
+ SyncVerThumb();
+ if( pEntry == pCursor )
+ {
+ if( pView->IsEntryVisible( pCursor ) )
+ ShowCursor( true );
+ else
+ {
+ SvTreeListEntry* pParent = pEntry;
+ do {
+ pParent = pTree->GetParent( pParent );
+ }
+ while( !pView->IsEntryVisible( pParent ) );
+ SetCursor( pParent );
+ }
+ }
+ if( IsEntryInView( pEntry ) )
+ pView->Invalidate();
+}
+
+
+void SvImpLBox::EntryInserted( SvTreeListEntry* pEntry )
+{
+ if( !GetUpdateMode() )
+ return;
+
+ SvTreeListEntry* pParent = pTree->GetParent(pEntry);
+ if (pParent && pTree->GetChildList(pParent).size() == 1)
+ // draw plus sign
+ pTree->InvalidateEntry( pParent );
+
+ if( !pView->IsEntryVisible( pEntry ) )
+ return;
+ bool bDeselAll(nFlags & LBoxFlags::DeselectAll);
+ if( bDeselAll )
+ SelAllDestrAnch( false );
+ else
+ DestroyAnchor();
+ // nFlags &= (~LBoxFlags::DeselectAll);
+// ShowCursor( false ); // if cursor is moved lower
+ long nY = GetEntryLine( pEntry );
+ bool bEntryVisible = IsLineVisible( nY );
+ if( bEntryVisible )
+ {
+ ShowCursor( false ); // if cursor is moved lower
+ nY -= pView->GetEntryHeight(); // because of lines
+ InvalidateEntriesFrom( nY );
+ }
+ else if( pStartEntry && nY < GetEntryLine(pStartEntry) )
+ {
+ // Check if the view is filled completely. If not, then adjust
+ // pStartEntry and the Cursor (automatic scrolling).
+ sal_uInt16 nLast = static_cast<sal_uInt16>(pView->GetVisiblePos(pView->LastVisible()));
+ sal_uInt16 nThumb = static_cast<sal_uInt16>(pView->GetVisiblePos( pStartEntry ));
+ sal_uInt16 nCurDispEntries = nLast-nThumb+1;
+ if( nCurDispEntries < nVisibleCount )
+ {
+ // set at the next paint event
+ pStartEntry = nullptr;
+ SetCursor( nullptr );
+ pView->Invalidate();
+ }
+ }
+ else if( !pStartEntry )
+ pView->Invalidate();
+
+ SetMostRight( pEntry );
+ aVerSBar->SetRange( Range(0, pView->GetVisibleCount()-1));
+ SyncVerThumb(); // if something was inserted before the thumb
+ ShowVerSBar();
+ ShowCursor( true );
+ if( pStartEntry != pView->First() && (nFlags & LBoxFlags::Filling) )
+ pView->Update();
+}
+
+
+// ********************************************************************
+// Event handler
+// ********************************************************************
+
+
+// ****** Control the control animation
+
+bool SvImpLBox::ButtonDownCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry* pEntry)
+{
+ SvLBoxItem* pItem = pView->GetItem(pEntry,rMEvt.GetPosPixel().X(),&pActiveTab);
+ if (pItem && pItem->GetType() == SvLBoxItemType::Button)
+ {
+ pActiveButton = static_cast<SvLBoxButton*>(pItem);
+ pActiveEntry = pEntry;
+ if( pCursor == pActiveEntry )
+ pView->HideFocus();
+ pView->CaptureMouse();
+ pActiveButton->SetStateHilighted( true );
+ InvalidateEntry(pActiveEntry);
+ return true;
+ }
+ else
+ pActiveButton = nullptr;
+ return false;
+}
+
+bool SvImpLBox::MouseMoveCheckCtrl(const MouseEvent& rMEvt, SvTreeListEntry const * pEntry)
+{
+ if( pActiveButton )
+ {
+ long nMouseX = rMEvt.GetPosPixel().X();
+ if( pEntry == pActiveEntry &&
+ pView->GetItem(pActiveEntry, nMouseX) == pActiveButton )
+ {
+ if( !pActiveButton->IsStateHilighted() )
+ {
+ pActiveButton->SetStateHilighted(true );
+ InvalidateEntry(pActiveEntry);
+ }
+ }
+ else
+ {
+ if( pActiveButton->IsStateHilighted() )
+ {
+ pActiveButton->SetStateHilighted(false );
+ InvalidateEntry(pActiveEntry);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SvImpLBox::ButtonUpCheckCtrl( const MouseEvent& rMEvt )
+{
+ if( pActiveButton )
+ {
+ pView->ReleaseMouse();
+ SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
+ pActiveButton->SetStateHilighted( false );
+ long nMouseX = rMEvt.GetPosPixel().X();
+ if (pEntry == pActiveEntry && pView->GetItem(pActiveEntry, nMouseX) == pActiveButton)
+ pActiveButton->ClickHdl(pActiveEntry);
+ InvalidateEntry(pActiveEntry);
+ if (pCursor == pActiveEntry)
+ ShowCursor(true);
+ pActiveButton = nullptr;
+ pActiveEntry = nullptr;
+ pActiveTab = nullptr;
+ return true;
+ }
+ return false;
+}
+
+// ******* Control plus/minus button for expanding/collapsing
+
+// false == no expand/collapse button hit
+bool SvImpLBox::IsNodeButton( const Point& rPosPixel, SvTreeListEntry* pEntry ) const
+{
+ if( !pEntry->HasChildren() && !pEntry->HasChildrenOnDemand() )
+ return false;
+
+ SvLBoxTab* pFirstDynamicTab = pView->GetFirstDynamicTab();
+ if( !pFirstDynamicTab )
+ return false;
+
+ long nMouseX = rPosPixel.X();
+ // convert to document coordinates
+ Point aOrigin( pView->GetMapMode().GetOrigin() );
+ nMouseX -= aOrigin.X();
+
+ long nX = pView->GetTabPos( pEntry, pFirstDynamicTab);
+ nX += nNodeBmpTabDistance;
+ if( nMouseX < nX )
+ return false;
+ nX += nNodeBmpWidth;
+ return nMouseX <= nX;
+}
+
+// false == hit no node button
+bool SvImpLBox::ButtonDownCheckExpand( const MouseEvent& rMEvt, SvTreeListEntry* pEntry )
+{
+ bool bRet = false;
+
+ if ( pView->IsEditingActive() && pEntry == pView->pEdEntry )
+ // inplace editing -> nothing to do
+ bRet = true;
+ else if ( IsNodeButton( rMEvt.GetPosPixel(), pEntry ) )
+ {
+ if ( pView->IsExpanded( pEntry ) )
+ {
+ pView->EndEditing( true );
+ pView->Collapse( pEntry );
+ }
+ else
+ {
+ // you can expand an entry, which is in editing
+ pView->Expand( pEntry );
+ }
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void SvImpLBox::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( !rMEvt.IsLeft() && !rMEvt.IsRight())
+ return;
+
+ aEditIdle.Stop();
+ Point aPos( rMEvt.GetPosPixel());
+
+ if( aPos.X() > aOutputSize.Width() || aPos.Y() > aOutputSize.Height() )
+ return;
+
+ SvTreeListEntry* pEntry = GetEntry( aPos );
+ if ( pEntry != pCursor )
+ // new entry selected -> reset current tab position to first tab
+ nCurTabPos = FIRST_ENTRY_TAB;
+ nFlags &= (~LBoxFlags::Filling);
+ pView->GrabFocus();
+ //fdo#82270 Grabbing focus can invalidate the entries, re-fetch
+ pEntry = GetEntry(aPos);
+ // the entry can still be invalid!
+ if( !pEntry || !pView->GetViewData( pEntry ))
+ return;
+
+ long nY = GetEntryLine( pEntry );
+ // Node-Button?
+ if( ButtonDownCheckExpand( rMEvt, pEntry ) )
+ return;
+
+ if( !EntryReallyHit(pEntry,aPos,nY))
+ return;
+
+ SvLBoxItem* pXItem = pView->GetItem( pEntry, aPos.X() );
+ if( pXItem )
+ {
+ SvLBoxTab* pXTab = pView->GetTab( pEntry, pXItem );
+ if ( !rMEvt.IsMod1() && !rMEvt.IsMod2() && rMEvt.IsLeft() && pXTab->IsEditable()
+ && pEntry == pView->FirstSelected() && nullptr == pView->NextSelected( pEntry ) )
+ // #i8234# FirstSelected() and NextSelected() ensures, that inplace editing is only triggered, when only one entry is selected
+ nFlags |= LBoxFlags::StartEditTimer;
+ if ( !pView->IsSelected( pEntry ) )
+ nFlags &= ~LBoxFlags::StartEditTimer;
+ }
+
+
+ if( (rMEvt.GetClicks() % 2) == 0 )
+ {
+ nFlags &= (~LBoxFlags::StartEditTimer);
+ pView->pHdlEntry = pEntry;
+ if( !pView->DoubleClickHdl() )
+ {
+ // Handler signals nothing to be done anymore, bail out, 'this' may
+ // even be dead and destroyed.
+ return;
+ }
+ else
+ {
+ // if the entry was deleted within the handler
+ pEntry = GetClickedEntry( aPos );
+ if( !pEntry )
+ return;
+ if( pEntry != pView->pHdlEntry )
+ {
+ // select anew & bye
+ if( !bSimpleTravel && !aSelEng.IsAlwaysAdding())
+ SelAllDestrAnch( false ); // DeselectAll();
+ SetCursor( pEntry );
+
+ return;
+ }
+ if( pEntry->HasChildren() || pEntry->HasChildrenOnDemand() )
+ {
+ if( bSubLstOpDblClick )
+ {
+ if( pView->IsExpanded(pEntry) )
+ pView->Collapse( pEntry );
+ else
+ pView->Expand( pEntry );
+ }
+ if( pEntry == pCursor ) // only if Entryitem was clicked
+ // (Nodebutton is not an Entryitem!)
+ pView->Select( pCursor );
+ return;
+ }
+ }
+ }
+ else
+ {
+ // CheckButton? (TreeListBox: Check + Info)
+ if( ButtonDownCheckCtrl(rMEvt, pEntry) )
+ return;
+ // Inplace-Editing?
+ }
+ if ( aSelEng.GetSelectionMode() != SelectionMode::NONE )
+ aSelEng.SelMouseButtonDown( rMEvt );
+}
+
+void SvImpLBox::MouseButtonUp( const MouseEvent& rMEvt)
+{
+ if ( !ButtonUpCheckCtrl( rMEvt ) && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
+ aSelEng.SelMouseButtonUp( rMEvt );
+ EndScroll();
+ if( nFlags & LBoxFlags::StartEditTimer )
+ {
+ nFlags &= (~LBoxFlags::StartEditTimer);
+ aEditClickPos = rMEvt.GetPosPixel();
+ aEditIdle.Start();
+ }
+}
+
+void SvImpLBox::MouseMove( const MouseEvent& rMEvt)
+{
+ SvTreeListEntry* pEntry = GetClickedEntry( rMEvt.GetPosPixel() );
+ if ( !MouseMoveCheckCtrl( rMEvt, pEntry ) && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
+ aSelEng.SelMouseMove( rMEvt );
+}
+
+bool SvImpLBox::KeyInput( const KeyEvent& rKEvt)
+{
+ aEditIdle.Stop();
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+ if( rKeyCode.IsMod2() )
+ return false; // don't evaluate Alt key
+
+ nFlags &= (~LBoxFlags::Filling);
+
+ if( !pCursor )
+ pCursor = pStartEntry;
+ if( !pCursor )
+ return false;
+
+ bool bKeyUsed = true;
+
+ sal_uInt16 nDelta = static_cast<sal_uInt16>(aVerSBar->GetPageSize());
+ sal_uInt16 aCode = rKeyCode.GetCode();
+
+ bool bShift = rKeyCode.IsShift();
+ bool bMod1 = rKeyCode.IsMod1();
+
+ SvTreeListEntry* pNewCursor;
+
+ const WinBits nWindowStyle = pView->GetStyle();
+ switch( aCode )
+ {
+ case KEY_UP:
+ if( !IsEntryInView( pCursor ) )
+ MakeVisible( pCursor );
+
+ pNewCursor = pCursor;
+ do
+ {
+ pNewCursor = pView->PrevVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+
+ if ( pNewCursor )
+ // new entry selected -> reset current tab position to first tab
+ nCurTabPos = FIRST_ENTRY_TAB;
+ // if there is no next entry, take the current one
+ // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
+ // the cursor key
+ if (!pNewCursor)
+ pNewCursor = pCursor;
+
+ aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ if( !IsEntryInView( pNewCursor ) )
+ KeyUp( false );
+ break;
+
+ case KEY_DOWN:
+ if( !IsEntryInView( pCursor ) )
+ MakeVisible( pCursor );
+
+ pNewCursor = pCursor;
+ do
+ {
+ pNewCursor = pView->NextVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+
+ if ( pNewCursor )
+ // new entry selected -> reset current tab position to first tab
+ nCurTabPos = FIRST_ENTRY_TAB;
+
+ // if there is no next entry, take the current one
+ // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
+ // the cursor key
+ // 06.09.20001 - 83416 - frank.schoenheit@sun.com
+ if ( !pNewCursor && pCursor )
+ pNewCursor = pCursor;
+
+ if( pNewCursor )
+ {
+ aSelEng.CursorPosChanging( bShift, bMod1 );
+ if( IsEntryInView( pNewCursor ) )
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ else
+ {
+ if( pCursor )
+ pView->Select( pCursor, false );
+ KeyDown( false );
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ }
+ }
+ else
+ KeyDown( false ); // because scrollbar range might still
+ // allow scrolling
+ break;
+
+ case KEY_RIGHT:
+ {
+ if( bSubLstOpLR )
+ {
+ // only try to expand if sublist is expandable,
+ // otherwise ignore the key press
+ if( IsExpandable() && !pView->IsExpanded( pCursor ) )
+ pView->Expand( pCursor );
+ }
+ else if ( bIsCellFocusEnabled && pCursor )
+ {
+ if ( nCurTabPos < ( pView->TabCount() - 1 /*!2*/ ) )
+ {
+ ++nCurTabPos;
+ ShowCursor( true );
+ CallEventListeners( VclEventId::ListboxSelect, pCursor );
+ }
+ }
+ else if( nWindowStyle & WB_HSCROLL )
+ {
+ long nThumb = aHorSBar->GetThumbPos();
+ nThumb += aHorSBar->GetLineSize();
+ long nOldThumb = aHorSBar->GetThumbPos();
+ aHorSBar->SetThumbPos( nThumb );
+ nThumb = nOldThumb;
+ nThumb -= aHorSBar->GetThumbPos();
+ nThumb *= -1;
+ if( nThumb )
+ {
+ KeyLeftRight( nThumb );
+ EndScroll();
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+ }
+
+ case KEY_LEFT:
+ {
+ if ( bIsCellFocusEnabled && pCursor )
+ {
+ if ( nCurTabPos > FIRST_ENTRY_TAB )
+ {
+ --nCurTabPos;
+ ShowCursor( true );
+ CallEventListeners( VclEventId::ListboxSelect, pCursor );
+ }
+ }
+ else if ( nWindowStyle & WB_HSCROLL )
+ {
+ long nThumb = aHorSBar->GetThumbPos();
+ nThumb -= aHorSBar->GetLineSize();
+ long nOldThumb = aHorSBar->GetThumbPos();
+ aHorSBar->SetThumbPos( nThumb );
+ nThumb = nOldThumb;
+ nThumb -= aHorSBar->GetThumbPos();
+ if( nThumb )
+ {
+ KeyLeftRight( -nThumb );
+ EndScroll();
+ }
+ else if( bSubLstOpLR )
+ {
+ if( IsExpandable() && pView->IsExpanded( pCursor ) )
+ pView->Collapse( pCursor );
+ else
+ {
+ pNewCursor = pView->GetParent( pCursor );
+ if( pNewCursor )
+ SetCursor( pNewCursor );
+ }
+ }
+ }
+ else if( bSubLstOpLR )
+ {
+ if( IsExpandable() && pView->IsExpanded( pCursor ) )
+ pView->Collapse( pCursor );
+ else
+ {
+ pNewCursor = pView->GetParent( pCursor );
+ if( pNewCursor )
+ SetCursor( pNewCursor );
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+ }
+
+ case KEY_PAGEUP:
+ if( !bMod1 )
+ {
+ pNewCursor = pView->PrevVisible(pCursor, nDelta);
+
+ while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = pView->NextVisible(pNewCursor);
+ nDelta--;
+ }
+
+ if( nDelta )
+ {
+ DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
+ aSelEng.CursorPosChanging( bShift, bMod1 );
+ if( IsEntryInView( pNewCursor ) )
+ SetCursor( pNewCursor );
+ else
+ {
+ SetCursor( pNewCursor );
+ KeyUp( true );
+ }
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_PAGEDOWN:
+ if( !bMod1 )
+ {
+ pNewCursor= pView->NextVisible(pCursor, nDelta);
+
+ while( nDelta && pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = pView->PrevVisible(pNewCursor);
+ nDelta--;
+ }
+
+ if( nDelta && pNewCursor )
+ {
+ DBG_ASSERT(pNewCursor && pNewCursor!=pCursor, "Cursor?");
+ aSelEng.CursorPosChanging( bShift, bMod1 );
+ if( IsEntryInView( pNewCursor ) )
+ SetCursor( pNewCursor );
+ else
+ {
+ SetCursor( pNewCursor );
+ KeyDown( true );
+ }
+ }
+ else
+ KeyDown( false ); // see also: KEY_DOWN
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_SPACE:
+ if ( pView->GetSelectionMode() != SelectionMode::NONE )
+ {
+ if ( bMod1 )
+ {
+ if ( pView->GetSelectionMode() == SelectionMode::Multiple && !bShift )
+ // toggle selection
+ pView->Select( pCursor, !pView->IsSelected( pCursor ) );
+ }
+ else if ( !bShift /*&& !bMod1*/ )
+ {
+ if ( aSelEng.IsAddMode() )
+ {
+ // toggle selection
+ pView->Select( pCursor, !pView->IsSelected( pCursor ) );
+ }
+ else if ( !pView->IsSelected( pCursor ) )
+ {
+ SelAllDestrAnch( false );
+ pView->Select( pCursor );
+ }
+ else
+ bKeyUsed = false;
+ }
+ else
+ bKeyUsed = false;
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_RETURN:
+ if( bSubLstOpRet && IsExpandable() )
+ {
+ if( pView->IsExpanded( pCursor ) )
+ pView->Collapse( pCursor );
+ else
+ pView->Expand( pCursor );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_F2:
+ if( !bShift && !bMod1 )
+ {
+ aEditClickPos = Point( -1, -1 );
+ EditTimerCall( nullptr );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_F8:
+ if( bShift && pView->GetSelectionMode()==SelectionMode::Multiple &&
+ !(m_nStyle & WB_SIMPLEMODE))
+ {
+ if( aSelEng.IsAlwaysAdding() )
+ aSelEng.AddAlways( false );
+ else
+ aSelEng.AddAlways( true );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_ADD:
+ if( pCursor )
+ {
+ if( !pView->IsExpanded(pCursor))
+ pView->Expand( pCursor );
+ if( bMod1 )
+ {
+ sal_uInt16 nRefDepth = pTree->GetDepth( pCursor );
+ SvTreeListEntry* pCur = pTree->Next( pCursor );
+ while( pCur && pTree->GetDepth(pCur) > nRefDepth )
+ {
+ if( pCur->HasChildren() && !pView->IsExpanded(pCur))
+ pView->Expand( pCur );
+ pCur = pTree->Next( pCur );
+ }
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_A:
+ if( bMod1 )
+ SelAllDestrAnch( true );
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_SUBTRACT:
+ if( pCursor )
+ {
+ if( pView->IsExpanded(pCursor))
+ pView->Collapse( pCursor );
+ if( bMod1 )
+ {
+ // collapse all parents until we get to the root
+ SvTreeListEntry* pParentToCollapse = pTree->GetRootLevelParent(pCursor);
+ if( pParentToCollapse )
+ {
+ sal_uInt16 nRefDepth;
+ // special case explorer: if the root only has a single
+ // entry, don't collapse the root entry
+ if (pTree->GetChildList(nullptr).size() < 2)
+ {
+ nRefDepth = 1;
+ pParentToCollapse = pCursor;
+ while( pTree->GetParent(pParentToCollapse) &&
+ pTree->GetDepth( pTree->GetParent(pParentToCollapse)) > 0)
+ {
+ pParentToCollapse = pTree->GetParent(pParentToCollapse);
+ }
+ }
+ else
+ nRefDepth = 0;
+
+ if( pView->IsExpanded(pParentToCollapse) )
+ pView->Collapse( pParentToCollapse );
+ SvTreeListEntry* pCur = pTree->Next( pParentToCollapse );
+ while( pCur && pTree->GetDepth(pCur) > nRefDepth )
+ {
+ if( pCur->HasChildren() && pView->IsExpanded(pCur) )
+ pView->Collapse( pCur );
+ pCur = pTree->Next( pCur );
+ }
+ }
+ }
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_DIVIDE :
+ if( bMod1 )
+ SelAllDestrAnch( true );
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_COMMA :
+ if( bMod1 )
+ SelAllDestrAnch( false );
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_HOME :
+ pNewCursor = pView->GetModel()->First();
+
+ while( pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = pView->NextVisible(pNewCursor);
+ }
+
+ if( pNewCursor && pNewCursor != pCursor )
+ {
+// SelAllDestrAnch( false );
+ aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor );
+ if( !IsEntryInView( pNewCursor ) )
+ MakeVisible( pNewCursor );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_END :
+ pNewCursor = pView->GetModel()->Last();
+
+ while( pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = pView->PrevVisible(pNewCursor);
+ }
+
+ if( pNewCursor && pNewCursor != pCursor)
+ {
+// SelAllDestrAnch( false );
+ aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor );
+ if( !IsEntryInView( pNewCursor ) )
+ MakeVisible( pNewCursor );
+ }
+ else
+ bKeyUsed = false;
+ break;
+
+ case KEY_ESCAPE:
+ case KEY_TAB:
+ case KEY_DELETE:
+ case KEY_BACKSPACE:
+ // must not be handled because this quits dialogs and does other magic things...
+ // if there are other single keys which should not be handled, they can be added here
+ bKeyUsed = false;
+ break;
+
+ default:
+ // is there any reason why we should eat the events here? The only place where this is called
+ // is from SvTreeListBox::KeyInput. If we set bKeyUsed to true here, then the key input
+ // is just silenced. However, we want SvLBox::KeyInput to get a chance, to do the QuickSelection
+ // handling.
+ // (The old code here which intentionally set bKeyUsed to sal_True said this was because of "quick search"
+ // handling, but actually there was no quick search handling anymore. We just re-implemented it.)
+ // #i31275# / 2009-06-16 / frank.schoenheit@sun.com
+ bKeyUsed = false;
+ break;
+ }
+ return bKeyUsed;
+}
+
+void SvImpLBox::GetFocus()
+{
+ if( pCursor )
+ {
+ pView->SetEntryFocus( pCursor, true );
+ ShowCursor( true );
+// auskommentiert wg. deselectall
+// if( bSimpleTravel && !pView->IsSelected(pCursor) )
+// pView->Select( pCursor, true );
+ }
+ if( m_nStyle & WB_HIDESELECTION )
+ {
+ SvTreeListEntry* pEntry = pView->FirstSelected();
+ while( pEntry )
+ {
+ InvalidateEntry( pEntry );
+ pEntry = pView->NextSelected( pEntry );
+ }
+ }
+}
+
+void SvImpLBox::LoseFocus()
+{
+ aEditIdle.Stop();
+ if( pCursor )
+ pView->SetEntryFocus( pCursor,false );
+ ShowCursor( false );
+
+ if( m_nStyle & WB_HIDESELECTION )
+ {
+ SvTreeListEntry* pEntry = pView ? pView->FirstSelected() : nullptr;
+ while( pEntry )
+ {
+ InvalidateEntry( pEntry );
+ pEntry = pView->NextSelected( pEntry );
+ }
+ }
+}
+
+
+// ********************************************************************
+// SelectionEngine
+// ********************************************************************
+
+void SvImpLBox::SelectEntry( SvTreeListEntry* pEntry, bool bSelect )
+{
+ pView->Select( pEntry, bSelect );
+}
+
+ImpLBSelEng::ImpLBSelEng( SvImpLBox* pImpl, SvTreeListBox* pV )
+{
+ pImp = pImpl;
+ pView = pV;
+}
+
+ImpLBSelEng::~ImpLBSelEng()
+{
+}
+
+void ImpLBSelEng::BeginDrag()
+{
+ pImp->BeginDrag();
+}
+
+void ImpLBSelEng::CreateAnchor()
+{
+ pImp->pAnchor = pImp->pCursor;
+}
+
+void ImpLBSelEng::DestroyAnchor()
+{
+ pImp->pAnchor = nullptr;
+}
+
+void ImpLBSelEng::SetCursorAtPoint(const Point& rPoint, bool bDontSelectAtCursor)
+{
+ SvTreeListEntry* pNewCursor = pImp->MakePointVisible( rPoint );
+ if( pNewCursor != pImp->pCursor )
+ pImp->BeginScroll();
+
+ if( pNewCursor )
+ {
+ // at SimpleTravel, the SetCursor is selected and the select handler is
+ // called
+ //if( !bDontSelectAtCursor && !pImp->bSimpleTravel )
+ // pImp->SelectEntry( pNewCursor, true );
+ pImp->SetCursor( pNewCursor, bDontSelectAtCursor );
+ }
+}
+
+bool ImpLBSelEng::IsSelectionAtPoint( const Point& rPoint )
+{
+ SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
+ if( pEntry )
+ return pView->IsSelected(pEntry);
+ return false;
+}
+
+void ImpLBSelEng::DeselectAtPoint( const Point& rPoint )
+{
+ SvTreeListEntry* pEntry = pImp->MakePointVisible( rPoint );
+ if( !pEntry )
+ return;
+ pImp->SelectEntry( pEntry, false );
+}
+
+void ImpLBSelEng::DeselectAll()
+{
+ pImp->SelAllDestrAnch( false, false ); // don't reset SelectionEngine!
+ pImp->nFlags &= (~LBoxFlags::DeselectAll);
+}
+
+// ***********************************************************************
+// Selection
+// ***********************************************************************
+
+void SvImpLBox::SetAnchorSelection(SvTreeListEntry* pOldCursor,SvTreeListEntry* pNewCursor)
+{
+ SvTreeListEntry* pEntry;
+ sal_uLong nAnchorVisPos = pView->GetVisiblePos( pAnchor );
+ sal_uLong nOldVisPos = pView->GetVisiblePos( pOldCursor );
+ sal_uLong nNewVisPos = pView->GetVisiblePos( pNewCursor );
+
+ if( nOldVisPos > nAnchorVisPos ||
+ ( nAnchorVisPos==nOldVisPos && nNewVisPos > nAnchorVisPos) )
+ {
+ if( nNewVisPos > nOldVisPos )
+ {
+ pEntry = pOldCursor;
+ while( pEntry && pEntry != pNewCursor )
+ {
+ pView->Select( pEntry );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos < nAnchorVisPos )
+ {
+ pEntry = pAnchor;
+ while( pEntry && pEntry != pOldCursor )
+ {
+ pView->Select( pEntry, false );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ pView->Select( pEntry, false );
+
+ pEntry = pNewCursor;
+ while( pEntry && pEntry != pAnchor )
+ {
+ pView->Select( pEntry );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos < nOldVisPos )
+ {
+ pEntry = pNewCursor;
+ pEntry = pView->NextVisible(pEntry);
+ while( pEntry && pEntry != pOldCursor )
+ {
+ pView->Select( pEntry, false );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ pView->Select( pEntry, false );
+ return;
+ }
+ }
+ else
+ {
+ if( nNewVisPos < nOldVisPos ) // enlarge selection
+ {
+ pEntry = pNewCursor;
+ while( pEntry && pEntry != pOldCursor )
+ {
+ pView->Select( pEntry );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos > nAnchorVisPos )
+ {
+ pEntry = pOldCursor;
+ while( pEntry && pEntry != pAnchor )
+ {
+ pView->Select( pEntry, false );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ pView->Select( pEntry, false );
+ pEntry = pAnchor;
+ while( pEntry && pEntry != pNewCursor )
+ {
+ pView->Select( pEntry );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ if( pEntry )
+ pView->Select( pEntry );
+ return;
+ }
+
+ if( nNewVisPos > nOldVisPos )
+ {
+ pEntry = pOldCursor;
+ while( pEntry && pEntry != pNewCursor )
+ {
+ pView->Select( pEntry, false );
+ pEntry = pView->NextVisible(pEntry);
+ }
+ return;
+ }
+ }
+}
+
+void SvImpLBox::SelAllDestrAnch(
+ bool bSelect, bool bDestroyAnchor, bool bSingleSelToo )
+{
+ SvTreeListEntry* pEntry;
+ nFlags &= (~LBoxFlags::DeselectAll);
+ if( bSelect && bSimpleTravel )
+ {
+ if( pCursor && !pView->IsSelected( pCursor ))
+ {
+ pView->Select( pCursor );
+ }
+ return;
+ }
+ if( !bSelect && pView->GetSelectionCount() == 0 )
+ {
+ if( bSimpleTravel && ( !GetUpdateMode() || !pCursor) )
+ nFlags |= LBoxFlags::DeselectAll;
+ return;
+ }
+ if( bSelect && pView->GetSelectionCount() == pView->GetEntryCount())
+ return;
+ if( !bSingleSelToo && bSimpleTravel )
+ return;
+
+ if( !bSelect && pView->GetSelectionCount()==1 && pCursor &&
+ pView->IsSelected( pCursor ))
+ {
+ pView->Select( pCursor, false );
+ if( bDestroyAnchor )
+ DestroyAnchor(); // delete anchor & reset SelectionEngine
+ else
+ pAnchor = nullptr; // always delete internal anchor
+ return;
+ }
+
+ if( bSimpleTravel && !pCursor && !GetUpdateMode() )
+ nFlags |= LBoxFlags::DeselectAll;
+
+ ShowCursor( false );
+ bool bUpdate = GetUpdateMode();
+
+ nFlags |= LBoxFlags::IgnoreSelect; // EntryInserted should not do anything
+ pEntry = pTree->First();
+ while( pEntry )
+ {
+ if( pView->Select( pEntry, bSelect ) )
+ {
+ if( bUpdate && pView->IsEntryVisible(pEntry) )
+ {
+ long nY = GetEntryLine( pEntry );
+ if( IsLineVisible( nY ) )
+ InvalidateEntry(pEntry);
+ }
+ }
+ pEntry = pTree->Next( pEntry );
+ }
+ nFlags &= ~LBoxFlags::IgnoreSelect;
+
+ if( bDestroyAnchor )
+ DestroyAnchor(); // delete anchor & reset SelectionEngine
+ else
+ pAnchor = nullptr; // always delete internal anchor
+ ShowCursor( true );
+}
+
+void SvImpLBox::SetSelectionMode( SelectionMode eSelMode )
+{
+ aSelEng.SetSelectionMode( eSelMode);
+ if( eSelMode == SelectionMode::Single )
+ bSimpleTravel = true;
+ else
+ bSimpleTravel = false;
+ if( (m_nStyle & WB_SIMPLEMODE) && (eSelMode == SelectionMode::Multiple) )
+ aSelEng.AddAlways( true );
+}
+
+// ***********************************************************************
+// Drag & Drop
+// ***********************************************************************
+
+void SvImpLBox::SetDragDropMode( DragDropMode eDDMode )
+{
+ if( eDDMode != DragDropMode::NONE && eDDMode != DragDropMode::APP_DROP )
+ {
+ aSelEng.ExpandSelectionOnMouseMove( false );
+ aSelEng.EnableDrag( true );
+ }
+ else
+ {
+ aSelEng.ExpandSelectionOnMouseMove();
+ aSelEng.EnableDrag( false );
+ }
+}
+
+void SvImpLBox::BeginDrag()
+{
+ nFlags &= (~LBoxFlags::Filling);
+ if( !bAsyncBeginDrag )
+ {
+ BeginScroll();
+ pView->StartDrag( 0, aSelEng.GetMousePosPixel() );
+ EndScroll();
+ }
+ else
+ {
+ aAsyncBeginDragPos = aSelEng.GetMousePosPixel();
+ aAsyncBeginDragIdle.Start();
+ }
+}
+
+IMPL_LINK_NOARG(SvImpLBox, BeginDragHdl, Timer *, void)
+{
+ pView->StartDrag( 0, aAsyncBeginDragPos );
+}
+
+void SvImpLBox::PaintDDCursor( SvTreeListEntry* pInsertionPos )
+{
+ long nY;
+ if( pInsertionPos )
+ {
+ nY = GetEntryLine( pInsertionPos );
+ nY += pView->GetEntryHeight();
+ }
+ else
+ nY = 1;
+ RasterOp eOldOp = pView->GetRasterOp();
+ pView->SetRasterOp( RasterOp::Invert );
+ Color aOldLineColor = pView->GetLineColor();
+ pView->SetLineColor( COL_BLACK );
+ pView->DrawLine( Point( 0, nY ), Point( aOutputSize.Width(), nY ) );
+ pView->SetLineColor( aOldLineColor );
+ pView->SetRasterOp( eOldOp );
+}
+
+void SvImpLBox::Command( const CommandEvent& rCEvt )
+{
+ CommandEventId nCommand = rCEvt.GetCommand();
+
+ if( nCommand == CommandEventId::ContextMenu )
+ aEditIdle.Stop();
+
+ // scroll mouse event?
+ if( ( ( nCommand == CommandEventId::Wheel ) || ( nCommand == CommandEventId::StartAutoScroll ) || ( nCommand == CommandEventId::AutoScroll ) )
+ && pView->HandleScrollCommand( rCEvt, aHorSBar.get(), aVerSBar.get() ) )
+ return;
+
+ if( bContextMenuHandling && nCommand == CommandEventId::ContextMenu )
+ {
+ Point aPopupPos;
+ bool bClickedIsFreePlace = false;
+ std::stack<SvTreeListEntry*> aSelRestore;
+
+ if( rCEvt.IsMouseEvent() )
+ { // change selection, if mouse position doesn't fit to selection
+
+ aPopupPos = rCEvt.GetMousePosPixel();
+
+ SvTreeListEntry* pClickedEntry = GetEntry( aPopupPos );
+ if( pClickedEntry )
+ { // mouse in non empty area
+ bool bClickedIsSelected = false;
+
+ // collect the currently selected entries
+ SvTreeListEntry* pSelected = pView->FirstSelected();
+ while( pSelected )
+ {
+ bClickedIsSelected |= ( pClickedEntry == pSelected );
+ pSelected = pView->NextSelected( pSelected );
+ }
+
+ // if the entry which the user clicked at is not selected
+ if( !bClickedIsSelected )
+ { // deselect all other and select the clicked one
+ pView->SelectAll( false );
+ pView->SetCursor( pClickedEntry );
+ }
+ }
+ else if( aSelEng.GetSelectionMode() == SelectionMode::Single )
+ {
+ bClickedIsFreePlace = true;
+ sal_Int32 nSelectedEntries = pView->GetSelectionCount();
+ SvTreeListEntry* pSelected = pView->FirstSelected();
+ for(sal_Int32 nSel = 0; nSel < nSelectedEntries; nSel++ )
+ {
+ aSelRestore.push(pSelected);
+ pSelected = pView->NextSelected( pSelected );
+ }
+ pView->SelectAll( false );
+ }
+ else
+ { // deselect all
+ pView->SelectAll( false );
+ }
+
+
+ }
+ else
+ { // key event (or at least no mouse event)
+ sal_Int32 nSelectionCount = pView->GetSelectionCount();
+
+ if( nSelectionCount )
+ { // now always take first visible as base for positioning the menu
+ SvTreeListEntry* pSelected = pView->FirstSelected();
+ while( pSelected )
+ {
+ if( IsEntryInView( pSelected ) )
+ break;
+
+ pSelected = pView->NextSelected( pSelected );
+ }
+
+ if( !pSelected )
+ {
+ // no one was visible
+ pSelected = pView->FirstSelected();
+ pView->MakeVisible( pSelected );
+ }
+
+ aPopupPos = pView->GetFocusRect( pSelected, pView->GetEntryPosition( pSelected ).Y() ).Center();
+ }
+ else
+ aPopupPos = Point( 0, 0 );
+ }
+
+ {
+ VclPtr<PopupMenu> pPopup = pView->CreateContextMenu();
+ if (pPopup)
+ {
+ // do action for selected entry in popup menu
+ sal_uInt16 nMenuAction = pPopup->Execute( pView, aPopupPos );
+ if ( nMenuAction )
+ pView->ExecuteContextMenuAction( nMenuAction );
+ pPopup.disposeAndClear();
+ }
+ }
+
+ if( bClickedIsFreePlace )
+ {
+ while(!aSelRestore.empty())
+ {
+ SvTreeListEntry* pEntry = aSelRestore.top();
+ //#i19717# the entry is maybe already deleted
+ bool bFound = false;
+ for(sal_uLong nEntry = 0; nEntry < pView->GetEntryCount(); nEntry++)
+ if(pEntry == pView->GetEntry(nEntry))
+ {
+ bFound = true;
+ break;
+ }
+ if(bFound)
+ SetCurEntry( pEntry );
+ aSelRestore.pop();
+ }
+ }
+ }
+ else
+ {
+ const Point& rPos = rCEvt.GetMousePosPixel();
+ if( rPos.X() < aOutputSize.Width() && rPos.Y() < aOutputSize.Height() )
+ aSelEng.Command( rCEvt );
+ }
+}
+
+void SvImpLBox::BeginScroll()
+{
+ if( !(nFlags & LBoxFlags::InScrolling))
+ {
+ nFlags |= LBoxFlags::InScrolling;
+ }
+}
+
+void SvImpLBox::EndScroll()
+{
+ if( nFlags & LBoxFlags::InScrolling)
+ {
+ pView->NotifyEndScroll();
+ nFlags &= (~LBoxFlags::InScrolling);
+ }
+}
+
+
+tools::Rectangle SvImpLBox::GetVisibleArea() const
+{
+ Point aPos( pView->GetMapMode().GetOrigin() );
+ aPos.setX( aPos.X() * -1 );
+ tools::Rectangle aRect( aPos, aOutputSize );
+ return aRect;
+}
+
+void SvImpLBox::Invalidate()
+{
+ pView->SetClipRegion();
+}
+
+void SvImpLBox::SetCurEntry( SvTreeListEntry* pEntry )
+{
+ if ( ( aSelEng.GetSelectionMode() != SelectionMode::Single )
+ && ( aSelEng.GetSelectionMode() != SelectionMode::NONE )
+ )
+ SelAllDestrAnch( false );
+ if ( pEntry )
+ MakeVisible( pEntry );
+ SetCursor( pEntry );
+ if ( pEntry && ( aSelEng.GetSelectionMode() != SelectionMode::NONE ) )
+ pView->Select( pEntry );
+}
+
+IMPL_LINK_NOARG(SvImpLBox, EditTimerCall, Timer *, void)
+{
+ if( !pView->IsInplaceEditingEnabled() )
+ return;
+
+ bool bIsMouseTriggered = aEditClickPos.X() >= 0;
+ if ( bIsMouseTriggered )
+ {
+ Point aCurrentMousePos = pView->GetPointerPosPixel();
+ if ( ( std::abs( aCurrentMousePos.X() - aEditClickPos.X() ) > 5 )
+ || ( std::abs( aCurrentMousePos.Y() - aEditClickPos.Y() ) > 5 )
+ )
+ {
+ return;
+ }
+ }
+
+ SvTreeListEntry* pEntry = GetCurEntry();
+ if( pEntry )
+ {
+ ShowCursor( false );
+ pView->ImplEditEntry( pEntry );
+ ShowCursor( true );
+ }
+}
+
+bool SvImpLBox::RequestHelp( const HelpEvent& rHEvt )
+{
+ if( rHEvt.GetMode() & HelpEventMode::QUICK )
+ {
+ Point aPos( pView->ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
+ if( !GetVisibleArea().IsInside( aPos ))
+ return false;
+
+ SvTreeListEntry* pEntry = GetEntry( aPos );
+ if( pEntry )
+ {
+ // recalculate text rectangle
+ SvLBoxTab* pTab;
+ SvLBoxItem* pItem = pView->GetItem( pEntry, aPos.X(), &pTab );
+ if (!pItem || pItem->GetType() != SvLBoxItemType::String)
+ return false;
+
+ aPos = GetEntryPosition( pEntry );
+ aPos.setX( pView->GetTabPos( pEntry, pTab ) ); //pTab->GetPos();
+ Size aSize( pItem->GetSize( pView, pEntry ) );
+ SvLBoxTab* pNextTab = NextTab( pTab );
+ bool bItemClipped = false;
+ // is the item cut off by its right neighbor?
+ if( pNextTab && pView->GetTabPos(pEntry,pNextTab) < aPos.X()+aSize.Width() )
+ {
+ aSize.setWidth( pNextTab->GetPos() - pTab->GetPos() );
+ bItemClipped = true;
+ }
+ tools::Rectangle aItemRect( aPos, aSize );
+
+ tools::Rectangle aViewRect( GetVisibleArea() );
+
+ if( bItemClipped || !aViewRect.IsInside( aItemRect ) )
+ {
+ // clip the right edge of the item at the edge of the view
+ //if( aItemRect.Right() > aViewRect.Right() )
+ // aItemRect.Right() = aViewRect.Right();
+
+ Point aPt = pView->OutputToScreenPixel( aItemRect.TopLeft() );
+ aItemRect.SetLeft( aPt.X() );
+ aItemRect.SetTop( aPt.Y() );
+ aPt = pView->OutputToScreenPixel( aItemRect.BottomRight() );
+ aItemRect.SetRight( aPt.X() );
+ aItemRect.SetBottom( aPt.Y() );
+
+ Help::ShowQuickHelp( pView, aItemRect,
+ static_cast<SvLBoxString*>(pItem)->GetText(), QuickHelpFlags::Left | QuickHelpFlags::VCenter );
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SvLBoxTab* SvImpLBox::NextTab( SvLBoxTab const * pTab )
+{
+ sal_uInt16 nTabCount = pView->TabCount();
+ if( nTabCount <= 1 )
+ return nullptr;
+ for( int nTab=0; nTab < (nTabCount-1); nTab++)
+ {
+ if( pView->aTabs[nTab].get() == pTab )
+ return pView->aTabs[nTab+1].get();
+ }
+ return nullptr;
+}
+
+void SvImpLBox::EndSelection()
+{
+ DestroyAnchor();
+ nFlags &= ~LBoxFlags::StartEditTimer;
+}
+
+void SvImpLBox::SetUpdateMode( bool bMode )
+{
+ if( bUpdateMode != bMode )
+ {
+ bUpdateMode = bMode;
+ if( bUpdateMode )
+ UpdateAll( false );
+ }
+}
+
+bool SvImpLBox::SetMostRight( SvTreeListEntry* pEntry )
+{
+ if( pView->nTreeFlags & SvTreeFlags::RECALCTABS )
+ {
+ nFlags |= LBoxFlags::IgnoreChangedTabs;
+ pView->SetTabs();
+ nFlags &= ~LBoxFlags::IgnoreChangedTabs;
+ }
+
+ sal_uInt16 nLastTab = pView->aTabs.size() - 1;
+ sal_uInt16 nLastItem = pEntry->ItemCount() - 1;
+ if( !pView->aTabs.empty() && nLastItem != USHRT_MAX )
+ {
+ if( nLastItem < nLastTab )
+ nLastTab = nLastItem;
+
+ SvLBoxTab* pTab = pView->aTabs[ nLastTab ].get();
+ SvLBoxItem& rItem = pEntry->GetItem( nLastTab );
+
+ long nTabPos = pView->GetTabPos( pEntry, pTab );
+
+ long nMaxRight = GetOutputSize().Width();
+ Point aPos( pView->GetMapMode().GetOrigin() );
+ aPos.setX( aPos.X() * -1 ); // conversion document coordinates
+ nMaxRight = nMaxRight + aPos.X() - 1;
+
+ long nNextTab = nTabPos < nMaxRight ? nMaxRight : nMaxRight + 50;
+ long nTabWidth = nNextTab - nTabPos + 1;
+ long nItemSize = rItem.GetSize(pView,pEntry).Width();
+ long nOffset = pTab->CalcOffset( nItemSize, nTabWidth );
+
+ long nRight = nTabPos + nOffset + nItemSize;
+ if( nRight > nMostRight )
+ {
+ nMostRight = nRight;
+ pMostRightEntry = pEntry;
+ return true;
+ }
+ }
+ return false;
+}
+
+void SvImpLBox::FindMostRight( SvTreeListEntry const * pEntryToIgnore )
+{
+ nMostRight = -1;
+ pMostRightEntry = nullptr;
+ if( !pView->GetModel() )
+ return;
+
+ SvTreeListEntry* pEntry = pView->FirstVisible();
+ while( pEntry )
+ {
+ if( pEntry != pEntryToIgnore )
+ SetMostRight( pEntry );
+ pEntry = pView->NextVisible( pEntry );
+ }
+}
+
+void SvImpLBox::FindMostRight( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
+{
+ if( !pParent )
+ FindMostRight( pEntryToIgnore );
+ else
+ FindMostRight_Impl( pParent, pEntryToIgnore );
+}
+
+void SvImpLBox::FindMostRight_Impl( SvTreeListEntry* pParent, SvTreeListEntry* pEntryToIgnore )
+{
+ SvTreeListEntries& rList = pTree->GetChildList( pParent );
+
+ size_t nCount = rList.size();
+ for( size_t nCur = 0; nCur < nCount; nCur++ )
+ {
+ SvTreeListEntry* pChild = rList[nCur].get();
+ if( pChild != pEntryToIgnore )
+ {
+ SetMostRight( pChild );
+ if( pChild->HasChildren() && pView->IsExpanded( pChild ))
+ FindMostRight_Impl( pChild, pEntryToIgnore );
+ }
+ }
+}
+
+void SvImpLBox::NotifyTabsChanged()
+{
+ if( GetUpdateMode() && !(nFlags & LBoxFlags::IgnoreChangedTabs ) &&
+ nCurUserEvent == nullptr )
+ {
+ nCurUserEvent = Application::PostUserEvent(LINK(this,SvImpLBox,MyUserEvent));
+ }
+}
+
+bool SvImpLBox::IsExpandable() const
+{
+ return pCursor->HasChildren() || pCursor->HasChildrenOnDemand();
+}
+
+IMPL_LINK(SvImpLBox, MyUserEvent, void*, pArg, void )
+{
+ nCurUserEvent = nullptr;
+ if( !pArg )
+ {
+ pView->Invalidate();
+ pView->Update();
+ }
+ else
+ {
+ FindMostRight( nullptr );
+ ShowVerSBar();
+ pView->Invalidate( GetVisibleArea() );
+ }
+}
+
+
+void SvImpLBox::StopUserEvent()
+{
+ if( nCurUserEvent != nullptr )
+ {
+ Application::RemoveUserEvent( nCurUserEvent );
+ nCurUserEvent = nullptr;
+ }
+}
+
+void SvImpLBox::ShowFocusRect( const SvTreeListEntry* pEntry )
+{
+ if( pEntry )
+ {
+ long nY = GetEntryLine( const_cast<SvTreeListEntry*>(pEntry) );
+ tools::Rectangle aRect = pView->GetFocusRect( const_cast<SvTreeListEntry*>(pEntry), nY );
+ vcl::Region aOldClip( pView->GetClipRegion());
+ vcl::Region aClipRegion( GetClipRegionRect() );
+ pView->SetClipRegion( aClipRegion );
+ pView->ShowFocus( aRect );
+ pView->SetClipRegion( aOldClip );
+
+ }
+ else
+ {
+ pView->HideFocus();
+ }
+}
+
+
+void SvImpLBox::implInitDefaultNodeImages()
+{
+ if ( s_pDefCollapsed )
+ // assume that all or nothing is initialized
+ return;
+
+ s_pDefCollapsed = new Image(BitmapEx(RID_BMP_TREENODE_COLLAPSED));
+ s_pDefExpanded = new Image(BitmapEx(RID_BMP_TREENODE_EXPANDED));
+}
+
+
+const Image& SvImpLBox::GetDefaultExpandedNodeImage( )
+{
+ implInitDefaultNodeImages();
+ return *s_pDefExpanded;
+}
+
+
+const Image& SvImpLBox::GetDefaultCollapsedNodeImage( )
+{
+ implInitDefaultNodeImages();
+ return *s_pDefCollapsed;
+}
+
+
+void SvImpLBox::CallEventListeners( VclEventId nEvent, void* pData )
+{
+ if ( pView )
+ pView->CallImplEventListeners( nEvent, pData);
+}
+
+
+bool SvImpLBox::SetCurrentTabPos( sal_uInt16 _nNewPos )
+{
+ bool bRet = false;
+
+ if ( pView && _nNewPos < ( pView->TabCount() - 2 ) )
+ {
+ nCurTabPos = _nNewPos;
+ ShowCursor( true );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+
+bool SvImpLBox::IsSelectable( const SvTreeListEntry* pEntry )
+{
+ if( pEntry )
+ {
+ SvViewDataEntry* pViewDataNewCur = pView->GetViewDataEntry(pEntry);
+ return (pViewDataNewCur == nullptr) || pViewDataNewCur->IsSelectable();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/svlbitm.cxx b/vcl/source/treelist/svlbitm.cxx
new file mode 100644
index 000000000000..734c5a43a976
--- /dev/null
+++ b/vcl/source/treelist/svlbitm.cxx
@@ -0,0 +1,461 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/treelistbox.hxx>
+#include <vcl/svlbitm.hxx>
+#include <vcl/treelistentry.hxx>
+#include <vcl/viewdataentry.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/button.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/salnativewidgets.hxx>
+
+struct SvLBoxButtonData_Impl
+{
+ SvTreeListEntry* pEntry;
+ bool bDefaultImages;
+ bool bShowRadioButton;
+
+ SvLBoxButtonData_Impl() : pEntry( nullptr ), bDefaultImages( false ), bShowRadioButton( false ) {}
+};
+
+void SvLBoxButtonData::InitData( bool _bRadioBtn, const Control* pCtrl )
+{
+ nWidth = nHeight = 0;
+
+ aBmps.resize(int(SvBmp::STATICIMAGE)+1);
+
+ bDataOk = false;
+ pImpl->bDefaultImages = true;
+ pImpl->bShowRadioButton = _bRadioBtn;
+
+ SetDefaultImages( pCtrl );
+}
+
+SvLBoxButtonData::SvLBoxButtonData( const Control* pControlForSettings )
+ : pImpl( new SvLBoxButtonData_Impl )
+{
+ InitData( false, pControlForSettings );
+}
+
+SvLBoxButtonData::SvLBoxButtonData( const Control* pControlForSettings, bool _bRadioBtn )
+ : pImpl( new SvLBoxButtonData_Impl )
+{
+ InitData( _bRadioBtn, pControlForSettings );
+}
+
+SvLBoxButtonData::~SvLBoxButtonData()
+{
+}
+
+void SvLBoxButtonData::CallLink()
+{
+ aLink.Call( this );
+}
+
+SvBmp SvLBoxButtonData::GetIndex( SvItemStateFlags nItemState )
+{
+ SvBmp nIdx;
+ if (nItemState == SvItemStateFlags::UNCHECKED)
+ nIdx = SvBmp::UNCHECKED;
+ else if (nItemState == SvItemStateFlags::CHECKED)
+ nIdx = SvBmp::CHECKED;
+ else if (nItemState == SvItemStateFlags::TRISTATE)
+ nIdx = SvBmp::TRISTATE;
+ else if (nItemState == (SvItemStateFlags::UNCHECKED | SvItemStateFlags::HILIGHTED))
+ nIdx = SvBmp::HIUNCHECKED;
+ else if (nItemState == (SvItemStateFlags::CHECKED | SvItemStateFlags::HILIGHTED))
+ nIdx = SvBmp::HICHECKED;
+ else if (nItemState == (SvItemStateFlags::TRISTATE | SvItemStateFlags::HILIGHTED))
+ nIdx = SvBmp::HITRISTATE;
+ else
+ nIdx = SvBmp::UNCHECKED;
+ return nIdx;
+}
+
+void SvLBoxButtonData::SetWidthAndHeight()
+{
+ Size aSize = aBmps[int(SvBmp::UNCHECKED)].GetSizePixel();
+ nWidth = aSize.Width();
+ nHeight = aSize.Height();
+ bDataOk = true;
+}
+
+
+void SvLBoxButtonData::StoreButtonState( SvTreeListEntry* pActEntry )
+{
+ pImpl->pEntry = pActEntry;
+}
+
+SvButtonState SvLBoxButtonData::ConvertToButtonState( SvItemStateFlags nItemFlags )
+{
+ nItemFlags &= (SvItemStateFlags::UNCHECKED |
+ SvItemStateFlags::CHECKED |
+ SvItemStateFlags::TRISTATE);
+ switch( nItemFlags )
+ {
+ case SvItemStateFlags::UNCHECKED:
+ return SvButtonState::Unchecked;
+ case SvItemStateFlags::CHECKED:
+ return SvButtonState::Checked;
+ case SvItemStateFlags::TRISTATE:
+ return SvButtonState::Tristate;
+ default:
+ return SvButtonState::Unchecked;
+ }
+}
+
+SvTreeListEntry* SvLBoxButtonData::GetActEntry() const
+{
+ assert(pImpl && "-SvLBoxButtonData::GetActEntry(): don't use me that way!");
+ return pImpl->pEntry;
+}
+
+void SvLBoxButtonData::SetDefaultImages( const Control* pCtrl )
+{
+ const AllSettings& rSettings = pCtrl? pCtrl->GetSettings() : Application::GetSettings();
+
+ if ( pImpl->bShowRadioButton )
+ {
+ SetImage(SvBmp::UNCHECKED, RadioButton::GetRadioImage( rSettings, DrawButtonFlags::Default ) );
+ SetImage(SvBmp::CHECKED, RadioButton::GetRadioImage( rSettings, DrawButtonFlags::Checked ) );
+ SetImage(SvBmp::HICHECKED, RadioButton::GetRadioImage( rSettings, DrawButtonFlags::Checked | DrawButtonFlags::Pressed ) );
+ SetImage(SvBmp::HIUNCHECKED, RadioButton::GetRadioImage( rSettings, DrawButtonFlags::Default | DrawButtonFlags::Pressed ) );
+ SetImage(SvBmp::TRISTATE, RadioButton::GetRadioImage( rSettings, DrawButtonFlags::DontKnow ) );
+ SetImage(SvBmp::HITRISTATE, RadioButton::GetRadioImage( rSettings, DrawButtonFlags::DontKnow | DrawButtonFlags::Pressed ) );
+ }
+ else
+ {
+ SetImage(SvBmp::UNCHECKED, CheckBox::GetCheckImage( rSettings, DrawButtonFlags::Default ) );
+ SetImage(SvBmp::CHECKED, CheckBox::GetCheckImage( rSettings, DrawButtonFlags::Checked ) );
+ SetImage(SvBmp::HICHECKED, CheckBox::GetCheckImage( rSettings, DrawButtonFlags::Checked | DrawButtonFlags::Pressed ) );
+ SetImage(SvBmp::HIUNCHECKED, CheckBox::GetCheckImage( rSettings, DrawButtonFlags::Default | DrawButtonFlags::Pressed ) );
+ SetImage(SvBmp::TRISTATE, CheckBox::GetCheckImage( rSettings, DrawButtonFlags::DontKnow ) );
+ SetImage(SvBmp::HITRISTATE, CheckBox::GetCheckImage( rSettings, DrawButtonFlags::DontKnow | DrawButtonFlags::Pressed ) );
+ }
+}
+
+bool SvLBoxButtonData::HasDefaultImages() const
+{
+ return pImpl->bDefaultImages;
+}
+
+bool SvLBoxButtonData::IsRadio() {
+ return pImpl->bShowRadioButton;
+}
+
+// ***************************************************************
+// class SvLBoxString
+// ***************************************************************
+
+
+SvLBoxString::SvLBoxString(const OUString& rStr)
+{
+ SetText(rStr);
+}
+
+SvLBoxString::SvLBoxString() : SvLBoxItem()
+{
+}
+
+SvLBoxString::~SvLBoxString()
+{
+}
+
+SvLBoxItemType SvLBoxString::GetType() const
+{
+ return SvLBoxItemType::String;
+}
+
+void SvLBoxString::Paint(
+ const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext,
+ const SvViewDataEntry* /*pView*/, const SvTreeListEntry& rEntry)
+{
+ Size aSize = GetSize(&rDev, &rEntry);
+ DrawTextFlags nStyle = rDev.IsEnabled() ? DrawTextFlags::NONE : DrawTextFlags::Disable;
+ if (rDev.IsEntryMnemonicsEnabled())
+ nStyle |= DrawTextFlags::Mnemonic;
+ if (rDev.TextCenterAndClipEnabled())
+ {
+ nStyle |= DrawTextFlags::PathEllipsis | DrawTextFlags::Center;
+ aSize.setWidth( rDev.GetEntryWidth() );
+ }
+ rRenderContext.DrawText(tools::Rectangle(rPos, aSize), maText, nStyle);
+}
+
+std::unique_ptr<SvLBoxItem> SvLBoxString::Clone(SvLBoxItem const * pSource) const
+{
+ std::unique_ptr<SvLBoxString> pNew(new SvLBoxString);
+ pNew->maText = static_cast<SvLBoxString const *>(pSource)->maText;
+ return std::unique_ptr<SvLBoxItem>(pNew.release());
+}
+
+void SvLBoxString::InitViewData(
+ SvTreeListBox* pView, SvTreeListEntry* pEntry, SvViewDataItem* pViewData)
+{
+ if( !pViewData )
+ pViewData = pView->GetViewDataItem( pEntry, this );
+ pViewData->maSize = Size(pView->GetTextWidth(maText), pView->GetTextHeight());
+}
+
+// ***************************************************************
+// class SvLBoxButton
+// ***************************************************************
+
+
+SvLBoxButton::SvLBoxButton( SvLBoxButtonKind eTheKind,
+ SvLBoxButtonData* pBData )
+ : isVis(true)
+ , pData(pBData)
+ , eKind(eTheKind)
+ , nItemFlags(SvItemStateFlags::NONE)
+{
+ SetStateUnchecked();
+}
+
+SvLBoxButton::SvLBoxButton()
+ : SvLBoxItem()
+ , isVis(false)
+ , pData(nullptr)
+ , eKind(SvLBoxButtonKind::EnabledCheckbox)
+ , nItemFlags(SvItemStateFlags::NONE)
+{
+ SetStateUnchecked();
+}
+
+SvLBoxButton::~SvLBoxButton()
+{
+}
+
+SvLBoxItemType SvLBoxButton::GetType() const
+{
+ return SvLBoxItemType::Button;
+}
+
+void SvLBoxButton::ClickHdl( SvTreeListEntry* pEntry )
+{
+ if ( CheckModification() )
+ {
+ if ( IsStateChecked() )
+ SetStateUnchecked();
+ else
+ SetStateChecked();
+ pData->StoreButtonState( pEntry );
+ pData->CallLink();
+ }
+}
+
+void SvLBoxButton::Paint(
+ const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext,
+ const SvViewDataEntry* /*pView*/, const SvTreeListEntry& /*rEntry*/)
+{
+ SvBmp nIndex = eKind == SvLBoxButtonKind::StaticImage ? SvBmp::STATICIMAGE : SvLBoxButtonData::GetIndex(nItemFlags);
+ DrawImageFlags nStyle = eKind != SvLBoxButtonKind::DisabledCheckbox && rDev.IsEnabled() ? DrawImageFlags::NONE : DrawImageFlags::Disable;
+
+ //Native drawing
+ bool bNativeOK = false;
+ ControlType eCtrlType = (pData->IsRadio())? ControlType::Radiobutton : ControlType::Checkbox;
+ if ( nIndex != SvBmp::STATICIMAGE && rRenderContext.IsNativeControlSupported( eCtrlType, ControlPart::Entire) )
+
+ {
+ Size aSize(pData->Width(), pData->Height());
+ ImplAdjustBoxSize(aSize, eCtrlType, rRenderContext);
+ ImplControlValue aControlValue;
+ tools::Rectangle aCtrlRegion( rPos, aSize );
+ ControlState nState = ControlState::NONE;
+
+ //states ControlState::DEFAULT, ControlState::PRESSED and ControlState::ROLLOVER are not implemented
+ if (IsStateHilighted())
+ nState |= ControlState::FOCUSED;
+ if (nStyle != DrawImageFlags::Disable)
+ nState |= ControlState::ENABLED;
+ if (IsStateChecked())
+ aControlValue.setTristateVal(ButtonValue::On);
+ else if (IsStateUnchecked())
+ aControlValue.setTristateVal(ButtonValue::Off);
+ else if (IsStateTristate())
+ aControlValue.setTristateVal( ButtonValue::Mixed );
+
+ if (isVis)
+ bNativeOK = rRenderContext.DrawNativeControl(eCtrlType, ControlPart::Entire,
+ aCtrlRegion, nState, aControlValue, OUString());
+ }
+
+ if (!bNativeOK && isVis)
+ rRenderContext.DrawImage(rPos, pData->GetImage(nIndex), nStyle);
+}
+
+std::unique_ptr<SvLBoxItem> SvLBoxButton::Clone(SvLBoxItem const * pSource) const
+{
+ std::unique_ptr<SvLBoxButton> pNew(new SvLBoxButton);
+ pNew->pData = static_cast<SvLBoxButton const *>(pSource)->pData;
+ return std::unique_ptr<SvLBoxItem>(pNew.release());
+}
+
+void SvLBoxButton::ImplAdjustBoxSize(Size& io_rSize, ControlType i_eType, vcl::RenderContext const & rRenderContext)
+{
+ if (!rRenderContext.IsNativeControlSupported( i_eType, ControlPart::Entire) )
+ return;
+
+ ImplControlValue aControlValue;
+ tools::Rectangle aCtrlRegion( Point( 0, 0 ), io_rSize );
+
+ aControlValue.setTristateVal( ButtonValue::On );
+
+ tools::Rectangle aNativeBounds, aNativeContent;
+ bool bNativeOK = rRenderContext.GetNativeControlRegion( i_eType,
+ ControlPart::Entire,
+ aCtrlRegion,
+ ControlState::ENABLED,
+ aControlValue,
+ aNativeBounds,
+ aNativeContent );
+ if( bNativeOK )
+ {
+ Size aContentSize( aNativeContent.GetSize() );
+ // leave a little space around the box image (looks better)
+ if( aContentSize.Height() + 2 > io_rSize.Height() )
+ io_rSize.setHeight( aContentSize.Height() + 2 );
+ if( aContentSize.Width() + 2 > io_rSize.Width() )
+ io_rSize.setWidth( aContentSize.Width() + 2 );
+ }
+}
+
+void SvLBoxButton::InitViewData(SvTreeListBox* pView,SvTreeListEntry* pEntry, SvViewDataItem* pViewData)
+{
+ if( !pViewData )
+ pViewData = pView->GetViewDataItem( pEntry, this );
+ Size aSize( pData->Width(), pData->Height() );
+
+ ControlType eCtrlType = (pData->IsRadio())? ControlType::Radiobutton : ControlType::Checkbox;
+ if ( eKind != SvLBoxButtonKind::StaticImage && pView )
+ ImplAdjustBoxSize(aSize, eCtrlType, *pView);
+ pViewData->maSize = aSize;
+}
+
+bool SvLBoxButton::CheckModification() const
+{
+ return eKind == SvLBoxButtonKind::EnabledCheckbox;
+}
+
+void SvLBoxButton::SetStateInvisible()
+{
+ isVis = false;
+}
+
+// ***************************************************************
+// class SvLBoxContextBmp
+// ***************************************************************
+
+struct SvLBoxContextBmp_Impl
+{
+ Image m_aImage1;
+ Image m_aImage2;
+
+ bool m_bExpanded;
+};
+
+// ***************************************************************
+
+SvLBoxContextBmp::SvLBoxContextBmp(const Image& aBmp1, const Image& aBmp2,
+ bool bExpanded)
+ :m_pImpl( new SvLBoxContextBmp_Impl )
+{
+
+ m_pImpl->m_bExpanded = bExpanded;
+ SetModeImages( aBmp1, aBmp2 );
+}
+
+SvLBoxContextBmp::SvLBoxContextBmp()
+ :SvLBoxItem( )
+ ,m_pImpl( new SvLBoxContextBmp_Impl )
+{
+ m_pImpl->m_bExpanded = false;
+}
+
+SvLBoxContextBmp::~SvLBoxContextBmp()
+{
+}
+
+SvLBoxItemType SvLBoxContextBmp::GetType() const
+{
+ return SvLBoxItemType::ContextBmp;
+}
+
+void SvLBoxContextBmp::SetModeImages( const Image& _rBitmap1, const Image& _rBitmap2 )
+{
+ m_pImpl->m_aImage1 = _rBitmap1;
+ m_pImpl->m_aImage2 = _rBitmap2;
+}
+
+Image& SvLBoxContextBmp::implGetImageStore( bool _bFirst )
+{
+
+ // OJ: #i27071# wrong mode so we just return the normal images
+ return _bFirst ? m_pImpl->m_aImage1 : m_pImpl->m_aImage2;
+}
+
+void SvLBoxContextBmp::InitViewData( SvTreeListBox* pView,SvTreeListEntry* pEntry,
+ SvViewDataItem* pViewData)
+{
+ if( !pViewData )
+ pViewData = pView->GetViewDataItem( pEntry, this );
+ pViewData->maSize = m_pImpl->m_aImage1.GetSizePixel();
+}
+
+void SvLBoxContextBmp::Paint(
+ const Point& _rPos, SvTreeListBox& _rDev, vcl::RenderContext& rRenderContext,
+ const SvViewDataEntry* pView, const SvTreeListEntry& rEntry)
+{
+
+ // get the image.
+ const Image& rImage = implGetImageStore(pView->IsExpanded() != m_pImpl->m_bExpanded);
+
+ bool _bSemiTransparent = bool( SvTLEntryFlags::SEMITRANSPARENT & rEntry.GetFlags( ) );
+ // draw
+ DrawImageFlags nStyle = _rDev.IsEnabled() ? DrawImageFlags::NONE : DrawImageFlags::Disable;
+ if (_bSemiTransparent)
+ nStyle |= DrawImageFlags::SemiTransparent;
+ rRenderContext.DrawImage(_rPos, rImage, nStyle);
+}
+
+std::unique_ptr<SvLBoxItem> SvLBoxContextBmp::Clone(SvLBoxItem const * pSource) const
+{
+ std::unique_ptr<SvLBoxContextBmp> pNew(new SvLBoxContextBmp);
+ pNew->m_pImpl->m_aImage1 = static_cast< SvLBoxContextBmp const * >( pSource )->m_pImpl->m_aImage1;
+ pNew->m_pImpl->m_aImage2 = static_cast< SvLBoxContextBmp const * >( pSource )->m_pImpl->m_aImage2;
+ pNew->m_pImpl->m_bExpanded = static_cast<SvLBoxContextBmp const *>(pSource)->m_pImpl->m_bExpanded;
+ return std::unique_ptr<SvLBoxItem>(pNew.release());
+}
+
+long SvLBoxButtonData::Width()
+{
+ if ( !bDataOk )
+ SetWidthAndHeight();
+ return nWidth;
+}
+
+long SvLBoxButtonData::Height()
+{
+ if ( !bDataOk )
+ SetWidthAndHeight();
+ return nHeight;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/transfer.cxx b/vcl/source/treelist/transfer.cxx
new file mode 100644
index 000000000000..cc1a79c6068c
--- /dev/null
+++ b/vcl/source/treelist/transfer.cxx
@@ -0,0 +1,2177 @@
+/* -*- 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 .
+ */
+
+#ifdef _WIN32
+#include <prewin.h>
+#include <postwin.h>
+#include <shlobj.h>
+#endif
+#include <osl/mutex.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <sot/exchange.hxx>
+#include <sot/storage.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <sot/filelist.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <comphelper/seqstream.hxx>
+#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
+#include <com/sun/star/datatransfer/XMimeContentType.hpp>
+#include <com/sun/star/datatransfer/XTransferable2.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+
+#include <svl/urlbmk.hxx>
+#include <vcl/inetimg.hxx>
+#include <vcl/wmf.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/transfer.hxx>
+#include <rtl/strbuf.hxx>
+#include <cstdio>
+#include <vcl/dibtools.hxx>
+#include <vcl/pngread.hxx>
+#include <vcl/pngwrite.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <memory>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::datatransfer::clipboard;
+using namespace ::com::sun::star::datatransfer::dnd;
+
+
+#define TOD_SIG1 0x01234567
+#define TOD_SIG2 0x89abcdef
+
+SvStream& WriteTransferableObjectDescriptor( SvStream& rOStm, const TransferableObjectDescriptor& rObjDesc )
+{
+ const sal_uInt32 nFirstPos = rOStm.Tell(), nViewAspect = rObjDesc.mnViewAspect;
+ const sal_uInt32 nSig1 = TOD_SIG1, nSig2 = TOD_SIG2;
+
+ rOStm.SeekRel( 4 );
+ WriteSvGlobalName( rOStm, rObjDesc.maClassName );
+ rOStm.WriteUInt32( nViewAspect );
+ rOStm.WriteInt32( rObjDesc.maSize.Width() );
+ rOStm.WriteInt32( rObjDesc.maSize.Height() );
+ rOStm.WriteInt32( rObjDesc.maDragStartPos.X() );
+ rOStm.WriteInt32( rObjDesc.maDragStartPos.Y() );
+ rOStm.WriteUniOrByteString( rObjDesc.maTypeName, osl_getThreadTextEncoding() );
+ rOStm.WriteUniOrByteString( rObjDesc.maDisplayName, osl_getThreadTextEncoding() );
+ rOStm.WriteUInt32( nSig1 ).WriteUInt32( nSig2 );
+
+ const sal_uInt32 nLastPos = rOStm.Tell();
+
+ rOStm.Seek( nFirstPos );
+ rOStm.WriteUInt32( nLastPos - nFirstPos );
+ rOStm.Seek( nLastPos );
+
+ return rOStm;
+}
+
+
+// the reading of the parameter is done using the special service css::datatransfer::MimeContentType,
+// a similar approach should be implemented for creation of the mimetype string;
+// for now the set of acceptable characters has to be hardcoded, in future it should be part of the service that creates the mimetype
+
+static OUString ImplGetParameterString( const TransferableObjectDescriptor& rObjDesc )
+{
+ const OUString aClassName( rObjDesc.maClassName.GetHexName() );
+ OUString aParams;
+
+ if( !aClassName.isEmpty() )
+ {
+ aParams += ";classname=\"" + aClassName + "\"";
+ }
+
+ if( !rObjDesc.maTypeName.isEmpty() )
+ {
+ aParams += ";typename=\"" + rObjDesc.maTypeName + "\"";
+ }
+
+ if( !rObjDesc.maDisplayName.isEmpty() )
+ {
+ // the display name might contain unacceptable characters, encode all of them
+ // this seems to be the only parameter currently that might contain such characters
+ sal_Bool pToAccept[128];
+ for (sal_Bool & rb : pToAccept)
+ rb = false;
+
+ const char aQuotedParamChars[] =
+ "()<>@,;:/[]?=!#$&'*+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~. ";
+
+ for ( sal_Int32 nInd = 0; nInd < RTL_CONSTASCII_LENGTH(aQuotedParamChars); ++nInd )
+ {
+ sal_Unicode nChar = aQuotedParamChars[nInd];
+ if ( nChar < 128 )
+ pToAccept[nChar] = true;
+ }
+
+ aParams += ";displayname=\""
+ + rtl::Uri::encode(
+ rObjDesc.maDisplayName, pToAccept, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8)
+ + "\"";
+ }
+
+ aParams += ";viewaspect=\"" + OUString::number(rObjDesc.mnViewAspect)
+ + "\";width=\"" + OUString::number(rObjDesc.maSize.Width())
+ + "\";height=\"" + OUString::number(rObjDesc.maSize.Height())
+ + "\";posx=\"" + OUString::number(rObjDesc.maDragStartPos.X())
+ + "\";posy=\"" + OUString::number(rObjDesc.maDragStartPos.X()) + "\"";
+
+ return aParams;
+}
+
+
+static void ImplSetParameterString( TransferableObjectDescriptor& rObjDesc, const DataFlavorEx& rFlavorEx )
+{
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+
+ try
+ {
+ Reference< XMimeContentTypeFactory > xMimeFact = MimeContentTypeFactory::create( xContext );
+
+ Reference< XMimeContentType > xMimeType( xMimeFact->createMimeContentType( rFlavorEx.MimeType ) );
+
+ if( xMimeType.is() )
+ {
+ const OUString aClassNameString( "classname" );
+ const OUString aTypeNameString( "typename" );
+ const OUString aDisplayNameString( "displayname" );
+ const OUString aViewAspectString( "viewaspect" );
+ const OUString aWidthString( "width" );
+ const OUString aHeightString( "height" );
+ const OUString aPosXString( "posx" );
+ const OUString aPosYString( "posy" );
+
+ if( xMimeType->hasParameter( aClassNameString ) )
+ {
+ rObjDesc.maClassName.MakeId( xMimeType->getParameterValue( aClassNameString ) );
+ }
+
+ if( xMimeType->hasParameter( aTypeNameString ) )
+ {
+ rObjDesc.maTypeName = xMimeType->getParameterValue( aTypeNameString );
+ }
+
+ if( xMimeType->hasParameter( aDisplayNameString ) )
+ {
+ // the display name might contain unacceptable characters, in this case they should be encoded
+ // this seems to be the only parameter currently that might contain such characters
+ rObjDesc.maDisplayName = ::rtl::Uri::decode( xMimeType->getParameterValue( aDisplayNameString ), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ }
+
+ if( xMimeType->hasParameter( aViewAspectString ) )
+ {
+ rObjDesc.mnViewAspect = static_cast< sal_uInt16 >( xMimeType->getParameterValue( aViewAspectString ).toInt32() );
+ }
+
+ if( xMimeType->hasParameter( aWidthString ) )
+ {
+ rObjDesc.maSize.setWidth( xMimeType->getParameterValue( aWidthString ).toInt32() );
+ }
+
+ if( xMimeType->hasParameter( aHeightString ) )
+ {
+ rObjDesc.maSize.setHeight( xMimeType->getParameterValue( aHeightString ).toInt32() );
+ }
+
+ if( xMimeType->hasParameter( aPosXString ) )
+ {
+ rObjDesc.maDragStartPos.setX( xMimeType->getParameterValue( aPosXString ).toInt32() );
+ }
+
+ if( xMimeType->hasParameter( aPosYString ) )
+ {
+ rObjDesc.maDragStartPos.setY( xMimeType->getParameterValue( aPosYString ).toInt32() );
+ }
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+TransferableHelper::TerminateListener::TerminateListener( TransferableHelper& rTransferableHelper ) :
+ mrParent( rTransferableHelper )
+{
+}
+
+
+TransferableHelper::TerminateListener::~TerminateListener()
+{
+}
+
+
+void SAL_CALL TransferableHelper::TerminateListener::disposing( const EventObject& )
+{
+}
+
+
+void SAL_CALL TransferableHelper::TerminateListener::queryTermination( const EventObject& )
+{
+}
+
+
+void SAL_CALL TransferableHelper::TerminateListener::notifyTermination( const EventObject& )
+{
+ mrParent.ImplFlush();
+}
+
+OUString SAL_CALL TransferableHelper::TerminateListener::getImplementationName()
+{
+ return OUString("com.sun.star.comp.svt.TransferableHelperTerminateListener");
+}
+
+sal_Bool SAL_CALL TransferableHelper::TerminateListener::supportsService(const OUString& /*rServiceName*/)
+{
+ return false;
+}
+
+css::uno::Sequence<OUString> TransferableHelper::TerminateListener::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>();
+}
+
+
+Any SAL_CALL TransferableHelper::getTransferData( const DataFlavor& rFlavor )
+{
+ return getTransferData2(rFlavor, OUString());
+}
+
+Any SAL_CALL TransferableHelper::getTransferData2( const DataFlavor& rFlavor, const OUString& rDestDoc )
+{
+ if( !maAny.hasValue() || maFormats.empty() || ( maLastFormat != rFlavor.MimeType ) )
+ {
+ const SolarMutexGuard aGuard;
+
+ maLastFormat = rFlavor.MimeType;
+ maAny = Any();
+
+ try
+ {
+ DataFlavor aSubstFlavor;
+ bool bDone = false;
+
+ // add formats if not already done
+ if (maFormats.empty())
+ AddSupportedFormats();
+
+ // check alien formats first and try to get a substitution format
+ if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aSubstFlavor ) &&
+ TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) )
+ {
+ GetData(aSubstFlavor, rDestDoc);
+ bDone = maAny.hasValue();
+ }
+ else if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BMP, aSubstFlavor )
+ && TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor )
+ && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BITMAP, aSubstFlavor))
+ {
+ GetData(aSubstFlavor, rDestDoc);
+ bDone = true;
+ }
+ else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EMF, aSubstFlavor ) &&
+ TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
+ {
+ GetData(aSubstFlavor, rDestDoc);
+
+ if( maAny.hasValue() )
+ {
+ Sequence< sal_Int8 > aSeq;
+
+ if( maAny >>= aSeq )
+ {
+ std::unique_ptr<SvMemoryStream> pSrcStm(new SvMemoryStream( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC ));
+ GDIMetaFile aMtf;
+
+ ReadGDIMetaFile( *pSrcStm, aMtf );
+ pSrcStm.reset();
+
+ Graphic aGraphic( aMtf );
+ SvMemoryStream aDstStm( 65535, 65535 );
+
+ if( GraphicConverter::Export( aDstStm, aGraphic, ConvertDataFormat::EMF ) == ERRCODE_NONE )
+ {
+ maAny <<= ( aSeq = Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aDstStm.GetData() ),
+ aDstStm.TellEnd() ) );
+ bDone = true;
+ }
+ }
+ }
+ }
+ else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::WMF, aSubstFlavor ) &&
+ TransferableDataHelper::IsEqual( aSubstFlavor, rFlavor ) &&
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aSubstFlavor ) )
+ {
+ GetData(aSubstFlavor, rDestDoc);
+
+ if( maAny.hasValue() )
+ {
+ Sequence< sal_Int8 > aSeq;
+
+ if( maAny >>= aSeq )
+ {
+ std::unique_ptr<SvMemoryStream> pSrcStm(new SvMemoryStream( aSeq.getArray(), aSeq.getLength(), StreamMode::WRITE | StreamMode::TRUNC ));
+ GDIMetaFile aMtf;
+
+ ReadGDIMetaFile( *pSrcStm, aMtf );
+ pSrcStm.reset();
+
+ SvMemoryStream aDstStm( 65535, 65535 );
+
+ // taking wmf without file header
+ if ( ConvertGDIMetaFileToWMF( aMtf, aDstStm, nullptr, false ) )
+ {
+ maAny <<= ( aSeq = Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aDstStm.GetData() ),
+ aDstStm.TellEnd() ) );
+ bDone = true;
+ }
+ }
+ }
+ }
+
+ // reset Any if substitute doesn't work
+ if( !bDone && maAny.hasValue() )
+ maAny = Any();
+
+ // if any is not yet filled, use standard format
+ if( !maAny.hasValue() )
+ GetData(rFlavor, rDestDoc);
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ if( !maAny.hasValue() )
+ throw UnsupportedFlavorException();
+ }
+
+ return maAny;
+}
+
+
+Sequence< DataFlavor > SAL_CALL TransferableHelper::getTransferDataFlavors()
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ if(maFormats.empty())
+ AddSupportedFormats();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ return comphelper::containerToSequence<DataFlavor>(maFormats);
+}
+
+
+sal_Bool SAL_CALL TransferableHelper::isDataFlavorSupported( const DataFlavor& rFlavor )
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ if (maFormats.empty())
+ AddSupportedFormats();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ for (auto const& format : maFormats)
+ {
+ if( TransferableDataHelper::IsEqual( format, rFlavor ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void SAL_CALL TransferableHelper::lostOwnership( const Reference< XClipboard >&, const Reference< XTransferable >& )
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ if( mxTerminateListener.is() )
+ {
+ Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ xDesktop->removeTerminateListener( mxTerminateListener );
+
+ mxTerminateListener.clear();
+ }
+
+ ObjectReleased();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+void SAL_CALL TransferableHelper::disposing( const EventObject& )
+{
+}
+
+
+void SAL_CALL TransferableHelper::dragDropEnd( const DragSourceDropEvent& rDSDE )
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ DragFinished( rDSDE.DropSuccess ? ( rDSDE.DropAction & ~DNDConstants::ACTION_DEFAULT ) : DNDConstants::ACTION_NONE );
+ ObjectReleased();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+void SAL_CALL TransferableHelper::dragEnter( const DragSourceDragEvent& )
+{
+}
+
+
+void SAL_CALL TransferableHelper::dragExit( const DragSourceEvent& )
+{
+}
+
+
+void SAL_CALL TransferableHelper::dragOver( const DragSourceDragEvent& )
+{
+}
+
+
+void SAL_CALL TransferableHelper::dropActionChanged( const DragSourceDragEvent& )
+{
+}
+
+
+sal_Int64 SAL_CALL TransferableHelper::getSomething( const Sequence< sal_Int8 >& rId )
+{
+ sal_Int64 nRet;
+
+ if( ( rId.getLength() == 16 ) &&
+ ( 0 == memcmp( getUnoTunnelId().getConstArray(), rId.getConstArray(), 16 ) ) )
+ {
+ nRet = sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this));
+ }
+ else
+ nRet = 0;
+
+ return nRet;
+}
+
+
+void TransferableHelper::ImplFlush()
+{
+ if( !mxClipboard.is() )
+ return;
+
+ Reference< XFlushableClipboard > xFlushableClipboard( mxClipboard, UNO_QUERY );
+ SolarMutexReleaser aReleaser;
+
+ try
+ {
+ if( xFlushableClipboard.is() )
+ xFlushableClipboard->flushClipboard();
+ }
+ catch( const css::uno::Exception& )
+ {
+ OSL_FAIL( "Could not flush clipboard" );
+ }
+}
+
+
+void TransferableHelper::AddFormat( SotClipboardFormatId nFormat )
+{
+ DataFlavor aFlavor;
+
+ if( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
+ AddFormat( aFlavor );
+}
+
+
+void TransferableHelper::AddFormat( const DataFlavor& rFlavor )
+{
+ bool bAdd = true;
+
+ for (auto & format : maFormats)
+ {
+ if( TransferableDataHelper::IsEqual( format, rFlavor ) )
+ {
+ // update MimeType for SotClipboardFormatId::OBJECTDESCRIPTOR in every case
+ if ((SotClipboardFormatId::OBJECTDESCRIPTOR == format.mnSotId) && mxObjDesc)
+ {
+ DataFlavor aObjDescFlavor;
+
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDescFlavor );
+ format.MimeType = aObjDescFlavor.MimeType;
+ format.MimeType += ::ImplGetParameterString(*mxObjDesc);
+ }
+
+ bAdd = false;
+ break;
+ }
+ }
+
+ if( !bAdd )
+ return;
+
+ DataFlavorEx aFlavorEx;
+
+ aFlavorEx.MimeType = rFlavor.MimeType;
+ aFlavorEx.HumanPresentableName = rFlavor.HumanPresentableName;
+ aFlavorEx.DataType = rFlavor.DataType;
+ aFlavorEx.mnSotId = SotExchange::RegisterFormat( rFlavor );
+
+ if ((SotClipboardFormatId::OBJECTDESCRIPTOR == aFlavorEx.mnSotId) && mxObjDesc)
+ aFlavorEx.MimeType += ::ImplGetParameterString(*mxObjDesc);
+
+ maFormats.push_back(aFlavorEx);
+
+ if( SotClipboardFormatId::BITMAP == aFlavorEx.mnSotId )
+ {
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BMP );
+ }
+ else if( SotClipboardFormatId::GDIMETAFILE == aFlavorEx.mnSotId )
+ {
+ AddFormat( SotClipboardFormatId::EMF );
+ AddFormat( SotClipboardFormatId::WMF );
+ }
+}
+
+
+void TransferableHelper::RemoveFormat( SotClipboardFormatId nFormat )
+{
+ DataFlavor aFlavor;
+
+ if( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
+ RemoveFormat( aFlavor );
+}
+
+
+void TransferableHelper::RemoveFormat( const DataFlavor& rFlavor )
+{
+ DataFlavorExVector::iterator aIter(maFormats.begin());
+
+ while (aIter != maFormats.end())
+ {
+ if( TransferableDataHelper::IsEqual( *aIter, rFlavor ) )
+ aIter = maFormats.erase(aIter);
+ else
+ ++aIter;
+ }
+}
+
+
+bool TransferableHelper::HasFormat( SotClipboardFormatId nFormat )
+{
+ return std::any_of(maFormats.begin(), maFormats.end(),
+ [&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
+}
+
+
+void TransferableHelper::ClearFormats()
+{
+ maFormats.clear();
+ maAny.clear();
+}
+
+
+bool TransferableHelper::SetAny( const Any& rAny )
+{
+ maAny = rAny;
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetString( const OUString& rString, const DataFlavor& rFlavor )
+{
+ DataFlavor aFileFlavor;
+
+ if( !rString.isEmpty() &&
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::SIMPLE_FILE, aFileFlavor ) &&
+ TransferableDataHelper::IsEqual( aFileFlavor, rFlavor ) )
+ {
+ const OString aByteStr(OUStringToOString(rString, osl_getThreadTextEncoding()));
+ Sequence< sal_Int8 > aSeq( aByteStr.getLength() + 1 );
+
+ memcpy( aSeq.getArray(), aByteStr.getStr(), aByteStr.getLength() );
+ aSeq[ aByteStr.getLength() ] = 0;
+ maAny <<= aSeq;
+ }
+ else
+ maAny <<= rString;
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetBitmapEx( const BitmapEx& rBitmapEx, const DataFlavor& rFlavor )
+{
+ if( !rBitmapEx.IsEmpty() )
+ {
+ SvMemoryStream aMemStm( 65535, 65535 );
+
+ if(rFlavor.MimeType.equalsIgnoreAsciiCase("image/png"))
+ {
+ // write a PNG
+ vcl::PNGWriter aPNGWriter(rBitmapEx);
+
+ aPNGWriter.Write(aMemStm);
+ }
+ else
+ {
+ const Bitmap aBitmap(rBitmapEx.GetBitmap());
+
+ // explicitly use Bitmap::Write with bCompressed = sal_False and bFileHeader = sal_True
+ WriteDIB(aBitmap, aMemStm, false, true);
+ }
+
+ maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
+ }
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetGDIMetaFile( const GDIMetaFile& rMtf )
+{
+ if( rMtf.GetActionSize() )
+ {
+ SvMemoryStream aMemStm( 65535, 65535 );
+
+ const_cast<GDIMetaFile&>(rMtf).Write( aMemStm );
+ maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() );
+ }
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetGraphic( const Graphic& rGraphic )
+{
+ if( rGraphic.GetType() != GraphicType::NONE )
+ {
+ SvMemoryStream aMemStm( 65535, 65535 );
+
+ aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
+ aMemStm.SetCompressMode( SvStreamCompressFlags::NATIVE );
+ WriteGraphic( aMemStm, rGraphic );
+ maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Seek( STREAM_SEEK_TO_END ) );
+ }
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetImageMap( const ImageMap& rIMap )
+{
+ SvMemoryStream aMemStm( 8192, 8192 );
+
+ aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
+ rIMap.Write( aMemStm );
+ maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Seek( STREAM_SEEK_TO_END ) );
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetTransferableObjectDescriptor( const TransferableObjectDescriptor& rDesc )
+{
+ PrepareOLE( rDesc );
+
+ SvMemoryStream aMemStm( 1024, 1024 );
+
+ WriteTransferableObjectDescriptor( aMemStm, rDesc );
+ maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Tell() );
+
+ return maAny.hasValue();
+ }
+
+
+bool TransferableHelper::SetINetBookmark( const INetBookmark& rBmk,
+ const css::datatransfer::DataFlavor& rFlavor )
+{
+ rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding();
+
+ switch( SotExchange::GetFormat( rFlavor ) )
+ {
+ case SotClipboardFormatId::SOLK:
+ {
+ OString sURL(OUStringToOString(rBmk.GetURL(), eSysCSet));
+ OString sDesc(OUStringToOString(rBmk.GetDescription(), eSysCSet));
+ OStringBuffer sOut;
+ sOut.append(sURL.getLength());
+ sOut.append('@').append(sURL);
+ sOut.append(sDesc.getLength());
+ sOut.append('@').append(sDesc);
+
+ Sequence< sal_Int8 > aSeq(sOut.getLength());
+ memcpy(aSeq.getArray(), sOut.getStr(), sOut.getLength());
+ maAny <<= aSeq;
+ }
+ break;
+
+ case SotClipboardFormatId::STRING:
+ maAny <<= rBmk.GetURL();
+ break;
+
+ case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
+ {
+ OString sURL(OUStringToOString(rBmk.GetURL(), eSysCSet));
+ Sequence< sal_Int8 > aSeq( sURL.getLength() );
+ memcpy( aSeq.getArray(), sURL.getStr(), sURL.getLength() );
+ maAny <<= aSeq;
+ }
+ break;
+
+ case SotClipboardFormatId::NETSCAPE_BOOKMARK:
+ {
+ Sequence< sal_Int8 > aSeq( 2048 );
+
+ memset( aSeq.getArray(), 0, 2048 );
+ strcpy( reinterpret_cast< char* >( aSeq.getArray() ), OUStringToOString(rBmk.GetURL(), eSysCSet).getStr() );
+ strcpy( reinterpret_cast< char* >( aSeq.getArray() ) + 1024, OUStringToOString(rBmk.GetDescription(), eSysCSet).getStr() );
+
+ maAny <<= aSeq;
+ }
+ break;
+
+#ifdef _WIN32
+ case SotClipboardFormatId::FILEGRPDESCRIPTOR:
+ {
+ Sequence< sal_Int8 > aSeq( sizeof( FILEGROUPDESCRIPTOR ) );
+ FILEGROUPDESCRIPTOR* pFDesc = reinterpret_cast<FILEGROUPDESCRIPTOR*>(aSeq.getArray());
+ FILEDESCRIPTOR& rFDesc1 = pFDesc->fgd[ 0 ];
+
+ pFDesc->cItems = 1;
+ memset( &rFDesc1, 0, sizeof( FILEDESCRIPTOR ) );
+ rFDesc1.dwFlags = FD_LINKUI;
+
+ OStringBuffer aStr(OUStringToOString(
+ rBmk.GetDescription(), eSysCSet));
+ for( sal_Int32 nChar = 0; nChar < aStr.getLength(); ++nChar )
+ if( strchr( "\\/:*?\"<>|", aStr[nChar] ) )
+ aStr.remove(nChar--, 1);
+
+ aStr.insert(0, "Shortcut to ");
+ aStr.append(".URL");
+ strcpy( rFDesc1.cFileName, aStr.getStr() );
+
+ maAny <<= aSeq;
+ }
+ break;
+
+ case SotClipboardFormatId::FILECONTENT:
+ {
+ OUString aStr( "[InternetShortcut]\x0aURL=" );
+ maAny <<= ( aStr += rBmk.GetURL() );
+ }
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetINetImage( const INetImage& rINtImg,
+ const css::datatransfer::DataFlavor& rFlavor )
+{
+ SvMemoryStream aMemStm( 1024, 1024 );
+
+ aMemStm.SetVersion( SOFFICE_FILEFORMAT_50 );
+ rINtImg.Write( aMemStm, SotExchange::GetFormat( rFlavor ) );
+
+ maAny <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.Seek( STREAM_SEEK_TO_END ) );
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::SetObject( void* pUserObject, sal_uInt32 nUserObjectId, const DataFlavor& rFlavor )
+{
+ tools::SvRef<SotStorageStream> xStm( new SotStorageStream( OUString() ) );
+
+ xStm->SetVersion( SOFFICE_FILEFORMAT_50 );
+
+ if( pUserObject && WriteObject( xStm, pUserObject, nUserObjectId, rFlavor ) )
+ {
+ const sal_uInt32 nLen = xStm->TellEnd();
+ Sequence< sal_Int8 > aSeq( nLen );
+
+ xStm->Seek( STREAM_SEEK_TO_BEGIN );
+ xStm->ReadBytes(aSeq.getArray(), nLen);
+
+ if( nLen && ( SotExchange::GetFormat( rFlavor ) == SotClipboardFormatId::STRING ) )
+ {
+ //JP 24.7.2001: as I know was this only for the writer application and this
+ // writes now UTF16 format into the stream
+ //JP 6.8.2001: and now it writes UTF8 because then exist no problem with
+ // little / big endians! - Bug 88121
+ maAny <<= OUString( reinterpret_cast< const sal_Char* >( aSeq.getConstArray() ), nLen - 1, RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ maAny <<= aSeq;
+ }
+
+ return maAny.hasValue();
+}
+
+
+bool TransferableHelper::WriteObject( tools::SvRef<SotStorageStream>&, void*, sal_uInt32, const DataFlavor& )
+{
+ OSL_FAIL( "TransferableHelper::WriteObject( ... ) not implemented" );
+ return false;
+}
+
+
+void TransferableHelper::DragFinished( sal_Int8 )
+{
+}
+
+
+void TransferableHelper::ObjectReleased()
+{
+}
+
+
+void TransferableHelper::PrepareOLE( const TransferableObjectDescriptor& rObjDesc )
+{
+ mxObjDesc.reset(new TransferableObjectDescriptor(rObjDesc));
+
+ if( HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) )
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+}
+
+
+void TransferableHelper::CopyToClipboard( vcl::Window *pWindow ) const
+{
+ DBG_ASSERT( pWindow, "Window pointer is NULL" );
+ Reference< XClipboard > xClipboard;
+
+ if( pWindow )
+ xClipboard = pWindow->GetClipboard();
+
+ if( xClipboard.is() )
+ mxClipboard = xClipboard;
+
+ if( !(mxClipboard.is() && !mxTerminateListener.is()) )
+ return;
+
+ try
+ {
+ TransferableHelper* pThis = const_cast< TransferableHelper* >( this );
+ Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ xDesktop->addTerminateListener( pThis->mxTerminateListener = new TerminateListener( *pThis ) );
+
+ mxClipboard->setContents( pThis, pThis );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+void TransferableHelper::CopyToSelection( vcl::Window *pWindow ) const
+{
+ DBG_ASSERT( pWindow, "Window pointer is NULL" );
+ Reference< XClipboard > xSelection;
+
+ if( pWindow )
+ xSelection = pWindow->GetPrimarySelection();
+
+ if( !(xSelection.is() && !mxTerminateListener.is()) )
+ return;
+
+ try
+ {
+ TransferableHelper* pThis = const_cast< TransferableHelper* >( this );
+ Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ xDesktop->addTerminateListener( pThis->mxTerminateListener = new TerminateListener( *pThis ) );
+
+ xSelection->setContents( pThis, pThis );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+void TransferableHelper::StartDrag( vcl::Window* pWindow, sal_Int8 nDnDSourceActions,
+ sal_Int32 nDnDPointer )
+
+{
+ DBG_ASSERT( pWindow, "Window pointer is NULL" );
+ Reference< XDragSource > xDragSource( pWindow->GetDragSource() );
+
+ if( !xDragSource.is() )
+ return;
+
+ /*
+ * #96792# release mouse before actually starting DnD.
+ * This is necessary for the X11 DnD implementation to work.
+ */
+ if( pWindow->IsMouseCaptured() )
+ pWindow->ReleaseMouse();
+
+ const Point aPt( pWindow->GetPointerPosPixel() );
+
+ // On Mac OS X we are forced to execute 'startDrag' synchronously
+ // contrary to the XDragSource interface specification because
+ // we can receive drag events from the system only in the main
+ // thread
+#if !defined(MACOSX)
+ SolarMutexReleaser aReleaser;
+#endif
+
+ try
+ {
+ DragGestureEvent aEvt;
+ aEvt.DragAction = DNDConstants::ACTION_COPY;
+ aEvt.DragOriginX = aPt.X();
+ aEvt.DragOriginY = aPt.Y();
+ aEvt.DragSource = xDragSource;
+
+ xDragSource->startDrag( aEvt, nDnDSourceActions, nDnDPointer, DND_IMAGE_NONE, this, this );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+void TransferableHelper::ClearSelection( vcl::Window *pWindow )
+{
+ DBG_ASSERT( pWindow, "Window pointer is NULL" );
+ Reference< XClipboard > xSelection( pWindow->GetPrimarySelection() );
+
+ if( xSelection.is() )
+ xSelection->setContents( nullptr, nullptr );
+}
+
+namespace
+{
+ class theTransferableHelperUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theTransferableHelperUnoTunnelId > {};
+}
+
+const Sequence< sal_Int8 >& TransferableHelper::getUnoTunnelId()
+{
+ return theTransferableHelperUnoTunnelId::get().getSeq();
+}
+
+class TransferableClipboardNotifier : public ::cppu::WeakImplHelper< XClipboardListener >
+{
+private:
+ ::osl::Mutex& mrMutex;
+ Reference< XClipboardNotifier > mxNotifier;
+ TransferableDataHelper* mpListener;
+
+protected:
+ // XClipboardListener
+ virtual void SAL_CALL changedContents( const clipboard::ClipboardEvent& event ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+
+public:
+ TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener, ::osl::Mutex& _rMutex );
+
+ /// determines whether we're currently listening
+ bool isListening() const { return mpListener != nullptr; }
+
+ /// makes the instance non-functional
+ void dispose();
+};
+
+
+TransferableClipboardNotifier::TransferableClipboardNotifier( const Reference< XClipboard >& _rxClipboard, TransferableDataHelper& _rListener, ::osl::Mutex& _rMutex )
+ :mrMutex( _rMutex )
+ ,mxNotifier( _rxClipboard, UNO_QUERY )
+ ,mpListener( &_rListener )
+{
+ osl_atomic_increment( &m_refCount );
+ {
+ if ( mxNotifier.is() )
+ mxNotifier->addClipboardListener( this );
+ else
+ // born dead
+ mpListener = nullptr;
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+void SAL_CALL TransferableClipboardNotifier::changedContents( const clipboard::ClipboardEvent& event )
+{
+ SolarMutexGuard aSolarGuard;
+ // the SolarMutex here is necessary, since
+ // - we cannot call mpListener without our own mutex locked
+ // - Rebind respectively InitFormats (called by Rebind) will
+ // try to lock the SolarMutex, too
+ ::osl::MutexGuard aGuard( mrMutex );
+ if( mpListener )
+ mpListener->Rebind( event.Contents );
+}
+
+
+void SAL_CALL TransferableClipboardNotifier::disposing( const EventObject& )
+{
+ // clipboard is being disposed. Hmm. Okay, become disfunctional myself.
+ dispose();
+}
+
+
+void TransferableClipboardNotifier::dispose()
+{
+ ::osl::MutexGuard aGuard( mrMutex );
+
+ Reference< XClipboardListener > xKeepMeAlive( this );
+
+ if ( mxNotifier.is() )
+ mxNotifier->removeClipboardListener( this );
+ mxNotifier.clear();
+
+ mpListener = nullptr;
+}
+
+struct TransferableDataHelper_Impl
+{
+ ::osl::Mutex maMutex;
+ rtl::Reference<TransferableClipboardNotifier> mxClipboardListener;
+
+ TransferableDataHelper_Impl()
+ {
+ }
+};
+
+TransferableDataHelper::TransferableDataHelper()
+ : mxObjDesc(new TransferableObjectDescriptor)
+ , mxImpl(new TransferableDataHelper_Impl)
+{
+}
+
+TransferableDataHelper::TransferableDataHelper(const Reference< css::datatransfer::XTransferable >& rxTransferable)
+ : mxTransfer(rxTransferable)
+ , mxObjDesc(new TransferableObjectDescriptor)
+ , mxImpl(new TransferableDataHelper_Impl)
+{
+ InitFormats();
+}
+
+TransferableDataHelper::TransferableDataHelper(const TransferableDataHelper& rDataHelper)
+ : mxTransfer(rDataHelper.mxTransfer)
+ , mxClipboard(rDataHelper.mxClipboard)
+ , maFormats(rDataHelper.maFormats)
+ , mxObjDesc(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc))
+ , mxImpl(new TransferableDataHelper_Impl)
+{
+}
+
+TransferableDataHelper::TransferableDataHelper(TransferableDataHelper&& rDataHelper)
+ : mxTransfer(std::move(rDataHelper.mxTransfer))
+ , mxClipboard(std::move(rDataHelper.mxClipboard))
+ , maFormats(std::move(rDataHelper.maFormats))
+ , mxObjDesc(std::move(rDataHelper.mxObjDesc))
+ , mxImpl(new TransferableDataHelper_Impl)
+{
+}
+
+TransferableDataHelper& TransferableDataHelper::operator=( const TransferableDataHelper& rDataHelper )
+{
+ if ( this != &rDataHelper )
+ {
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+
+ const bool bWasClipboardListening = mxImpl->mxClipboardListener.is();
+
+ if (bWasClipboardListening)
+ StopClipboardListening();
+
+ mxTransfer = rDataHelper.mxTransfer;
+ maFormats = rDataHelper.maFormats;
+ mxObjDesc.reset(new TransferableObjectDescriptor(*rDataHelper.mxObjDesc));
+ mxClipboard = rDataHelper.mxClipboard;
+
+ if (bWasClipboardListening)
+ StartClipboardListening();
+ }
+
+ return *this;
+}
+
+TransferableDataHelper& TransferableDataHelper::operator=(TransferableDataHelper&& rDataHelper)
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+
+ const bool bWasClipboardListening = mxImpl->mxClipboardListener.is();
+
+ if (bWasClipboardListening)
+ StopClipboardListening();
+
+ mxTransfer = std::move(rDataHelper.mxTransfer);
+ maFormats = std::move(rDataHelper.maFormats);
+ mxObjDesc = std::move(rDataHelper.mxObjDesc);
+ mxClipboard = std::move(rDataHelper.mxClipboard);
+
+ if (bWasClipboardListening)
+ StartClipboardListening();
+
+ return *this;
+}
+
+TransferableDataHelper::~TransferableDataHelper()
+{
+ StopClipboardListening( );
+ {
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+ maFormats.clear();
+ mxObjDesc.reset();
+ }
+}
+
+void TransferableDataHelper::FillDataFlavorExVector( const Sequence< DataFlavor >& rDataFlavorSeq,
+ DataFlavorExVector& rDataFlavorExVector )
+{
+ try
+ {
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ Reference< XMimeContentTypeFactory > xMimeFact = MimeContentTypeFactory::create( xContext );
+ DataFlavorEx aFlavorEx;
+ const OUString aCharsetStr( "charset" );
+
+
+ for (auto const& rFlavor : rDataFlavorSeq)
+ {
+ Reference< XMimeContentType > xMimeType;
+
+ try
+ {
+ if( !rFlavor.MimeType.isEmpty() )
+ xMimeType = xMimeFact->createMimeContentType( rFlavor.MimeType );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ aFlavorEx.MimeType = rFlavor.MimeType;
+ aFlavorEx.HumanPresentableName = rFlavor.HumanPresentableName;
+ aFlavorEx.DataType = rFlavor.DataType;
+ aFlavorEx.mnSotId = SotExchange::RegisterFormat( rFlavor );
+
+ rDataFlavorExVector.push_back( aFlavorEx );
+
+ // add additional formats for special mime types
+ if(SotClipboardFormatId::BMP == aFlavorEx.mnSotId || SotClipboardFormatId::PNG == aFlavorEx.mnSotId)
+ {
+ if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavorEx ) )
+ {
+ aFlavorEx.mnSotId = SotClipboardFormatId::BITMAP;
+ rDataFlavorExVector.push_back( aFlavorEx );
+ }
+ }
+ else if( SotClipboardFormatId::WMF == aFlavorEx.mnSotId || SotClipboardFormatId::EMF == aFlavorEx.mnSotId )
+ {
+ if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavorEx ) )
+ {
+ aFlavorEx.mnSotId = SotClipboardFormatId::GDIMETAFILE;
+ rDataFlavorExVector.push_back( aFlavorEx );
+ }
+ }
+ else if ( SotClipboardFormatId::HTML_SIMPLE == aFlavorEx.mnSotId )
+ {
+ // #104735# HTML_SIMPLE may also be inserted without comments
+ aFlavorEx.mnSotId = SotClipboardFormatId::HTML_NO_COMMENT;
+ rDataFlavorExVector.push_back( aFlavorEx );
+ }
+ else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
+ {
+ // add, if it is a UTF-8 byte buffer
+ if( xMimeType->hasParameter( aCharsetStr ) )
+ {
+ if( xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "unicode" ) ||
+ xMimeType->getParameterValue( aCharsetStr ).equalsIgnoreAsciiCase( "utf-16" ) )
+ {
+ rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::STRING;
+
+ }
+ }
+ }
+ else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/rtf" ) )
+ {
+ rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RTF;
+ }
+ else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/richtext" ) )
+ {
+ rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::RICHTEXT;
+ }
+ else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/html" ) )
+
+ {
+ rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::HTML;
+ }
+ else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "text/uri-list" ) )
+ {
+ rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::FILE_LIST;
+ }
+ else if( xMimeType.is() && xMimeType->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice-objectdescriptor-xml" ) )
+ {
+ rDataFlavorExVector[ rDataFlavorExVector.size() - 1 ].mnSotId = SotClipboardFormatId::OBJECTDESCRIPTOR;
+ }
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+void TransferableDataHelper::InitFormats()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+
+ maFormats.clear();
+ mxObjDesc.reset(new TransferableObjectDescriptor);
+
+ if( !mxTransfer.is() )
+ return;
+
+ TransferableDataHelper::FillDataFlavorExVector(mxTransfer->getTransferDataFlavors(), maFormats);
+
+ for (auto const& format : maFormats)
+ {
+ if( SotClipboardFormatId::OBJECTDESCRIPTOR == format.mnSotId )
+ {
+ ImplSetParameterString(*mxObjDesc, format);
+ break;
+ }
+ }
+}
+
+
+bool TransferableDataHelper::HasFormat( SotClipboardFormatId nFormat ) const
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+ return std::any_of(maFormats.begin(), maFormats.end(),
+ [&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
+}
+
+bool TransferableDataHelper::HasFormat( const DataFlavor& rFlavor ) const
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+ for (auto const& format : maFormats)
+ {
+ if( TransferableDataHelper::IsEqual( rFlavor, format ) )
+ return true;
+ }
+
+ return false;
+}
+
+sal_uInt32 TransferableDataHelper::GetFormatCount() const
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+ return maFormats.size();
+}
+
+SotClipboardFormatId TransferableDataHelper::GetFormat( sal_uInt32 nFormat ) const
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+ DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index");
+ return( ( nFormat < maFormats.size() ) ? (maFormats)[ nFormat ].mnSotId : SotClipboardFormatId::NONE );
+}
+
+DataFlavor TransferableDataHelper::GetFormatDataFlavor( sal_uInt32 nFormat ) const
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+ DBG_ASSERT(nFormat < maFormats.size(), "TransferableDataHelper::GetFormat: invalid format index");
+
+ DataFlavor aRet;
+
+ if (nFormat < maFormats.size())
+ aRet = maFormats[nFormat];
+
+ return aRet;
+}
+
+
+Reference< XTransferable > TransferableDataHelper::GetXTransferable() const
+{
+ Reference< XTransferable > xRet;
+
+ if( mxTransfer.is() )
+ {
+ try
+ {
+ xRet = mxTransfer;
+
+ // do a dummy call to check, if this interface is valid (nasty)
+ xRet->getTransferDataFlavors();
+
+ }
+ catch( const css::uno::Exception& )
+ {
+ xRet.clear();
+ }
+ }
+
+ return xRet;
+}
+
+
+Any TransferableDataHelper::GetAny( SotClipboardFormatId nFormat, const OUString& rDestDoc ) const
+{
+ Any aReturn;
+
+ DataFlavor aFlavor;
+ if ( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) )
+ aReturn = GetAny(aFlavor, rDestDoc);
+
+ return aReturn;
+}
+
+Any TransferableDataHelper::GetAny( const DataFlavor& rFlavor, const OUString& rDestDoc ) const
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+ Any aRet;
+
+ try
+ {
+ if( mxTransfer.is() )
+ {
+ const SotClipboardFormatId nRequestFormat = SotExchange::GetFormat( rFlavor );
+
+ Reference<css::datatransfer::XTransferable2> xTransfer2(mxTransfer, UNO_QUERY);
+
+ if( nRequestFormat != SotClipboardFormatId::NONE )
+ {
+ // try to get alien format first
+ for (auto const& format : maFormats)
+ {
+ if( ( nRequestFormat == format.mnSotId ) && !rFlavor.MimeType.equalsIgnoreAsciiCase( format.MimeType ) )
+ {
+ if (xTransfer2.is())
+ aRet = xTransfer2->getTransferData2(format, rDestDoc);
+ else
+ aRet = mxTransfer->getTransferData(format);
+ }
+
+ if( aRet.hasValue() )
+ break;
+ }
+ }
+
+ if( !aRet.hasValue() )
+ {
+ if (xTransfer2.is())
+ aRet = xTransfer2->getTransferData2(rFlavor, rDestDoc);
+ else
+ aRet = mxTransfer->getTransferData(rFlavor);
+ }
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ return aRet;
+}
+
+
+bool TransferableDataHelper::GetString( SotClipboardFormatId nFormat, OUString& rStr )
+{
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetString( aFlavor, rStr ) );
+}
+
+
+bool TransferableDataHelper::GetString( const DataFlavor& rFlavor, OUString& rStr )
+{
+ Any aAny = GetAny(rFlavor, OUString());
+ bool bRet = false;
+
+ if( aAny.hasValue() )
+ {
+ OUString aOUString;
+ Sequence< sal_Int8 > aSeq;
+
+ if( aAny >>= aOUString )
+ {
+ rStr = aOUString;
+ bRet = true;
+ }
+ else if( aAny >>= aSeq )
+ {
+
+ const sal_Char* pChars = reinterpret_cast< const sal_Char* >( aSeq.getConstArray() );
+ sal_Int32 nLen = aSeq.getLength();
+
+ //JP 10.10.2001: 92930 - don't copy the last zero character into the string.
+ //DVO 2002-05-27: strip _all_ trailing zeros
+ while( nLen && ( 0 == *( pChars + nLen - 1 ) ) )
+ --nLen;
+
+ rStr = OUString( pChars, nLen, osl_getThreadTextEncoding() );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+
+bool TransferableDataHelper::GetBitmapEx( SotClipboardFormatId nFormat, BitmapEx& rBmpEx )
+{
+ if(SotClipboardFormatId::BITMAP == nFormat)
+ {
+ // try to get PNG first
+ DataFlavor aFlavor;
+
+ if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
+ {
+ if(GetBitmapEx(aFlavor, rBmpEx))
+ {
+ return true;
+ }
+ }
+ }
+
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetBitmapEx( aFlavor, rBmpEx ) );
+}
+
+
+bool TransferableDataHelper::GetBitmapEx( const DataFlavor& rFlavor, BitmapEx& rBmpEx )
+{
+ tools::SvRef<SotStorageStream> xStm;
+ DataFlavor aSubstFlavor;
+ bool bRet(GetSotStorageStream(rFlavor, xStm));
+ bool bSuppressPNG(false); // #122982# If PNG stream not accessed, but BMP one, suppress trying to load PNG
+ bool bSuppressJPEG(false);
+
+ if(!bRet && HasFormat(SotClipboardFormatId::PNG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aSubstFlavor))
+ {
+ // when no direct success, try if PNG is available
+ bRet = GetSotStorageStream(aSubstFlavor, xStm);
+ bSuppressJPEG = bRet;
+ }
+
+ if(!bRet && HasFormat(SotClipboardFormatId::JPEG) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aSubstFlavor))
+ {
+ bRet = GetSotStorageStream(aSubstFlavor, xStm);
+ bSuppressPNG = bRet;
+ }
+
+ if(!bRet && HasFormat(SotClipboardFormatId::BMP) && SotExchange::GetFormatDataFlavor(SotClipboardFormatId::BMP, aSubstFlavor))
+ {
+ // when no direct success, try if BMP is available
+ bRet = GetSotStorageStream(aSubstFlavor, xStm);
+ bSuppressPNG = bRet;
+ bSuppressJPEG = bRet;
+ }
+
+ if(bRet)
+ {
+ if(!bSuppressPNG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/png"))
+ {
+ // it's a PNG, import to BitmapEx
+ vcl::PNGReader aPNGReader(*xStm);
+
+ rBmpEx = aPNGReader.Read();
+ }
+ else if(!bSuppressJPEG && rFlavor.MimeType.equalsIgnoreAsciiCase("image/jpeg"))
+ {
+ // it's a JPEG, import to BitmapEx
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ Graphic aGraphic;
+ if (rFilter.ImportGraphic(aGraphic, "", *xStm) == ERRCODE_NONE)
+ rBmpEx = aGraphic.GetBitmapEx();
+ }
+
+ if(rBmpEx.IsEmpty())
+ {
+ Bitmap aBitmap;
+ AlphaMask aMask;
+
+ // explicitly use Bitmap::Read with bFileHeader = sal_True
+ // #i124085# keep DIBV5 for read from clipboard, but should not happen
+ ReadDIBV5(aBitmap, aMask, *xStm);
+
+ if(aMask.GetBitmap().IsEmpty())
+ {
+ rBmpEx = aBitmap;
+ }
+ else
+ {
+ rBmpEx = BitmapEx(aBitmap, aMask);
+ }
+ }
+
+ bRet = (ERRCODE_NONE == xStm->GetError() && !rBmpEx.IsEmpty());
+
+ /* SJ: #110748# At the moment we are having problems with DDB inserted as DIB. The
+ problem is, that some graphics are inserted much too big because the nXPelsPerMeter
+ and nYPelsPerMeter of the bitmap fileheader isn't including the correct value.
+ Due to this reason the following code assumes that bitmaps with a logical size
+ greater than 50 cm aren't having the correct mapmode set.
+
+ The following code should be removed if DDBs and DIBs are supported via clipboard
+ properly.
+ */
+ if(bRet)
+ {
+ const MapMode aMapMode(rBmpEx.GetPrefMapMode());
+
+ if(MapUnit::MapPixel != aMapMode.GetMapUnit())
+ {
+ const Size aSize(OutputDevice::LogicToLogic(rBmpEx.GetPrefSize(), aMapMode, MapMode(MapUnit::Map100thMM)));
+
+ // #i122388# This wrongly corrects in the given case; changing from 5000 100th mm to
+ // the described 50 cm (which is 50000 100th mm)
+ if((aSize.Width() > 50000) || (aSize.Height() > 50000))
+ {
+ rBmpEx.SetPrefMapMode(MapMode(MapUnit::MapPixel));
+
+ // #i122388# also adapt size by applying the mew MapMode
+ const Size aNewSize(OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapPixel)));
+ rBmpEx.SetPrefSize(aNewSize);
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+
+bool TransferableDataHelper::GetGDIMetaFile(SotClipboardFormatId nFormat, GDIMetaFile& rMtf, size_t nMaxActions)
+{
+ DataFlavor aFlavor;
+ return SotExchange::GetFormatDataFlavor(nFormat, aFlavor) &&
+ GetGDIMetaFile(aFlavor, rMtf) &&
+ (nMaxActions == 0 || rMtf.GetActionSize() < nMaxActions);
+}
+
+
+bool TransferableDataHelper::GetGDIMetaFile( const DataFlavor& rFlavor, GDIMetaFile& rMtf )
+{
+ tools::SvRef<SotStorageStream> xStm;
+ DataFlavor aSubstFlavor;
+ bool bRet = false;
+
+ if( GetSotStorageStream( rFlavor, xStm ) )
+ {
+ ReadGDIMetaFile( *xStm, rMtf );
+ bRet = ( xStm->GetError() == ERRCODE_NONE );
+ }
+
+ if( !bRet &&
+ HasFormat( SotClipboardFormatId::EMF ) &&
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::EMF, aSubstFlavor ) &&
+ GetSotStorageStream( aSubstFlavor, xStm ) )
+ {
+ Graphic aGraphic;
+
+ if( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
+ {
+ rMtf = aGraphic.GetGDIMetaFile();
+ bRet = true;
+ }
+ }
+
+ if( !bRet &&
+ HasFormat( SotClipboardFormatId::WMF ) &&
+ SotExchange::GetFormatDataFlavor( SotClipboardFormatId::WMF, aSubstFlavor ) &&
+ GetSotStorageStream( aSubstFlavor, xStm ) )
+ {
+ Graphic aGraphic;
+
+ if( GraphicConverter::Import( *xStm, aGraphic ) == ERRCODE_NONE )
+ {
+ rMtf = aGraphic.GetGDIMetaFile();
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+
+bool TransferableDataHelper::GetGraphic( SotClipboardFormatId nFormat, Graphic& rGraphic )
+{
+ if(SotClipboardFormatId::BITMAP == nFormat)
+ {
+ // try to get PNG first
+ DataFlavor aFlavor;
+
+ if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor))
+ {
+ if(GetGraphic(aFlavor, rGraphic))
+ {
+ return true;
+ }
+ }
+ }
+
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetGraphic( aFlavor, rGraphic ) );
+}
+
+
+bool TransferableDataHelper::GetGraphic( const css::datatransfer::DataFlavor& rFlavor, Graphic& rGraphic )
+{
+ DataFlavor aFlavor;
+ bool bRet = false;
+
+ if(SotExchange::GetFormatDataFlavor(SotClipboardFormatId::PNG, aFlavor) &&
+ TransferableDataHelper::IsEqual(aFlavor, rFlavor))
+ {
+ // try to get PNG first
+ BitmapEx aBmpEx;
+
+ bRet = GetBitmapEx( aFlavor, aBmpEx );
+ if( bRet )
+ rGraphic = aBmpEx;
+ }
+ else if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::JPEG, aFlavor) && TransferableDataHelper::IsEqual(aFlavor, rFlavor))
+ {
+ BitmapEx aBitmapEx;
+
+ bRet = GetBitmapEx(aFlavor, aBitmapEx);
+ if (bRet)
+ rGraphic = aBitmapEx;
+ }
+ else if(SotExchange::GetFormatDataFlavor( SotClipboardFormatId::BITMAP, aFlavor ) &&
+ TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
+ {
+ BitmapEx aBmpEx;
+
+ bRet = GetBitmapEx( aFlavor, aBmpEx );
+ if( bRet )
+ rGraphic = aBmpEx;
+ }
+ else if( SotExchange::GetFormatDataFlavor( SotClipboardFormatId::GDIMETAFILE, aFlavor ) &&
+ TransferableDataHelper::IsEqual( aFlavor, rFlavor ) )
+ {
+ GDIMetaFile aMtf;
+
+ bRet = GetGDIMetaFile( aFlavor, aMtf );
+ if( bRet )
+ rGraphic = aMtf;
+ }
+ else
+ {
+ tools::SvRef<SotStorageStream> xStm;
+
+ if( GetSotStorageStream( rFlavor, xStm ) )
+ {
+ ReadGraphic( *xStm, rGraphic );
+ bRet = ( xStm->GetError() == ERRCODE_NONE );
+ }
+ }
+
+ return bRet;
+}
+
+
+bool TransferableDataHelper::GetImageMap( SotClipboardFormatId nFormat, ImageMap& rIMap )
+{
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetImageMap( aFlavor, rIMap ) );
+}
+
+
+bool TransferableDataHelper::GetImageMap( const css::datatransfer::DataFlavor& rFlavor, ImageMap& rIMap )
+{
+ tools::SvRef<SotStorageStream> xStm;
+ bool bRet = GetSotStorageStream( rFlavor, xStm );
+
+ if( bRet )
+ {
+ rIMap.Read( *xStm );
+ bRet = ( xStm->GetError() == ERRCODE_NONE );
+ }
+
+ return bRet;
+}
+
+
+bool TransferableDataHelper::GetTransferableObjectDescriptor( SotClipboardFormatId nFormat, TransferableObjectDescriptor& rDesc )
+{
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetTransferableObjectDescriptor( rDesc ) );
+}
+
+
+bool TransferableDataHelper::GetTransferableObjectDescriptor( TransferableObjectDescriptor& rDesc )
+{
+ rDesc = *mxObjDesc;
+ return true;
+}
+
+
+bool TransferableDataHelper::GetINetBookmark( SotClipboardFormatId nFormat, INetBookmark& rBmk )
+{
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetBookmark( aFlavor, rBmk ) );
+}
+
+
+bool TransferableDataHelper::GetINetBookmark( const css::datatransfer::DataFlavor& rFlavor, INetBookmark& rBmk )
+{
+ bool bRet = false;
+ if( HasFormat( rFlavor ))
+ {
+ const SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
+ switch( nFormat )
+ {
+ case SotClipboardFormatId::SOLK:
+ case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
+ {
+ OUString aString;
+ if( GetString( rFlavor, aString ) )
+ {
+ if( SotClipboardFormatId::UNIFORMRESOURCELOCATOR == nFormat )
+ {
+ rBmk = INetBookmark( aString, aString );
+ bRet = true;
+ }
+ else
+ {
+ OUString aURL, aDesc;
+ sal_Int32 nStart = aString.indexOf( '@' ), nLen = aString.toInt32();
+
+ if( !nLen && aString[ 0 ] != '0' )
+ {
+ SAL_INFO( "svtools", "SOLK: 1. len=0" );
+ }
+ if( nStart == -1 || nLen > aString.getLength() - nStart - 3 )
+ {
+ SAL_INFO( "svtools", "SOLK: 1. illegal start or wrong len" );
+ }
+ aURL = aString.copy( nStart + 1, nLen );
+
+ aString = aString.replaceAt( 0, nStart + 1 + nLen, "" );
+ nStart = aString.indexOf( '@' );
+ nLen = aString.toInt32();
+
+ if( !nLen && aString[ 0 ] != '0' )
+ {
+ SAL_INFO( "svtools", "SOLK: 2. len=0" );
+ }
+ if( nStart == -1 || nLen > aString.getLength() - nStart - 1 )
+ {
+ SAL_INFO( "svtools", "SOLK: 2. illegal start or wrong len" );
+ }
+ aDesc = aString.copy( nStart+1, nLen );
+
+ rBmk = INetBookmark( aURL, aDesc );
+ bRet = true;
+ }
+ }
+ }
+ break;
+
+ case SotClipboardFormatId::NETSCAPE_BOOKMARK:
+ {
+ Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
+
+ if (2048 == aSeq.getLength())
+ {
+ const sal_Char* p1 = reinterpret_cast< const sal_Char* >( aSeq.getConstArray() );
+ const sal_Char* p2 = reinterpret_cast< const sal_Char* >( aSeq.getConstArray() ) + 1024;
+ rBmk = INetBookmark( OUString( p1, strlen(p1), osl_getThreadTextEncoding() ),
+ OUString( p2, strlen(p2), osl_getThreadTextEncoding() ) );
+ bRet = true;
+ }
+ }
+ break;
+
+#ifdef _WIN32
+ case SotClipboardFormatId::FILEGRPDESCRIPTOR:
+ {
+ Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
+
+ if (aSeq.getLength())
+ {
+ FILEGROUPDESCRIPTOR const * pFDesc = reinterpret_cast<FILEGROUPDESCRIPTOR const *>(aSeq.getConstArray());
+
+ if( pFDesc->cItems )
+ {
+ OString aDesc( pFDesc->fgd[ 0 ].cFileName );
+ rtl_TextEncoding eTextEncoding = osl_getThreadTextEncoding();
+
+ if( ( aDesc.getLength() > 4 ) && aDesc.copy(aDesc.getLength() - 4).equalsIgnoreAsciiCase(".URL") )
+ {
+ std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( INetURLObject( OStringToOUString(aDesc, eTextEncoding) ).GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ StreamMode::STD_READ ));
+
+ if( !pStream || pStream->GetError() )
+ {
+ DataFlavor aFileContentFlavor;
+
+ aSeq.realloc( 0 );
+ pStream.reset();
+
+ if (SotExchange::GetFormatDataFlavor(SotClipboardFormatId::FILECONTENT, aFileContentFlavor))
+ {
+ aSeq = GetSequence(aFileContentFlavor, OUString());
+ if (aSeq.getLength())
+ pStream.reset(new SvMemoryStream( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(), StreamMode::STD_READ ));
+ }
+ }
+
+ if( pStream )
+ {
+ OString aLine;
+ bool bSttFnd = false;
+
+ while( pStream->ReadLine( aLine ) )
+ {
+ if (aLine.equalsIgnoreAsciiCase("[InternetShortcut]"))
+ bSttFnd = true;
+ else if (bSttFnd && aLine.copy(0, 4).equalsIgnoreAsciiCase("URL="))
+ {
+ rBmk = INetBookmark( OStringToOUString(aLine.copy(4), eTextEncoding),
+ OStringToOUString(aDesc.copy(0, aDesc.getLength() - 4), eTextEncoding) );
+ bRet = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+#endif
+ default: break;
+ }
+ }
+ return bRet;
+}
+
+
+bool TransferableDataHelper::GetINetImage( SotClipboardFormatId nFormat,
+ INetImage& rINtImg )
+{
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetINetImage( aFlavor, rINtImg ) );
+}
+
+
+bool TransferableDataHelper::GetINetImage(
+ const css::datatransfer::DataFlavor& rFlavor,
+ INetImage& rINtImg )
+{
+ tools::SvRef<SotStorageStream> xStm;
+ bool bRet = GetSotStorageStream( rFlavor, xStm );
+
+ if( bRet )
+ bRet = rINtImg.Read( *xStm, SotExchange::GetFormat( rFlavor ) );
+ return bRet;
+}
+
+
+bool TransferableDataHelper::GetFileList( SotClipboardFormatId nFormat,
+ FileList& rFileList )
+{
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetFileList( rFileList ) );
+}
+
+
+bool TransferableDataHelper::GetFileList( FileList& rFileList )
+{
+ tools::SvRef<SotStorageStream> xStm;
+ bool bRet = false;
+
+ for( sal_uInt32 i = 0, nFormatCount = GetFormatCount(); ( i < nFormatCount ) && !bRet; ++i )
+ {
+ if( SotClipboardFormatId::FILE_LIST == GetFormat( i ) )
+ {
+ const DataFlavor aFlavor( GetFormatDataFlavor( i ) );
+
+ if( GetSotStorageStream( aFlavor, xStm ) )
+ {
+ if( aFlavor.MimeType.indexOf( "text/uri-list" ) > -1 )
+ {
+ OString aDiskString;
+
+ while( xStm->ReadLine( aDiskString ) )
+ if( !aDiskString.isEmpty() && aDiskString[0] != '#' )
+ rFileList.AppendFile( OStringToOUString(aDiskString, RTL_TEXTENCODING_UTF8) );
+
+ bRet = true;
+ }
+ else
+ bRet = ( ReadFileList( *xStm, rFileList ).GetError() == ERRCODE_NONE );
+ }
+ }
+ }
+
+ return bRet;
+}
+
+
+Sequence<sal_Int8> TransferableDataHelper::GetSequence( SotClipboardFormatId nFormat, const OUString& rDestDoc )
+{
+ DataFlavor aFlavor;
+ if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
+ return Sequence<sal_Int8>();
+
+ return GetSequence(aFlavor, rDestDoc);
+}
+
+Sequence<sal_Int8> TransferableDataHelper::GetSequence( const DataFlavor& rFlavor, const OUString& rDestDoc )
+{
+ const Any aAny = GetAny(rFlavor, rDestDoc);
+ Sequence<sal_Int8> aSeq;
+ if (aAny.hasValue())
+ aAny >>= aSeq;
+
+ return aSeq;
+}
+
+
+bool TransferableDataHelper::GetSotStorageStream( SotClipboardFormatId nFormat, tools::SvRef<SotStorageStream>& rxStream )
+{
+ DataFlavor aFlavor;
+ return( SotExchange::GetFormatDataFlavor( nFormat, aFlavor ) && GetSotStorageStream( aFlavor, rxStream ) );
+}
+
+
+bool TransferableDataHelper::GetSotStorageStream( const DataFlavor& rFlavor, tools::SvRef<SotStorageStream>& rxStream )
+{
+ Sequence<sal_Int8> aSeq = GetSequence(rFlavor, OUString());
+
+ if (aSeq.getLength())
+ {
+ rxStream = new SotStorageStream( "" );
+ rxStream->WriteBytes( aSeq.getConstArray(), aSeq.getLength() );
+ rxStream->Seek( 0 );
+ }
+
+ return aSeq.getLength();
+}
+
+Reference<XInputStream> TransferableDataHelper::GetInputStream( SotClipboardFormatId nFormat, const OUString& rDestDoc )
+{
+ DataFlavor aFlavor;
+ if (!SotExchange::GetFormatDataFlavor(nFormat, aFlavor))
+ return Reference<XInputStream>();
+
+ return GetInputStream(aFlavor, rDestDoc);
+}
+
+Reference<XInputStream> TransferableDataHelper::GetInputStream( const DataFlavor& rFlavor, const OUString& rDestDoc )
+{
+ Sequence<sal_Int8> aSeq = GetSequence(rFlavor, rDestDoc);
+
+ if (!aSeq.getLength())
+ return Reference<XInputStream>();
+
+ Reference<XInputStream> xStream(new comphelper::SequenceInputStream(aSeq));
+ return xStream;
+}
+
+void TransferableDataHelper::Rebind( const Reference< XTransferable >& _rxNewContent )
+{
+ mxTransfer = _rxNewContent;
+ InitFormats();
+}
+
+bool TransferableDataHelper::StartClipboardListening( )
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+
+ StopClipboardListening( );
+
+ mxImpl->mxClipboardListener = new TransferableClipboardNotifier(mxClipboard, *this, mxImpl->maMutex);
+
+ return mxImpl->mxClipboardListener->isListening();
+}
+
+void TransferableDataHelper::StopClipboardListening( )
+{
+ ::osl::MutexGuard aGuard(mxImpl->maMutex);
+
+ if (mxImpl->mxClipboardListener.is())
+ {
+ mxImpl->mxClipboardListener->dispose();
+ mxImpl->mxClipboardListener.clear();
+ }
+}
+
+
+TransferableDataHelper TransferableDataHelper::CreateFromSystemClipboard( vcl::Window * pWindow )
+{
+ DBG_ASSERT( pWindow, "Window pointer is NULL" );
+
+ Reference< XClipboard > xClipboard;
+ TransferableDataHelper aRet;
+
+ if( pWindow )
+ xClipboard = pWindow->GetClipboard();
+
+ if( xClipboard.is() )
+ {
+ try
+ {
+ Reference< XTransferable > xTransferable( xClipboard->getContents() );
+
+ if( xTransferable.is() )
+ {
+ aRet = TransferableDataHelper( xTransferable );
+ // also copy the clipboard
+ aRet.mxClipboard = xClipboard;
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+ }
+
+ return aRet;
+}
+
+
+TransferableDataHelper TransferableDataHelper::CreateFromSelection( vcl::Window* pWindow )
+{
+ DBG_ASSERT( pWindow, "Window pointer is NULL" );
+
+ Reference< XClipboard > xSelection;
+ TransferableDataHelper aRet;
+
+ if( pWindow )
+ xSelection = pWindow->GetPrimarySelection();
+
+ if( xSelection.is() )
+ {
+ SolarMutexReleaser aReleaser;
+
+ try
+ {
+ Reference< XTransferable > xTransferable( xSelection->getContents() );
+
+ if( xTransferable.is() )
+ {
+ aRet = TransferableDataHelper( xTransferable );
+ aRet.mxClipboard = xSelection;
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+ }
+
+ return aRet;
+}
+
+
+bool TransferableDataHelper::IsEqual( const css::datatransfer::DataFlavor& rInternalFlavor,
+ const css::datatransfer::DataFlavor& rRequestFlavor )
+{
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ bool bRet = false;
+
+ try
+ {
+ Reference< XMimeContentTypeFactory > xMimeFact = MimeContentTypeFactory::create( xContext );
+
+ Reference< XMimeContentType > xRequestType1( xMimeFact->createMimeContentType( rInternalFlavor.MimeType ) );
+ Reference< XMimeContentType > xRequestType2( xMimeFact->createMimeContentType( rRequestFlavor.MimeType ) );
+
+ if( xRequestType1.is() && xRequestType2.is() )
+ {
+ if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( xRequestType2->getFullMediaType() ) )
+ {
+ if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "text/plain" ) )
+ {
+ // special handling for text/plain media types
+ const OUString aCharsetString( "charset" );
+
+ if( !xRequestType2->hasParameter( aCharsetString ) ||
+ xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "utf-16" ) ||
+ xRequestType2->getParameterValue( aCharsetString ).equalsIgnoreAsciiCase( "unicode" ) )
+ {
+ bRet = true;
+ }
+ }
+ else if( xRequestType1->getFullMediaType().equalsIgnoreAsciiCase( "application/x-openoffice" ) )
+ {
+ // special handling for application/x-openoffice media types
+ const OUString aFormatString( "windows_formatname" );
+
+ if( xRequestType1->hasParameter( aFormatString ) &&
+ xRequestType2->hasParameter( aFormatString ) &&
+ xRequestType1->getParameterValue( aFormatString ).equalsIgnoreAsciiCase( xRequestType2->getParameterValue( aFormatString ) ) )
+ {
+ bRet = true;
+ }
+ }
+ else
+ bRet = true;
+ }
+ }
+ }
+ catch( const css::uno::Exception& )
+ {
+ bRet = rInternalFlavor.MimeType.equalsIgnoreAsciiCase( rRequestFlavor.MimeType );
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/transfer2.cxx b/vcl/source/treelist/transfer2.cxx
new file mode 100644
index 000000000000..020f98aae940
--- /dev/null
+++ b/vcl/source/treelist/transfer2.cxx
@@ -0,0 +1,472 @@
+/* -*- 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 <osl/mutex.hxx>
+#include <sot/exchange.hxx>
+#include <sot/storage.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <comphelper/fileformat.h>
+#include <com/sun/star/datatransfer/dnd/XDropTargetDragContext.hpp>
+#include <svl/urlbmk.hxx>
+#include <vcl/inetimg.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/transfer.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::datatransfer::clipboard;
+using namespace ::com::sun::star::datatransfer::dnd;
+
+
+DragSourceHelper::DragGestureListener::DragGestureListener( DragSourceHelper& rDragSourceHelper ) :
+ mrParent( rDragSourceHelper )
+{
+}
+
+
+DragSourceHelper::DragGestureListener::~DragGestureListener()
+{
+}
+
+
+void SAL_CALL DragSourceHelper::DragGestureListener::disposing( const EventObject& )
+{
+}
+
+
+void SAL_CALL DragSourceHelper::DragGestureListener::dragGestureRecognized( const DragGestureEvent& rDGE )
+{
+ const SolarMutexGuard aGuard;
+
+ const Point aPtPixel( rDGE.DragOriginX, rDGE.DragOriginY );
+ mrParent.StartDrag( rDGE.DragAction, aPtPixel );
+}
+
+
+DragSourceHelper::DragSourceHelper( vcl::Window* pWindow ) :
+ mxDragGestureRecognizer( pWindow->GetDragGestureRecognizer() )
+{
+ if( mxDragGestureRecognizer.is() )
+ {
+ mxDragGestureListener = new DragSourceHelper::DragGestureListener( *this );
+ mxDragGestureRecognizer->addDragGestureListener( mxDragGestureListener );
+ }
+}
+
+
+void DragSourceHelper::dispose()
+{
+ Reference<XDragGestureRecognizer> xTmp;
+ {
+ osl::MutexGuard aGuard( maMutex );
+ xTmp = mxDragGestureRecognizer;
+ mxDragGestureRecognizer.clear();
+ }
+ if( xTmp.is() )
+ xTmp->removeDragGestureListener( mxDragGestureListener );
+}
+
+DragSourceHelper::~DragSourceHelper()
+{
+ dispose();
+}
+
+
+void DragSourceHelper::StartDrag( sal_Int8, const Point& )
+{
+}
+
+
+DropTargetHelper::DropTargetListener::DropTargetListener( DropTargetHelper& rDropTargetHelper ) :
+ mrParent( rDropTargetHelper )
+{
+}
+
+
+DropTargetHelper::DropTargetListener::~DropTargetListener()
+{
+}
+
+
+void SAL_CALL DropTargetHelper::DropTargetListener::disposing( const EventObject& )
+{
+}
+
+
+void SAL_CALL DropTargetHelper::DropTargetListener::drop( const DropTargetDropEvent& rDTDE )
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ AcceptDropEvent aAcceptEvent;
+ ExecuteDropEvent aExecuteEvt( rDTDE.DropAction & ~DNDConstants::ACTION_DEFAULT, Point( rDTDE.LocationX, rDTDE.LocationY ), rDTDE );
+
+ aExecuteEvt.mbDefault = ( ( rDTDE.DropAction & DNDConstants::ACTION_DEFAULT ) != 0 );
+
+ // in case of a default action, call ::AcceptDrop first and use the returned
+ // accepted action as the execute action in the call to ::ExecuteDrop
+ aAcceptEvent.mnAction = aExecuteEvt.mnAction;
+ aAcceptEvent.maPosPixel = aExecuteEvt.maPosPixel;
+ static_cast<DropTargetEvent&>(const_cast<DropTargetDragEvent&>( aAcceptEvent.maDragEvent )) = rDTDE;
+ const_cast<DropTargetDragEvent&>( aAcceptEvent.maDragEvent ).DropAction = rDTDE.DropAction;
+ const_cast<DropTargetDragEvent&>( aAcceptEvent.maDragEvent ).LocationX = rDTDE.LocationX;
+ const_cast<DropTargetDragEvent&>( aAcceptEvent.maDragEvent ).LocationY = rDTDE.LocationY;
+ const_cast<DropTargetDragEvent&>( aAcceptEvent.maDragEvent ).SourceActions = rDTDE.SourceActions;
+ aAcceptEvent.mbLeaving = false;
+ aAcceptEvent.mbDefault = aExecuteEvt.mbDefault;
+
+ sal_Int8 nRet = mrParent.AcceptDrop( aAcceptEvent );
+
+ if( DNDConstants::ACTION_NONE != nRet )
+ {
+ rDTDE.Context->acceptDrop( nRet );
+
+ if( aExecuteEvt.mbDefault )
+ aExecuteEvt.mnAction = nRet;
+
+ nRet = mrParent.ExecuteDrop( aExecuteEvt );
+ }
+
+ rDTDE.Context->dropComplete( DNDConstants::ACTION_NONE != nRet );
+
+ mpLastDragOverEvent.reset();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+void SAL_CALL DropTargetHelper::DropTargetListener::dragEnter( const DropTargetDragEnterEvent& rDTDEE )
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ mrParent.ImplBeginDrag( rDTDEE.SupportedDataFlavors );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ dragOver( rDTDEE );
+}
+
+
+void SAL_CALL DropTargetHelper::DropTargetListener::dragOver( const DropTargetDragEvent& rDTDE )
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ mpLastDragOverEvent.reset( new AcceptDropEvent( rDTDE.DropAction & ~DNDConstants::ACTION_DEFAULT, Point( rDTDE.LocationX, rDTDE.LocationY ), rDTDE ) );
+ mpLastDragOverEvent->mbDefault = ( ( rDTDE.DropAction & DNDConstants::ACTION_DEFAULT ) != 0 );
+
+ const sal_Int8 nRet = mrParent.AcceptDrop( *mpLastDragOverEvent );
+
+ if( DNDConstants::ACTION_NONE == nRet )
+ rDTDE.Context->rejectDrag();
+ else
+ rDTDE.Context->acceptDrag( nRet );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+void SAL_CALL DropTargetHelper::DropTargetListener::dragExit( const DropTargetEvent& )
+{
+ const SolarMutexGuard aGuard;
+
+ try
+ {
+ if( mpLastDragOverEvent )
+ {
+ mpLastDragOverEvent->mbLeaving = true;
+ mrParent.AcceptDrop( *mpLastDragOverEvent );
+ mpLastDragOverEvent.reset();
+ }
+
+ mrParent.ImplEndDrag();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+
+void SAL_CALL DropTargetHelper::DropTargetListener::dropActionChanged( const DropTargetDragEvent& )
+{
+}
+
+
+DropTargetHelper::DropTargetHelper( vcl::Window* pWindow ) :
+ mxDropTarget( pWindow->GetDropTarget() )
+{
+ ImplConstruct();
+}
+
+
+DropTargetHelper::DropTargetHelper( const Reference< XDropTarget >& rxDropTarget ) :
+ mxDropTarget( rxDropTarget )
+{
+ ImplConstruct();
+}
+
+
+void DropTargetHelper::dispose()
+{
+ Reference< XDropTarget > xTmp;
+ {
+ osl::MutexGuard aGuard( maMutex );
+ xTmp = mxDropTarget;
+ mxDropTarget.clear();
+ }
+ if( xTmp.is() )
+ xTmp->removeDropTargetListener( mxDropTargetListener );
+}
+
+DropTargetHelper::~DropTargetHelper()
+{
+ dispose();
+}
+
+
+void DropTargetHelper::ImplConstruct()
+{
+ if( mxDropTarget.is() )
+ {
+ mxDropTargetListener = new DropTargetHelper::DropTargetListener( *this );
+ mxDropTarget->addDropTargetListener( mxDropTargetListener );
+ mxDropTarget->setActive( true );
+ }
+}
+
+
+void DropTargetHelper::ImplBeginDrag( const Sequence< DataFlavor >& rSupportedDataFlavors )
+{
+ maFormats.clear();
+ TransferableDataHelper::FillDataFlavorExVector( rSupportedDataFlavors, maFormats );
+}
+
+
+void DropTargetHelper::ImplEndDrag()
+{
+ maFormats.clear();
+}
+
+
+sal_Int8 DropTargetHelper::AcceptDrop( const AcceptDropEvent& )
+{
+ return DNDConstants::ACTION_NONE;
+}
+
+
+sal_Int8 DropTargetHelper::ExecuteDrop( const ExecuteDropEvent& )
+{
+ return DNDConstants::ACTION_NONE;
+}
+
+
+bool DropTargetHelper::IsDropFormatSupported( SotClipboardFormatId nFormat )
+{
+ return std::any_of(maFormats.begin(), maFormats.end(),
+ [&](const DataFlavorEx& data) { return data.mnSotId == nFormat; });
+}
+
+
+// TransferDataContainer
+
+
+struct TDataCntnrEntry_Impl
+{
+ css::uno::Any aAny;
+ SotClipboardFormatId nId;
+};
+
+
+typedef ::std::vector< TDataCntnrEntry_Impl > TDataCntnrEntryList;
+
+
+struct TransferDataContainer_Impl
+{
+ TDataCntnrEntryList aFmtList;
+ Link<sal_Int8,void> aFinshedLnk;
+ std::unique_ptr<INetBookmark> pBookmk;
+
+ TransferDataContainer_Impl()
+ {
+ }
+};
+
+
+TransferDataContainer::TransferDataContainer()
+ : pImpl( new TransferDataContainer_Impl )
+{
+}
+
+
+TransferDataContainer::~TransferDataContainer()
+{
+}
+
+
+void TransferDataContainer::AddSupportedFormats()
+{
+}
+
+
+bool TransferDataContainer::GetData(
+ const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
+{
+ bool bFnd = false;
+ SotClipboardFormatId nFmtId = SotExchange::GetFormat( rFlavor );
+
+ // test first the list
+ for (auto const& format : pImpl->aFmtList)
+ {
+ if( nFmtId == format.nId )
+ {
+ bFnd = SetAny( format.aAny );
+ break;
+ }
+ }
+
+ // test second the bookmark pointer
+ if( !bFnd )
+ switch( nFmtId )
+ {
+ case SotClipboardFormatId::STRING:
+ case SotClipboardFormatId::SOLK:
+ case SotClipboardFormatId::NETSCAPE_BOOKMARK:
+ case SotClipboardFormatId::FILECONTENT:
+ case SotClipboardFormatId::FILEGRPDESCRIPTOR:
+ case SotClipboardFormatId::UNIFORMRESOURCELOCATOR:
+ if( pImpl->pBookmk )
+ bFnd = SetINetBookmark( *pImpl->pBookmk, rFlavor );
+ break;
+
+ default: break;
+ }
+
+ return bFnd;
+}
+
+
+void TransferDataContainer::CopyINetBookmark( const INetBookmark& rBkmk )
+{
+ if( !pImpl->pBookmk )
+ pImpl->pBookmk.reset( new INetBookmark( rBkmk ) );
+ else
+ *pImpl->pBookmk = rBkmk;
+
+ AddFormat( SotClipboardFormatId::STRING );
+ AddFormat( SotClipboardFormatId::SOLK );
+ AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK );
+ AddFormat( SotClipboardFormatId::FILECONTENT );
+ AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR );
+}
+
+
+void TransferDataContainer::CopyAnyData( SotClipboardFormatId nFormatId,
+ const sal_Char* pData, sal_uLong nLen )
+{
+ if( nLen )
+ {
+ TDataCntnrEntry_Impl aEntry;
+ aEntry.nId = nFormatId;
+
+ Sequence< sal_Int8 > aSeq( nLen );
+ memcpy( aSeq.getArray(), pData, nLen );
+ aEntry.aAny <<= aSeq;
+ pImpl->aFmtList.push_back( aEntry );
+ AddFormat( nFormatId );
+ }
+}
+
+
+void TransferDataContainer::CopyByteString( SotClipboardFormatId nFormatId,
+ const OString& rStr )
+{
+ CopyAnyData( nFormatId, rStr.getStr(), rStr.getLength() );
+}
+
+
+void TransferDataContainer::CopyString( SotClipboardFormatId nFmt, const OUString& rStr )
+{
+ if( !rStr.isEmpty() )
+ {
+ TDataCntnrEntry_Impl aEntry;
+ aEntry.nId = nFmt;
+ aEntry.aAny <<= rStr;
+ pImpl->aFmtList.push_back( aEntry );
+ AddFormat( aEntry.nId );
+ }
+}
+
+
+void TransferDataContainer::CopyString( const OUString& rStr )
+{
+ CopyString( SotClipboardFormatId::STRING, rStr );
+}
+
+
+void TransferDataContainer::CopyAny( SotClipboardFormatId nFmt,
+ const css::uno::Any& rAny )
+{
+ TDataCntnrEntry_Impl aEntry;
+ aEntry.nId = nFmt;
+ aEntry.aAny = rAny;
+ pImpl->aFmtList.push_back( aEntry );
+ AddFormat( aEntry.nId );
+}
+
+
+bool TransferDataContainer::HasAnyData() const
+{
+ return !pImpl->aFmtList.empty() ||
+ nullptr != pImpl->pBookmk;
+}
+
+
+void TransferDataContainer::StartDrag(
+ vcl::Window* pWindow, sal_Int8 nDragSourceActions,
+ const Link<sal_Int8,void>& rLnk )
+{
+ pImpl->aFinshedLnk = rLnk;
+ TransferableHelper::StartDrag( pWindow, nDragSourceActions );
+}
+
+
+void TransferDataContainer::DragFinished( sal_Int8 nDropAction )
+{
+ pImpl->aFinshedLnk.Call( nDropAction );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/treelist.cxx b/vcl/source/treelist/treelist.cxx
new file mode 100644
index 000000000000..cc5297111c78
--- /dev/null
+++ b/vcl/source/treelist/treelist.cxx
@@ -0,0 +1,1630 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/treelist.hxx>
+#include <vcl/treelistentry.hxx>
+#include <vcl/viewdataentry.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/make_unique.hxx>
+
+#include <memory>
+#include <map>
+
+
+typedef std::map<SvTreeListEntry*, std::unique_ptr<SvViewDataEntry>> SvDataTable;
+
+struct SvListView::Impl
+{
+ SvListView & m_rThis;
+
+ SvDataTable m_DataTable; // Mapping SvTreeListEntry -> ViewData
+
+ sal_uLong m_nVisibleCount;
+ sal_uLong m_nSelectionCount;
+ bool m_bVisPositionsValid;
+
+ explicit Impl(SvListView & rThis)
+ : m_rThis(rThis)
+ , m_nVisibleCount(0)
+ , m_nSelectionCount(0)
+ , m_bVisPositionsValid(false)
+ {}
+
+ void InitTable();
+ void RemoveViewData( SvTreeListEntry* pParent );
+
+ void ActionMoving(SvTreeListEntry* pEntry);
+ void ActionMoved();
+ void ActionInserted(SvTreeListEntry* pEntry);
+ void ActionInsertedTree(SvTreeListEntry* pEntry);
+ void ActionRemoving(SvTreeListEntry* pEntry);
+ void ActionClear();
+};
+
+
+SvTreeList::SvTreeList() :
+ mbEnableInvalidate(true)
+{
+ nEntryCount = 0;
+ bAbsPositionsValid = false;
+ nRefCount = 1;
+ pRootItem.reset(new SvTreeListEntry);
+ eSortMode = SortNone;
+}
+
+SvTreeList::~SvTreeList()
+{
+ Clear();
+}
+
+void SvTreeList::Broadcast(
+ SvListAction nActionId,
+ SvTreeListEntry* pEntry1,
+ SvTreeListEntry* pEntry2,
+ sal_uLong nPos
+) {
+ for (auto const& view : aViewList)
+ {
+ if(view)
+ view->ModelNotification(nActionId, pEntry1, pEntry2, nPos);
+ }
+}
+
+void SvTreeList::InsertView( SvListView* pView )
+{
+ if (std::find(aViewList.begin(), aViewList.end(), pView) != aViewList.end())
+ return;
+
+ aViewList.push_back( pView );
+ nRefCount++;
+}
+
+void SvTreeList::RemoveView( SvListView const * pView )
+{
+ auto viewFound = std::find(aViewList.begin(), aViewList.end(), pView);
+ if (viewFound != aViewList.end())
+ {
+ aViewList.erase( viewFound );
+ --nRefCount;
+ }
+}
+
+
+// an entry is visible if all parents are expanded
+bool SvTreeList::IsEntryVisible( const SvListView* pView, SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pView&&pEntry,"IsVisible:Invalid Params");
+ bool bRetVal = false;
+ do
+ {
+ if ( pEntry == pRootItem.get() )
+ {
+ bRetVal = true;
+ break;
+ }
+ pEntry = pEntry->pParent;
+ } while( pView->IsExpanded( pEntry ) );
+ return bRetVal;
+}
+
+sal_uInt16 SvTreeList::GetDepth( const SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pEntry && pEntry!=pRootItem.get(),"GetDepth:Bad Entry");
+ sal_uInt16 nDepth = 0;
+ while( pEntry->pParent != pRootItem.get() )
+ {
+ nDepth++;
+ pEntry = pEntry->pParent;
+ }
+ return nDepth;
+}
+
+bool SvTreeList::IsAtRootDepth( const SvTreeListEntry* pEntry ) const
+{
+ return pEntry->pParent == pRootItem.get();
+}
+
+void SvTreeList::Clear()
+{
+ Broadcast( SvListAction::CLEARING );
+ pRootItem->ClearChildren();
+ nEntryCount = 0;
+ Broadcast( SvListAction::CLEARED );
+}
+
+bool SvTreeList::IsChild(const SvTreeListEntry* pParent, const SvTreeListEntry* pChild) const
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ if (pParent->m_Children.empty())
+ return false;
+
+ for (auto const& it : pParent->m_Children)
+ {
+ const SvTreeListEntry* pThis = it.get();
+ if (pThis == pChild)
+ return true;
+ else
+ {
+ bool bIsChild = IsChild(pThis, pChild);
+ if (bIsChild)
+ return true;
+ }
+ }
+ return false;
+}
+
+namespace {
+
+class FindByPointer
+{
+ const SvTreeListEntry* mpEntry;
+public:
+ explicit FindByPointer(const SvTreeListEntry* p) : mpEntry(p) {}
+
+ bool operator() (std::unique_ptr<SvTreeListEntry> const& rpEntry) const
+ {
+ return mpEntry == rpEntry.get();
+ }
+};
+
+sal_uLong findEntryPosition(const SvTreeListEntries& rDst, const SvTreeListEntry* pEntry)
+{
+ SvTreeListEntries::const_iterator itPos = std::find_if(rDst.begin(), rDst.end(), FindByPointer(pEntry));
+ if (itPos == rDst.end())
+ return static_cast<sal_uLong>(~0);
+
+ return static_cast<sal_uLong>(std::distance(rDst.begin(), itPos));
+}
+
+}
+
+sal_uLong SvTreeList::Move(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uLong nListPos)
+{
+ // pDest may be 0!
+ DBG_ASSERT(pSrcEntry,"Entry?");
+ if ( !pTargetParent )
+ pTargetParent = pRootItem.get();
+ DBG_ASSERT(pSrcEntry!=pTargetParent,"Move:Source=Target");
+
+ Broadcast( SvListAction::MOVING, pSrcEntry, pTargetParent, nListPos );
+
+ if ( pSrcEntry == pTargetParent )
+ // You can't move an entry onto itself as the parent. Just return its
+ // position and bail out.
+ return pSrcEntry->GetChildListPos();
+
+ bAbsPositionsValid = false;
+
+ SvTreeListEntries& rDst = pTargetParent->m_Children;
+ SvTreeListEntries& rSrc = pSrcEntry->pParent->m_Children;
+
+ bool bSameParent = pTargetParent == pSrcEntry->pParent;
+
+ // Find the position of the entry being moved in the source container.
+ SvTreeListEntries::iterator itSrcPos = rSrc.begin(), itEnd = rSrc.end();
+ for (; itSrcPos != itEnd; ++itSrcPos)
+ {
+ const SvTreeListEntry* p = (*itSrcPos).get();
+ if (p == pSrcEntry)
+ // Found
+ break;
+ }
+
+ if (itSrcPos == itEnd)
+ {
+ OSL_FAIL("Source entry not found! This should never happen.");
+ return pSrcEntry->GetChildListPos();
+ }
+
+ if (bSameParent)
+ {
+ // Moving within the same parent.
+
+ size_t nSrcPos = std::distance(rSrc.begin(), itSrcPos);
+ if (nSrcPos == nListPos)
+ // Nothing to move here.
+ return pSrcEntry->GetChildListPos();
+
+ if (nSrcPos < nListPos)
+ // Destination position shifts left after removing the original.
+ --nListPos;
+
+ // Release the original.
+ std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos));
+ assert(pOriginal.get());
+ rSrc.erase(itSrcPos);
+
+ // Determine the insertion position.
+ SvTreeListEntries::iterator itDstPos = rSrc.end();
+ if (nListPos < rSrc.size())
+ {
+ itDstPos = rSrc.begin();
+ std::advance(itDstPos, nListPos);
+ }
+ rSrc.insert(itDstPos, std::move(pOriginal));
+ }
+ else
+ {
+ // Moving from one parent to another.
+ SvTreeListEntries::iterator itDstPos = rDst.end();
+ if (nListPos < rDst.size())
+ {
+ itDstPos = rDst.begin();
+ std::advance(itDstPos, nListPos);
+ }
+ std::unique_ptr<SvTreeListEntry> pOriginal(std::move(*itSrcPos));
+ assert(pOriginal.get());
+ rSrc.erase(itSrcPos);
+ rDst.insert(itDstPos, std::move(pOriginal));
+ }
+
+ // move parent (do this only now, because we need the parent for
+ // deleting the old child list!)
+ pSrcEntry->pParent = pTargetParent;
+
+ // correct list position in target list
+ SetListPositions(rDst);
+ if (!bSameParent)
+ SetListPositions(rSrc);
+
+ sal_uLong nRetVal = findEntryPosition(rDst, pSrcEntry);
+ OSL_ENSURE(nRetVal == pSrcEntry->GetChildListPos(), "ListPos not valid");
+ Broadcast( SvListAction::MOVED,pSrcEntry,pTargetParent,nRetVal);
+ return nRetVal;
+}
+
+sal_uLong SvTreeList::Copy(SvTreeListEntry* pSrcEntry,SvTreeListEntry* pTargetParent,sal_uLong nListPos)
+{
+ // pDest may be 0!
+ DBG_ASSERT(pSrcEntry,"Entry?");
+ if ( !pTargetParent )
+ pTargetParent = pRootItem.get();
+
+ bAbsPositionsValid = false;
+
+ sal_uLong nCloneCount = 0;
+ SvTreeListEntry* pClonedEntry = Clone( pSrcEntry, nCloneCount );
+ nEntryCount += nCloneCount;
+
+ SvTreeListEntries& rDst = pTargetParent->m_Children;
+
+ pClonedEntry->pParent = pTargetParent; // move parent
+
+ if (nListPos < rDst.size())
+ {
+ SvTreeListEntries::iterator itPos = rDst.begin(); // insertion position.
+ std::advance(itPos, nListPos);
+ rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pClonedEntry));
+ }
+ else
+ rDst.push_back(std::unique_ptr<SvTreeListEntry>(pClonedEntry));
+
+ SetListPositions(rDst); // correct list position in target list
+
+ Broadcast( SvListAction::INSERTED_TREE, pClonedEntry );
+ sal_uLong nRetVal = findEntryPosition(rDst, pClonedEntry);
+ return nRetVal;
+}
+
+void SvTreeList::Move( SvTreeListEntry* pSrcEntry, SvTreeListEntry* pDstEntry )
+{
+ SvTreeListEntry* pParent;
+ sal_uLong nPos;
+
+ if ( !pDstEntry )
+ {
+ pParent = pRootItem.get();
+ nPos = 0;
+ }
+ else
+ {
+ pParent = pDstEntry->pParent;
+ nPos = pDstEntry->GetChildListPos();
+ nPos++; // (On screen:) insert _below_ pDstEntry
+ }
+ Move( pSrcEntry, pParent, nPos );
+}
+
+void SvTreeList::InsertTree(SvTreeListEntry* pSrcEntry,
+ SvTreeListEntry* pTargetParent,sal_uLong nListPos)
+{
+ DBG_ASSERT(pSrcEntry,"InsertTree:Entry?");
+ if ( !pSrcEntry )
+ return;
+
+ if ( !pTargetParent )
+ pTargetParent = pRootItem.get();
+
+ // take sorting into account
+ GetInsertionPos( pSrcEntry, pTargetParent, nListPos );
+
+ bAbsPositionsValid = false;
+
+ pSrcEntry->pParent = pTargetParent; // move parent
+ SvTreeListEntries& rDst = pTargetParent->m_Children;
+
+ if (nListPos < rDst.size())
+ {
+ SvTreeListEntries::iterator itPos = rDst.begin();
+ std::advance(itPos, nListPos);
+ rDst.insert(itPos, std::unique_ptr<SvTreeListEntry>(pSrcEntry));
+ }
+ else
+ rDst.push_back(std::unique_ptr<SvTreeListEntry>(pSrcEntry));
+
+ SetListPositions(rDst); // correct list position in target list
+ nEntryCount += GetChildCount( pSrcEntry );
+ nEntryCount++; // the parent is new, too
+
+ Broadcast(SvListAction::INSERTED_TREE, pSrcEntry );
+}
+
+SvTreeListEntry* SvTreeList::CloneEntry( SvTreeListEntry* pSource ) const
+{
+ if( aCloneLink.IsSet() )
+ return aCloneLink.Call( pSource );
+ SvTreeListEntry* pEntry = new SvTreeListEntry;
+ pEntry->Clone(pSource);
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::Clone( SvTreeListEntry* pEntry, sal_uLong& nCloneCount ) const
+{
+ SvTreeListEntry* pClonedEntry = CloneEntry( pEntry );
+ nCloneCount = 1;
+ if (!pEntry->m_Children.empty())
+ // Clone the child entries.
+ CloneChildren(pClonedEntry->m_Children, nCloneCount, pEntry->m_Children, *pClonedEntry);
+
+ return pClonedEntry;
+}
+
+void SvTreeList::CloneChildren(
+ SvTreeListEntries& rDst, sal_uLong& rCloneCount, SvTreeListEntries& rSrc, SvTreeListEntry& rNewParent) const
+{
+ SvTreeListEntries aClone;
+ for (auto const& elem : rSrc)
+ {
+ SvTreeListEntry& rEntry = *elem;
+ std::unique_ptr<SvTreeListEntry> pNewEntry(CloneEntry(&rEntry));
+ ++rCloneCount;
+ pNewEntry->pParent = &rNewParent;
+ if (!rEntry.m_Children.empty())
+ // Clone entries recursively.
+ CloneChildren(pNewEntry->m_Children, rCloneCount, rEntry.m_Children, *pNewEntry);
+
+ aClone.push_back(std::move(pNewEntry));
+ }
+
+ rDst.swap(aClone);
+}
+
+sal_uLong SvTreeList::GetChildCount( const SvTreeListEntry* pParent ) const
+{
+ if ( !pParent )
+ return GetEntryCount();
+
+ if (pParent->m_Children.empty())
+ return 0;
+
+ sal_uLong nCount = 0;
+ sal_uInt16 nRefDepth = GetDepth( pParent );
+ sal_uInt16 nActDepth = nRefDepth;
+ do
+ {
+ pParent = Next(const_cast<SvTreeListEntry*>(pParent), &nActDepth);
+ nCount++;
+ } while( pParent && nRefDepth < nActDepth );
+ nCount--;
+ return nCount;
+}
+
+sal_uLong SvTreeList::GetVisibleChildCount(const SvListView* pView, SvTreeListEntry* pParent) const
+{
+ DBG_ASSERT(pView,"GetVisChildCount:No View");
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ if (!pParent || !pView->IsExpanded(pParent) || pParent->m_Children.empty())
+ return 0;
+
+ sal_uLong nCount = 0;
+ sal_uInt16 nRefDepth = GetDepth( pParent );
+ sal_uInt16 nActDepth = nRefDepth;
+ do
+ {
+ pParent = NextVisible( pView, pParent, &nActDepth );
+ nCount++;
+ } while( pParent && nRefDepth < nActDepth );
+ nCount--;
+ return nCount;
+}
+
+sal_uLong SvTreeList::GetChildSelectionCount(const SvListView* pView,SvTreeListEntry* pParent) const
+{
+ DBG_ASSERT(pView,"GetChildSelCount:No View");
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ if (!pParent || pParent->m_Children.empty())
+ return 0;
+
+ sal_uLong nCount = 0;
+ sal_uInt16 nRefDepth = GetDepth( pParent );
+ sal_uInt16 nActDepth = nRefDepth;
+ do
+ {
+ pParent = Next( pParent, &nActDepth );
+ if( pParent && pView->IsSelected( pParent ) && nRefDepth < nActDepth)
+ nCount++;
+ } while( pParent && nRefDepth < nActDepth );
+// nCount--;
+ return nCount;
+}
+
+SvTreeListEntry* SvTreeList::First() const
+{
+ if ( nEntryCount )
+ return pRootItem->m_Children[0].get();
+ else
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::Next( SvTreeListEntry* pActEntry, sal_uInt16* pDepth ) const
+{
+ DBG_ASSERT( pActEntry && pActEntry->pParent, "SvTreeList::Next: invalid entry/parent!" );
+ if ( !pActEntry || !pActEntry->pParent )
+ return nullptr;
+
+ sal_uInt16 nDepth = 0;
+ bool bWithDepth = false;
+ if ( pDepth )
+ {
+ nDepth = *pDepth;
+ bWithDepth = true;
+ }
+
+ // Get the list where the current entry belongs to (from its parent).
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uLong nActualPos = pActEntry->GetChildListPos();
+
+ if (!pActEntry->m_Children.empty())
+ {
+ // The current entry has children. Get its first child entry.
+ nDepth++;
+ pActEntry = pActEntry->m_Children[0].get();
+ if ( bWithDepth )
+ *pDepth = nDepth;
+ return pActEntry;
+ }
+
+ if (pActualList->size() > (nActualPos+1))
+ {
+ // Get the next sibling of the current entry.
+ pActEntry = (*pActualList)[nActualPos+1].get();
+ if ( bWithDepth )
+ *pDepth = nDepth;
+ return pActEntry;
+ }
+
+ // Move up level(s) until we find the level where the next sibling exists.
+ SvTreeListEntry* pParent = pActEntry->pParent;
+ nDepth--;
+ while( pParent != pRootItem.get() && pParent != nullptr )
+ {
+ DBG_ASSERT(pParent!=nullptr,"TreeData corrupt!");
+ pActualList = &pParent->pParent->m_Children;
+ nActualPos = pParent->GetChildListPos();
+ if (pActualList->size() > (nActualPos+1))
+ {
+ pActEntry = (*pActualList)[nActualPos+1].get();
+ if ( bWithDepth )
+ *pDepth = nDepth;
+ return pActEntry;
+ }
+ pParent = pParent->pParent;
+ nDepth--;
+ }
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::Prev( SvTreeListEntry* pActEntry ) const
+{
+ DBG_ASSERT(pActEntry!=nullptr,"Entry?");
+
+ sal_uInt16 nDepth = 0;
+
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uLong nActualPos = pActEntry->GetChildListPos();
+
+ if ( nActualPos > 0 )
+ {
+ pActEntry = (*pActualList)[nActualPos-1].get();
+ while (!pActEntry->m_Children.empty())
+ {
+ pActualList = &pActEntry->m_Children;
+ nDepth++;
+ pActEntry = pActualList->back().get();
+ }
+ return pActEntry;
+ }
+ if ( pActEntry->pParent == pRootItem.get() )
+ return nullptr;
+
+ pActEntry = pActEntry->pParent;
+
+ if ( pActEntry )
+ {
+ nDepth--;
+ return pActEntry;
+ }
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::Last() const
+{
+ SvTreeListEntries* pActList = &pRootItem->m_Children;
+ SvTreeListEntry* pEntry = nullptr;
+ while (!pActList->empty())
+ {
+ pEntry = pActList->back().get();
+ pActList = &pEntry->m_Children;
+ }
+ return pEntry;
+}
+
+sal_uLong SvTreeList::GetVisiblePos( const SvListView* pView, SvTreeListEntry const * pEntry ) const
+{
+ DBG_ASSERT(pView&&pEntry,"View/Entry?");
+
+ if (!pView->m_pImpl->m_bVisPositionsValid)
+ {
+ // to make GetVisibleCount refresh the positions
+ const_cast<SvListView*>(pView)->m_pImpl->m_nVisibleCount = 0;
+ GetVisibleCount( const_cast<SvListView*>(pView) );
+ }
+ const SvViewDataEntry* pViewData = pView->GetViewData( pEntry );
+ return pViewData->nVisPos;
+}
+
+sal_uLong SvTreeList::GetVisibleCount( SvListView* pView ) const
+{
+ assert(pView && "GetVisCount:No View");
+ if( !pView->HasViewData() )
+ return 0;
+ if (pView->m_pImpl->m_nVisibleCount)
+ return pView->m_pImpl->m_nVisibleCount;
+
+ sal_uLong nPos = 0;
+ SvTreeListEntry* pEntry = First(); // first entry is always visible
+ while ( pEntry )
+ {
+ SvViewDataEntry* pViewData = pView->GetViewData( pEntry );
+ pViewData->nVisPos = nPos;
+ nPos++;
+ pEntry = NextVisible( pView, pEntry );
+ }
+#ifdef DBG_UTIL
+ if( nPos > 10000000 )
+ {
+ OSL_FAIL("nVisibleCount bad");
+ }
+#endif
+ pView->m_pImpl->m_nVisibleCount = nPos;
+ pView->m_pImpl->m_bVisPositionsValid = true;
+ return nPos;
+}
+
+
+// For performance reasons, this function assumes that the passed entry is
+// already visible.
+SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pActEntry,sal_uInt16* pActDepth) const
+{
+ DBG_ASSERT(pView,"NextVisible:No View");
+ if ( !pActEntry )
+ return nullptr;
+
+ sal_uInt16 nDepth = 0;
+ bool bWithDepth = false;
+ if ( pActDepth )
+ {
+ nDepth = *pActDepth;
+ bWithDepth = true;
+ }
+
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uLong nActualPos = pActEntry->GetChildListPos();
+
+ if ( pView->IsExpanded(pActEntry) )
+ {
+ OSL_ENSURE(!pActEntry->m_Children.empty(), "Pass entry is supposed to have child entries.");
+
+ nDepth++;
+ pActEntry = pActEntry->m_Children[0].get();
+ if ( bWithDepth )
+ *pActDepth = nDepth;
+ return pActEntry;
+ }
+
+ nActualPos++;
+ if ( pActualList->size() > nActualPos )
+ {
+ pActEntry = (*pActualList)[nActualPos].get();
+ if ( bWithDepth )
+ *pActDepth = nDepth;
+ return pActEntry;
+ }
+
+ SvTreeListEntry* pParent = pActEntry->pParent;
+ nDepth--;
+ while( pParent != pRootItem.get() )
+ {
+ pActualList = &pParent->pParent->m_Children;
+ nActualPos = pParent->GetChildListPos();
+ nActualPos++;
+ if ( pActualList->size() > nActualPos )
+ {
+ pActEntry = (*pActualList)[nActualPos].get();
+ if ( bWithDepth )
+ *pActDepth = nDepth;
+ return pActEntry;
+ }
+ pParent = pParent->pParent;
+ nDepth--;
+ }
+ return nullptr;
+}
+
+
+// For performance reasons, this function assumes that the passed entry is
+// already visible.
+
+SvTreeListEntry* SvTreeList::PrevVisible(const SvListView* pView, SvTreeListEntry* pActEntry) const
+{
+ DBG_ASSERT(pView&&pActEntry,"PrevVis:View/Entry?");
+
+ sal_uInt16 nDepth = 0;
+
+ SvTreeListEntries* pActualList = &pActEntry->pParent->m_Children;
+ sal_uLong nActualPos = pActEntry->GetChildListPos();
+
+ if ( nActualPos > 0 )
+ {
+ pActEntry = (*pActualList)[nActualPos-1].get();
+ while( pView->IsExpanded(pActEntry) )
+ {
+ pActualList = &pActEntry->m_Children;
+ nDepth++;
+ pActEntry = pActualList->back().get();
+ }
+ return pActEntry;
+ }
+
+ if ( pActEntry->pParent == pRootItem.get() )
+ return nullptr;
+
+ pActEntry = pActEntry->pParent;
+ if ( pActEntry )
+ {
+ nDepth--;
+ return pActEntry;
+ }
+ return nullptr;
+}
+
+SvTreeListEntry* SvTreeList::LastVisible( const SvListView* pView) const
+{
+ DBG_ASSERT(pView,"LastVis:No View");
+ SvTreeListEntry* pEntry = Last();
+ while( pEntry && !IsEntryVisible( pView, pEntry ) )
+ pEntry = PrevVisible( pView, pEntry );
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::NextVisible(const SvListView* pView,SvTreeListEntry* pEntry,sal_uInt16& nDelta) const
+{
+ DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"NextVis:Wrong Prms/!Vis");
+
+ sal_uLong nVisPos = GetVisiblePos( pView, pEntry );
+ // nDelta entries existent?
+ // example: 0,1,2,3,4,5,6,7,8,9 nVisPos=5 nDelta=7
+ // nNewDelta = 10-nVisPos-1 == 4
+ if (nVisPos+nDelta >= pView->m_pImpl->m_nVisibleCount)
+ {
+ nDelta = static_cast<sal_uInt16>(pView->m_pImpl->m_nVisibleCount-nVisPos);
+ nDelta--;
+ }
+ sal_uInt16 nDeltaTmp = nDelta;
+ while( nDeltaTmp )
+ {
+ pEntry = NextVisible( pView, pEntry );
+ nDeltaTmp--;
+ DBG_ASSERT(pEntry,"Entry?");
+ }
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::PrevVisible( const SvListView* pView, SvTreeListEntry* pEntry, sal_uInt16& nDelta ) const
+{
+ DBG_ASSERT(pView&&pEntry&&IsEntryVisible(pView,pEntry),"PrevVis:Parms/!Vis");
+
+ sal_uLong nVisPos = GetVisiblePos( pView, pEntry );
+ // nDelta entries existent?
+ // example: 0,1,2,3,4,5,6,7,8,9 nVisPos=8 nDelta=20
+ // nNewDelta = nNewVisPos
+ if ( nDelta > nVisPos )
+ nDelta = static_cast<sal_uInt16>(nVisPos);
+ sal_uInt16 nDeltaTmp = nDelta;
+ while( nDeltaTmp )
+ {
+ pEntry = PrevVisible( pView, pEntry );
+ nDeltaTmp--;
+ DBG_ASSERT(pEntry,"Entry?");
+ }
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::FirstSelected( const SvListView* pView) const
+{
+ DBG_ASSERT(pView,"FirstSel:No View");
+ if( !pView )
+ return nullptr;
+ SvTreeListEntry* pActSelEntry = First();
+ while( pActSelEntry && !pView->IsSelected(pActSelEntry) )
+ pActSelEntry = NextVisible( pView, pActSelEntry );
+ return pActSelEntry;
+}
+
+
+SvTreeListEntry* SvTreeList::FirstChild( SvTreeListEntry* pParent ) const
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+ SvTreeListEntry* pResult;
+ if (!pParent->m_Children.empty())
+ pResult = pParent->m_Children[0].get();
+ else
+ pResult = nullptr;
+ return pResult;
+}
+
+SvTreeListEntry* SvTreeList::NextSelected( const SvListView* pView, SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pView&&pEntry,"NextSel:View/Entry?");
+ pEntry = Next( pEntry );
+ while( pEntry && !pView->IsSelected(pEntry) )
+ pEntry = Next( pEntry );
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::PrevSelected( const SvListView* pView, SvTreeListEntry* pEntry) const
+{
+ DBG_ASSERT(pView&&pEntry,"PrevSel:View/Entry?");
+ pEntry = Prev( pEntry );
+ while( pEntry && !pView->IsSelected(pEntry) )
+ pEntry = Prev( pEntry );
+
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::LastSelected( const SvListView* pView ) const
+{
+ DBG_ASSERT(pView,"LastSel:No View");
+ SvTreeListEntry* pEntry = Last();
+ while( pEntry && !pView->IsSelected(pEntry) )
+ pEntry = Prev( pEntry );
+ return pEntry;
+}
+
+sal_uLong SvTreeList::Insert( SvTreeListEntry* pEntry,SvTreeListEntry* pParent,sal_uLong nPos )
+{
+ DBG_ASSERT( pEntry,"Entry?");
+
+ if ( !pParent )
+ pParent = pRootItem.get();
+
+ SvTreeListEntries& rList = pParent->m_Children;
+
+ // take sorting into account
+ GetInsertionPos( pEntry, pParent, nPos );
+
+ bAbsPositionsValid = false;
+ pEntry->pParent = pParent;
+
+ if (nPos < rList.size())
+ {
+ SvTreeListEntries::iterator itPos = rList.begin();
+ std::advance(itPos, nPos);
+ rList.insert(itPos, std::unique_ptr<SvTreeListEntry>(pEntry));
+ }
+ else
+ rList.push_back(std::unique_ptr<SvTreeListEntry>(pEntry));
+
+ nEntryCount++;
+ if (nPos != TREELIST_APPEND && (nPos != (rList.size()-1)))
+ SetListPositions(rList);
+ else
+ pEntry->nListPos = rList.size()-1;
+
+ Broadcast( SvListAction::INSERTED, pEntry );
+ return nPos; // pEntry->nListPos;
+}
+
+sal_uLong SvTreeList::GetAbsPos( const SvTreeListEntry* pEntry) const
+{
+ if ( !bAbsPositionsValid )
+ const_cast<SvTreeList*>(this)->SetAbsolutePositions();
+ return pEntry->nAbsPos;
+}
+
+sal_uLong SvTreeList::GetRelPos( const SvTreeListEntry* pChild )
+{
+ return pChild->GetChildListPos();
+}
+
+void SvTreeList::SetAbsolutePositions()
+{
+ sal_uLong nPos = 0;
+ SvTreeListEntry* pEntry = First();
+ while ( pEntry )
+ {
+ pEntry->nAbsPos = nPos;
+ nPos++;
+ pEntry = Next( pEntry );
+ }
+ bAbsPositionsValid = true;
+}
+
+void SvTreeList::Expand( SvListView* pView, SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry&&pView,"Expand:View/Entry?");
+ if ( pView->IsExpanded(pEntry) )
+ return;
+
+ DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Expand: We expected to have child entries.");
+
+ SvViewDataEntry* pViewData = pView->GetViewData(pEntry);
+ pViewData->SetExpanded(true);
+ SvTreeListEntry* pParent = pEntry->pParent;
+ // if parent is visible, invalidate status data
+ if ( pView->IsExpanded( pParent ) )
+ {
+ pView->m_pImpl->m_bVisPositionsValid = false;
+ pView->m_pImpl->m_nVisibleCount = 0;
+ }
+}
+
+void SvTreeList::Collapse( SvListView* pView, SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pView&&pEntry,"Collapse:View/Entry?");
+ if ( !pView->IsExpanded(pEntry) )
+ return;
+
+ DBG_ASSERT(!pEntry->m_Children.empty(), "SvTreeList::Collapse: We expected to have child entries.");
+
+ SvViewDataEntry* pViewData = pView->GetViewData( pEntry );
+ pViewData->SetExpanded(false);
+
+ SvTreeListEntry* pParent = pEntry->pParent;
+ if ( pView->IsExpanded(pParent) )
+ {
+ pView->m_pImpl->m_nVisibleCount = 0;
+ pView->m_pImpl->m_bVisPositionsValid = false;
+ }
+}
+
+bool SvTreeList::Select( SvListView* pView, SvTreeListEntry* pEntry, bool bSelect )
+{
+ DBG_ASSERT(pView&&pEntry,"Select:View/Entry?");
+ SvViewDataEntry* pViewData = pView->GetViewData( pEntry );
+ if ( bSelect )
+ {
+ if ( pViewData->IsSelected() || !pViewData->IsSelectable() )
+ return false;
+ else
+ {
+ pViewData->SetSelected(true);
+ pView->m_pImpl->m_nSelectionCount++;
+ }
+ }
+ else
+ {
+ if ( !pViewData->IsSelected() )
+ return false;
+ else
+ {
+ pViewData->SetSelected(false);
+ pView->m_pImpl->m_nSelectionCount--;
+ }
+ }
+ return true;
+}
+
+bool SvTreeList::Remove( const SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Cannot remove root, use clear");
+
+ if( !pEntry->pParent )
+ {
+ OSL_FAIL("Removing entry not in model!");
+ // Under certain circumstances (which?), the explorer deletes entries
+ // from the view that it hasn't inserted into the view. We don't want
+ // to crash, so we catch this case here.
+ return false;
+ }
+
+ Broadcast(SvListAction::REMOVING, const_cast<SvTreeListEntry*>(pEntry));
+ sal_uLong nRemoved = 1 + GetChildCount(pEntry);
+ bAbsPositionsValid = false;
+
+ SvTreeListEntry* pParent = pEntry->pParent;
+ SvTreeListEntries& rList = pParent->m_Children;
+ bool bLastEntry = false;
+
+ // Since we need the live instance of SvTreeListEntry for broadcasting,
+ // we first need to pop it from the container, broadcast it, then delete
+ // the instance manually at the end.
+
+ std::unique_ptr<SvTreeListEntry> pEntryDeleter;
+ if ( pEntry->HasChildListPos() )
+ {
+ size_t nListPos = pEntry->GetChildListPos();
+ bLastEntry = (nListPos == (rList.size()-1));
+ SvTreeListEntries::iterator it = rList.begin();
+ std::advance(it, nListPos);
+ pEntryDeleter = std::move(*it);
+ rList.erase(it);
+ }
+ else
+ {
+ SvTreeListEntries::iterator it =
+ std::find_if(rList.begin(), rList.end(), FindByPointer(pEntry));
+ if (it != rList.end())
+ {
+ pEntryDeleter = std::move(*it);
+ rList.erase(it);
+ }
+ }
+
+ if (!rList.empty() && !bLastEntry)
+ SetListPositions(rList);
+
+ nEntryCount -= nRemoved;
+ Broadcast(SvListAction::REMOVED, const_cast<SvTreeListEntry*>(pEntry));
+
+ return true;
+}
+
+SvTreeListEntry* SvTreeList::GetEntryAtAbsPos( sal_uLong nAbsPos ) const
+{
+ SvTreeListEntry* pEntry = First();
+ while ( nAbsPos && pEntry )
+ {
+ pEntry = Next( pEntry );
+ nAbsPos--;
+ }
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeList::GetEntryAtVisPos( const SvListView* pView, sal_uLong nVisPos ) const
+{
+ DBG_ASSERT(pView,"GetEntryAtVisPos:No View");
+ SvTreeListEntry* pEntry = First();
+ while ( nVisPos && pEntry )
+ {
+ pEntry = NextVisible( pView, pEntry );
+ nVisPos--;
+ }
+ return pEntry;
+}
+
+void SvTreeList::SetListPositions( SvTreeListEntries& rEntries )
+{
+ if (rEntries.empty())
+ return;
+
+ SvTreeListEntry& rFirst = *rEntries.front();
+ if (rFirst.pParent)
+ rFirst.pParent->InvalidateChildrensListPositions();
+}
+
+void SvTreeList::EnableInvalidate( bool bEnable )
+{
+ mbEnableInvalidate = bEnable;
+}
+
+void SvTreeList::InvalidateEntry( SvTreeListEntry* pEntry )
+{
+ if (!mbEnableInvalidate)
+ return;
+
+ Broadcast( SvListAction::INVALIDATE_ENTRY, pEntry );
+}
+
+SvTreeListEntry* SvTreeList::GetRootLevelParent( SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pEntry,"GetRootLevelParent:No Entry");
+ SvTreeListEntry* pCurParent = nullptr;
+ if ( pEntry )
+ {
+ pCurParent = pEntry->pParent;
+ if ( pCurParent == pRootItem.get() )
+ return pEntry; // is its own parent
+ while( pCurParent && pCurParent->pParent != pRootItem.get() )
+ pCurParent = pCurParent->pParent;
+ }
+ return pCurParent;
+}
+
+std::pair<SvTreeListEntries::iterator, SvTreeListEntries::iterator>
+ SvTreeList::GetChildIterators(SvTreeListEntry* pParent)
+{
+ typedef std::pair<SvTreeListEntries::iterator, SvTreeListEntries::iterator> IteratorPair;
+
+ static SvTreeListEntries dummy; // prevent singular iterator asserts
+ IteratorPair aRet(dummy.begin(), dummy.end());
+
+ if (!pParent)
+ pParent = pRootItem.get();
+
+ if (pParent->m_Children.empty())
+ // This entry has no children.
+ return aRet;
+
+ aRet.first = pParent->m_Children.begin();
+ aRet.second = pParent->m_Children.end();
+
+ return aRet;
+}
+
+
+SvListView::SvListView()
+ : m_pImpl(new Impl(*this))
+ , pModel(nullptr)
+{
+}
+
+SvListView::~SvListView()
+{
+ m_pImpl->m_DataTable.clear();
+}
+
+sal_uLong SvListView::GetSelectionCount() const
+{ return m_pImpl->m_nSelectionCount; }
+
+bool SvListView::HasViewData() const
+{ return m_pImpl->m_DataTable.size() > 1; } // There's always a ROOT
+
+
+void SvListView::Impl::InitTable()
+{
+ DBG_ASSERT(m_rThis.pModel,"InitTable:No Model");
+ DBG_ASSERT(!m_nSelectionCount && !m_nVisibleCount && !m_bVisPositionsValid,
+ "InitTable: Not cleared!");
+
+ if (!m_DataTable.empty())
+ {
+ DBG_ASSERT(m_DataTable.size() == 1, "InitTable: TableCount != 1");
+ // Delete the view data allocated to the Clear in the root.
+ // Attention: The model belonging to the root entry (and thus the entry
+ // itself) might already be deleted.
+ m_DataTable.clear();
+ }
+
+ SvTreeListEntry* pEntry;
+
+ // insert root entry
+ pEntry = m_rThis.pModel->pRootItem.get();
+ std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry);
+ pViewData->SetExpanded(true);
+ m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
+ // now all the other entries
+ pEntry = m_rThis.pModel->First();
+ while( pEntry )
+ {
+ pViewData = m_rThis.CreateViewData( pEntry );
+ DBG_ASSERT(pViewData,"InitTable:No ViewData");
+ m_rThis.InitViewData( pViewData.get(), pEntry );
+ m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
+ pEntry = m_rThis.pModel->Next( pEntry );
+ }
+}
+
+std::unique_ptr<SvViewDataEntry> SvListView::CreateViewData( SvTreeListEntry* )
+{
+ return o3tl::make_unique<SvViewDataEntry>();
+}
+
+void SvListView::Clear()
+{
+ m_pImpl->m_DataTable.clear();
+ m_pImpl->m_nSelectionCount = 0;
+ m_pImpl->m_nVisibleCount = 0;
+ m_pImpl->m_bVisPositionsValid = false;
+ if( pModel )
+ {
+ // insert root entry
+ SvTreeListEntry* pEntry = pModel->pRootItem.get();
+ std::unique_ptr<SvViewDataEntry> pViewData(new SvViewDataEntry);
+ pViewData->SetExpanded(true);
+ m_pImpl->m_DataTable.insert(std::make_pair(pEntry, std::move(pViewData)));
+ }
+}
+
+void SvListView::SetModel( SvTreeList* pNewModel )
+{
+ bool bBroadcastCleared = false;
+ if ( pModel )
+ {
+ pModel->RemoveView( this );
+ bBroadcastCleared = true;
+ ModelNotification( SvListAction::CLEARING,nullptr,nullptr,0 );
+ if ( pModel->GetRefCount() == 0 )
+ delete pModel;
+ }
+ pModel = pNewModel;
+ m_pImpl->InitTable();
+ pNewModel->InsertView( this );
+ if( bBroadcastCleared )
+ ModelNotification( SvListAction::CLEARED,nullptr,nullptr,0 );
+}
+
+
+void SvListView::ModelHasCleared()
+{
+}
+
+void SvListView::ModelHasInserted( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelHasInsertedTree( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelIsMoving( SvTreeListEntry* /* pSource */ ,
+ SvTreeListEntry* /* pTargetParent */ , sal_uLong /* nPos */ )
+{
+}
+
+
+void SvListView::ModelHasMoved( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelIsRemoving( SvTreeListEntry* )
+{
+}
+
+void SvListView::ModelHasRemoved( SvTreeListEntry* )
+{
+ //WARNING WARNING WARNING
+ //The supplied pointer should have been deleted
+ //before this call. Be careful not to use it!!!
+}
+
+void SvListView::ModelHasEntryInvalidated( SvTreeListEntry*)
+{
+}
+
+void SvListView::Impl::ActionMoving( SvTreeListEntry* pEntry )
+{
+ SvTreeListEntry* pParent = pEntry->pParent;
+ DBG_ASSERT(pParent,"Model not consistent");
+ if (pParent != m_rThis.pModel->pRootItem.get() && pParent->m_Children.size() == 1)
+ {
+ SvViewDataEntry* pViewData = m_DataTable.find( pParent )->second.get();
+ pViewData->SetExpanded(false);
+ }
+ // preliminary
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+}
+
+void SvListView::Impl::ActionMoved()
+{
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+}
+
+void SvListView::Impl::ActionInserted( SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Insert:No Entry");
+ std::unique_ptr<SvViewDataEntry> pData(m_rThis.CreateViewData( pEntry ));
+ m_rThis.InitViewData( pData.get(), pEntry );
+ std::pair<SvDataTable::iterator, bool> aSuccess =
+ m_DataTable.insert(std::make_pair(pEntry, std::move(pData)));
+ DBG_ASSERT(aSuccess.second,"Entry already in View");
+ if (m_nVisibleCount && m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
+ {
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+ }
+}
+
+void SvListView::Impl::ActionInsertedTree( SvTreeListEntry* pEntry )
+{
+ if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
+ {
+ m_nVisibleCount = 0;
+ m_bVisPositionsValid = false;
+ }
+ // iterate over entry and its children
+ SvTreeListEntry* pCurEntry = pEntry;
+ sal_uInt16 nRefDepth = m_rThis.pModel->GetDepth( pCurEntry );
+ while( pCurEntry )
+ {
+ DBG_ASSERT(m_DataTable.find(pCurEntry) != m_DataTable.end(),"Entry already in Table");
+ std::unique_ptr<SvViewDataEntry> pViewData(m_rThis.CreateViewData(pCurEntry));
+ DBG_ASSERT(pViewData,"No ViewData");
+ m_rThis.InitViewData( pViewData.get(), pEntry );
+ m_DataTable.insert(std::make_pair(pCurEntry, std::move(pViewData)));
+ pCurEntry = m_rThis.pModel->Next( pCurEntry );
+ if ( pCurEntry && m_rThis.pModel->GetDepth(pCurEntry) <= nRefDepth)
+ pCurEntry = nullptr;
+ }
+}
+
+void SvListView::Impl::RemoveViewData( SvTreeListEntry* pParent )
+{
+ for (auto const& it : pParent->m_Children)
+ {
+ SvTreeListEntry& rEntry = *it;
+ m_DataTable.erase(&rEntry);
+ if (rEntry.HasChildren())
+ RemoveViewData(&rEntry);
+ }
+}
+
+
+void SvListView::Impl::ActionRemoving( SvTreeListEntry* pEntry )
+{
+ DBG_ASSERT(pEntry,"Remove:No Entry");
+
+ SvViewDataEntry* pViewData = m_DataTable.find( pEntry )->second.get();
+ sal_uLong nSelRemoved = 0;
+ if ( pViewData->IsSelected() )
+ nSelRemoved = 1 + m_rThis.pModel->GetChildSelectionCount(&m_rThis, pEntry);
+ m_nSelectionCount -= nSelRemoved;
+ sal_uLong nVisibleRemoved = 0;
+ if (m_rThis.pModel->IsEntryVisible(&m_rThis, pEntry))
+ nVisibleRemoved = 1 + m_rThis.pModel->GetVisibleChildCount(&m_rThis, pEntry);
+ if( m_nVisibleCount )
+ {
+#ifdef DBG_UTIL
+ if (m_nVisibleCount < nVisibleRemoved)
+ {
+ OSL_FAIL("nVisibleRemoved bad");
+ }
+#endif
+ m_nVisibleCount -= nVisibleRemoved;
+ }
+ m_bVisPositionsValid = false;
+
+ m_DataTable.erase(pEntry);
+ RemoveViewData( pEntry );
+
+ SvTreeListEntry* pCurEntry = pEntry->pParent;
+ if (pCurEntry && pCurEntry != m_rThis.pModel->pRootItem.get() && pCurEntry->m_Children.size() == 1)
+ {
+ pViewData = m_DataTable.find(pCurEntry)->second.get();
+ pViewData->SetExpanded(false);
+ }
+}
+
+void SvListView::Impl::ActionClear()
+{
+ m_rThis.Clear();
+}
+
+void SvListView::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1,
+ SvTreeListEntry* pEntry2, sal_uLong nPos )
+{
+ switch( nActionId )
+ {
+ case SvListAction::INSERTED:
+ m_pImpl->ActionInserted( pEntry1 );
+ ModelHasInserted( pEntry1 );
+ break;
+ case SvListAction::INSERTED_TREE:
+ m_pImpl->ActionInsertedTree( pEntry1 );
+ ModelHasInsertedTree( pEntry1 );
+ break;
+ case SvListAction::REMOVING:
+ ModelIsRemoving( pEntry1 );
+ m_pImpl->ActionRemoving( pEntry1 );
+ break;
+ case SvListAction::REMOVED:
+ ModelHasRemoved( pEntry1 );
+ break;
+ case SvListAction::MOVING:
+ ModelIsMoving( pEntry1, pEntry2, nPos );
+ m_pImpl->ActionMoving( pEntry1 );
+ break;
+ case SvListAction::MOVED:
+ m_pImpl->ActionMoved();
+ ModelHasMoved( pEntry1 );
+ break;
+ case SvListAction::CLEARING:
+ m_pImpl->ActionClear();
+ ModelHasCleared(); // sic! for compatibility reasons!
+ break;
+ case SvListAction::CLEARED:
+ break;
+ case SvListAction::INVALIDATE_ENTRY:
+ // no action for the base class
+ ModelHasEntryInvalidated( pEntry1 );
+ break;
+ case SvListAction::RESORTED:
+ m_pImpl->m_bVisPositionsValid = false;
+ break;
+ case SvListAction::RESORTING:
+ break;
+ case SvListAction::REVERSING:
+ break;
+ case SvListAction::REVERSED:
+ m_pImpl->m_bVisPositionsValid = false;
+ break;
+ default:
+ OSL_FAIL("unknown ActionId");
+ }
+}
+
+void SvListView::InitViewData( SvViewDataEntry*, SvTreeListEntry* )
+{
+}
+
+bool SvListView::IsExpanded( SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pEntry,"IsExpanded:No Entry");
+ SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(pEntry);
+ DBG_ASSERT(itr != m_pImpl->m_DataTable.end(),"Entry not in Table");
+ if (itr == m_pImpl->m_DataTable.end())
+ return false;
+ return itr->second->IsExpanded();
+}
+
+bool SvListView::IsSelected( SvTreeListEntry* pEntry ) const
+{
+ DBG_ASSERT(pEntry,"IsExpanded:No Entry");
+ SvDataTable::const_iterator itr = m_pImpl->m_DataTable.find(pEntry);
+ if (itr == m_pImpl->m_DataTable.end())
+ return false;
+ return itr->second->IsSelected();
+}
+
+void SvListView::SetEntryFocus( SvTreeListEntry* pEntry, bool bFocus )
+{
+ DBG_ASSERT(pEntry,"SetEntryFocus:No Entry");
+ SvDataTable::iterator itr = m_pImpl->m_DataTable.find(pEntry);
+ DBG_ASSERT(itr != m_pImpl->m_DataTable.end(),"Entry not in Table");
+ itr->second->SetFocus(bFocus);
+}
+
+const SvViewDataEntry* SvListView::GetViewData( const SvTreeListEntry* pEntry ) const
+{
+ SvDataTable::const_iterator itr =
+ m_pImpl->m_DataTable.find(const_cast<SvTreeListEntry*>(pEntry));
+ if (itr == m_pImpl->m_DataTable.end())
+ return nullptr;
+ return itr->second.get();
+}
+
+SvViewDataEntry* SvListView::GetViewData( SvTreeListEntry* pEntry )
+{
+ SvDataTable::iterator itr = m_pImpl->m_DataTable.find( pEntry );
+ DBG_ASSERT(itr != m_pImpl->m_DataTable.end(),"Entry not in model or wrong view");
+ return itr->second.get();
+}
+
+sal_Int32 SvTreeList::Compare(const SvTreeListEntry* pLeft, const SvTreeListEntry* pRight) const
+{
+ if( aCompareLink.IsSet())
+ {
+ SvSortData aSortData;
+ aSortData.pLeft = pLeft;
+ aSortData.pRight = pRight;
+ return aCompareLink.Call( aSortData );
+ }
+ return 0;
+}
+
+void SvTreeList::Resort()
+{
+ Broadcast( SvListAction::RESORTING );
+ bAbsPositionsValid = false;
+ ResortChildren( pRootItem.get() );
+ Broadcast( SvListAction::RESORTED );
+}
+
+namespace {
+
+class SortComparator
+{
+ SvTreeList& mrList;
+public:
+
+ explicit SortComparator( SvTreeList& rList ) : mrList(rList) {}
+
+ bool operator() (std::unique_ptr<SvTreeListEntry> const& rpLeft,
+ std::unique_ptr<SvTreeListEntry> const& rpRight) const
+ {
+ return mrList.Compare(rpLeft.get(), rpRight.get()) < 0;
+ }
+};
+
+}
+
+void SvTreeList::ResortChildren( SvTreeListEntry* pParent )
+{
+ DBG_ASSERT(pParent,"Parent not set");
+
+ if (pParent->m_Children.empty())
+ return;
+
+ SortComparator aComp(*this);
+ std::sort(pParent->m_Children.begin(), pParent->m_Children.end(), aComp);
+
+ // Recursively sort child entries.
+ for (auto const& it : pParent->m_Children)
+ {
+ SvTreeListEntry& r = *it;
+ ResortChildren(&r);
+ }
+
+ SetListPositions(pParent->m_Children); // correct list position in target list
+}
+
+void SvTreeList::Reverse()
+{
+ Broadcast(SvListAction::REVERSING);
+ bAbsPositionsValid = false;
+ ReverseChildren(pRootItem.get());
+ Broadcast(SvListAction::REVERSED);
+}
+
+void SvTreeList::ReverseChildren( SvTreeListEntry* pParent )
+{
+ DBG_ASSERT(pParent,"Parent not set");
+
+ if (pParent->m_Children.empty())
+ return;
+
+ std::reverse(pParent->m_Children.begin(), pParent->m_Children.end());
+ // Recursively sort child entries.
+ for (auto const& it : pParent->m_Children)
+ {
+ SvTreeListEntry& r = *it;
+ ReverseChildren(&r);
+ }
+
+ SetListPositions(pParent->m_Children); // correct list position in target list
+}
+
+void SvTreeList::GetInsertionPos( SvTreeListEntry const * pEntry, SvTreeListEntry* pParent,
+ sal_uLong& rPos )
+{
+ DBG_ASSERT(pEntry,"No Entry");
+
+ if( eSortMode == SortNone )
+ return;
+
+ rPos = TREELIST_ENTRY_NOTFOUND;
+ const SvTreeListEntries& rChildList = GetChildList(pParent);
+
+ if (rChildList.empty())
+ return;
+
+ long i = 0;
+ long j = rChildList.size()-1;
+ long k;
+ sal_Int32 nCompare = 1;
+
+ do
+ {
+ k = (i+j)/2;
+ const SvTreeListEntry* pTempEntry = rChildList[k].get();
+ nCompare = Compare( pEntry, pTempEntry );
+ if( eSortMode == SortDescending && nCompare != 0 )
+ {
+ if( nCompare < 0 )
+ nCompare = 1;
+ else
+ nCompare = -1;
+ }
+ if( nCompare > 0 )
+ i = k + 1;
+ else
+ j = k - 1;
+ } while( (nCompare != 0) && (i <= j) );
+
+ if( nCompare != 0 )
+ {
+ if (i > static_cast<long>(rChildList.size()-1)) // not found, end of list
+ rPos = TREELIST_ENTRY_NOTFOUND;
+ else
+ rPos = i; // not found, middle of list
+ }
+ else
+ rPos = k;
+}
+
+bool SvTreeList::HasChildren( const SvTreeListEntry* pEntry ) const
+{
+ if ( !pEntry )
+ pEntry = pRootItem.get();
+
+ return !pEntry->m_Children.empty();
+}
+
+bool SvTreeList::HasParent( const SvTreeListEntry* pEntry ) const
+{
+ return pEntry->pParent != pRootItem.get();
+}
+
+SvTreeListEntry* SvTreeList::GetEntry( SvTreeListEntry* pParent, sal_uLong nPos ) const
+{ if ( !pParent )
+ pParent = pRootItem.get();
+ SvTreeListEntry* pRet = nullptr;
+ if (nPos < pParent->m_Children.size())
+ pRet = pParent->m_Children[nPos].get();
+ return pRet;
+}
+
+SvTreeListEntry* SvTreeList::GetEntry( sal_uLong nRootPos ) const
+{
+ SvTreeListEntry* pRet = nullptr;
+ if (nEntryCount && nRootPos < pRootItem->m_Children.size())
+ pRet = pRootItem->m_Children[nRootPos].get();
+ return pRet;
+}
+
+const SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent ) const
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+ return pParent->m_Children;
+}
+
+SvTreeListEntries& SvTreeList::GetChildList( SvTreeListEntry* pParent )
+{
+ if ( !pParent )
+ pParent = pRootItem.get();
+ return pParent->m_Children;
+}
+
+const SvTreeListEntry* SvTreeList::GetParent( const SvTreeListEntry* pEntry ) const
+{
+ const SvTreeListEntry* pParent = pEntry->pParent;
+ if (pParent == pRootItem.get())
+ pParent = nullptr;
+ return pParent;
+}
+
+SvTreeListEntry* SvTreeList::GetParent( SvTreeListEntry* pEntry )
+{
+ SvTreeListEntry* pParent = pEntry->pParent;
+ if (pParent == pRootItem.get())
+ pParent = nullptr;
+ return pParent;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/treelistbox.cxx b/vcl/source/treelist/treelistbox.cxx
new file mode 100644
index 000000000000..a1f580e66a64
--- /dev/null
+++ b/vcl/source/treelist/treelistbox.cxx
@@ -0,0 +1,3666 @@
+/* -*- 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 .
+ */
+
+/*
+ TODO:
+ - delete anchor in SelectionEngine when selecting manually
+ - SelectAll( false ) => only repaint the deselected entries
+*/
+
+#include <vcl/treelistbox.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/accel.hxx>
+#include <vcl/i18nhelp.hxx>
+#include <vcl/builderfactory.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/uitest/uiobject.hxx>
+#include <sot/formats.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <rtl/instance.hxx>
+#include <comphelper/string.hxx>
+#include <sal/log.hxx>
+
+#include <vcl/svlbitm.hxx>
+#include <vcl/treelistentry.hxx>
+#include <vcl/viewdataentry.hxx>
+#include <vcl/svimpbox.hxx>
+
+#include <set>
+#include <string.h>
+#include <vector>
+#include <o3tl/make_unique.hxx>
+using namespace css::accessibility;
+
+// Drag&Drop
+static VclPtr<SvTreeListBox> g_pDDSource;
+static VclPtr<SvTreeListBox> g_pDDTarget;
+
+#define SVLBOX_ACC_RETURN 1
+#define SVLBOX_ACC_ESCAPE 2
+
+// ***************************************************************
+
+class MyEdit_Impl : public Edit
+{
+ SvInplaceEdit2* pOwner;
+public:
+ MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* pOwner );
+ virtual ~MyEdit_Impl() override { disposeOnce(); }
+ virtual void dispose() override { pOwner = nullptr; Edit::dispose(); }
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+ virtual void LoseFocus() override;
+};
+
+MyEdit_Impl::MyEdit_Impl( vcl::Window* pParent, SvInplaceEdit2* _pOwner ) :
+
+ Edit( pParent, WB_LEFT ),
+
+ pOwner( _pOwner )
+
+{
+}
+
+void MyEdit_Impl::KeyInput( const KeyEvent& rKEvt )
+{
+ if( !pOwner->KeyInput( rKEvt ))
+ Edit::KeyInput( rKEvt );
+}
+
+void MyEdit_Impl::LoseFocus()
+{
+ if (pOwner)
+ pOwner->LoseFocus();
+}
+
+SvInplaceEdit2::SvInplaceEdit2
+(
+ vcl::Window* pParent, const Point& rPos,
+ const Size& rSize,
+ const OUString& rData,
+ const Link<SvInplaceEdit2&,void>& rNotifyEditEnd,
+ const Selection& rSelection
+) :
+
+ aCallBackHdl ( rNotifyEditEnd ),
+ bCanceled ( false ),
+ bAlreadyInCallBack ( false )
+
+{
+
+ pEdit = VclPtr<MyEdit_Impl>::Create( pParent, this );
+
+ vcl::Font aFont( pParent->GetFont() );
+ aFont.SetTransparent( false );
+ Color aColor( pParent->GetBackground().GetColor() );
+ aFont.SetFillColor(aColor );
+ pEdit->SetFont( aFont );
+ pEdit->SetBackground( pParent->GetBackground() );
+ pEdit->SetPosPixel( rPos );
+ pEdit->SetSizePixel( rSize );
+ pEdit->SetText( rData );
+ pEdit->SetSelection( rSelection );
+ pEdit->SaveValue();
+
+ aAccReturn.InsertItem( SVLBOX_ACC_RETURN, vcl::KeyCode(KEY_RETURN) );
+ aAccEscape.InsertItem( SVLBOX_ACC_ESCAPE, vcl::KeyCode(KEY_ESCAPE) );
+
+ aAccReturn.SetActivateHdl( LINK( this, SvInplaceEdit2, ReturnHdl_Impl) );
+ aAccEscape.SetActivateHdl( LINK( this, SvInplaceEdit2, EscapeHdl_Impl) );
+ Application::InsertAccel( &aAccReturn );
+ Application::InsertAccel( &aAccEscape );
+
+ pEdit->Show();
+ pEdit->GrabFocus();
+}
+
+SvInplaceEdit2::~SvInplaceEdit2()
+{
+ if( !bAlreadyInCallBack )
+ {
+ Application::RemoveAccel( &aAccReturn );
+ Application::RemoveAccel( &aAccEscape );
+ }
+ pEdit.disposeAndClear();
+}
+
+OUString const & SvInplaceEdit2::GetSavedValue() const
+{
+ return pEdit->GetSavedValue();
+}
+
+void SvInplaceEdit2::Hide()
+{
+ pEdit->Hide();
+}
+
+
+IMPL_LINK_NOARG(SvInplaceEdit2, ReturnHdl_Impl, Accelerator&, void)
+{
+ bCanceled = false;
+ CallCallBackHdl_Impl();
+}
+
+IMPL_LINK_NOARG(SvInplaceEdit2, EscapeHdl_Impl, Accelerator&, void)
+{
+ bCanceled = true;
+ CallCallBackHdl_Impl();
+}
+
+bool SvInplaceEdit2::KeyInput( const KeyEvent& rKEvt )
+{
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = aCode.GetCode();
+
+ switch ( nCode )
+ {
+ case KEY_ESCAPE:
+ bCanceled = true;
+ CallCallBackHdl_Impl();
+ return true;
+
+ case KEY_RETURN:
+ bCanceled = false;
+ CallCallBackHdl_Impl();
+ return true;
+ }
+ return false;
+}
+
+void SvInplaceEdit2::StopEditing( bool bCancel )
+{
+ if ( !bAlreadyInCallBack )
+ {
+ bCanceled = bCancel;
+ CallCallBackHdl_Impl();
+ }
+}
+
+void SvInplaceEdit2::LoseFocus()
+{
+ if ( !bAlreadyInCallBack
+ && ((!Application::GetFocusWindow()) || !pEdit->IsChild( Application::GetFocusWindow()) )
+ )
+ {
+ bCanceled = false;
+ aIdle.SetPriority(TaskPriority::REPAINT);
+ aIdle.SetInvokeHandler(LINK(this,SvInplaceEdit2,Timeout_Impl));
+ aIdle.SetDebugName( "svtools::SvInplaceEdit2 aIdle" );
+ aIdle.Start();
+ }
+}
+
+IMPL_LINK_NOARG(SvInplaceEdit2, Timeout_Impl, Timer *, void)
+{
+ CallCallBackHdl_Impl();
+}
+
+void SvInplaceEdit2::CallCallBackHdl_Impl()
+{
+ aIdle.Stop();
+ if ( !bAlreadyInCallBack )
+ {
+ bAlreadyInCallBack = true;
+ Application::RemoveAccel( &aAccReturn );
+ Application::RemoveAccel( &aAccEscape );
+ pEdit->Hide();
+ aCallBackHdl.Call( *this );
+ }
+}
+
+OUString SvInplaceEdit2::GetText() const
+{
+ return pEdit->GetText();
+}
+
+// ***************************************************************
+// class SvLBoxTab
+// ***************************************************************
+
+
+SvLBoxTab::SvLBoxTab()
+{
+ nPos = 0;
+ nFlags = SvLBoxTabFlags::NONE;
+}
+
+SvLBoxTab::SvLBoxTab( long nPosition, SvLBoxTabFlags nTabFlags )
+{
+ nPos = nPosition;
+ nFlags = nTabFlags;
+}
+
+SvLBoxTab::SvLBoxTab( const SvLBoxTab& rTab )
+{
+ nPos = rTab.nPos;
+ nFlags = rTab.nFlags;
+}
+
+SvLBoxTab::~SvLBoxTab()
+{
+}
+
+
+long SvLBoxTab::CalcOffset( long nItemWidth, long nTabWidth )
+{
+ long nOffset = 0;
+ if ( nFlags & SvLBoxTabFlags::ADJUST_RIGHT )
+ {
+ nOffset = nTabWidth - nItemWidth;
+ if( nOffset < 0 )
+ nOffset = 0;
+ }
+ else if ( nFlags & SvLBoxTabFlags::ADJUST_CENTER )
+ {
+ if( nFlags & SvLBoxTabFlags::FORCE )
+ {
+ // correct implementation of centering
+ nOffset = ( nTabWidth - nItemWidth ) / 2;
+ if( nOffset < 0 )
+ nOffset = 0;
+ }
+ else
+ {
+ // historically grown, wrong calculation of tabs which is needed by
+ // Abo-Tabbox, Tools/Options/Customize etc.
+ nItemWidth++;
+ nOffset = -( nItemWidth / 2 );
+ }
+ }
+ return nOffset;
+}
+
+// ***************************************************************
+// class SvLBoxItem
+// ***************************************************************
+
+
+SvLBoxItem::SvLBoxItem()
+{
+}
+
+SvLBoxItem::~SvLBoxItem()
+{
+}
+
+const Size& SvLBoxItem::GetSize(const SvTreeListBox* pView, const SvTreeListEntry* pEntry) const
+{
+ const SvViewDataItem* pViewData = pView->GetViewDataItem( pEntry, this );
+ return pViewData->maSize;
+}
+
+const Size& SvLBoxItem::GetSize(const SvViewDataEntry* pData, sal_uInt16 nItemPos)
+{
+ const SvViewDataItem& rIData = pData->GetItem(nItemPos);
+ return rIData.maSize;
+}
+
+struct SvTreeListBoxImpl
+{
+ bool m_bIsEmptyTextAllowed:1;
+ bool m_bEntryMnemonicsEnabled:1;
+ bool m_bDoingQuickSelection:1;
+
+ vcl::MnemonicEngine m_aMnemonicEngine;
+ vcl::QuickSelectionEngine m_aQuickSelectionEngine;
+
+ explicit SvTreeListBoxImpl(SvTreeListBox& _rBox) :
+ m_bIsEmptyTextAllowed(true),
+ m_bEntryMnemonicsEnabled(false),
+ m_bDoingQuickSelection(false),
+ m_aMnemonicEngine(_rBox),
+ m_aQuickSelectionEngine(_rBox) {}
+};
+
+
+SvTreeListBox::SvTreeListBox(vcl::Window* pParent, WinBits nWinStyle) :
+ Control(pParent, nWinStyle | WB_CLIPCHILDREN),
+ DropTargetHelper(this),
+ DragSourceHelper(this),
+ mpImpl(new SvTreeListBoxImpl(*this)),
+ mbContextBmpExpanded(false),
+ mbAlternatingRowColors(false),
+ mbUpdateAlternatingRows(false),
+ mbQuickSearch(false),
+ eSelMode(SelectionMode::NONE),
+ nMinWidthInChars(0),
+ mbCenterAndClipText(false)
+{
+ nDragOptions = DND_ACTION_COPYMOVE | DND_ACTION_LINK;
+ nImpFlags = SvTreeListBoxFlags::NONE;
+ pTargetEntry = nullptr;
+ nDragDropMode = DragDropMode::NONE;
+ SvTreeList* pTempModel = new SvTreeList;
+ pTempModel->SetRefCount( 0 );
+ SetBaseModel(pTempModel);
+ pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
+ pModel->InsertView( this );
+ pHdlEntry = nullptr;
+ eSelMode = SelectionMode::Single;
+ nDragDropMode = DragDropMode::NONE;
+ SetType(WindowType::TREELISTBOX);
+
+ InitTreeView();
+
+ SetSublistOpenWithLeftRight();
+}
+
+VCL_BUILDER_FACTORY_CONSTRUCTOR(SvTreeListBox, WB_TABSTOP)
+
+void SvTreeListBox::Clear()
+{
+ if (pModel)
+ pModel->Clear(); // Model calls SvTreeListBox::ModelHasCleared()
+}
+
+void SvTreeListBox::EnableEntryMnemonics()
+{
+ if ( IsEntryMnemonicsEnabled() )
+ return;
+
+ mpImpl->m_bEntryMnemonicsEnabled = true;
+ Invalidate();
+}
+
+bool SvTreeListBox::IsEntryMnemonicsEnabled() const
+{
+ return mpImpl->m_bEntryMnemonicsEnabled;
+}
+
+IMPL_LINK( SvTreeListBox, CloneHdl_Impl, SvTreeListEntry*, pEntry, SvTreeListEntry* )
+{
+ return CloneEntry(pEntry);
+}
+
+sal_uLong SvTreeListBox::Insert( SvTreeListEntry* pEntry, SvTreeListEntry* pParent, sal_uLong nPos )
+{
+ sal_uLong nInsPos = pModel->Insert( pEntry, pParent, nPos );
+ pEntry->SetBackColor( GetBackground().GetColor() );
+ SetAlternatingRowColors( mbAlternatingRowColors );
+ return nInsPos;
+}
+
+sal_uLong SvTreeListBox::Insert( SvTreeListEntry* pEntry,sal_uLong nRootPos )
+{
+ sal_uLong nInsPos = pModel->Insert( pEntry, nRootPos );
+ pEntry->SetBackColor( GetBackground().GetColor() );
+ SetAlternatingRowColors( mbAlternatingRowColors );
+ return nInsPos;
+}
+
+bool SvTreeListBox::ExpandingHdl()
+{
+ return !aExpandingHdl.IsSet() || aExpandingHdl.Call( this );
+}
+
+void SvTreeListBox::ExpandedHdl()
+{
+ aExpandedHdl.Call( this );
+}
+
+void SvTreeListBox::SelectHdl()
+{
+ aSelectHdl.Call( this );
+}
+
+void SvTreeListBox::DeselectHdl()
+{
+ aDeselectHdl.Call( this );
+}
+
+bool SvTreeListBox::DoubleClickHdl()
+{
+ return !aDoubleClickHdl.IsSet() || aDoubleClickHdl.Call(this);
+}
+
+
+bool SvTreeListBox::CheckDragAndDropMode( SvTreeListBox const * pSource, sal_Int8 nAction )
+{
+ if ( pSource == this )
+ {
+ if ( !(nDragDropMode & (DragDropMode::CTRL_MOVE | DragDropMode::CTRL_COPY) ) )
+ return false; // D&D locked within list
+ if( DND_ACTION_MOVE == nAction )
+ {
+ if ( !(nDragDropMode & DragDropMode::CTRL_MOVE) )
+ return false; // no local move
+ }
+ else
+ {
+ if ( !(nDragDropMode & DragDropMode::CTRL_COPY))
+ return false; // no local copy
+ }
+ }
+ else
+ {
+ if ( !(nDragDropMode & DragDropMode::APP_DROP ) )
+ return false; // no drop
+ if ( DND_ACTION_MOVE == nAction )
+ {
+ if ( !(nDragDropMode & DragDropMode::APP_MOVE) )
+ return false; // no global move
+ }
+ else
+ {
+ if ( !(nDragDropMode & DragDropMode::APP_COPY))
+ return false; // no global copy
+ }
+ }
+ return true;
+}
+
+
+/*
+ NotifyMoving/Copying
+ ====================
+
+ default behavior:
+
+ 1. target doesn't have children
+ - entry becomes sibling of target. entry comes after target
+ (->Window: below the target)
+ 2. target is an expanded parent
+ - entry inserted at the beginning of the target childlist
+ 3. target is a collapsed parent
+ - entry is inserted at the end of the target childlist
+*/
+TriState SvTreeListBox::NotifyMoving(
+ SvTreeListEntry* pTarget, // D&D dropping position in GetModel()
+ SvTreeListEntry* pEntry, // entry that we want to move, from
+ // GetSourceListBox()->GetModel()
+ SvTreeListEntry*& rpNewParent, // new target parent
+ sal_uLong& rNewChildPos) // position in childlist of target parent
+{
+ DBG_ASSERT(pEntry,"NotifyMoving:SourceEntry?");
+ if( !pTarget )
+ {
+ rpNewParent = nullptr;
+ rNewChildPos = 0;
+ return TRISTATE_TRUE;
+ }
+ if ( !pTarget->HasChildren() && !pTarget->HasChildrenOnDemand() )
+ {
+ // case 1
+ rpNewParent = GetParent( pTarget );
+ rNewChildPos = SvTreeList::GetRelPos( pTarget ) + 1;
+ rNewChildPos += nCurEntrySelPos;
+ nCurEntrySelPos++;
+ }
+ else
+ {
+ // cases 2 & 3
+ rpNewParent = pTarget;
+ if( IsExpanded(pTarget))
+ rNewChildPos = 0;
+ else
+ rNewChildPos = TREELIST_APPEND;
+ }
+ return TRISTATE_TRUE;
+}
+
+TriState SvTreeListBox::NotifyCopying(
+ SvTreeListEntry* pTarget, // D&D dropping position in GetModel()
+ SvTreeListEntry* pEntry, // entry that we want to move, from
+ // GetSourceListBox()->GetModel()
+ SvTreeListEntry*& rpNewParent, // new target parent
+ sal_uLong& rNewChildPos) // position in childlist of target parent
+{
+ return NotifyMoving(pTarget,pEntry,rpNewParent,rNewChildPos);
+}
+
+SvTreeListEntry* SvTreeListBox::FirstChild( SvTreeListEntry* pParent ) const
+{
+ return pModel->FirstChild(pParent);
+}
+
+// return: all entries copied
+bool SvTreeListBox::CopySelection( SvTreeListBox* pSource, SvTreeListEntry* pTarget )
+{
+ nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying
+ bool bSuccess = true;
+ std::vector<SvTreeListEntry*> aList;
+ bool bClone = ( pSource->GetModel() != GetModel() );
+ Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() );
+ pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
+
+ // cache selection to simplify iterating over the selection when doing a D&D
+ // exchange within the same listbox
+ SvTreeListEntry* pSourceEntry = pSource->FirstSelected();
+ while ( pSourceEntry )
+ {
+ // children are copied automatically
+ pSource->SelectChildren( pSourceEntry, false );
+ aList.push_back( pSourceEntry );
+ pSourceEntry = pSource->NextSelected( pSourceEntry );
+ }
+
+ for (auto const& elem : aList)
+ {
+ pSourceEntry = elem;
+ SvTreeListEntry* pNewParent = nullptr;
+ sal_uLong nInsertionPos = TREELIST_APPEND;
+ TriState nOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos);
+ if ( nOk )
+ {
+ if ( bClone )
+ {
+ sal_uLong nCloneCount = 0;
+ pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount);
+ pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos);
+ }
+ else
+ {
+ sal_uLong nListPos = pModel->Copy(pSourceEntry, pNewParent, nInsertionPos);
+ pSourceEntry = GetEntry( pNewParent, nListPos );
+ }
+ }
+ else
+ bSuccess = false;
+
+ if (nOk == TRISTATE_INDET) // HACK: make visible moved entry
+ MakeVisible( pSourceEntry );
+ }
+ pModel->SetCloneLink( aCloneLink );
+ return bSuccess;
+}
+
+// return: all entries were moved
+bool SvTreeListBox::MoveSelectionCopyFallbackPossible( SvTreeListBox* pSource, SvTreeListEntry* pTarget, bool bAllowCopyFallback )
+{
+ nCurEntrySelPos = 0; // selection counter for NotifyMoving/Copying
+ bool bSuccess = true;
+ std::vector<SvTreeListEntry*> aList;
+ bool bClone = ( pSource->GetModel() != GetModel() );
+ Link<SvTreeListEntry*,SvTreeListEntry*> aCloneLink( pModel->GetCloneLink() );
+ if ( bClone )
+ pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
+
+ SvTreeListEntry* pSourceEntry = pSource->FirstSelected();
+ while ( pSourceEntry )
+ {
+ // children are automatically moved
+ pSource->SelectChildren( pSourceEntry, false );
+ aList.push_back( pSourceEntry );
+ pSourceEntry = pSource->NextSelected( pSourceEntry );
+ }
+
+ for (auto const& elem : aList)
+ {
+ pSourceEntry = elem;
+ SvTreeListEntry* pNewParent = nullptr;
+ sal_uLong nInsertionPos = TREELIST_APPEND;
+ TriState nOk = NotifyMoving(pTarget,pSourceEntry,pNewParent,nInsertionPos);
+ TriState nCopyOk = nOk;
+ if ( !nOk && bAllowCopyFallback )
+ {
+ nInsertionPos = TREELIST_APPEND;
+ nCopyOk = NotifyCopying(pTarget,pSourceEntry,pNewParent,nInsertionPos);
+ }
+
+ if ( nOk || nCopyOk )
+ {
+ if ( bClone )
+ {
+ sal_uLong nCloneCount = 0;
+ pSourceEntry = pModel->Clone(pSourceEntry, nCloneCount);
+ pModel->InsertTree(pSourceEntry, pNewParent, nInsertionPos);
+ }
+ else
+ {
+ if ( nOk )
+ pModel->Move(pSourceEntry, pNewParent, nInsertionPos);
+ else
+ pModel->Copy(pSourceEntry, pNewParent, nInsertionPos);
+ }
+ }
+ else
+ bSuccess = false;
+
+ if (nOk == TRISTATE_INDET) // HACK: make moved entry visible
+ MakeVisible( pSourceEntry );
+ }
+ pModel->SetCloneLink( aCloneLink );
+ return bSuccess;
+}
+
+void SvTreeListBox::RemoveSelection()
+{
+ std::vector<const SvTreeListEntry*> aList;
+ // cache selection, as the implementation deselects everything on the first
+ // remove
+ SvTreeListEntry* pEntry = FirstSelected();
+ while ( pEntry )
+ {
+ aList.push_back( pEntry );
+ if ( pEntry->HasChildren() )
+ // remove deletes all children automatically
+ SelectChildren(pEntry, false);
+ pEntry = NextSelected( pEntry );
+ }
+
+ for (auto const& elem : aList)
+ pModel->Remove(elem);
+}
+
+void SvTreeListBox::RemoveEntry(SvTreeListEntry const * pEntry)
+{
+ pModel->Remove(pEntry);
+}
+
+void SvTreeListBox::RecalcViewData()
+{
+ SvTreeListEntry* pEntry = First();
+ while( pEntry )
+ {
+ sal_uInt16 nCount = pEntry->ItemCount();
+ sal_uInt16 nCurPos = 0;
+ while ( nCurPos < nCount )
+ {
+ SvLBoxItem& rItem = pEntry->GetItem( nCurPos );
+ rItem.InitViewData( this, pEntry );
+ nCurPos++;
+ }
+ pEntry = Next( pEntry );
+ }
+}
+
+void SvTreeListBox::ImplShowTargetEmphasis( SvTreeListEntry* pEntry, bool bShow)
+{
+ if ( bShow && (nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
+ return;
+ if ( !bShow && !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
+ return;
+ pImpl->PaintDDCursor( pEntry );
+ if( bShow )
+ nImpFlags |= SvTreeListBoxFlags::TARGEMPH_VIS;
+ else
+ nImpFlags &= ~SvTreeListBoxFlags::TARGEMPH_VIS;
+}
+
+void SvTreeListBox::OnCurrentEntryChanged()
+{
+ if ( !mpImpl->m_bDoingQuickSelection )
+ mpImpl->m_aQuickSelectionEngine.Reset();
+}
+
+SvTreeListEntry* SvTreeListBox::GetEntry( SvTreeListEntry* pParent, sal_uLong nPos ) const
+{
+ return pModel->GetEntry(pParent, nPos);
+}
+
+SvTreeListEntry* SvTreeListBox::GetEntry( sal_uLong nRootPos ) const
+{
+ return pModel->GetEntry(nRootPos);
+}
+
+SvTreeListEntry* SvTreeListBox::GetEntryFromPath( const ::std::deque< sal_Int32 >& _rPath ) const
+{
+
+ SvTreeListEntry* pEntry = nullptr;
+ SvTreeListEntry* pParent = nullptr;
+ for (auto const& elem : _rPath)
+ {
+ pEntry = GetEntry( pParent, elem );
+ if ( !pEntry )
+ break;
+ pParent = pEntry;
+ }
+
+ return pEntry;
+}
+
+void SvTreeListBox::FillEntryPath( SvTreeListEntry* pEntry, ::std::deque< sal_Int32 >& _rPath ) const
+{
+
+ if ( !pEntry )
+ return;
+
+ SvTreeListEntry* pParentEntry = GetParent( pEntry );
+ while ( true )
+ {
+ sal_uLong i, nCount = GetLevelChildCount( pParentEntry );
+ for ( i = 0; i < nCount; ++i )
+ {
+ SvTreeListEntry* pTemp = GetEntry( pParentEntry, i );
+ DBG_ASSERT( pEntry, "invalid entry" );
+ if ( pEntry == pTemp )
+ {
+ _rPath.push_front( static_cast<sal_Int32>(i) );
+ break;
+ }
+ }
+
+ if ( pParentEntry )
+ {
+ pEntry = pParentEntry;
+ pParentEntry = GetParent( pParentEntry );
+ }
+ else
+ break;
+ }
+}
+
+const SvTreeListEntry* SvTreeListBox::GetParent( const SvTreeListEntry* pEntry ) const
+{
+ return pModel->GetParent(pEntry);
+}
+
+SvTreeListEntry* SvTreeListBox::GetParent( SvTreeListEntry* pEntry ) const
+{
+ return pModel->GetParent(pEntry);
+}
+
+SvTreeListEntry* SvTreeListBox::GetRootLevelParent( SvTreeListEntry* pEntry ) const
+{
+ return pModel->GetRootLevelParent(pEntry);
+}
+
+sal_uLong SvTreeListBox::GetChildCount( SvTreeListEntry const * pParent ) const
+{
+ return pModel->GetChildCount(pParent);
+}
+
+sal_uLong SvTreeListBox::GetLevelChildCount( SvTreeListEntry* _pParent ) const
+{
+
+ //if _pParent is 0, then pEntry is the first child of the root.
+ SvTreeListEntry* pEntry = FirstChild( _pParent );
+
+ if( !pEntry )//there is only root, root don't have children
+ return 0;
+
+ if( !_pParent )//root and children of root
+ return pEntry->pParent->m_Children.size();
+
+ return _pParent->m_Children.size();
+}
+
+SvViewDataEntry* SvTreeListBox::GetViewDataEntry( SvTreeListEntry const * pEntry ) const
+{
+ return const_cast<SvViewDataEntry*>(SvListView::GetViewData(pEntry));
+}
+
+SvViewDataItem* SvTreeListBox::GetViewDataItem(SvTreeListEntry const * pEntry, SvLBoxItem const * pItem)
+{
+ return const_cast<SvViewDataItem*>(static_cast<const SvTreeListBox*>(this)->GetViewDataItem(pEntry, pItem));
+}
+
+const SvViewDataItem* SvTreeListBox::GetViewDataItem(const SvTreeListEntry* pEntry, const SvLBoxItem* pItem) const
+{
+ const SvViewDataEntry* pEntryData = SvListView::GetViewData(pEntry);
+ assert(pEntryData && "Entry not in View");
+ sal_uInt16 nItemPos = pEntry->GetPos(pItem);
+ return &pEntryData->GetItem(nItemPos);
+}
+
+std::unique_ptr<SvViewDataEntry> SvTreeListBox::CreateViewData( SvTreeListEntry* )
+{
+ return o3tl::make_unique<SvViewDataEntry>();
+}
+
+void SvTreeListBox::InitViewData( SvViewDataEntry* pData, SvTreeListEntry* pEntry )
+{
+ SvTreeListEntry* pInhEntry = pEntry;
+ SvViewDataEntry* pEntryData = pData;
+
+ pEntryData->Init(pInhEntry->ItemCount());
+ sal_uInt16 nCount = pInhEntry->ItemCount();
+ sal_uInt16 nCurPos = 0;
+ while( nCurPos < nCount )
+ {
+ SvLBoxItem& rItem = pInhEntry->GetItem( nCurPos );
+ SvViewDataItem& rItemData = pEntryData->GetItem(nCurPos);
+ rItem.InitViewData( this, pInhEntry, &rItemData );
+ nCurPos++;
+ }
+}
+
+void SvTreeListBox::EnableSelectionAsDropTarget( bool bEnable )
+{
+ sal_uInt16 nRefDepth;
+ SvTreeListEntry* pTemp;
+
+ SvTreeListEntry* pSelEntry = FirstSelected();
+ while( pSelEntry )
+ {
+ if ( !bEnable )
+ {
+ pSelEntry->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP;
+ nRefDepth = pModel->GetDepth( pSelEntry );
+ pTemp = Next( pSelEntry );
+ while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth )
+ {
+ pTemp->nEntryFlags |= SvTLEntryFlags::DISABLE_DROP;
+ pTemp = Next( pTemp );
+ }
+ }
+ else
+ {
+ pSelEntry->nEntryFlags &= (~SvTLEntryFlags::DISABLE_DROP);
+ nRefDepth = pModel->GetDepth( pSelEntry );
+ pTemp = Next( pSelEntry );
+ while( pTemp && pModel->GetDepth( pTemp ) > nRefDepth )
+ {
+ pTemp->nEntryFlags &= (~SvTLEntryFlags::DISABLE_DROP);
+ pTemp = Next( pTemp );
+ }
+ }
+ pSelEntry = NextSelected( pSelEntry );
+ }
+}
+
+// ******************************************************************
+// InplaceEditing
+// ******************************************************************
+
+void SvTreeListBox::EditText( const OUString& rStr, const tools::Rectangle& rRect,
+ const Selection& rSel )
+{
+ pEdCtrl.reset();
+ nImpFlags |= SvTreeListBoxFlags::IN_EDT;
+ nImpFlags &= ~SvTreeListBoxFlags::EDTEND_CALLED;
+ HideFocus();
+ pEdCtrl.reset( new SvInplaceEdit2(
+ this, rRect.TopLeft(), rRect.GetSize(), rStr,
+ LINK( this, SvTreeListBox, TextEditEndedHdl_Impl ),
+ rSel ) );
+}
+
+IMPL_LINK_NOARG(SvTreeListBox, TextEditEndedHdl_Impl, SvInplaceEdit2&, void)
+{
+ if ( nImpFlags & SvTreeListBoxFlags::EDTEND_CALLED ) // avoid nesting
+ return;
+ nImpFlags |= SvTreeListBoxFlags::EDTEND_CALLED;
+ OUString aStr;
+ if ( !pEdCtrl->EditingCanceled() )
+ aStr = pEdCtrl->GetText();
+ else
+ aStr = pEdCtrl->GetSavedValue();
+ if ( mpImpl->m_bIsEmptyTextAllowed || !aStr.isEmpty() )
+ EditedText( aStr );
+ // Hide may only be called after the new text was put into the entry, so
+ // that we don't call the selection handler in the GetFocus of the listbox
+ // with the old entry text.
+ pEdCtrl->Hide();
+ nImpFlags &= (~SvTreeListBoxFlags::IN_EDT);
+ GrabFocus();
+}
+
+void SvTreeListBox::CancelTextEditing()
+{
+ if ( pEdCtrl )
+ pEdCtrl->StopEditing( true );
+ nImpFlags &= (~SvTreeListBoxFlags::IN_EDT);
+}
+
+void SvTreeListBox::EndEditing( bool bCancel )
+{
+ if( pEdCtrl )
+ pEdCtrl->StopEditing( bCancel );
+ nImpFlags &= (~SvTreeListBoxFlags::IN_EDT);
+}
+
+
+void SvTreeListBox::ForbidEmptyText()
+{
+ mpImpl->m_bIsEmptyTextAllowed = false;
+}
+
+SvTreeListEntry* SvTreeListBox::CreateEntry() const
+{
+ return new SvTreeListEntry;
+}
+
+const void* SvTreeListBox::FirstSearchEntry( OUString& _rEntryText ) const
+{
+ SvTreeListEntry* pEntry = GetCurEntry();
+ if ( pEntry )
+ pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( NextSearchEntry( pEntry, _rEntryText ) ) );
+ else
+ {
+ pEntry = FirstSelected();
+ if ( !pEntry )
+ pEntry = First();
+ }
+
+ if ( pEntry )
+ _rEntryText = GetEntryText( pEntry );
+
+ return pEntry;
+}
+
+const void* SvTreeListBox::NextSearchEntry( const void* _pCurrentSearchEntry, OUString& _rEntryText ) const
+{
+ SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pCurrentSearchEntry ) );
+
+ if ( ( ( GetChildCount( pEntry ) > 0 )
+ || ( pEntry->HasChildrenOnDemand() )
+ )
+ && !IsExpanded( pEntry )
+ )
+ {
+ pEntry = pEntry->NextSibling();
+ }
+ else
+ {
+ pEntry = Next( pEntry );
+ }
+
+ if ( !pEntry )
+ pEntry = First();
+
+ if ( pEntry )
+ _rEntryText = GetEntryText( pEntry );
+
+ return pEntry;
+}
+
+void SvTreeListBox::SelectSearchEntry( const void* _pEntry )
+{
+ SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pEntry ) );
+ DBG_ASSERT( pEntry, "SvTreeListBox::SelectSearchEntry: invalid entry!" );
+ if ( !pEntry )
+ return;
+
+ SelectAll( false );
+ SetCurEntry( pEntry );
+ Select( pEntry );
+}
+
+void SvTreeListBox::ExecuteSearchEntry( const void* /*_pEntry*/ ) const
+{
+ // nothing to do here, we have no "execution"
+}
+
+vcl::StringEntryIdentifier SvTreeListBox::CurrentEntry( OUString& _out_entryText ) const
+{
+ // always accept the current entry if there is one
+ SvTreeListEntry* pCurrentEntry( GetCurEntry() );
+ if ( pCurrentEntry )
+ {
+ _out_entryText = GetEntryText( pCurrentEntry );
+ return pCurrentEntry;
+ }
+ return FirstSearchEntry( _out_entryText );
+}
+
+vcl::StringEntryIdentifier SvTreeListBox::NextEntry( vcl::StringEntryIdentifier _currentEntry, OUString& _out_entryText ) const
+{
+ return NextSearchEntry( _currentEntry, _out_entryText );
+}
+
+void SvTreeListBox::SelectEntry( vcl::StringEntryIdentifier _entry )
+{
+ SelectSearchEntry( _entry );
+}
+
+bool SvTreeListBox::HandleKeyInput( const KeyEvent& _rKEvt )
+{
+ if ( IsEntryMnemonicsEnabled()
+ && mpImpl->m_aMnemonicEngine.HandleKeyEvent( _rKEvt )
+ )
+ return true;
+
+ if (mbQuickSearch)
+ {
+ mpImpl->m_bDoingQuickSelection = true;
+ const bool bHandled = mpImpl->m_aQuickSelectionEngine.HandleKeyEvent( _rKEvt );
+ mpImpl->m_bDoingQuickSelection = false;
+ if ( bHandled )
+ return true;
+ }
+
+ return false;
+}
+
+bool SvTreeListBox::EditingCanceled() const
+{
+ return pEdCtrl && pEdCtrl->EditingCanceled();
+}
+
+
+//JP 28.3.2001: new Drag & Drop API
+sal_Int8 SvTreeListBox::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if (rEvt.mbLeaving || !CheckDragAndDropMode(g_pDDSource, rEvt.mnAction))
+ {
+ ImplShowTargetEmphasis( pTargetEntry, false );
+ }
+ else if( nDragDropMode == DragDropMode::NONE )
+ {
+ SAL_WARN( "svtools.contnr", "SvTreeListBox::QueryDrop(): no target" );
+ }
+ else
+ {
+ SvTreeListEntry* pEntry = GetDropTarget( rEvt.maPosPixel );
+ if( !IsDropFormatSupported( SotClipboardFormatId::TREELISTBOX ) )
+ {
+ SAL_WARN( "svtools.contnr", "SvTreeListBox::QueryDrop(): no format" );
+ }
+ else
+ {
+ DBG_ASSERT(g_pDDSource, "SvTreeListBox::QueryDrop(): SourceBox == 0");
+ if (!( pEntry && g_pDDSource->GetModel() == GetModel()
+ && DND_ACTION_MOVE == rEvt.mnAction
+ && (pEntry->nEntryFlags & SvTLEntryFlags::DISABLE_DROP)))
+ {
+ if( NotifyAcceptDrop( pEntry ))
+ nRet = rEvt.mnAction;
+ }
+ }
+
+ // **** draw emphasis ****
+ if( DND_ACTION_NONE == nRet )
+ ImplShowTargetEmphasis( pTargetEntry, false );
+ else if( pEntry != pTargetEntry || !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS) )
+ {
+ ImplShowTargetEmphasis( pTargetEntry, false );
+ pTargetEntry = pEntry;
+ ImplShowTargetEmphasis( pTargetEntry, true );
+ }
+ }
+ return nRet;
+}
+
+sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt, SvTreeListBox* pSourceView )
+{
+ assert(pSourceView);
+ pSourceView->EnableSelectionAsDropTarget();
+
+ ImplShowTargetEmphasis( pTargetEntry, false );
+ g_pDDTarget = this;
+
+ TransferableDataHelper aData( rEvt.maDropEvent.Transferable );
+
+ sal_Int8 nRet;
+ if( aData.HasFormat( SotClipboardFormatId::TREELISTBOX ))
+ nRet = rEvt.mnAction;
+ else
+ nRet = DND_ACTION_NONE;
+
+ if( DND_ACTION_NONE != nRet )
+ {
+ nRet = DND_ACTION_NONE;
+
+ SvTreeListEntry* pTarget = pTargetEntry; // may be 0!
+
+ if( DND_ACTION_COPY == rEvt.mnAction )
+ {
+ if (CopySelection(g_pDDSource, pTarget))
+ nRet = rEvt.mnAction;
+ }
+ else if( DND_ACTION_MOVE == rEvt.mnAction )
+ {
+ if (MoveSelectionCopyFallbackPossible( g_pDDSource, pTarget, false ))
+ nRet = rEvt.mnAction;
+ }
+ else if( DND_ACTION_COPYMOVE == rEvt.mnAction )
+ {
+ if (MoveSelectionCopyFallbackPossible(g_pDDSource, pTarget, true))
+ nRet = rEvt.mnAction;
+ }
+ }
+ return nRet;
+}
+
+sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ return ExecuteDrop( rEvt, g_pDDSource );
+}
+
+/**
+ * This sets the global variables used to determine the
+ * in-process drag source.
+ */
+void SvTreeListBox::SetupDragOrigin()
+{
+ g_pDDSource = this;
+ g_pDDTarget = nullptr;
+}
+
+void SvTreeListBox::StartDrag( sal_Int8, const Point& rPosPixel )
+{
+
+ Point aEventPos( rPosPixel );
+ MouseEvent aMouseEvt( aEventPos, 1, MouseEventModifiers::SELECT, MOUSE_LEFT );
+ MouseButtonUp( aMouseEvt );
+
+ nOldDragMode = GetDragDropMode();
+ if ( nOldDragMode == DragDropMode::NONE )
+ return;
+
+ ReleaseMouse();
+
+ SvTreeListEntry* pEntry = GetEntry( rPosPixel ); // GetDropTarget( rPos );
+ if( !pEntry )
+ {
+ DragFinished( DND_ACTION_NONE );
+ return;
+ }
+
+ rtl::Reference<TransferDataContainer> pContainer = new TransferDataContainer;
+ nDragDropMode = NotifyStartDrag( *pContainer, pEntry );
+ if( nDragDropMode == DragDropMode::NONE || 0 == GetSelectionCount() )
+ {
+ nDragDropMode = nOldDragMode;
+ DragFinished( DND_ACTION_NONE );
+ return;
+ }
+
+ SetupDragOrigin();
+
+ // apparently some (unused) content is needed
+ pContainer->CopyAnyData( SotClipboardFormatId::TREELISTBOX,
+ "unused", SAL_N_ELEMENTS("unused") );
+
+ bool bOldUpdateMode = Control::IsUpdateMode();
+ Control::SetUpdateMode( true );
+ Update();
+ Control::SetUpdateMode( bOldUpdateMode );
+
+ // Disallow using the selection and its children as drop targets.
+ // Important: If the selection of the SourceListBox is changed in the
+ // DropHandler, the entries have to be allowed as drop targets again:
+ // (GetSourceListBox()->EnableSelectionAsDropTarget( true, true );)
+ EnableSelectionAsDropTarget( false );
+
+ pContainer->StartDrag( this, nDragOptions, GetDragFinishedHdl() );
+}
+
+void SvTreeListBox::DragFinished( sal_Int8
+#ifndef UNX
+nAction
+#endif
+)
+{
+ EnableSelectionAsDropTarget();
+
+#ifndef UNX
+ if ( (nAction == DND_ACTION_MOVE)
+ && ( (g_pDDTarget && (g_pDDTarget->GetModel() != GetModel()))
+ || !g_pDDTarget))
+ {
+ RemoveSelection();
+ }
+#endif
+
+ ImplShowTargetEmphasis( pTargetEntry, false );
+ g_pDDSource = nullptr;
+ g_pDDTarget = nullptr;
+ pTargetEntry = nullptr;
+ nDragDropMode = nOldDragMode;
+}
+
+DragDropMode SvTreeListBox::NotifyStartDrag( TransferDataContainer&, SvTreeListEntry* )
+{
+ return DragDropMode(0xffff);
+}
+
+bool SvTreeListBox::NotifyAcceptDrop( SvTreeListEntry* )
+{
+ return true;
+}
+
+// Handler and methods for Drag - finished handler.
+// The with get GetDragFinishedHdl() get link can set on the
+// TransferDataContainer. This link is a callback for the DragFinished
+// call. AddBox method is called from the GetDragFinishedHdl() and the
+// remove is called in link callback and in the destructor. So it can't
+// called to a deleted object.
+
+namespace
+{
+ struct SortLBoxes : public rtl::Static<std::set<sal_uLong>, SortLBoxes> {};
+}
+
+void SvTreeListBox::AddBoxToDDList_Impl( const SvTreeListBox& rB )
+{
+ sal_uLong nVal = reinterpret_cast<sal_uLong>(&rB);
+ SortLBoxes::get().insert( nVal );
+}
+
+void SvTreeListBox::RemoveBoxFromDDList_Impl( const SvTreeListBox& rB )
+{
+ sal_uLong nVal = reinterpret_cast<sal_uLong>(&rB);
+ SortLBoxes::get().erase( nVal );
+}
+
+IMPL_LINK( SvTreeListBox, DragFinishHdl_Impl, sal_Int8, nAction, void )
+{
+ sal_uLong nVal = reinterpret_cast<sal_uLong>(this);
+ std::set<sal_uLong> &rSortLBoxes = SortLBoxes::get();
+ std::set<sal_uLong>::const_iterator it = rSortLBoxes.find(nVal);
+ if( it != rSortLBoxes.end() )
+ {
+ DragFinished( nAction );
+ rSortLBoxes.erase( it );
+ }
+}
+
+Link<sal_Int8,void> SvTreeListBox::GetDragFinishedHdl() const
+{
+ AddBoxToDDList_Impl( *this );
+ return LINK( const_cast<SvTreeListBox*>(this), SvTreeListBox, DragFinishHdl_Impl );
+}
+
+/*
+ Bugs/TODO
+
+ - calculate rectangle when editing in-place (bug with some fonts)
+ - SetSpaceBetweenEntries: offset is not taken into account in SetEntryHeight
+*/
+
+#define SV_LBOX_DEFAULT_INDENT_PIXEL 20
+
+void SvTreeListBox::InitTreeView()
+{
+ pCheckButtonData = nullptr;
+ pEdEntry = nullptr;
+ pEdItem = nullptr;
+ nEntryHeight = 0;
+ pEdCtrl = nullptr;
+ nFirstSelTab = 0;
+ nLastSelTab = 0;
+ nFocusWidth = -1;
+ nAllItemAccRoleType = SvTreeAccRoleType::NONE;
+ mnCheckboxItemWidth = 0;
+
+ nTreeFlags = SvTreeFlags::RECALCTABS;
+ nIndent = SV_LBOX_DEFAULT_INDENT_PIXEL;
+ nEntryHeightOffs = SV_ENTRYHEIGHTOFFS_PIXEL;
+ pImpl.reset( new SvImpLBox( this, GetModel(), GetStyle() ) );
+
+ mbContextBmpExpanded = true;
+ nContextBmpWidthMax = 0;
+
+ SetFont( GetFont() );
+ AdjustEntryHeightAndRecalc();
+
+ SetSpaceBetweenEntries( 0 );
+ SetLineColor();
+ InitSettings();
+ ImplInitStyle();
+ SetTabs();
+}
+
+OUString SvTreeListBox::GetEntryAltText( SvTreeListEntry* ) const
+{
+ return OUString();
+}
+
+OUString SvTreeListBox::GetEntryLongDescription( SvTreeListEntry* ) const
+{
+ return OUString();
+}
+
+OUString SvTreeListBox::SearchEntryTextWithHeadTitle( SvTreeListEntry* pEntry )
+{
+ assert(pEntry);
+ OUStringBuffer sRet;
+
+ sal_uInt16 nCount = pEntry->ItemCount();
+ sal_uInt16 nCur = 0;
+ while( nCur < nCount )
+ {
+ SvLBoxItem& rItem = pEntry->GetItem( nCur );
+ if ( (rItem.GetType() == SvLBoxItemType::String) &&
+ !static_cast<SvLBoxString&>( rItem ).GetText().isEmpty() )
+ {
+ sRet.append(static_cast<SvLBoxString&>( rItem ).GetText()).append(",");
+ }
+ nCur++;
+ }
+
+ if (!sRet.isEmpty())
+ sRet = sRet.copy(0, sRet.getLength() - 1);
+ return sRet.makeStringAndClear();
+}
+
+SvTreeListBox::~SvTreeListBox()
+{
+ disposeOnce();
+}
+
+void SvTreeListBox::dispose()
+{
+ if( pImpl )
+ {
+ pImpl->CallEventListeners( VclEventId::ObjectDying );
+ pImpl.reset();
+ }
+ if( mpImpl )
+ {
+ ClearTabList();
+
+ pEdCtrl.reset();
+
+ if( pModel )
+ {
+ pModel->RemoveView( this );
+ if ( pModel->GetRefCount() == 0 )
+ {
+ pModel->Clear();
+ delete pModel;
+ pModel = nullptr;
+ }
+ }
+
+ SvTreeListBox::RemoveBoxFromDDList_Impl( *this );
+
+ if (this == g_pDDSource)
+ g_pDDSource = nullptr;
+ if (this == g_pDDTarget)
+ g_pDDTarget = nullptr;
+ mpImpl.reset();
+ }
+
+ DropTargetHelper::dispose();
+ DragSourceHelper::dispose();
+ Control::dispose();
+}
+
+void SvTreeListBox::SetNoAutoCurEntry( bool b )
+{
+ pImpl->SetNoAutoCurEntry( b );
+}
+
+void SvTreeListBox::SetModel( SvTreeList* pNewModel )
+{
+ pImpl->SetModel( pNewModel );
+ SetBaseModel(pNewModel);
+}
+
+void SvTreeListBox::SetBaseModel( SvTreeList* pNewModel )
+{
+ // does the CleanUp
+ SvListView::SetModel( pNewModel );
+ pModel->SetCloneLink( LINK(this, SvTreeListBox, CloneHdl_Impl ));
+ SvTreeListEntry* pEntry = First();
+ while( pEntry )
+ {
+ ModelHasInserted( pEntry );
+ pEntry = Next( pEntry );
+ }
+}
+
+void SvTreeListBox::SetSublistOpenWithReturn()
+{
+ pImpl->bSubLstOpRet = true;
+}
+
+void SvTreeListBox::SetSublistOpenWithLeftRight()
+{
+ pImpl->bSubLstOpLR = true;
+}
+
+void SvTreeListBox::SetSublistDontOpenWithDoubleClick(bool bDontOpen)
+{
+ pImpl->bSubLstOpDblClick = !bDontOpen;
+}
+
+void SvTreeListBox::Resize()
+{
+ if( IsEditingActive() )
+ EndEditing( true );
+
+ Control::Resize();
+
+ pImpl->Resize();
+ nFocusWidth = -1;
+ pImpl->ShowCursor( false );
+ pImpl->ShowCursor( true );
+}
+
+/* Cases:
+
+ A) entries have bitmaps
+ 0. no buttons
+ 1. node buttons (can optionally also be on root items)
+ 2. node buttons (can optionally also be on root items) + CheckButton
+ 3. CheckButton
+ B) entries don't have bitmaps (=>via WindowBits because of D&D!)
+ 0. no buttons
+ 1. node buttons (can optionally also be on root items)
+ 2. node buttons (can optionally also be on root items) + CheckButton
+ 3. CheckButton
+*/
+
+#define NO_BUTTONS 0
+#define NODE_BUTTONS 1
+#define NODE_AND_CHECK_BUTTONS 2
+#define CHECK_BUTTONS 3
+
+#define TABFLAGS_TEXT (SvLBoxTabFlags::DYNAMIC | \
+ SvLBoxTabFlags::ADJUST_LEFT | \
+ SvLBoxTabFlags::EDITABLE | \
+ SvLBoxTabFlags::SHOW_SELECTION)
+
+#define TABFLAGS_CONTEXTBMP (SvLBoxTabFlags::DYNAMIC | SvLBoxTabFlags::ADJUST_CENTER)
+
+#define TABFLAGS_CHECKBTN (SvLBoxTabFlags::DYNAMIC | \
+ SvLBoxTabFlags::ADJUST_CENTER | \
+ SvLBoxTabFlags::PUSHABLE)
+
+#define TAB_STARTPOS 2
+
+// take care of GetTextOffset when doing changes
+void SvTreeListBox::SetTabs()
+{
+ if( IsEditingActive() )
+ EndEditing( true );
+ nTreeFlags &= (~SvTreeFlags::RECALCTABS);
+ nFocusWidth = -1;
+ const WinBits nStyle( GetStyle() );
+ bool bHasButtons = (nStyle & WB_HASBUTTONS)!=0;
+ bool bHasButtonsAtRoot = (nStyle & (WB_HASLINESATROOT |
+ WB_HASBUTTONSATROOT))!=0;
+ long nStartPos = TAB_STARTPOS;
+ long nNodeWidthPixel = GetExpandedNodeBmp().GetSizePixel().Width();
+
+ // pCheckButtonData->Width() knows nothing about the native checkbox width,
+ // so we have mnCheckboxItemWidth which becomes valid when something is added.
+ long nCheckWidth = 0;
+ if( nTreeFlags & SvTreeFlags::CHKBTN )
+ nCheckWidth = mnCheckboxItemWidth;
+ long nCheckWidthDIV2 = nCheckWidth / 2;
+
+ long nContextWidth = nContextBmpWidthMax;
+ long nContextWidthDIV2 = nContextWidth / 2;
+
+ ClearTabList();
+
+ int nCase = NO_BUTTONS;
+ if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
+ {
+ if( bHasButtons )
+ nCase = NODE_BUTTONS;
+ }
+ else
+ {
+ if( bHasButtons )
+ nCase = NODE_AND_CHECK_BUTTONS;
+ else
+ nCase = CHECK_BUTTONS;
+ }
+
+ switch( nCase )
+ {
+ case NO_BUTTONS :
+ nStartPos += nContextWidthDIV2; // because of centering
+ AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
+ nStartPos += nContextWidthDIV2; // right edge of context bitmap
+ // only set a distance if there are bitmaps
+ if( nContextBmpWidthMax )
+ nStartPos += 5; // distance context bitmap to text
+ AddTab( nStartPos, TABFLAGS_TEXT );
+ break;
+
+ case NODE_BUTTONS :
+ if( bHasButtonsAtRoot )
+ nStartPos += ( nIndent + (nNodeWidthPixel/2) );
+ else
+ nStartPos += nContextWidthDIV2;
+ AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
+ nStartPos += nContextWidthDIV2; // right edge of context bitmap
+ // only set a distance if there are bitmaps
+ if( nContextBmpWidthMax )
+ nStartPos += 5; // distance context bitmap to text
+ AddTab( nStartPos, TABFLAGS_TEXT );
+ break;
+
+ case NODE_AND_CHECK_BUTTONS :
+ if( bHasButtonsAtRoot )
+ nStartPos += ( nIndent + nNodeWidthPixel );
+ else
+ nStartPos += nCheckWidthDIV2;
+ AddTab( nStartPos, TABFLAGS_CHECKBTN );
+ nStartPos += nCheckWidthDIV2; // right edge of CheckButton
+ nStartPos += 3; // distance CheckButton to context bitmap
+ nStartPos += nContextWidthDIV2; // center of context bitmap
+ AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
+ nStartPos += nContextWidthDIV2; // right edge of context bitmap
+ // only set a distance if there are bitmaps
+ if( nContextBmpWidthMax )
+ nStartPos += 5; // distance context bitmap to text
+ AddTab( nStartPos, TABFLAGS_TEXT );
+ break;
+
+ case CHECK_BUTTONS :
+ nStartPos += nCheckWidthDIV2;
+ AddTab( nStartPos, TABFLAGS_CHECKBTN );
+ nStartPos += nCheckWidthDIV2; // right edge of CheckButton
+ nStartPos += 3; // distance CheckButton to context bitmap
+ nStartPos += nContextWidthDIV2; // center of context bitmap
+ AddTab( nStartPos, TABFLAGS_CONTEXTBMP );
+ nStartPos += nContextWidthDIV2; // right edge of context bitmap
+ // only set a distance if there are bitmaps
+ if( nContextBmpWidthMax )
+ nStartPos += 5; // distance context bitmap to text
+ AddTab( nStartPos, TABFLAGS_TEXT );
+ break;
+ }
+ pImpl->NotifyTabsChanged();
+}
+
+void SvTreeListBox::InitEntry(SvTreeListEntry* pEntry,
+ const OUString& aStr, const Image& aCollEntryBmp, const Image& aExpEntryBmp,
+ SvLBoxButtonKind eButtonKind)
+{
+ if( nTreeFlags & SvTreeFlags::CHKBTN )
+ {
+ pEntry->AddItem(o3tl::make_unique<SvLBoxButton>(eButtonKind, pCheckButtonData));
+ }
+
+ pEntry->AddItem(o3tl::make_unique<SvLBoxContextBmp>( aCollEntryBmp,aExpEntryBmp, mbContextBmpExpanded));
+
+ pEntry->AddItem(o3tl::make_unique<SvLBoxString>(aStr));
+}
+
+OUString SvTreeListBox::GetEntryText(SvTreeListEntry* pEntry) const
+{
+ assert(pEntry);
+ SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String));
+ assert(pItem);
+ return pItem->GetText();
+}
+
+const Image& SvTreeListBox::GetExpandedEntryBmp(const SvTreeListEntry* pEntry)
+{
+ assert(pEntry);
+ const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
+ assert(pItem);
+ return pItem->GetBitmap2( );
+}
+
+const Image& SvTreeListBox::GetCollapsedEntryBmp( const SvTreeListEntry* pEntry )
+{
+ assert(pEntry);
+ const SvLBoxContextBmp* pItem = static_cast<const SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
+ assert(pItem);
+ return pItem->GetBitmap1( );
+}
+
+IMPL_LINK( SvTreeListBox, CheckButtonClick, SvLBoxButtonData *, pData, void )
+{
+ pHdlEntry = pData->GetActEntry();
+ CheckButtonHdl();
+}
+
+SvTreeListEntry* SvTreeListBox::InsertEntry(
+ const OUString& rText,
+ SvTreeListEntry* pParent,
+ bool bChildrenOnDemand, sal_uLong nPos,
+ void* pUser,
+ SvLBoxButtonKind eButtonKind
+)
+{
+ nTreeFlags |= SvTreeFlags::MANINS;
+
+ const Image& rDefExpBmp = pImpl->GetDefaultEntryExpBmp( );
+ const Image& rDefColBmp = pImpl->GetDefaultEntryColBmp( );
+
+ aCurInsertedExpBmp = rDefExpBmp;
+ aCurInsertedColBmp = rDefColBmp;
+
+ SvTreeListEntry* pEntry = CreateEntry();
+ pEntry->SetUserData( pUser );
+ InitEntry( pEntry, rText, rDefColBmp, rDefExpBmp, eButtonKind );
+ pEntry->EnableChildrenOnDemand( bChildrenOnDemand );
+
+ if( !pParent )
+ Insert( pEntry, nPos );
+ else
+ Insert( pEntry, pParent, nPos );
+
+ aPrevInsertedExpBmp = rDefExpBmp;
+ aPrevInsertedColBmp = rDefColBmp;
+
+ nTreeFlags &= (~SvTreeFlags::MANINS);
+
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeListBox::InsertEntry( const OUString& rText,
+ const Image& aExpEntryBmp, const Image& aCollEntryBmp,
+ SvTreeListEntry* pParent, bool bChildrenOnDemand, sal_uLong nPos, void* pUser,
+ SvLBoxButtonKind eButtonKind )
+{
+ nTreeFlags |= SvTreeFlags::MANINS;
+
+ aCurInsertedExpBmp = aExpEntryBmp;
+ aCurInsertedColBmp = aCollEntryBmp;
+
+ SvTreeListEntry* pEntry = CreateEntry();
+ pEntry->SetUserData( pUser );
+ InitEntry( pEntry, rText, aCollEntryBmp, aExpEntryBmp, eButtonKind );
+
+ pEntry->EnableChildrenOnDemand( bChildrenOnDemand );
+
+ if( !pParent )
+ Insert( pEntry, nPos );
+ else
+ Insert( pEntry, pParent, nPos );
+
+ aPrevInsertedExpBmp = aExpEntryBmp;
+ aPrevInsertedColBmp = aCollEntryBmp;
+
+ nTreeFlags &= (~SvTreeFlags::MANINS);
+
+ return pEntry;
+}
+
+void SvTreeListBox::SetEntryText(SvTreeListEntry* pEntry, const OUString& rStr)
+{
+ SvLBoxString* pItem = static_cast<SvLBoxString*>(pEntry->GetFirstItem(SvLBoxItemType::String));
+ assert(pItem);
+ pItem->SetText(rStr);
+ pItem->InitViewData( this, pEntry );
+ GetModel()->InvalidateEntry( pEntry );
+}
+
+void SvTreeListBox::SetExpandedEntryBmp( SvTreeListEntry* pEntry, const Image& aBmp )
+{
+ SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
+
+ assert(pItem);
+ pItem->SetBitmap2( aBmp );
+
+ GetModel()->InvalidateEntry( pEntry );
+ SetEntryHeight( pEntry );
+ Size aSize = aBmp.GetSizePixel();
+ short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) );
+ if( nWidth > nContextBmpWidthMax )
+ {
+ nContextBmpWidthMax = nWidth;
+ SetTabs();
+ }
+}
+
+void SvTreeListBox::SetCollapsedEntryBmp(SvTreeListEntry* pEntry,const Image& aBmp )
+{
+ SvLBoxContextBmp* pItem = static_cast<SvLBoxContextBmp*>(pEntry->GetFirstItem(SvLBoxItemType::ContextBmp));
+
+ assert(pItem);
+ pItem->SetBitmap1( aBmp );
+
+ GetModel()->InvalidateEntry( pEntry );
+ SetEntryHeight( pEntry );
+ Size aSize = aBmp.GetSizePixel();
+ short nWidth = pImpl->UpdateContextBmpWidthVector( pEntry, static_cast<short>(aSize.Width()) );
+ if( nWidth > nContextBmpWidthMax )
+ {
+ nContextBmpWidthMax = nWidth;
+ SetTabs();
+ }
+}
+
+void SvTreeListBox::ImpEntryInserted( SvTreeListEntry* pEntry )
+{
+
+ SvTreeListEntry* pParent = pModel->GetParent( pEntry );
+ if( pParent )
+ {
+ SvTLEntryFlags nFlags = pParent->GetFlags();
+ nFlags &= ~SvTLEntryFlags::NO_NODEBMP;
+ pParent->SetFlags( nFlags );
+ }
+
+ if(!((nTreeFlags & SvTreeFlags::MANINS) &&
+ (aPrevInsertedExpBmp == aCurInsertedExpBmp) &&
+ (aPrevInsertedColBmp == aCurInsertedColBmp) ))
+ {
+ Size aSize = GetCollapsedEntryBmp( pEntry ).GetSizePixel();
+ if( aSize.Width() > nContextBmpWidthMax )
+ {
+ nContextBmpWidthMax = static_cast<short>(aSize.Width());
+ nTreeFlags |= SvTreeFlags::RECALCTABS;
+ }
+ aSize = GetExpandedEntryBmp( pEntry ).GetSizePixel();
+ if( aSize.Width() > nContextBmpWidthMax )
+ {
+ nContextBmpWidthMax = static_cast<short>(aSize.Width());
+ nTreeFlags |= SvTreeFlags::RECALCTABS;
+ }
+ }
+ SetEntryHeight( pEntry );
+
+ if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
+ return;
+
+ SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
+ if( pItem )
+ {
+ long nWidth = pItem->GetSize(this, pEntry).Width();
+ if( mnCheckboxItemWidth < nWidth )
+ {
+ mnCheckboxItemWidth = nWidth;
+ nTreeFlags |= SvTreeFlags::RECALCTABS;
+ }
+ }
+}
+
+
+void SvTreeListBox::SetCheckButtonState( SvTreeListEntry* pEntry, SvButtonState eState)
+{
+ if( !(nTreeFlags & SvTreeFlags::CHKBTN) )
+ return;
+
+ SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
+ if(!(pItem && pItem->CheckModification()))
+ return ;
+ switch( eState )
+ {
+ case SvButtonState::Checked:
+ pItem->SetStateChecked();
+ break;
+
+ case SvButtonState::Unchecked:
+ pItem->SetStateUnchecked();
+ break;
+
+ case SvButtonState::Tristate:
+ pItem->SetStateTristate();
+ break;
+ }
+ InvalidateEntry( pEntry );
+}
+
+void SvTreeListBox::SetCheckButtonInvisible( SvTreeListEntry* pEntry)
+{
+ SvLBoxButton* pItem = (nTreeFlags & SvTreeFlags::CHKBTN) ?
+ static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button)) :
+ nullptr;
+
+ if (!pItem)
+ return;
+
+ pItem->SetStateInvisible();
+ InvalidateEntry(pEntry);
+}
+
+SvButtonState SvTreeListBox::GetCheckButtonState( SvTreeListEntry* pEntry ) const
+{
+ SvButtonState eState = SvButtonState::Unchecked;
+ if( pEntry && ( nTreeFlags & SvTreeFlags::CHKBTN ) )
+ {
+ SvLBoxButton* pItem = static_cast<SvLBoxButton*>(pEntry->GetFirstItem(SvLBoxItemType::Button));
+ if(!pItem)
+ return SvButtonState::Tristate;
+ SvItemStateFlags nButtonFlags = pItem->GetButtonFlags();
+ eState = SvLBoxButtonData::ConvertToButtonState( nButtonFlags );
+ }
+ return eState;
+}
+
+void SvTreeListBox::CheckButtonHdl()
+{
+ aCheckButtonHdl.Call( this );
+ if ( pCheckButtonData )
+ pImpl->CallEventListeners( VclEventId::CheckboxToggle, static_cast<void*>(pCheckButtonData->GetActEntry()) );
+}
+
+
+// TODO: Currently all data is cloned so that they conform to the default tree
+// view format. Actually, the model should be used as a reference here. This
+// leads to us _not_ calling SvTreeListEntry::Clone, but only its base class
+// SvTreeListEntry.
+
+
+SvTreeListEntry* SvTreeListBox::CloneEntry( SvTreeListEntry* pSource )
+{
+ OUString aStr;
+ Image aCollEntryBmp;
+ Image aExpEntryBmp;
+ SvLBoxButtonKind eButtonKind = SvLBoxButtonKind::EnabledCheckbox;
+
+ SvLBoxString* pStringItem = static_cast<SvLBoxString*>(pSource->GetFirstItem(SvLBoxItemType::String));
+ if( pStringItem )
+ aStr = pStringItem->GetText();
+ SvLBoxContextBmp* pBmpItem = static_cast<SvLBoxContextBmp*>(pSource->GetFirstItem(SvLBoxItemType::ContextBmp));
+ if( pBmpItem )
+ {
+ aCollEntryBmp = pBmpItem->GetBitmap1( );
+ aExpEntryBmp = pBmpItem->GetBitmap2( );
+ }
+ SvLBoxButton* pButtonItem = static_cast<SvLBoxButton*>(pSource->GetFirstItem(SvLBoxItemType::Button));
+ if( pButtonItem )
+ eButtonKind = pButtonItem->GetKind();
+ SvTreeListEntry* pClone = CreateEntry();
+ InitEntry( pClone, aStr, aCollEntryBmp, aExpEntryBmp, eButtonKind );
+ pClone->SvTreeListEntry::Clone( pSource );
+ pClone->EnableChildrenOnDemand( pSource->HasChildrenOnDemand() );
+ pClone->SetUserData( pSource->GetUserData() );
+
+ return pClone;
+}
+
+void SvTreeListBox::SetIndent( short nNewIndent )
+{
+ nIndent = nNewIndent;
+ SetTabs();
+ if( IsUpdateMode() )
+ Invalidate();
+}
+
+const Image& SvTreeListBox::GetDefaultExpandedEntryBmp( ) const
+{
+ return pImpl->GetDefaultEntryExpBmp( );
+}
+
+const Image& SvTreeListBox::GetDefaultCollapsedEntryBmp( ) const
+{
+ return pImpl->GetDefaultEntryColBmp( );
+}
+
+void SvTreeListBox::SetDefaultExpandedEntryBmp( const Image& aBmp )
+{
+ Size aSize = aBmp.GetSizePixel();
+ if( aSize.Width() > nContextBmpWidthMax )
+ nContextBmpWidthMax = static_cast<short>(aSize.Width());
+ SetTabs();
+
+ pImpl->SetDefaultEntryExpBmp( aBmp );
+}
+
+void SvTreeListBox::SetDefaultCollapsedEntryBmp( const Image& aBmp )
+{
+ Size aSize = aBmp.GetSizePixel();
+ if( aSize.Width() > nContextBmpWidthMax )
+ nContextBmpWidthMax = static_cast<short>(aSize.Width());
+ SetTabs();
+
+ pImpl->SetDefaultEntryColBmp( aBmp );
+}
+
+void SvTreeListBox::EnableCheckButton( SvLBoxButtonData* pData )
+{
+ DBG_ASSERT(!GetEntryCount(),"EnableCheckButton: Entry count != 0");
+ if( !pData )
+ nTreeFlags &= (~SvTreeFlags::CHKBTN);
+ else
+ {
+ SetCheckButtonData( pData );
+ nTreeFlags |= SvTreeFlags::CHKBTN;
+ pData->SetLink( LINK(this, SvTreeListBox, CheckButtonClick));
+ }
+
+ SetTabs();
+ if( IsUpdateMode() )
+ Invalidate();
+}
+
+void SvTreeListBox::SetCheckButtonData( SvLBoxButtonData* pData )
+{
+ if ( pData )
+ pCheckButtonData = pData;
+}
+
+const Image& SvTreeListBox::GetDefaultExpandedNodeImage( )
+{
+ return SvImpLBox::GetDefaultExpandedNodeImage( );
+}
+
+const Image& SvTreeListBox::GetDefaultCollapsedNodeImage( )
+{
+ return SvImpLBox::GetDefaultCollapsedNodeImage( );
+}
+
+void SvTreeListBox::SetNodeBitmaps( const Image& rCollapsedNodeBmp, const Image& rExpandedNodeBmp )
+{
+ SetExpandedNodeBmp( rExpandedNodeBmp );
+ SetCollapsedNodeBmp( rCollapsedNodeBmp );
+ SetTabs();
+}
+
+bool SvTreeListBox::EditingEntry( SvTreeListEntry*, Selection& )
+{
+ return true;
+}
+
+bool SvTreeListBox::EditedEntry( SvTreeListEntry* /*pEntry*/,const OUString& /*rNewText*/)
+{
+ return true;
+}
+
+void SvTreeListBox::EnableInplaceEditing( bool bOn )
+{
+ if (bOn)
+ nImpFlags |= SvTreeListBoxFlags::EDT_ENABLED;
+ else
+ nImpFlags &= ~SvTreeListBoxFlags::EDT_ENABLED;
+}
+
+void SvTreeListBox::KeyInput( const KeyEvent& rKEvt )
+{
+ // under OS/2, we get key up/down even while editing
+ if( IsEditingActive() )
+ return;
+
+ nImpFlags |= SvTreeListBoxFlags::IS_TRAVELSELECT;
+
+ if( !pImpl->KeyInput( rKEvt ) )
+ {
+ bool bHandled = HandleKeyInput( rKEvt );
+ if ( !bHandled )
+ Control::KeyInput( rKEvt );
+ }
+
+ nImpFlags &= ~SvTreeListBoxFlags::IS_TRAVELSELECT;
+}
+
+void SvTreeListBox::RequestingChildren( SvTreeListEntry* pParent )
+{
+ if( !pParent->HasChildren() )
+ InsertEntry( OUString("<dummy>"), pParent );
+}
+
+void SvTreeListBox::GetFocus()
+{
+ //If there is no item in the tree, draw focus.
+ if( !First())
+ {
+ Invalidate();
+ }
+ pImpl->GetFocus();
+ Control::GetFocus();
+
+ SvTreeListEntry* pEntry = FirstSelected();
+ if ( !pEntry )
+ {
+ pEntry = pImpl->GetCurrentEntry();
+ }
+ if (pImpl->pCursor)
+ {
+ if (pEntry != pImpl->pCursor)
+ pEntry = pImpl->pCursor;
+ }
+ if ( pEntry )
+ pImpl->CallEventListeners( VclEventId::ListboxTreeFocus, pEntry );
+
+}
+
+void SvTreeListBox::LoseFocus()
+{
+ // If there is no item in the tree, delete visual focus.
+ if ( !First() )
+ Invalidate();
+ if ( pImpl )
+ pImpl->LoseFocus();
+ Control::LoseFocus();
+}
+
+void SvTreeListBox::ModelHasCleared()
+{
+ pImpl->pCursor = nullptr; // else we crash in GetFocus when editing in-place
+ pEdCtrl.reset();
+ pImpl->Clear();
+ nFocusWidth = -1;
+
+ nContextBmpWidthMax = 0;
+ SetDefaultExpandedEntryBmp( GetDefaultExpandedEntryBmp() );
+ SetDefaultCollapsedEntryBmp( GetDefaultCollapsedEntryBmp() );
+
+ if( !(nTreeFlags & SvTreeFlags::FIXEDHEIGHT ))
+ nEntryHeight = 0;
+ AdjustEntryHeight();
+ AdjustEntryHeight( GetDefaultExpandedEntryBmp() );
+ AdjustEntryHeight( GetDefaultCollapsedEntryBmp() );
+
+ SvListView::ModelHasCleared();
+}
+
+void SvTreeListBox::ScrollOutputArea( short nDeltaEntries )
+{
+ if( !nDeltaEntries || !pImpl->aVerSBar->IsVisible() )
+ return;
+
+ long nThumb = pImpl->aVerSBar->GetThumbPos();
+ long nMax = pImpl->aVerSBar->GetRange().Max();
+
+ if( nDeltaEntries < 0 )
+ {
+ // move window up
+ nDeltaEntries *= -1;
+ long nVis = pImpl->aVerSBar->GetVisibleSize();
+ long nTemp = nThumb + nVis;
+ if( nDeltaEntries > (nMax - nTemp) )
+ nDeltaEntries = static_cast<short>(nMax - nTemp);
+ pImpl->PageDown( static_cast<sal_uInt16>(nDeltaEntries) );
+ }
+ else
+ {
+ if( nDeltaEntries > nThumb )
+ nDeltaEntries = static_cast<short>(nThumb);
+ pImpl->PageUp( static_cast<sal_uInt16>(nDeltaEntries) );
+ }
+ pImpl->SyncVerThumb();
+ NotifyEndScroll();
+}
+
+void SvTreeListBox::ScrollToAbsPos( long nPos )
+{
+ pImpl->ScrollToAbsPos( nPos );
+}
+
+void SvTreeListBox::SetSelectionMode( SelectionMode eSelectMode )
+{
+ eSelMode = eSelectMode;
+ pImpl->SetSelectionMode( eSelectMode );
+}
+
+void SvTreeListBox::SetDragDropMode( DragDropMode nDDMode )
+{
+ nDragDropMode = nDDMode;
+ pImpl->SetDragDropMode( nDDMode );
+}
+
+void SvTreeListBox::SetEntryHeight( SvTreeListEntry const * pEntry )
+{
+ short nHeightMax=0;
+ sal_uInt16 nCount = pEntry->ItemCount();
+ sal_uInt16 nCur = 0;
+ SvViewDataEntry* pViewData = GetViewDataEntry( pEntry );
+ while( nCur < nCount )
+ {
+ short nHeight = static_cast<short>(SvLBoxItem::GetSize( pViewData, nCur ).Height());
+ if( nHeight > nHeightMax )
+ nHeightMax = nHeight;
+ nCur++;
+ }
+
+ if( nHeightMax > nEntryHeight )
+ {
+ nEntryHeight = nHeightMax;
+ Control::SetFont( GetFont() );
+ pImpl->SetEntryHeight();
+ }
+}
+
+void SvTreeListBox::SetEntryHeight( short nHeight, bool bForce )
+{
+ if( nHeight > nEntryHeight || bForce )
+ {
+ nEntryHeight = nHeight;
+ if( nEntryHeight )
+ nTreeFlags |= SvTreeFlags::FIXEDHEIGHT;
+ else
+ nTreeFlags &= ~SvTreeFlags::FIXEDHEIGHT;
+ Control::SetFont( GetFont() );
+ pImpl->SetEntryHeight();
+ }
+}
+
+void SvTreeListBox::SetEntryWidth( short nWidth )
+{
+ nEntryWidth = nWidth;
+}
+
+void SvTreeListBox::AdjustEntryHeight( const Image& rBmp )
+{
+ const Size aSize( rBmp.GetSizePixel() );
+ if( aSize.Height() > nEntryHeight )
+ {
+ nEntryHeight = static_cast<short>(aSize.Height()) + nEntryHeightOffs;
+ pImpl->SetEntryHeight();
+ }
+}
+
+void SvTreeListBox::AdjustEntryHeight()
+{
+ Size aSize( GetTextWidth(OUString('X')), GetTextHeight() );
+ if( aSize.Height() > nEntryHeight )
+ {
+ nEntryHeight = static_cast<short>(aSize.Height()) + nEntryHeightOffs;
+ pImpl->SetEntryHeight();
+ }
+}
+
+bool SvTreeListBox::Expand( SvTreeListEntry* pParent )
+{
+ pHdlEntry = pParent;
+ bool bExpanded = false;
+ SvTLEntryFlags nFlags;
+
+ if( pParent->HasChildrenOnDemand() )
+ RequestingChildren( pParent );
+ if( pParent->HasChildren() )
+ {
+ nImpFlags |= SvTreeListBoxFlags::IS_EXPANDING;
+ if( ExpandingHdl() )
+ {
+ bExpanded = true;
+ ExpandListEntry( pParent );
+ pImpl->EntryExpanded( pParent );
+ pHdlEntry = pParent;
+ ExpandedHdl();
+ SetAlternatingRowColors( mbAlternatingRowColors );
+ }
+ nFlags = pParent->GetFlags();
+ nFlags &= ~SvTLEntryFlags::NO_NODEBMP;
+ nFlags |= SvTLEntryFlags::HAD_CHILDREN;
+ pParent->SetFlags( nFlags );
+ }
+ else
+ {
+ nFlags = pParent->GetFlags();
+ nFlags |= SvTLEntryFlags::NO_NODEBMP;
+ pParent->SetFlags( nFlags );
+ GetModel()->InvalidateEntry( pParent ); // repaint
+ }
+
+ // #i92103#
+ if ( bExpanded )
+ {
+ pImpl->CallEventListeners( VclEventId::ItemExpanded, pParent );
+ }
+
+ return bExpanded;
+}
+
+bool SvTreeListBox::Collapse( SvTreeListEntry* pParent )
+{
+ nImpFlags &= ~SvTreeListBoxFlags::IS_EXPANDING;
+ pHdlEntry = pParent;
+ bool bCollapsed = false;
+
+ if( ExpandingHdl() )
+ {
+ bCollapsed = true;
+ pImpl->CollapsingEntry( pParent );
+ CollapseListEntry( pParent );
+ pImpl->EntryCollapsed( pParent );
+ pHdlEntry = pParent;
+ ExpandedHdl();
+ SetAlternatingRowColors( mbAlternatingRowColors );
+ }
+
+ // #i92103#
+ if ( bCollapsed )
+ {
+ pImpl->CallEventListeners( VclEventId::ItemCollapsed, pParent );
+ }
+
+ return bCollapsed;
+}
+
+bool SvTreeListBox::Select( SvTreeListEntry* pEntry, bool bSelect )
+{
+ DBG_ASSERT(pEntry,"Select: Null-Ptr");
+ bool bRetVal = SelectListEntry( pEntry, bSelect );
+ DBG_ASSERT(IsSelected(pEntry)==bSelect,"Select failed");
+ if( bRetVal )
+ {
+ pImpl->EntrySelected( pEntry, bSelect );
+ pHdlEntry = pEntry;
+ if( bSelect )
+ {
+ SelectHdl();
+ CallEventListeners( VclEventId::ListboxTreeSelect, pEntry);
+ }
+ else
+ DeselectHdl();
+ }
+ return bRetVal;
+}
+
+sal_uLong SvTreeListBox::SelectChildren( SvTreeListEntry* pParent, bool bSelect )
+{
+ pImpl->DestroyAnchor();
+ sal_uLong nRet = 0;
+ if( !pParent->HasChildren() )
+ return 0;
+ sal_uInt16 nRefDepth = pModel->GetDepth( pParent );
+ SvTreeListEntry* pChild = FirstChild( pParent );
+ do {
+ nRet++;
+ Select( pChild, bSelect );
+ pChild = Next( pChild );
+ } while( pChild && pModel->GetDepth( pChild ) > nRefDepth );
+ return nRet;
+}
+
+void SvTreeListBox::SelectAll( bool bSelect, bool )
+{
+ pImpl->SelAllDestrAnch(
+ bSelect,
+ true, // delete anchor,
+ true ); // even when using SelectionMode::Single, deselect the cursor
+}
+
+void SvTreeListBox::ModelHasInsertedTree( SvTreeListEntry* pEntry )
+{
+ sal_uInt16 nRefDepth = pModel->GetDepth( pEntry );
+ SvTreeListEntry* pTmp = pEntry;
+ do
+ {
+ ImpEntryInserted( pTmp );
+ pTmp = Next( pTmp );
+ } while( pTmp && nRefDepth < pModel->GetDepth( pTmp ) );
+ pImpl->TreeInserted( pEntry );
+}
+
+void SvTreeListBox::ModelHasInserted( SvTreeListEntry* pEntry )
+{
+ ImpEntryInserted( pEntry );
+ pImpl->EntryInserted( pEntry );
+}
+
+void SvTreeListBox::ModelIsMoving(SvTreeListEntry* pSource,
+ SvTreeListEntry* /* pTargetParent */,
+ sal_uLong /* nChildPos */ )
+{
+ pImpl->MovingEntry( pSource );
+}
+
+void SvTreeListBox::ModelHasMoved( SvTreeListEntry* pSource )
+{
+ pImpl->EntryMoved( pSource );
+}
+
+void SvTreeListBox::ModelIsRemoving( SvTreeListEntry* pEntry )
+{
+ if(pEdEntry == pEntry)
+ pEdEntry = nullptr;
+
+ pImpl->RemovingEntry( pEntry );
+}
+
+void SvTreeListBox::ModelHasRemoved( SvTreeListEntry* pEntry )
+{
+ if ( pEntry == pHdlEntry)
+ pHdlEntry = nullptr;
+ pImpl->EntryRemoved();
+}
+
+void SvTreeListBox::SetCollapsedNodeBmp( const Image& rBmp)
+{
+ AdjustEntryHeight( rBmp );
+ pImpl->SetCollapsedNodeBmp( rBmp );
+}
+
+void SvTreeListBox::SetExpandedNodeBmp( const Image& rBmp )
+{
+ AdjustEntryHeight( rBmp );
+ pImpl->SetExpandedNodeBmp( rBmp );
+}
+
+
+void SvTreeListBox::SetFont( const vcl::Font& rFont )
+{
+ vcl::Font aTempFont( rFont );
+ vcl::Font aOrigFont( GetFont() );
+ aTempFont.SetTransparent( true );
+ if (aTempFont == aOrigFont)
+ return;
+ Control::SetFont( aTempFont );
+
+ aTempFont.SetColor(aOrigFont.GetColor());
+ aTempFont.SetFillColor(aOrigFont.GetFillColor());
+ aTempFont.SetTransparent(aOrigFont.IsTransparent());
+
+ if (aTempFont == aOrigFont)
+ return;
+
+ AdjustEntryHeightAndRecalc();
+}
+
+void SvTreeListBox::AdjustEntryHeightAndRecalc()
+{
+ AdjustEntryHeight();
+ // always invalidate, else things go wrong in SetEntryHeight
+ RecalcViewData();
+}
+
+void SvTreeListBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ Control::Paint(rRenderContext, rRect);
+ if (nTreeFlags & SvTreeFlags::RECALCTABS)
+ SetTabs();
+ pImpl->Paint(rRenderContext, rRect);
+
+ //Add visual focus draw
+ if (First())
+ return;
+
+ if (HasFocus())
+ {
+ long nHeight = rRenderContext.GetTextHeight();
+ tools::Rectangle aRect(Point(0, 0), Size(GetSizePixel().Width(), nHeight));
+ ShowFocus(aRect);
+ }
+ else
+ {
+ HideFocus();
+ }
+}
+
+void SvTreeListBox::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ pImpl->MouseButtonDown( rMEvt );
+}
+
+void SvTreeListBox::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ pImpl->MouseButtonUp( rMEvt );
+}
+
+void SvTreeListBox::MouseMove( const MouseEvent& rMEvt )
+{
+ pImpl->MouseMove( rMEvt );
+}
+
+
+void SvTreeListBox::SetUpdateMode( bool bUpdate )
+{
+ pImpl->SetUpdateMode( bUpdate );
+ mbUpdateAlternatingRows = bUpdate;
+ SetAlternatingRowColors( mbAlternatingRowColors );
+}
+
+void SvTreeListBox::SetSpaceBetweenEntries( short nOffsLogic )
+{
+ if( nOffsLogic != nEntryHeightOffs )
+ {
+ nEntryHeight = nEntryHeight - nEntryHeightOffs;
+ nEntryHeightOffs = nOffsLogic;
+ nEntryHeight = nEntryHeight + nOffsLogic;
+ AdjustEntryHeightAndRecalc();
+ pImpl->SetEntryHeight();
+ }
+}
+
+void SvTreeListBox::SetCursor( SvTreeListEntry* pEntry, bool bForceNoSelect )
+{
+ pImpl->SetCursor(pEntry, bForceNoSelect);
+}
+
+void SvTreeListBox::SetCurEntry( SvTreeListEntry* pEntry )
+{
+ pImpl->SetCurEntry( pEntry );
+}
+
+Image const & SvTreeListBox::GetExpandedNodeBmp( ) const
+{
+ return pImpl->GetExpandedNodeBmp( );
+}
+
+Point SvTreeListBox::GetEntryPosition( SvTreeListEntry* pEntry ) const
+{
+ return pImpl->GetEntryPosition( pEntry );
+}
+
+void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry )
+{
+ pImpl->MakeVisible(pEntry);
+}
+
+void SvTreeListBox::MakeVisible( SvTreeListEntry* pEntry, bool bMoveToTop )
+{
+ pImpl->MakeVisible( pEntry, bMoveToTop );
+}
+
+void SvTreeListBox::ModelHasEntryInvalidated( SvTreeListEntry* pEntry )
+{
+
+ // reinitialize the separate items of the entries
+ sal_uInt16 nCount = pEntry->ItemCount();
+ for( sal_uInt16 nIdx = 0; nIdx < nCount; nIdx++ )
+ {
+ SvLBoxItem& rItem = pEntry->GetItem( nIdx );
+ rItem.InitViewData( this, pEntry );
+ }
+
+ // repaint
+ pImpl->InvalidateEntry( pEntry );
+}
+
+void SvTreeListBox::EditItemText(SvTreeListEntry* pEntry, SvLBoxString* pItem, const Selection& rSelection)
+{
+ assert(pEntry && pItem);
+ if( IsSelected( pEntry ))
+ {
+ pImpl->ShowCursor( false );
+ SelectListEntry( pEntry, false );
+ pImpl->InvalidateEntry(pEntry);
+ SelectListEntry( pEntry, true );
+ pImpl->ShowCursor( true );
+ }
+ pEdEntry = pEntry;
+ pEdItem = pItem;
+ SvLBoxTab* pTab = GetTab( pEntry, pItem );
+ DBG_ASSERT(pTab,"EditItemText:Tab not found");
+
+ Size aItemSize( pItem->GetSize(this, pEntry) );
+ Point aPos = GetEntryPosition( pEntry );
+ aPos.AdjustY(( nEntryHeight - aItemSize.Height() ) / 2 );
+ aPos.setX( GetTabPos( pEntry, pTab ) );
+ long nOutputWidth = pImpl->GetOutputSize().Width();
+ Size aSize( nOutputWidth - aPos.X(), aItemSize.Height() );
+ sal_uInt16 nPos = std::find_if( aTabs.begin(), aTabs.end(),
+ [pTab](const std::unique_ptr<SvLBoxTab>& p) { return p.get() == pTab; })
+ - aTabs.begin();
+ if( nPos+1 < static_cast<sal_uInt16>(aTabs.size()) )
+ {
+ SvLBoxTab* pRightTab = aTabs[ nPos + 1 ].get();
+ long nRight = GetTabPos( pEntry, pRightTab );
+ if( nRight <= nOutputWidth )
+ aSize.setWidth( nRight - aPos.X() );
+ }
+ Point aOrigin( GetMapMode().GetOrigin() );
+ aPos += aOrigin; // convert to win coordinates
+ aSize.AdjustWidth( -(aOrigin.X()) );
+ tools::Rectangle aRect( aPos, aSize );
+ EditText( pItem->GetText(), aRect, rSelection );
+}
+
+void SvTreeListBox::EditEntry( SvTreeListEntry* pEntry )
+{
+ pImpl->aEditClickPos = Point( -1, -1 );
+ ImplEditEntry( pEntry );
+}
+
+void SvTreeListBox::ImplEditEntry( SvTreeListEntry* pEntry )
+{
+ if( IsEditingActive() )
+ EndEditing();
+ if( !pEntry )
+ pEntry = GetCurEntry();
+ if( !pEntry )
+ return;
+
+ long nClickX = pImpl->aEditClickPos.X();
+ bool bIsMouseTriggered = nClickX >= 0;
+
+ SvLBoxString* pItem = nullptr;
+ sal_uInt16 nCount = pEntry->ItemCount();
+ long nTabPos, nNextTabPos = 0;
+ for( sal_uInt16 i = 0 ; i < nCount ; i++ )
+ {
+ SvLBoxItem& rTmpItem = pEntry->GetItem( i );
+ if (rTmpItem.GetType() != SvLBoxItemType::String)
+ continue;
+
+ SvLBoxTab* pTab = GetTab( pEntry, &rTmpItem );
+ nNextTabPos = -1;
+ if( i < nCount - 1 )
+ {
+ SvLBoxItem& rNextItem = pEntry->GetItem( i + 1 );
+ SvLBoxTab* pNextTab = GetTab( pEntry, &rNextItem );
+ nNextTabPos = pNextTab->GetPos();
+ }
+
+ if( pTab && pTab->IsEditable() )
+ {
+ nTabPos = pTab->GetPos();
+ if( !bIsMouseTriggered || (nClickX > nTabPos && (nNextTabPos == -1 || nClickX < nNextTabPos ) ) )
+ {
+ pItem = static_cast<SvLBoxString*>( &rTmpItem );
+ break;
+ }
+ }
+ }
+
+ Selection aSel( SELECTION_MIN, SELECTION_MAX );
+ if( pItem && EditingEntry( pEntry, aSel ) )
+ {
+ SelectAll( false );
+ MakeVisible( pEntry );
+ EditItemText( pEntry, pItem, aSel );
+ }
+}
+
+bool SvTreeListBox::AreChildrenTransient() const
+{
+ return pImpl->AreChildrenTransient();
+}
+
+void SvTreeListBox::SetChildrenNotTransient()
+{
+ pImpl->SetChildrenNotTransient();
+}
+
+void SvTreeListBox::EditedText( const OUString& rStr )
+
+{
+ if(pEdEntry) // we have to check if this entry is null that means that it is removed while editing
+ {
+ if( EditedEntry( pEdEntry, rStr ) )
+ {
+ static_cast<SvLBoxString*>(pEdItem)->SetText( rStr );
+ pModel->InvalidateEntry( pEdEntry );
+ }
+ if( GetSelectionCount() == 0 )
+ Select( pEdEntry );
+ if( GetSelectionMode() == SelectionMode::Multiple && !GetCurEntry() )
+ SetCurEntry( pEdEntry );
+ }
+}
+
+SvTreeListEntry* SvTreeListBox::GetDropTarget( const Point& rPos )
+{
+ // scroll
+ if( rPos.Y() < 12 )
+ {
+ ImplShowTargetEmphasis(pTargetEntry, false);
+ ScrollOutputArea( +1 );
+ }
+ else
+ {
+ Size aSize( pImpl->GetOutputSize() );
+ if( rPos.Y() > aSize.Height() - 12 )
+ {
+ ImplShowTargetEmphasis(pTargetEntry, false);
+ ScrollOutputArea( -1 );
+ }
+ }
+
+ SvTreeListEntry* pTarget = pImpl->GetEntry( rPos );
+ // when dropping in a vacant space, use the last entry
+ if( !pTarget )
+ return LastVisible();
+ else if( (GetDragDropMode() & DragDropMode::ENABLE_TOP) &&
+ pTarget == First() && rPos.Y() < 6 )
+ return nullptr;
+
+ return pTarget;
+}
+
+
+SvTreeListEntry* SvTreeListBox::GetEntry( const Point& rPos, bool bHit ) const
+{
+ SvTreeListEntry* pEntry = pImpl->GetEntry( rPos );
+ if( pEntry && bHit )
+ {
+ long nLine = pImpl->GetEntryLine( pEntry );
+ if( !(pImpl->EntryReallyHit( pEntry, rPos, nLine)) )
+ return nullptr;
+ }
+ return pEntry;
+}
+
+SvTreeListEntry* SvTreeListBox::GetCurEntry() const
+{
+ return pImpl ? pImpl->GetCurEntry() : nullptr;
+}
+
+void SvTreeListBox::ImplInitStyle()
+{
+ const WinBits nWindowStyle = GetStyle();
+
+ nTreeFlags |= SvTreeFlags::RECALCTABS;
+ if (nWindowStyle & WB_SORT)
+ {
+ GetModel()->SetSortMode(SortAscending);
+ GetModel()->SetCompareHdl(LINK(this, SvTreeListBox, DefaultCompare));
+ }
+ else
+ {
+ GetModel()->SetSortMode(SortNone);
+ GetModel()->SetCompareHdl(Link<const SvSortData&,sal_Int32>());
+ }
+ pImpl->SetStyle(nWindowStyle);
+ pImpl->Resize();
+ Invalidate();
+}
+
+void SvTreeListBox::InvalidateEntry(SvTreeListEntry* pEntry)
+{
+ DBG_ASSERT(pEntry,"InvalidateEntry:No Entry");
+ if (pEntry)
+ {
+ GetModel()->InvalidateEntry(pEntry);
+ }
+}
+
+void SvTreeListBox::PaintEntry1(SvTreeListEntry& rEntry, long nLine, vcl::RenderContext& rRenderContext)
+{
+
+ tools::Rectangle aRect; // multi purpose
+
+ bool bHorSBar = pImpl->HasHorScrollBar();
+ PreparePaint(rRenderContext, rEntry);
+
+ pImpl->UpdateContextBmpWidthMax(&rEntry);
+
+ if (nTreeFlags & SvTreeFlags::RECALCTABS)
+ SetTabs();
+
+ short nTempEntryHeight = GetEntryHeight();
+ long nWidth = pImpl->GetOutputSize().Width();
+
+ // Did we turn on the scrollbar within PreparePaints? If yes, we have to set
+ // the ClipRegion anew.
+ if (!bHorSBar && pImpl->HasHorScrollBar())
+ rRenderContext.SetClipRegion(vcl::Region(pImpl->GetClipRegionRect()));
+
+ Point aEntryPos(rRenderContext.GetMapMode().GetOrigin());
+ aEntryPos.setX( aEntryPos.X() * -1 ); // conversion document coordinates
+ long nMaxRight = nWidth + aEntryPos.X() - 1;
+
+ Color aBackupTextColor(rRenderContext.GetTextColor());
+ vcl::Font aBackupFont(rRenderContext.GetFont());
+ Color aBackupColor = rRenderContext.GetFillColor();
+
+ bool bCurFontIsSel = false;
+ bool bInUse = rEntry.HasInUseEmphasis();
+ // if a ClipRegion was set from outside, we don't have to reset it
+ const WinBits nWindowStyle = GetStyle();
+ const bool bHideSelection = (nWindowStyle & WB_HIDESELECTION) !=0 && !HasFocus();
+ const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ vcl::Font aHighlightFont(rRenderContext.GetFont());
+ const Color aHighlightTextColor(rSettings.GetHighlightTextColor());
+ aHighlightFont.SetColor(aHighlightTextColor);
+
+ Size aRectSize(0, nTempEntryHeight);
+
+ SvViewDataEntry* pViewDataEntry = GetViewDataEntry( &rEntry );
+
+ sal_uInt16 nTabCount = aTabs.size();
+ sal_uInt16 nItemCount = rEntry.ItemCount();
+ sal_uInt16 nCurTab = 0;
+ sal_uInt16 nCurItem = 0;
+
+ while (nCurTab < nTabCount && nCurItem < nItemCount)
+ {
+ SvLBoxTab* pTab = aTabs[nCurTab].get();
+ sal_uInt16 nNextTab = nCurTab + 1;
+ SvLBoxTab* pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr;
+ SvLBoxItem* pItem = nCurItem < nItemCount ? &rEntry.GetItem(nCurItem) : nullptr;
+
+ SvLBoxTabFlags nFlags = pTab->nFlags;
+ Size aSize(SvLBoxItem::GetSize(pViewDataEntry, nCurItem));
+ long nTabPos = GetTabPos(&rEntry, pTab);
+
+ long nNextTabPos;
+ if (pNextTab)
+ nNextTabPos = GetTabPos(&rEntry, pNextTab);
+ else
+ {
+ nNextTabPos = nMaxRight;
+ if (nTabPos > nMaxRight)
+ nNextTabPos += 50;
+ }
+
+ long nX;
+ if( pTab->nFlags & SvLBoxTabFlags::ADJUST_RIGHT )
+ // avoid cutting the right edge off the tab separation
+ nX = nTabPos + pTab->CalcOffset(aSize.Width(), (nNextTabPos - SV_TAB_BORDER - 1) - nTabPos);
+ else
+ nX = nTabPos + pTab->CalcOffset(aSize.Width(), nNextTabPos - nTabPos);
+
+ aEntryPos.setX( nX );
+ aEntryPos.setY( nLine );
+
+ // set background pattern/color
+
+ Wallpaper aWallpaper = rRenderContext.GetBackground();
+
+ bool bSelTab = bool(nFlags & SvLBoxTabFlags::SHOW_SELECTION);
+ SvLBoxItemType nItemType = pItem->GetType();
+
+ if (pViewDataEntry->IsHighlighted() && bSelTab)
+ {
+ Color aNewWallColor = rSettings.GetHighlightColor();
+ if (!bInUse || nItemType != SvLBoxItemType::ContextBmp)
+ {
+ // if the face color is bright then the deactive color is also bright
+ // -> so you can't see any deactive selection
+ if (bHideSelection && !rSettings.GetFaceColor().IsBright()
+ && aWallpaper.GetColor().IsBright() != rSettings.GetDeactiveColor().IsBright())
+ {
+ aNewWallColor = rSettings.GetDeactiveColor();
+ }
+ // set font color to highlight
+ if (!bCurFontIsSel)
+ {
+ rRenderContext.SetTextColor(aHighlightTextColor);
+ rRenderContext.SetFont(aHighlightFont);
+ bCurFontIsSel = true;
+ }
+ }
+ aWallpaper.SetColor(aNewWallColor);
+ }
+ else // no selection
+ {
+ if (bInUse && nItemType == SvLBoxItemType::ContextBmp)
+ {
+ aWallpaper.SetColor(rSettings.GetFieldColor());
+ }
+ else if (bCurFontIsSel)
+ {
+ bCurFontIsSel = false;
+ rRenderContext.SetTextColor(aBackupTextColor);
+ rRenderContext.SetFont(aBackupFont);
+ }
+ else
+ {
+ aWallpaper.SetColor(rEntry.GetBackColor());
+ }
+ }
+
+ // draw background
+ if (!(nTreeFlags & SvTreeFlags::USESEL))
+ {
+ // only draw the area that is used by the item
+ aRectSize.setWidth( aSize.Width() );
+ aRect.SetPos(aEntryPos);
+ aRect.SetSize(aRectSize);
+ }
+ else
+ {
+ // draw from the current to the next tab
+ if (nCurTab != 0)
+ aRect.SetLeft( nTabPos );
+ else
+ // if we're in the 0th tab, always draw from column 0 --
+ // else we get problems with centered tabs
+ aRect.SetLeft( 0 );
+ aRect.SetTop( nLine );
+ aRect.SetBottom( nLine + nTempEntryHeight - 1 );
+ if (pNextTab)
+ {
+ long nRight;
+ nRight = GetTabPos(&rEntry, pNextTab) - 1;
+ if (nRight > nMaxRight)
+ nRight = nMaxRight;
+ aRect.SetRight( nRight );
+ }
+ else
+ {
+ aRect.SetRight( nMaxRight );
+ }
+ }
+ // A custom selection that starts at a tab position > 0, do not fill
+ // the background of the 0th item, else e.g. we might not be able to
+ // realize tab listboxes with lines.
+ if (!(nCurTab == 0 && (nTreeFlags & SvTreeFlags::USESEL) && nFirstSelTab))
+ {
+ Color aBackgroundColor = aWallpaper.GetColor();
+ if (aBackgroundColor != COL_TRANSPARENT)
+ {
+ rRenderContext.SetFillColor(aBackgroundColor);
+ // this case may occur for smaller horizontal resizes
+ if (aRect.Left() < aRect.Right())
+ rRenderContext.DrawRect(aRect);
+ }
+ }
+ // draw item
+ // center vertically
+ aEntryPos.AdjustY((nTempEntryHeight - aSize.Height()) / 2 );
+ pViewDataEntry->SetPaintRectangle(aRect);
+
+ pItem->Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry);
+
+ // division line between tabs
+ if (pNextTab && pItem->GetType() == SvLBoxItemType::String &&
+ // not at the right edge of the window!
+ aRect.Right() < nMaxRight)
+ {
+ aRect.SetLeft( aRect.Right() - SV_TAB_BORDER );
+ rRenderContext.DrawRect(aRect);
+ }
+
+ rRenderContext.SetFillColor(aBackupColor);
+
+ nCurItem++;
+ nCurTab++;
+ }
+
+ if (bCurFontIsSel)
+ {
+ rRenderContext.SetTextColor(aBackupTextColor);
+ rRenderContext.SetFont(aBackupFont);
+ }
+
+ sal_uInt16 nFirstDynTabPos(0);
+ SvLBoxTab* pFirstDynamicTab = GetFirstDynamicTab(nFirstDynTabPos);
+ long nDynTabPos = GetTabPos(&rEntry, pFirstDynamicTab);
+ nDynTabPos += pImpl->nNodeBmpTabDistance;
+ nDynTabPos += pImpl->nNodeBmpWidth / 2;
+ nDynTabPos += 4; // 4 pixels of buffer, so the node bitmap is not too close
+ // to the next tab
+
+ if( !((!(rEntry.GetFlags() & SvTLEntryFlags::NO_NODEBMP)) &&
+ (nWindowStyle & WB_HASBUTTONS) && pFirstDynamicTab &&
+ (rEntry.HasChildren() || rEntry.HasChildrenOnDemand())))
+ return;
+
+ // find first tab and check if the node bitmap extends into it
+ sal_uInt16 nNextTab = nFirstDynTabPos;
+ SvLBoxTab* pNextTab;
+ do
+ {
+ nNextTab++;
+ pNextTab = nNextTab < nTabCount ? aTabs[nNextTab].get() : nullptr;
+ } while (pNextTab && pNextTab->IsDynamic());
+
+ if (!(!pNextTab || (GetTabPos( &rEntry, pNextTab ) > nDynTabPos)))
+ return;
+
+ if (!((nWindowStyle & WB_HASBUTTONSATROOT) || pModel->GetDepth(&rEntry) > 0))
+ return;
+
+ Point aPos(GetTabPos(&rEntry, pFirstDynamicTab), nLine);
+ aPos.AdjustX(pImpl->nNodeBmpTabDistance );
+
+ const Image* pImg = nullptr;
+
+ if (IsExpanded(&rEntry))
+ pImg = &pImpl->GetExpandedNodeBmp();
+ else
+ {
+ if ((!rEntry.HasChildren()) && rEntry.HasChildrenOnDemand() &&
+ (!(rEntry.GetFlags() & SvTLEntryFlags::HAD_CHILDREN)) &&
+ pImpl->GetDontKnowNodeBmp().GetSizePixel().Width())
+ {
+ pImg = &pImpl->GetDontKnowNodeBmp( );
+ }
+ else
+ {
+ pImg = &pImpl->GetCollapsedNodeBmp( );
+ }
+ }
+ aPos.AdjustY((nTempEntryHeight - pImg->GetSizePixel().Height()) / 2 );
+
+ DrawImageFlags nStyle = DrawImageFlags::NONE;
+ if (!IsEnabled())
+ nStyle |= DrawImageFlags::Disable;
+
+ //native
+ bool bNativeOK = false;
+ if (rRenderContext.IsNativeControlSupported(ControlType::ListNode, ControlPart::Entire))
+ {
+ ImplControlValue aControlValue;
+ tools::Rectangle aCtrlRegion(aPos, pImg->GetSizePixel());
+ ControlState nState = ControlState::NONE;
+
+ if (IsEnabled())
+ nState |= ControlState::ENABLED;
+
+ if (IsExpanded(&rEntry))
+ aControlValue.setTristateVal(ButtonValue::On); //expanded node
+ else
+ {
+ if ((!rEntry.HasChildren()) && rEntry.HasChildrenOnDemand() &&
+ (!(rEntry.GetFlags() & SvTLEntryFlags::HAD_CHILDREN)) &&
+ pImpl->GetDontKnowNodeBmp().GetSizePixel().Width())
+ {
+ aControlValue.setTristateVal( ButtonValue::DontKnow ); //don't know
+ }
+ else
+ {
+ aControlValue.setTristateVal( ButtonValue::Off ); //collapsed node
+ }
+ }
+
+ bNativeOK = rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion, nState, aControlValue, OUString());
+ }
+
+ if (!bNativeOK)
+ {
+ rRenderContext.DrawImage(aPos, *pImg ,nStyle);
+ }
+}
+
+void SvTreeListBox::PreparePaint(vcl::RenderContext& /*rRenderContext*/, SvTreeListEntry& /*rEntry*/)
+{
+}
+
+tools::Rectangle SvTreeListBox::GetFocusRect( SvTreeListEntry* pEntry, long nLine )
+{
+ pImpl->UpdateContextBmpWidthMax( pEntry );
+
+ Size aSize;
+ tools::Rectangle aRect;
+ aRect.SetTop( nLine );
+ aSize.setHeight( GetEntryHeight() );
+
+ long nRealWidth = pImpl->GetOutputSize().Width();
+ nRealWidth -= GetMapMode().GetOrigin().X();
+
+ sal_uInt16 nCurTab;
+ SvLBoxTab* pTab = GetFirstTab( SvLBoxTabFlags::SHOW_SELECTION, nCurTab );
+ long nTabPos = 0;
+ if( pTab )
+ nTabPos = GetTabPos( pEntry, pTab );
+ long nNextTabPos;
+ if( pTab && nCurTab < aTabs.size() - 1 )
+ {
+ SvLBoxTab* pNextTab = aTabs[ nCurTab + 1 ].get();
+ nNextTabPos = GetTabPos( pEntry, pNextTab );
+ }
+ else
+ {
+ nNextTabPos = nRealWidth;
+ if( nTabPos > nRealWidth )
+ nNextTabPos += 50;
+ }
+
+ bool bUserSelection = bool( nTreeFlags & SvTreeFlags::USESEL );
+ if( !bUserSelection )
+ {
+ if( pTab && nCurTab < pEntry->ItemCount() )
+ {
+ SvLBoxItem& rItem = pEntry->GetItem( nCurTab );
+ aSize.setWidth( rItem.GetSize( this, pEntry ).Width() );
+ if( !aSize.Width() )
+ aSize.setWidth( 15 );
+ long nX = nTabPos; //GetTabPos( pEntry, pTab );
+ // alignment
+ nX += pTab->CalcOffset( aSize.Width(), nNextTabPos - nTabPos );
+ aRect.SetLeft( nX );
+ // make sure that first and last letter aren't cut off slightly
+ aRect.SetSize( aSize );
+ if( aRect.Left() > 0 )
+ aRect.AdjustLeft( -1 );
+ aRect.AdjustRight( 1 );
+ }
+ }
+ else
+ {
+ // if SelTab != 0, we have to calculate also
+ if( nFocusWidth == -1 || nFirstSelTab )
+ {
+ SvLBoxTab* pLastTab = nullptr; // default to select whole width
+
+ sal_uInt16 nLastTab;
+ GetLastTab(SvLBoxTabFlags::SHOW_SELECTION,nLastTab);
+ nLastTab++;
+ if( nLastTab < aTabs.size() ) // is there another one?
+ pLastTab = aTabs[ nLastTab ].get();
+
+ aSize.setWidth( pLastTab ? pLastTab->GetPos() : 0x0fffffff );
+ nFocusWidth = static_cast<short>(aSize.Width());
+ if( pTab )
+ nFocusWidth = nFocusWidth - static_cast<short>(nTabPos); //pTab->GetPos();
+ }
+ else
+ {
+ aSize.setWidth( nFocusWidth );
+ if( pTab )
+ {
+ if( nCurTab )
+ aSize.AdjustWidth(nTabPos );
+ else
+ aSize.AdjustWidth(pTab->GetPos() ); // Tab0 always from the leftmost position
+ }
+ }
+ // if selection starts with 0th tab, draw from column 0 on
+ if( nCurTab != 0 )
+ {
+ aRect.SetLeft( nTabPos );
+ aSize.AdjustWidth( -nTabPos );
+ }
+ aRect.SetSize( aSize );
+ }
+ // adjust right edge because of clipping
+ if( aRect.Right() >= nRealWidth )
+ {
+ aRect.SetRight( nRealWidth-1 );
+ nFocusWidth = static_cast<short>(aRect.GetWidth());
+ }
+ return aRect;
+}
+
+
+sal_IntPtr SvTreeListBox::GetTabPos( SvTreeListEntry* pEntry, SvLBoxTab* pTab)
+{
+ assert(pTab);
+ sal_IntPtr nPos = pTab->GetPos();
+ if( pTab->IsDynamic() )
+ {
+ sal_uInt16 nDepth = pModel->GetDepth( pEntry );
+ nDepth = nDepth * static_cast<sal_uInt16>(nIndent);
+ nPos += static_cast<sal_IntPtr>(nDepth);
+ }
+ return nPos;
+}
+
+SvLBoxItem* SvTreeListBox::GetItem_Impl( SvTreeListEntry* pEntry, long nX,
+ SvLBoxTab** ppTab )
+{
+ SvLBoxItem* pItemClicked = nullptr;
+ sal_uInt16 nTabCount = aTabs.size();
+ sal_uInt16 nItemCount = pEntry->ItemCount();
+ SvLBoxTab* pTab = aTabs.front().get();
+ SvLBoxItem* pItem = &pEntry->GetItem(0);
+ sal_uInt16 nNextItem = 1;
+ nX -= GetMapMode().GetOrigin().X();
+ long nRealWidth = pImpl->GetOutputSize().Width();
+ nRealWidth -= GetMapMode().GetOrigin().X();
+
+ while( true )
+ {
+ SvLBoxTab* pNextTab=nNextItem<nTabCount ? aTabs[nNextItem].get() : nullptr;
+ long nStart = GetTabPos( pEntry, pTab );
+
+ long nNextTabPos;
+ if( pNextTab )
+ nNextTabPos = GetTabPos( pEntry, pNextTab );
+ else
+ {
+ nNextTabPos = nRealWidth;
+ if( nStart > nRealWidth )
+ nNextTabPos += 50;
+ }
+
+ Size aItemSize( pItem->GetSize(this, pEntry));
+ nStart += pTab->CalcOffset( aItemSize.Width(), nNextTabPos - nStart );
+ long nLen = aItemSize.Width();
+ if( pNextTab )
+ {
+ long nTabWidth = GetTabPos( pEntry, pNextTab ) - nStart;
+ if( nTabWidth < nLen )
+ nLen = nTabWidth;
+ }
+
+ if( nX >= nStart && nX < (nStart+nLen ) )
+ {
+ pItemClicked = pItem;
+ if( ppTab )
+ {
+ *ppTab = pTab;
+ break;
+ }
+ }
+ if( nNextItem >= nItemCount || nNextItem >= nTabCount)
+ break;
+ pTab = aTabs[ nNextItem ].get();
+ pItem = &pEntry->GetItem( nNextItem );
+ nNextItem++;
+ }
+ return pItemClicked;
+}
+
+long SvTreeListBox::getPreferredDimensions(std::vector<long> &rWidths) const
+{
+ long nHeight = 0;
+ rWidths.clear();
+ SvTreeListEntry* pEntry = First();
+ while (pEntry)
+ {
+ sal_uInt16 nCount = pEntry->ItemCount();
+ sal_uInt16 nCurPos = 0;
+ if (nCount > rWidths.size())
+ rWidths.resize(nCount);
+ while (nCurPos < nCount)
+ {
+ SvLBoxItem& rItem = pEntry->GetItem( nCurPos );
+ long nWidth = rItem.GetSize(this, pEntry).Width();
+ if (nWidth)
+ {
+ nWidth += SV_TAB_BORDER * 2;
+ if (nWidth > rWidths[nCurPos])
+ rWidths[nCurPos] = nWidth;
+ }
+ ++nCurPos;
+ }
+ pEntry = Next( pEntry );
+ nHeight += GetEntryHeight();
+ }
+ return nHeight;
+}
+
+Size SvTreeListBox::GetOptimalSize() const
+{
+ std::vector<long> aWidths;
+ Size aRet(0, getPreferredDimensions(aWidths));
+ for (long aWidth : aWidths)
+ aRet.AdjustWidth(aWidth );
+ if (GetStyle() & WB_BORDER)
+ {
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ aRet.AdjustWidth(rStyleSettings.GetBorderSize() * 2 );
+ aRet.AdjustHeight(rStyleSettings.GetBorderSize() * 2 );
+ }
+ long nMinWidth = nMinWidthInChars * approximate_char_width();
+ aRet.setWidth( std::max(aRet.Width(), nMinWidth) );
+ return aRet;
+}
+
+void SvTreeListBox::SetAlternatingRowColors( bool bEnable )
+{
+ if( !mbUpdateAlternatingRows )
+ {
+ mbAlternatingRowColors = bEnable;
+ return;
+ }
+
+ if( bEnable )
+ {
+ SvTreeListEntry* pEntry = pModel->First();
+ for(size_t i = 0; pEntry; ++i)
+ {
+ pEntry->SetBackColor( i % 2 == 0 ? GetBackground().GetColor() : GetSettings().GetStyleSettings().GetAlternatingRowColor());
+ SvTreeListEntry *pNextEntry = nullptr;
+ if( IsExpanded( pEntry ) )
+ pNextEntry = pModel->FirstChild( pEntry );
+ else
+ pNextEntry = pEntry->NextSibling();
+
+ if( !pNextEntry )
+ pEntry = pModel->Next( pEntry );
+ else
+ pEntry = pNextEntry;
+ }
+ }
+ else if( mbAlternatingRowColors )
+ for(SvTreeListEntry* pEntry = pModel->First(); pEntry; pEntry = pModel->Next(pEntry))
+ pEntry->SetBackColor( GetBackground().GetColor() );
+
+ mbAlternatingRowColors = bEnable;
+ pImpl->UpdateAll(true);
+}
+
+void SvTreeListBox::SetForceMakeVisible( bool bEnable )
+{
+ pImpl->SetForceMakeVisible(bEnable);
+}
+
+SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,long nX,SvLBoxTab** ppTab)
+{
+ return GetItem_Impl( pEntry, nX, ppTab );
+}
+
+SvLBoxItem* SvTreeListBox::GetItem(SvTreeListEntry* pEntry,long nX )
+{
+ SvLBoxTab* pDummyTab;
+ return GetItem_Impl( pEntry, nX, &pDummyTab );
+}
+
+void SvTreeListBox::AddTab(long nTabPos, SvLBoxTabFlags nFlags )
+{
+ nFocusWidth = -1;
+ SvLBoxTab* pTab = new SvLBoxTab( nTabPos, nFlags );
+ aTabs.emplace_back( pTab );
+ if( nTreeFlags & SvTreeFlags::USESEL )
+ {
+ sal_uInt16 nPos = aTabs.size() - 1;
+ if( nPos >= nFirstSelTab && nPos <= nLastSelTab )
+ pTab->nFlags |= SvLBoxTabFlags::SHOW_SELECTION;
+ else
+ // string items usually have to be selected -- turn this off
+ // explicitly
+ pTab->nFlags &= ~SvLBoxTabFlags::SHOW_SELECTION;
+ }
+}
+
+
+SvLBoxTab* SvTreeListBox::GetFirstDynamicTab( sal_uInt16& rPos ) const
+{
+ sal_uInt16 nCurTab = 0;
+ sal_uInt16 nTabCount = aTabs.size();
+ while( nCurTab < nTabCount )
+ {
+ SvLBoxTab* pTab = aTabs[nCurTab].get();
+ if( pTab->nFlags & SvLBoxTabFlags::DYNAMIC )
+ {
+ rPos = nCurTab;
+ return pTab;
+ }
+ nCurTab++;
+ }
+ return nullptr;
+}
+
+SvLBoxTab* SvTreeListBox::GetFirstDynamicTab() const
+{
+ sal_uInt16 nDummy;
+ return GetFirstDynamicTab( nDummy );
+}
+
+SvLBoxTab* SvTreeListBox::GetTab( SvTreeListEntry const * pEntry, SvLBoxItem const * pItem) const
+{
+ sal_uInt16 nPos = pEntry->GetPos( pItem );
+ return aTabs[ nPos ].get();
+}
+
+void SvTreeListBox::ClearTabList()
+{
+ aTabs.clear();
+}
+
+
+Size SvTreeListBox::GetOutputSizePixel() const
+{
+ Size aSize = pImpl->GetOutputSize();
+ return aSize;
+}
+
+void SvTreeListBox::NotifyEndScroll()
+{
+}
+
+void SvTreeListBox::NotifyScrolled()
+{
+ aScrolledHdl.Call( this );
+}
+
+void SvTreeListBox::Invalidate( InvalidateFlags nInvalidateFlags )
+{
+ if (!pImpl)
+ return;
+ if( nFocusWidth == -1 )
+ // to make sure that the control doesn't show the wrong focus rectangle
+ // after painting
+ pImpl->RecalcFocusRect();
+ Control::Invalidate( nInvalidateFlags );
+ pImpl->Invalidate();
+}
+
+void SvTreeListBox::Invalidate( const tools::Rectangle& rRect, InvalidateFlags nInvalidateFlags )
+{
+ if( nFocusWidth == -1 )
+ // to make sure that the control doesn't show the wrong focus rectangle
+ // after painting
+ pImpl->RecalcFocusRect();
+ Control::Invalidate( rRect, nInvalidateFlags );
+}
+
+
+void SvTreeListBox::SetHighlightRange( sal_uInt16 nStart, sal_uInt16 nEnd)
+{
+
+ sal_uInt16 nTemp;
+ nTreeFlags |= SvTreeFlags::USESEL;
+ if( nStart > nEnd )
+ {
+ nTemp = nStart;
+ nStart = nEnd;
+ nEnd = nTemp;
+ }
+ // select all tabs that lie within the area
+ nTreeFlags |= SvTreeFlags::RECALCTABS;
+ nFirstSelTab = nStart;
+ nLastSelTab = nEnd;
+ pImpl->RecalcFocusRect();
+}
+
+void SvTreeListBox::Command(const CommandEvent& rCEvt)
+{
+ pImpl->Command(rCEvt);
+ //pass at least alt press/release to parent impl
+ if (rCEvt.GetCommand() == CommandEventId::ModKeyChange)
+ Control::Command(rCEvt);
+}
+
+void SvTreeListBox::RemoveParentKeepChildren( SvTreeListEntry* pParent )
+{
+ assert(pParent);
+ SvTreeListEntry* pNewParent = GetParent( pParent );
+ if( pParent->HasChildren())
+ {
+ SvTreeListEntry* pChild = FirstChild( pParent );
+ while( pChild )
+ {
+ pModel->Move( pChild, pNewParent, TREELIST_APPEND );
+ pChild = FirstChild( pParent );
+ }
+ }
+ pModel->Remove( pParent );
+}
+
+SvLBoxTab* SvTreeListBox::GetFirstTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rPos )
+{
+ sal_uInt16 nTabCount = aTabs.size();
+ for( sal_uInt16 nPos = 0; nPos < nTabCount; nPos++ )
+ {
+ SvLBoxTab* pTab = aTabs[ nPos ].get();
+ if( pTab->nFlags & nFlagMask )
+ {
+ rPos = nPos;
+ return pTab;
+ }
+ }
+ rPos = 0xffff;
+ return nullptr;
+}
+
+void SvTreeListBox::GetLastTab( SvLBoxTabFlags nFlagMask, sal_uInt16& rTabPos )
+{
+ sal_uInt16 nPos = static_cast<sal_uInt16>(aTabs.size());
+ while( nPos )
+ {
+ --nPos;
+ SvLBoxTab* pTab = aTabs[ nPos ].get();
+ if( pTab->nFlags & nFlagMask )
+ {
+ rTabPos = nPos;
+ return;
+ }
+ }
+ rTabPos = 0xffff;
+}
+
+void SvTreeListBox::RequestHelp( const HelpEvent& rHEvt )
+{
+ if( !pImpl->RequestHelp( rHEvt ) )
+ Control::RequestHelp( rHEvt );
+}
+
+IMPL_LINK( SvTreeListBox, DefaultCompare, const SvSortData&, rData, sal_Int32 )
+{
+ const SvTreeListEntry* pLeft = rData.pLeft;
+ const SvTreeListEntry* pRight = rData.pRight;
+ OUString aLeft( static_cast<const SvLBoxString*>(pLeft->GetFirstItem(SvLBoxItemType::String))->GetText());
+ OUString aRight( static_cast<const SvLBoxString*>(pRight->GetFirstItem(SvLBoxItemType::String))->GetText());
+ pImpl->UpdateStringSorter();
+ return pImpl->m_pStringSorter->compare(aLeft, aRight);
+}
+
+void SvTreeListBox::ModelNotification( SvListAction nActionId, SvTreeListEntry* pEntry1,
+ SvTreeListEntry* pEntry2, sal_uLong nPos )
+{
+ SolarMutexGuard aSolarGuard;
+
+ if( nActionId == SvListAction::CLEARING )
+ CancelTextEditing();
+
+ SvListView::ModelNotification( nActionId, pEntry1, pEntry2, nPos );
+ switch( nActionId )
+ {
+ case SvListAction::INSERTED:
+ {
+ SvLBoxContextBmp* pBmpItem = static_cast< SvLBoxContextBmp* >( pEntry1->GetFirstItem( SvLBoxItemType::ContextBmp ) );
+ if ( !pBmpItem )
+ break;
+ const Image& rBitmap1( pBmpItem->GetBitmap1() );
+ const Image& rBitmap2( pBmpItem->GetBitmap2() );
+ short nMaxWidth = short( std::max( rBitmap1.GetSizePixel().Width(), rBitmap2.GetSizePixel().Width() ) );
+ nMaxWidth = pImpl->UpdateContextBmpWidthVector( pEntry1, nMaxWidth );
+ if( nMaxWidth > nContextBmpWidthMax )
+ {
+ nContextBmpWidthMax = nMaxWidth;
+ SetTabs();
+ }
+ if (get_width_request() == -1)
+ queue_resize();
+ }
+ break;
+
+ case SvListAction::RESORTING:
+ SetUpdateMode( false );
+ break;
+
+ case SvListAction::RESORTED:
+ // after a selection: show first entry and also keep the selection
+ MakeVisible( pModel->First(), true );
+ SetUpdateMode( true );
+ break;
+
+ case SvListAction::CLEARED:
+ if( IsUpdateMode() )
+ Update();
+ break;
+
+ default: break;
+ }
+}
+
+void SvTreeListBox::EndSelection()
+{
+ pImpl->EndSelection();
+}
+
+ScrollBar *SvTreeListBox::GetVScroll()
+{
+ return pImpl->aVerSBar.get();
+}
+
+ScrollBar *SvTreeListBox::GetHScroll()
+{
+ return pImpl->aHorSBar.get();
+}
+
+void SvTreeListBox::EnableAsyncDrag( bool b )
+{
+ pImpl->EnableAsyncDrag( b );
+}
+
+SvTreeListEntry* SvTreeListBox::GetFirstEntryInView() const
+{
+ return GetEntry( Point() );
+}
+
+SvTreeListEntry* SvTreeListBox::GetNextEntryInView(SvTreeListEntry* pEntry ) const
+{
+ SvTreeListEntry* pNext = NextVisible( pEntry );
+ if( pNext )
+ {
+ Point aPos( GetEntryPosition(pNext) );
+ const Size& rSize = pImpl->GetOutputSize();
+ if( aPos.Y() < 0 || aPos.Y() >= rSize.Height() )
+ return nullptr;
+ }
+ return pNext;
+}
+
+SvTreeListEntry* SvTreeListBox::GetPrevEntryInView(SvTreeListEntry* pEntry ) const
+{
+ SvTreeListEntry* pPrev = PrevVisible( pEntry );
+ if( pPrev )
+ {
+ Point aPos( GetEntryPosition(pPrev) );
+ const Size& rSize = pImpl->GetOutputSize();
+ if( aPos.Y() < 0 || aPos.Y() >= rSize.Height() )
+ return nullptr;
+ }
+ return pPrev;
+}
+
+SvTreeListEntry* SvTreeListBox::GetLastEntryInView() const
+{
+ SvTreeListEntry* pEntry = GetFirstEntryInView();
+ SvTreeListEntry* pNext = nullptr;
+ while( pEntry )
+ {
+ pNext = NextVisible( pEntry );
+ if( pNext )
+ {
+ Point aPos( GetEntryPosition(pNext) );
+ const Size& rSize = pImpl->GetOutputSize();
+ if( aPos.Y() < 0 || aPos.Y() + GetEntryHeight() >= rSize.Height() )
+ break;
+ else
+ pEntry = pNext;
+ }
+ else
+ break;
+ }
+ return pEntry;
+}
+
+void SvTreeListBox::ShowFocusRect( const SvTreeListEntry* pEntry )
+{
+ pImpl->ShowFocusRect( pEntry );
+}
+
+void SvTreeListBox::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if( (rDCEvt.GetType()==DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ nEntryHeight = 0; // _together_ with true of 1. par (bFont) of InitSettings() a zero-height
+ // forces complete recalc of heights!
+ InitSettings();
+ Invalidate();
+ }
+ else
+ Control::DataChanged( rDCEvt );
+}
+
+void SvTreeListBox::StateChanged( StateChangedType eType )
+{
+ if( eType == StateChangedType::Enable )
+ Invalidate( InvalidateFlags::Children );
+
+ Control::StateChanged( eType );
+
+ if ( eType == StateChangedType::Style )
+ ImplInitStyle();
+}
+
+void SvTreeListBox::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ SetPointFont(rRenderContext, GetPointFont(*this));
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
+ rRenderContext.SetTextFillColor();
+ rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
+
+ // always try to re-create default-SvLBoxButtonData
+ if (pCheckButtonData && pCheckButtonData->HasDefaultImages())
+ pCheckButtonData->SetDefaultImages(this);
+}
+
+void SvTreeListBox::InitSettings()
+{
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ vcl::Font aFont;
+ aFont = rStyleSettings.GetFieldFont();
+ aFont.SetColor(rStyleSettings.GetWindowTextColor());
+ SetPointFont(*this, aFont);
+ AdjustEntryHeightAndRecalc();
+
+ SetTextColor(rStyleSettings.GetFieldTextColor());
+ SetTextFillColor();
+
+ SetBackground(rStyleSettings.GetFieldColor());
+
+ // always try to re-create default-SvLBoxButtonData
+ if( pCheckButtonData && pCheckButtonData->HasDefaultImages() )
+ pCheckButtonData->SetDefaultImages(this);
+}
+
+bool SvTreeListBox::IsCellFocusEnabled() const
+{
+ return pImpl->IsCellFocusEnabled();
+}
+
+bool SvTreeListBox::SetCurrentTabPos( sal_uInt16 _nNewPos )
+{
+ return pImpl->SetCurrentTabPos( _nNewPos );
+}
+
+sal_uInt16 SvTreeListBox::GetCurrentTabPos() const
+{
+ return pImpl->GetCurrentTabPos();
+}
+
+void SvTreeListBox::InitStartEntry()
+{
+ if( !pImpl->pStartEntry )
+ pImpl->pStartEntry = GetModel()->First();
+}
+
+VclPtr<PopupMenu> SvTreeListBox::CreateContextMenu()
+{
+ return nullptr;
+}
+
+void SvTreeListBox::ExecuteContextMenuAction( sal_uInt16 )
+{
+ SAL_INFO( "svtools.contnr", "SvTreeListBox::ExecuteContextMenuAction(): now there's happening nothing!" );
+}
+
+void SvTreeListBox::EnableContextMenuHandling()
+{
+ assert(pImpl && "-SvTreeListBox::EnableContextMenuHandling(): No implementation!");
+ pImpl->bContextMenuHandling = true;
+}
+
+void SvTreeListBox::EnableList( bool _bEnable )
+{
+ // call base class method
+ Window::Enable(_bEnable);
+ // then invalidate
+ Invalidate(tools::Rectangle(Point(), GetSizePixel()));
+}
+
+void SvTreeListBox::FillAccessibleEntryStateSet( SvTreeListEntry* pEntry, ::utl::AccessibleStateSetHelper& rStateSet ) const
+{
+ assert(pEntry && "SvTreeListBox::FillAccessibleEntryStateSet: invalid entry");
+
+ if ( pEntry->HasChildrenOnDemand() || pEntry->HasChildren() )
+ {
+ rStateSet.AddState( AccessibleStateType::EXPANDABLE );
+ if ( IsExpanded( pEntry ) )
+ rStateSet.AddState( sal_Int16(AccessibleStateType::EXPANDED) );
+ }
+
+ if ( GetCheckButtonState( pEntry ) == SvButtonState::Checked )
+ rStateSet.AddState( AccessibleStateType::CHECKED );
+ if ( IsEntryVisible( pEntry ) )
+ rStateSet.AddState( AccessibleStateType::VISIBLE );
+ if ( IsSelected( pEntry ) )
+ rStateSet.AddState( AccessibleStateType::SELECTED );
+ if ( IsEnabled() )
+ {
+ rStateSet.AddState( AccessibleStateType::ENABLED );
+ rStateSet.AddState( AccessibleStateType::FOCUSABLE );
+ rStateSet.AddState( AccessibleStateType::SELECTABLE );
+ SvViewDataEntry* pViewDataNewCur = GetViewDataEntry(pEntry);
+ if (pViewDataNewCur && pViewDataNewCur->HasFocus())
+ rStateSet.AddState( AccessibleStateType::FOCUSED );
+ }
+}
+
+tools::Rectangle SvTreeListBox::GetBoundingRect( SvTreeListEntry* pEntry )
+{
+ Point aPos = GetEntryPosition( pEntry );
+ tools::Rectangle aRect = GetFocusRect( pEntry, aPos.Y() );
+ return aRect;
+}
+
+void SvTreeListBox::EnableCellFocus()
+{
+ pImpl->EnableCellFocus();
+}
+
+void SvTreeListBox::CallImplEventListeners(VclEventId nEvent, void* pData)
+{
+ CallEventListeners(nEvent, pData);
+}
+
+void SvTreeListBox::set_min_width_in_chars(sal_Int32 nChars)
+{
+ nMinWidthInChars = nChars;
+ queue_resize();
+}
+
+bool SvTreeListBox::set_property(const OString &rKey, const OUString &rValue)
+{
+ if (rKey == "min-width-chars")
+ {
+ set_min_width_in_chars(rValue.toInt32());
+ }
+ else
+ return Control::set_property(rKey, rValue);
+ return true;
+}
+
+FactoryFunction SvTreeListBox::GetUITestFactory() const
+{
+ return TreeListUIObject::create;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/treelistentry.cxx b/vcl/source/treelist/treelistentry.cxx
new file mode 100644
index 000000000000..8bcca01a5eaf
--- /dev/null
+++ b/vcl/source/treelist/treelistentry.cxx
@@ -0,0 +1,241 @@
+/* -*- 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 <memory>
+#include <vcl/treelistentry.hxx>
+#include <vcl/treelist.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <limits>
+
+void SvTreeListEntry::ClearChildren()
+{
+ m_Children.clear();
+}
+
+void SvTreeListEntry::SetListPositions()
+{
+ sal_uLong nCur = 0;
+ for (auto const& pEntry : m_Children)
+ {
+ SvTreeListEntry& rEntry = *pEntry;
+ rEntry.nListPos &= 0x80000000;
+ rEntry.nListPos |= nCur;
+ ++nCur;
+ }
+
+ nListPos &= (~0x80000000); // remove the invalid bit.
+}
+
+void SvTreeListEntry::InvalidateChildrensListPositions()
+{
+ nListPos |= 0x80000000;
+}
+
+SvTreeListEntry::SvTreeListEntry()
+ : pParent(nullptr)
+ , nAbsPos(0)
+ , nListPos(0)
+ , pUserData(nullptr)
+ , nEntryFlags(SvTLEntryFlags::NONE)
+ , maBackColor(Application::GetSettings().GetStyleSettings().GetWindowColor())
+{
+}
+
+SvTreeListEntry::~SvTreeListEntry()
+{
+#ifdef DBG_UTIL
+ pParent = nullptr;
+#endif
+
+ m_Children.clear();
+ m_Items.clear();
+}
+
+bool SvTreeListEntry::HasChildren() const
+{
+ return !m_Children.empty();
+}
+
+bool SvTreeListEntry::HasChildListPos() const
+{
+ return pParent && !(pParent->nListPos & 0x80000000);
+}
+
+sal_uLong SvTreeListEntry::GetChildListPos() const
+{
+ if( pParent && (pParent->nListPos & 0x80000000) )
+ pParent->SetListPositions();
+ return ( nListPos & 0x7fffffff );
+}
+
+
+void SvTreeListEntry::Clone(SvTreeListEntry* pSource)
+{
+ nListPos &= 0x80000000;
+ nListPos |= ( pSource->nListPos & 0x7fffffff);
+ nAbsPos = pSource->nAbsPos;
+
+ m_Items.clear();
+ for (auto const& it : pSource->m_Items)
+ {
+ SvLBoxItem* pItem = &(*it);
+ std::unique_ptr<SvLBoxItem> pNewItem(pItem->Clone(pItem));
+ m_Items.push_back(std::move(pNewItem));
+ }
+
+ pUserData = pSource->GetUserData();
+ nEntryFlags = pSource->nEntryFlags;
+}
+
+size_t SvTreeListEntry::ItemCount() const
+{
+ return m_Items.size();
+}
+
+void SvTreeListEntry::AddItem(std::unique_ptr<SvLBoxItem> pItem)
+{
+ m_Items.push_back(std::move(pItem));
+}
+
+void SvTreeListEntry::EnableChildrenOnDemand( bool bEnable )
+{
+ if ( bEnable )
+ nEntryFlags |= SvTLEntryFlags::CHILDREN_ON_DEMAND;
+ else
+ nEntryFlags &= (~SvTLEntryFlags::CHILDREN_ON_DEMAND);
+}
+
+void SvTreeListEntry::ReplaceItem(std::unique_ptr<SvLBoxItem> pNewItem, size_t const nPos)
+{
+ DBG_ASSERT(pNewItem,"ReplaceItem:No Item");
+ if (nPos >= m_Items.size())
+ {
+ // Out of bound. Bail out.
+ pNewItem.reset();
+ return;
+ }
+
+ m_Items.erase(m_Items.begin()+nPos);
+ m_Items.insert(m_Items.begin()+nPos, std::move(pNewItem));
+}
+
+const SvLBoxItem& SvTreeListEntry::GetItem( size_t nPos ) const
+{
+ return *m_Items[nPos];
+}
+
+SvLBoxItem& SvTreeListEntry::GetItem( size_t nPos )
+{
+ return *m_Items[nPos];
+}
+
+namespace {
+
+class FindByType
+{
+ SvLBoxItemType const meType;
+public:
+ explicit FindByType(SvLBoxItemType eType) : meType(eType) {}
+ bool operator() (const std::unique_ptr<SvLBoxItem>& rpItem) const
+ {
+ return rpItem->GetType() == meType;
+ }
+};
+
+class FindByPointer
+{
+ const SvLBoxItem* mpItem;
+public:
+ explicit FindByPointer(const SvLBoxItem* p) : mpItem(p) {}
+ bool operator() (const std::unique_ptr<SvLBoxItem>& rpItem) const
+ {
+ return rpItem.get() == mpItem;
+ }
+};
+
+}
+
+const SvLBoxItem* SvTreeListEntry::GetFirstItem(SvLBoxItemType eType) const
+{
+ ItemsType::const_iterator it = std::find_if(m_Items.begin(), m_Items.end(), FindByType(eType));
+ return (it == m_Items.end()) ? nullptr : (*it).get();
+}
+
+SvLBoxItem* SvTreeListEntry::GetFirstItem(SvLBoxItemType eType)
+{
+ ItemsType::iterator it = std::find_if(m_Items.begin(), m_Items.end(), FindByType(eType));
+ return (it == m_Items.end()) ? nullptr : (*it).get();
+}
+
+size_t SvTreeListEntry::GetPos( const SvLBoxItem* pItem ) const
+{
+ ItemsType::const_iterator it = std::find_if(m_Items.begin(), m_Items.end(), FindByPointer(pItem));
+ return it == m_Items.end() ? ITEM_NOT_FOUND : std::distance(m_Items.begin(), it);
+}
+
+
+void SvTreeListEntry::SetUserData( void* pPtr )
+{
+ pUserData = pPtr;
+}
+
+bool SvTreeListEntry::HasChildrenOnDemand() const
+{
+ return static_cast<bool>(nEntryFlags & SvTLEntryFlags::CHILDREN_ON_DEMAND);
+}
+
+bool SvTreeListEntry::HasInUseEmphasis() const
+{
+ return static_cast<bool>(nEntryFlags & SvTLEntryFlags::IN_USE);
+}
+
+
+void SvTreeListEntry::SetFlags( SvTLEntryFlags nFlags )
+{
+ nEntryFlags = nFlags;
+}
+
+SvTreeListEntry* SvTreeListEntry::NextSibling() const
+{
+ SvTreeListEntries& rList = pParent->m_Children;
+ sal_uLong nPos = GetChildListPos();
+ nPos++;
+ return (nPos < rList.size()) ? rList[nPos].get() : nullptr;
+}
+
+SvTreeListEntry* SvTreeListEntry::PrevSibling() const
+{
+ SvTreeListEntries& rList = pParent->m_Children;
+ sal_uLong nPos = GetChildListPos();
+ if ( nPos == 0 )
+ return nullptr;
+ nPos--;
+ return rList[nPos].get();
+}
+
+
+SvTreeListEntry* SvTreeListEntry::LastSibling() const
+{
+ SvTreeListEntries& rChildren = pParent->m_Children;
+ return (rChildren.empty()) ? nullptr : rChildren.back().get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/uiobject.cxx b/vcl/source/treelist/uiobject.cxx
new file mode 100644
index 000000000000..2c6312861865
--- /dev/null
+++ b/vcl/source/treelist/uiobject.cxx
@@ -0,0 +1,170 @@
+/* -*- 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/.
+ */
+
+#include <memory>
+#include <vcl/uitest/uiobject.hxx>
+#include <vcl/treelistbox.hxx>
+
+TreeListUIObject::TreeListUIObject(const VclPtr<SvTreeListBox>& xTreeList):
+ WindowUIObject(xTreeList),
+ mxTreeList(xTreeList)
+{
+}
+
+namespace {
+
+bool isCheckBoxList(const VclPtr<SvTreeListBox>& xTreeList)
+{
+ return (xTreeList->GetTreeFlags() & SvTreeFlags::CHKBTN) == SvTreeFlags::CHKBTN;
+}
+
+}
+
+StringMap TreeListUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+
+ aMap["SelectionCount"] = OUString::number(mxTreeList->GetSelectionCount());
+ aMap["VisibleCount"] = OUString::number(mxTreeList->GetVisibleCount());
+ aMap["Children"] = OUString::number(mxTreeList->GetChildCount(nullptr));
+ aMap["LevelChildren"] = OUString::number(mxTreeList->GetLevelChildCount(nullptr));
+ aMap["CheckBoxList"] = OUString::boolean(isCheckBoxList(mxTreeList));
+ return aMap;
+}
+
+void TreeListUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction.isEmpty())
+ {
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+std::unique_ptr<UIObject> TreeListUIObject::get_child(const OUString& rID)
+{
+ sal_Int32 nID = rID.toInt32();
+ if (nID >= 0)
+ {
+ SvTreeListEntry* pEntry = mxTreeList->GetEntry(nullptr, nID);
+ if (!pEntry)
+ return nullptr;
+
+ return std::unique_ptr<UIObject>(new TreeListEntryUIObject(mxTreeList, pEntry));
+ }
+
+ return nullptr;
+}
+
+std::set<OUString> TreeListUIObject::get_children() const
+{
+ std::set<OUString> aChildren;
+
+ size_t nChildren = mxTreeList->GetLevelChildCount(nullptr);
+ for (size_t i = 0; i < nChildren; ++i)
+ {
+ aChildren.insert(OUString::number(i));
+ }
+
+ return aChildren;
+}
+
+OUString TreeListUIObject::get_name() const
+{
+ return OUString("TreeListUIObject");
+}
+
+std::unique_ptr<UIObject> TreeListUIObject::create(vcl::Window* pWindow)
+{
+ SvTreeListBox* pTreeList = dynamic_cast<SvTreeListBox*>(pWindow);
+ assert(pTreeList);
+ return std::unique_ptr<UIObject>(new TreeListUIObject(pTreeList));
+}
+
+TreeListEntryUIObject::TreeListEntryUIObject(const VclPtr<SvTreeListBox>& xTreeList, SvTreeListEntry* pEntry):
+ mxTreeList(xTreeList),
+ mpEntry(pEntry)
+{
+}
+
+StringMap TreeListEntryUIObject::get_state()
+{
+ StringMap aMap;
+
+ aMap["Text"] = mxTreeList->GetEntryText(mpEntry);
+ aMap["Children"] = OUString::number(mxTreeList->GetLevelChildCount(mpEntry));
+ aMap["VisibleChildCount"] = OUString::number(mxTreeList->GetVisibleChildCount(mpEntry));
+
+ return aMap;
+}
+
+void TreeListEntryUIObject::execute(const OUString& rAction, const StringMap& /*rParameters*/)
+{
+ if (rAction == "COLLAPSE")
+ {
+ mxTreeList->Collapse(mpEntry);
+ }
+ else if (rAction == "EXPAND")
+ {
+ mxTreeList->Expand(mpEntry);
+ }
+ else if (rAction == "SELECT")
+ {
+ mxTreeList->Select(mpEntry);
+ }
+ else if (rAction == "DESELECT")
+ {
+ mxTreeList->Select(mpEntry, false);
+ }
+ else if (rAction == "CLICK")
+ {
+ if (!isCheckBoxList(mxTreeList))
+ return;
+ SvButtonState eState = mxTreeList->GetCheckButtonState(mpEntry);
+ eState = eState == SvButtonState::Checked ? SvButtonState::Unchecked : SvButtonState::Checked;
+ mxTreeList->SetCheckButtonState(mpEntry, eState);
+ mxTreeList->CheckButtonHdl();
+ }
+}
+
+std::unique_ptr<UIObject> TreeListEntryUIObject::get_child(const OUString& rID)
+{
+ sal_Int32 nID = rID.toInt32();
+ if (nID >= 0)
+ {
+ SvTreeListEntry* pEntry = mxTreeList->GetEntry(mpEntry, nID);
+ if (!pEntry)
+ return nullptr;
+
+ return std::unique_ptr<UIObject>(new TreeListEntryUIObject(mxTreeList, pEntry));
+ }
+
+ return nullptr;
+}
+
+std::set<OUString> TreeListEntryUIObject::get_children() const
+{
+ std::set<OUString> aChildren;
+
+ size_t nChildren = mxTreeList->GetLevelChildCount(mpEntry);
+ for (size_t i = 0; i < nChildren; ++i)
+ {
+ aChildren.insert(OUString::number(i));
+ }
+
+ return aChildren;
+}
+
+OUString TreeListEntryUIObject::get_type() const
+{
+ return OUString("TreeListEntry");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/viewdataentry.cxx b/vcl/source/treelist/viewdataentry.cxx
new file mode 100644
index 000000000000..b6eb6d79562c
--- /dev/null
+++ b/vcl/source/treelist/viewdataentry.cxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/viewdataentry.hxx>
+
+SvViewDataEntry::SvViewDataEntry() :
+ nVisPos(0),
+ mbSelected(false),
+ mbHighlighted(false),
+ mbExpanded(false),
+ mbFocused(false),
+ mbSelectable(true),
+ maPaintRectangle()
+{
+}
+
+SvViewDataEntry::SvViewDataEntry( const SvViewDataEntry& rData ) :
+ nVisPos(rData.nVisPos),
+ mbSelected(false),
+ mbHighlighted(false),
+ mbExpanded(rData.mbExpanded),
+ mbFocused(false),
+ mbSelectable(rData.mbSelectable),
+ maPaintRectangle(rData.maPaintRectangle)
+{
+}
+
+SvViewDataEntry::~SvViewDataEntry()
+{
+#ifdef DBG_UTIL
+ nVisPos = 0x12345678;
+#endif
+}
+
+void SvViewDataEntry::SetFocus( bool bFocus )
+{
+ mbFocused = bFocus;
+}
+
+void SvViewDataEntry::SetSelected( bool bSelected )
+{
+ mbSelected = bSelected;
+ mbHighlighted = bSelected;
+}
+
+void SvViewDataEntry::SetHighlighted( bool bHighlighted )
+{
+ mbHighlighted = bHighlighted;
+}
+
+void SvViewDataEntry::SetExpanded( bool bExpanded )
+{
+ mbExpanded = bExpanded;
+}
+
+void SvViewDataEntry::SetSelectable( bool bSelectable )
+{
+ mbSelectable = bSelectable;
+}
+
+void SvViewDataEntry::Init(size_t nSize)
+{
+ maItems.resize(nSize);
+}
+
+const SvViewDataItem& SvViewDataEntry::GetItem(size_t nPos) const
+{
+ return maItems[nPos];
+}
+
+SvViewDataItem& SvViewDataEntry::GetItem(size_t nPos)
+{
+ return maItems[nPos];
+}
+
+void SvViewDataEntry::SetPaintRectangle(tools::Rectangle aRectangle)
+{
+ maPaintRectangle = aRectangle;
+}
+
+const tools::Rectangle& SvViewDataEntry::GetPaintRectangle() const
+{
+ return maPaintRectangle;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/accessibility.cxx b/vcl/source/window/accessibility.cxx
index 069cac8e11e7..de117cce9554 100644
--- a/vcl/source/window/accessibility.cxx
+++ b/vcl/source/window/accessibility.cxx
@@ -143,7 +143,7 @@ void Window::SetAccessible( const css::uno::Reference< css::accessibility::XAcce
mpWindowImpl->mxAccessible = x;
}
-// skip all border windows that are no top level frames
+// skip all border windows that are not top level frames
bool Window::ImplIsAccessibleCandidate() const
{
if( !mpWindowImpl->mbBorderWin )
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index e3c317099000..df0b82a9b6f6 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -1809,11 +1809,10 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString &
else if (name == "GtkTreeView")
{
//To-Do
- //a) move svtools SvTreeViewBox into vcl
- //b) make that the default target for GtkTreeView
- //c) remove the non-drop down mode of ListBox and convert
+ //a) make SvTreeViewBox the default target for GtkTreeView
+ //b) remove the non-drop down mode of ListBox and convert
// everything over to SvTreeViewBox
- //d) remove the users of makeSvTreeViewBox
+ //c) remove the users of makeSvTreeViewBox
extractModel(id, rMap);
WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_SIMPLEMODE;
if (m_bLegacy)