diff options
Diffstat (limited to 'sc/source/ui/vba/vbarange.cxx')
-rw-r--r-- | sc/source/ui/vba/vbarange.cxx | 416 |
1 files changed, 350 insertions, 66 deletions
diff --git a/sc/source/ui/vba/vbarange.cxx b/sc/source/ui/vba/vbarange.cxx index 1a153b440b15..ccf5a3bc5b54 100644 --- a/sc/source/ui/vba/vbarange.cxx +++ b/sc/source/ui/vba/vbarange.cxx @@ -81,6 +81,7 @@ #include <com/sun/star/sheet/XSheetFilterable.hpp> #include <com/sun/star/sheet/FilterConnection.hpp> #include <com/sun/star/util/CellProtection.hpp> +#include <com/sun/star/util/TriState.hpp> #include <com/sun/star/style/XStyleFamiliesSupplier.hpp> #include <com/sun/star/awt/XDevice.hpp> @@ -141,6 +142,7 @@ #include "vbaborders.hxx" #include "vbaworksheet.hxx" #include "vbavalidation.hxx" +#include "vbahyperlinks.hxx" #include "tabvwsh.hxx" #include "rangelst.hxx" @@ -367,7 +369,7 @@ ScVbaRangeAreas::createCollectionObject( const uno::Any& aSource ) ScDocShell* getDocShellFromIf( const uno::Reference< uno::XInterface >& xIf ) throw ( uno::RuntimeException ) { - ScCellRangesBase* pUno= dynamic_cast< ScCellRangesBase* >( xIf.get() ); + ScCellRangesBase* pUno = ScCellRangesBase::getImplementation( xIf ); if ( !pUno ) throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Failed to access underlying uno range object" ) ), uno::Reference< uno::XInterface >() ); return pUno->GetDocShell(); @@ -381,6 +383,14 @@ getDocShellFromRange( const uno::Reference< table::XCellRange >& xRange ) throw return getDocShellFromIf(xIf ); } +ScDocShell* +getDocShellFromRanges( const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges ) throw ( uno::RuntimeException ) +{ + // need the ScCellRangesBase to get docshell + uno::Reference< uno::XInterface > xIf( xRanges, uno::UNO_QUERY_THROW ); + return getDocShellFromIf(xIf ); +} + uno::Reference< frame::XModel > getModelFromXIf( const uno::Reference< uno::XInterface >& xIf ) throw ( uno::RuntimeException ) { ScDocShell* pDocShell = getDocShellFromIf(xIf ); @@ -405,7 +415,7 @@ getDocumentFromRange( const uno::Reference< table::XCellRange >& xRange ) ScDocument* -ScVbaRange::getScDocument() +ScVbaRange::getScDocument() throw (uno::RuntimeException) { if ( mxRanges.is() ) { @@ -417,7 +427,7 @@ ScVbaRange::getScDocument() } ScDocShell* -ScVbaRange::getScDocShell() +ScVbaRange::getScDocShell() throw (uno::RuntimeException) { if ( mxRanges.is() ) { @@ -428,6 +438,41 @@ ScVbaRange::getScDocShell() return getDocShellFromRange( mxRange ); } +/*static*/ 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() throw (uno::RuntimeException) +{ + if( ScDocShell* pDocShell = getScDocShell() ) + return pDocShell->GetModel(); + throw uno::RuntimeException(); +} + +/*static*/ uno::Reference< frame::XModel > ScVbaRange::getUnoModel( const uno::Reference< excel::XRange >& rxRange ) throw (uno::RuntimeException) +{ + if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) ) + return pScVbaRange->getUnoModel(); + throw uno::RuntimeException(); +} + +const ScRangeList& ScVbaRange::getScRangeList() throw (uno::RuntimeException) +{ + if( ScCellRangesBase* pScRangesBase = getCellRangesBase() ) + return pScRangesBase->GetRangeList(); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Cannot obtain UNO range implementation object" ) ), uno::Reference< uno::XInterface >() ); +} + +/*static*/ const ScRangeList& ScVbaRange::getScRangeList( const uno::Reference< excel::XRange >& rxRange ) throw (uno::RuntimeException) +{ + if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) ) + return pScVbaRange->getScRangeList(); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Cannot obtain VBA range implementation object" ) ), uno::Reference< uno::XInterface >() ); +} + + class NumFormatHelper { uno::Reference< util::XNumberFormatsSupplier > mxSupplier; @@ -472,7 +517,7 @@ public: rtl::OUString getNumberFormatString() { uno::Reference< uno::XInterface > xIf( mxRangeProps, uno::UNO_QUERY_THROW ); - ScCellRangeObj* pUnoCellRange = dynamic_cast< ScCellRangeObj* >( xIf.get() ); + ScCellRangesBase* pUnoCellRange = ScCellRangesBase::getImplementation( xIf ); if ( pUnoCellRange ) { @@ -577,20 +622,17 @@ class CellsEnumeration : public CellsEnumeration_BASE uno::Reference< XCollection > m_xAreas; vCellPos m_CellPositions; vCellPos::const_iterator m_it; + uno::Reference< table::XCellRange > getArea( sal_Int32 nVBAIndex ) throw ( uno::RuntimeException ) { if ( nVBAIndex < 1 || nVBAIndex > m_xAreas->getCount() ) throw uno::RuntimeException(); uno::Reference< excel::XRange > xRange( m_xAreas->Item( uno::makeAny(nVBAIndex), uno::Any() ), uno::UNO_QUERY_THROW ); - ScVbaRange* pRange = dynamic_cast< ScVbaRange* >( xRange.get() ); - uno::Reference< table::XCellRange > xCellRange; - if ( !pRange ) - throw uno::RuntimeException(); - xCellRange.set( pRange->getCellRange(), uno::UNO_QUERY_THROW );; + uno::Reference< table::XCellRange > xCellRange( ScVbaRange::getCellRange( xRange ), uno::UNO_QUERY_THROW ); return xCellRange; - } - void populateArea( sal_Int32 nVBAIndex ) + + void populateArea( sal_Int32 nVBAIndex ) { uno::Reference< table::XCellRange > xRange = getArea( nVBAIndex ); uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, uno::UNO_QUERY_THROW ); @@ -1136,7 +1178,7 @@ bool getScRangeListForAddress( const rtl::OUString& sName, ScDocShell* pDocSh, S ScVbaRange* -getRangeForName( const uno::Reference< uno::XComponentContext >& xContext, const rtl::OUString& sName, ScDocShell* pDocSh, table::CellRangeAddress& pAddr, formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_XL_A1 ) throw ( uno::RuntimeException ) +getRangeForName( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const rtl::OUString& sName, ScDocShell* pDocSh, table::CellRangeAddress& pAddr, formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_XL_A1 ) throw ( uno::RuntimeException ) { ScRangeList aCellRanges; ScRange refRange; @@ -1147,20 +1189,107 @@ getRangeForName( const uno::Reference< uno::XComponentContext >& xContext, const if ( aCellRanges.First() == aCellRanges.Last() ) { uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pDocSh, *aCellRanges.First() ) ); - // #FIXME need proper (WorkSheet) parent - return new ScVbaRange( uno::Reference< XHelperInterface >(), xContext, xRange ); + return new ScVbaRange( xParent, xContext, xRange ); } uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDocSh, aCellRanges ) ); + return new ScVbaRange( xParent, xContext, xRanges ); +} - // #FIXME need proper (WorkSheet) parent - return new ScVbaRange( uno::Reference< XHelperInterface >(), xContext, xRanges ); +// ---------------------------------------------------------------------------- + +namespace { + +template< typename RangeType > +inline table::CellRangeAddress lclGetRangeAddress( const uno::Reference< RangeType >& rxCellRange ) throw (uno::RuntimeException) +{ + return uno::Reference< sheet::XCellRangeAddressable >( rxCellRange, uno::UNO_QUERY_THROW )->getRangeAddress(); +} + +uno::Reference< sheet::XSheetCellRange > lclExpandToMerged( const uno::Reference< table::XCellRange >& rxCellRange, bool bRecursive ) throw (uno::RuntimeException) +{ + 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 ); + xCursor->collapseToMergedArea(); + xNewCellRange.set( xCursor, uno::UNO_QUERY_THROW ); + aNewAddress = lclGetRangeAddress( xNewCellRange ); + } + while( bRecursive && (aOldAddress != aNewAddress) ); + return xNewCellRange; } +uno::Reference< sheet::XSheetCellRangeContainer > lclExpandToMerged( const uno::Reference< sheet::XSheetCellRangeContainer >& rxCellRanges, bool bRecursive ) throw (uno::RuntimeException) +{ + if( !rxCellRanges.is() ) + throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Missing cell ranges object" ) ), uno::Reference< uno::XInterface >() ); + sal_Int32 nCount = rxCellRanges->getCount(); + if( nCount < 1 ) + throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Missing cell ranges object" ) ), uno::Reference< uno::XInterface >() ); + + 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 ) ); + ScRange aScRange; + ScUnoConversion::FillScRange( aScRange, aRangeAddr ); + aScRanges.Append( aScRange ); + } + return new ScCellRangesObj( getDocShellFromRanges( rxCellRanges ), aScRanges ); +} + +void lclExpandAndMerge( const uno::Reference< table::XCellRange >& rxCellRange, bool bMerge ) throw (uno::RuntimeException) +{ + uno::Reference< util::XMergeable > xMerge( lclExpandToMerged( rxCellRange, true ), uno::UNO_QUERY_THROW ); + // Calc cannot merge over merged ranges, always unmerge first + xMerge->merge( sal_False ); + if( bMerge ) + xMerge->merge( sal_True ); + // FIXME need to check whether all the cell contents are retained or lost by popping up a dialog +} + +util::TriState lclGetMergedState( const uno::Reference< table::XCellRange >& rxCellRange ) throw (uno::RuntimeException) +{ + /* 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 excude 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, HASATTR_MERGED | HASATTR_OVERLAPPED ); + return bHasMerged ? util::TriState_INDETERMINATE : util::TriState_NO; +} + +} // namespace + +// ---------------------------------------------------------------------------- + css::uno::Reference< excel::XRange > -ScVbaRange::getRangeObjectForName( const uno::Reference< uno::XComponentContext >& xContext, const rtl::OUString& sRangeName, ScDocShell* pDocSh, formula::FormulaGrammar::AddressConvention eConv ) throw ( uno::RuntimeException ) +ScVbaRange::getRangeObjectForName( const css::uno::Reference< ov::XHelperInterface >& xParent, + const uno::Reference< uno::XComponentContext >& xContext, const rtl::OUString& sRangeName, + ScDocShell* pDocSh, formula::FormulaGrammar::AddressConvention eConv ) throw ( uno::RuntimeException ) { table::CellRangeAddress refAddr; - return getRangeForName( xContext, sRangeName, pDocSh, refAddr, eConv ); + return getRangeForName( xParent, xContext, sRangeName, pDocSh, refAddr, eConv ); } @@ -1196,9 +1325,7 @@ table::CellRangeAddress getCellRangeAddressForVBARange( const uno::Any& aParam, default: throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Can't extact CellRangeAddress from type" ) ), uno::Reference< uno::XInterface >() ); } - uno::Reference< sheet::XCellRangeAddressable > xAddressable( xRangeParam, uno::UNO_QUERY_THROW ); - return xAddressable->getRangeAddress(); - + return lclGetRangeAddress( xRangeParam ); } uno::Reference< XCollection > @@ -1398,7 +1525,7 @@ ScVbaRange::ClearContents( sal_Int32 nFlags ) throw (uno::RuntimeException) for ( sal_Int32 index=1; index <= nItems; ++index ) { uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::makeAny(index), uno::Any() ), uno::UNO_QUERY_THROW ); - ScVbaRange* pRange = dynamic_cast< ScVbaRange* >( xRange.get() ); + ScVbaRange* pRange = getImplementation( xRange ); if ( pRange ) pRange->ClearContents( nFlags ); } @@ -1595,7 +1722,7 @@ ScVbaRange::fillSeries( sheet::FillDirection nFillDirection, sheet::FillMode nFi for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index ) { uno::Reference< excel::XRange > xRange( xCollection->Item( uno::makeAny( index ), uno::Any() ), uno::UNO_QUERY_THROW ); - ScVbaRange* pThisRange = dynamic_cast< ScVbaRange* >( xRange.get() ); + ScVbaRange* pThisRange = getImplementation( xRange ); pThisRange->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue ); } @@ -1975,9 +2102,9 @@ ScVbaRange::Select() throw (uno::RuntimeException) uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY_THROW ); uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW ); if ( mxRanges.is() ) - xSelection->select( uno::makeAny( mxRanges ) ); + xSelection->select( uno::Any( lclExpandToMerged( mxRanges, true ) ) ); else - xSelection->select( uno::makeAny( mxRange ) ); + xSelection->select( uno::Any( lclExpandToMerged( mxRange, true ) ) ); // set focus on document e.g. // ThisComponent.CurrentController.Frame.getContainerWindow.SetFocus try @@ -1990,7 +2117,6 @@ ScVbaRange::Select() throw (uno::RuntimeException) catch( uno::Exception& ) { } - } } @@ -2168,36 +2294,65 @@ ScVbaRange::Columns(const uno::Any& aIndex ) throw (uno::RuntimeException) void ScVbaRange::setMergeCells( const uno::Any& aIsMerged ) throw (script::BasicErrorException, uno::RuntimeException) { - sal_Bool bIsMerged = sal_False; - aIsMerged >>= bIsMerged; - uno::Reference< util::XMergeable > xMerge( mxRange, ::uno::UNO_QUERY_THROW ); - //FIXME need to check whether all the cell contents are retained or lost by popping up a dialog - xMerge->merge( bIsMerged ); + bool bMerge = false; + aIsMerged >>= bMerge; + + 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(); + for( ::std::vector< table::CellRangeAddress >::const_iterator aIt = aList.begin(), aEnd = aList.end(); aIt != aEnd; ++aIt ) + if( ScUnoConversion::Intersects( *aIt, 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() throw (script::BasicErrorException, uno::RuntimeException) { - sal_Int32 nItems = m_Areas->getCount(); - - if ( nItems > 1 ) + if( mxRanges.is() ) { - uno::Any aResult = aNULL(); - for ( sal_Int32 index=1; index != nItems; ++index ) + sal_Int32 nCount = mxRanges->getCount(); + for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex ) { - uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::makeAny(index), uno::Any() ), uno::UNO_QUERY_THROW ); - if ( index > 1 ) - if ( aResult != xRange->getMergeCells() ) - return aNULL(); - aResult = xRange->getMergeCells(); - if ( aNULL() == aResult ) + 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(); } - return aResult; + // 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(); } - uno::Reference< util::XMergeable > xMerge( mxRange, ::uno::UNO_QUERY_THROW ); - return uno::makeAny( xMerge->getIsMerged() ); } void @@ -2425,7 +2580,7 @@ ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2, bool bForceUseI Cell1 >>= sName; RangeHelper referRange( xReferrer ); table::CellRangeAddress referAddress = referRange.getCellRangeAddressable()->getRangeAddress(); - return getRangeForName( mxContext, sName, getScDocShell(), referAddress ); + return getRangeForName( getParent(), mxContext, sName, getScDocShell(), referAddress ); } else @@ -2488,8 +2643,7 @@ ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2, bool bForceUseI // Allow access to underlying openoffice uno api ( useful for debugging // with openoffice basic ) -::com::sun::star::uno::Any SAL_CALL -ScVbaRange::getCellRange( ) throw (::com::sun::star::uno::RuntimeException) +uno::Any SAL_CALL ScVbaRange::getCellRange( ) throw (uno::RuntimeException) { uno::Any aAny; if ( mxRanges.is() ) @@ -2499,6 +2653,13 @@ ScVbaRange::getCellRange( ) throw (::com::sun::star::uno::RuntimeException) return aAny; } +/*static*/ uno::Any ScVbaRange::getCellRange( const uno::Reference< excel::XRange >& rxRange ) throw (uno::RuntimeException) +{ + if( ScVbaRange* pVbaRange = getImplementation( rxRange ) ) + return pVbaRange->getCellRange(); + throw uno::RuntimeException(); +} + static USHORT getPasteFlags (sal_Int32 Paste) { @@ -2632,14 +2793,25 @@ ScVbaRange::getEntireColumn() throw (uno::RuntimeException) uno::Reference< excel::XComment > SAL_CALL ScVbaRange::AddComment( const uno::Any& Text ) throw (uno::RuntimeException) { + // if there is already a comment in the top-left cell then throw + if( getComment().is() ) + throw uno::RuntimeException(); - uno::Reference< excel::XComment > xComment( new ScVbaComment( this, mxContext, mxRange ) ); - // if you don't pass a valid text or if there is already a comment - // associated with the range then return NULL - if ( !xComment->Text( Text, uno::Any(), uno::Any() ).getLength() - || xComment->Text( uno::Any(), uno::Any(), uno::Any() ).getLength() ) - return NULL; - return xComment; + // workaround: Excel allows to create empty comment, Calc does not + ::rtl::OUString aNoteText; + if( Text.hasValue() && !(Text >>= aNoteText) ) + throw uno::RuntimeException(); + if( aNoteText.getLength() == 0 ) + aNoteText = ::rtl::OUString( sal_Unicode( ' ' ) ); + + // 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 @@ -2647,7 +2819,7 @@ ScVbaRange::getComment() throw (uno::RuntimeException) { // intentional behavior to return a null object if no // comment defined - uno::Reference< excel::XComment > xComment( new ScVbaComment( this, mxContext, mxRange ) ); + uno::Reference< excel::XComment > xComment( new ScVbaComment( this, mxContext, getUnoModel(), mxRange ) ); if ( !xComment->Text( uno::Any(), uno::Any(), uno::Any() ).getLength() ) return NULL; return xComment; @@ -2976,7 +3148,7 @@ uno::Reference< table::XCellRange > processKey( const uno::Any& Key, uno::Refere table::CellRangeAddress aRefAddr; if ( !pDocSh ) throw uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Range::Sort no docshell to calculate key param")), uno::Reference< uno::XInterface >() ); - xKeyRange = getRangeForName( xContext, sRangeName, pDocSh, aRefAddr ); + xKeyRange = getRangeForName( uno::Reference< XHelperInterface >(), xContext, sRangeName, pDocSh, aRefAddr ); } else throw uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Range::Sort illegal type value for key param")), uno::Reference< uno::XInterface >() ); @@ -3734,16 +3906,16 @@ ScVbaRange::getPageBreak() throw (uno::RuntimeException) { ScDocument* pDoc = getDocumentFromRange( mxRange ); - BYTE nFlag = 0; + ScBreakType nBreak = BREAK_NONE; if ( !bColumn ) - nFlag = pDoc -> GetRowFlags(thisAddress.StartRow, thisAddress.Sheet); + nBreak = pDoc->HasRowBreak(thisAddress.StartRow, thisAddress.Sheet); else - nFlag = pDoc -> GetColFlags(static_cast<SCCOL>(thisAddress.StartColumn), thisAddress.Sheet); + nBreak = pDoc->HasColBreak(thisAddress.StartColumn, thisAddress.Sheet); - if ( nFlag & CR_PAGEBREAK) + if (nBreak & BREAK_PAGE) nPageBreak = excel::XlPageBreak::xlPageBreakAutomatic; - if ( nFlag & CR_MANUALBREAK) + if (nBreak & BREAK_MANUAL) nPageBreak = excel::XlPageBreak::xlPageBreakManual; } } @@ -4147,7 +4319,7 @@ ScVbaRange::AutoFilter( const uno::Any& Field, const uno::Any& Criteria1, const uno::Reference< excel::XRange > xCurrent( CurrentRegion() ); if ( xCurrent.is() ) { - ScVbaRange* pRange = dynamic_cast< ScVbaRange* >( xCurrent.get() ); + ScVbaRange* pRange = getImplementation( xCurrent ); if ( pRange->isSingleCellRange() ) throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Can't create AutoFilter") ), uno::Reference< uno::XInterface >() ); if ( pRange ) @@ -4569,6 +4741,27 @@ ScVbaRange::TextToColumns( const css::uno::Any& Destination, const css::uno::Any //TODO* TrailingMinusNumbers Optional Variant. Numbers that begin with a minus character. } +uno::Any SAL_CALL +ScVbaRange::Hyperlinks( const uno::Any& aIndex ) throw (uno::RuntimeException) +{ + /* The range object always returns a new Hyperlinks object containing a + fixed list of existing hyperlinks in the range. + See vbahyperlinks.hxx for more details. */ + + // get the global hyperlink object of the sheet (sheet should always be the parent of a Range object) + uno::Reference< excel::XWorksheet > xWorksheet( getParent(), uno::UNO_QUERY_THROW ); + uno::Reference< excel::XHyperlinks > xSheetHlinks( xWorksheet->Hyperlinks( uno::Any() ), uno::UNO_QUERY_THROW ); + ScVbaHyperlinksRef xScSheetHlinks( dynamic_cast< ScVbaHyperlinks* >( xSheetHlinks.get() ) ); + if( !xScSheetHlinks.is() ) + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Cannot obtain hyperlinks implementation object" ) ), uno::Reference< uno::XInterface >() ); + + // create a new local hyperlinks object based on the sheet hyperlinks + ScVbaHyperlinksRef xHlinks( new ScVbaHyperlinks( getParent(), mxContext, xScSheetHlinks, getScRangeList() ) ); + if( aIndex.hasValue() ) + return xHlinks->Item( aIndex, uno::Any() ); + return uno::Any( uno::Reference< excel::XHyperlinks >( xHlinks.get() ) ); +} + css::uno::Reference< excel::XValidation > SAL_CALL ScVbaRange::getValidation() throw (css::uno::RuntimeException) { @@ -4577,6 +4770,97 @@ ScVbaRange::getValidation() throw (css::uno::RuntimeException) return m_xValidation; } +namespace { + +sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCell >& rxCell ) throw (uno::RuntimeException) +{ + /* TODO/FIXME: We need an apostroph-prefix property at the cell to + implement this correctly. For now, return an apostroph for every text + cell. + + TODO/FIXME: When Application.TransitionNavigKeys is supported and true, + this function needs to inspect the cell formatting and return different + prefixes according to the horizontal cell alignment. + */ + return (rxCell->getType() == table::CellContentType_TEXT) ? '\'' : 0; +} + +sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCellRange >& rxRange ) throw (uno::RuntimeException) +{ + /* This implementation is able to handle different prefixes (needed if + Application.TransitionNavigKeys is true). The function lclGetPrefixChar + for single cells called from here may return any prefix. If that + function returns an empty prefix (NUL character) or different non-empty + prefixes for two cells, this function returns 0. + */ + sal_Unicode cCurrPrefix = 0; + table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxRange ); + sal_Int32 nEndCol = aRangeAddr.EndColumn - aRangeAddr.StartColumn; + sal_Int32 nEndRow = aRangeAddr.EndRow - aRangeAddr.StartRow; + for( sal_Int32 nRow = 0; nRow <= nEndRow; ++nRow ) + { + for( sal_Int32 nCol = 0; nCol <= nEndCol; ++nCol ) + { + uno::Reference< table::XCell > xCell( rxRange->getCellByPosition( nCol, nRow ), uno::UNO_SET_THROW ); + sal_Unicode cNewPrefix = lclGetPrefixChar( xCell ); + if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) ) + return 0; + cCurrPrefix = cNewPrefix; + } + } + // all cells contain the same prefix - return it + return cCurrPrefix; +} + +sal_Unicode lclGetPrefixChar( const uno::Reference< sheet::XSheetCellRangeContainer >& rxRanges ) throw (uno::RuntimeException) +{ + sal_Unicode cCurrPrefix = 0; + uno::Reference< container::XEnumerationAccess > xRangesEA( rxRanges, uno::UNO_QUERY_THROW ); + uno::Reference< container::XEnumeration > xRangesEnum( xRangesEA->createEnumeration(), uno::UNO_SET_THROW ); + while( xRangesEnum->hasMoreElements() ) + { + uno::Reference< table::XCellRange > xRange( xRangesEnum->nextElement(), uno::UNO_QUERY_THROW ); + sal_Unicode cNewPrefix = lclGetPrefixChar( xRange ); + if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) ) + return 0; + cCurrPrefix = cNewPrefix; + } + // all ranges contain the same prefix - return it + return cCurrPrefix; +} + +inline uno::Any lclGetPrefixVariant( sal_Unicode cPrefixChar ) +{ + return uno::Any( (cPrefixChar == 0) ? ::rtl::OUString() : ::rtl::OUString( cPrefixChar ) ); +} + +} // namespace + +uno::Any SAL_CALL ScVbaRange::getPrefixCharacter() throw (uno::RuntimeException) +{ + /* (1) If Application.TransitionNavigKeys is false, this function returns + an apostroph character if the text cell begins with an apostroph + character (formula return values are not taken into account); otherwise + an empty string. + + (2) If Application.TransitionNavigKeys is true, this function returns + an apostroph character, if the cell is left-aligned; a double-quote + character, if the cell is right-aligned; a circumflex character, if the + cell is centered; a backslash character, if the cell is set to filled; + or an empty string, if nothing of the above. + + If a range or a list of ranges contains texts with leading apostroph + character as well as other cells, this function returns an empty + string. + */ + + if( mxRange.is() ) + return lclGetPrefixVariant( lclGetPrefixChar( mxRange ) ); + if( mxRanges.is() ) + return lclGetPrefixVariant( lclGetPrefixChar( mxRanges ) ); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected empty Range object" ) ), uno::Reference< uno::XInterface >() ); +} + uno::Any ScVbaRange::getShowDetail() throw ( css::uno::RuntimeException) { // #FIXME, If the specified range is in a PivotTable report @@ -4701,7 +4985,7 @@ ScVbaRange::PrintOut( const uno::Any& From, const uno::Any& To, const uno::Any& table::CellRangeAddress rangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress(); if ( index == 1 ) { - ScVbaRange* pRange = dynamic_cast< ScVbaRange* >( xRange.get() ); + ScVbaRange* pRange = getImplementation( xRange ); // initialise the doc shell and the printareas pShell = getDocShellFromRange( pRange->mxRange ); xPrintAreas.set( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW ); @@ -4723,7 +5007,7 @@ void SAL_CALL ScVbaRange::AutoFill( const uno::Reference< excel::XRange >& Destination, const uno::Any& Type ) throw (uno::RuntimeException) { uno::Reference< excel::XRange > xDest( Destination, uno::UNO_QUERY_THROW ); - ScVbaRange* pRange = dynamic_cast< ScVbaRange* >( xDest.get() ); + ScVbaRange* pRange = getImplementation( xDest ); RangeHelper destRangeHelper( pRange->mxRange ); table::CellRangeAddress destAddress = destRangeHelper.getCellRangeAddressable()->getRangeAddress(); @@ -5133,7 +5417,7 @@ ScVbaRange::SpecialCells( const uno::Any& _oType, const uno::Any& _oValue) throw { uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::makeAny(index), uno::Any() ), uno::UNO_QUERY_THROW ); xRange = xRange->SpecialCells( _oType, _oValue); - ScVbaRange* pRange = dynamic_cast< ScVbaRange* >( xRange.get() ); + ScVbaRange* pRange = getImplementation( xRange ); if ( xRange.is() && pRange ) { sal_Int32 nElems = ( pRange->m_Areas->getCount() + 1 ); |