Quelle vbarange.cxx
Sprache: C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include "vbarange.hxx"
#include <comphelper/types.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <o3tl/any.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/unit_conversion.hxx>
#include <rtl/math.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <o3tl/string_view.hxx>
#include <com/sun/star/script/ArrayWrapper.hpp>
#include <com/sun/star/script/XTypeConverter.hpp>
#include <com/sun/star/script/vba/VBAEventId.hpp>
#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
#include <com/sun/star/sheet/XDatabaseRange.hpp>
#include <com/sun/star/sheet/XUnnamedDatabaseRanges.hpp>
#include <com/sun/star/sheet/XSheetOperation.hpp>
#include <com/sun/star/sheet/CellFlags.hpp>
#include <com/sun/star/table/XColumnRowRange.hpp>
#include <com/sun/star/sheet/XCellAddressable.hpp>
#include <com/sun/star/table/CellContentType.hpp>
#include <com/sun/star/sheet/XCellSeries.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
#include <com/sun/star/table/CellAddress.hpp>
#include <com/sun/star/table/CellRangeAddress.hpp>
#include <com/sun/star/sheet/XSpreadsheetView.hpp>
#include <com/sun/star/sheet/XCellRangeReferrer.hpp>
#include <com/sun/star/sheet/XSheetCellRange.hpp>
#include <com/sun/star/sheet/XSpreadsheet.hpp>
#include <com/sun/star/sheet/XSheetCellCursor.hpp>
#include <com/sun/star/sheet/XArrayFormulaRange.hpp>
#include <com/sun/star/sheet/XNamedRange.hpp>
#include <com/sun/star/sheet/XNamedRanges.hpp>
#include <com/sun/star/sheet/XPrintAreas.hpp>
#include <com/sun/star/sheet/XCellRangesQuery.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/table/XTableRows.hpp>
#include <com/sun/star/table/XTableColumns.hpp>
#include <com/sun/star/table/TableSortField.hpp>
#include <com/sun/star/util/XMergeable.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <com/sun/star/util/XNumberFormats.hpp>
#include <com/sun/star/util/NumberFormat.hpp>
#include <com/sun/star/util/XNumberFormatTypes.hpp>
#include <com/sun/star/util/XReplaceable.hpp>
#include <com/sun/star/util/XSortable.hpp>
#include <com/sun/star/sheet/XCellRangeMovement.hpp>
#include <com/sun/star/sheet/FormulaResult.hpp>
#include <com/sun/star/sheet/FilterOperator2.hpp>
#include <com/sun/star/sheet/TableFilterField2.hpp>
#include <com/sun/star/sheet/XSheetFilterDescriptor2.hpp>
#include <com/sun/star/sheet/FilterConnection.hpp>
#include <com/sun/star/util/TriState.hpp>
#include <com/sun/star/sheet/XSubTotalCalculatable.hpp>
#include <com/sun/star/sheet/XSubTotalDescriptor.hpp>
#include <com/sun/star/sheet/GeneralFunction.hpp>
#include <com/sun/star/sheet/XSheetAnnotationsSupplier.hpp>
#include <com/sun/star/sheet/XSheetAnnotations.hpp>
#include <ooo/vba/excel/XlPasteSpecialOperation.hpp>
#include <ooo/vba/excel/XlPasteType.hpp>
#include <ooo/vba/excel/XlFindLookIn.hpp>
#include <ooo/vba/excel/XlLookAt.hpp>
#include <ooo/vba/excel/XlSearchOrder.hpp>
#include <ooo/vba/excel/XlSortOrder.hpp>
#include <ooo/vba/excel/XlYesNoGuess.hpp>
#include <ooo/vba/excel/XlSortOrientation.hpp>
#include <ooo/vba/excel/XlSortMethod.hpp>
#include <ooo/vba/excel/XlDirection.hpp>
#include <ooo/vba/excel/XlSortDataOption.hpp>
#include <ooo/vba/excel/XlDeleteShiftDirection.hpp>
#include <ooo/vba/excel/XlInsertShiftDirection.hpp>
#include <ooo/vba/excel/XlReferenceStyle.hpp>
#include <ooo/vba/excel/XlBordersIndex.hpp>
#include <ooo/vba/excel/XlPageBreak.hpp>
#include <ooo/vba/excel/XlAutoFilterOperator.hpp>
#include <ooo/vba/excel/XlAutoFillType.hpp>
#include <ooo/vba/excel/XlCellType.hpp>
#include <ooo/vba/excel/XlSpecialCellsValue.hpp>
#include <ooo/vba/excel/XlConsolidationFunction.hpp>
#include <ooo/vba/excel/XlSearchDirection.hpp>
#include <scitems.hxx>
#include <svl/srchitem.hxx>
#include <cellsuno.hxx>
#include <dbdata.hxx>
#include <docfunc.hxx>
#include <columnspanset.hxx>
#include <queryparam.hxx>
#include <sortparam.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/app.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/viewfrm.hxx>
#include <sc.hrc>
#include <unonames.hxx>
#include "excelvbahelper.hxx"
#include "vbaapplication.hxx"
#include "vbafont.hxx"
#include "vbacomment.hxx"
#include "vbainterior.hxx"
#include "vbacharacters.hxx"
#include "vbaborders.hxx"
#include "vbaworksheet.hxx"
#include "vbavalidation.hxx"
#include "vbahyperlinks.hxx"
#include <tabvwsh.hxx>
#include <rangelst.hxx>
#include <convuno.hxx>
#include <compiler.hxx>
#include <patattr.hxx>
#include <olinetab.hxx>
#include <transobj.hxx>
#include <queryentry.hxx>
#include <markdata.hxx>
#include <basic/sberrors.hxx>
#include <cppuhelper/implbase.hxx>
#include <global.hxx>
#include "vbastyle.hxx"
#include "vbaname.hxx"
#include <utility>
#include <vector>
#include <vbahelper/vbacollectionimpl.hxx>
#include <com/sun/star/bridge/oleautomation/Date.hpp>
#include <tokenarray.hxx>
#include <tokenuno.hxx>
#include <memory>
using namespace ::ooo::vba;
using namespace ::com::sun::star;
using ::std::vector;
// difference between VBA and file format width, in character units
const double fExtraWidth = 182.0 / 256.0;
const sal_Int16 supportedIndexTable[] = { excel::XlBordersIndex::xlEdgeLeft, excel::Xl
BordersIndex::xlEdgeTop, excel::XlBordersIndex::xlEdgeBottom, excel::XlBordersIndex::xlEdgeRight, excel::XlBordersIndex::xlDiagonalDown, excel::XlBordersIndex::xlDiagonalUp, excel::XlBordersIndex::xlInsideVertical, excel::XlBordersIndex::xlInsideHorizontal };
static sal_uInt16 lcl_pointsToTwips( double nVal )
{
nVal = nVal * static_cast <double >(20);
short nTwips = static_cast <short >(nVal);
return nTwips;
}
static double lcl_TwipsToPoints( sal_uInt16 nVal )
{
double nPoints = nVal;
return nPoints / 20;
}
static double lcl_Round2DecPlaces( double nVal )
{
nVal = (nVal * double (100));
tools::Long tmp = static_cast <tools::Long >(nVal);
if ( ( nVal - tmp ) >= 0.5 )
++tmp;
nVal = double (tmp)/100;
return nVal;
}
static uno::Any lcl_makeRange( const uno::Reference< XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Any& rAny, bool bIsRows, bool bIsColumns )
{
uno::Reference< table::XCellRange > xCellRange(rAny, uno::UNO_QUERY_THROW);
return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( rParent, rContext, xCellRange, bIsRows, bIsColumns ) ) );
}
static rtl::Reference< ScVbaRange > lcl_makeXRangeFromSheetCellRanges( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRanges >& xLocSheetCellRanges, ScDocShell* pDoc )
{
rtl::Reference< ScVbaRange > xRange;
const uno::Sequence< table::CellRangeAddress > sAddresses = xLocSheetCellRanges->getRangeAddresses();
ScRangeList aCellRanges;
if ( sAddresses.hasElements() )
{
for ( const auto & rAddress : sAddresses )
{
ScRange refRange;
ScUnoConversion::FillScRange( refRange, rAddress );
aCellRanges.push_back( refRange );
}
// Single range
if ( aCellRanges.size() == 1 )
{
uno::Reference< table::XCellRange > xTmpRange( new ScCellRangeObj( pDoc, aCellRanges.front() ) );
xRange = new ScVbaRange( xParent, xContext, xTmpRange );
}
else
{
uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDoc, aCellRanges ) );
xRange = new ScVbaRange( xParent, xContext, xRanges );
}
}
return xRange;
}
ScCellRangesBase* ScVbaRange::getCellRangesBase()
{
if ( mxRanges.is() )
return dynamic_cast <ScCellRangesBase*>( mxRanges.get() );
if ( mxRange.is() )
return dynamic_cast <ScCellRangesBase*>( mxRange.get() );
throw uno::RuntimeException(u"General Error creating range - Unknown" _ustr );
}
ScCellRangeObj* ScVbaRange::getCellRangeObj()
{
return dynamic_cast < ScCellRangeObj* >( getCellRangesBase() );
}
SfxItemSet* ScVbaRange::getCurrentDataSet( )
{
SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( getCellRangesBase() );
if ( !pDataSet )
throw uno::RuntimeException(u"Can't access Itemset for range" _ustr );
return pDataSet;
}
void ScVbaRange::fireChangeEvent()
{
if ( !ScVbaApplication::getDocumentEventsEnabled() )
return ;
ScDocument& rDoc = getScDocument();
const uno::Reference< script::vba::XVBAEventProcessor >& xVBAEvents = rDoc.GetVbaEventProcessor();
if ( xVBAEvents.is() ) try
{
uno::Sequence< uno::Any > aArgs{ uno::Any(uno::Reference< excel::XRange >( this )) };
xVBAEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_CHANGE, aArgs );
}
catch ( uno::Exception& )
{
}
}
namespace {
class SingleRangeEnumeration : public EnumerationHelper_BASE
{
uno::Reference< table::XCellRange > m_xRange;
bool bHasMore;
public :
/// @throws uno::RuntimeException
explicit SingleRangeEnumeration( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )), bHasMore( true ) { }
virtual sal_Bool SAL_CALL hasMoreElements( ) override { return bHasMore; }
virtual uno::Any SAL_CALL nextElement( ) override
{
if ( !bHasMore )
throw container::NoSuchElementException();
bHasMore = false ;
return uno::Any( m_xRange );
}
};
// very simple class to pass to ScVbaCollectionBaseImpl containing
// just one item
class SingleRangeIndexAccess : public ::cppu::WeakImplHelper< container::XIndexAccess,
container::XEnumerationAccess >
{
private :
uno::Reference< table::XCellRange > m_xRange;
public :
explicit SingleRangeIndexAccess( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )) {}
// XIndexAccess
virtual ::sal_Int32 SAL_CALL getCount() override { return 1; }
virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
{
if ( Index != 0 )
throw lang::IndexOutOfBoundsException();
return uno::Any( m_xRange );
}
// XElementAccess
virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<table::XCellRange>::get(); }
virtual sal_Bool SAL_CALL hasElements() override { return true ; }
// XEnumerationAccess
virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override { return new SingleRangeEnumeration( m_xRange ); }
};
class RangesEnumerationImpl : public EnumerationHelperImpl
{
bool mbIsRows;
bool mbIsColumns;
public :
/// @throws uno::RuntimeException
RangesEnumerationImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, bool bIsRows, bool bIsColumns ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), mbIsRows( bIsRows ), mbIsColumns( bIsColumns ) {}
virtual uno::Any SAL_CALL nextElement( ) override
{
return lcl_makeRange( m_xParent, m_xContext, m_xEnumeration->nextElement(), mbIsRows, mbIsColumns );
}
};
}
class ScVbaRangeAreas : public ScVbaCollectionBaseImpl
{
bool mbIsRows;
bool mbIsColumns;
public :
ScVbaRangeAreas( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XIndexAccess >& xIndexAccess, bool bIsRows, bool bIsColumns ) : ScVbaCollectionBaseImpl( xParent, xContext, xIndexAccess ), mbIsRows( bIsRows ), mbIsColumns( bIsColumns ) {}
// XEnumerationAccess
virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override;
// XElementAccess
virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<excel::XRange>::get(); }
virtual uno::Any createCollectionObject( const uno::Any& aSource ) override;
virtual OUString getServiceImplName() override { return OUString(); }
virtual uno::Sequence< OUString > getServiceNames() override { return uno::Sequence< OUString >(); }
};
uno::Reference< container::XEnumeration > SAL_CALL
ScVbaRangeAreas::createEnumeration()
{
uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
return new RangesEnumerationImpl( mxParent, mxContext, xEnumAccess->createEnumeration(), mbIsRows, mbIsColumns );
}
uno::Any
ScVbaRangeAreas::createCollectionObject( const uno::Any& aSource )
{
return lcl_makeRange( mxParent, mxContext, aSource, mbIsRows, mbIsColumns );
}
// assume that xIf is in fact a ScCellRangesBase
/// @throws uno::RuntimeException
static ScDocShell*
getDocShellFromIf( const uno::Reference< uno::XInterface >& xIf )
{
ScCellRangesBase* pUno = dynamic_cast <ScCellRangesBase*>( xIf.get() );
if ( !pUno )
throw uno::RuntimeException(u"Failed to access underlying uno range object" _ustr );
return pUno->GetDocShell();
}
/// @throws uno::RuntimeException
static ScDocShell*
getDocShellFromRange( const uno::Reference< table::XCellRange >& xRange )
{
// need the ScCellRangesBase to get docshell
uno::Reference< uno::XInterface > xIf( xRange );
return getDocShellFromIf(xIf );
}
/// @throws uno::RuntimeException
static ScDocShell*
getDocShellFromRanges( const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges )
{
// need the ScCellRangesBase to get docshell
uno::Reference< uno::XInterface > xIf( xRanges );
return getDocShellFromIf(xIf );
}
/// @throws uno::RuntimeException
static uno::Reference< frame::XModel > getModelFromXIf( const uno::Reference< uno::XInterface >& xIf )
{
ScDocShell* pDocShell = getDocShellFromIf(xIf );
return pDocShell->GetModel();
}
/// @throws uno::RuntimeException
static uno::Reference< frame::XModel > getModelFromRange( const uno::Reference< table::XCellRange >& xRange )
{
// the XInterface for getImplementation can be any derived interface, no need for queryInterface
uno::Reference< uno::XInterface > xIf( xRange );
return getModelFromXIf( xIf );
}
static ScDocument&
getDocumentFromRange( const uno::Reference< table::XCellRange >& xRange )
{
ScDocShell* pDocShell = getDocShellFromRange( xRange );
if ( !pDocShell )
throw uno::RuntimeException(u"Failed to access underlying docshell from uno range object" _ustr );
ScDocument& rDoc = pDocShell->GetDocument();
return rDoc;
}
ScDocument&
ScVbaRange::getScDocument()
{
if ( mxRanges.is() )
{
uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
return getDocumentFromRange( xRange );
}
return getDocumentFromRange( mxRange );
}
ScDocShell*
ScVbaRange::getScDocShell()
{
if ( mxRanges.is() )
{
uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
return getDocShellFromRange( xRange );
}
return getDocShellFromRange( mxRange );
}
ScVbaRange* ScVbaRange::getImplementation( const uno::Reference< excel::XRange >& rxRange )
{
// FIXME: always save to use dynamic_cast? Or better to (implement and) use XTunnel?
return dynamic_cast < ScVbaRange* >( rxRange.get() );
}
uno::Reference< frame::XModel > ScVbaRange::getUnoModel()
{
if ( ScDocShell* pDocShell = getScDocShell() )
return pDocShell->GetModel();
throw uno::RuntimeException();
}
uno::Reference< frame::XModel > ScVbaRange::getUnoModel( const uno::Reference< excel::XRange >& rxRange )
{
if ( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
return pScVbaRange->getUnoModel();
throw uno::RuntimeException();
}
const ScRangeList& ScVbaRange::getScRangeList()
{
if ( ScCellRangesBase* pScRangesBase = getCellRangesBase() )
return pScRangesBase->GetRangeList();
throw uno::RuntimeException(u"Cannot obtain UNO range implementation object" _ustr );
}
const ScRangeList& ScVbaRange::getScRangeList( const uno::Reference< excel::XRange >& rxRange )
{
if ( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
return pScVbaRange->getScRangeList();
throw uno::RuntimeException(u"Cannot obtain VBA range implementation object" _ustr );
}
namespace {
class NumFormatHelper
{
uno::Reference< util::XNumberFormatsSupplier > mxSupplier;
uno::Reference< beans::XPropertySet > mxRangeProps;
uno::Reference< util::XNumberFormats > mxFormats;
public :
explicit NumFormatHelper( const uno::Reference< table::XCellRange >& xRange )
{
mxSupplier.set( getModelFromRange( xRange ), uno::UNO_QUERY_THROW );
mxRangeProps.set( xRange, uno::UNO_QUERY_THROW);
mxFormats = mxSupplier->getNumberFormats();
}
uno::Reference< beans::XPropertySet > getNumberProps()
{
tools::Long nIndexKey = 0;
uno::Any aValue = mxRangeProps->getPropertyValue( u"NumberFormat" _ustr );
aValue >>= nIndexKey;
if ( mxFormats.is() )
return mxFormats->getByKey( nIndexKey );
return uno::Reference< beans::XPropertySet > ();
}
bool isBooleanType()
{
return (getNumberFormat() & util::NumberFormat::LOGICAL) != 0;
}
bool isDateType()
{
sal_Int16 nType = getNumberFormat();
return ( nType & util::NumberFormat::DATETIME ) != 0;
}
OUString getNumberFormatString()
{
uno::Reference< uno::XInterface > xIf( mxRangeProps, uno::UNO_QUERY_THROW );
ScCellRangesBase* pUnoCellRange = dynamic_cast <ScCellRangesBase*>( xIf.get() );
if ( pUnoCellRange )
{
SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( pUnoCellRange );
SfxItemState eState = pDataSet->GetItemState( ATTR_VALUE_FORMAT);
// one of the cells in the range is not like the other ;-)
// so return a zero length format to indicate that
if ( eState == SfxItemState::INVALID )
return OUString();
}
uno::Reference< beans::XPropertySet > xNumberProps( getNumberProps(), uno::UNO_SET_THROW );
OUString aFormatString;
uno::Any aString = xNumberProps->getPropertyValue( u"FormatString" _ustr );
aString >>= aFormatString;
return aFormatString;
}
sal_Int16 getNumberFormat()
{
uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
sal_Int16 nType = ::comphelper::getINT16(
xNumberProps->getPropertyValue( u"Type" _ustr ) );
return nType;
}
void setNumberFormat( const OUString& rFormat )
{
// #163288# treat "General" as "Standard" format
sal_Int32 nNewIndex = 0;
if ( !rFormat.equalsIgnoreAsciiCase( "General" ) )
{
lang::Locale aLocale;
uno::Reference< beans::XPropertySet > xNumProps = getNumberProps();
xNumProps->getPropertyValue( u"Locale" _ustr ) >>= aLocale;
nNewIndex = mxFormats->queryKey( rFormat, aLocale, false );
if ( nNewIndex == -1 ) // format not defined
nNewIndex = mxFormats->addNew( rFormat, aLocale );
}
mxRangeProps->setPropertyValue( u"NumberFormat" _ustr, uno::Any( nNewIndex ) );
}
void setNumberFormat( sal_Int16 nType )
{
uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
lang::Locale aLocale;
xNumberProps->getPropertyValue( u"Locale" _ustr ) >>= aLocale;
uno::Reference<util::XNumberFormatTypes> xTypes( mxFormats, uno::UNO_QUERY );
if ( xTypes.is() )
{
sal_Int32 nNewIndex = xTypes->getStandardFormat( nType, aLocale );
mxRangeProps->setPropertyValue( u"NumberFormat" _ustr, uno::Any( nNewIndex ) );
}
}
};
struct CellPos
{
CellPos( sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nArea ):m_nRow(nRow), m_nCol(nCol), m_nArea( nArea ) {};
sal_Int32 m_nRow;
sal_Int32 m_nCol;
sal_Int32 m_nArea;
};
}
typedef ::cppu::WeakImplHelper< container::XEnumeration > CellsEnumeration_BASE;
typedef ::std::vector< CellPos > vCellPos;
namespace {
// #FIXME - QUICK
// we could probably could and should modify CellsEnumeration below
// to handle rows and columns (but I do this separately for now
// and... this class only handles single areas (does it have to handle
// multi area ranges??)
class ColumnsRowEnumeration: public CellsEnumeration_BASE
{
uno::Reference< excel::XRange > mxRange;
sal_Int32 mMaxElems;
sal_Int32 mCurElem;
public :
ColumnsRowEnumeration( uno::Reference< excel::XRange > xRange, sal_Int32 nElems ) : mxRange(std::move( xRange )), mMaxElems( nElems ), mCurElem( 0 )
{
}
virtual sal_Bool SAL_CALL hasMoreElements() override { return mCurElem < mMaxElems; }
virtual uno::Any SAL_CALL nextElement() override
{
if ( !hasMoreElements() )
throw container::NoSuchElementException();
sal_Int32 vbaIndex = 1 + mCurElem++;
return uno::Any( mxRange->Item( uno::Any( vbaIndex ), uno::Any() ) );
}
};
class CellsEnumeration : public CellsEnumeration_BASE
{
uno::WeakReference< XHelperInterface > mxParent;
uno::Reference< uno::XComponentContext > mxContext;
uno::Reference< XCollection > m_xAreas;
vCellPos m_CellPositions;
vCellPos::const_iterator m_it;
/// @throws uno::RuntimeException
uno::Reference< table::XCellRange > getArea( sal_Int32 nVBAIndex )
{
if ( nVBAIndex < 1 || nVBAIndex > m_xAreas->getCount() )
throw uno::RuntimeException();
uno::Reference< excel::XRange > xRange( m_xAreas->Item( uno::Any(nVBAIndex), uno::Any() ), uno::UNO_QUERY_THROW );
uno::Reference< table::XCellRange > xCellRange( ScVbaRange::getCellRange( xRange ), uno::UNO_QUERY_THROW );
return xCellRange;
}
void populateArea( sal_Int32 nVBAIndex )
{
uno::Reference< table::XCellRange > xRange = getArea( nVBAIndex );
uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, uno::UNO_QUERY_THROW );
sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
for ( sal_Int32 i=0; i<nRowCount; ++i )
{
for ( sal_Int32 j=0; j<nColCount; ++j )
m_CellPositions.emplace_back( i,j,nVBAIndex );
}
}
public :
CellsEnumeration( const uno::Reference< XHelperInterface >& xParent, uno::Reference< uno::XComponentContext > xContext, uno::Reference< XCollection > xAreas ): mxParent( xParent ), mxContext(std::move( xContext )), m_xAreas(std::move( xAreas ))
{
sal_Int32 nItems = m_xAreas->getCount();
for ( sal_Int32 index=1; index <= nItems; ++index )
{
populateArea( index );
}
m_it = m_CellPositions.begin();
}
virtual sal_Bool SAL_CALL hasMoreElements() override { return m_it != m_CellPositions.end(); }
virtual uno::Any SAL_CALL nextElement() override
{
if ( !hasMoreElements() )
throw container::NoSuchElementException();
CellPos aPos = *m_it++;
uno::Reference< table::XCellRange > xRangeArea = getArea( aPos.m_nArea );
uno::Reference< table::XCellRange > xCellRange( xRangeArea->getCellByPosition( aPos.m_nCol, aPos.m_nRow ), uno::UNO_QUERY_THROW );
return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( mxParent, mxContext, xCellRange ) ) );
}
};
}
constexpr OUString ISVISIBLE = u"IsVisible" _ustr;
const char EQUALS[] = "=" ;
const char NOTEQUALS[] = "<>" ;
const char GREATERTHAN[] = ">" ;
const char GREATERTHANEQUALS[] = ">=" ;
const char LESSTHAN[] = "<" ;
const char LESSTHANEQUALS[] = "<=" ;
constexpr OUString STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY(u"The command you chose cannot be performed with multiple selections.\nSelect a single range and click the command again" _ustr);
constexpr OUString CELLSTYLE = u"CellStyle" _ustr;
namespace {
class CellValueSetter : public ValueSetter
{
protected :
uno::Any maValue;
public :
explicit CellValueSetter( uno::Any aValue );
virtual bool processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell ) override;
virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
};
}
CellValueSetter::CellValueSetter( uno::Any aValue ): maValue(std::move( aValue )) {}
void
CellValueSetter::visitNode( sal_Int32 /*i*/, sal_Int32 /*j*/, const uno::Reference< table::XCell >& xCell )
{
processValue( maValue, xCell );
}
bool
CellValueSetter::processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell )
{
bool isExtracted = true ;
switch ( aValue.getValueTypeClass() )
{
case uno::TypeClass_BOOLEAN:
{
bool bState = false ;
if ( aValue >>= bState )
{
uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
if ( bState )
xCell->setValue( double (1) );
else
xCell->setValue( double (0) );
NumFormatHelper cellNumFormat( xRange );
cellNumFormat.setNumberFormat( util::NumberFormat::LOGICAL );
}
break ;
}
case uno::TypeClass_STRING:
{
OUString aString;
if ( aValue >>= aString )
{
// The required behavior for a string value is:
// 1. If the first character is a single quote, use the rest as a string cell, regardless of the cell's number format.
// 2. Otherwise, if the cell's number format is "text", use the string value as a string cell.
// 3. Otherwise, parse the string value in English locale, and apply a corresponding number format with the cell's locale
// if the cell's number format was "General".
// Case 1 is handled here, the rest in ScCellObj::InputEnglishString
if ( aString.toChar() == '\' ' ) // case 1 - handle with XTextRange
{
OUString aRemainder( aString.copy(1) ); // strip the quote
uno::Reference< text::XTextRange > xTextRange( xCell, uno::UNO_QUERY_THROW );
xTextRange->setString( aRemainder );
}
else
{
// call implementation method InputEnglishString
ScCellObj* pCellObj = dynamic_cast < ScCellObj* >( xCell.get() );
if ( pCellObj )
pCellObj->InputEnglishString( aString );
}
}
else
isExtracted = false ;
break ;
}
default :
{
double nDouble = 0.0;
if (!(aValue >>= nDouble))
{
if (css::bridge::oleautomation::Date date; aValue >>= date)
nDouble = date.Value;
else
isExtracted = false ;
}
if (isExtracted)
{
uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
NumFormatHelper cellFormat( xRange );
// If we are setting a number and the cell types was logical
// then we need to reset the logical format. ( see case uno::TypeClass_BOOLEAN:
// handling above )
if ( cellFormat.isBooleanType() )
cellFormat.setNumberFormat(u"General" _ustr);
xCell->setValue( nDouble );
}
break ;
}
}
return isExtracted;
}
namespace {
class CellValueGetter : public ValueGetter
{
protected :
RangeValueType meValueType;
uno::Any maValue;
public :
CellValueGetter(RangeValueType eValueType) { meValueType = eValueType; }
virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
virtual void processValue( const uno::Any& aValue ) override;
const uno::Any& getValue() const override { return maValue; }
};
}
void
CellValueGetter::processValue( const uno::Any& aValue )
{
maValue = aValue;
}
void CellValueGetter::visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell )
{
uno::Any aValue;
table::CellContentType eCellContentType = xCell->getType();
if ( eCellContentType == table::CellContentType_VALUE || eCellContentType == table::CellContentType_FORMULA )
{
if ( eCellContentType == table::CellContentType_FORMULA )
{
OUString sFormula = xCell->getFormula();
if ( sFormula == "=TRUE()" )
aValue <<= true ;
else if ( sFormula == "=FALSE()" )
aValue <<= false ;
else
{
uno::Reference< beans::XPropertySet > xProp( xCell, uno::UNO_QUERY_THROW );
sal_Int32 nResultType = sheet::FormulaResult::VALUE;
// some formulas give textual results
xProp->getPropertyValue( u"FormulaResultType2" _ustr ) >>= nResultType;
if ( nResultType == sheet::FormulaResult::STRING )
{
uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
aValue <<= xTextRange->getString();
}
else
aValue <<= xCell->getValue();
}
}
else
{
uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
NumFormatHelper cellFormat( xRange );
if ( cellFormat.isBooleanType() )
aValue <<= ( xCell->getValue() != 0.0 );
else if ( cellFormat.isDateType() && meValueType == RangeValueType::value)
aValue <<= bridge::oleautomation::Date( xCell->getValue() );
else
aValue <<= xCell->getValue();
}
}
if ( eCellContentType == table::CellContentType_TEXT )
{
uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
aValue <<= xTextRange->getString();
}
processValue( aValue );
}
namespace {
class CellFormulaValueSetter : public CellValueSetter
{
private :
ScDocument& m_rDoc;
formula::FormulaGrammar::Grammar m_eGrammar;
public :
CellFormulaValueSetter( const uno::Any& aValue, ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ):CellValueSetter( aValue ), m_rDoc( rDoc ), m_eGrammar( eGram ){}
protected :
bool processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell ) override
{
OUString sFormula;
double aDblValue = 0.0;
if ( aValue >>= sFormula )
{
// convert to GRAM_API style grammar because XCell::setFormula
// always compile it in that grammar. Perhaps
// css.sheet.FormulaParser should be used in future to directly
// pass formula tokens when that API stabilizes.
if ( m_eGrammar != formula::FormulaGrammar::GRAM_API && ( o3tl::starts_with(o3tl::trim(sFormula), u"=" ) ) )
{
uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
ScCellRangesBase* pUnoRangesBase
= dynamic_cast < ScCellRangesBase* >( xIf.get() );
if ( pUnoRangesBase )
{
const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
if (!rCellRanges.empty())
{
ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, m_eGrammar );
// compile the string in the format passed in
std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sFormula));
// convert to API grammar
aCompiler.SetGrammar( formula::FormulaGrammar::GRAM_API );
OUString sConverted;
aCompiler.CreateStringFromTokenArray(sConverted);
sFormula = EQUALS + sConverted;
}
}
}
xCell->setFormula( sFormula );
return true ;
}
else if ( aValue >>= aDblValue )
{
xCell->setValue( aDblValue );
return true ;
}
return false ;
}
};
class CellFormulaValueGetter : public CellValueGetter
{
private :
ScDocument& m_rDoc;
formula::FormulaGrammar::Grammar m_eGrammar;
public :
CellFormulaValueGetter(ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ) :
CellValueGetter( RangeValueType::value ), m_rDoc( rDoc ), m_eGrammar( eGram ) {}
virtual void visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell ) override
{
uno::Any aValue;
aValue <<= xCell->getFormula();
// XCell::getFormula() returns the formula in API grammar, convert.
if ((xCell->getType() == table::CellContentType_FORMULA)
&& m_eGrammar != formula::FormulaGrammar::GRAM_API)
{
uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
ScCellRangesBase* pUnoRangesBase
= dynamic_cast < ScCellRangesBase* >( xIf.get() );
if (pUnoRangesBase)
{
OUString sVal;
aValue >>= sVal;
const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
if (!rCellRanges.empty())
{
// Compile string from API grammar.
ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, formula::FormulaGrammar::GRAM_API );
std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sVal));
// Convert to desired grammar.
aCompiler.SetGrammar( m_eGrammar );
OUString sConverted;
aCompiler.CreateStringFromTokenArray(sConverted);
sVal = EQUALS + sConverted;
aValue <<= sVal;
}
}
}
processValue( aValue );
}
};
class Dim2ArrayValueGetter : public ArrayVisitor
{
protected :
uno::Any maValue;
ValueGetter& mValueGetter;
void processValue( sal_Int32 x, sal_Int32 y, const uno::Any& aValue )
{
uno::Sequence< uno::Sequence< uno::Any > >& aMatrix = const_cast <css::uno::Sequence<css::uno::Sequence<css::uno::Any>> &>(*o3tl::doAccess<uno::Sequence<uno::Sequence<uno::Any>>>(maValue));
aMatrix.getArray()[x].getArray()[y] = aValue;
}
public :
Dim2ArrayValueGetter(sal_Int32 nRowCount, sal_Int32 nColCount, ValueGetter& rValueGetter ): mValueGetter(rValueGetter)
{
uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
aMatrix.realloc( nRowCount );
auto pMatrix = aMatrix.getArray();
for ( sal_Int32 index = 0; index < nRowCount; ++index )
pMatrix[index].realloc( nColCount );
maValue <<= aMatrix;
}
void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
{
mValueGetter.visitNode( x, y, xCell );
processValue( x, y, mValueGetter.getValue() );
}
const uno::Any& getValue() const { return maValue; }
};
}
constexpr OUString sNA = u"#N/A" _ustr;
namespace {
class Dim1ArrayValueSetter : public ArrayVisitor
{
uno::Sequence< uno::Any > aMatrix;
sal_Int32 nColCount;
ValueSetter& mCellValueSetter;
public :
Dim1ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ):mCellValueSetter( rCellValueSetter )
{
aValue >>= aMatrix;
nColCount = aMatrix.getLength();
}
virtual void visitNode( sal_Int32 /*x*/, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
{
if ( y < nColCount )
mCellValueSetter.processValue( aMatrix[ y ], xCell );
else
mCellValueSetter.processValue( uno::Any( sNA ), xCell );
}
};
class Dim2ArrayValueSetter : public ArrayVisitor
{
uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
ValueSetter& mCellValueSetter;
sal_Int32 nRowCount;
sal_Int32 nColCount;
public :
Dim2ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ) : mCellValueSetter( rCellValueSetter )
{
aValue >>= aMatrix;
nRowCount = aMatrix.getLength();
nColCount = aMatrix[0].getLength();
}
virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
{
if ( x < nRowCount && y < nColCount )
mCellValueSetter.processValue( aMatrix[ x ][ y ], xCell );
else
mCellValueSetter.processValue( uno::Any( sNA ), xCell );
}
};
class RangeProcessor
{
public :
virtual void process( const uno::Reference< excel::XRange >& xRange ) = 0;
protected :
~RangeProcessor() {}
};
class RangeValueProcessor : public RangeProcessor
{
const uno::Any& m_aVal;
public :
explicit RangeValueProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
virtual ~RangeValueProcessor() {}
virtual void process( const uno::Reference< excel::XRange >& xRange ) override
{
xRange->setValue( m_aVal );
}
};
class RangeFormulaProcessor : public RangeProcessor
{
const uno::Any& m_aVal;
public :
explicit RangeFormulaProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
virtual ~RangeFormulaProcessor() {}
virtual void process( const uno::Reference< excel::XRange >& xRange ) override
{
xRange->setFormula( m_aVal );
}
};
class RangeCountProcessor : public RangeProcessor
{
sal_Int32 nCount;
public :
RangeCountProcessor():nCount(0){}
virtual ~RangeCountProcessor() {}
virtual void process( const uno::Reference< excel::XRange >& xRange ) override
{
nCount = nCount + xRange->getCount();
}
sal_Int32 value() { return nCount; }
};
class AreasVisitor
{
private :
uno::Reference< XCollection > m_Areas;
public :
explicit AreasVisitor( uno::Reference< XCollection > xAreas ):m_Areas(std::move( xAreas )){}
void visit( RangeProcessor& processor )
{
if ( m_Areas.is() )
{
sal_Int32 nItems = m_Areas->getCount();
for ( sal_Int32 index=1; index <= nItems; ++index )
{
uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
processor.process( xRange );
}
}
}
};
class RangeHelper
{
uno::Reference< table::XCellRange > m_xCellRange;
public :
/// @throws uno::RuntimeException
explicit RangeHelper( uno::Reference< table::XCellRange > xCellRange ) : m_xCellRange(std::move( xCellRange ))
{
if ( !m_xCellRange.is() )
throw uno::RuntimeException();
}
/// @throws uno::RuntimeException
explicit RangeHelper( const uno::Any& rCellRange )
{
m_xCellRange.set(rCellRange, uno::UNO_QUERY_THROW);
}
/// @throws uno::RuntimeException
uno::Reference< sheet::XSheetCellRange > getSheetCellRange() const
{
return uno::Reference< sheet::XSheetCellRange >(m_xCellRange, uno::UNO_QUERY_THROW);
}
/// @throws uno::RuntimeException
uno::Reference< sheet::XSpreadsheet > getSpreadSheet() const
{
return getSheetCellRange()->getSpreadsheet();
}
/// @throws uno::RuntimeException
uno::Reference< table::XCellRange > getCellRangeFromSheet() const
{
return uno::Reference< table::XCellRange >(getSpreadSheet(), uno::UNO_QUERY_THROW );
}
/// @throws uno::RuntimeException
uno::Reference< sheet::XCellRangeAddressable > getCellRangeAddressable() const
{
return uno::Reference< sheet::XCellRangeAddressable >(m_xCellRange, ::uno::UNO_QUERY_THROW);
}
/// @throws uno::RuntimeException
uno::Reference< sheet::XSheetCellCursor > getSheetCellCursor() const
{
return uno::Reference< sheet::XSheetCellCursor >( getSpreadSheet()->createCursorByRange( getSheetCellRange() ), uno::UNO_SET_THROW );
}
static uno::Reference< excel::XRange > createRangeFromRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference<uno::XComponentContext >& xContext,
const uno::Reference< table::XCellRange >& xRange, const uno::Reference< sheet::XCellRangeAddressable >& xCellRangeAddressable )
{
const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext,
xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow)));
}
};
}
bool
ScVbaRange::getCellRangesForAddress( ScRefFlags& rResFlags, std::u16string_view sAddress, ScDocShell* pDocSh, ScRangeList& rCellRanges, formula::FormulaGrammar::AddressConvention eConv, char cDelimiter )
{
if ( pDocSh )
{
ScDocument& rDoc = pDocSh->GetDocument();
rResFlags = rCellRanges.Parse( sAddress, rDoc, eConv, 0, cDelimiter );
if ( rResFlags & ScRefFlags::VALID )
{
return true ;
}
}
return false ;
}
bool getScRangeListForAddress( const OUString& sName, ScDocShell* pDocSh, const ScRange& refRange, ScRangeList& aCellRanges, formula::FormulaGrammar::AddressConvention aConv )
{
// see if there is a match with a named range
uno::Reference< container::XNameAccess > xNameAccess( pDocSh->GetModel()->getPropertyValue( u"NamedRanges" _ustr ), uno::UNO_QUERY_THROW );
// Strange enough you can have Range( "namedRange1, namedRange2, etc," )
// loop around each ',' separated name
std::vector< OUString > vNames;
sal_Int32 nIndex = 0;
do
{
OUString aToken = sName.getToken( 0, ',' , nIndex );
vNames.push_back( aToken );
} while ( nIndex >= 0 );
if ( vNames.empty() )
vNames.push_back( sName );
for ( const auto & rName : vNames )
{
formula::FormulaGrammar::AddressConvention eConv = aConv;
// spaces are illegal ( but the user of course can enter them )
OUString sAddress = rName.trim();
// if a local name ( on the active sheet ) exists this will
// take precedence over a global with the same name
if ( !xNameAccess->hasByName( sAddress ) )
{
// try a local name
ScDocument& rDoc = pDocSh->GetDocument();
SCTAB nCurTab = ScDocShell::GetCurTab();
ScRangeName* pRangeName = rDoc.GetRangeName(nCurTab);
if (pRangeName)
{
// TODO: Handle local names correctly:
// bool bLocalName = pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sAddress)) != nullptr;
}
}
char aChar = 0;
if ( xNameAccess->hasByName( sAddress ) )
{
uno::Reference< sheet::XNamedRange > xNamed( xNameAccess->getByName( sAddress ), uno::UNO_QUERY_THROW );
sAddress = xNamed->getContent();
// As the address comes from OOO, the addressing
// style is may not be XL_A1
eConv = pDocSh->GetDocument().GetAddressConvention();
aChar = ';' ;
}
ScRefFlags nFlags = ScRefFlags::ZERO;
if ( !ScVbaRange::getCellRangesForAddress( nFlags, sAddress, pDocSh, aCellRanges, eConv, aChar ) )
return false ;
bool bTabFromReferrer = !( nFlags & ScRefFlags::TAB_3D );
for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
{
ScRange & rRange = aCellRanges[ i ];
rRange.aStart.SetCol( refRange.aStart.Col() + rRange.aStart.Col() );
rRange.aStart.SetRow( refRange.aStart.Row() + rRange.aStart.Row() );
rRange.aStart.SetTab( bTabFromReferrer ? refRange.aStart.Tab() : rRange.aStart.Tab() );
rRange.aEnd.SetCol( refRange.aStart.Col() + rRange.aEnd.Col() );
rRange.aEnd.SetRow( refRange.aStart.Row() + rRange.aEnd.Row() );
rRange.aEnd.SetTab( bTabFromReferrer ? refRange.aEnd.Tab() : rRange.aEnd.Tab() );
}
}
return true ;
}
/// @throws uno::RuntimeException
static rtl::Reference<ScVbaRange>
getRangeForName( const uno::Reference< uno::XComponentContext >& xContext, const OUString& sName, ScDocShell* pDocSh, const table::CellRangeAddress& pAddr, formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_XL_A1 )
{
ScRangeList aCellRanges;
ScRange refRange;
ScUnoConversion::FillScRange( refRange, pAddr );
if ( !getScRangeListForAddress ( sName, pDocSh, refRange, aCellRanges, eConv ) )
throw uno::RuntimeException();
// Single range
if ( aCellRanges.size() == 1 )
{
uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pDocSh, aCellRanges.front() ) );
uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRange );
return new ScVbaRange( xFixThisParent, xContext, xRange );
}
uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDocSh, aCellRanges ) );
uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRanges );
return new ScVbaRange( xFixThisParent, xContext, xRanges );
}
namespace {
/// @throws uno::RuntimeException
template < typename RangeType >
table::CellRangeAddress lclGetRangeAddress( const uno::Reference< RangeType >& rxCellRange )
{
return uno::Reference< sheet::XCellRangeAddressable >( rxCellRange, uno::UNO_QUERY_THROW )->getRangeAddress();
}
/// @throws uno::RuntimeException
void lclClearRange( const uno::Reference< table::XCellRange >& rxCellRange )
{
using namespace ::com::sun::star::sheet::CellFlags;
sal_Int32 const nFlags = VALUE | DATETIME | STRING | ANNOTATION | FORMULA | HARDATTR | STYLES | EDITATTR | FORMATTED;
uno::Reference< sheet::XSheetOperation > xSheetOperation( rxCellRange, uno::UNO_QUERY_THROW );
xSheetOperation->clearContents( nFlags );
}
/// @throws uno::RuntimeException
uno::Reference< sheet::XSheetCellRange > lclExpandToMerged( const uno::Reference< table::XCellRange >& rxCellRange, bool bRecursive )
{
uno::Reference< sheet::XSheetCellRange > xNewCellRange( rxCellRange, uno::UNO_QUERY_THROW );
uno::Reference< sheet::XSpreadsheet > xSheet( xNewCellRange->getSpreadsheet(), uno::UNO_SET_THROW );
table::CellRangeAddress aNewAddress = lclGetRangeAddress( xNewCellRange );
table::CellRangeAddress aOldAddress;
// expand as long as there are new merged ranges included
do
{
aOldAddress = aNewAddress;
uno::Reference< sheet::XSheetCellCursor > xCursor( xSheet->createCursorByRange( xNewCellRange ), uno::UNO_SET_THROW );
if (xCursor.is())
{
xCursor->collapseToMergedArea();
xNewCellRange.set( xCursor, uno::UNO_QUERY_THROW );
aNewAddress = lclGetRangeAddress( xNewCellRange );
}
}
while ( bRecursive && (aOldAddress != aNewAddress) );
return xNewCellRange;
}
/// @throws uno::RuntimeException
uno::Reference< sheet::XSheetCellRangeContainer > lclExpandToMerged( const uno::Reference< sheet::XSheetCellRangeContainer >& rxCellRanges )
{
if ( !rxCellRanges.is() )
throw uno::RuntimeException(u"Missing cell ranges object" _ustr );
sal_Int32 nCount = rxCellRanges->getCount();
if ( nCount < 1 )
throw uno::RuntimeException(u"Missing cell ranges object" _ustr );
ScRangeList aScRanges;
for ( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
{
uno::Reference< table::XCellRange > xRange( rxCellRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
table::CellRangeAddress aRangeAddr = lclGetRangeAddress( lclExpandToMerged( xRange, /*bRecursive*/true ) );
ScRange aScRange;
ScUnoConversion::FillScRange( aScRange, aRangeAddr );
aScRanges.push_back( aScRange );
}
return new ScCellRangesObj( getDocShellFromRanges( rxCellRanges ), aScRanges );
}
/// @throws uno::RuntimeException
void lclExpandAndMerge( const uno::Reference< table::XCellRange >& rxCellRange, bool bMerge )
{
uno::Reference< util::XMergeable > xMerge( lclExpandToMerged( rxCellRange, true ), uno::UNO_QUERY_THROW );
// Calc cannot merge over merged ranges, always unmerge first
xMerge->merge( false );
if ( !bMerge )
return ;
// clear all contents of the covered cells (not the top-left cell)
table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
sal_Int32 nLastColIdx = aRangeAddr.EndColumn - aRangeAddr.StartColumn;
sal_Int32 nLastRowIdx = aRangeAddr.EndRow - aRangeAddr.StartRow;
// clear cells of top row, right of top-left cell
if ( nLastColIdx > 0 )
lclClearRange( rxCellRange->getCellRangeByPosition( 1, 0, nLastColIdx, 0 ) );
// clear all rows below top row
if ( nLastRowIdx > 0 )
lclClearRange( rxCellRange->getCellRangeByPosition( 0, 1, nLastColIdx, nLastRowIdx ) );
// merge the range
xMerge->merge( true );
}
/// @throws uno::RuntimeException
util::TriState lclGetMergedState( const uno::Reference< table::XCellRange >& rxCellRange )
{
/* 1) Check if range is completely inside one single merged range. To do
this, try to extend from top-left cell only (not from entire range).
This will exclude cases where this range consists of several merged
ranges (or parts of them). */
table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
uno::Reference< table::XCellRange > xTopLeft( rxCellRange->getCellRangeByPosition( 0, 0, 0, 0 ), uno::UNO_SET_THROW );
uno::Reference< sheet::XSheetCellRange > xExpanded( lclExpandToMerged( xTopLeft, false ), uno::UNO_SET_THROW );
table::CellRangeAddress aExpAddr = lclGetRangeAddress( xExpanded );
// check that expanded range has more than one cell (really merged)
if ( ((aExpAddr.StartColumn < aExpAddr.EndColumn) || (aExpAddr.StartRow < aExpAddr.EndRow)) && ScUnoConversion::Contains( aExpAddr, aRangeAddr ) )
return util::TriState_YES;
/* 2) Check if this range contains any merged cells (completely or
partly). This seems to be hardly possible via API, as
XMergeable::getIsMerged() returns only true, if the top-left cell of a
merged range is part of this range, so cases where just the lower part
of a merged range is part of this range are not covered. */
ScRange aScRange;
ScUnoConversion::FillScRange( aScRange, aRangeAddr );
bool bHasMerged = getDocumentFromRange( rxCellRange ).HasAttrib( aScRange, HasAttrFlags::Merged | HasAttrFlags::Overlapped );
return bHasMerged ? util::TriState_INDETERMINATE : util::TriState_NO;
}
} // namespace
css::uno::Reference< excel::XRange >
ScVbaRange::getRangeObjectForName(
const uno::Reference< uno::XComponentContext >& xContext, const OUString& sRangeName,
ScDocShell* pDocSh, formula::FormulaGrammar::AddressConvention eConv )
{
table::CellRangeAddress refAddr;
return getRangeForName( xContext, sRangeName, pDocSh, refAddr, eConv );
}
/// @throws uno::RuntimeException
static table::CellRangeAddress getCellRangeAddressForVBARange( const uno::Any& aParam, ScDocShell* pDocSh )
{
uno::Reference< table::XCellRange > xRangeParam;
switch ( aParam.getValueTypeClass() )
{
case uno::TypeClass_STRING:
{
OUString rString;
aParam >>= rString;
ScRangeList aCellRanges;
ScRange refRange;
if ( getScRangeListForAddress ( rString, pDocSh, refRange, aCellRanges ) )
{
if ( aCellRanges.size() == 1 )
{
table::CellRangeAddress aRangeAddress;
ScUnoConversion::FillApiRange( aRangeAddress, aCellRanges.front() );
return aRangeAddress;
}
}
}
break ;
case uno::TypeClass_INTERFACE:
{
uno::Reference< excel::XRange > xRange;
aParam >>= xRange;
if ( xRange.is() )
xRange->getCellRange() >>= xRangeParam;
}
break ;
default :
throw uno::RuntimeException(u"Can't extract CellRangeAddress from type" _ustr );
}
return lclGetRangeAddress( xRangeParam );
}
/// @throws uno::RuntimeException
static uno::Reference< XCollection >
lcl_setupBorders( const uno::Reference< excel::XRange >& xParentRange, const uno::Reference<uno::XComponentContext>& xContext, const uno::Reference< table::XCellRange >& xRange )
{
uno::Reference< XHelperInterface > xParent( xParentRange, uno::UNO_QUERY_THROW );
ScDocument& rDoc = getDocumentFromRange(xRange);
ScVbaPalette aPalette( rDoc.GetDocumentShell() );
uno::Reference< XCollection > borders( new ScVbaBorders( xParent, xContext, xRange, aPalette ) );
return borders;
}
ScVbaRange::ScVbaRange( uno::Sequence< uno::Any> const & args,
uno::Reference< uno::XComponentContext> const & xContext ) : ScVbaRange_BASE( getXSomethingFromArgs< XHelperInterface >( args, 0 ), xContext, getXSomethingFromArgs< beans::XPropertySet >( args, 1, false ), getModelFromXIf( getXSomethingFromArgs< uno::XInterface >( args, 1 ) ), true ), mbIsRows( false ), mbIsColumns( false )
{
mxRange.set( mxPropertySet, uno::UNO_QUERY );
mxRanges.set( mxPropertySet, uno::UNO_QUERY );
uno::Reference< container::XIndexAccess > xIndex;
if ( mxRange.is() )
{
xIndex = new SingleRangeIndexAccess( mxRange );
}
else if ( mxRanges.is() )
{
xIndex.set( mxRanges, uno::UNO_QUERY_THROW );
}
m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
}
ScVbaRange::ScVbaRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< table::XCellRange >& xRange, bool bIsRows, bool bIsColumns )
: ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRange, uno::UNO_QUERY_THROW ), getModelFromRange( xRange), true ), mxRange( xRange ),
mbIsRows( bIsRows ),
mbIsColumns( bIsColumns )
{
if ( !xContext.is() )
throw lang::IllegalArgumentException(u"context is not set " _ustr, uno::Reference< uno::XInterface >() , 1 );
if ( !xRange.is() )
throw lang::IllegalArgumentException(u"range is not set " _ustr, uno::Reference< uno::XInterface >() , 1 );
uno::Reference< container::XIndexAccess > xIndex( new SingleRangeIndexAccess( xRange ) );
m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
}
ScVbaRange::ScVbaRange(const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges, bool bIsRows, bool bIsColumns)
: ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRanges, uno::UNO_QUERY_THROW ), getModelFromXIf( uno::Reference< uno::XInterface >( xRanges, uno::UNO_QUERY_THROW ) ), true ), mxRanges( xRanges ),mbIsRows( bIsRows ), mbIsColumns( bIsColumns )
{
uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
m_Areas = new ScVbaRangeAreas( xParent, mxContext, xIndex, mbIsRows, mbIsColumns );
}
ScVbaRange::~ScVbaRange()
{
}
uno::Reference< XCollection >& ScVbaRange::getBorders()
{
if ( !m_Borders.is() )
{
uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
m_Borders = lcl_setupBorders( this , mxContext, uno::Reference< table::XCellRange >( xRange->getCellRange(), uno::UNO_QUERY_THROW ) );
}
return m_Borders;
}
void
ScVbaRange::visitArray( ArrayVisitor& visitor )
{
ScDocShell* pDocSh = nullptr;
if (ScCellRangeObj* range = dynamic_cast <ScCellRangeObj*>(mxRange.get()))
pDocSh = range->GetDocShell();
if ( pDocSh )
pDocSh->LockPaint();
table::CellRangeAddress aRangeAddr = lclGetRangeAddress( mxRange );
sal_Int32 nRowCount = aRangeAddr.EndRow - aRangeAddr.StartRow + 1;
sal_Int32 nColCount = aRangeAddr.EndColumn - aRangeAddr.StartColumn + 1;
for ( sal_Int32 i=0; i<nRowCount; ++i )
{
for ( sal_Int32 j=0; j<nColCount; ++j )
{
uno::Reference< table::XCell > xCell( mxRange->getCellByPosition( j, i ), uno::UNO_SET_THROW );
visitor.visitNode( i, j, xCell );
}
}
if ( pDocSh )
pDocSh->UnlockPaint();
}
uno::Any
ScVbaRange::getValue( ValueGetter& valueGetter)
{
uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
// single cell range
if ( isSingleCellRange() )
{
visitArray( valueGetter );
return valueGetter.getValue();
}
sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
// multi cell range ( return array )
Dim2ArrayValueGetter arrayGetter( nRowCount, nColCount, valueGetter );
visitArray( arrayGetter );
return uno::Any( script::ArrayWrapper( false , arrayGetter.getValue() ) );
}
css::uno::Any ScVbaRange::DoGetValue( RangeValueType eValueType )
{
// #TODO code within the test below "if ( m_Areas... " can be removed
// Test is performed only because m_xRange is NOT set to be
// the first range in m_Areas ( to force failure while
// the implementations for each method are being updated )
if ( m_Areas->getCount() > 1 )
{
uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
return xRange->getValue();
}
CellValueGetter valueGetter( eValueType );
return getValue( valueGetter );
}
uno::Any SAL_CALL
ScVbaRange::getValue()
{
return DoGetValue( RangeValueType::value );
}
uno::Any SAL_CALL
ScVbaRange::getValue2()
{
return DoGetValue( RangeValueType::value2 );
}
void
ScVbaRange::setValue( const uno::Any& aValue, ValueSetter& valueSetter )
{
uno::TypeClass aClass = aValue.getValueTypeClass();
if ( aClass == uno::TypeClass_SEQUENCE )
{
const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
uno::Any aConverted;
try
{
// test for single dimension, could do
// with a better test than this
if ( aValue.getValueTypeName().indexOf('[' ) == aValue.getValueTypeName().lastIndexOf('[' ) )
{
aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Any >>::get() );
Dim1ArrayValueSetter setter( aConverted, valueSetter );
visitArray( setter );
}
else
{
aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() );
Dim2ArrayValueSetter setter( aConverted, valueSetter );
visitArray( setter );
}
}
catch ( const uno::Exception& )
{
TOOLS_WARN_EXCEPTION("sc" , "Bahhh, caught" );
}
}
else
{
visitArray( valueSetter );
}
fireChangeEvent();
}
void SAL_CALL
ScVbaRange::setValue( const uno::Any &aValue )
{
// If this is a multiple selection apply setValue over all areas
if ( m_Areas->getCount() > 1 )
{
AreasVisitor aVisitor( m_Areas );
RangeValueProcessor valueProcessor( aValue );
aVisitor.visit( valueProcessor );
return ;
}
CellValueSetter valueSetter( aValue );
setValue( aValue, valueSetter );
}
void SAL_CALL
ScVbaRange::setValue2( const uno::Any &aValue )
{
return setValue( aValue );
}
void SAL_CALL
ScVbaRange::Clear()
{
using namespace ::com::sun::star::sheet::CellFlags;
sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA | HARDATTR | EDITATTR | FORMATTED;
ClearContents( nFlags, true );
}
//helper ClearContent
void
ScVbaRange::ClearContents( sal_Int32 nFlags, bool bFireEvent )
{
// #TODO code within the test below "if ( m_Areas... " can be removed
// Test is performed only because m_xRange is NOT set to be
// the first range in m_Areas ( to force failure while
// the implementations for each method are being updated )
if ( m_Areas->getCount() > 1 )
{
sal_Int32 nItems = m_Areas->getCount();
for ( sal_Int32 index=1; index <= nItems; ++index )
{
uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
ScVbaRange* pRange = getImplementation( xRange );
if ( pRange )
pRange->ClearContents( nFlags, false ); // do not fire for single ranges
}
// fire change event for the entire range list
if ( bFireEvent ) fireChangeEvent();
return ;
}
uno::Reference< sheet::XSheetOperation > xSheetOperation(mxRange, uno::UNO_QUERY_THROW);
xSheetOperation->clearContents( nFlags );
if ( bFireEvent ) fireChangeEvent();
}
void SAL_CALL
ScVbaRange::ClearComments()
{
ClearContents( sheet::CellFlags::ANNOTATION, false );
}
void SAL_CALL
ScVbaRange::ClearContents()
{
using namespace ::com::sun::star::sheet::CellFlags;
sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA;
ClearContents( nFlags, true );
}
void SAL_CALL
ScVbaRange::ClearFormats()
{
// FIXME: need to check if we need to combine FORMATTED
using namespace ::com::sun::star::sheet::CellFlags;
sal_Int32 const nFlags = HARDATTR | FORMATTED | EDITATTR;
ClearContents( nFlags, false );
}
void
ScVbaRange::setFormulaValue( const uno::Any& rFormula, formula::FormulaGrammar::Grammar eGram )
{
// If this is a multiple selection apply setFormula over all areas
if ( m_Areas->getCount() > 1 )
{
AreasVisitor aVisitor( m_Areas );
RangeFormulaProcessor valueProcessor( rFormula );
aVisitor.visit( valueProcessor );
return ;
}
CellFormulaValueSetter formulaValueSetter( rFormula, getScDocument(), eGram );
setValue( rFormula, formulaValueSetter );
}
uno::Any
ScVbaRange::getFormulaValue( formula::FormulaGrammar::Grammar eGram )
{
// #TODO code within the test below "if ( m_Areas... " can be removed
// Test is performed only because m_xRange is NOT set to be
// the first range in m_Areas ( to force failure while
// the implementations for each method are being updated )
if ( m_Areas->getCount() > 1 )
{
uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
return xRange->getFormula();
}
CellFormulaValueGetter valueGetter( getScDocument(), eGram );
return getValue( valueGetter );
}
uno::Any
ScVbaRange::getFormula()
{
return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
}
void
ScVbaRange::setFormula(const uno::Any &rFormula )
{
setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
}
uno::Any
ScVbaRange::getFormulaR1C1()
{
return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
}
void
ScVbaRange::setFormulaR1C1(const uno::Any& rFormula )
{
setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
}
uno::Any
ScVbaRange::getFormulaLocal()
{
return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
}
void
ScVbaRange::setFormulaLocal(const uno::Any &rFormula )
{
setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
}
uno::Any
ScVbaRange::getFormulaR1C1Local()
{
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=92 H=95 G=93
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland
2026-04-04