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()
{
return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
}
void
ScVbaRange::setFormulaR1C1Local(const uno::Any& rFormula )
{
setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
}
sal_Int32
ScVbaRange::getCount()
{
// If this is a multiple selection apply setValue over all areas
if ( m_Areas->getCount() > 1 )
{
AreasVisitor aVisitor( m_Areas );
RangeCountProcessor valueProcessor;
aVisitor.visit( valueProcessor );
return valueProcessor.value();
}
sal_Int32 rowCount = 0;
sal_Int32 colCount = 0;
uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
rowCount = xColumnRowRange->getRows()->getCount();
colCount = xColumnRowRange->getColumns()->getCount();
if( mbIsRows )
return rowCount;
if( mbIsColumns )
return colCount;
return rowCount * colCount;
}
sal_Int32
ScVbaRange::getRow()
{
// #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->getRow();
}
uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
return xCellAddressable->getCellAddress().Row + 1; // Zero value indexing
}
sal_Int32
ScVbaRange::getColumn()
{
// #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->getColumn();
}
uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
return xCellAddressable->getCellAddress().Column + 1; // Zero value indexing
}
uno::Any
ScVbaRange::HasFormula()
{
if ( m_Areas->getCount() > 1 )
{
sal_Int32 nItems = m_Areas->getCount();
uno::Any aResult = aNULL();
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 );
// if the HasFormula for any area is different to another
// return null
if ( index > 1 )
if ( aResult != xRange->HasFormula() )
return aNULL();
aResult = xRange->HasFormula();
if ( aNULL() == aResult )
return aNULL();
}
return aResult;
}
uno::Reference< uno::XInterface > xIf( mxRange, uno::UNO_QUERY_THROW );
ScCellRangesBase* pThisRanges = dynamic_cast< ScCellRangesBase* > ( xIf.get() );
if ( pThisRanges )
{
uno::Reference<uno::XInterface> xRanges( pThisRanges->queryFormulaCells( sheet::FormulaResult::ERROR | sheet::FormulaResult::VALUE | sheet::FormulaResult::STRING ), uno::UNO_QUERY_THROW );
ScCellRangesBase* pFormulaRanges
= dynamic_cast< ScCellRangesBase* > ( xRanges.get() );
assert(pFormulaRanges);
// check if there are no formula cell, return false
if ( pFormulaRanges->GetRangeList().empty() )
return uno::Any(false);
// check if there are holes (where some cells are not formulas)
// or returned range is not equal to this range
if ( ( pFormulaRanges->GetRangeList().size() > 1 )
|| ( pFormulaRanges->GetRangeList().front().aStart != pThisRanges->GetRangeList().front().aStart )
|| ( pFormulaRanges->GetRangeList().front().aEnd != pThisRanges->GetRangeList().front().aEnd )
)
return aNULL(); // should return aNULL;
}
return uno::Any( true );
}
void
ScVbaRange::fillSeries( sheet::FillDirection nFillDirection, sheet::FillMode nFillMode, sheet::FillDateMode nFillDateMode, double fStep, double fEndValue )
{
if ( m_Areas->getCount() > 1 )
{
// Multi-Area Range
uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
{
uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
ScVbaRange* pThisRange = getImplementation( xRange );
pThisRange->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
}
return;
}
uno::Reference< sheet::XCellSeries > xCellSeries(mxRange, uno::UNO_QUERY_THROW );
xCellSeries->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
fireChangeEvent();
}
void
ScVbaRange::FillLeft()
{
fillSeries(sheet::FillDirection_TO_LEFT,
sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
}
void
ScVbaRange::FillRight()
{
fillSeries(sheet::FillDirection_TO_RIGHT,
sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
}
void
ScVbaRange::FillUp()
{
fillSeries(sheet::FillDirection_TO_TOP,
sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
}
void
ScVbaRange::FillDown()
{
fillSeries(sheet::FillDirection_TO_BOTTOM,
sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
}
OUString
ScVbaRange::getText()
{
// #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->getText();
}
uno::Reference< text::XTextRange > xTextRange(mxRange->getCellByPosition(0,0), uno::UNO_QUERY_THROW );
return xTextRange->getString();
}
uno::Reference< excel::XRange >
ScVbaRange::Offset( const ::uno::Any &nRowOff, const uno::Any &nColOff )
{
SCROW nRowOffset = 0;
SCCOL nColOffset = 0;
bool bIsRowOffset = ( nRowOff >>= nRowOffset );
bool bIsColumnOffset = ( nColOff >>= nColOffset );
ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
{
ScRange & rRange = aCellRanges[ i ];
if ( bIsColumnOffset )
{
rRange.aStart.SetCol( rRange.aStart.Col() + nColOffset );
rRange.aEnd.SetCol( rRange.aEnd.Col() + nColOffset );
}
if ( bIsRowOffset )
{
rRange.aStart.SetRow( rRange.aStart.Row() + nRowOffset );
rRange.aEnd.SetRow( rRange.aEnd.Row() + nRowOffset );
}
}
if ( aCellRanges.size() > 1 ) // Multi-Area
{
uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
return new ScVbaRange( mxParent, mxContext, xRanges );
}
// normal range
const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
return new ScVbaRange( mxParent, mxContext, xRange );
}
uno::Reference< excel::XRange >
ScVbaRange::CurrentRegion()
{
// #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->CurrentRegion();
}
RangeHelper helper( mxRange );
uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
helper.getSheetCellCursor();
xSheetCellCursor->collapseToCurrentRegion();
uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
}
uno::Reference< excel::XRange >
ScVbaRange::CurrentArray()
{
// #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->CurrentArray();
}
RangeHelper helper( mxRange );
uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
helper.getSheetCellCursor();
xSheetCellCursor->collapseToCurrentArray();
uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
}
uno::Any
ScVbaRange::getFormulaArray()
{
// #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->getFormulaArray();
}
// return a formula if there is one or else an array
// still not sure when the return as array code should run
// ( I think it is if there is more than one formula ) at least
// that is what the doc says ( but I am not even sure how to detect that )
// for the moment any tests we have pass
uno::Reference< sheet::XArrayFormulaRange> xFormulaArray( mxRange, uno::UNO_QUERY_THROW );
if ( !xFormulaArray->getArrayFormula().isEmpty() )
return uno::Any( xFormulaArray->getArrayFormula() );
uno::Reference< sheet::XCellRangeFormula> xCellRangeFormula( mxRange, uno::UNO_QUERY_THROW );
const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
uno::Any aSingleValueOrMatrix;
// When dealing with a single element ( embedded in the sequence of sequence ) unwrap and return
// that value
uno::Sequence< uno::Sequence<OUString> > aTmpSeq = xCellRangeFormula->getFormulaArray();
if ( aTmpSeq.getLength() == 1 )
{
if ( aTmpSeq[ 0 ].getLength() == 1 )
aSingleValueOrMatrix <<= aTmpSeq[ 0 ][ 0 ];
}
else
aSingleValueOrMatrix = xConverter->convertTo( uno::Any( aTmpSeq ) , cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() ) ;
return aSingleValueOrMatrix;
}
void
ScVbaRange::setFormulaArray(const uno::Any& rFormula)
{
// #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->setFormulaArray( rFormula );
}
// #TODO need to distinguish between getFormula and getFormulaArray e.g. (R1C1)
// but for the moment it's just easier to treat them the same for setting
// seems
uno::Reference< lang::XMultiServiceFactory > xModelFactory( getUnoModel(), uno::UNO_QUERY_THROW );
uno::Reference< sheet::XFormulaParser > xParser( xModelFactory->createInstance( u"com.sun.star.sheet.FormulaParser"_ustr ), uno::UNO_QUERY_THROW );
uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY_THROW);
table::CellRangeAddress aRangeAddress = xSource->getRangeAddress();
// #TODO check if api orders the address
// e.g. do we need to order the RangeAddress to get the topleft ( or can we assume it
// is in the correct order )
table::CellAddress aAddress;
aAddress.Sheet = aRangeAddress.Sheet;
aAddress.Column = aRangeAddress.StartColumn;
aAddress.Row = aRangeAddress.StartRow;
OUString sFormula;
rFormula >>= sFormula;
uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( sFormula, aAddress );
ScTokenArray aTokenArray(getScDocument());
(void)ScTokenConversion::ConvertToTokenArray( getScDocument(), aTokenArray, aTokens );
getScDocShell()->GetDocFunc().EnterMatrix( getScRangeList()[0], nullptr, &aTokenArray, OUString(), true, true, OUString(), formula::FormulaGrammar::GRAM_API );
}
OUString
ScVbaRange::Characters(const uno::Any& Start, const uno::Any& Length)
{
// #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->Characters( Start, Length );
}
tools::Long nIndex = 0, nCount = 0;
OUString rString;
uno::Reference< text::XTextRange > xTextRange(mxRange, ::uno::UNO_QUERY_THROW );
rString = xTextRange->getString();
if( !( Start >>= nIndex ) && !( Length >>= nCount ) )
return rString;
if(!( Start >>= nIndex ) )
nIndex = 1;
if(!( Length >>= nCount ) )
nIndex = rString.getLength();
return rString.copy( --nIndex, nCount ); // Zero value indexing
}
OUString
ScVbaRange::Address( const uno::Any& RowAbsolute, const uno::Any& ColumnAbsolute, const uno::Any& ReferenceStyle, const uno::Any& External, const uno::Any& RelativeTo )
{
if ( m_Areas->getCount() > 1 )
{
// Multi-Area Range
OUStringBuffer sAddress;
uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
uno::Any aExternalCopy = External;
for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
{
uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
if ( index > 1 )
{
sAddress.append(",");
// force external to be false
// only first address should have the
// document and sheet specifications
aExternalCopy <<= false;
}
sAddress.append(xRange->Address( RowAbsolute, ColumnAbsolute, ReferenceStyle, aExternalCopy, RelativeTo ));
}
return sAddress.makeStringAndClear();
}
ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
if ( ReferenceStyle.hasValue() )
{
sal_Int32 refStyle = excel::XlReferenceStyle::xlA1;
ReferenceStyle >>= refStyle;
if ( refStyle == excel::XlReferenceStyle::xlR1C1 )
dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, 0, 0 );
}
// default
ScRefFlags nFlags = ScRefFlags::RANGE_ABS;
ScDocShell* pDocShell = getScDocShell();
ScDocument& rDoc = pDocShell->GetDocument();
RangeHelper thisRange( mxRange );
table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
ScRange aRange( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCROW >( thisAddress.StartRow ), static_cast< SCTAB >( thisAddress.Sheet ), static_cast< SCCOL >( thisAddress.EndColumn ), static_cast< SCROW >( thisAddress.EndRow ), static_cast< SCTAB >( thisAddress.Sheet ) );
constexpr ScRefFlags ROW_ABS = ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
constexpr ScRefFlags COL_ABS = ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
if ( RowAbsolute.hasValue() )
{
bool bVal = true;
RowAbsolute >>= bVal;
if ( !bVal )
nFlags &= ~ROW_ABS;
}
if ( ColumnAbsolute.hasValue() )
{
bool bVal = true;
ColumnAbsolute >>= bVal;
if ( !bVal )
nFlags &= ~COL_ABS;
}
if ( External.hasValue() )
{
bool bLocal = false;
External >>= bLocal;
if ( bLocal )
nFlags |= ScRefFlags::TAB_3D | ScRefFlags::FORCE_DOC;
}
if ( RelativeTo.hasValue() )
{
// #TODO should I throw an error if R1C1 is not set?
table::CellRangeAddress refAddress = getCellRangeAddressForVBARange( RelativeTo, pDocShell );
dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, static_cast< SCROW >( refAddress.StartRow ), static_cast< SCCOL >( refAddress.StartColumn ) );
}
return aRange.Format(rDoc, nFlags, dDetails);
}
uno::Reference < excel::XFont >
ScVbaRange::Font()
{
uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY );
ScDocument& rDoc = getScDocument();
if ( mxRange.is() )
xProps.set(mxRange, ::uno::UNO_QUERY );
else if ( mxRanges.is() )
xProps.set(mxRanges, ::uno::UNO_QUERY );
ScVbaPalette aPalette( rDoc.GetDocumentShell() );
ScCellRangeObj* pRangeObj = nullptr;
try
{
pRangeObj = getCellRangeObj();
}
catch( uno::Exception& )
{
}
return new ScVbaFont( this, mxContext, aPalette, xProps, pRangeObj );
}
uno::Reference< excel::XRange >
ScVbaRange::Cells( const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
{
// #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->Cells( nRowIndex, nColumnIndex );
}
// Performance: Use a common helper method for ScVbaRange::Cells and ScVbaWorksheet::Cells,
// instead of creating a new ScVbaRange object in often-called ScVbaWorksheet::Cells
return CellsHelper( getScDocument(), mxParent, mxContext, mxRange, nRowIndex, nColumnIndex );
}
// static
uno::Reference< excel::XRange >
ScVbaRange::CellsHelper( const ScDocument& rDoc,
const uno::Reference< ov::XHelperInterface >& xParent,
const uno::Reference< uno::XComponentContext >& xContext,
const uno::Reference< css::table::XCellRange >& xRange,
const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
{
sal_Int32 nRow = 0, nColumn = 0;
bool bIsIndex = nRowIndex.hasValue();
bool bIsColumnIndex = nColumnIndex.hasValue();
// Sometimes we might get a float or a double or whatever
// set in the Any, we should convert as appropriate
// #FIXME - perhaps worth turning this into some sort of
// conversion routine e.g. bSuccess = getValueFromAny( nRow, nRowIndex, cppu::UnoType<sal_Int32>::get() )
if ( nRowIndex.hasValue() && !( nRowIndex >>= nRow ) )
{
const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
uno::Any aConverted;
try
{
aConverted = xConverter->convertTo( nRowIndex, cppu::UnoType<sal_Int32>::get() );
bIsIndex = ( aConverted >>= nRow );
}
catch( uno::Exception& ) {} // silence any errors
}
if ( bIsColumnIndex )
{
// Column index can be a col address e.g Cells( 1, "B" ) etc.
OUString sCol;
if ( nColumnIndex >>= sCol )
{
ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
ScRange tmpRange;
ScRefFlags flags = tmpRange.ParseCols( rDoc, sCol, dDetails );
if ( (flags & ScRefFlags::COL_VALID) == ScRefFlags::ZERO )
throw uno::RuntimeException();
nColumn = tmpRange.aStart.Col() + 1;
}
else
{
if ( !( nColumnIndex >>= nColumn ) )
{
const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
uno::Any aConverted;
try
{
aConverted = xConverter->convertTo( nColumnIndex, cppu::UnoType<sal_Int32>::get() );
bIsColumnIndex = ( aConverted >>= nColumn );
}
catch( uno::Exception& ) {} // silence any errors
}
}
}
RangeHelper thisRange( xRange );
table::CellRangeAddress thisRangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
uno::Reference< table::XCellRange > xSheetRange = thisRange.getCellRangeFromSheet();
if( !bIsIndex && !bIsColumnIndex ) // .Cells
// #FIXME needs proper parent ( Worksheet )
return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext, xRange ) );
sal_Int32 nIndex = --nRow;
if( bIsIndex && !bIsColumnIndex ) // .Cells(n)
{
uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, ::uno::UNO_QUERY_THROW);
sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
if ( !nIndex || nIndex < 0 )
nRow = 0;
else
nRow = nIndex / nColCount;
nColumn = nIndex % nColCount;
}
else
--nColumn;
nRow = nRow + thisRangeAddress.StartRow;
nColumn = nColumn + thisRangeAddress.StartColumn;
return new ScVbaRange( xParent, xContext, xSheetRange->getCellRangeByPosition( nColumn, nRow, nColumn, nRow ) );
}
void
ScVbaRange::Select()
{
ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
if ( !pUnoRangesBase )
throw uno::RuntimeException(u"Failed to access underlying uno range object"_ustr );
ScDocShell* pShell = pUnoRangesBase->GetDocShell();
if ( !pShell )
return;
uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_SET_THROW );
uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
if ( mxRanges.is() )
xSelection->select( uno::Any( lclExpandToMerged( mxRanges ) ) );
else
xSelection->select( uno::Any( lclExpandToMerged( mxRange, true ) ) );
// set focus on document e.g.
// ThisComponent.CurrentController.Frame.getContainerWindow.SetFocus
try
{
uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW );
uno::Reference< frame::XFrame > xFrame( xController->getFrame(), uno::UNO_SET_THROW );
uno::Reference< awt::XWindow > xWin( xFrame->getContainerWindow(), uno::UNO_SET_THROW );
xWin->setFocus();
}
catch( uno::Exception& )
{
}
}
static bool cellInRange( const table::CellRangeAddress& rAddr, sal_Int32 nCol, sal_Int32 nRow )
{
return nCol >= rAddr.StartColumn && nCol <= rAddr.EndColumn &&
nRow >= rAddr.StartRow && nRow <= rAddr.EndRow;
}
static void setCursor( SCCOL nCol, SCROW nRow, const uno::Reference< frame::XModel >& xModel, bool bInSel = true )
{
ScTabViewShell* pShell = excel::getBestViewShell( xModel );
if ( pShell )
{
if ( bInSel )
pShell->SetCursor( nCol, nRow );
else
pShell->MoveCursorAbs( nCol, nRow, SC_FOLLOW_NONE, false, false, true );
}
}
void
ScVbaRange::Activate()
{
// get first cell of current range
uno::Reference< table::XCellRange > xCellRange;
if ( mxRanges.is() )
{
uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
}
else
xCellRange.set( mxRange, uno::UNO_SET_THROW );
RangeHelper thisRange( xCellRange );
uno::Reference< sheet::XCellRangeAddressable > xThisRangeAddress = thisRange.getCellRangeAddressable();
table::CellRangeAddress thisRangeAddress = xThisRangeAddress->getRangeAddress();
uno::Reference< frame::XModel > xModel;
ScDocShell* pShell = getScDocShell();
if ( pShell )
xModel = pShell->GetModel();
if ( !xModel.is() )
throw uno::RuntimeException();
// get current selection
uno::Reference< sheet::XCellRangeAddressable > xRange( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
uno::Reference< sheet::XSheetCellRanges > xRanges( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
if ( xRanges.is() )
{
const uno::Sequence< table::CellRangeAddress > nAddrs = xRanges->getRangeAddresses();
for ( const auto& rAddr : nAddrs )
{
if ( cellInRange( rAddr, thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
{
setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
return;
}
}
}
if ( xRange.is() && cellInRange( xRange->getRangeAddress(), thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
else
{
// if this range is multi cell select the range other
// wise just position the cell at this single range position
if ( isSingleCellRange() )
// This top-leftmost cell of this Range is not in the current
// selection so just select this range
setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel, false );
else
Select();
}
}
ScRange ScVbaRange::obtainRangeEvenIfRangeListIsEmpty( const ScRangeList& rCellRanges ) const
{
// XXX It may be that using the current range list was never correct, but
// always the initial sheet range would be instead, history is unclear.
if (!rCellRanges.empty())
return rCellRanges.front();
table::CellRangeAddress aRA( lclGetRangeAddress( mxRange ));
return ScRange( aRA.StartColumn, aRA.StartRow, aRA.Sheet, aRA.EndColumn, aRA.EndRow, aRA.Sheet);
}
uno::Reference< excel::XRange >
ScVbaRange::Rows(const uno::Any& aIndex )
{
if ( aIndex.hasValue() )
{
ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
sal_Int32 nValue = 0;
OUString sAddress;
if( aIndex >>= nValue )
{
aRange.aStart.SetRow( aRange.aStart.Row() + --nValue );
aRange.aEnd.SetRow( aRange.aStart.Row() );
}
else if ( aIndex >>= sAddress )
{
ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
ScRange tmpRange;
tmpRange.ParseRows( getScDocument(), sAddress, dDetails );
SCROW nStartRow = tmpRange.aStart.Row();
SCROW nEndRow = tmpRange.aEnd.Row();
aRange.aStart.SetRow( aRange.aStart.Row() + nStartRow );
aRange.aEnd.SetRow( aRange.aStart.Row() + ( nEndRow - nStartRow ));
}
else
throw uno::RuntimeException(u"Illegal param"_ustr );
if ( aRange.aStart.Row() < 0 || aRange.aEnd.Row() < 0 )
throw uno::RuntimeException(u"Internal failure, illegal param"_ustr );
// return a normal range ( even for multi-selection
uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
return new ScVbaRange( mxParent, mxContext, xRange, true );
}
// Rows() - no params
if ( m_Areas->getCount() > 1 )
return new ScVbaRange( mxParent, mxContext, mxRanges, true );
return new ScVbaRange( mxParent, mxContext, mxRange, true );
}
uno::Reference< excel::XRange >
ScVbaRange::Columns(const uno::Any& aIndex )
{
ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
if ( aIndex.hasValue() )
{
OUString sAddress;
sal_Int32 nValue = 0;
if ( aIndex >>= nValue )
{
aRange.aStart.SetCol( aRange.aStart.Col() + static_cast< SCCOL > ( --nValue ) );
aRange.aEnd.SetCol( aRange.aStart.Col() );
}
else if ( aIndex >>= sAddress )
{
ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
ScRange tmpRange;
tmpRange.ParseCols( getScDocument(), sAddress, dDetails );
SCCOL nStartCol = tmpRange.aStart.Col();
SCCOL nEndCol = tmpRange.aEnd.Col();
aRange.aStart.SetCol( aRange.aStart.Col() + nStartCol );
aRange.aEnd.SetCol( aRange.aStart.Col() + ( nEndCol - nStartCol ));
}
else
throw uno::RuntimeException(u"Illegal param"_ustr );
if ( aRange.aStart.Col() < 0 || aRange.aEnd.Col() < 0 )
throw uno::RuntimeException(u"Internal failure, illegal param"_ustr );
}
// Columns() - no params
uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
return new ScVbaRange( mxParent, mxContext, xRange, false, true );
}
void
ScVbaRange::setMergeCells( const uno::Any& aIsMerged )
{
bool bMerge = extractBoolFromAny( aIsMerged );
if( mxRanges.is() )
{
sal_Int32 nCount = mxRanges->getCount();
// VBA does nothing (no error) if the own ranges overlap somehow
::std::vector< table::CellRangeAddress > aList;
for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
{
uno::Reference< sheet::XCellRangeAddressable > xRangeAddr( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
table::CellRangeAddress aAddress = xRangeAddr->getRangeAddress();
if (std::any_of(aList.begin(), aList.end(),
[&aAddress](const table::CellRangeAddress& rAddress)
{ return ScUnoConversion::Intersects( rAddress, aAddress ); }))
return;
aList.push_back( aAddress );
}
// (un)merge every range after it has been extended to intersecting merged ranges from sheet
for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
{
uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
lclExpandAndMerge( xRange, bMerge );
}
return;
}
// otherwise, merge single range
lclExpandAndMerge( mxRange, bMerge );
}
uno::Any
ScVbaRange::getMergeCells()
{
if( mxRanges.is() )
{
sal_Int32 nCount = mxRanges->getCount();
for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
{
uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
util::TriState eMerged = lclGetMergedState( xRange );
/* Excel always returns NULL, if one range of the range list is
partly or completely merged. Even if all ranges are completely
merged, the return value is still NULL. */
if( eMerged != util::TriState_NO )
return aNULL();
}
// no range is merged anyhow, return false
return uno::Any( false );
}
// otherwise, check single range
switch( lclGetMergedState( mxRange ) )
{
case util::TriState_YES: return uno::Any( true );
case util::TriState_NO: return uno::Any( false );
default: return aNULL();
}
}
void
ScVbaRange::Copy(const ::uno::Any& Destination)
{
if ( Destination.hasValue() )
{
// TODO copy with multiple selections should work here too
if ( m_Areas->getCount() > 1 )
throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
uno::Any aRange = xRange->getCellRange();
uno::Reference< table::XCellRange > xCellRange;
aRange >>= xCellRange;
uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW);
uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY_THROW );
uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
xMover->copyRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
if ( ScVbaRange* pRange = getImplementation( xRange ) )
pRange->fireChangeEvent();
}
else
{
Select();
excel::implnCopy(getUnoModel());
}
}
void
ScVbaRange::Cut(const ::uno::Any& Destination)
{
if ( m_Areas->getCount() > 1 )
throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
if (Destination.hasValue())
{
uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
uno::Reference< table::XCellRange > xCellRange( xRange->getCellRange(), uno::UNO_QUERY_THROW );
uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW );
uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY);
uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
xMover->moveRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
}
else
{
uno::Reference< frame::XModel > xModel = getModelFromRange( mxRange );
Select();
excel::implnCut( xModel );
}
}
void
ScVbaRange::setNumberFormat( const uno::Any& aFormat )
{
OUString sFormat;
aFormat >>= sFormat;
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 );
xRange->setNumberFormat( aFormat );
}
return;
}
NumFormatHelper numFormat( mxRange );
numFormat.setNumberFormat( sFormat );
}
uno::Any
ScVbaRange::getNumberFormat()
{
if ( m_Areas->getCount() > 1 )
{
sal_Int32 nItems = m_Areas->getCount();
uno::Any aResult = aNULL();
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 );
// if the numberformat of one area is different to another
// return null
if ( index > 1 )
if ( aResult != xRange->getNumberFormat() )
return aNULL();
aResult = xRange->getNumberFormat();
if ( aNULL() == aResult )
return aNULL();
}
return aResult;
}
NumFormatHelper numFormat( mxRange );
OUString sFormat = numFormat.getNumberFormatString();
if ( !sFormat.isEmpty() )
return uno::Any( sFormat );
return aNULL();
}
uno::Reference< excel::XRange >
ScVbaRange::Resize( const uno::Any &RowSize, const uno::Any &ColumnSize )
{
tools::Long nRowSize = 0, nColumnSize = 0;
bool bIsRowChanged = ( RowSize >>= nRowSize ), bIsColumnChanged = ( ColumnSize >>= nColumnSize );
uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, ::uno::UNO_QUERY_THROW);
uno::Reference< sheet::XSheetCellRange > xSheetRange(mxRange, ::uno::UNO_QUERY_THROW);
uno::Reference< sheet::XSheetCellCursor > xCursor( xSheetRange->getSpreadsheet()->createCursorByRange(xSheetRange), ::uno::UNO_SET_THROW );
if( !bIsRowChanged )
nRowSize = xColumnRowRange->getRows()->getCount();
if( !bIsColumnChanged )
nColumnSize = xColumnRowRange->getColumns()->getCount();
xCursor->collapseToSize( nColumnSize, nRowSize );
uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xCursor, ::uno::UNO_QUERY_THROW );
uno::Reference< table::XCellRange > xRange( xSheetRange->getSpreadsheet(), ::uno::UNO_QUERY_THROW );
const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
return new ScVbaRange( mxParent, mxContext, xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow));
}
void
ScVbaRange::setWrapText( const uno::Any& aIsWrapped )
{
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 );
xRange->setWrapText( aIsWrapped );
}
return;
}
uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
bool bIsWrapped = extractBoolFromAny( aIsWrapped );
xProps->setPropertyValue( u"IsTextWrapped"_ustr, uno::Any( bIsWrapped ) );
}
uno::Any
ScVbaRange::getWrapText()
{
if ( m_Areas->getCount() > 1 )
{
sal_Int32 nItems = m_Areas->getCount();
uno::Any aResult;
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 );
if ( index > 1 )
if ( aResult != xRange->getWrapText() )
return aNULL();
aResult = xRange->getWrapText();
}
return aResult;
}
SfxItemSet* pDataSet = getCurrentDataSet();
SfxItemState eState = pDataSet->GetItemState( ATTR_LINEBREAK);
if ( eState == SfxItemState::INVALID )
return aNULL();
uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
uno::Any aValue = xProps->getPropertyValue( u"IsTextWrapped"_ustr );
return aValue;
}
uno::Reference< excel::XInterior > ScVbaRange::Interior( )
{
uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
return new ScVbaInterior ( this, mxContext, xProps, &getScDocument() );
}
uno::Reference< excel::XRange >
ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2 )
{
return Range( Cell1, Cell2, false );
}
uno::Reference< excel::XRange >
ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2, bool bForceUseInpuRangeTab )
{
uno::Reference< table::XCellRange > xCellRange = mxRange;
if ( m_Areas->getCount() > 1 )
{
uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
}
else
xCellRange.set( mxRange );
RangeHelper thisRange( xCellRange );
uno::Reference< table::XCellRange > xRanges = thisRange.getCellRangeFromSheet();
uno::Reference< sheet::XCellRangeAddressable > xAddressable( xRanges, uno::UNO_QUERY_THROW );
uno::Reference< table::XCellRange > xReferrer =
xRanges->getCellRangeByPosition( getColumn()-1, getRow()-1,
xAddressable->getRangeAddress().EndColumn,
xAddressable->getRangeAddress().EndRow );
// xAddressable now for this range
xAddressable.set( xReferrer, uno::UNO_QUERY_THROW );
if( !Cell1.hasValue() )
throw uno::RuntimeException( u"Invalid Argument"_ustr );
table::CellRangeAddress parentRangeAddress = xAddressable->getRangeAddress();
ScRange aRange;
// Cell1 defined only
if ( !Cell2.hasValue() )
{
OUString sName;
Cell1 >>= sName;
RangeHelper referRange( xReferrer );
table::CellRangeAddress referAddress = referRange.getCellRangeAddressable()->getRangeAddress();
return getRangeForName( mxContext, sName, getScDocShell(), referAddress );
}
else
{
table::CellRangeAddress cell1, cell2;
cell1 = getCellRangeAddressForVBARange( Cell1, getScDocShell() );
// Cell1 & Cell2 defined
// Excel seems to combine the range as the range defined by
// the combination of Cell1 & Cell2
cell2 = getCellRangeAddressForVBARange( Cell2, getScDocShell() );
table::CellRangeAddress resultAddress;
resultAddress.StartColumn = ( cell1.StartColumn < cell2.StartColumn ) ? cell1.StartColumn : cell2.StartColumn;
resultAddress.StartRow = ( cell1.StartRow < cell2.StartRow ) ? cell1.StartRow : cell2.StartRow;
resultAddress.EndColumn = std::max( cell1.EndColumn, cell2.EndColumn );
resultAddress.EndRow = std::max( cell1.EndRow, cell2.EndRow );
if ( bForceUseInpuRangeTab )
{
// this is a call from Application.Range( x,y )
// it's possible for x or y to specify a different sheet from
// the current or active on ( but they must be the same )
if ( cell1.Sheet != cell2.Sheet )
throw uno::RuntimeException();
parentRangeAddress.Sheet = cell1.Sheet;
}
else
{
// this is not a call from Application.Range( x,y )
// if a different sheet from this range is specified it's
// an error
if ( parentRangeAddress.Sheet != cell1.Sheet
|| parentRangeAddress.Sheet != cell2.Sheet
)
throw uno::RuntimeException();
}
ScUnoConversion::FillScRange( aRange, resultAddress );
}
ScRange parentAddress;
ScUnoConversion::FillScRange( parentAddress, parentRangeAddress);
if ( aRange.aStart.Col() >= 0 && aRange.aStart.Row() >= 0 && aRange.aEnd.Col() >= 0 && aRange.aEnd.Row() >= 0 )
{
sal_Int32 nStartX = parentAddress.aStart.Col() + aRange.aStart.Col();
sal_Int32 nStartY = parentAddress.aStart.Row() + aRange.aStart.Row();
sal_Int32 nEndX = parentAddress.aStart.Col() + aRange.aEnd.Col();
sal_Int32 nEndY = parentAddress.aStart.Row() + aRange.aEnd.Row();
if ( nStartX <= nEndX && nEndX <= parentAddress.aEnd.Col() &&
nStartY <= nEndY && nEndY <= parentAddress.aEnd.Row() )
{
ScRange aNew( static_cast<SCCOL>(nStartX), static_cast<SCROW>(nStartY), parentAddress.aStart.Tab(),
static_cast<SCCOL>(nEndX), static_cast<SCROW>(nEndY), parentAddress.aEnd.Tab() );
xCellRange = new ScCellRangeObj( getScDocShell(), aNew );
}
}
return new ScVbaRange( mxParent, mxContext, xCellRange );
}
// Allow access to underlying openoffice uno api ( useful for debugging
// with openoffice basic )
uno::Any SAL_CALL ScVbaRange::getCellRange( )
{
uno::Any aAny;
if ( mxRanges.is() )
aAny <<= mxRanges;
else if ( mxRange.is() )
aAny <<= mxRange;
return aAny;
}
uno::Any ScVbaRange::getCellRange( const uno::Reference< excel::XRange >& rxRange )
{
if( ScVbaRange* pVbaRange = getImplementation( rxRange ) )
return pVbaRange->getCellRange();
throw uno::RuntimeException();
}
static InsertDeleteFlags getPasteFlags (sal_Int32 Paste)
{
InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
switch (Paste) {
case excel::XlPasteType::xlPasteComments:
nFlags = InsertDeleteFlags::NOTE;break;
case excel::XlPasteType::xlPasteFormats:
nFlags = InsertDeleteFlags::ATTRIB;break;
case excel::XlPasteType::xlPasteFormulas:
nFlags = InsertDeleteFlags::FORMULA;break;
case excel::XlPasteType::xlPasteFormulasAndNumberFormats :
case excel::XlPasteType::xlPasteValues:
nFlags = ( InsertDeleteFlags::VALUE | InsertDeleteFlags::DATETIME | InsertDeleteFlags::STRING | InsertDeleteFlags::SPECIAL_BOOLEAN ); break;
case excel::XlPasteType::xlPasteValuesAndNumberFormats:
nFlags = InsertDeleteFlags::VALUE | InsertDeleteFlags::ATTRIB; break;
case excel::XlPasteType::xlPasteColumnWidths:
case excel::XlPasteType::xlPasteValidation:
nFlags = InsertDeleteFlags::NONE;break;
case excel::XlPasteType::xlPasteAll:
case excel::XlPasteType::xlPasteAllExceptBorders:
default:
nFlags = InsertDeleteFlags::ALL;break;
}
return nFlags;
}
static ScPasteFunc
getPasteFormulaBits( sal_Int32 Operation)
{
ScPasteFunc nFormulaBits = ScPasteFunc::NONE;
switch (Operation)
{
case excel::XlPasteSpecialOperation::xlPasteSpecialOperationAdd:
nFormulaBits = ScPasteFunc::ADD; break;
case excel::XlPasteSpecialOperation::xlPasteSpecialOperationSubtract:
nFormulaBits = ScPasteFunc::SUB;break;
case excel::XlPasteSpecialOperation::xlPasteSpecialOperationMultiply:
nFormulaBits = ScPasteFunc::MUL;break;
case excel::XlPasteSpecialOperation::xlPasteSpecialOperationDivide:
nFormulaBits = ScPasteFunc::DIV;break;
case excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone:
default:
nFormulaBits = ScPasteFunc::NONE; break;
}
return nFormulaBits;
}
void SAL_CALL
ScVbaRange::PasteSpecial( const uno::Any& Paste, const uno::Any& Operation, const uno::Any& SkipBlanks, const uno::Any& Transpose )
{
if ( m_Areas->getCount() > 1 )
throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
ScDocShell* pShell = getScDocShell();
if (!pShell)
throw uno::RuntimeException(u"That command cannot be used with no ScDocShell"_ustr );
uno::Reference< frame::XModel > xModel(pShell->GetModel(), uno::UNO_SET_THROW);
uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
// select this range
xSelection->select( uno::Any( mxRange ) );
// set up defaults
sal_Int32 nPaste = excel::XlPasteType::xlPasteAll;
sal_Int32 nOperation = excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone;
bool bTranspose = false;
bool bSkipBlanks = false;
if ( Paste.hasValue() )
Paste >>= nPaste;
if ( Operation.hasValue() )
Operation >>= nOperation;
if ( SkipBlanks.hasValue() )
SkipBlanks >>= bSkipBlanks;
if ( Transpose.hasValue() )
Transpose >>= bTranspose;
InsertDeleteFlags nFlags = getPasteFlags(nPaste);
ScPasteFunc nFormulaBits = getPasteFormulaBits(nOperation);
excel::implnPasteSpecial(xModel, nFlags, nFormulaBits, bSkipBlanks, bTranspose);
}
uno::Reference< excel::XRange >
ScVbaRange::getEntireColumnOrRow( bool bColumn )
{
ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
// copy the range list
ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
ScDocument& rDoc = getScDocument();
for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
{
ScRange & rRange = aCellRanges[ i ];
if ( bColumn )
{
rRange.aStart.SetRow( 0 );
rRange.aEnd.SetRow( rDoc.MaxRow() );
}
else
{
rRange.aStart.SetCol( 0 );
rRange.aEnd.SetCol( rDoc.MaxCol() );
}
}
if ( aCellRanges.size() > 1 ) // Multi-Area
{
uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
return new ScVbaRange( mxParent, mxContext, xRanges, !bColumn, bColumn );
}
const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
return new ScVbaRange( mxParent, mxContext, xRange, !bColumn, bColumn );
}
uno::Reference< excel::XRange > SAL_CALL
ScVbaRange::getEntireRow()
{
return getEntireColumnOrRow(false);
}
uno::Reference< excel::XRange > SAL_CALL
ScVbaRange::getEntireColumn()
{
return getEntireColumnOrRow(true);
}
uno::Reference< excel::XComment > SAL_CALL
ScVbaRange::AddComment( const uno::Any& Text )
{
// if there is already a comment in the top-left cell then throw
if( getComment().is() )
throw uno::RuntimeException();
// workaround: Excel allows to create empty comment, Calc does not
OUString aNoteText;
if( Text.hasValue() && !(Text >>= aNoteText) )
throw uno::RuntimeException();
if( aNoteText.isEmpty() )
aNoteText = " ";
// try to create a new annotation
table::CellRangeAddress aRangePos = lclGetRangeAddress( mxRange );
table::CellAddress aNotePos( aRangePos.Sheet, aRangePos.StartColumn, aRangePos.StartRow );
uno::Reference< sheet::XSheetCellRange > xCellRange( mxRange, uno::UNO_QUERY_THROW );
uno::Reference< sheet::XSheetAnnotationsSupplier > xAnnosSupp( xCellRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
uno::Reference< sheet::XSheetAnnotations > xAnnos( xAnnosSupp->getAnnotations(), uno::UNO_SET_THROW );
xAnnos->insertNew( aNotePos, aNoteText );
return new ScVbaComment( this, mxContext, getUnoModel(), mxRange );
}
uno::Reference< excel::XComment > SAL_CALL
ScVbaRange::getComment()
{
// intentional behavior to return a null object if no
// comment defined
uno::Reference< excel::XComment > xComment( new ScVbaComment( this, mxContext, getUnoModel(), mxRange ) );
if ( xComment->Text( uno::Any(), uno::Any(), uno::Any() ).isEmpty() )
return nullptr;
return xComment;
}
/// @throws uno::RuntimeException
static uno::Reference< beans::XPropertySet >
getRowOrColumnProps( const uno::Reference< table::XCellRange >& xCellRange, bool bRows )
{
uno::Reference< table::XColumnRowRange > xColRow( xCellRange, uno::UNO_QUERY_THROW );
uno::Reference< beans::XPropertySet > xProps;
if ( bRows )
xProps.set( xColRow->getRows(), uno::UNO_QUERY_THROW );
else
xProps.set( xColRow->getColumns(), uno::UNO_QUERY_THROW );
return xProps;
}
uno::Any SAL_CALL
ScVbaRange::getHidden()
{
// if multi-area result is the result of the
// first area
if ( m_Areas->getCount() > 1 )
{
uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(sal_Int32(1)), uno::Any() ), uno::UNO_QUERY_THROW );
return xRange->getHidden();
}
bool bIsVisible = false;
try
{
uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
if ( !( xProps->getPropertyValue( ISVISIBLE ) >>= bIsVisible ) )
throw uno::RuntimeException(u"Failed to get IsVisible property"_ustr );
}
catch( const uno::Exception& e )
{
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException( e.Message,
nullptr, anyEx );
}
return uno::Any( !bIsVisible );
}
void SAL_CALL
ScVbaRange::setHidden( const uno::Any& _hidden )
{
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 );
xRange->setHidden( _hidden );
}
return;
}
bool bHidden = extractBoolFromAny( _hidden );
try
{
uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
xProps->setPropertyValue( ISVISIBLE, uno::Any( !bHidden ) );
}
catch( const uno::Exception& e )
{
css::uno::Any anyEx = cppu::getCaughtException();
throw css::lang::WrappedTargetRuntimeException( e.Message,
nullptr, anyEx );
}
}
sal_Bool SAL_CALL
ScVbaRange::Replace( const OUString& What, const OUString& Replacement, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& MatchCase, const uno::Any& MatchByte, const uno::Any& SearchFormat, const uno::Any& ReplaceFormat )
{
if ( m_Areas->getCount() > 1 )
{
for ( sal_Int32 index = 1; index <= m_Areas->getCount(); ++index )
{
uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
xRange->Replace( What, Replacement, LookAt, SearchOrder, MatchCase, MatchByte, SearchFormat, ReplaceFormat );
}
return true; // seems to return true always ( or at least I haven't found the trick of
}
// sanity check required params
if ( What.isEmpty() )
throw uno::RuntimeException(u"Range::Replace, missing params"_ustr );
// #TODO #FIXME SearchFormat & ReplacesFormat are not processed
// What do we do about MatchByte... we don't seem to support that
const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
SvxSearchItem newOptions( globalSearchOptions );
uno::Reference< util::XReplaceable > xReplace( mxRange, uno::UNO_QUERY );
if ( xReplace.is() )
{
uno::Reference< util::XReplaceDescriptor > xDescriptor =
xReplace->createReplaceDescriptor();
xDescriptor->setSearchString(What);
xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
xDescriptor->setReplaceString( Replacement);
if ( LookAt.hasValue() )
{
// sets SearchWords ( true is Cell match )
sal_Int16 nLook = ::comphelper::getINT16( LookAt );
bool bSearchWords = false;
if ( nLook == excel::XlLookAt::xlPart )
bSearchWords = false;
else if ( nLook == excel::XlLookAt::xlWhole )
bSearchWords = true;
else
throw uno::RuntimeException(u"Range::Replace, illegal value for LookAt"_ustr );
// set global search props ( affects the find dialog
// and of course the defaults for this method
newOptions.SetWordOnly( bSearchWords );
xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
}
// sets SearchByRow ( true for Rows )
if ( SearchOrder.hasValue() )
{
sal_Int16 nSearchOrder = ::comphelper::getINT16( SearchOrder );
bool bSearchByRow = false;
if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
bSearchByRow = false;
else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
bSearchByRow = true;
else
throw uno::RuntimeException(u"Range::Replace, illegal value for SearchOrder"_ustr );
newOptions.SetRowDirection( bSearchByRow );
xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
}
if ( MatchCase.hasValue() )
{
bool bMatchCase = false;
// SearchCaseSensitive
MatchCase >>= bMatchCase;
xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
}
ScGlobal::SetSearchItem( newOptions );
// ignore MatchByte for the moment, it's not supported in
// OOo.org afaik
uno::Reference< container::XIndexAccess > xIndexAccess = xReplace->findAll( xDescriptor );
xReplace->replaceAll( xDescriptor );
if ( xIndexAccess.is() && xIndexAccess->getCount() > 0 )
{
for ( sal_Int32 i = 0; i < xIndexAccess->getCount(); ++i )
{
uno::Reference< table::XCellRange > xCellRange( xIndexAccess->getByIndex( i ), uno::UNO_QUERY );
if ( xCellRange.is() )
{
uno::Reference< excel::XRange > xRange( new ScVbaRange( mxParent, mxContext, xCellRange ) );
uno::Reference< container::XEnumerationAccess > xEnumAccess( xRange, uno::UNO_QUERY_THROW );
uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
while ( xEnum->hasMoreElements() )
{
uno::Reference< excel::XRange > xNextRange( xEnum->nextElement(), uno::UNO_QUERY_THROW );
ScVbaRange* pRange = dynamic_cast< ScVbaRange * > ( xNextRange.get() );
if ( pRange )
pRange->fireChangeEvent();
}
}
}
}
}
return true; // always
}
uno::Reference< excel::XRange > SAL_CALL
ScVbaRange::Find( const uno::Any& What, const uno::Any& After, const uno::Any& LookIn, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& SearchDirection, const uno::Any& MatchCase, const uno::Any& /*MatchByte*/, const uno::Any& /*SearchFormat*/ )
{
// return a Range object that represents the first cell where that information is found.
OUString sWhat;
sal_Int32 nWhat = 0;
double fWhat = 0.0;
// string.
if( What >>= sWhat )
{}
else if( What >>= nWhat )
{
sWhat = OUString::number( nWhat );
}
else if( What >>= fWhat )
{
sWhat = OUString::number( fWhat );
}
else
throw uno::RuntimeException(u"Range::Find, missing search-for-what param"_ustr );
const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
SvxSearchItem newOptions( globalSearchOptions );
uno::Reference< util::XSearchable > xSearch( mxRange, uno::UNO_QUERY );
if( xSearch.is() )
{
uno::Reference< util::XSearchDescriptor > xDescriptor = xSearch->createSearchDescriptor();
xDescriptor->setSearchString(sWhat);
xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
uno::Reference< excel::XRange > xAfterRange;
uno::Reference< table::XCellRange > xStartCell;
if( After >>= xAfterRange )
{
// After must be a single cell in the range
if( xAfterRange->getCount() > 1 )
throw uno::RuntimeException(u"After must be a single cell."_ustr );
uno::Reference< excel::XRange > xCell( Cells( uno::Any( xAfterRange->getRow() ), uno::Any( xAfterRange->getColumn() ) ), uno::UNO_SET_THROW );
xStartCell.set( xAfterRange->getCellRange(), uno::UNO_QUERY_THROW );
}
// LookIn
if( LookIn.hasValue() )
{
sal_Int32 nLookIn = 0;
if( LookIn >>= nLookIn )
{
SvxSearchCellType nSearchType;
switch( nLookIn )
{
case excel::XlFindLookIn::xlComments :
nSearchType = SvxSearchCellType::NOTE; // Notes
break;
case excel::XlFindLookIn::xlFormulas :
nSearchType = SvxSearchCellType::FORMULA;
break;
case excel::XlFindLookIn::xlValues :
nSearchType = SvxSearchCellType::VALUE;
break;
default:
throw uno::RuntimeException(u"Range::Find, illegal value for LookIn."_ustr );
}
newOptions.SetCellType( nSearchType );
xDescriptor->setPropertyValue( u"SearchType"_ustr, uno::Any( static_cast<sal_uInt16>(nSearchType) ) );
}
}
// LookAt
if ( LookAt.hasValue() )
{
sal_Int16 nLookAt = ::comphelper::getINT16( LookAt );
bool bSearchWords = false;
if ( nLookAt == excel::XlLookAt::xlPart )
bSearchWords = false;
else if ( nLookAt == excel::XlLookAt::xlWhole )
bSearchWords = true;
else
throw uno::RuntimeException(u"Range::Find, illegal value for LookAt"_ustr );
newOptions.SetWordOnly( bSearchWords );
xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
}
// SearchOrder
if ( SearchOrder.hasValue() )
{
sal_Int16 nSearchOrder = ::comphelper::getINT16( SearchOrder );
bool bSearchByRow = false;
if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
bSearchByRow = false;
else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
bSearchByRow = true;
else
throw uno::RuntimeException(u"Range::Find, illegal value for SearchOrder"_ustr );
newOptions.SetRowDirection( bSearchByRow );
xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
}
// SearchDirection
if ( SearchDirection.hasValue() )
{
sal_Int32 nSearchDirection = 0;
if( SearchDirection >>= nSearchDirection )
{
bool bSearchBackwards = false;
if ( nSearchDirection == excel::XlSearchDirection::xlNext )
bSearchBackwards = false;
else if( nSearchDirection == excel::XlSearchDirection::xlPrevious )
bSearchBackwards = true;
else
throw uno::RuntimeException(u"Range::Find, illegal value for SearchDirection"_ustr );
newOptions.SetBackward( bSearchBackwards );
xDescriptor->setPropertyValue( u"SearchBackwards"_ustr, uno::Any( bSearchBackwards ) );
}
}
// MatchCase
bool bMatchCase = false;
if ( MatchCase.hasValue() )
{
// SearchCaseSensitive
if( !( MatchCase >>= bMatchCase ) )
throw uno::RuntimeException(u"Range::Find illegal value for MatchCase"_ustr );
}
xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
// MatchByte
// SearchFormat
// ignore
ScGlobal::SetSearchItem( newOptions );
uno::Reference< uno::XInterface > xInterface = xStartCell.is() ? xSearch->findNext( xStartCell, xDescriptor) : xSearch->findFirst( xDescriptor );
uno::Reference< table::XCellRange > xCellRange( xInterface, uno::UNO_QUERY );
// if we are searching from a starting cell and failed to find a match
// then try from the beginning
if ( !xCellRange.is() && xStartCell.is() )
{
xInterface = xSearch->findFirst( xDescriptor );
xCellRange.set( xInterface, uno::UNO_QUERY );
}
if ( xCellRange.is() )
{
uno::Reference< excel::XRange > xResultRange = new ScVbaRange( mxParent, mxContext, xCellRange );
if( xResultRange.is() )
{
return xResultRange;
}
}
}
return uno::Reference< excel::XRange >();
}
static uno::Reference< table::XCellRange > processKey( const uno::Any& Key, const uno::Reference< uno::XComponentContext >& xContext, ScDocShell* pDocSh )
{
uno::Reference< excel::XRange > xKeyRange;
if (Key.getValueTypeClass() == css::uno::TypeClass_INTERFACE)
{
xKeyRange.set( Key, uno::UNO_QUERY_THROW );
}
else if ( Key.getValueType() == ::cppu::UnoType<OUString>::get() )
{
OUString sRangeName = ::comphelper::getString( Key );
table::CellRangeAddress aRefAddr;
if ( !pDocSh )
throw uno::RuntimeException(u"Range::Sort no docshell to calculate key param"_ustr );
xKeyRange = getRangeForName( xContext, sRangeName, pDocSh, aRefAddr );
}
else
throw uno::RuntimeException(u"Range::Sort illegal type value for key param"_ustr );
uno::Reference< table::XCellRange > xKey;
xKey.set( xKeyRange->getCellRange(), uno::UNO_QUERY_THROW );
return xKey;
}
// helper method for Sort
/// @throws uno::RuntimeException
static sal_Int32 findSortPropertyIndex( const uno::Sequence< beans::PropertyValue >& props,
const OUString& sPropName )
{
const beans::PropertyValue* pProp = std::find_if(props.begin(), props.end(),
[&sPropName](const beans::PropertyValue& rProp) { return rProp.Name == sPropName; });
if ( pProp == props.end() )
throw uno::RuntimeException(u"Range::Sort unknown sort property"_ustr );
return static_cast<sal_Int32>(std::distance(props.begin(), pProp));
}
// helper method for Sort
/// @throws uno::RuntimeException
static void updateTableSortField( const uno::Reference< table::XCellRange >& xParentRange,
const uno::Reference< table::XCellRange >& xColRowKey, sal_Int16 nOrder,
table::TableSortField& aTableField, bool bIsSortColumn, bool bMatchCase )
{
RangeHelper parentRange( xParentRange );
RangeHelper colRowRange( xColRowKey );
table::CellRangeAddress parentRangeAddress = parentRange.getCellRangeAddressable()->getRangeAddress();
table::CellRangeAddress colRowKeyAddress = colRowRange.getCellRangeAddressable()->getRangeAddress();
// make sure that upper left point of key range is within the
// parent range
if (
( bIsSortColumn || colRowKeyAddress.StartColumn < parentRangeAddress.StartColumn ||
colRowKeyAddress.StartColumn > parentRangeAddress.EndColumn )
&&
( !bIsSortColumn || colRowKeyAddress.StartRow < parentRangeAddress.StartRow ||
colRowKeyAddress.StartRow > parentRangeAddress.EndRow )
)
throw uno::RuntimeException(u"Illegal Key param"_ustr );
//determine col/row index
if ( bIsSortColumn )
aTableField.Field = colRowKeyAddress.StartRow - parentRangeAddress.StartRow;
else
aTableField.Field = colRowKeyAddress.StartColumn - parentRangeAddress.StartColumn;
aTableField.IsCaseSensitive = bMatchCase;
if ( nOrder == excel::XlSortOrder::xlAscending )
aTableField.IsAscending = true;
else
aTableField.IsAscending = false;
}
void SAL_CALL
ScVbaRange::Sort( const uno::Any& Key1, const uno::Any& Order1, const uno::Any& Key2, const uno::Any& /*Type*/, const uno::Any& Order2, const uno::Any& Key3, const uno::Any& Order3, const uno::Any& Header, const uno::Any& OrderCustom, const uno::Any& MatchCase, const uno::Any& Orientation, const uno::Any& Sort | |