Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


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::XlBordersIndex::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 >&&nbsp;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 >&&nbsp;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 >&&nbsp;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 >&&nbsp;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
templatetypename 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 >&&nbsp;xRange  )
{
    uno::Reference< XHelperInterface > xParent( xParentRange, uno::UNO_QUERY_THROW );
    ScDocument& rDoc = getDocumentFromRange(xRange);
    ScVbaPalette aPalette( rDoc.GetDocumentShell() );
    uno::Reference< XCollection > borders( new ScVbaBorders( xParent, xContext, xRange, aPalette ) );
    return borders;
}

ScVbaRange::ScVbaRange( uno::Sequence< uno::Any> const & args,
    uno::Reference< uno::XComponentContext> const & xContext ) : ScVbaRange_BASE( getXSomethingFromArgs< XHelperInterface >( args, 0 ), xContext, getXSomethingFromArgs< beans::XPropertySet >( args, 1, false ), getModelFromXIf( getXSomethingFromArgs< uno::XInterface >( args, 1 ) ), true ), mbIsRows( false ), mbIsColumns( false )
{
    mxRange.set( mxPropertySet, uno::UNO_QUERY );
    mxRanges.set( mxPropertySet, uno::UNO_QUERY );
    uno::Reference< container::XIndexAccess >  xIndex;
    if ( mxRange.is() )
    {
        xIndex = new SingleRangeIndexAccess( mxRange );
    }
    else if ( mxRanges.is() )
    {
        xIndex.set( mxRanges, uno::UNO_QUERY_THROW );
    }
    m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
}

ScVbaRange::ScVbaRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< table::XCellRange >& xRange, bool bIsRows, bool bIsColumns )
: ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRange, uno::UNO_QUERY_THROW ), getModelFromRange( xRange), true ), mxRange( xRange ),
                mbIsRows( bIsRows ),
                mbIsColumns( bIsColumns )
{
    if  ( !xContext.is() )
        throw lang::IllegalArgumentException(u"context is not set "_ustr, uno::Reference< uno::XInterface >() , 1 );
    if  ( !xRange.is() )
        throw lang::IllegalArgumentException(u"range is not set "_ustr, uno::Reference< uno::XInterface >() , 1 );

    uno::Reference< container::XIndexAccess > xIndex( new SingleRangeIndexAccess( xRange ) );
    m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );

}

ScVbaRange::ScVbaRange(const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges, bool bIsRows, bool bIsColumns)
: ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRanges, uno::UNO_QUERY_THROW ), getModelFromXIf( uno::Reference< uno::XInterface >( xRanges, uno::UNO_QUERY_THROW ) ), true ), mxRanges( xRanges ),mbIsRows( bIsRows ), mbIsColumns( bIsColumns )

{
    uno::Reference< container::XIndexAccess >  xIndex( mxRanges, uno::UNO_QUERY_THROW );
    m_Areas  = new ScVbaRangeAreas( xParent, mxContext, xIndex, mbIsRows, mbIsColumns );

}

ScVbaRange::~ScVbaRange()
{
}

uno::Reference< XCollection >& ScVbaRange::getBorders()
{
    if ( !m_Borders.is() )
    {
        uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
        m_Borders = lcl_setupBorders( this, mxContext, uno::Reference< table::XCellRange >( xRange->getCellRange(), uno::UNO_QUERY_THROW ) );
    }
    return m_Borders;
}

void
ScVbaRange::visitArray( ArrayVisitor& visitor )
{
    ScDocShell* pDocSh = nullptr;
    if(ScCellRangeObj* range = dynamic_cast<ScCellRangeObj*>(mxRange.get()))
        pDocSh = range->GetDocShell();
    if ( pDocSh )
        pDocSh->LockPaint();
    table::CellRangeAddress aRangeAddr = lclGetRangeAddress( mxRange );
    sal_Int32 nRowCount = aRangeAddr.EndRow - aRangeAddr.StartRow + 1;
    sal_Int32 nColCount = aRangeAddr.EndColumn - aRangeAddr.StartColumn + 1;
    for ( sal_Int32 i=0; i<nRowCount; ++i )
    {
        for ( sal_Int32 j=0; j<nColCount; ++j )
        {
            uno::Reference< table::XCell > xCell( mxRange->getCellByPosition( j, i ), uno::UNO_SET_THROW );

            visitor.visitNode( i, j, xCell );
        }
    }
    if ( pDocSh )
        pDocSh->UnlockPaint();
}

uno::Any
ScVbaRange::getValue( ValueGetter& valueGetter)
{
    uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
    // single cell range
    if ( isSingleCellRange() )
    {
        visitArray( valueGetter );
        return valueGetter.getValue();
    }
    sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
    sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
    // multi cell range ( return array )
    Dim2ArrayValueGetter arrayGetter( nRowCount, nColCount, valueGetter );
    visitArray( arrayGetter );
    return uno::Any( script::ArrayWrapper( false, arrayGetter.getValue() ) );
}

css::uno::Any ScVbaRange::DoGetValue( RangeValueType eValueType )
{
    // #TODO code within the test below "if ( m_Areas... " can be removed
    // Test is performed only because m_xRange is NOT set to be
    // the first range in m_Areas ( to force failure while
    // the implementations for each method are being updated )
    if ( m_Areas->getCount() > 1 )
    {
        uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
        return xRange->getValue();
    }

    CellValueGetter valueGetter( eValueType );
    return getValue( valueGetter );
}

uno::Any SAL_CALL
ScVbaRange::getValue()
{
    return DoGetValue( RangeValueType::value );
}

uno::Any SAL_CALL
ScVbaRange::getValue2()
{
    return DoGetValue( RangeValueType::value2 );
}


void
ScVbaRange::setValue( const uno::Any& aValue, ValueSetter& valueSetter )
{
    uno::TypeClass aClass = aValue.getValueTypeClass();
    if ( aClass == uno::TypeClass_SEQUENCE )
    {
        const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
        uno::Any aConverted;
        try
        {
            // test for single dimension, could do
            // with a better test than this
            if ( aValue.getValueTypeName().indexOf('[') ==  aValue.getValueTypeName().lastIndexOf('[') )
            {
                aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Any >>::get() );
                Dim1ArrayValueSetter setter( aConverted, valueSetter );
                visitArray( setter );
            }
            else
            {
                aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() );
                Dim2ArrayValueSetter setter( aConverted, valueSetter );
                visitArray( setter );
            }
        }
        catch ( const uno::Exception& )
        {
            TOOLS_WARN_EXCEPTION("sc""Bahhh, caught" );
        }
    }
    else
    {
        visitArray( valueSetter );
    }
    fireChangeEvent();
}

void SAL_CALL
ScVbaRange::setValue( const uno::Any  &aValue )
{
    // If this is a multiple selection apply setValue over all areas
    if ( m_Areas->getCount() > 1 )
    {
        AreasVisitor aVisitor( m_Areas );
        RangeValueProcessor valueProcessor( aValue );
        aVisitor.visit( valueProcessor );
        return;
    }
    CellValueSetter valueSetter( aValue );
    setValue( aValue, valueSetter );
}

void SAL_CALL
ScVbaRange::setValue2( const uno::Any  &aValue )
{
    return setValue( aValue );
}


void SAL_CALL
ScVbaRange::Clear()
{
    using namespace ::com::sun::star::sheet::CellFlags;
    sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA | HARDATTR | EDITATTR | FORMATTED;
    ClearContents( nFlags, true );
}

//helper ClearContent
void
ScVbaRange::ClearContents( sal_Int32 nFlags, bool bFireEvent )
{
    // #TODO code within the test below "if ( m_Areas... " can be removed
    // Test is performed only because m_xRange is NOT set to be
    // the first range in m_Areas ( to force failure while
    // the implementations for each method are being updated )
    if ( m_Areas->getCount() > 1 )
    {
        sal_Int32 nItems = m_Areas->getCount();
        for ( sal_Int32 index=1; index <= nItems; ++index )
        {
            uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
            ScVbaRange* pRange = getImplementation( xRange );
            if ( pRange )
                pRange->ClearContents( nFlags, false ); // do not fire for single ranges
        }
        // fire change event for the entire range list
        if( bFireEvent ) fireChangeEvent();
        return;
    }

    uno::Reference< sheet::XSheetOperation > xSheetOperation(mxRange, uno::UNO_QUERY_THROW);
    xSheetOperation->clearContents( nFlags );
    if( bFireEvent ) fireChangeEvent();
}

void SAL_CALL
ScVbaRange::ClearComments()
{
    ClearContents( sheet::CellFlags::ANNOTATION, false );
}

void SAL_CALL
ScVbaRange::ClearContents()
{
    using namespace ::com::sun::star::sheet::CellFlags;
    sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA;
    ClearContents( nFlags, true );
}

void SAL_CALL
ScVbaRange::ClearFormats()
{
    // FIXME: need to check if we need to combine FORMATTED
    using namespace ::com::sun::star::sheet::CellFlags;
    sal_Int32 const nFlags = HARDATTR | FORMATTED | EDITATTR;
    ClearContents( nFlags, false );
}

void
ScVbaRange::setFormulaValue( const uno::Any& rFormula, formula::FormulaGrammar::Grammar eGram )
{
    // If this is a multiple selection apply setFormula over all areas
    if ( m_Areas->getCount() > 1 )
    {
        AreasVisitor aVisitor( m_Areas );
        RangeFormulaProcessor valueProcessor( rFormula );
        aVisitor.visit( valueProcessor );
        return;
    }
    CellFormulaValueSetter formulaValueSetter( rFormula, getScDocument(), eGram );
    setValue( rFormula, formulaValueSetter );
}

uno::Any
ScVbaRange::getFormulaValue( formula::FormulaGrammar::Grammar eGram )
{
    // #TODO code within the test below "if ( m_Areas... " can be removed
    // Test is performed only because m_xRange is NOT set to be
    // the first range in m_Areas ( to force failure while
    // the implementations for each method are being updated )
    if ( m_Areas->getCount() > 1 )
    {
        uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
        return xRange->getFormula();
    }
    CellFormulaValueGetter valueGetter( getScDocument(), eGram );
    return getValue( valueGetter );

}

uno::Any
ScVbaRange::getFormula()
{
    return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
}

void
ScVbaRange::setFormula(const uno::Any &rFormula )
{
    setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
}

uno::Any
ScVbaRange::getFormulaR1C1()
{
    return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
}

void
ScVbaRange::setFormulaR1C1(const uno::Any& rFormula )
{
    setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
}

uno::Any
ScVbaRange::getFormulaLocal()
{
    return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
}

void
ScVbaRange::setFormulaLocal(const uno::Any &rFormula )
{
    setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
}

uno::Any
ScVbaRange::getFormulaR1C1Local()
{
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=95 G=93

¤ Dauer der Verarbeitung: 0.9 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge