xref: /core/sc/source/ui/vba/vbarange.cxx (revision 6376fe01859a14a22b2601ff04691ceb894c33d4)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "vbarange.hxx"
21 
22 #include <comphelper/types.hxx>
23 #include <cppuhelper/exc_hlp.hxx>
24 #include <o3tl/any.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <o3tl/unit_conversion.hxx>
27 #include <rtl/math.hxx>
28 #include <comphelper/diagnose_ex.hxx>
29 #include <o3tl/string_view.hxx>
30 
31 #include <com/sun/star/script/ArrayWrapper.hpp>
32 #include <com/sun/star/script/XTypeConverter.hpp>
33 #include <com/sun/star/script/vba/VBAEventId.hpp>
34 #include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
35 #include <com/sun/star/sheet/XDatabaseRange.hpp>
36 #include <com/sun/star/sheet/XUnnamedDatabaseRanges.hpp>
37 #include <com/sun/star/sheet/XSheetOperation.hpp>
38 #include <com/sun/star/sheet/CellFlags.hpp>
39 #include <com/sun/star/table/XColumnRowRange.hpp>
40 #include <com/sun/star/sheet/XCellAddressable.hpp>
41 #include <com/sun/star/table/CellContentType.hpp>
42 #include <com/sun/star/sheet/XCellSeries.hpp>
43 #include <com/sun/star/text/XTextRange.hpp>
44 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
45 #include <com/sun/star/table/CellAddress.hpp>
46 #include <com/sun/star/table/CellRangeAddress.hpp>
47 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
48 #include <com/sun/star/sheet/XCellRangeReferrer.hpp>
49 #include <com/sun/star/sheet/XSheetCellRange.hpp>
50 #include <com/sun/star/sheet/XSpreadsheet.hpp>
51 #include <com/sun/star/sheet/XSheetCellCursor.hpp>
52 #include <com/sun/star/sheet/XArrayFormulaRange.hpp>
53 #include <com/sun/star/sheet/XNamedRange.hpp>
54 #include <com/sun/star/sheet/XNamedRanges.hpp>
55 #include <com/sun/star/sheet/XPrintAreas.hpp>
56 #include <com/sun/star/sheet/XCellRangesQuery.hpp>
57 #include <com/sun/star/beans/XPropertySet.hpp>
58 #include <com/sun/star/frame/XModel.hpp>
59 #include <com/sun/star/view/XSelectionSupplier.hpp>
60 #include <com/sun/star/table/XTableRows.hpp>
61 #include <com/sun/star/table/XTableColumns.hpp>
62 #include <com/sun/star/table/TableSortField.hpp>
63 #include <com/sun/star/util/XMergeable.hpp>
64 #include <com/sun/star/uno/XComponentContext.hpp>
65 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
66 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
67 #include <com/sun/star/util/XNumberFormats.hpp>
68 #include <com/sun/star/util/NumberFormat.hpp>
69 #include <com/sun/star/util/XNumberFormatTypes.hpp>
70 #include <com/sun/star/util/XReplaceable.hpp>
71 #include <com/sun/star/util/XSortable.hpp>
72 #include <com/sun/star/sheet/XCellRangeMovement.hpp>
73 #include <com/sun/star/sheet/FormulaResult.hpp>
74 #include <com/sun/star/sheet/FilterOperator2.hpp>
75 #include <com/sun/star/sheet/TableFilterField2.hpp>
76 #include <com/sun/star/sheet/XSheetFilterDescriptor2.hpp>
77 #include <com/sun/star/sheet/FilterConnection.hpp>
78 #include <com/sun/star/util/TriState.hpp>
79 
80 #include <com/sun/star/sheet/XSubTotalCalculatable.hpp>
81 #include <com/sun/star/sheet/XSubTotalDescriptor.hpp>
82 #include <com/sun/star/sheet/GeneralFunction.hpp>
83 
84 #include <com/sun/star/sheet/XSheetAnnotationsSupplier.hpp>
85 #include <com/sun/star/sheet/XSheetAnnotations.hpp>
86 
87 #include <ooo/vba/excel/XlPasteSpecialOperation.hpp>
88 #include <ooo/vba/excel/XlPasteType.hpp>
89 #include <ooo/vba/excel/XlFindLookIn.hpp>
90 #include <ooo/vba/excel/XlLookAt.hpp>
91 #include <ooo/vba/excel/XlSearchOrder.hpp>
92 #include <ooo/vba/excel/XlSortOrder.hpp>
93 #include <ooo/vba/excel/XlYesNoGuess.hpp>
94 #include <ooo/vba/excel/XlSortOrientation.hpp>
95 #include <ooo/vba/excel/XlSortMethod.hpp>
96 #include <ooo/vba/excel/XlDirection.hpp>
97 #include <ooo/vba/excel/XlSortDataOption.hpp>
98 #include <ooo/vba/excel/XlDeleteShiftDirection.hpp>
99 #include <ooo/vba/excel/XlInsertShiftDirection.hpp>
100 #include <ooo/vba/excel/XlReferenceStyle.hpp>
101 #include <ooo/vba/excel/XlBordersIndex.hpp>
102 #include <ooo/vba/excel/XlPageBreak.hpp>
103 #include <ooo/vba/excel/XlAutoFilterOperator.hpp>
104 #include <ooo/vba/excel/XlAutoFillType.hpp>
105 #include <ooo/vba/excel/XlCellType.hpp>
106 #include <ooo/vba/excel/XlSpecialCellsValue.hpp>
107 #include <ooo/vba/excel/XlConsolidationFunction.hpp>
108 #include <ooo/vba/excel/XlSearchDirection.hpp>
109 
110 #include <scitems.hxx>
111 #include <svl/srchitem.hxx>
112 #include <cellsuno.hxx>
113 #include <dbdata.hxx>
114 #include <docfunc.hxx>
115 #include <columnspanset.hxx>
116 #include <queryparam.hxx>
117 #include <sortparam.hxx>
118 
119 #include <sfx2/dispatch.hxx>
120 #include <sfx2/app.hxx>
121 #include <sfx2/bindings.hxx>
122 #include <sfx2/viewfrm.hxx>
123 #include <sc.hrc>
124 #include <unonames.hxx>
125 
126 #include "excelvbahelper.hxx"
127 #include "vbaapplication.hxx"
128 #include "vbafont.hxx"
129 #include "vbacomment.hxx"
130 #include "vbainterior.hxx"
131 #include "vbacharacters.hxx"
132 #include "vbaborders.hxx"
133 #include "vbaworksheet.hxx"
134 #include "vbavalidation.hxx"
135 #include "vbahyperlinks.hxx"
136 
137 #include <tabvwsh.hxx>
138 #include <rangelst.hxx>
139 #include <convuno.hxx>
140 #include <compiler.hxx>
141 #include <patattr.hxx>
142 #include <olinetab.hxx>
143 #include <transobj.hxx>
144 #include <queryentry.hxx>
145 #include <markdata.hxx>
146 #include <basic/sberrors.hxx>
147 #include <cppuhelper/implbase.hxx>
148 
149 #include <global.hxx>
150 
151 #include "vbastyle.hxx"
152 #include "vbaname.hxx"
153 #include <utility>
154 #include <vector>
155 #include <vbahelper/vbacollectionimpl.hxx>
156 
157 #include <com/sun/star/bridge/oleautomation/Date.hpp>
158 #include <tokenarray.hxx>
159 #include <tokenuno.hxx>
160 
161 #include <memory>
162 
163 using namespace ::ooo::vba;
164 using namespace ::com::sun::star;
165 
166 // difference between VBA and file format width, in character units
167 const double fExtraWidth = 182.0 / 256.0;
168 
169 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 };
170 
lcl_pointsToTwips(double nVal)171 static sal_uInt16 lcl_pointsToTwips( double nVal )
172 {
173     nVal = nVal * static_cast<double>(20);
174     short nTwips = static_cast<short>(nVal);
175     return nTwips;
176 }
lcl_TwipsToPoints(sal_uInt16 nVal)177 static double lcl_TwipsToPoints( sal_uInt16 nVal )
178 {
179     double nPoints = nVal;
180     return nPoints / 20;
181 }
182 
lcl_Round2DecPlaces(double nVal)183 static double lcl_Round2DecPlaces( double nVal )
184 {
185     nVal  = (nVal * double(100));
186     tools::Long tmp = static_cast<tools::Long>(nVal);
187     if ( ( nVal - tmp ) >= 0.5 )
188         ++tmp;
189     nVal = double(tmp)/100;
190     return nVal;
191 }
192 
lcl_makeRange(const uno::Reference<XHelperInterface> & rParent,const uno::Reference<uno::XComponentContext> & rContext,const uno::Any & rAny,bool bIsRows,bool bIsColumns)193 static uno::Any lcl_makeRange( const uno::Reference< XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Any& rAny, bool bIsRows, bool bIsColumns )
194 {
195     uno::Reference< table::XCellRange > xCellRange(rAny, uno::UNO_QUERY_THROW);
196     return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( rParent, rContext, xCellRange, bIsRows, bIsColumns ) ) );
197 }
198 
lcl_makeXRangeFromSheetCellRanges(const uno::Reference<XHelperInterface> & xParent,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<sheet::XSheetCellRanges> & xLocSheetCellRanges,ScDocShell * pDoc)199 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 )
200 {
201     rtl::Reference< ScVbaRange > xRange;
202     const uno::Sequence< table::CellRangeAddress  > sAddresses = xLocSheetCellRanges->getRangeAddresses();
203     ScRangeList aCellRanges;
204     if ( sAddresses.hasElements() )
205     {
206         for ( const auto& rAddress : sAddresses )
207         {
208             ScRange refRange;
209             ScUnoConversion::FillScRange( refRange, rAddress );
210             aCellRanges.push_back( refRange );
211         }
212         // Single range
213         if ( aCellRanges.size() == 1 )
214         {
215             uno::Reference< table::XCellRange > xTmpRange( new ScCellRangeObj( pDoc, aCellRanges.front() ) );
216             xRange = new ScVbaRange( xParent, xContext, xTmpRange );
217         }
218         else
219         {
220             uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDoc, aCellRanges ) );
221             xRange = new ScVbaRange( xParent, xContext, xRanges );
222         }
223     }
224     return xRange;
225 }
226 
getCellRangesBase()227 ScCellRangesBase* ScVbaRange::getCellRangesBase()
228 {
229     if( mxRanges.is() )
230         return dynamic_cast<ScCellRangesBase*>( mxRanges.get() );
231     if( mxRange.is() )
232         return dynamic_cast<ScCellRangesBase*>( mxRange.get() );
233     throw uno::RuntimeException(u"General Error creating range - Unknown"_ustr );
234 }
235 
getCellRangeObj()236 ScCellRangeObj* ScVbaRange::getCellRangeObj()
237 {
238     return dynamic_cast< ScCellRangeObj* >( getCellRangesBase() );
239 }
240 
getCurrentDataSet()241 SfxItemSet*  ScVbaRange::getCurrentDataSet( )
242 {
243     SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( getCellRangesBase() );
244     if ( !pDataSet )
245         throw uno::RuntimeException(u"Can't access Itemset for range"_ustr );
246     return pDataSet;
247 }
248 
fireChangeEvent()249 void ScVbaRange::fireChangeEvent()
250 {
251     if( !ScVbaApplication::getDocumentEventsEnabled() )
252         return;
253 
254     ScDocument& rDoc = getScDocument();
255     const uno::Reference< script::vba::XVBAEventProcessor >& xVBAEvents = rDoc.GetVbaEventProcessor();
256     if( xVBAEvents.is() ) try
257     {
258         uno::Sequence< uno::Any > aArgs{ uno::Any(uno::Reference< excel::XRange >( this )) };
259         xVBAEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_CHANGE, aArgs );
260     }
261     catch( uno::Exception& )
262     {
263     }
264 }
265 
266 namespace {
267 
268 class SingleRangeEnumeration : public EnumerationHelper_BASE
269 {
270     uno::Reference< table::XCellRange > m_xRange;
271     bool bHasMore;
272 public:
273     /// @throws uno::RuntimeException
SingleRangeEnumeration(uno::Reference<table::XCellRange> xRange)274     explicit SingleRangeEnumeration( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )), bHasMore( true ) { }
hasMoreElements()275     virtual sal_Bool SAL_CALL hasMoreElements(  ) override { return bHasMore; }
nextElement()276     virtual uno::Any SAL_CALL nextElement(  ) override
277     {
278         if ( !bHasMore )
279             throw container::NoSuchElementException();
280         bHasMore = false;
281         return uno::Any( m_xRange );
282     }
283 };
284 
285 // very simple class to pass to ScVbaCollectionBaseImpl containing
286 // just one item
287 
288 class SingleRangeIndexAccess : public ::cppu::WeakImplHelper< container::XIndexAccess,
289                                                                container::XEnumerationAccess >
290 {
291 private:
292     uno::Reference< table::XCellRange > m_xRange;
293 
294 public:
SingleRangeIndexAccess(uno::Reference<table::XCellRange> xRange)295     explicit SingleRangeIndexAccess( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )) {}
296     // XIndexAccess
getCount()297     virtual ::sal_Int32 SAL_CALL getCount() override { return 1; }
getByIndex(::sal_Int32 Index)298     virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
299     {
300         if ( Index != 0 )
301             throw lang::IndexOutOfBoundsException();
302         return uno::Any( m_xRange );
303     }
304     // XElementAccess
getElementType()305     virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<table::XCellRange>::get(); }
hasElements()306     virtual sal_Bool SAL_CALL hasElements() override { return true; }
307     // XEnumerationAccess
createEnumeration()308     virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override { return new SingleRangeEnumeration( m_xRange ); }
309 
310 };
311 
312 class RangesEnumerationImpl : public EnumerationHelperImpl
313 {
314     bool mbIsRows;
315     bool mbIsColumns;
316 public:
317     /// @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)318     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 ) {}
nextElement()319     virtual uno::Any SAL_CALL nextElement(  ) override
320     {
321         return lcl_makeRange( m_xParent, m_xContext, m_xEnumeration->nextElement(), mbIsRows, mbIsColumns );
322     }
323 };
324 
325 }
326 
327 class ScVbaRangeAreas : public ScVbaCollectionBaseImpl
328 {
329     bool mbIsRows;
330     bool mbIsColumns;
331 public:
ScVbaRangeAreas(const uno::Reference<XHelperInterface> & xParent,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<container::XIndexAccess> & xIndexAccess,bool bIsRows,bool bIsColumns)332     ScVbaRangeAreas( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XIndexAccess >& xIndexAccess, bool bIsRows, bool bIsColumns ) : ScVbaCollectionBaseImpl( xParent, xContext, xIndexAccess ), mbIsRows( bIsRows ), mbIsColumns( bIsColumns ) {}
333 
334     // XEnumerationAccess
335     virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override;
336 
337     // XElementAccess
getElementType()338     virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<excel::XRange>::get(); }
339 
340     virtual uno::Any createCollectionObject( const uno::Any& aSource ) override;
341 
getServiceImplName()342     virtual OUString getServiceImplName() override { return OUString(); }
343 
getServiceNames()344     virtual uno::Sequence< OUString > getServiceNames() override { return uno::Sequence< OUString >(); }
345 
346 };
347 
348 uno::Reference< container::XEnumeration > SAL_CALL
createEnumeration()349 ScVbaRangeAreas::createEnumeration()
350 {
351     uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
352     return new RangesEnumerationImpl( mxParent, mxContext, xEnumAccess->createEnumeration(), mbIsRows, mbIsColumns );
353 }
354 
355 uno::Any
createCollectionObject(const uno::Any & aSource)356 ScVbaRangeAreas::createCollectionObject( const uno::Any& aSource )
357 {
358     return lcl_makeRange( mxParent, mxContext, aSource, mbIsRows, mbIsColumns );
359 }
360 
361 // assume that xIf is in fact a ScCellRangesBase
362 /// @throws uno::RuntimeException
363 static ScDocShell*
getDocShellFromIf(const uno::Reference<uno::XInterface> & xIf)364 getDocShellFromIf( const uno::Reference< uno::XInterface >& xIf )
365 {
366     ScCellRangesBase* pUno = dynamic_cast<ScCellRangesBase*>( xIf.get() );
367     if ( !pUno )
368             throw uno::RuntimeException(u"Failed to access underlying uno range object"_ustr  );
369     return pUno->GetDocShell();
370 }
371 
372 /// @throws uno::RuntimeException
373 static ScDocShell*
getDocShellFromRange(const uno::Reference<table::XCellRange> & xRange)374 getDocShellFromRange( const uno::Reference< table::XCellRange >& xRange )
375 {
376     // need the ScCellRangesBase to get docshell
377     uno::Reference< uno::XInterface > xIf( xRange );
378     return getDocShellFromIf(xIf );
379 }
380 
381 /// @throws uno::RuntimeException
382 static ScDocShell*
getDocShellFromRanges(const uno::Reference<sheet::XSheetCellRangeContainer> & xRanges)383 getDocShellFromRanges( const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges )
384 {
385     // need the ScCellRangesBase to get docshell
386     uno::Reference< uno::XInterface > xIf( xRanges );
387     return getDocShellFromIf(xIf );
388 }
389 
390 /// @throws uno::RuntimeException
getModelFromXIf(const uno::Reference<uno::XInterface> & xIf)391 static uno::Reference< frame::XModel > getModelFromXIf( const uno::Reference< uno::XInterface >& xIf )
392 {
393     ScDocShell* pDocShell = getDocShellFromIf(xIf );
394     return pDocShell->GetModel();
395 }
396 
397 /// @throws uno::RuntimeException
getModelFromRange(const uno::Reference<table::XCellRange> & xRange)398 static uno::Reference< frame::XModel > getModelFromRange( const uno::Reference< table::XCellRange >& xRange )
399 {
400     // the XInterface for getImplementation can be any derived interface, no need for queryInterface
401     uno::Reference< uno::XInterface > xIf( xRange );
402     return getModelFromXIf( xIf );
403 }
404 
405 static ScDocument&
getDocumentFromRange(const uno::Reference<table::XCellRange> & xRange)406 getDocumentFromRange( const uno::Reference< table::XCellRange >& xRange )
407 {
408     ScDocShell* pDocShell = getDocShellFromRange( xRange );
409     if ( !pDocShell )
410             throw uno::RuntimeException(u"Failed to access underlying docshell from uno range object"_ustr );
411     ScDocument& rDoc = pDocShell->GetDocument();
412     return rDoc;
413 }
414 
415 ScDocument&
getScDocument()416 ScVbaRange::getScDocument()
417 {
418     if ( mxRanges.is() )
419     {
420         uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
421         uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
422         return getDocumentFromRange( xRange );
423     }
424     return getDocumentFromRange( mxRange );
425 }
426 
427 ScDocShell*
getScDocShell()428 ScVbaRange::getScDocShell()
429 {
430     if ( mxRanges.is() )
431     {
432         uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
433         uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
434         return getDocShellFromRange( xRange );
435     }
436     return getDocShellFromRange( mxRange );
437 }
438 
getImplementation(const uno::Reference<excel::XRange> & rxRange)439 ScVbaRange* ScVbaRange::getImplementation( const uno::Reference< excel::XRange >& rxRange )
440 {
441     // FIXME: always save to use dynamic_cast? Or better to (implement and) use XTunnel?
442     return dynamic_cast< ScVbaRange* >( rxRange.get() );
443 }
444 
getUnoModel()445 uno::Reference< frame::XModel > ScVbaRange::getUnoModel()
446 {
447     if( ScDocShell* pDocShell = getScDocShell() )
448         return pDocShell->GetModel();
449     throw uno::RuntimeException();
450 }
451 
getUnoModel(const uno::Reference<excel::XRange> & rxRange)452 uno::Reference< frame::XModel > ScVbaRange::getUnoModel( const uno::Reference< excel::XRange >& rxRange )
453 {
454     if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
455         return pScVbaRange->getUnoModel();
456     throw uno::RuntimeException();
457 }
458 
getScRangeList()459 const ScRangeList& ScVbaRange::getScRangeList()
460 {
461     if( ScCellRangesBase* pScRangesBase = getCellRangesBase() )
462         return pScRangesBase->GetRangeList();
463     throw uno::RuntimeException(u"Cannot obtain UNO range implementation object"_ustr );
464 }
465 
getScRangeList(const uno::Reference<excel::XRange> & rxRange)466 const ScRangeList& ScVbaRange::getScRangeList( const uno::Reference< excel::XRange >& rxRange )
467 {
468     if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
469         return pScVbaRange->getScRangeList();
470     throw uno::RuntimeException(u"Cannot obtain VBA range implementation object"_ustr );
471 }
472 
473 namespace {
474 
475 class NumFormatHelper
476 {
477     uno::Reference< util::XNumberFormatsSupplier > mxSupplier;
478     uno::Reference< beans::XPropertySet > mxRangeProps;
479     uno::Reference< util::XNumberFormats > mxFormats;
480 public:
NumFormatHelper(const uno::Reference<table::XCellRange> & xRange)481     explicit NumFormatHelper( const uno::Reference< table::XCellRange >& xRange )
482     {
483         mxSupplier.set( getModelFromRange( xRange ), uno::UNO_QUERY_THROW );
484         mxRangeProps.set( xRange, uno::UNO_QUERY_THROW);
485         mxFormats = mxSupplier->getNumberFormats();
486     }
getNumberProps()487     uno::Reference< beans::XPropertySet > getNumberProps()
488     {
489         tools::Long nIndexKey = 0;
490         uno::Any aValue = mxRangeProps->getPropertyValue( u"NumberFormat"_ustr );
491         aValue >>= nIndexKey;
492 
493         if ( mxFormats.is() )
494             return  mxFormats->getByKey( nIndexKey );
495         return  uno::Reference< beans::XPropertySet > ();
496     }
497 
isBooleanType()498     bool isBooleanType()
499     {
500 
501         return (getNumberFormat() & util::NumberFormat::LOGICAL) != 0;
502     }
503 
isDateType()504     bool isDateType()
505     {
506         sal_Int16 nType = getNumberFormat();
507         return ( nType & util::NumberFormat::DATETIME ) != 0;
508     }
509 
getNumberFormatString()510     OUString getNumberFormatString()
511     {
512         uno::Reference< uno::XInterface > xIf( mxRangeProps, uno::UNO_QUERY_THROW );
513         ScCellRangesBase* pUnoCellRange = dynamic_cast<ScCellRangesBase*>( xIf.get() );
514         if ( pUnoCellRange )
515         {
516 
517             SfxItemSet* pDataSet =  excel::ScVbaCellRangeAccess::GetDataSet( pUnoCellRange );
518             SfxItemState eState = pDataSet->GetItemState( ATTR_VALUE_FORMAT);
519             // one of the cells in the range is not like the other ;-)
520             // so return a zero length format to indicate that
521             if ( eState == SfxItemState::INVALID )
522                 return OUString();
523         }
524 
525         uno::Reference< beans::XPropertySet > xNumberProps( getNumberProps(), uno::UNO_SET_THROW );
526         OUString aFormatString;
527         uno::Any aString = xNumberProps->getPropertyValue( u"FormatString"_ustr );
528         aString >>= aFormatString;
529         return aFormatString;
530     }
531 
getNumberFormat()532     sal_Int16 getNumberFormat()
533     {
534         uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
535         sal_Int16 nType = ::comphelper::getINT16(
536             xNumberProps->getPropertyValue( u"Type"_ustr ) );
537         return nType;
538     }
539 
setNumberFormat(const OUString & rFormat)540     void setNumberFormat( const OUString& rFormat )
541     {
542         // #163288# treat "General" as "Standard" format
543         sal_Int32 nNewIndex = 0;
544         if( !rFormat.equalsIgnoreAsciiCase( "General" ) )
545         {
546             lang::Locale aLocale;
547             uno::Reference< beans::XPropertySet > xNumProps = getNumberProps();
548             xNumProps->getPropertyValue( u"Locale"_ustr ) >>= aLocale;
549             nNewIndex = mxFormats->queryKey( rFormat, aLocale, false );
550             if ( nNewIndex == -1 ) // format not defined
551                 nNewIndex = mxFormats->addNew( rFormat, aLocale );
552         }
553         mxRangeProps->setPropertyValue( u"NumberFormat"_ustr, uno::Any( nNewIndex ) );
554     }
555 
setNumberFormat(sal_Int16 nType)556     void setNumberFormat( sal_Int16 nType )
557     {
558         uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
559         lang::Locale aLocale;
560         xNumberProps->getPropertyValue( u"Locale"_ustr ) >>= aLocale;
561         uno::Reference<util::XNumberFormatTypes> xTypes( mxFormats, uno::UNO_QUERY );
562         if ( xTypes.is() )
563         {
564             sal_Int32 nNewIndex = xTypes->getStandardFormat( nType, aLocale );
565             mxRangeProps->setPropertyValue( u"NumberFormat"_ustr, uno::Any( nNewIndex ) );
566         }
567     }
568 
569 };
570 
571 struct CellPos
572 {
CellPos__anone662d2f60211::CellPos573     CellPos( sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nArea ):m_nRow(nRow), m_nCol(nCol), m_nArea( nArea ) {};
574 sal_Int32 m_nRow;
575 sal_Int32 m_nCol;
576 sal_Int32 m_nArea;
577 };
578 
579 }
580 
581 typedef ::cppu::WeakImplHelper< container::XEnumeration > CellsEnumeration_BASE;
582 typedef std::vector< CellPos > vCellPos;
583 
584 namespace {
585 
586 // #FIXME - QUICK
587 // we could probably could and should modify CellsEnumeration below
588 // to handle rows and columns (but I do this separately for now
589 // and... this class only handles single areas (does it have to handle
590 // multi area ranges??)
591 class ColumnsRowEnumeration: public CellsEnumeration_BASE
592 {
593     uno::Reference< excel::XRange > mxRange;
594     sal_Int32 mMaxElems;
595     sal_Int32 mCurElem;
596 
597 public:
ColumnsRowEnumeration(uno::Reference<excel::XRange> xRange,sal_Int32 nElems)598     ColumnsRowEnumeration( uno::Reference< excel::XRange > xRange, sal_Int32 nElems ) : mxRange(std::move( xRange )), mMaxElems( nElems ), mCurElem( 0 )
599     {
600     }
601 
hasMoreElements()602     virtual sal_Bool SAL_CALL hasMoreElements() override { return mCurElem < mMaxElems; }
603 
nextElement()604     virtual uno::Any SAL_CALL nextElement() override
605     {
606         if ( !hasMoreElements() )
607             throw container::NoSuchElementException();
608         sal_Int32 vbaIndex = 1 + mCurElem++;
609         return uno::Any( mxRange->Item( uno::Any( vbaIndex ), uno::Any() ) );
610     }
611 };
612 
613 class CellsEnumeration : public CellsEnumeration_BASE
614 {
615     uno::WeakReference< XHelperInterface > mxParent;
616     uno::Reference< uno::XComponentContext > mxContext;
617     uno::Reference< XCollection > m_xAreas;
618     vCellPos m_CellPositions;
619     vCellPos::const_iterator m_it;
620 
621     /// @throws uno::RuntimeException
getArea(sal_Int32 nVBAIndex)622     uno::Reference< table::XCellRange > getArea( sal_Int32 nVBAIndex )
623     {
624         if ( nVBAIndex < 1 || nVBAIndex > m_xAreas->getCount() )
625             throw uno::RuntimeException();
626         uno::Reference< excel::XRange > xRange( m_xAreas->Item( uno::Any(nVBAIndex), uno::Any() ), uno::UNO_QUERY_THROW );
627         uno::Reference< table::XCellRange > xCellRange( ScVbaRange::getCellRange( xRange ), uno::UNO_QUERY_THROW );
628         return xCellRange;
629     }
630 
populateArea(sal_Int32 nVBAIndex)631     void populateArea( sal_Int32 nVBAIndex )
632     {
633         uno::Reference< table::XCellRange > xRange = getArea( nVBAIndex );
634         uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, uno::UNO_QUERY_THROW );
635         sal_Int32 nRowCount =  xColumnRowRange->getRows()->getCount();
636         sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
637         for ( sal_Int32 i=0; i<nRowCount; ++i )
638         {
639             for ( sal_Int32 j=0; j<nColCount; ++j )
640                 m_CellPositions.emplace_back( i,j,nVBAIndex );
641         }
642     }
643 public:
CellsEnumeration(const uno::Reference<XHelperInterface> & xParent,uno::Reference<uno::XComponentContext> xContext,uno::Reference<XCollection> xAreas)644     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 ))
645     {
646         sal_Int32 nItems = m_xAreas->getCount();
647         for ( sal_Int32 index=1; index <= nItems; ++index )
648         {
649                 populateArea( index );
650         }
651         m_it = m_CellPositions.begin();
652     }
hasMoreElements()653     virtual sal_Bool SAL_CALL hasMoreElements() override { return m_it != m_CellPositions.end(); }
654 
nextElement()655     virtual uno::Any SAL_CALL nextElement() override
656     {
657         if ( !hasMoreElements() )
658             throw container::NoSuchElementException();
659         CellPos aPos = *m_it++;
660 
661         uno::Reference< table::XCellRange > xRangeArea = getArea( aPos.m_nArea );
662         uno::Reference< table::XCellRange > xCellRange( xRangeArea->getCellByPosition(  aPos.m_nCol, aPos.m_nRow ), uno::UNO_QUERY_THROW );
663         return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( mxParent, mxContext, xCellRange ) ) );
664 
665     }
666 };
667 
668 }
669 
670 constexpr OUString ISVISIBLE = u"IsVisible"_ustr;
671 const char EQUALS[] = "=";
672 const char NOTEQUALS[] = "<>";
673 const char GREATERTHAN[] = ">";
674 const char GREATERTHANEQUALS[] = ">=";
675 const char LESSTHAN[] = "<";
676 const char LESSTHANEQUALS[] = "<=";
677 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);
678 constexpr OUString CELLSTYLE = u"CellStyle"_ustr;
679 
680 namespace {
681 
682 class CellValueSetter : public ValueSetter
683 {
684 protected:
685     uno::Any maValue;
686 public:
687     explicit CellValueSetter( uno::Any  aValue );
688     virtual bool processValue( const uno::Any& aValue,  const uno::Reference< table::XCell >& xCell ) override;
689     virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
690 
691 };
692 
693 }
694 
CellValueSetter(uno::Any aValue)695 CellValueSetter::CellValueSetter( uno::Any aValue ): maValue(std::move( aValue )) {}
696 
697 void
visitNode(sal_Int32,sal_Int32,const uno::Reference<table::XCell> & xCell)698 CellValueSetter::visitNode( sal_Int32 /*i*/, sal_Int32 /*j*/, const uno::Reference< table::XCell >& xCell )
699 {
700     processValue( maValue, xCell );
701 }
702 
703 bool
processValue(const uno::Any & aValue,const uno::Reference<table::XCell> & xCell)704 CellValueSetter::processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell )
705 {
706     if (bool bState; aValue >>= bState)
707     {
708         uno::Reference<table::XCellRange> xRange(xCell, uno::UNO_QUERY_THROW);
709         xCell->setValue(bState ? 1 : 0);
710         NumFormatHelper cellNumFormat(xRange);
711         cellNumFormat.setNumberFormat(util::NumberFormat::LOGICAL);
712     }
713     else if (OUString aString; aValue >>= aString)
714     {
715         // The required behavior for a string value is:
716         // 1. If the first character is a single quote, use the rest as a string cell, regardless of the cell's number format.
717         // 2. Otherwise, if the cell's number format is "text", use the string value as a string cell.
718         // 3. Otherwise, parse the string value in English locale, and apply a corresponding number format with the cell's locale
719         //    if the cell's number format was "General".
720         // Case 1 is handled here, the rest in ScCellObj::InputEnglishString
721 
722         if (aString.toChar() == '\'') // case 1 - handle with XTextRange
723         {
724             OUString aRemainder(aString.copy(1)); // strip the quote
725             uno::Reference<text::XTextRange> xTextRange(xCell, uno::UNO_QUERY_THROW);
726             xTextRange->setString(aRemainder);
727         }
728         else
729         {
730             // call implementation method InputEnglishString
731             ScCellObj* pCellObj = dynamic_cast<ScCellObj*>(xCell.get());
732             if (pCellObj)
733                 pCellObj->InputEnglishString(aString);
734         }
735     }
736     else if (double nDouble; aValue >>= nDouble)
737     {
738         uno::Reference<table::XCellRange> xRange(xCell, uno::UNO_QUERY_THROW);
739         NumFormatHelper cellFormat(xRange);
740         // If we are setting a number and the cell types was logical
741         // then we need to reset the logical format. ( see bool handling above )
742         if (cellFormat.isBooleanType())
743             cellFormat.setNumberFormat(u"General"_ustr);
744         xCell->setValue(nDouble);
745     }
746     else if (css::bridge::oleautomation::Date aDate; aValue >>= aDate)
747     {
748         uno::Reference<table::XCellRange> xRange(xCell, uno::UNO_QUERY_THROW);
749         NumFormatHelper cellFormat(xRange);
750         sal_Int16 format;
751         double wholePart = floor(aDate.Value);
752         if (wholePart == 0)
753             format = util::NumberFormat::TIME;
754         else if (wholePart == aDate.Value) // no fractional part
755             format = util::NumberFormat::DATE;
756         else
757             format = util::NumberFormat::DATETIME;
758         cellFormat.setNumberFormat(format);
759         xCell->setValue(aDate.Value);
760     }
761     else
762         return false;
763 
764     return true;
765 }
766 
767 namespace {
768 
769 class CellValueGetter : public ValueGetter
770 {
771 protected:
772     RangeValueType meValueType;
773     uno::Any maValue;
774 public:
CellValueGetter(RangeValueType eValueType)775     CellValueGetter(RangeValueType eValueType) { meValueType = eValueType; }
776     virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
777     virtual void processValue( const uno::Any& aValue ) override;
getValue() const778     const uno::Any& getValue() const override { return maValue; }
779 };
780 
781 }
782 
783 void
processValue(const uno::Any & aValue)784 CellValueGetter::processValue(  const uno::Any& aValue )
785 {
786     maValue = aValue;
787 }
visitNode(sal_Int32,sal_Int32,const uno::Reference<table::XCell> & xCell)788 void CellValueGetter::visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell )
789 {
790     uno::Any aValue;
791     table::CellContentType eCellContentType = xCell->getType();
792     if( eCellContentType == table::CellContentType_VALUE || eCellContentType == table::CellContentType_FORMULA )
793     {
794         if ( eCellContentType == table::CellContentType_FORMULA )
795         {
796 
797             OUString sFormula = xCell->getFormula();
798             if ( sFormula == "=TRUE()" )
799                 aValue <<= true;
800             else if ( sFormula == "=FALSE()" )
801                 aValue <<= false;
802             else
803             {
804                 uno::Reference< beans::XPropertySet > xProp( xCell, uno::UNO_QUERY_THROW );
805 
806                 sal_Int32 nResultType = sheet::FormulaResult::VALUE;
807                 // some formulas give textual results
808                 xProp->getPropertyValue( u"FormulaResultType2"_ustr ) >>= nResultType;
809 
810                 if ( nResultType == sheet::FormulaResult::STRING )
811                 {
812                     uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
813                     aValue <<= xTextRange->getString();
814                 }
815                 else
816                     aValue <<= xCell->getValue();
817             }
818         }
819         else
820         {
821             uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
822             NumFormatHelper cellFormat( xRange );
823             if ( cellFormat.isBooleanType() )
824                 aValue <<= ( xCell->getValue() != 0.0 );
825             else if ( cellFormat.isDateType() && meValueType == RangeValueType::value)
826                 aValue <<= bridge::oleautomation::Date( xCell->getValue() );
827             else
828                 aValue <<= xCell->getValue();
829         }
830     }
831     if( eCellContentType == table::CellContentType_TEXT )
832     {
833         uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
834         aValue <<= xTextRange->getString();
835     }
836     processValue( aValue );
837 }
838 
839 namespace {
840 
841 class CellFormulaValueSetter : public CellValueSetter
842 {
843 private:
844     ScDocument& m_rDoc;
845     formula::FormulaGrammar::Grammar m_eGrammar;
846 public:
CellFormulaValueSetter(const uno::Any & aValue,ScDocument & rDoc,formula::FormulaGrammar::Grammar eGram)847     CellFormulaValueSetter( const uno::Any& aValue, ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ):CellValueSetter( aValue ),  m_rDoc( rDoc ), m_eGrammar( eGram ){}
848 protected:
processValue(const uno::Any & aValue,const uno::Reference<table::XCell> & xCell)849     bool processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell ) override
850     {
851         OUString sFormula;
852         double aDblValue = 0.0;
853         if ( aValue >>= sFormula )
854         {
855             // convert to GRAM_API style grammar because XCell::setFormula
856             // always compile it in that grammar. Perhaps
857             // css.sheet.FormulaParser should be used in future to directly
858             // pass formula tokens when that API stabilizes.
859             if ( m_eGrammar != formula::FormulaGrammar::GRAM_API && ( o3tl::starts_with(o3tl::trim(sFormula), u"=") ) )
860             {
861                 uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
862                 ScCellRangesBase* pUnoRangesBase
863                     = dynamic_cast< ScCellRangesBase* >( xIf.get() );
864                 if ( pUnoRangesBase )
865                 {
866                     const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
867                     if (!rCellRanges.empty())
868                     {
869                         ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, m_eGrammar );
870                         // compile the string in the format passed in
871                         std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sFormula));
872                         // convert to API grammar
873                         aCompiler.SetGrammar( formula::FormulaGrammar::GRAM_API );
874                         OUString sConverted;
875                         aCompiler.CreateStringFromTokenArray(sConverted);
876                         sFormula = EQUALS + sConverted;
877                     }
878                 }
879             }
880 
881             xCell->setFormula( sFormula );
882             return true;
883         }
884         else if ( aValue >>= aDblValue )
885         {
886             xCell->setValue( aDblValue );
887             return true;
888         }
889         return false;
890     }
891 
892 };
893 
894 class CellFormulaValueGetter : public CellValueGetter
895 {
896 private:
897     ScDocument& m_rDoc;
898     formula::FormulaGrammar::Grammar m_eGrammar;
899 public:
CellFormulaValueGetter(ScDocument & rDoc,formula::FormulaGrammar::Grammar eGram)900     CellFormulaValueGetter(ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ) :
901          CellValueGetter( RangeValueType::value ), m_rDoc( rDoc ), m_eGrammar( eGram ) {}
visitNode(sal_Int32,sal_Int32,const uno::Reference<table::XCell> & xCell)902     virtual void visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell ) override
903     {
904         uno::Any aValue;
905         aValue <<= xCell->getFormula();
906         // XCell::getFormula() returns the formula in API grammar, convert.
907         if ((xCell->getType() == table::CellContentType_FORMULA)
908                 && m_eGrammar != formula::FormulaGrammar::GRAM_API)
909         {
910             uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
911             ScCellRangesBase* pUnoRangesBase
912                 = dynamic_cast< ScCellRangesBase* >( xIf.get() );
913             if (pUnoRangesBase)
914             {
915                 OUString sVal;
916                 aValue >>= sVal;
917                 const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
918                 if (!rCellRanges.empty())
919                 {
920                     // Compile string from API grammar.
921                     ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, formula::FormulaGrammar::GRAM_API );
922                     std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sVal));
923                     // Convert to desired grammar.
924                     aCompiler.SetGrammar( m_eGrammar );
925                     OUString sConverted;
926                     aCompiler.CreateStringFromTokenArray(sConverted);
927                     sVal = EQUALS + sConverted;
928                     aValue <<= sVal;
929                 }
930             }
931         }
932 
933         processValue( aValue );
934     }
935 
936 };
937 
938 class Dim2ArrayValueGetter : public ArrayVisitor
939 {
940 protected:
941     uno::Any maValue;
942     ValueGetter& mValueGetter;
processValue(sal_Int32 x,sal_Int32 y,const uno::Any & aValue)943     void processValue( sal_Int32 x, sal_Int32 y, const uno::Any& aValue )
944     {
945         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));
946         aMatrix.getArray()[x].getArray()[y] = aValue;
947     }
948 
949 public:
Dim2ArrayValueGetter(sal_Int32 nRowCount,sal_Int32 nColCount,ValueGetter & rValueGetter)950     Dim2ArrayValueGetter(sal_Int32 nRowCount, sal_Int32 nColCount, ValueGetter& rValueGetter ): mValueGetter(rValueGetter)
951     {
952         uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
953         aMatrix.realloc( nRowCount );
954         auto pMatrix = aMatrix.getArray();
955         for ( sal_Int32 index = 0; index < nRowCount; ++index )
956             pMatrix[index].realloc( nColCount );
957         maValue <<= aMatrix;
958     }
visitNode(sal_Int32 x,sal_Int32 y,const uno::Reference<table::XCell> & xCell)959     void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
960 
961     {
962         mValueGetter.visitNode( x, y, xCell );
963         processValue( x, y, mValueGetter.getValue() );
964     }
getValue() const965     const uno::Any& getValue() const { return maValue; }
966 
967 };
968 
969 }
970 
971 constexpr OUString sNA = u"#N/A"_ustr;
972 
973 namespace {
974 
975 class Dim1ArrayValueSetter : public ArrayVisitor
976 {
977     uno::Sequence< uno::Any > aMatrix;
978     sal_Int32 nColCount;
979     ValueSetter& mCellValueSetter;
980 public:
Dim1ArrayValueSetter(const uno::Any & aValue,ValueSetter & rCellValueSetter)981     Dim1ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ):mCellValueSetter( rCellValueSetter )
982     {
983         aValue >>= aMatrix;
984         nColCount = aMatrix.getLength();
985     }
visitNode(sal_Int32,sal_Int32 y,const uno::Reference<table::XCell> & xCell)986     virtual void visitNode( sal_Int32 /*x*/, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
987     {
988         if ( y < nColCount )
989             mCellValueSetter.processValue( aMatrix[ y ], xCell );
990         else
991             mCellValueSetter.processValue( uno::Any( sNA ), xCell );
992     }
993 };
994 
995 class Dim2ArrayValueSetter : public ArrayVisitor
996 {
997     uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
998     ValueSetter& mCellValueSetter;
999     sal_Int32 nRowCount;
1000     sal_Int32 nColCount;
1001 public:
Dim2ArrayValueSetter(const uno::Any & aValue,ValueSetter & rCellValueSetter)1002     Dim2ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ) : mCellValueSetter( rCellValueSetter )
1003     {
1004         aValue >>= aMatrix;
1005         nRowCount = aMatrix.getLength();
1006         nColCount = aMatrix[0].getLength();
1007     }
1008 
visitNode(sal_Int32 x,sal_Int32 y,const uno::Reference<table::XCell> & xCell)1009     virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
1010     {
1011         if ( x < nRowCount && y < nColCount )
1012             mCellValueSetter.processValue( aMatrix[ x ][ y ], xCell );
1013         else
1014             mCellValueSetter.processValue( uno::Any( sNA ), xCell );
1015 
1016     }
1017 };
1018 
1019 class RangeProcessor
1020 {
1021 public:
1022     virtual void process( const uno::Reference< excel::XRange >& xRange ) = 0;
1023 
1024 protected:
~RangeProcessor()1025     ~RangeProcessor() {}
1026 };
1027 
1028 class RangeValueProcessor : public RangeProcessor
1029 {
1030     const uno::Any& m_aVal;
1031 public:
RangeValueProcessor(const uno::Any & rVal)1032     explicit RangeValueProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
~RangeValueProcessor()1033     virtual ~RangeValueProcessor() {}
process(const uno::Reference<excel::XRange> & xRange)1034     virtual void process( const uno::Reference< excel::XRange >& xRange ) override
1035     {
1036         xRange->setValue( m_aVal );
1037     }
1038 };
1039 
1040 class RangeFormulaProcessor : public RangeProcessor
1041 {
1042     const uno::Any& m_aVal;
1043 public:
RangeFormulaProcessor(const uno::Any & rVal)1044     explicit RangeFormulaProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
~RangeFormulaProcessor()1045     virtual ~RangeFormulaProcessor() {}
process(const uno::Reference<excel::XRange> & xRange)1046     virtual void process( const uno::Reference< excel::XRange >& xRange ) override
1047     {
1048         xRange->setFormula( m_aVal );
1049     }
1050 };
1051 
1052 class RangeCountProcessor : public RangeProcessor
1053 {
1054     sal_Int32 nCount;
1055 public:
RangeCountProcessor()1056     RangeCountProcessor():nCount(0){}
~RangeCountProcessor()1057     virtual ~RangeCountProcessor() {}
process(const uno::Reference<excel::XRange> & xRange)1058     virtual void process( const uno::Reference< excel::XRange >& xRange ) override
1059     {
1060         nCount = nCount + xRange->getCount();
1061     }
value()1062     sal_Int32 value() { return nCount; }
1063 };
1064 class AreasVisitor
1065 {
1066 private:
1067     uno::Reference< XCollection > m_Areas;
1068 public:
AreasVisitor(uno::Reference<XCollection> xAreas)1069     explicit AreasVisitor( uno::Reference< XCollection > xAreas ):m_Areas(std::move( xAreas )){}
1070 
visit(RangeProcessor & processor)1071     void visit( RangeProcessor& processor )
1072     {
1073         if ( m_Areas.is() )
1074         {
1075             sal_Int32 nItems = m_Areas->getCount();
1076             for ( sal_Int32 index=1; index <= nItems; ++index )
1077             {
1078                 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
1079                 processor.process( xRange );
1080             }
1081         }
1082     }
1083 };
1084 
1085 class RangeHelper
1086 {
1087     uno::Reference< table::XCellRange > m_xCellRange;
1088 
1089 public:
1090     /// @throws uno::RuntimeException
RangeHelper(uno::Reference<table::XCellRange> xCellRange)1091     explicit RangeHelper( uno::Reference< table::XCellRange > xCellRange ) : m_xCellRange(std::move( xCellRange ))
1092     {
1093         if ( !m_xCellRange.is() )
1094             throw uno::RuntimeException();
1095     }
1096     /// @throws uno::RuntimeException
RangeHelper(const uno::Any & rCellRange)1097     explicit RangeHelper( const uno::Any& rCellRange )
1098     {
1099         m_xCellRange.set(rCellRange, uno::UNO_QUERY_THROW);
1100     }
1101     /// @throws uno::RuntimeException
getSheetCellRange() const1102     uno::Reference< sheet::XSheetCellRange > getSheetCellRange() const
1103     {
1104         return uno::Reference< sheet::XSheetCellRange >(m_xCellRange, uno::UNO_QUERY_THROW);
1105     }
1106     /// @throws uno::RuntimeException
getSpreadSheet() const1107     uno::Reference< sheet::XSpreadsheet >  getSpreadSheet() const
1108     {
1109         return getSheetCellRange()->getSpreadsheet();
1110     }
1111 
1112     /// @throws uno::RuntimeException
getCellRangeFromSheet() const1113     uno::Reference< table::XCellRange > getCellRangeFromSheet() const
1114     {
1115         return uno::Reference< table::XCellRange >(getSpreadSheet(), uno::UNO_QUERY_THROW );
1116     }
1117 
1118     /// @throws uno::RuntimeException
getCellRangeAddressable() const1119     uno::Reference< sheet::XCellRangeAddressable >  getCellRangeAddressable() const
1120     {
1121         return uno::Reference< sheet::XCellRangeAddressable >(m_xCellRange, ::uno::UNO_QUERY_THROW);
1122 
1123     }
1124 
1125     /// @throws uno::RuntimeException
getSheetCellCursor() const1126     uno::Reference< sheet::XSheetCellCursor > getSheetCellCursor() const
1127     {
1128         return  uno::Reference< sheet::XSheetCellCursor >( getSpreadSheet()->createCursorByRange( getSheetCellRange() ), uno::UNO_SET_THROW );
1129     }
1130 
createRangeFromRange(const uno::Reference<XHelperInterface> & xParent,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<table::XCellRange> & xRange,const uno::Reference<sheet::XCellRangeAddressable> & xCellRangeAddressable)1131     static uno::Reference< excel::XRange > createRangeFromRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference<uno::XComponentContext >& xContext,
1132         const uno::Reference< table::XCellRange >& xRange, const uno::Reference< sheet::XCellRangeAddressable >& xCellRangeAddressable )
1133     {
1134         const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
1135         return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext,
1136                     xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow)));
1137     }
1138 
1139 };
1140 
1141 }
1142 
1143 bool
getCellRangesForAddress(ScRefFlags & rResFlags,std::u16string_view sAddress,ScDocShell * pDocSh,ScRangeList & rCellRanges,formula::FormulaGrammar::AddressConvention eConv,char cDelimiter)1144 ScVbaRange::getCellRangesForAddress( ScRefFlags& rResFlags, std::u16string_view sAddress, ScDocShell* pDocSh, ScRangeList& rCellRanges, formula::FormulaGrammar::AddressConvention eConv, char cDelimiter )
1145 {
1146 
1147     if ( pDocSh )
1148     {
1149         ScDocument& rDoc = pDocSh->GetDocument();
1150         rResFlags = rCellRanges.Parse( sAddress, rDoc, eConv, 0, cDelimiter );
1151         if ( rResFlags & ScRefFlags::VALID )
1152         {
1153             return true;
1154         }
1155     }
1156     return false;
1157 }
1158 
getScRangeListForAddress(const OUString & sName,ScDocShell * pDocSh,const ScRange & refRange,ScRangeList & aCellRanges,formula::FormulaGrammar::AddressConvention aConv)1159 bool getScRangeListForAddress( const OUString& sName, ScDocShell* pDocSh, const ScRange& refRange, ScRangeList& aCellRanges, formula::FormulaGrammar::AddressConvention aConv )
1160 {
1161     // see if there is a match with a named range
1162     uno::Reference< container::XNameAccess > xNameAccess( pDocSh->GetModel()->getPropertyValue( u"NamedRanges"_ustr ), uno::UNO_QUERY_THROW );
1163     // Strange enough you can have Range( "namedRange1, namedRange2, etc," )
1164     // loop around each ',' separated name
1165     std::vector< OUString > vNames;
1166     sal_Int32 nIndex = 0;
1167     do
1168     {
1169         OUString aToken = sName.getToken( 0, ',', nIndex );
1170         vNames.push_back( aToken );
1171     } while ( nIndex >= 0 );
1172 
1173     if ( vNames.empty() )
1174         vNames.push_back( sName );
1175 
1176     for ( const auto& rName : vNames )
1177     {
1178         formula::FormulaGrammar::AddressConvention eConv = aConv;
1179         // spaces are illegal ( but the user of course can enter them )
1180         OUString sAddress = rName.trim();
1181         // if a local name ( on the active sheet ) exists this will
1182         // take precedence over a global with the same name
1183         if ( !xNameAccess->hasByName( sAddress ) )
1184         {
1185             // try a local name
1186             ScDocument& rDoc = pDocSh->GetDocument();
1187             SCTAB nCurTab = ScDocShell::GetCurTab();
1188             ScRangeName* pRangeName = rDoc.GetRangeName(nCurTab);
1189             if (pRangeName)
1190             {
1191                 // TODO: Handle local names correctly:
1192                 // bool bLocalName = pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sAddress)) != nullptr;
1193             }
1194         }
1195         char aChar = 0;
1196         if ( xNameAccess->hasByName( sAddress ) )
1197         {
1198             uno::Reference< sheet::XNamedRange > xNamed( xNameAccess->getByName( sAddress ), uno::UNO_QUERY_THROW );
1199             sAddress = xNamed->getContent();
1200             // As the address comes from OOO, the addressing
1201             // style is may not be XL_A1
1202             eConv = pDocSh->GetDocument().GetAddressConvention();
1203             aChar = ';';
1204         }
1205 
1206         ScRefFlags nFlags = ScRefFlags::ZERO;
1207         if ( !ScVbaRange::getCellRangesForAddress( nFlags, sAddress, pDocSh, aCellRanges, eConv, aChar ) )
1208             return false;
1209 
1210         bool bTabFromReferrer = !( nFlags & ScRefFlags::TAB_3D );
1211 
1212         for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
1213         {
1214             ScRange & rRange = aCellRanges[ i ];
1215             rRange.aStart.SetCol( refRange.aStart.Col() + rRange.aStart.Col() );
1216             rRange.aStart.SetRow( refRange.aStart.Row() + rRange.aStart.Row() );
1217             rRange.aStart.SetTab( bTabFromReferrer ? refRange.aStart.Tab()  : rRange.aStart.Tab() );
1218             rRange.aEnd.SetCol( refRange.aStart.Col() + rRange.aEnd.Col() );
1219             rRange.aEnd.SetRow( refRange.aStart.Row() + rRange.aEnd.Row() );
1220             rRange.aEnd.SetTab( bTabFromReferrer ? refRange.aEnd.Tab()  : rRange.aEnd.Tab() );
1221         }
1222     }
1223     return true;
1224 }
1225 
1226 /// @throws uno::RuntimeException
1227 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)1228 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 )
1229 {
1230     ScRangeList aCellRanges;
1231     ScRange refRange;
1232     ScUnoConversion::FillScRange( refRange, pAddr );
1233     if ( !getScRangeListForAddress ( sName, pDocSh, refRange, aCellRanges, eConv ) )
1234         throw uno::RuntimeException();
1235     // Single range
1236     if ( aCellRanges.size() == 1 )
1237     {
1238         uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pDocSh, aCellRanges.front() ) );
1239         uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRange );
1240         return new ScVbaRange( xFixThisParent, xContext, xRange );
1241     }
1242     uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDocSh, aCellRanges ) );
1243 
1244     uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRanges );
1245     return new ScVbaRange( xFixThisParent, xContext, xRanges );
1246 }
1247 
1248 namespace {
1249 
1250 /// @throws uno::RuntimeException
1251 template< typename RangeType >
lclGetRangeAddress(const uno::Reference<RangeType> & rxCellRange)1252 table::CellRangeAddress lclGetRangeAddress( const uno::Reference< RangeType >& rxCellRange )
1253 {
1254     return uno::Reference< sheet::XCellRangeAddressable >( rxCellRange, uno::UNO_QUERY_THROW )->getRangeAddress();
1255 }
1256 
1257 /// @throws uno::RuntimeException
lclClearRange(const uno::Reference<table::XCellRange> & rxCellRange)1258 void lclClearRange( const uno::Reference< table::XCellRange >& rxCellRange )
1259 {
1260     using namespace ::com::sun::star::sheet::CellFlags;
1261     sal_Int32 const nFlags = VALUE | DATETIME | STRING | ANNOTATION | FORMULA | HARDATTR | STYLES | EDITATTR | FORMATTED;
1262     uno::Reference< sheet::XSheetOperation > xSheetOperation( rxCellRange, uno::UNO_QUERY_THROW );
1263     xSheetOperation->clearContents( nFlags );
1264 }
1265 
1266 /// @throws uno::RuntimeException
lclExpandToMerged(const uno::Reference<table::XCellRange> & rxCellRange,bool bRecursive)1267 uno::Reference< sheet::XSheetCellRange > lclExpandToMerged( const uno::Reference< table::XCellRange >& rxCellRange, bool bRecursive )
1268 {
1269     uno::Reference< sheet::XSheetCellRange > xNewCellRange( rxCellRange, uno::UNO_QUERY_THROW );
1270     uno::Reference< sheet::XSpreadsheet > xSheet( xNewCellRange->getSpreadsheet(), uno::UNO_SET_THROW );
1271     table::CellRangeAddress aNewAddress = lclGetRangeAddress( xNewCellRange );
1272     table::CellRangeAddress aOldAddress;
1273     // expand as long as there are new merged ranges included
1274     do
1275     {
1276         aOldAddress = aNewAddress;
1277         uno::Reference< sheet::XSheetCellCursor > xCursor( xSheet->createCursorByRange( xNewCellRange ), uno::UNO_SET_THROW );
1278         if (xCursor.is())
1279         {
1280             xCursor->collapseToMergedArea();
1281             xNewCellRange.set( xCursor, uno::UNO_QUERY_THROW );
1282             aNewAddress = lclGetRangeAddress( xNewCellRange );
1283         }
1284     }
1285     while( bRecursive && (aOldAddress != aNewAddress) );
1286     return xNewCellRange;
1287 }
1288 
1289 /// @throws uno::RuntimeException
lclExpandToMerged(const uno::Reference<sheet::XSheetCellRangeContainer> & rxCellRanges)1290 uno::Reference< sheet::XSheetCellRangeContainer > lclExpandToMerged( const uno::Reference< sheet::XSheetCellRangeContainer >& rxCellRanges )
1291 {
1292     if( !rxCellRanges.is() )
1293         throw uno::RuntimeException(u"Missing cell ranges object"_ustr );
1294     sal_Int32 nCount = rxCellRanges->getCount();
1295     if( nCount < 1 )
1296         throw uno::RuntimeException(u"Missing cell ranges object"_ustr );
1297 
1298     ScRangeList aScRanges;
1299     for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
1300     {
1301         uno::Reference< table::XCellRange > xRange( rxCellRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
1302         table::CellRangeAddress aRangeAddr = lclGetRangeAddress( lclExpandToMerged( xRange, /*bRecursive*/true ) );
1303         ScRange aScRange;
1304         ScUnoConversion::FillScRange( aScRange, aRangeAddr );
1305         aScRanges.push_back( aScRange );
1306     }
1307     return new ScCellRangesObj( getDocShellFromRanges( rxCellRanges ), aScRanges );
1308 }
1309 
1310 /// @throws uno::RuntimeException
lclExpandAndMerge(const uno::Reference<table::XCellRange> & rxCellRange,bool bMerge)1311 void lclExpandAndMerge( const uno::Reference< table::XCellRange >& rxCellRange, bool bMerge )
1312 {
1313     uno::Reference< util::XMergeable > xMerge( lclExpandToMerged( rxCellRange, true ), uno::UNO_QUERY_THROW );
1314     // Calc cannot merge over merged ranges, always unmerge first
1315     xMerge->merge( false );
1316     if( !bMerge )
1317         return;
1318 
1319     // clear all contents of the covered cells (not the top-left cell)
1320     table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
1321     sal_Int32 nLastColIdx = aRangeAddr.EndColumn - aRangeAddr.StartColumn;
1322     sal_Int32 nLastRowIdx = aRangeAddr.EndRow - aRangeAddr.StartRow;
1323     // clear cells of top row, right of top-left cell
1324     if( nLastColIdx > 0 )
1325         lclClearRange( rxCellRange->getCellRangeByPosition( 1, 0, nLastColIdx, 0 ) );
1326     // clear all rows below top row
1327     if( nLastRowIdx > 0 )
1328         lclClearRange( rxCellRange->getCellRangeByPosition( 0, 1, nLastColIdx, nLastRowIdx ) );
1329     // merge the range
1330     xMerge->merge( true );
1331 }
1332 
1333 /// @throws uno::RuntimeException
lclGetMergedState(const uno::Reference<table::XCellRange> & rxCellRange)1334 util::TriState lclGetMergedState( const uno::Reference< table::XCellRange >& rxCellRange )
1335 {
1336     /*  1) Check if range is completely inside one single merged range. To do
1337         this, try to extend from top-left cell only (not from entire range).
1338         This will exclude cases where this range consists of several merged
1339         ranges (or parts of them). */
1340     table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
1341     uno::Reference< table::XCellRange > xTopLeft( rxCellRange->getCellRangeByPosition( 0, 0, 0, 0 ), uno::UNO_SET_THROW );
1342     uno::Reference< sheet::XSheetCellRange > xExpanded( lclExpandToMerged( xTopLeft, false ), uno::UNO_SET_THROW );
1343     table::CellRangeAddress aExpAddr = lclGetRangeAddress( xExpanded );
1344     // check that expanded range has more than one cell (really merged)
1345     if( ((aExpAddr.StartColumn < aExpAddr.EndColumn) || (aExpAddr.StartRow < aExpAddr.EndRow)) && ScUnoConversion::Contains( aExpAddr, aRangeAddr ) )
1346         return util::TriState_YES;
1347 
1348     /*  2) Check if this range contains any merged cells (completely or
1349         partly). This seems to be hardly possible via API, as
1350         XMergeable::getIsMerged() returns only true, if the top-left cell of a
1351         merged range is part of this range, so cases where just the lower part
1352         of a merged range is part of this range are not covered. */
1353     ScRange aScRange;
1354     ScUnoConversion::FillScRange( aScRange, aRangeAddr );
1355     bool bHasMerged = getDocumentFromRange( rxCellRange ).HasAttrib( aScRange, HasAttrFlags::Merged | HasAttrFlags::Overlapped );
1356     return bHasMerged ? util::TriState_INDETERMINATE : util::TriState_NO;
1357 }
1358 
1359 } // namespace
1360 
1361 css::uno::Reference< excel::XRange >
getRangeObjectForName(const uno::Reference<uno::XComponentContext> & xContext,const OUString & sRangeName,ScDocShell * pDocSh,formula::FormulaGrammar::AddressConvention eConv)1362 ScVbaRange::getRangeObjectForName(
1363         const uno::Reference< uno::XComponentContext >& xContext, const OUString& sRangeName,
1364         ScDocShell* pDocSh, formula::FormulaGrammar::AddressConvention eConv )
1365 {
1366     table::CellRangeAddress refAddr;
1367     return getRangeForName( xContext, sRangeName, pDocSh, refAddr, eConv );
1368 }
1369 
1370 /// @throws uno::RuntimeException
getCellRangeAddressForVBARange(const uno::Any & aParam,ScDocShell * pDocSh)1371 static table::CellRangeAddress getCellRangeAddressForVBARange( const uno::Any& aParam, ScDocShell* pDocSh )
1372 {
1373     uno::Reference< table::XCellRange > xRangeParam;
1374     switch ( aParam.getValueTypeClass() )
1375     {
1376         case uno::TypeClass_STRING:
1377         {
1378             OUString rString;
1379             aParam >>= rString;
1380             ScRangeList aCellRanges;
1381             ScRange refRange;
1382             if ( getScRangeListForAddress ( rString, pDocSh, refRange, aCellRanges ) )
1383             {
1384                 if ( aCellRanges.size() == 1 )
1385                 {
1386                     table::CellRangeAddress aRangeAddress;
1387                     ScUnoConversion::FillApiRange( aRangeAddress, aCellRanges.front() );
1388                     return aRangeAddress;
1389                 }
1390             }
1391         }
1392         break;
1393 
1394         case uno::TypeClass_INTERFACE:
1395         {
1396             uno::Reference< excel::XRange > xRange;
1397             aParam >>= xRange;
1398             if ( xRange.is() )
1399                 xRange->getCellRange() >>= xRangeParam;
1400         }
1401         break;
1402 
1403         default:
1404             throw uno::RuntimeException(u"Can't extract CellRangeAddress from type"_ustr );
1405     }
1406     return lclGetRangeAddress( xRangeParam );
1407 }
1408 
1409 /// @throws uno::RuntimeException
1410 static uno::Reference< XCollection >
lcl_setupBorders(const uno::Reference<excel::XRange> & xParentRange,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<table::XCellRange> & xRange)1411 lcl_setupBorders( const uno::Reference< excel::XRange >& xParentRange, const uno::Reference<uno::XComponentContext>& xContext,  const uno::Reference< table::XCellRange >& xRange  )
1412 {
1413     uno::Reference< XHelperInterface > xParent( xParentRange, uno::UNO_QUERY_THROW );
1414     ScDocument& rDoc = getDocumentFromRange(xRange);
1415     ScVbaPalette aPalette( rDoc.GetDocumentShell() );
1416     uno::Reference< XCollection > borders( new ScVbaBorders( xParent, xContext, xRange, aPalette ) );
1417     return borders;
1418 }
1419 
ScVbaRange(uno::Sequence<uno::Any> const & args,uno::Reference<uno::XComponentContext> const & xContext)1420 ScVbaRange::ScVbaRange( uno::Sequence< uno::Any> const & args,
1421     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 )
1422 {
1423     mxRange.set( mxPropertySet, uno::UNO_QUERY );
1424     mxRanges.set( mxPropertySet, uno::UNO_QUERY );
1425     uno::Reference< container::XIndexAccess >  xIndex;
1426     if ( mxRange.is() )
1427     {
1428         xIndex = new SingleRangeIndexAccess( mxRange );
1429     }
1430     else if ( mxRanges.is() )
1431     {
1432         xIndex.set( mxRanges, uno::UNO_QUERY_THROW );
1433     }
1434     m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
1435 }
1436 
ScVbaRange(const uno::Reference<XHelperInterface> & xParent,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<table::XCellRange> & xRange,bool bIsRows,bool bIsColumns)1437 ScVbaRange::ScVbaRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< table::XCellRange >& xRange, bool bIsRows, bool bIsColumns )
1438 : ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRange, uno::UNO_QUERY_THROW ), getModelFromRange( xRange), true ), mxRange( xRange ),
1439                 mbIsRows( bIsRows ),
1440                 mbIsColumns( bIsColumns )
1441 {
1442     if  ( !xContext.is() )
1443         throw lang::IllegalArgumentException(u"context is not set "_ustr, uno::Reference< uno::XInterface >() , 1 );
1444     if  ( !xRange.is() )
1445         throw lang::IllegalArgumentException(u"range is not set "_ustr, uno::Reference< uno::XInterface >() , 1 );
1446 
1447     uno::Reference< container::XIndexAccess > xIndex( new SingleRangeIndexAccess( xRange ) );
1448     m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
1449 
1450 }
1451 
ScVbaRange(const uno::Reference<XHelperInterface> & xParent,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<sheet::XSheetCellRangeContainer> & xRanges,bool bIsRows,bool bIsColumns)1452 ScVbaRange::ScVbaRange(const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges, bool bIsRows, bool bIsColumns)
1453 : 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 )
1454 
1455 {
1456     uno::Reference< container::XIndexAccess >  xIndex( mxRanges, uno::UNO_QUERY_THROW );
1457     m_Areas  = new ScVbaRangeAreas( xParent, mxContext, xIndex, mbIsRows, mbIsColumns );
1458 
1459 }
1460 
~ScVbaRange()1461 ScVbaRange::~ScVbaRange()
1462 {
1463 }
1464 
getBorders()1465 uno::Reference< XCollection >& ScVbaRange::getBorders()
1466 {
1467     if ( !m_Borders.is() )
1468     {
1469         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
1470         m_Borders = lcl_setupBorders( this, mxContext, uno::Reference< table::XCellRange >( xRange->getCellRange(), uno::UNO_QUERY_THROW ) );
1471     }
1472     return m_Borders;
1473 }
1474 
1475 void
visitArray(ArrayVisitor & visitor)1476 ScVbaRange::visitArray( ArrayVisitor& visitor )
1477 {
1478     ScDocShell* pDocSh = nullptr;
1479     if(ScCellRangeObj* range = dynamic_cast<ScCellRangeObj*>(mxRange.get()))
1480         pDocSh = range->GetDocShell();
1481     if ( pDocSh )
1482         pDocSh->LockPaint();
1483     table::CellRangeAddress aRangeAddr = lclGetRangeAddress( mxRange );
1484     sal_Int32 nRowCount = aRangeAddr.EndRow - aRangeAddr.StartRow + 1;
1485     sal_Int32 nColCount = aRangeAddr.EndColumn - aRangeAddr.StartColumn + 1;
1486     for ( sal_Int32 i=0; i<nRowCount; ++i )
1487     {
1488         for ( sal_Int32 j=0; j<nColCount; ++j )
1489         {
1490             uno::Reference< table::XCell > xCell( mxRange->getCellByPosition( j, i ), uno::UNO_SET_THROW );
1491 
1492             visitor.visitNode( i, j, xCell );
1493         }
1494     }
1495     if ( pDocSh )
1496         pDocSh->UnlockPaint();
1497 }
1498 
1499 uno::Any
getValue(ValueGetter & valueGetter)1500 ScVbaRange::getValue( ValueGetter& valueGetter)
1501 {
1502     uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
1503     // single cell range
1504     if ( isSingleCellRange() )
1505     {
1506         visitArray( valueGetter );
1507         return valueGetter.getValue();
1508     }
1509     sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
1510     sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
1511     // multi cell range ( return array )
1512     Dim2ArrayValueGetter arrayGetter( nRowCount, nColCount, valueGetter );
1513     visitArray( arrayGetter );
1514     return uno::Any( script::ArrayWrapper( false, arrayGetter.getValue() ) );
1515 }
1516 
DoGetValue(RangeValueType eValueType)1517 css::uno::Any ScVbaRange::DoGetValue( RangeValueType eValueType )
1518 {
1519     // #TODO code within the test below "if ( m_Areas... " can be removed
1520     // Test is performed only because m_xRange is NOT set to be
1521     // the first range in m_Areas ( to force failure while
1522     // the implementations for each method are being updated )
1523     if ( m_Areas->getCount() > 1 )
1524     {
1525         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1526         return xRange->getValue();
1527     }
1528 
1529     CellValueGetter valueGetter( eValueType );
1530     return getValue( valueGetter );
1531 }
1532 
1533 uno::Any SAL_CALL
getValue()1534 ScVbaRange::getValue()
1535 {
1536     return DoGetValue( RangeValueType::value );
1537 }
1538 
1539 uno::Any SAL_CALL
getValue2()1540 ScVbaRange::getValue2()
1541 {
1542     return DoGetValue( RangeValueType::value2 );
1543 }
1544 
1545 
1546 void
setValue(const uno::Any & aValue,ValueSetter & valueSetter)1547 ScVbaRange::setValue( const uno::Any& aValue, ValueSetter& valueSetter )
1548 {
1549     uno::TypeClass aClass = aValue.getValueTypeClass();
1550     if ( aClass == uno::TypeClass_SEQUENCE )
1551     {
1552         const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
1553         uno::Any aConverted;
1554         try
1555         {
1556             // test for single dimension, could do
1557             // with a better test than this
1558             if ( aValue.getValueTypeName().indexOf('[') ==  aValue.getValueTypeName().lastIndexOf('[') )
1559             {
1560                 aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Any >>::get() );
1561                 Dim1ArrayValueSetter setter( aConverted, valueSetter );
1562                 visitArray( setter );
1563             }
1564             else
1565             {
1566                 aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() );
1567                 Dim2ArrayValueSetter setter( aConverted, valueSetter );
1568                 visitArray( setter );
1569             }
1570         }
1571         catch ( const uno::Exception& )
1572         {
1573             TOOLS_WARN_EXCEPTION("sc", "Bahhh, caught" );
1574         }
1575     }
1576     else
1577     {
1578         visitArray( valueSetter );
1579     }
1580     fireChangeEvent();
1581 }
1582 
1583 void SAL_CALL
setValue(const uno::Any & aValue)1584 ScVbaRange::setValue( const uno::Any  &aValue )
1585 {
1586     // If this is a multiple selection apply setValue over all areas
1587     if ( m_Areas->getCount() > 1 )
1588     {
1589         AreasVisitor aVisitor( m_Areas );
1590         RangeValueProcessor valueProcessor( aValue );
1591         aVisitor.visit( valueProcessor );
1592         return;
1593     }
1594     CellValueSetter valueSetter( aValue );
1595     setValue( aValue, valueSetter );
1596 }
1597 
1598 void SAL_CALL
setValue2(const uno::Any & aValue)1599 ScVbaRange::setValue2( const uno::Any  &aValue )
1600 {
1601     return setValue( aValue );
1602 }
1603 
1604 
1605 void SAL_CALL
Clear()1606 ScVbaRange::Clear()
1607 {
1608     using namespace ::com::sun::star::sheet::CellFlags;
1609     sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA | HARDATTR | EDITATTR | FORMATTED;
1610     ClearContents( nFlags, true );
1611 }
1612 
1613 //helper ClearContent
1614 void
ClearContents(sal_Int32 nFlags,bool bFireEvent)1615 ScVbaRange::ClearContents( sal_Int32 nFlags, bool bFireEvent )
1616 {
1617     // #TODO code within the test below "if ( m_Areas... " can be removed
1618     // Test is performed only because m_xRange is NOT set to be
1619     // the first range in m_Areas ( to force failure while
1620     // the implementations for each method are being updated )
1621     if ( m_Areas->getCount() > 1 )
1622     {
1623         sal_Int32 nItems = m_Areas->getCount();
1624         for ( sal_Int32 index=1; index <= nItems; ++index )
1625         {
1626             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
1627             ScVbaRange* pRange = getImplementation( xRange );
1628             if ( pRange )
1629                 pRange->ClearContents( nFlags, false ); // do not fire for single ranges
1630         }
1631         // fire change event for the entire range list
1632         if( bFireEvent ) fireChangeEvent();
1633         return;
1634     }
1635 
1636     uno::Reference< sheet::XSheetOperation > xSheetOperation(mxRange, uno::UNO_QUERY_THROW);
1637     xSheetOperation->clearContents( nFlags );
1638     if( bFireEvent ) fireChangeEvent();
1639 }
1640 
1641 void SAL_CALL
ClearComments()1642 ScVbaRange::ClearComments()
1643 {
1644     ClearContents( sheet::CellFlags::ANNOTATION, false );
1645 }
1646 
1647 void SAL_CALL
ClearContents()1648 ScVbaRange::ClearContents()
1649 {
1650     using namespace ::com::sun::star::sheet::CellFlags;
1651     sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA;
1652     ClearContents( nFlags, true );
1653 }
1654 
1655 void SAL_CALL
ClearFormats()1656 ScVbaRange::ClearFormats()
1657 {
1658     // FIXME: need to check if we need to combine FORMATTED
1659     using namespace ::com::sun::star::sheet::CellFlags;
1660     sal_Int32 const nFlags = HARDATTR | FORMATTED | EDITATTR;
1661     ClearContents( nFlags, false );
1662 }
1663 
1664 void
setFormulaValue(const uno::Any & rFormula,formula::FormulaGrammar::Grammar eGram)1665 ScVbaRange::setFormulaValue( const uno::Any& rFormula, formula::FormulaGrammar::Grammar eGram )
1666 {
1667     // If this is a multiple selection apply setFormula over all areas
1668     if ( m_Areas->getCount() > 1 )
1669     {
1670         AreasVisitor aVisitor( m_Areas );
1671         RangeFormulaProcessor valueProcessor( rFormula );
1672         aVisitor.visit( valueProcessor );
1673         return;
1674     }
1675     CellFormulaValueSetter formulaValueSetter( rFormula, getScDocument(), eGram );
1676     setValue( rFormula, formulaValueSetter );
1677 }
1678 
1679 uno::Any
getFormulaValue(formula::FormulaGrammar::Grammar eGram)1680 ScVbaRange::getFormulaValue( formula::FormulaGrammar::Grammar eGram )
1681 {
1682     // #TODO code within the test below "if ( m_Areas... " can be removed
1683     // Test is performed only because m_xRange is NOT set to be
1684     // the first range in m_Areas ( to force failure while
1685     // the implementations for each method are being updated )
1686     if ( m_Areas->getCount() > 1 )
1687     {
1688         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1689         return xRange->getFormula();
1690     }
1691     CellFormulaValueGetter valueGetter( getScDocument(), eGram );
1692     return getValue( valueGetter );
1693 
1694 }
1695 
1696 uno::Any
getFormula()1697 ScVbaRange::getFormula()
1698 {
1699     return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
1700 }
1701 
1702 void
setFormula(const uno::Any & rFormula)1703 ScVbaRange::setFormula(const uno::Any &rFormula )
1704 {
1705     setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
1706 }
1707 
1708 uno::Any
getFormulaR1C1()1709 ScVbaRange::getFormulaR1C1()
1710 {
1711     return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
1712 }
1713 
1714 void
setFormulaR1C1(const uno::Any & rFormula)1715 ScVbaRange::setFormulaR1C1(const uno::Any& rFormula )
1716 {
1717     setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
1718 }
1719 
1720 uno::Any
getFormulaLocal()1721 ScVbaRange::getFormulaLocal()
1722 {
1723     return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
1724 }
1725 
1726 void
setFormulaLocal(const uno::Any & rFormula)1727 ScVbaRange::setFormulaLocal(const uno::Any &rFormula )
1728 {
1729     setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
1730 }
1731 
1732 uno::Any
getFormulaR1C1Local()1733 ScVbaRange::getFormulaR1C1Local()
1734 {
1735     return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
1736 }
1737 
1738 void
setFormulaR1C1Local(const uno::Any & rFormula)1739 ScVbaRange::setFormulaR1C1Local(const uno::Any& rFormula )
1740 {
1741     setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
1742 }
1743 
1744 sal_Int32
getCount()1745 ScVbaRange::getCount()
1746 {
1747     // If this is a multiple selection apply setValue over all areas
1748     if ( m_Areas->getCount() > 1 )
1749     {
1750         AreasVisitor aVisitor( m_Areas );
1751         RangeCountProcessor valueProcessor;
1752         aVisitor.visit( valueProcessor );
1753         return valueProcessor.value();
1754     }
1755     sal_Int32 rowCount = 0;
1756     sal_Int32 colCount = 0;
1757     uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
1758     rowCount = xColumnRowRange->getRows()->getCount();
1759     colCount = xColumnRowRange->getColumns()->getCount();
1760 
1761     if( mbIsRows )
1762         return rowCount;
1763     if( mbIsColumns )
1764         return colCount;
1765     return rowCount * colCount;
1766 }
1767 
1768 sal_Int32
getRow()1769 ScVbaRange::getRow()
1770 {
1771     // #TODO code within the test below "if ( m_Areas... " can be removed
1772     // Test is performed only because m_xRange is NOT set to be
1773     // the first range in m_Areas ( to force failure while
1774     // the implementations for each method are being updated )
1775     if ( m_Areas->getCount() > 1 )
1776     {
1777         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1778         return xRange->getRow();
1779     }
1780     uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
1781     return xCellAddressable->getCellAddress().Row + 1; // Zero value indexing
1782 }
1783 
1784 sal_Int32
getColumn()1785 ScVbaRange::getColumn()
1786 {
1787     // #TODO code within the test below "if ( m_Areas... " can be removed
1788     // Test is performed only because m_xRange is NOT set to be
1789     // the first range in m_Areas ( to force failure while
1790     // the implementations for each method are being updated )
1791     if ( m_Areas->getCount() > 1 )
1792     {
1793         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1794         return xRange->getColumn();
1795     }
1796     uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
1797     return xCellAddressable->getCellAddress().Column + 1; // Zero value indexing
1798 }
1799 
1800 uno::Any
HasFormula()1801 ScVbaRange::HasFormula()
1802 {
1803     if ( m_Areas->getCount() > 1 )
1804     {
1805         sal_Int32 nItems = m_Areas->getCount();
1806         uno::Any aResult = aNULL();
1807         for ( sal_Int32 index=1; index <= nItems; ++index )
1808         {
1809             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
1810             // if the HasFormula for any area is different to another
1811             // return null
1812             if ( index > 1 )
1813                 if ( aResult != xRange->HasFormula() )
1814                     return aNULL();
1815             aResult = xRange->HasFormula();
1816             if ( aNULL() == aResult )
1817                 return aNULL();
1818         }
1819         return aResult;
1820     }
1821     uno::Reference< uno::XInterface > xIf( mxRange, uno::UNO_QUERY_THROW );
1822     ScCellRangesBase* pThisRanges = dynamic_cast< ScCellRangesBase* > ( xIf.get() );
1823     if ( pThisRanges )
1824     {
1825         uno::Reference<uno::XInterface>  xRanges( pThisRanges->queryFormulaCells( sheet::FormulaResult::ERROR | sheet::FormulaResult::VALUE | sheet::FormulaResult::STRING ), uno::UNO_QUERY_THROW );
1826         ScCellRangesBase* pFormulaRanges
1827             = dynamic_cast< ScCellRangesBase* > ( xRanges.get() );
1828         assert(pFormulaRanges);
1829         // check if there are no formula cell, return false
1830         if ( pFormulaRanges->GetRangeList().empty() )
1831             return uno::Any(false);
1832 
1833         // check if there are holes (where some cells are not formulas)
1834         // or returned range is not equal to this range
1835         if (  ( pFormulaRanges->GetRangeList().size() > 1 )
1836            || ( pFormulaRanges->GetRangeList().front().aStart != pThisRanges->GetRangeList().front().aStart )
1837            || ( pFormulaRanges->GetRangeList().front().aEnd   != pThisRanges->GetRangeList().front().aEnd   )
1838            )
1839             return aNULL(); // should return aNULL;
1840     }
1841     return uno::Any( true );
1842 }
1843 void
fillSeries(sheet::FillDirection nFillDirection,sheet::FillMode nFillMode,sheet::FillDateMode nFillDateMode,double fStep,double fEndValue)1844 ScVbaRange::fillSeries( sheet::FillDirection nFillDirection, sheet::FillMode nFillMode, sheet::FillDateMode nFillDateMode, double fStep, double fEndValue )
1845 {
1846     if ( m_Areas->getCount() > 1 )
1847     {
1848         // Multi-Area Range
1849         uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
1850         for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
1851         {
1852             uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
1853             ScVbaRange* pThisRange = getImplementation( xRange );
1854             pThisRange->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
1855 
1856         }
1857         return;
1858     }
1859 
1860     uno::Reference< sheet::XCellSeries > xCellSeries(mxRange, uno::UNO_QUERY_THROW );
1861     xCellSeries->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
1862     fireChangeEvent();
1863 }
1864 
1865 void
FillLeft()1866 ScVbaRange::FillLeft()
1867 {
1868     fillSeries(sheet::FillDirection_TO_LEFT,
1869         sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1870 }
1871 
1872 void
FillRight()1873 ScVbaRange::FillRight()
1874 {
1875     fillSeries(sheet::FillDirection_TO_RIGHT,
1876         sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1877 }
1878 
1879 void
FillUp()1880 ScVbaRange::FillUp()
1881 {
1882     fillSeries(sheet::FillDirection_TO_TOP,
1883         sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1884 }
1885 
1886 void
FillDown()1887 ScVbaRange::FillDown()
1888 {
1889     fillSeries(sheet::FillDirection_TO_BOTTOM,
1890         sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1891 }
1892 
1893 OUString
getText()1894 ScVbaRange::getText()
1895 {
1896     // #TODO code within the test below "if ( m_Areas... " can be removed
1897     // Test is performed only because m_xRange is NOT set to be
1898     // the first range in m_Areas ( to force failure while
1899     // the implementations for each method are being updated )
1900     if ( m_Areas->getCount() > 1 )
1901     {
1902         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1903         return xRange->getText();
1904     }
1905     uno::Reference< text::XTextRange > xTextRange(mxRange->getCellByPosition(0,0), uno::UNO_QUERY_THROW );
1906     return xTextRange->getString();
1907 }
1908 
1909 uno::Reference< excel::XRange >
Offset(const::uno::Any & nRowOff,const uno::Any & nColOff)1910 ScVbaRange::Offset( const ::uno::Any &nRowOff, const uno::Any &nColOff )
1911 {
1912     SCROW nRowOffset = 0;
1913     SCCOL nColOffset = 0;
1914     bool bIsRowOffset = ( nRowOff >>= nRowOffset );
1915     bool bIsColumnOffset = ( nColOff >>= nColOffset );
1916     ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
1917 
1918     ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
1919 
1920     for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
1921     {
1922         ScRange & rRange = aCellRanges[ i ];
1923         if ( bIsColumnOffset )
1924         {
1925             rRange.aStart.SetCol( rRange.aStart.Col() + nColOffset );
1926             rRange.aEnd.SetCol( rRange.aEnd.Col() + nColOffset );
1927         }
1928         if ( bIsRowOffset )
1929         {
1930             rRange.aStart.SetRow( rRange.aStart.Row() + nRowOffset );
1931             rRange.aEnd.SetRow( rRange.aEnd.Row() + nRowOffset );
1932         }
1933     }
1934 
1935     if ( aCellRanges.size() > 1 ) // Multi-Area
1936     {
1937         uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
1938         return new ScVbaRange( mxParent, mxContext, xRanges );
1939     }
1940     // normal range
1941     const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
1942     uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
1943     return new ScVbaRange( mxParent, mxContext, xRange  );
1944 }
1945 
1946 uno::Reference< excel::XRange >
CurrentRegion()1947 ScVbaRange::CurrentRegion()
1948 {
1949     // #TODO code within the test below "if ( m_Areas... " can be removed
1950     // Test is performed only because m_xRange is NOT set to be
1951     // the first range in m_Areas ( to force failure while
1952     // the implementations for each method are being updated )
1953     if ( m_Areas->getCount() > 1 )
1954     {
1955         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1956         return xRange->CurrentRegion();
1957     }
1958 
1959     RangeHelper helper( mxRange );
1960     uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
1961         helper.getSheetCellCursor();
1962     xSheetCellCursor->collapseToCurrentRegion();
1963     uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
1964     return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
1965 }
1966 
1967 uno::Reference< excel::XRange >
CurrentArray()1968 ScVbaRange::CurrentArray()
1969 {
1970     // #TODO code within the test below "if ( m_Areas... " can be removed
1971     // Test is performed only because m_xRange is NOT set to be
1972     // the first range in m_Areas ( to force failure while
1973     // the implementations for each method are being updated )
1974     if ( m_Areas->getCount() > 1 )
1975     {
1976         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1977         return xRange->CurrentArray();
1978     }
1979     RangeHelper helper( mxRange );
1980     uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
1981         helper.getSheetCellCursor();
1982     xSheetCellCursor->collapseToCurrentArray();
1983     uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
1984     return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
1985 }
1986 
1987 uno::Any
getFormulaArray()1988 ScVbaRange::getFormulaArray()
1989 {
1990     // #TODO code within the test below "if ( m_Areas... " can be removed
1991     // Test is performed only because m_xRange is NOT set to be
1992     // the first range in m_Areas ( to force failure while
1993     // the implementations for each method are being updated )
1994     if ( m_Areas->getCount() > 1 )
1995     {
1996         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1997         return xRange->getFormulaArray();
1998     }
1999 
2000     // return a formula if there is one or else an array
2001     // still not sure when the return as array code should run
2002     // ( I think it is if there is more than one formula ) at least
2003     // that is what the doc says ( but I am not even sure how to detect that )
2004     // for the moment any tests we have pass
2005     uno::Reference< sheet::XArrayFormulaRange> xFormulaArray( mxRange, uno::UNO_QUERY_THROW );
2006     if ( !xFormulaArray->getArrayFormula().isEmpty() )
2007         return uno::Any( xFormulaArray->getArrayFormula() );
2008 
2009     uno::Reference< sheet::XCellRangeFormula> xCellRangeFormula( mxRange, uno::UNO_QUERY_THROW );
2010     const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
2011     uno::Any aSingleValueOrMatrix;
2012     // When dealing with a single element ( embedded in the sequence of sequence ) unwrap and return
2013     // that value
2014     uno::Sequence< uno::Sequence<OUString> > aTmpSeq = xCellRangeFormula->getFormulaArray();
2015     if ( aTmpSeq.getLength() == 1 )
2016     {
2017         if ( aTmpSeq[ 0 ].getLength() == 1  )
2018             aSingleValueOrMatrix <<= aTmpSeq[ 0 ][ 0 ];
2019     }
2020     else
2021         aSingleValueOrMatrix = xConverter->convertTo( uno::Any( aTmpSeq ) , cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get()  ) ;
2022     return aSingleValueOrMatrix;
2023 }
2024 
2025 void
setFormulaArray(const uno::Any & rFormula)2026 ScVbaRange::setFormulaArray(const uno::Any& rFormula)
2027 {
2028     // #TODO code within the test below "if ( m_Areas... " can be removed
2029     // Test is performed only because m_xRange is NOT set to be
2030     // the first range in m_Areas ( to force failure while
2031     // the implementations for each method are being updated )
2032     if ( m_Areas->getCount() > 1 )
2033     {
2034         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
2035         return xRange->setFormulaArray( rFormula );
2036     }
2037     // #TODO need to distinguish between getFormula and getFormulaArray e.g. (R1C1)
2038     // but for the moment it's just easier to treat them the same for setting
2039     // seems
2040     uno::Reference< lang::XMultiServiceFactory > xModelFactory( getUnoModel(), uno::UNO_QUERY_THROW );
2041     uno::Reference< sheet::XFormulaParser > xParser( xModelFactory->createInstance( u"com.sun.star.sheet.FormulaParser"_ustr ), uno::UNO_QUERY_THROW );
2042     uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY_THROW);
2043 
2044     table::CellRangeAddress aRangeAddress = xSource->getRangeAddress();
2045     // #TODO check if api orders the address
2046     // e.g. do we need to order the RangeAddress to get the topleft ( or can we assume it
2047     // is in the correct order )
2048     table::CellAddress aAddress;
2049     aAddress.Sheet = aRangeAddress.Sheet;
2050     aAddress.Column = aRangeAddress.StartColumn;
2051     aAddress.Row = aRangeAddress.StartRow;
2052     OUString sFormula;
2053     rFormula >>= sFormula;
2054     uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( sFormula, aAddress );
2055     ScTokenArray aTokenArray(getScDocument());
2056     (void)ScTokenConversion::ConvertToTokenArray( getScDocument(), aTokenArray, aTokens );
2057 
2058     getScDocShell()->GetDocFunc().EnterMatrix( getScRangeList()[0], nullptr, &aTokenArray, OUString(), true, true, OUString(), formula::FormulaGrammar::GRAM_API );
2059 }
2060 
2061 OUString
Characters(const uno::Any & Start,const uno::Any & Length)2062 ScVbaRange::Characters(const uno::Any& Start, const uno::Any& Length)
2063 {
2064     // #TODO code within the test below "if ( m_Areas... " can be removed
2065     // Test is performed only because m_xRange is NOT set to be
2066     // the first range in m_Areas ( to force failure while
2067     // the implementations for each method are being updated )
2068     if ( m_Areas->getCount() > 1 )
2069     {
2070         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
2071         return xRange->Characters( Start, Length );
2072     }
2073 
2074     tools::Long nIndex = 0, nCount = 0;
2075     OUString rString;
2076     uno::Reference< text::XTextRange > xTextRange(mxRange, ::uno::UNO_QUERY_THROW );
2077     rString = xTextRange->getString();
2078     if( !( Start >>= nIndex ) && !( Length >>= nCount ) )
2079         return rString;
2080     if(!( Start >>= nIndex ) )
2081         nIndex = 1;
2082     if(!( Length >>= nCount ) )
2083         nIndex = rString.getLength();
2084     return rString.copy( --nIndex, nCount ); // Zero value indexing
2085 }
2086 
2087 OUString
Address(const uno::Any & RowAbsolute,const uno::Any & ColumnAbsolute,const uno::Any & ReferenceStyle,const uno::Any & External,const uno::Any & RelativeTo)2088 ScVbaRange::Address(  const uno::Any& RowAbsolute, const uno::Any& ColumnAbsolute, const uno::Any& ReferenceStyle, const uno::Any& External, const uno::Any& RelativeTo )
2089 {
2090     if ( m_Areas->getCount() > 1 )
2091     {
2092         // Multi-Area Range
2093         OUStringBuffer sAddress;
2094         uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
2095         uno::Any aExternalCopy = External;
2096         for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
2097         {
2098             uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
2099             if ( index > 1 )
2100             {
2101                 sAddress.append(",");
2102                 // force external to be false
2103                 // only first address should have the
2104                 // document and sheet specifications
2105                 aExternalCopy <<= false;
2106             }
2107             sAddress.append(xRange->Address( RowAbsolute, ColumnAbsolute, ReferenceStyle, aExternalCopy, RelativeTo ));
2108         }
2109         return sAddress.makeStringAndClear();
2110 
2111     }
2112     ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2113     if ( ReferenceStyle.hasValue() )
2114     {
2115         sal_Int32 refStyle = excel::XlReferenceStyle::xlA1;
2116         ReferenceStyle >>= refStyle;
2117         if ( refStyle == excel::XlReferenceStyle::xlR1C1 )
2118             dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, 0, 0 );
2119     }
2120     // default
2121     ScRefFlags nFlags = ScRefFlags::RANGE_ABS;
2122     ScDocShell* pDocShell =  getScDocShell();
2123     ScDocument& rDoc =  pDocShell->GetDocument();
2124 
2125     RangeHelper thisRange( mxRange );
2126     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
2127     ScRange aRange( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCROW >( thisAddress.StartRow ), static_cast< SCTAB >( thisAddress.Sheet ), static_cast< SCCOL >( thisAddress.EndColumn ), static_cast< SCROW >( thisAddress.EndRow ), static_cast< SCTAB >( thisAddress.Sheet ) );
2128     constexpr ScRefFlags ROW_ABS = ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
2129     constexpr ScRefFlags COL_ABS = ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
2130 
2131     if ( RowAbsolute.hasValue() )
2132     {
2133         bool bVal = true;
2134         RowAbsolute >>= bVal;
2135         if ( !bVal )
2136             nFlags &= ~ROW_ABS;
2137     }
2138     if ( ColumnAbsolute.hasValue() )
2139     {
2140         bool bVal = true;
2141         ColumnAbsolute >>= bVal;
2142         if ( !bVal )
2143             nFlags &= ~COL_ABS;
2144     }
2145     if ( External.hasValue() )
2146     {
2147         bool bLocal = false;
2148         External >>= bLocal;
2149         if (  bLocal )
2150             nFlags |= ScRefFlags::TAB_3D | ScRefFlags::FORCE_DOC;
2151     }
2152     if ( RelativeTo.hasValue() )
2153     {
2154         // #TODO should I throw an error if R1C1 is not set?
2155 
2156         table::CellRangeAddress refAddress = getCellRangeAddressForVBARange( RelativeTo, pDocShell );
2157         dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, static_cast< SCROW >( refAddress.StartRow ), static_cast< SCCOL >( refAddress.StartColumn ) );
2158     }
2159     return aRange.Format(rDoc, nFlags, dDetails);
2160 }
2161 
2162 uno::Reference < excel::XFont >
Font()2163 ScVbaRange::Font()
2164 {
2165     uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY );
2166     ScDocument& rDoc = getScDocument();
2167     if ( mxRange.is() )
2168         xProps.set(mxRange, ::uno::UNO_QUERY );
2169     else if ( mxRanges.is() )
2170         xProps.set(mxRanges, ::uno::UNO_QUERY );
2171 
2172     ScVbaPalette aPalette( rDoc.GetDocumentShell() );
2173     ScCellRangeObj* pRangeObj = nullptr;
2174     try
2175     {
2176         pRangeObj = getCellRangeObj();
2177     }
2178     catch( uno::Exception& )
2179     {
2180     }
2181     return  new ScVbaFont( this, mxContext, aPalette, xProps, pRangeObj );
2182 }
2183 
2184 uno::Reference< excel::XRange >
Cells(const uno::Any & nRowIndex,const uno::Any & nColumnIndex)2185 ScVbaRange::Cells( const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
2186 {
2187     // #TODO code within the test below "if ( m_Areas... " can be removed
2188     // Test is performed only because m_xRange is NOT set to be
2189     // the first range in m_Areas ( to force failure while
2190     // the implementations for each method are being updated )
2191     if ( m_Areas->getCount() > 1 )
2192     {
2193         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
2194         return xRange->Cells( nRowIndex, nColumnIndex );
2195     }
2196 
2197     // Performance: Use a common helper method for ScVbaRange::Cells and ScVbaWorksheet::Cells,
2198     // instead of creating a new ScVbaRange object in often-called ScVbaWorksheet::Cells
2199     return CellsHelper( getScDocument(), mxParent, mxContext, mxRange, nRowIndex, nColumnIndex );
2200 }
2201 
2202 // static
2203 uno::Reference< excel::XRange >
CellsHelper(const ScDocument & rDoc,const uno::Reference<ov::XHelperInterface> & xParent,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<css::table::XCellRange> & xRange,const uno::Any & nRowIndex,const uno::Any & nColumnIndex)2204 ScVbaRange::CellsHelper( const ScDocument& rDoc,
2205                          const uno::Reference< ov::XHelperInterface >& xParent,
2206                          const uno::Reference< uno::XComponentContext >& xContext,
2207                          const uno::Reference< css::table::XCellRange >& xRange,
2208                          const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
2209 {
2210     sal_Int32 nRow = 0, nColumn = 0;
2211 
2212     bool bIsIndex = nRowIndex.hasValue();
2213     bool bIsColumnIndex = nColumnIndex.hasValue();
2214 
2215     // Sometimes we might get a float or a double or whatever
2216     // set in the Any, we should convert as appropriate
2217     // #FIXME - perhaps worth turning this into some sort of
2218     // conversion routine e.g. bSuccess = getValueFromAny( nRow, nRowIndex, cppu::UnoType<sal_Int32>::get() )
2219     if ( nRowIndex.hasValue() && !( nRowIndex >>= nRow ) )
2220     {
2221         const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
2222         uno::Any aConverted;
2223         try
2224         {
2225             aConverted = xConverter->convertTo( nRowIndex, cppu::UnoType<sal_Int32>::get() );
2226             bIsIndex = ( aConverted >>= nRow );
2227         }
2228         catch( uno::Exception& ) {} // silence any errors
2229     }
2230 
2231     if ( bIsColumnIndex )
2232     {
2233          // Column index can be a col address e.g Cells( 1, "B" ) etc.
2234         OUString sCol;
2235         if ( nColumnIndex >>= sCol )
2236         {
2237             ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2238             ScRange tmpRange;
2239             ScRefFlags flags = tmpRange.ParseCols( rDoc, sCol, dDetails );
2240             if ( (flags & ScRefFlags::COL_VALID) == ScRefFlags::ZERO )
2241                throw uno::RuntimeException();
2242             nColumn = tmpRange.aStart.Col() + 1;
2243         }
2244         else
2245         {
2246             if ( !( nColumnIndex >>= nColumn ) )
2247             {
2248                 const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
2249                 uno::Any aConverted;
2250                 try
2251                 {
2252                     aConverted = xConverter->convertTo( nColumnIndex, cppu::UnoType<sal_Int32>::get() );
2253                     bIsColumnIndex = ( aConverted >>= nColumn );
2254                 }
2255                 catch( uno::Exception& ) {} // silence any errors
2256             }
2257        }
2258     }
2259     RangeHelper thisRange( xRange );
2260     table::CellRangeAddress thisRangeAddress =  thisRange.getCellRangeAddressable()->getRangeAddress();
2261     uno::Reference< table::XCellRange > xSheetRange = thisRange.getCellRangeFromSheet();
2262     if( !bIsIndex && !bIsColumnIndex ) // .Cells
2263         // #FIXME needs proper parent ( Worksheet )
2264         return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext, xRange ) );
2265 
2266     sal_Int32 nIndex = --nRow;
2267     if( bIsIndex && !bIsColumnIndex ) // .Cells(n)
2268     {
2269         uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, ::uno::UNO_QUERY_THROW);
2270         sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
2271 
2272         if ( !nIndex || nIndex < 0 )
2273             nRow = 0;
2274         else
2275             nRow = nIndex / nColCount;
2276         nColumn = nIndex % nColCount;
2277     }
2278     else
2279         --nColumn;
2280     nRow = nRow + thisRangeAddress.StartRow;
2281     nColumn =  nColumn + thisRangeAddress.StartColumn;
2282     return new ScVbaRange( xParent, xContext, xSheetRange->getCellRangeByPosition( nColumn, nRow, nColumn, nRow ) );
2283 }
2284 
2285 void
Select()2286 ScVbaRange::Select()
2287 {
2288     ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2289     if ( !pUnoRangesBase )
2290         throw uno::RuntimeException(u"Failed to access underlying uno range object"_ustr  );
2291     ScDocShell* pShell = pUnoRangesBase->GetDocShell();
2292     if ( !pShell )
2293         return;
2294 
2295     uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_SET_THROW );
2296     uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
2297     if ( mxRanges.is() )
2298         xSelection->select( uno::Any( lclExpandToMerged( mxRanges ) ) );
2299     else
2300         xSelection->select( uno::Any( lclExpandToMerged( mxRange, true ) ) );
2301     // set focus on document e.g.
2302     // ThisComponent.CurrentController.Frame.getContainerWindow.SetFocus
2303     try
2304     {
2305         uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW );
2306         uno::Reference< frame::XFrame > xFrame( xController->getFrame(), uno::UNO_SET_THROW );
2307         uno::Reference< awt::XWindow > xWin( xFrame->getContainerWindow(), uno::UNO_SET_THROW );
2308         xWin->setFocus();
2309     }
2310     catch( uno::Exception& )
2311     {
2312     }
2313 }
2314 
cellInRange(const table::CellRangeAddress & rAddr,sal_Int32 nCol,sal_Int32 nRow)2315 static bool cellInRange( const table::CellRangeAddress& rAddr, sal_Int32 nCol, sal_Int32 nRow )
2316 {
2317     return nCol >= rAddr.StartColumn && nCol <= rAddr.EndColumn &&
2318         nRow >= rAddr.StartRow && nRow <= rAddr.EndRow;
2319 }
2320 
setCursor(SCCOL nCol,SCROW nRow,const uno::Reference<frame::XModel> & xModel,bool bInSel=true)2321 static void setCursor( SCCOL nCol, SCROW nRow, const uno::Reference< frame::XModel >& xModel,  bool bInSel = true )
2322 {
2323     ScTabViewShell* pShell = excel::getBestViewShell( xModel );
2324     if ( pShell )
2325     {
2326         if ( bInSel )
2327             pShell->SetCursor( nCol, nRow );
2328         else
2329             pShell->MoveCursorAbs( nCol, nRow, SC_FOLLOW_NONE, false, false, true );
2330     }
2331 }
2332 
2333 void
Activate()2334 ScVbaRange::Activate()
2335 {
2336     // get first cell of current range
2337     uno::Reference< table::XCellRange > xCellRange;
2338     if ( mxRanges.is() )
2339     {
2340         uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW  );
2341         xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
2342     }
2343     else
2344         xCellRange.set( mxRange, uno::UNO_SET_THROW );
2345 
2346     RangeHelper thisRange( xCellRange );
2347     uno::Reference< sheet::XCellRangeAddressable > xThisRangeAddress = thisRange.getCellRangeAddressable();
2348     table::CellRangeAddress thisRangeAddress = xThisRangeAddress->getRangeAddress();
2349     uno::Reference< frame::XModel > xModel;
2350     ScDocShell* pShell = getScDocShell();
2351 
2352     if ( pShell )
2353         xModel = pShell->GetModel();
2354 
2355     if ( !xModel.is() )
2356         throw uno::RuntimeException();
2357 
2358     // get current selection
2359     uno::Reference< sheet::XCellRangeAddressable > xRange( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
2360 
2361     uno::Reference< sheet::XSheetCellRanges > xRanges( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
2362 
2363     if ( xRanges.is() )
2364     {
2365         const uno::Sequence< table::CellRangeAddress > nAddrs = xRanges->getRangeAddresses();
2366         for ( const auto& rAddr : nAddrs )
2367         {
2368             if ( cellInRange( rAddr, thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
2369             {
2370                 setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
2371                 return;
2372             }
2373 
2374         }
2375     }
2376 
2377     if ( xRange.is() && cellInRange( xRange->getRangeAddress(), thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
2378         setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
2379     else
2380     {
2381         // if this range is multi cell select the range other
2382         // wise just position the cell at this single range position
2383         if ( isSingleCellRange() )
2384             // This top-leftmost cell of this Range is not in the current
2385             // selection so just select this range
2386             setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel, false  );
2387         else
2388             Select();
2389     }
2390 
2391 }
2392 
obtainRangeEvenIfRangeListIsEmpty(const ScRangeList & rCellRanges) const2393 ScRange ScVbaRange::obtainRangeEvenIfRangeListIsEmpty( const ScRangeList& rCellRanges ) const
2394 {
2395     // XXX It may be that using the current range list was never correct, but
2396     // always the initial sheet range would be instead, history is unclear.
2397 
2398     if (!rCellRanges.empty())
2399         return rCellRanges.front();
2400 
2401     table::CellRangeAddress aRA( lclGetRangeAddress( mxRange ));
2402     return ScRange( aRA.StartColumn, aRA.StartRow, aRA.Sheet, aRA.EndColumn, aRA.EndRow, aRA.Sheet);
2403 }
2404 
2405 uno::Reference< excel::XRange >
Rows(const uno::Any & aIndex)2406 ScVbaRange::Rows(const uno::Any& aIndex )
2407 {
2408     if ( aIndex.hasValue() )
2409     {
2410         ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2411         ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
2412 
2413         sal_Int32 nValue = 0;
2414         OUString sAddress;
2415         if( aIndex >>= nValue )
2416         {
2417             aRange.aStart.SetRow( aRange.aStart.Row() + --nValue );
2418             aRange.aEnd.SetRow( aRange.aStart.Row() );
2419         }
2420         else if ( aIndex >>= sAddress )
2421         {
2422             ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2423             ScRange tmpRange;
2424             tmpRange.ParseRows( getScDocument(), sAddress, dDetails );
2425             SCROW nStartRow = tmpRange.aStart.Row();
2426             SCROW nEndRow = tmpRange.aEnd.Row();
2427 
2428             aRange.aStart.SetRow( aRange.aStart.Row() + nStartRow );
2429             aRange.aEnd.SetRow( aRange.aStart.Row() + ( nEndRow  - nStartRow ));
2430         }
2431         else
2432             throw uno::RuntimeException(u"Illegal param"_ustr );
2433 
2434         if ( aRange.aStart.Row() < 0 || aRange.aEnd.Row() < 0 )
2435             throw uno::RuntimeException(u"Internal failure, illegal param"_ustr );
2436         // return a normal range ( even for multi-selection
2437         uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
2438         return new ScVbaRange( mxParent, mxContext, xRange, true  );
2439     }
2440     // Rows() - no params
2441     if ( m_Areas->getCount() > 1 )
2442         return new ScVbaRange(  mxParent, mxContext, mxRanges, true );
2443     return new ScVbaRange(  mxParent, mxContext, mxRange, true );
2444 }
2445 
2446 uno::Reference< excel::XRange >
Columns(const uno::Any & aIndex)2447 ScVbaRange::Columns(const uno::Any& aIndex )
2448 {
2449     ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2450     ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
2451 
2452     if ( aIndex.hasValue() )
2453     {
2454         OUString sAddress;
2455         sal_Int32 nValue = 0;
2456         if ( aIndex >>= nValue )
2457         {
2458             aRange.aStart.SetCol( aRange.aStart.Col() + static_cast< SCCOL > ( --nValue ) );
2459             aRange.aEnd.SetCol( aRange.aStart.Col() );
2460         }
2461 
2462         else if ( aIndex >>= sAddress )
2463         {
2464             ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2465             ScRange tmpRange;
2466             tmpRange.ParseCols( getScDocument(), sAddress, dDetails );
2467             SCCOL nStartCol = tmpRange.aStart.Col();
2468             SCCOL nEndCol = tmpRange.aEnd.Col();
2469 
2470             aRange.aStart.SetCol( aRange.aStart.Col() + nStartCol );
2471             aRange.aEnd.SetCol( aRange.aStart.Col() + ( nEndCol  - nStartCol ));
2472         }
2473         else
2474             throw uno::RuntimeException(u"Illegal param"_ustr );
2475 
2476         if ( aRange.aStart.Col() < 0 || aRange.aEnd.Col() < 0 )
2477             throw uno::RuntimeException(u"Internal failure, illegal param"_ustr );
2478     }
2479     // Columns() - no params
2480     uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
2481     return new ScVbaRange( mxParent, mxContext, xRange, false, true  );
2482 }
2483 
2484 void
setMergeCells(const uno::Any & aIsMerged)2485 ScVbaRange::setMergeCells( const uno::Any& aIsMerged )
2486 {
2487     bool bMerge = extractBoolFromAny( aIsMerged );
2488 
2489     if( mxRanges.is() )
2490     {
2491         sal_Int32 nCount = mxRanges->getCount();
2492 
2493         // VBA does nothing (no error) if the own ranges overlap somehow
2494         std::vector< table::CellRangeAddress > aList;
2495         for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
2496         {
2497             uno::Reference< sheet::XCellRangeAddressable > xRangeAddr( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
2498             table::CellRangeAddress aAddress = xRangeAddr->getRangeAddress();
2499             if (std::any_of(aList.begin(), aList.end(),
2500                     [&aAddress](const table::CellRangeAddress& rAddress)
2501                     { return ScUnoConversion::Intersects( rAddress, aAddress ); }))
2502                 return;
2503             aList.push_back( aAddress );
2504         }
2505 
2506         // (un)merge every range after it has been extended to intersecting merged ranges from sheet
2507         for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
2508         {
2509             uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
2510             lclExpandAndMerge( xRange, bMerge );
2511         }
2512         return;
2513     }
2514 
2515     // otherwise, merge single range
2516     lclExpandAndMerge( mxRange, bMerge );
2517 }
2518 
2519 uno::Any
getMergeCells()2520 ScVbaRange::getMergeCells()
2521 {
2522     if( mxRanges.is() )
2523     {
2524         sal_Int32 nCount = mxRanges->getCount();
2525         for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
2526         {
2527             uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
2528             util::TriState eMerged = lclGetMergedState( xRange );
2529             /*  Excel always returns NULL, if one range of the range list is
2530                 partly or completely merged. Even if all ranges are completely
2531                 merged, the return value is still NULL. */
2532             if( eMerged != util::TriState_NO )
2533                 return aNULL();
2534         }
2535         // no range is merged anyhow, return false
2536         return uno::Any( false );
2537     }
2538 
2539     // otherwise, check single range
2540     switch( lclGetMergedState( mxRange ) )
2541     {
2542         case util::TriState_YES:    return uno::Any( true );
2543         case util::TriState_NO:     return uno::Any( false );
2544         default:                    return aNULL();
2545     }
2546 }
2547 
2548 void
Copy(const::uno::Any & Destination)2549 ScVbaRange::Copy(const ::uno::Any& Destination)
2550 {
2551     if ( Destination.hasValue() )
2552     {
2553         // TODO copy with multiple selections should work here too
2554         if ( m_Areas->getCount() > 1 )
2555             throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
2556         uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
2557         uno::Any aRange = xRange->getCellRange();
2558         uno::Reference< table::XCellRange > xCellRange;
2559         aRange >>= xCellRange;
2560         uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW);
2561         uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
2562         uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
2563         uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
2564         uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
2565                                                 xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY_THROW );
2566         uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
2567         xMover->copyRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
2568         if ( ScVbaRange* pRange = getImplementation( xRange ) )
2569             pRange->fireChangeEvent();
2570     }
2571     else
2572     {
2573         Select();
2574         excel::implnCopy(getUnoModel());
2575     }
2576 }
2577 
2578 void
Cut(const::uno::Any & Destination)2579 ScVbaRange::Cut(const ::uno::Any& Destination)
2580 {
2581     if ( m_Areas->getCount() > 1 )
2582         throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
2583     if (Destination.hasValue())
2584     {
2585         uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
2586         uno::Reference< table::XCellRange > xCellRange( xRange->getCellRange(), uno::UNO_QUERY_THROW );
2587         uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW );
2588         uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
2589         uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
2590         uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
2591         uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
2592                                                 xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY);
2593         uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
2594         xMover->moveRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
2595     }
2596     else
2597     {
2598         uno::Reference< frame::XModel > xModel = getModelFromRange( mxRange );
2599         Select();
2600         excel::implnCut( xModel );
2601     }
2602 }
2603 
2604 void
setNumberFormat(const uno::Any & aFormat)2605 ScVbaRange::setNumberFormat( const uno::Any& aFormat )
2606 {
2607     OUString sFormat;
2608     aFormat >>= sFormat;
2609     if ( m_Areas->getCount() > 1 )
2610     {
2611         sal_Int32 nItems = m_Areas->getCount();
2612         for ( sal_Int32 index=1; index <= nItems; ++index )
2613         {
2614             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2615             xRange->setNumberFormat( aFormat );
2616         }
2617         return;
2618     }
2619     NumFormatHelper numFormat( mxRange );
2620     numFormat.setNumberFormat( sFormat );
2621 }
2622 
2623 uno::Any
getNumberFormat()2624 ScVbaRange::getNumberFormat()
2625 {
2626 
2627     if ( m_Areas->getCount() > 1 )
2628     {
2629         sal_Int32 nItems = m_Areas->getCount();
2630         uno::Any aResult = aNULL();
2631         for ( sal_Int32 index=1; index <= nItems; ++index )
2632         {
2633             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2634             // if the numberformat of one area is different to another
2635             // return null
2636             if ( index > 1 )
2637                 if ( aResult != xRange->getNumberFormat() )
2638                     return aNULL();
2639             aResult = xRange->getNumberFormat();
2640             if ( aNULL() == aResult )
2641                 return aNULL();
2642         }
2643         return aResult;
2644     }
2645     NumFormatHelper numFormat( mxRange );
2646     OUString sFormat = numFormat.getNumberFormatString();
2647     if ( !sFormat.isEmpty() )
2648         return uno::Any( sFormat );
2649     return aNULL();
2650 }
2651 
2652 uno::Reference< excel::XRange >
Resize(const uno::Any & RowSize,const uno::Any & ColumnSize)2653 ScVbaRange::Resize( const uno::Any &RowSize, const uno::Any &ColumnSize )
2654 {
2655     tools::Long nRowSize = 0, nColumnSize = 0;
2656     bool bIsRowChanged = ( RowSize >>= nRowSize ), bIsColumnChanged = ( ColumnSize >>= nColumnSize );
2657     uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, ::uno::UNO_QUERY_THROW);
2658     uno::Reference< sheet::XSheetCellRange > xSheetRange(mxRange, ::uno::UNO_QUERY_THROW);
2659     uno::Reference< sheet::XSheetCellCursor > xCursor( xSheetRange->getSpreadsheet()->createCursorByRange(xSheetRange), ::uno::UNO_SET_THROW );
2660 
2661     if( !bIsRowChanged )
2662         nRowSize = xColumnRowRange->getRows()->getCount();
2663     if( !bIsColumnChanged )
2664         nColumnSize = xColumnRowRange->getColumns()->getCount();
2665 
2666     xCursor->collapseToSize( nColumnSize, nRowSize );
2667     uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xCursor, ::uno::UNO_QUERY_THROW );
2668     uno::Reference< table::XCellRange > xRange( xSheetRange->getSpreadsheet(), ::uno::UNO_QUERY_THROW );
2669     const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
2670     return new ScVbaRange( mxParent, mxContext, xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow));
2671 }
2672 
2673 void
setWrapText(const uno::Any & aIsWrapped)2674 ScVbaRange::setWrapText( const uno::Any& aIsWrapped )
2675 {
2676     if ( m_Areas->getCount() > 1 )
2677     {
2678         sal_Int32 nItems = m_Areas->getCount();
2679         for ( sal_Int32 index=1; index <= nItems; ++index )
2680         {
2681             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2682             xRange->setWrapText( aIsWrapped );
2683         }
2684         return;
2685     }
2686 
2687     uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
2688     bool bIsWrapped = extractBoolFromAny( aIsWrapped );
2689     xProps->setPropertyValue( u"IsTextWrapped"_ustr, uno::Any( bIsWrapped ) );
2690 }
2691 
2692 uno::Any
getWrapText()2693 ScVbaRange::getWrapText()
2694 {
2695     if ( m_Areas->getCount() > 1 )
2696     {
2697         sal_Int32 nItems = m_Areas->getCount();
2698         uno::Any aResult;
2699         for ( sal_Int32 index=1; index <= nItems; ++index )
2700         {
2701             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2702             if ( index > 1 )
2703                 if ( aResult != xRange->getWrapText() )
2704                     return aNULL();
2705             aResult = xRange->getWrapText();
2706         }
2707         return aResult;
2708     }
2709 
2710     SfxItemSet* pDataSet = getCurrentDataSet();
2711 
2712     SfxItemState eState = pDataSet->GetItemState( ATTR_LINEBREAK);
2713     if ( eState == SfxItemState::INVALID )
2714         return aNULL();
2715 
2716     uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
2717     uno::Any aValue = xProps->getPropertyValue( u"IsTextWrapped"_ustr );
2718     return aValue;
2719 }
2720 
Interior()2721 uno::Reference< excel::XInterior > ScVbaRange::Interior( )
2722 {
2723     uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
2724     return new ScVbaInterior ( this, mxContext, xProps, &getScDocument() );
2725 }
2726 uno::Reference< excel::XRange >
Range(const uno::Any & Cell1,const uno::Any & Cell2)2727 ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2 )
2728 {
2729     return Range( Cell1, Cell2, false );
2730 }
2731 uno::Reference< excel::XRange >
Range(const uno::Any & Cell1,const uno::Any & Cell2,bool bForceUseInpuRangeTab)2732 ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2, bool bForceUseInpuRangeTab )
2733 
2734 {
2735     uno::Reference< table::XCellRange > xCellRange = mxRange;
2736 
2737     if ( m_Areas->getCount() > 1 )
2738     {
2739         uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
2740         xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
2741     }
2742     else
2743         xCellRange.set( mxRange );
2744 
2745     RangeHelper thisRange( xCellRange );
2746     uno::Reference< table::XCellRange > xRanges = thisRange.getCellRangeFromSheet();
2747     uno::Reference< sheet::XCellRangeAddressable > xAddressable( xRanges, uno::UNO_QUERY_THROW );
2748 
2749     uno::Reference< table::XCellRange > xReferrer =
2750         xRanges->getCellRangeByPosition( getColumn()-1, getRow()-1,
2751                 xAddressable->getRangeAddress().EndColumn,
2752                 xAddressable->getRangeAddress().EndRow );
2753     // xAddressable now for this range
2754     xAddressable.set( xReferrer, uno::UNO_QUERY_THROW );
2755 
2756     if( !Cell1.hasValue() )
2757         throw uno::RuntimeException( u"Invalid Argument"_ustr );
2758 
2759     table::CellRangeAddress parentRangeAddress = xAddressable->getRangeAddress();
2760 
2761     ScRange aRange;
2762     // Cell1 defined only
2763     if ( !Cell2.hasValue() )
2764     {
2765         OUString sName;
2766         Cell1 >>= sName;
2767         RangeHelper referRange( xReferrer );
2768         table::CellRangeAddress referAddress = referRange.getCellRangeAddressable()->getRangeAddress();
2769         return getRangeForName( mxContext, sName, getScDocShell(), referAddress );
2770 
2771     }
2772     else
2773     {
2774         table::CellRangeAddress  cell1, cell2;
2775         cell1 = getCellRangeAddressForVBARange( Cell1, getScDocShell() );
2776         // Cell1 & Cell2 defined
2777         // Excel seems to combine the range as the range defined by
2778         // the combination of Cell1 & Cell2
2779 
2780         cell2 = getCellRangeAddressForVBARange( Cell2, getScDocShell() );
2781 
2782         table::CellRangeAddress resultAddress;
2783         resultAddress.StartColumn = ( cell1.StartColumn <  cell2.StartColumn ) ? cell1.StartColumn : cell2.StartColumn;
2784         resultAddress.StartRow = ( cell1.StartRow <  cell2.StartRow ) ? cell1.StartRow : cell2.StartRow;
2785         resultAddress.EndColumn = std::max( cell1.EndColumn, cell2.EndColumn );
2786         resultAddress.EndRow = std::max( cell1.EndRow, cell2.EndRow );
2787         if ( bForceUseInpuRangeTab )
2788         {
2789             // this is a call from Application.Range( x,y )
2790             // it's possible for x or y to specify a different sheet from
2791             // the current or active on ( but they must be the same )
2792             if ( cell1.Sheet != cell2.Sheet )
2793                 throw uno::RuntimeException();
2794             parentRangeAddress.Sheet = cell1.Sheet;
2795         }
2796         else
2797         {
2798             // this is not a call from Application.Range( x,y )
2799             // if a different sheet from this range is specified it's
2800             // an error
2801             if ( parentRangeAddress.Sheet != cell1.Sheet
2802             || parentRangeAddress.Sheet != cell2.Sheet
2803             )
2804                 throw uno::RuntimeException();
2805 
2806         }
2807         ScUnoConversion::FillScRange( aRange, resultAddress );
2808     }
2809     ScRange parentAddress;
2810     ScUnoConversion::FillScRange( parentAddress, parentRangeAddress);
2811     if ( aRange.aStart.Col() >= 0 && aRange.aStart.Row() >= 0 && aRange.aEnd.Col() >= 0 && aRange.aEnd.Row() >= 0 )
2812     {
2813         sal_Int32 nStartX = parentAddress.aStart.Col() + aRange.aStart.Col();
2814         sal_Int32 nStartY = parentAddress.aStart.Row() + aRange.aStart.Row();
2815         sal_Int32 nEndX = parentAddress.aStart.Col() + aRange.aEnd.Col();
2816         sal_Int32 nEndY = parentAddress.aStart.Row() + aRange.aEnd.Row();
2817 
2818         if ( nStartX <= nEndX && nEndX <= parentAddress.aEnd.Col() &&
2819              nStartY <= nEndY && nEndY <= parentAddress.aEnd.Row() )
2820         {
2821             ScRange aNew( static_cast<SCCOL>(nStartX), static_cast<SCROW>(nStartY), parentAddress.aStart.Tab(),
2822                           static_cast<SCCOL>(nEndX), static_cast<SCROW>(nEndY), parentAddress.aEnd.Tab() );
2823             xCellRange = new ScCellRangeObj( getScDocShell(), aNew );
2824         }
2825     }
2826 
2827     return new ScVbaRange( mxParent, mxContext, xCellRange );
2828 
2829 }
2830 
2831 // Allow access to underlying openoffice uno api ( useful for debugging
2832 // with openoffice basic )
getCellRange()2833 uno::Any SAL_CALL ScVbaRange::getCellRange(  )
2834 {
2835     uno::Any aAny;
2836     if ( mxRanges.is() )
2837         aAny <<= mxRanges;
2838     else if ( mxRange.is() )
2839         aAny <<= mxRange;
2840     return aAny;
2841 }
2842 
getCellRange(const uno::Reference<excel::XRange> & rxRange)2843 uno::Any ScVbaRange::getCellRange( const uno::Reference< excel::XRange >& rxRange )
2844 {
2845     if( ScVbaRange* pVbaRange = getImplementation( rxRange ) )
2846         return pVbaRange->getCellRange();
2847     throw uno::RuntimeException();
2848 }
2849 
getPasteFlags(sal_Int32 Paste)2850 static InsertDeleteFlags getPasteFlags (sal_Int32 Paste)
2851 {
2852     InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
2853     switch (Paste) {
2854         case excel::XlPasteType::xlPasteComments:
2855         nFlags = InsertDeleteFlags::NOTE;break;
2856         case excel::XlPasteType::xlPasteFormats:
2857         nFlags = InsertDeleteFlags::ATTRIB;break;
2858         case excel::XlPasteType::xlPasteFormulas:
2859         nFlags = InsertDeleteFlags::FORMULA;break;
2860         case excel::XlPasteType::xlPasteFormulasAndNumberFormats :
2861         case excel::XlPasteType::xlPasteValues:
2862         nFlags = ( InsertDeleteFlags::VALUE | InsertDeleteFlags::DATETIME | InsertDeleteFlags::STRING | InsertDeleteFlags::SPECIAL_BOOLEAN ); break;
2863         case excel::XlPasteType::xlPasteValuesAndNumberFormats:
2864         nFlags = InsertDeleteFlags::VALUE | InsertDeleteFlags::ATTRIB; break;
2865         case excel::XlPasteType::xlPasteColumnWidths:
2866         case excel::XlPasteType::xlPasteValidation:
2867         nFlags = InsertDeleteFlags::NONE;break;
2868     case excel::XlPasteType::xlPasteAll:
2869         case excel::XlPasteType::xlPasteAllExceptBorders:
2870     default:
2871         nFlags = InsertDeleteFlags::ALL;break;
2872     }
2873     return nFlags;
2874 }
2875 
2876 static ScPasteFunc
getPasteFormulaBits(sal_Int32 Operation)2877 getPasteFormulaBits( sal_Int32 Operation)
2878 {
2879     ScPasteFunc nFormulaBits = ScPasteFunc::NONE;
2880     switch (Operation)
2881     {
2882     case excel::XlPasteSpecialOperation::xlPasteSpecialOperationAdd:
2883         nFormulaBits = ScPasteFunc::ADD; break;
2884     case excel::XlPasteSpecialOperation::xlPasteSpecialOperationSubtract:
2885         nFormulaBits = ScPasteFunc::SUB;break;
2886     case excel::XlPasteSpecialOperation::xlPasteSpecialOperationMultiply:
2887         nFormulaBits = ScPasteFunc::MUL;break;
2888     case excel::XlPasteSpecialOperation::xlPasteSpecialOperationDivide:
2889         nFormulaBits = ScPasteFunc::DIV;break;
2890 
2891     case excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone:
2892     default:
2893         nFormulaBits = ScPasteFunc::NONE; break;
2894     }
2895 
2896     return nFormulaBits;
2897 }
2898 void SAL_CALL
PasteSpecial(const uno::Any & Paste,const uno::Any & Operation,const uno::Any & SkipBlanks,const uno::Any & Transpose)2899 ScVbaRange::PasteSpecial( const uno::Any& Paste, const uno::Any& Operation, const uno::Any& SkipBlanks, const uno::Any& Transpose )
2900 {
2901     if ( m_Areas->getCount() > 1 )
2902         throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
2903     ScDocShell* pShell = getScDocShell();
2904 
2905     if (!pShell)
2906         throw uno::RuntimeException(u"That command cannot be used with no ScDocShell"_ustr );
2907 
2908     uno::Reference< frame::XModel > xModel(pShell->GetModel(), uno::UNO_SET_THROW);
2909     uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
2910     // select this range
2911     xSelection->select( uno::Any( mxRange ) );
2912     // set up defaults
2913     sal_Int32 nPaste = excel::XlPasteType::xlPasteAll;
2914     sal_Int32 nOperation = excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone;
2915     bool bTranspose = false;
2916     bool bSkipBlanks = false;
2917 
2918     if ( Paste.hasValue() )
2919         Paste >>= nPaste;
2920     if ( Operation.hasValue() )
2921         Operation >>= nOperation;
2922     if ( SkipBlanks.hasValue() )
2923         SkipBlanks >>= bSkipBlanks;
2924     if ( Transpose.hasValue() )
2925         Transpose >>= bTranspose;
2926 
2927     InsertDeleteFlags nFlags = getPasteFlags(nPaste);
2928     ScPasteFunc nFormulaBits = getPasteFormulaBits(nOperation);
2929 
2930     excel::implnPasteSpecial(xModel, nFlags, nFormulaBits, bSkipBlanks, bTranspose);
2931 }
2932 
2933 uno::Reference< excel::XRange >
getEntireColumnOrRow(bool bColumn)2934 ScVbaRange::getEntireColumnOrRow( bool bColumn )
2935 {
2936     ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2937     // copy the range list
2938     ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
2939     ScDocument& rDoc = getScDocument();
2940 
2941     for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
2942     {
2943         ScRange & rRange = aCellRanges[ i ];
2944         if ( bColumn )
2945         {
2946             rRange.aStart.SetRow( 0 );
2947             rRange.aEnd.SetRow( rDoc.MaxRow() );
2948         }
2949         else
2950         {
2951             rRange.aStart.SetCol( 0 );
2952             rRange.aEnd.SetCol( rDoc.MaxCol() );
2953         }
2954     }
2955     if ( aCellRanges.size() > 1 ) // Multi-Area
2956     {
2957         uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
2958 
2959         return new ScVbaRange( mxParent, mxContext, xRanges, !bColumn, bColumn );
2960     }
2961     const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
2962     uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
2963     return new ScVbaRange( mxParent, mxContext, xRange, !bColumn, bColumn  );
2964 }
2965 
2966 uno::Reference< excel::XRange > SAL_CALL
getEntireRow()2967 ScVbaRange::getEntireRow()
2968 {
2969     return getEntireColumnOrRow(false);
2970 }
2971 
2972 uno::Reference< excel::XRange > SAL_CALL
getEntireColumn()2973 ScVbaRange::getEntireColumn()
2974 {
2975     return getEntireColumnOrRow(true);
2976 }
2977 
2978 uno::Reference< excel::XComment > SAL_CALL
AddComment(const uno::Any & Text)2979 ScVbaRange::AddComment( const uno::Any& Text )
2980 {
2981     // if there is already a comment in the top-left cell then throw
2982     if( getComment().is() )
2983         throw uno::RuntimeException();
2984 
2985     // workaround: Excel allows to create empty comment, Calc does not
2986     OUString aNoteText;
2987     if( Text.hasValue() && !(Text >>= aNoteText) )
2988         throw uno::RuntimeException();
2989     if( aNoteText.isEmpty() )
2990         aNoteText = " ";
2991 
2992     // try to create a new annotation
2993     table::CellRangeAddress aRangePos = lclGetRangeAddress( mxRange );
2994     table::CellAddress aNotePos( aRangePos.Sheet, aRangePos.StartColumn, aRangePos.StartRow );
2995     uno::Reference< sheet::XSheetCellRange > xCellRange( mxRange, uno::UNO_QUERY_THROW );
2996     uno::Reference< sheet::XSheetAnnotationsSupplier > xAnnosSupp( xCellRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
2997     uno::Reference< sheet::XSheetAnnotations > xAnnos( xAnnosSupp->getAnnotations(), uno::UNO_SET_THROW );
2998     xAnnos->insertNew( aNotePos, aNoteText );
2999     return new ScVbaComment( this, mxContext, getUnoModel(), mxRange );
3000 }
3001 
3002 uno::Reference< excel::XComment > SAL_CALL
getComment()3003 ScVbaRange::getComment()
3004 {
3005     // intentional behavior to return a null object if no
3006     // comment defined
3007     uno::Reference< excel::XComment > xComment( new ScVbaComment( this, mxContext, getUnoModel(), mxRange ) );
3008     if ( xComment->Text( uno::Any(), uno::Any(), uno::Any() ).isEmpty() )
3009         return nullptr;
3010     return xComment;
3011 
3012 }
3013 
3014 /// @throws uno::RuntimeException
3015 static uno::Reference< beans::XPropertySet >
getRowOrColumnProps(const uno::Reference<table::XCellRange> & xCellRange,bool bRows)3016 getRowOrColumnProps( const uno::Reference< table::XCellRange >& xCellRange, bool bRows )
3017 {
3018     uno::Reference< table::XColumnRowRange > xColRow( xCellRange, uno::UNO_QUERY_THROW );
3019     uno::Reference< beans::XPropertySet > xProps;
3020     if ( bRows )
3021         xProps.set( xColRow->getRows(), uno::UNO_QUERY_THROW );
3022     else
3023         xProps.set( xColRow->getColumns(), uno::UNO_QUERY_THROW );
3024     return xProps;
3025 }
3026 
3027 uno::Any SAL_CALL
getHidden()3028 ScVbaRange::getHidden()
3029 {
3030     // if multi-area result is the result of the
3031     // first area
3032     if ( m_Areas->getCount() > 1 )
3033     {
3034         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(sal_Int32(1)), uno::Any() ), uno::UNO_QUERY_THROW );
3035         return xRange->getHidden();
3036     }
3037     bool bIsVisible = false;
3038     try
3039     {
3040         uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
3041         if ( !( xProps->getPropertyValue( ISVISIBLE ) >>= bIsVisible ) )
3042             throw uno::RuntimeException(u"Failed to get IsVisible property"_ustr );
3043     }
3044     catch( const uno::Exception& e )
3045     {
3046         css::uno::Any anyEx = cppu::getCaughtException();
3047         throw css::lang::WrappedTargetRuntimeException( e.Message,
3048                         nullptr, anyEx );
3049     }
3050     return uno::Any( !bIsVisible );
3051 }
3052 
3053 void SAL_CALL
setHidden(const uno::Any & _hidden)3054 ScVbaRange::setHidden( const uno::Any& _hidden )
3055 {
3056     if ( m_Areas->getCount() > 1 )
3057     {
3058         sal_Int32 nItems = m_Areas->getCount();
3059         for ( sal_Int32 index=1; index <= nItems; ++index )
3060         {
3061             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
3062             xRange->setHidden( _hidden );
3063         }
3064         return;
3065     }
3066 
3067     bool bHidden = extractBoolFromAny( _hidden );
3068     try
3069     {
3070         uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
3071         xProps->setPropertyValue( ISVISIBLE, uno::Any( !bHidden ) );
3072     }
3073     catch( const uno::Exception& e )
3074     {
3075         css::uno::Any anyEx = cppu::getCaughtException();
3076         throw css::lang::WrappedTargetRuntimeException( e.Message,
3077                         nullptr, anyEx );
3078     }
3079 }
3080 
3081 sal_Bool SAL_CALL
Replace(const OUString & What,const OUString & Replacement,const uno::Any & LookAt,const uno::Any & SearchOrder,const uno::Any & MatchCase,const uno::Any & MatchByte,const uno::Any & SearchFormat,const uno::Any & ReplaceFormat)3082 ScVbaRange::Replace( const OUString& What, const OUString& Replacement, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& MatchCase, const uno::Any& MatchByte, const uno::Any& SearchFormat, const uno::Any& ReplaceFormat  )
3083 {
3084     if ( m_Areas->getCount() > 1 )
3085     {
3086         for ( sal_Int32 index = 1; index <= m_Areas->getCount(); ++index )
3087         {
3088             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
3089             xRange->Replace( What, Replacement,  LookAt, SearchOrder, MatchCase, MatchByte, SearchFormat, ReplaceFormat );
3090         }
3091         return true; // seems to return true always ( or at least I haven't found the trick of
3092     }
3093 
3094     // sanity check required params
3095     if ( What.isEmpty()  )
3096         throw uno::RuntimeException(u"Range::Replace, missing params"_ustr );
3097     // #TODO #FIXME SearchFormat & ReplacesFormat are not processed
3098     // What do we do about MatchByte... we don't seem to support that
3099     const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
3100     SvxSearchItem newOptions( globalSearchOptions );
3101 
3102     uno::Reference< util::XReplaceable > xReplace( mxRange, uno::UNO_QUERY );
3103     if ( xReplace.is() )
3104     {
3105         uno::Reference< util::XReplaceDescriptor > xDescriptor =
3106             xReplace->createReplaceDescriptor();
3107 
3108         xDescriptor->setSearchString(What);
3109         xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
3110         xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
3111         xDescriptor->setReplaceString( Replacement);
3112         if ( LookAt.hasValue() )
3113         {
3114             // sets SearchWords ( true is Cell match )
3115             sal_Int16 nLook =  ::comphelper::getINT16( LookAt );
3116             bool bSearchWords = false;
3117             if ( nLook == excel::XlLookAt::xlPart )
3118                 bSearchWords = false;
3119             else if ( nLook == excel::XlLookAt::xlWhole )
3120                 bSearchWords = true;
3121             else
3122                 throw uno::RuntimeException(u"Range::Replace, illegal value for LookAt"_ustr );
3123             // set global search props ( affects the find dialog
3124             // and of course the defaults for this method
3125             newOptions.SetWordOnly( bSearchWords );
3126             xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
3127         }
3128         // sets SearchByRow ( true for Rows )
3129         if ( SearchOrder.hasValue() )
3130         {
3131             sal_Int16 nSearchOrder =  ::comphelper::getINT16( SearchOrder );
3132             bool bSearchByRow = false;
3133             if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
3134                 bSearchByRow = false;
3135             else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
3136                 bSearchByRow = true;
3137             else
3138                 throw uno::RuntimeException(u"Range::Replace, illegal value for SearchOrder"_ustr );
3139 
3140             newOptions.SetRowDirection( bSearchByRow );
3141             xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
3142         }
3143         if ( MatchCase.hasValue() )
3144         {
3145             bool bMatchCase = false;
3146 
3147             // SearchCaseSensitive
3148             MatchCase >>= bMatchCase;
3149             xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
3150         }
3151 
3152         ScGlobal::SetSearchItem( newOptions );
3153         // ignore MatchByte for the moment, it's not supported in
3154         // OOo.org afaik
3155 
3156         uno::Reference< container::XIndexAccess > xIndexAccess = xReplace->findAll( xDescriptor );
3157         xReplace->replaceAll( xDescriptor );
3158         if ( xIndexAccess.is() && xIndexAccess->getCount() > 0 )
3159         {
3160             for ( sal_Int32 i = 0; i < xIndexAccess->getCount(); ++i )
3161             {
3162                 uno::Reference< table::XCellRange > xCellRange( xIndexAccess->getByIndex( i ), uno::UNO_QUERY );
3163                 if ( xCellRange.is() )
3164                 {
3165                     uno::Reference< excel::XRange > xRange( new ScVbaRange( mxParent, mxContext, xCellRange ) );
3166                     uno::Reference< container::XEnumerationAccess > xEnumAccess( xRange, uno::UNO_QUERY_THROW );
3167                     uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
3168                     while ( xEnum->hasMoreElements() )
3169                     {
3170                         uno::Reference< excel::XRange > xNextRange( xEnum->nextElement(), uno::UNO_QUERY_THROW );
3171                         ScVbaRange* pRange = dynamic_cast< ScVbaRange * > ( xNextRange.get() );
3172                         if ( pRange )
3173                             pRange->fireChangeEvent();
3174                     }
3175                 }
3176             }
3177         }
3178     }
3179     return true; // always
3180 }
3181 
3182 uno::Reference< excel::XRange > SAL_CALL
Find(const uno::Any & What,const uno::Any & After,const uno::Any & LookIn,const uno::Any & LookAt,const uno::Any & SearchOrder,const uno::Any & SearchDirection,const uno::Any & MatchCase,const uno::Any &,const uno::Any &)3183 ScVbaRange::Find( const uno::Any& What, const uno::Any& After, const uno::Any& LookIn, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& SearchDirection, const uno::Any& MatchCase, const uno::Any& /*MatchByte*/, const uno::Any& /*SearchFormat*/ )
3184 {
3185     // return a Range object that represents the first cell where that information is found.
3186     OUString sWhat;
3187     sal_Int32 nWhat = 0;
3188     double fWhat = 0.0;
3189 
3190     // string.
3191     if( What >>= sWhat )
3192     {}
3193     else if( What >>= nWhat )
3194     {
3195         sWhat = OUString::number( nWhat );
3196     }
3197     else if( What >>= fWhat )
3198     {
3199         sWhat = OUString::number( fWhat );
3200     }
3201     else
3202         throw uno::RuntimeException(u"Range::Find, missing search-for-what param"_ustr );
3203 
3204     const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
3205     SvxSearchItem newOptions( globalSearchOptions );
3206 
3207     uno::Reference< util::XSearchable > xSearch( mxRange, uno::UNO_QUERY );
3208     if( xSearch.is() )
3209     {
3210         uno::Reference< util::XSearchDescriptor > xDescriptor = xSearch->createSearchDescriptor();
3211         xDescriptor->setSearchString(sWhat);
3212         xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
3213         xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
3214 
3215         uno::Reference< excel::XRange > xAfterRange;
3216         uno::Reference< table::XCellRange > xStartCell;
3217         if( After >>= xAfterRange )
3218         {
3219             // After must be a single cell in the range
3220             if( xAfterRange->getCount() > 1 )
3221                 throw uno::RuntimeException(u"After must be a single cell."_ustr );
3222             uno::Reference< excel::XRange > xCell( Cells( uno::Any( xAfterRange->getRow() ), uno::Any( xAfterRange->getColumn() ) ), uno::UNO_SET_THROW );
3223             xStartCell.set( xAfterRange->getCellRange(), uno::UNO_QUERY_THROW );
3224         }
3225 
3226         // LookIn
3227         if( LookIn.hasValue() )
3228         {
3229             sal_Int32 nLookIn = 0;
3230             if( LookIn >>= nLookIn )
3231             {
3232                 SvxSearchCellType nSearchType;
3233                 switch( nLookIn )
3234                 {
3235                     case excel::XlFindLookIn::xlComments :
3236                         nSearchType = SvxSearchCellType::NOTE; // Notes
3237                     break;
3238                     case excel::XlFindLookIn::xlFormulas :
3239                         nSearchType = SvxSearchCellType::FORMULA;
3240                     break;
3241                     case excel::XlFindLookIn::xlValues :
3242                         nSearchType = SvxSearchCellType::VALUE;
3243                     break;
3244                     default:
3245                         throw uno::RuntimeException(u"Range::Find, illegal value for LookIn."_ustr );
3246                 }
3247                 newOptions.SetCellType( nSearchType );
3248                 xDescriptor->setPropertyValue( u"SearchType"_ustr, uno::Any( static_cast<sal_uInt16>(nSearchType) ) );
3249             }
3250         }
3251 
3252         // LookAt
3253         if ( LookAt.hasValue() )
3254         {
3255             sal_Int16 nLookAt = ::comphelper::getINT16( LookAt );
3256             bool bSearchWords = false;
3257             if ( nLookAt == excel::XlLookAt::xlPart )
3258                 bSearchWords = false;
3259             else if ( nLookAt == excel::XlLookAt::xlWhole )
3260                 bSearchWords = true;
3261             else
3262                 throw uno::RuntimeException(u"Range::Find, illegal value for LookAt"_ustr );
3263             newOptions.SetWordOnly( bSearchWords );
3264             xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
3265         }
3266 
3267         // SearchOrder
3268         if ( SearchOrder.hasValue() )
3269         {
3270             sal_Int16 nSearchOrder =  ::comphelper::getINT16( SearchOrder );
3271             bool bSearchByRow = false;
3272             if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
3273                 bSearchByRow = false;
3274             else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
3275                 bSearchByRow = true;
3276             else
3277                 throw uno::RuntimeException(u"Range::Find, illegal value for SearchOrder"_ustr );
3278 
3279             newOptions.SetRowDirection( bSearchByRow );
3280             xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
3281         }
3282 
3283         // SearchDirection
3284         if ( SearchDirection.hasValue() )
3285         {
3286             sal_Int32 nSearchDirection = 0;
3287             if( SearchDirection >>= nSearchDirection )
3288             {
3289                 bool bSearchBackwards = false;
3290                 if ( nSearchDirection == excel::XlSearchDirection::xlNext )
3291                     bSearchBackwards = false;
3292                 else if( nSearchDirection == excel::XlSearchDirection::xlPrevious )
3293                     bSearchBackwards = true;
3294                 else
3295                     throw uno::RuntimeException(u"Range::Find, illegal value for SearchDirection"_ustr );
3296                 newOptions.SetBackward( bSearchBackwards );
3297                 xDescriptor->setPropertyValue( u"SearchBackwards"_ustr, uno::Any( bSearchBackwards ) );
3298             }
3299         }
3300 
3301         // MatchCase
3302         bool bMatchCase = false;
3303         if ( MatchCase.hasValue() )
3304         {
3305             // SearchCaseSensitive
3306             if( !( MatchCase >>= bMatchCase ) )
3307                 throw uno::RuntimeException(u"Range::Find illegal value for MatchCase"_ustr );
3308         }
3309         xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
3310 
3311         // MatchByte
3312         // SearchFormat
3313         // ignore
3314 
3315         ScGlobal::SetSearchItem( newOptions );
3316 
3317         uno::Reference< uno::XInterface > xInterface = xStartCell.is() ? xSearch->findNext( xStartCell, xDescriptor) : xSearch->findFirst( xDescriptor );
3318         uno::Reference< table::XCellRange > xCellRange( xInterface, uno::UNO_QUERY );
3319         // if we are searching from a starting cell and failed to find a match
3320         // then try from the beginning
3321         if ( !xCellRange.is() && xStartCell.is() )
3322         {
3323             xInterface = xSearch->findFirst( xDescriptor );
3324             xCellRange.set( xInterface, uno::UNO_QUERY );
3325         }
3326         if ( xCellRange.is() )
3327         {
3328             uno::Reference< excel::XRange > xResultRange = new ScVbaRange( mxParent, mxContext, xCellRange );
3329             if( xResultRange.is() )
3330             {
3331                 return xResultRange;
3332             }
3333         }
3334 
3335     }
3336 
3337     return uno::Reference< excel::XRange >();
3338 }
3339 
processKey(const uno::Any & Key,const uno::Reference<uno::XComponentContext> & xContext,ScDocShell * pDocSh)3340 static uno::Reference< table::XCellRange > processKey( const uno::Any& Key, const uno::Reference<  uno::XComponentContext >& xContext, ScDocShell* pDocSh )
3341 {
3342     uno::Reference< excel::XRange > xKeyRange;
3343     if (Key.getValueTypeClass() == css::uno::TypeClass_INTERFACE)
3344     {
3345         xKeyRange.set( Key, uno::UNO_QUERY_THROW );
3346     }
3347     else if ( Key.getValueType() == ::cppu::UnoType<OUString>::get()  )
3348 
3349     {
3350         OUString sRangeName = ::comphelper::getString( Key );
3351         table::CellRangeAddress  aRefAddr;
3352         if ( !pDocSh )
3353             throw uno::RuntimeException(u"Range::Sort no docshell to calculate key param"_ustr );
3354         xKeyRange = getRangeForName( xContext, sRangeName, pDocSh, aRefAddr );
3355     }
3356     else
3357         throw uno::RuntimeException(u"Range::Sort illegal type value for key param"_ustr );
3358     uno::Reference< table::XCellRange > xKey;
3359     xKey.set( xKeyRange->getCellRange(), uno::UNO_QUERY_THROW );
3360     return xKey;
3361 }
3362 
3363 // helper method for Sort
3364 /// @throws uno::RuntimeException
findSortPropertyIndex(const uno::Sequence<beans::PropertyValue> & props,const OUString & sPropName)3365 static sal_Int32 findSortPropertyIndex( const uno::Sequence< beans::PropertyValue >& props,
3366 const OUString& sPropName )
3367 {
3368     const beans::PropertyValue* pProp = std::find_if(props.begin(), props.end(),
3369         [&sPropName](const beans::PropertyValue& rProp) { return rProp.Name == sPropName; });
3370 
3371     if ( pProp == props.end() )
3372         throw uno::RuntimeException(u"Range::Sort unknown sort property"_ustr );
3373     return static_cast<sal_Int32>(std::distance(props.begin(), pProp));
3374 }
3375 
3376 // helper method for Sort
3377 /// @throws uno::RuntimeException
updateTableSortField(const uno::Reference<table::XCellRange> & xParentRange,const uno::Reference<table::XCellRange> & xColRowKey,sal_Int16 nOrder,table::TableSortField & aTableField,bool bIsSortColumn,bool bMatchCase)3378 static void updateTableSortField( const uno::Reference< table::XCellRange >& xParentRange,
3379     const uno::Reference< table::XCellRange >& xColRowKey, sal_Int16 nOrder,
3380     table::TableSortField& aTableField, bool bIsSortColumn, bool bMatchCase )
3381 {
3382         RangeHelper parentRange( xParentRange );
3383         RangeHelper colRowRange( xColRowKey );
3384 
3385         table::CellRangeAddress parentRangeAddress = parentRange.getCellRangeAddressable()->getRangeAddress();
3386 
3387         table::CellRangeAddress colRowKeyAddress = colRowRange.getCellRangeAddressable()->getRangeAddress();
3388 
3389         // make sure that upper left point of key range is within the
3390         // parent range
3391         if (
3392             ( bIsSortColumn || colRowKeyAddress.StartColumn < parentRangeAddress.StartColumn ||
3393               colRowKeyAddress.StartColumn > parentRangeAddress.EndColumn )
3394             &&
3395             ( !bIsSortColumn || colRowKeyAddress.StartRow < parentRangeAddress.StartRow ||
3396               colRowKeyAddress.StartRow > parentRangeAddress.EndRow  )
3397             )
3398             throw uno::RuntimeException(u"Illegal Key param"_ustr );
3399 
3400         //determine col/row index
3401         if ( bIsSortColumn )
3402             aTableField.Field = colRowKeyAddress.StartRow - parentRangeAddress.StartRow;
3403         else
3404             aTableField.Field = colRowKeyAddress.StartColumn - parentRangeAddress.StartColumn;
3405         aTableField.IsCaseSensitive = bMatchCase;
3406 
3407         if ( nOrder ==  excel::XlSortOrder::xlAscending )
3408             aTableField.IsAscending = true;
3409         else
3410             aTableField.IsAscending = false;
3411 
3412 
3413 }
3414 
3415 void SAL_CALL
Sort(const uno::Any & Key1,const uno::Any & Order1,const uno::Any & Key2,const uno::Any &,const uno::Any & Order2,const uno::Any & Key3,const uno::Any & Order3,const uno::Any & Header,const uno::Any & OrderCustom,const uno::Any & MatchCase,const uno::Any & Orientation,const uno::Any & SortMethod,const uno::Any & DataOption1,const uno::Any & DataOption2,const uno::Any & DataOption3)3416 ScVbaRange::Sort( const uno::Any& Key1, const uno::Any& Order1, const uno::Any& Key2, const uno::Any& /*Type*/, const uno::Any& Order2, const uno::Any& Key3, const uno::Any& Order3, const uno::Any& Header, const uno::Any& OrderCustom, const uno::Any& MatchCase, const uno::Any& Orientation, const uno::Any& SortMethod,  const uno::Any& DataOption1, const uno::Any& DataOption2, const uno::Any& DataOption3  )
3417 {
3418     // #TODO# #FIXME# can we do something with Type
3419     if ( m_Areas->getCount() > 1 )
3420         throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
3421 
3422     sal_Int16 nDataOption1 = excel::XlSortDataOption::xlSortNormal;
3423     sal_Int16 nDataOption2 = excel::XlSortDataOption::xlSortNormal;
3424     sal_Int16 nDataOption3 = excel::XlSortDataOption::xlSortNormal;
3425 
3426     ScDocument& rDoc = getScDocument();
3427 
3428     uno::Reference< table::XCellRange > xRangeCurrent;
3429     if (isSingleCellRange())
3430     {
3431         // Expand to CurrentRegion
3432         uno::Reference< excel::XRange > xCurrent( CurrentRegion());
3433         if (xCurrent.is())
3434         {
3435             const ScVbaRange* pRange = getImplementation( xCurrent );
3436             if (pRange)
3437                 xRangeCurrent = pRange->mxRange;
3438         }
3439     }
3440     if (!xRangeCurrent.is())
3441         xRangeCurrent = mxRange;
3442     RangeHelper thisRange( xRangeCurrent );
3443     table::CellRangeAddress thisRangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3444 
3445     ScSortParam aSortParam;
3446     SCTAB nTab = thisRangeAddress.Sheet;
3447     rDoc.GetSortParam( aSortParam, nTab );
3448 
3449     if ( DataOption1.hasValue() )
3450         DataOption1 >>= nDataOption1;
3451     if ( DataOption2.hasValue() )
3452         DataOption2 >>= nDataOption2;
3453     if ( DataOption3.hasValue() )
3454         DataOption3 >>= nDataOption3;
3455 
3456     // 1) #TODO #FIXME need to process DataOption[1..3] not used currently
3457     // 2) #TODO #FIXME need to refactor this ( below ) into an IsSingleCell() method
3458     uno::Reference< table::XColumnRowRange > xColumnRowRange(xRangeCurrent, uno::UNO_QUERY_THROW );
3459 
3460     // set up defaults
3461 
3462     sal_Int16 nOrder1 = aSortParam.maKeyState[0].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
3463     sal_Int16 nOrder2 = aSortParam.maKeyState[1].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
3464     sal_Int16 nOrder3 = aSortParam.maKeyState[2].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
3465 
3466     sal_Int16 nCustom = aSortParam.nUserIndex;
3467     sal_Int16 nSortMethod = excel::XlSortMethod::xlPinYin;
3468     bool bMatchCase = aSortParam.bCaseSens;
3469 
3470     // seems to work opposite to expected, see below
3471     sal_Int16 nOrientation = aSortParam.bByRow ?  excel::XlSortOrientation::xlSortColumns :  excel::XlSortOrientation::xlSortRows;
3472 
3473     if ( Orientation.hasValue() )
3474     {
3475         // Documentation says xlSortRows is default but that doesn't appear to be
3476         // the case. Also it appears that xlSortColumns is the default which
3477         // strangely enough sorts by Row
3478         nOrientation = ::comphelper::getINT16( Orientation );
3479         // persist new option to be next calls default
3480         if ( nOrientation == excel::XlSortOrientation::xlSortRows )
3481             aSortParam.bByRow = false;
3482         else
3483             aSortParam.bByRow = true;
3484 
3485     }
3486 
3487     bool bIsSortColumns=false; // sort by row
3488 
3489     if ( nOrientation == excel::XlSortOrientation::xlSortRows )
3490         bIsSortColumns = true;
3491     sal_Int16 nHeader = aSortParam.nCompatHeader;
3492     bool bContainsHeader = false;
3493 
3494     if ( Header.hasValue() )
3495     {
3496         nHeader = ::comphelper::getINT16( Header );
3497         aSortParam.nCompatHeader = nHeader;
3498     }
3499 
3500     if ( nHeader == excel::XlYesNoGuess::xlGuess )
3501     {
3502         bool bHasColHeader = rDoc.HasColHeader(  static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), static_cast< SCCOL >( thisRangeAddress.EndColumn ), static_cast< SCROW >( thisRangeAddress.EndRow ), static_cast< SCTAB >( thisRangeAddress.Sheet ));
3503         bool bHasRowHeader = rDoc.HasRowHeader(  static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), static_cast< SCCOL >( thisRangeAddress.EndColumn ), static_cast< SCROW >( thisRangeAddress.EndRow ), static_cast< SCTAB >( thisRangeAddress.Sheet ) );
3504         if ( bHasColHeader || bHasRowHeader )
3505             nHeader =  excel::XlYesNoGuess::xlYes;
3506         else
3507             nHeader =  excel::XlYesNoGuess::xlNo;
3508         aSortParam.nCompatHeader = nHeader;
3509     }
3510 
3511     if ( nHeader == excel::XlYesNoGuess::xlYes )
3512         bContainsHeader = true;
3513 
3514     if ( SortMethod.hasValue() )
3515     {
3516         nSortMethod = ::comphelper::getINT16( SortMethod );
3517     }
3518 
3519     if ( OrderCustom.hasValue() )
3520     {
3521         OrderCustom >>= nCustom;
3522         --nCustom; // 0-based in OOo
3523         aSortParam.nUserIndex = nCustom;
3524     }
3525 
3526     if ( MatchCase.hasValue() )
3527     {
3528         MatchCase >>= bMatchCase;
3529         aSortParam.bCaseSens = bMatchCase;
3530     }
3531 
3532     if ( Order1.hasValue() )
3533     {
3534         nOrder1 = ::comphelper::getINT16(Order1);
3535         if (  nOrder1 == excel::XlSortOrder::xlAscending )
3536             aSortParam.maKeyState[0].bAscending  = true;
3537         else
3538             aSortParam.maKeyState[0].bAscending  = false;
3539 
3540     }
3541     if ( Order2.hasValue() )
3542     {
3543         nOrder2 = ::comphelper::getINT16(Order2);
3544         if ( nOrder2 == excel::XlSortOrder::xlAscending )
3545             aSortParam.maKeyState[1].bAscending  = true;
3546         else
3547             aSortParam.maKeyState[1].bAscending  = false;
3548     }
3549     if ( Order3.hasValue() )
3550     {
3551         nOrder3 = ::comphelper::getINT16(Order3);
3552         if ( nOrder3 == excel::XlSortOrder::xlAscending )
3553             aSortParam.maKeyState[2].bAscending  = true;
3554         else
3555             aSortParam.maKeyState[2].bAscending  = false;
3556     }
3557 
3558     uno::Reference< table::XCellRange > xKey1;
3559     uno::Reference< table::XCellRange > xKey2;
3560     uno::Reference< table::XCellRange > xKey3;
3561     ScDocShell* pDocShell = getScDocShell();
3562     xKey1 = processKey( Key1, mxContext, pDocShell );
3563     if ( !xKey1.is() )
3564         throw uno::RuntimeException(u"Range::Sort needs a key1 param"_ustr );
3565 
3566     if ( Key2.hasValue() )
3567         xKey2 = processKey( Key2, mxContext, pDocShell );
3568     if ( Key3.hasValue() )
3569         xKey3 = processKey( Key3, mxContext, pDocShell );
3570 
3571     uno::Reference< util::XSortable > xSort( xRangeCurrent, uno::UNO_QUERY_THROW );
3572     uno::Sequence< beans::PropertyValue > sortDescriptor = xSort->createSortDescriptor();
3573     auto psortDescriptor = sortDescriptor.getArray();
3574     sal_Int32 nTableSortFieldIndex = findSortPropertyIndex( sortDescriptor, u"SortFields"_ustr );
3575 
3576     uno::Sequence< table::TableSortField > sTableFields(1);
3577     sal_Int32 nTableIndex = 0;
3578     updateTableSortField(  xRangeCurrent, xKey1, nOrder1, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
3579 
3580     if ( xKey2.is() )
3581     {
3582         sTableFields.realloc( sTableFields.getLength() + 1 );
3583         updateTableSortField(  xRangeCurrent, xKey2, nOrder2, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
3584     }
3585     if ( xKey3.is()  )
3586     {
3587         sTableFields.realloc( sTableFields.getLength() + 1 );
3588         updateTableSortField(  xRangeCurrent, xKey3, nOrder3, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
3589     }
3590     psortDescriptor[ nTableSortFieldIndex ].Value <<= sTableFields;
3591 
3592     sal_Int32 nIndex =  findSortPropertyIndex( sortDescriptor,  u"IsSortColumns"_ustr );
3593     psortDescriptor[ nIndex ].Value <<= bIsSortColumns;
3594 
3595     nIndex =    findSortPropertyIndex( sortDescriptor, u"ContainsHeader"_ustr );
3596     psortDescriptor[ nIndex ].Value <<= bContainsHeader;
3597 
3598     rDoc.SetSortParam( aSortParam, nTab );
3599     xSort->sort( sortDescriptor );
3600 
3601     // #FIXME #TODO
3602     // The SortMethod param is not processed ( not sure what its all about, need to
3603     (void)nSortMethod;
3604 }
3605 
3606 uno::Reference< excel::XRange > SAL_CALL
End(::sal_Int32 Direction)3607 ScVbaRange::End( ::sal_Int32 Direction )
3608 {
3609     if ( m_Areas->getCount() > 1 )
3610     {
3611         uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
3612         return xRange->End( Direction );
3613     }
3614 
3615     // #FIXME #TODO
3616     // euch! found my orig implementation sucked, so
3617     // trying this even sucker one (really need to use/expose code in
3618     // around  ScTabView::MoveCursorArea(), that's the bit that calculates
3619     // where the cursor should go)
3620     // Main problem with this method is the ultra hacky attempt to preserve
3621     // the ActiveCell, there should be no need to go to these extremes
3622 
3623     // Save ActiveSheet/ActiveCell pos (to restore later)
3624     uno::Any aDft;
3625     uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
3626     uno::Reference< excel::XWorksheet > sActiveSheet = xApplication->getActiveSheet();
3627     OUString sActiveCell = xApplication->getActiveCell()->Address(aDft, aDft, aDft, aDft, aDft );
3628 
3629     // position current cell upper left of this range
3630     Cells( uno::Any( sal_Int32(1) ), uno::Any( sal_Int32(1) ) )->Select();
3631 
3632     uno::Reference< frame::XModel > xModel = getModelFromRange( mxRange );
3633 
3634     SfxViewFrame* pViewFrame = excel::getViewFrame( xModel );
3635     if ( pViewFrame )
3636     {
3637         SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
3638         // Hoping this will make sure this slot is called
3639         // synchronously
3640         SfxBoolItem sfxAsync( SID_ASYNCHRON, false );
3641         aArgs.Put( sfxAsync );
3642         SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher();
3643 
3644         sal_uInt16 nSID = 0;
3645 
3646         switch( Direction )
3647         {
3648             case excel::XlDirection::xlDown:
3649                 nSID = SID_CURSORBLKDOWN;
3650                 break;
3651             case excel::XlDirection::xlUp:
3652                 nSID = SID_CURSORBLKUP;
3653                 break;
3654             case excel::XlDirection::xlToLeft:
3655                 nSID = SID_CURSORBLKLEFT;
3656                 break;
3657             case excel::XlDirection::xlToRight:
3658                 nSID = SID_CURSORBLKRIGHT;
3659                 break;
3660             default:
3661                 throw uno::RuntimeException(u": Invalid ColumnIndex"_ustr );
3662         }
3663         if ( pDispatcher )
3664         {
3665             pDispatcher->Execute( nSID, SfxCallMode::SYNCHRON, aArgs );
3666         }
3667     }
3668 
3669     // result is the ActiveCell
3670     OUString sMoved =    xApplication->getActiveCell()->Address(aDft, aDft, aDft, aDft, aDft );
3671 
3672     uno::Any aVoid;
3673     uno::Reference< excel::XRange > resultCell;
3674     resultCell.set( xApplication->getActiveSheet()->Range( uno::Any( sMoved ), aVoid ), uno::UNO_SET_THROW );
3675 
3676     // restore old ActiveCell
3677     uno::Reference< excel::XRange > xOldActiveCell( sActiveSheet->Range( uno::Any( sActiveCell ), aVoid ), uno::UNO_SET_THROW );
3678     xOldActiveCell->Select();
3679 
3680 
3681     // return result
3682     return resultCell;
3683 }
3684 
3685 bool
isSingleCellRange() const3686 ScVbaRange::isSingleCellRange() const
3687 {
3688     uno::Reference< sheet::XCellRangeAddressable > xAddressable( mxRange, uno::UNO_QUERY );
3689     if ( xAddressable.is() )
3690     {
3691         table::CellRangeAddress aRangeAddr = xAddressable->getRangeAddress();
3692         return ( aRangeAddr.EndColumn == aRangeAddr.StartColumn && aRangeAddr.EndRow == aRangeAddr.StartRow );
3693     }
3694     return false;
3695 }
3696 
3697 uno::Reference< excel::XCharacters > SAL_CALL
characters(const uno::Any & Start,const uno::Any & Length)3698 ScVbaRange::characters( const uno::Any& Start, const uno::Any& Length )
3699 {
3700     if ( !isSingleCellRange() )
3701         throw uno::RuntimeException(u"Can't create Characters property for multicell range "_ustr );
3702     uno::Reference< text::XSimpleText > xSimple(mxRange->getCellByPosition(0,0) , uno::UNO_QUERY_THROW );
3703     ScDocument& rDoc = getDocumentFromRange(mxRange);
3704 
3705     ScVbaPalette aPalette( rDoc.GetDocumentShell() );
3706     return new ScVbaCharacters( this, mxContext, aPalette, xSimple, Start, Length );
3707 }
3708 
3709  void SAL_CALL
Delete(const uno::Any & Shift)3710 ScVbaRange::Delete( const uno::Any& Shift )
3711 {
3712     if ( m_Areas->getCount() > 1 )
3713     {
3714         sal_Int32 nItems = m_Areas->getCount();
3715         for ( sal_Int32 index=1; index <= nItems; ++index )
3716         {
3717             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
3718             xRange->Delete( Shift );
3719         }
3720         return;
3721     }
3722     sheet::CellDeleteMode mode = sheet::CellDeleteMode_NONE ;
3723     RangeHelper thisRange( mxRange );
3724     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3725     if ( Shift.hasValue() )
3726     {
3727         sal_Int32 nShift = 0;
3728         Shift >>= nShift;
3729         switch ( nShift )
3730         {
3731             case excel::XlDeleteShiftDirection::xlShiftUp:
3732                 mode = sheet::CellDeleteMode_UP;
3733                 break;
3734             case excel::XlDeleteShiftDirection::xlShiftToLeft:
3735                 mode = sheet::CellDeleteMode_LEFT;
3736                 break;
3737             default:
3738                 throw uno::RuntimeException(u"Illegal parameter "_ustr );
3739         }
3740     }
3741     else
3742     {
3743         ScDocument& rDoc = getScDocument();
3744         bool bFullRow = ( thisAddress.StartColumn == 0 && thisAddress.EndColumn == rDoc.MaxCol() );
3745         sal_Int32 nCols = thisAddress.EndColumn - thisAddress.StartColumn;
3746         sal_Int32 nRows = thisAddress.EndRow - thisAddress.StartRow;
3747         if ( mbIsRows || bFullRow || ( nCols >=  nRows ) )
3748             mode = sheet::CellDeleteMode_UP;
3749         else
3750             mode = sheet::CellDeleteMode_LEFT;
3751     }
3752     uno::Reference< sheet::XCellRangeMovement > xCellRangeMove( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
3753     xCellRangeMove->removeRange( thisAddress, mode );
3754 
3755 }
3756 
3757 //XElementAccess
3758 sal_Bool SAL_CALL
hasElements()3759 ScVbaRange::hasElements()
3760 {
3761     uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY );
3762     if ( xColumnRowRange.is() )
3763         if ( xColumnRowRange->getRows()->getCount() ||
3764             xColumnRowRange->getColumns()->getCount() )
3765             return true;
3766     return false;
3767 }
3768 
3769 // XEnumerationAccess
3770 uno::Reference< container::XEnumeration > SAL_CALL
createEnumeration()3771 ScVbaRange::createEnumeration()
3772 {
3773     if ( mbIsColumns || mbIsRows )
3774     {
3775         uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY );
3776         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
3777         sal_Int32 nElems = 0;
3778         if ( mbIsColumns )
3779             nElems = xColumnRowRange->getColumns()->getCount();
3780         else
3781             nElems = xColumnRowRange->getRows()->getCount();
3782         return new ColumnsRowEnumeration( xRange, nElems );
3783 
3784     }
3785     return new CellsEnumeration( mxParent, mxContext, m_Areas );
3786 }
3787 
3788 OUString SAL_CALL
getDefaultMethodName()3789 ScVbaRange::getDefaultMethodName(  )
3790 {
3791     return u"Item"_ustr;
3792 }
3793 
3794 // returns calc internal col. width ( in points )
3795 double
getCalcColWidth(const table::CellRangeAddress & rAddress)3796 ScVbaRange::getCalcColWidth(const table::CellRangeAddress& rAddress)
3797 {
3798     ScDocument& rDoc = getScDocument();
3799     sal_uInt16 nWidth = rDoc.GetOriginalWidth( static_cast< SCCOL >( rAddress.StartColumn ), static_cast< SCTAB >( rAddress.Sheet ) );
3800     double nPoints = lcl_TwipsToPoints( nWidth );
3801     nPoints = lcl_Round2DecPlaces( nPoints );
3802     return nPoints;
3803 }
3804 
3805 double
getCalcRowHeight(const table::CellRangeAddress & rAddress)3806 ScVbaRange::getCalcRowHeight(const table::CellRangeAddress& rAddress)
3807 {
3808     ScDocument& rDoc = getDocumentFromRange( mxRange );
3809     sal_uInt16 nWidth = rDoc.GetOriginalHeight( rAddress.StartRow, rAddress.Sheet );
3810     double nPoints = lcl_TwipsToPoints( nWidth );
3811     nPoints = lcl_Round2DecPlaces( nPoints );
3812     return nPoints;
3813 }
3814 
3815 // return Char Width in points
getDefaultCharWidth(ScDocShell * pDocShell)3816 static double getDefaultCharWidth( ScDocShell* pDocShell )
3817 {
3818     ScDocument& rDoc = pDocShell->GetDocument();
3819     OutputDevice* pRefDevice = rDoc.GetRefDevice();
3820     vcl::Font aDefFont;
3821     rDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aDefFont, pRefDevice);
3822     pRefDevice->SetFont(aDefFont);
3823     tools::Long nCharWidth = pRefDevice->GetTextWidth( OUString( '0' ) );        // 1/100th mm
3824     return o3tl::convert<double>(nCharWidth, o3tl::Length::mm100, o3tl::Length::pt);
3825 }
3826 
3827 uno::Any SAL_CALL
getColumnWidth()3828 ScVbaRange::getColumnWidth()
3829 {
3830     sal_Int32 nLen = m_Areas->getCount();
3831     if ( nLen > 1 )
3832     {
3833         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
3834         return xRange->getColumnWidth();
3835     }
3836 
3837     double nColWidth =  0;
3838     ScDocShell* pShell = getScDocShell();
3839     if ( pShell )
3840     {
3841         double defaultCharWidth = getDefaultCharWidth( pShell );
3842         RangeHelper thisRange( mxRange );
3843         table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3844         sal_Int32 nStartCol = thisAddress.StartColumn;
3845         sal_Int32 nEndCol = thisAddress.EndColumn;
3846         sal_uInt16 nColTwips = 0;
3847         for( sal_Int32 nCol = nStartCol ; nCol <= nEndCol; ++nCol )
3848         {
3849             thisAddress.StartColumn = nCol;
3850             sal_uInt16 nCurTwips = pShell->GetDocument().GetOriginalWidth( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCTAB >( thisAddress.Sheet ) );
3851             if ( nCol == nStartCol )
3852                 nColTwips =  nCurTwips;
3853             if ( nColTwips != nCurTwips )
3854                 return aNULL();
3855         }
3856         nColWidth = lcl_TwipsToPoints( nColTwips );
3857         if ( nColWidth != 0.0 )
3858             nColWidth = ( nColWidth / defaultCharWidth ) - fExtraWidth;
3859     }
3860     nColWidth = lcl_Round2DecPlaces( nColWidth );
3861     return uno::Any( nColWidth );
3862 }
3863 
3864 void SAL_CALL
setColumnWidth(const uno::Any & _columnwidth)3865 ScVbaRange::setColumnWidth( const uno::Any& _columnwidth )
3866 {
3867     sal_Int32 nLen = m_Areas->getCount();
3868     if ( nLen > 1 )
3869     {
3870         for ( sal_Int32 index = 1; index != nLen; ++index )
3871         {
3872             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
3873             xRange->setColumnWidth( _columnwidth );
3874         }
3875         return;
3876     }
3877     double nColWidth = 0;
3878     _columnwidth >>= nColWidth;
3879     nColWidth = lcl_Round2DecPlaces( nColWidth );
3880     ScDocShell* pDocShell = getScDocShell();
3881     if ( !pDocShell )
3882         return;
3883 
3884     if ( nColWidth != 0.0 )
3885         nColWidth = ( nColWidth + fExtraWidth ) * getDefaultCharWidth( pDocShell );
3886     RangeHelper thisRange( mxRange );
3887     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3888     sal_uInt16 nTwips = lcl_pointsToTwips( nColWidth );
3889 
3890     std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(thisAddress.StartColumn, thisAddress.EndColumn));
3891     // #163561# use mode SC_SIZE_DIRECT: hide for width 0, show for other values
3892     pDocShell->GetDocFunc().SetWidthOrHeight(
3893         true, aColArr, thisAddress.Sheet, SC_SIZE_DIRECT, nTwips, true, true);
3894 }
3895 
3896 uno::Any SAL_CALL
getWidth()3897 ScVbaRange::getWidth()
3898 {
3899     if ( m_Areas->getCount() > 1 )
3900     {
3901         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
3902         return xRange->getWidth();
3903     }
3904     uno::Reference< table::XColumnRowRange > xColRowRange( mxRange, uno::UNO_QUERY_THROW );
3905     uno::Reference< container::XIndexAccess > xIndexAccess( xColRowRange->getColumns(), uno::UNO_QUERY_THROW );
3906     sal_Int32 nElems = xIndexAccess->getCount();
3907     double nWidth = 0;
3908     for ( sal_Int32 index=0; index<nElems; ++index )
3909     {
3910         uno::Reference< sheet::XCellRangeAddressable > xAddressable( xIndexAccess->getByIndex( index ), uno::UNO_QUERY_THROW );
3911         double nTmpWidth = getCalcColWidth( xAddressable->getRangeAddress() );
3912         nWidth += nTmpWidth;
3913     }
3914     return uno::Any( nWidth );
3915 }
3916 
3917 uno::Any SAL_CALL
Areas(const uno::Any & item)3918 ScVbaRange::Areas( const uno::Any& item)
3919 {
3920     if ( !item.hasValue() )
3921         return uno::Any( uno::Reference< ov::XCollection >(m_Areas) );
3922     return m_Areas->Item( item, uno::Any() );
3923 }
3924 
3925 uno::Reference< excel::XRange >
getArea(sal_Int32 nIndex)3926 ScVbaRange::getArea( sal_Int32 nIndex )
3927 {
3928     if ( !m_Areas.is() )
3929         throw uno::RuntimeException(u"No areas available"_ustr );
3930     uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( ++nIndex ), uno::Any() ), uno::UNO_QUERY_THROW );
3931     return xRange;
3932 }
3933 
3934 uno::Any
Borders(const uno::Any & item)3935 ScVbaRange::Borders( const uno::Any& item )
3936 {
3937     if ( !item.hasValue() )
3938         return uno::Any( getBorders() );
3939     return getBorders()->Item( item, uno::Any() );
3940 }
3941 
3942 uno::Any SAL_CALL
BorderAround(const css::uno::Any & LineStyle,const css::uno::Any & Weight,const css::uno::Any & ColorIndex,const css::uno::Any & Color)3943 ScVbaRange::BorderAround( const css::uno::Any& LineStyle, const css::uno::Any& Weight,
3944                 const css::uno::Any& ColorIndex, const css::uno::Any& Color )
3945 {
3946     sal_Int32 nCount = getBorders()->getCount();
3947 
3948     for( sal_Int32 i = 0; i < nCount; i++ )
3949     {
3950         const sal_Int32 nLineType = supportedIndexTable[i];
3951         switch( nLineType )
3952         {
3953             case excel::XlBordersIndex::xlEdgeLeft:
3954             case excel::XlBordersIndex::xlEdgeTop:
3955             case excel::XlBordersIndex::xlEdgeBottom:
3956             case excel::XlBordersIndex::xlEdgeRight:
3957             {
3958                 uno::Reference< excel::XBorder > xBorder( m_Borders->Item( uno::Any( nLineType ), uno::Any() ), uno::UNO_QUERY_THROW );
3959                 if( LineStyle.hasValue() )
3960                 {
3961                     xBorder->setLineStyle( LineStyle );
3962                 }
3963                 if( Weight.hasValue() )
3964                 {
3965                     xBorder->setWeight( Weight );
3966                 }
3967                 if( ColorIndex.hasValue() )
3968                 {
3969                     xBorder->setColorIndex( ColorIndex );
3970                 }
3971                 if( Color.hasValue() )
3972                 {
3973                     xBorder->setColor( Color );
3974                 }
3975                 break;
3976             }
3977             case excel::XlBordersIndex::xlInsideVertical:
3978             case excel::XlBordersIndex::xlInsideHorizontal:
3979             case excel::XlBordersIndex::xlDiagonalDown:
3980             case excel::XlBordersIndex::xlDiagonalUp:
3981                 break;
3982             default:
3983                 return uno::Any( false );
3984         }
3985     }
3986     return uno::Any( true );
3987 }
3988 
3989 uno::Any SAL_CALL
getRowHeight()3990 ScVbaRange::getRowHeight()
3991 {
3992     sal_Int32 nLen = m_Areas->getCount();
3993     if ( nLen > 1 )
3994     {
3995         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
3996         return xRange->getRowHeight();
3997     }
3998 
3999     // if any row's RowHeight in the
4000     // range is different from any other, then return NULL
4001     RangeHelper thisRange( mxRange );
4002     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4003 
4004     sal_Int32 nStartRow = thisAddress.StartRow;
4005     sal_Int32 nEndRow = thisAddress.EndRow;
4006     sal_uInt16 nRowTwips = 0;
4007     // #TODO probably possible to use the SfxItemSet (and see if
4008     //  SfxItemState::INVALID is set) to improve performance
4009 // #CHECKME looks like this is general behaviour not just row Range specific
4010 //  if ( mbIsRows )
4011     ScDocShell* pShell = getScDocShell();
4012     if ( pShell )
4013     {
4014         for ( sal_Int32 nRow = nStartRow ; nRow <= nEndRow; ++nRow )
4015         {
4016             thisAddress.StartRow = nRow;
4017             sal_uInt16 nCurTwips = pShell->GetDocument().GetOriginalHeight( thisAddress.StartRow, thisAddress.Sheet );
4018             if ( nRow == nStartRow )
4019                 nRowTwips = nCurTwips;
4020             if ( nRowTwips != nCurTwips )
4021                 return aNULL();
4022         }
4023     }
4024     double nHeight = lcl_Round2DecPlaces( lcl_TwipsToPoints( nRowTwips ) );
4025     return uno::Any( nHeight );
4026 }
4027 
4028 void SAL_CALL
setRowHeight(const uno::Any & _rowheight)4029 ScVbaRange::setRowHeight( const uno::Any& _rowheight)
4030 {
4031     sal_Int32 nLen = m_Areas->getCount();
4032     if ( nLen > 1 )
4033     {
4034         for ( sal_Int32 index = 1; index != nLen; ++index )
4035         {
4036             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
4037             xRange->setRowHeight( _rowheight );
4038         }
4039         return;
4040     }
4041     double nHeight = 0; // Incoming height is in points
4042     _rowheight >>= nHeight;
4043     nHeight = lcl_Round2DecPlaces( nHeight );
4044     RangeHelper thisRange( mxRange );
4045     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4046     sal_uInt16 nTwips = lcl_pointsToTwips( nHeight );
4047 
4048     ScDocShell* pDocShell = getDocShellFromRange( mxRange );
4049     std::vector<sc::ColRowSpan> aRowArr(1, sc::ColRowSpan(thisAddress.StartRow, thisAddress.EndRow));
4050     pDocShell->GetDocFunc().SetWidthOrHeight(
4051         false, aRowArr, thisAddress.Sheet, SC_SIZE_ORIGINAL, nTwips, true, true);
4052 }
4053 
4054 uno::Any SAL_CALL
getPageBreak()4055 ScVbaRange::getPageBreak()
4056 {
4057     sal_Int32 nPageBreak = excel::XlPageBreak::xlPageBreakNone;
4058     ScDocShell* pShell = getDocShellFromRange( mxRange );
4059     if ( pShell )
4060     {
4061         RangeHelper thisRange( mxRange );
4062         table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4063         bool bColumn = false;
4064 
4065         if (thisAddress.StartRow==0)
4066             bColumn = true;
4067 
4068         uno::Reference< frame::XModel > xModel = pShell->GetModel();
4069         if ( xModel.is() )
4070         {
4071             ScDocument& rDoc = getDocumentFromRange( mxRange );
4072 
4073             ScBreakType nBreak = ScBreakType::NONE;
4074             if ( !bColumn )
4075                 nBreak = rDoc.HasRowBreak(thisAddress.StartRow, thisAddress.Sheet);
4076             else
4077                 nBreak = rDoc.HasColBreak(thisAddress.StartColumn, thisAddress.Sheet);
4078 
4079             if (nBreak & ScBreakType::Page)
4080                 nPageBreak = excel::XlPageBreak::xlPageBreakAutomatic;
4081 
4082             if (nBreak & ScBreakType::Manual)
4083                 nPageBreak = excel::XlPageBreak::xlPageBreakManual;
4084         }
4085     }
4086 
4087     return uno::Any( nPageBreak );
4088 }
4089 
4090 void SAL_CALL
setPageBreak(const uno::Any & _pagebreak)4091 ScVbaRange::setPageBreak( const uno::Any& _pagebreak)
4092 {
4093     sal_Int32 nPageBreak = 0;
4094     _pagebreak >>= nPageBreak;
4095 
4096     ScDocShell* pShell = getDocShellFromRange( mxRange );
4097     if ( !pShell )
4098         return;
4099 
4100     RangeHelper thisRange( mxRange );
4101     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4102     if ((thisAddress.StartColumn==0) && (thisAddress.StartRow==0))
4103         return;
4104     bool bColumn = false;
4105 
4106     if (thisAddress.StartRow==0)
4107         bColumn = true;
4108 
4109     ScAddress aAddr( static_cast<SCCOL>(thisAddress.StartColumn), thisAddress.StartRow, thisAddress.Sheet );
4110     uno::Reference< frame::XModel > xModel = pShell->GetModel();
4111     if ( xModel.is() )
4112     {
4113         ScTabViewShell* pViewShell = excel::getBestViewShell( xModel );
4114         if ( nPageBreak == excel::XlPageBreak::xlPageBreakManual )
4115             pViewShell->InsertPageBreak( bColumn, true, &aAddr);
4116         else if ( nPageBreak == excel::XlPageBreak::xlPageBreakNone )
4117             pViewShell->DeletePageBreak( bColumn, true, &aAddr);
4118     }
4119 }
4120 
4121 uno::Any SAL_CALL
getHeight()4122 ScVbaRange::getHeight()
4123 {
4124     if ( m_Areas->getCount() > 1 )
4125     {
4126         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
4127         return xRange->getHeight();
4128     }
4129 
4130     uno::Reference< table::XColumnRowRange > xColRowRange( mxRange, uno::UNO_QUERY_THROW );
4131     uno::Reference< container::XIndexAccess > xIndexAccess( xColRowRange->getRows(), uno::UNO_QUERY_THROW );
4132     sal_Int32 nElems = xIndexAccess->getCount();
4133     double nHeight = 0;
4134     for ( sal_Int32 index=0; index<nElems; ++index )
4135     {
4136         uno::Reference< sheet::XCellRangeAddressable > xAddressable( xIndexAccess->getByIndex( index ), uno::UNO_QUERY_THROW );
4137         nHeight += getCalcRowHeight(xAddressable->getRangeAddress() );
4138     }
4139     return uno::Any( nHeight );
4140 }
4141 
4142 awt::Point
getPosition() const4143 ScVbaRange::getPosition() const
4144 {
4145     awt::Point aPoint;
4146     uno::Reference< beans::XPropertySet > xProps;
4147     if ( mxRange.is() )
4148         xProps.set( mxRange, uno::UNO_QUERY_THROW );
4149     else
4150         xProps.set( mxRanges, uno::UNO_QUERY_THROW );
4151     xProps->getPropertyValue( u"Position"_ustr ) >>= aPoint;
4152     return aPoint;
4153 }
4154 uno::Any SAL_CALL
getLeft()4155 ScVbaRange::getLeft()
4156 {
4157     // helperapi returns the first ranges left ( and top below )
4158     if ( m_Areas->getCount() > 1 )
4159         return getArea( 0 )->getLeft();
4160     awt::Point aPoint = getPosition();
4161     return uno::Any(o3tl::convert<double>(aPoint.X, o3tl::Length::mm100, o3tl::Length::pt));
4162 }
4163 
4164 uno::Any SAL_CALL
getTop()4165 ScVbaRange::getTop()
4166 {
4167     // helperapi returns the first ranges top
4168     if ( m_Areas->getCount() > 1 )
4169         return getArea( 0 )->getTop();
4170     awt::Point aPoint= getPosition();
4171     return uno::Any(o3tl::convert<double>(aPoint.Y, o3tl::Length::mm100, o3tl::Length::pt));
4172 }
4173 
getNamedRange(const uno::Reference<uno::XInterface> & xIf,const uno::Reference<table::XCellRange> & thisRange)4174 static uno::Reference< sheet::XCellRangeReferrer > getNamedRange( const uno::Reference< uno::XInterface >& xIf, const uno::Reference< table::XCellRange >& thisRange )
4175 {
4176     uno::Reference< beans::XPropertySet > xProps( xIf, uno::UNO_QUERY_THROW );
4177     uno::Reference< container::XNameAccess > xNameAccess( xProps->getPropertyValue( u"NamedRanges"_ustr ), uno::UNO_QUERY_THROW );
4178 
4179     const uno::Sequence< OUString > sNames = xNameAccess->getElementNames();
4180 //    uno::Reference< table::XCellRange > thisRange( getCellRange(), uno::UNO_QUERY_THROW );
4181     uno::Reference< sheet::XCellRangeReferrer > xNamedRange;
4182     for ( const auto& rName : sNames )
4183     {
4184         uno::Reference< sheet::XCellRangeReferrer > xName( xNameAccess->getByName( rName ), uno::UNO_QUERY );
4185         if ( xName.is() )
4186         {
4187             if ( thisRange == xName->getReferredCells() )
4188             {
4189                 xNamedRange = std::move(xName);
4190                 break;
4191             }
4192         }
4193     }
4194     return xNamedRange;
4195 }
4196 
4197 uno::Reference< excel::XName >
getName()4198 ScVbaRange::getName()
4199 {
4200     uno::Reference< beans::XPropertySet > xProps( getUnoModel(), uno::UNO_QUERY );
4201     uno::Reference< table::XCellRange > thisRange( getCellRange(), uno::UNO_QUERY_THROW );
4202     // Application range
4203     uno::Reference< sheet::XCellRangeReferrer > xNamedRange = getNamedRange( xProps, thisRange );
4204 
4205     if ( !xNamedRange.is() )
4206     {
4207         // not in application range then assume it might be in
4208         // sheet namedranges
4209         RangeHelper aRange( thisRange );
4210         uno::Reference< sheet::XSpreadsheet > xSheet = aRange.getSpreadSheet();
4211         xProps.set( xSheet, uno::UNO_QUERY );
4212         // impl here
4213         xNamedRange = getNamedRange( xProps, thisRange );
4214     }
4215     if ( xProps.is() && xNamedRange.is() )
4216     {
4217         uno::Reference< sheet::XNamedRanges > xNamedRanges( xProps, uno::UNO_QUERY_THROW );
4218         uno::Reference< sheet::XNamedRange > xName( xNamedRange, uno::UNO_QUERY_THROW );
4219         return new ScVbaName( mxParent, mxContext, xName, xNamedRanges, getUnoModel() );
4220     }
4221     return uno::Reference< excel::XName >();
4222 }
4223 
4224 uno::Reference< excel::XWorksheet >
getWorksheet()4225 ScVbaRange::getWorksheet()
4226 {
4227     // #TODO #FIXME parent should always be set up ( currently that's not
4228     // the case )
4229     uno::Reference< excel::XWorksheet > xSheet( getParent(), uno::UNO_QUERY );
4230     if ( !xSheet.is() )
4231     {
4232         uno::Reference< table::XCellRange > xRange = mxRange;
4233 
4234         if ( mxRanges.is() ) // assign xRange to first range
4235         {
4236             uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
4237             xRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
4238         }
4239         ScDocShell* pDocShell = getDocShellFromRange(xRange);
4240         RangeHelper rHelper(xRange);
4241         // parent should be Thisworkbook
4242         xSheet.set( new ScVbaWorksheet( uno::Reference< XHelperInterface >(), mxContext,rHelper.getSpreadSheet(),pDocShell->GetModel()) );
4243     }
4244     return xSheet;
4245 }
4246 
4247 // #TODO remove this ugly application processing
4248 // Process an application Range request e.g. 'Range("a1,b2,a4:b6")
4249 uno::Reference< excel::XRange >
ApplicationRange(const uno::Reference<uno::XComponentContext> & xContext,const css::uno::Any & Cell1,const css::uno::Any & Cell2)4250 ScVbaRange::ApplicationRange( const uno::Reference< uno::XComponentContext >& xContext, const css::uno::Any &Cell1, const css::uno::Any &Cell2 )
4251 {
4252     // Although the documentation seems clear that Range without a
4253     // qualifier then it's a shortcut for ActiveSheet.Range
4254     // however, similarly Application.Range is apparently also a
4255     // shortcut for ActiveSheet.Range
4256     // The is however a subtle behavioural difference I've come across
4257     // wrt to named ranges.
4258     // If a named range "test" exists { Sheet1!$A1 } and the active sheet
4259     // is Sheet2 then the following will fail
4260     // msgbox ActiveSheet.Range("test").Address ' fails
4261     // msgbox WorkSheets("Sheet2").Range("test").Address
4262     // but!!!
4263     // msgbox Range("test").Address ' works
4264     // msgbox Application.Range("test").Address ' works
4265 
4266     // Single param Range
4267     OUString sRangeName;
4268     Cell1 >>= sRangeName;
4269     if ( Cell1.hasValue() && !Cell2.hasValue() && !sRangeName.isEmpty() )
4270     {
4271         uno::Reference< beans::XPropertySet > xPropSet( getCurrentExcelDoc(xContext), uno::UNO_QUERY_THROW );
4272 
4273         uno::Reference< container::XNameAccess > xNamed( xPropSet->getPropertyValue( u"NamedRanges"_ustr ), uno::UNO_QUERY_THROW );
4274         uno::Reference< sheet::XCellRangeReferrer > xReferrer;
4275         try
4276         {
4277             xReferrer.set ( xNamed->getByName( sRangeName ), uno::UNO_QUERY );
4278         }
4279         catch( uno::Exception& /*e*/ )
4280         {
4281             // do nothing
4282         }
4283         if ( xReferrer.is() )
4284         {
4285             uno::Reference< table::XCellRange > xRange = xReferrer->getReferredCells();
4286             if ( xRange.is() )
4287             {
4288                 uno::Reference< excel::XRange > xVbRange =  new ScVbaRange( excel::getUnoSheetModuleObj( xRange ), xContext, xRange );
4289                 return xVbRange;
4290             }
4291         }
4292     }
4293 
4294     uno::Reference<table::XCellRange> xSheetRange;
4295 
4296     try
4297     {
4298         uno::Reference<sheet::XSpreadsheetView> xView(
4299             getCurrentExcelDoc(xContext)->getCurrentController(), uno::UNO_QUERY_THROW);
4300 
4301         xSheetRange.set(xView->getActiveSheet(), uno::UNO_QUERY_THROW);
4302     }
4303     catch (const uno::Exception&)
4304     {
4305         return uno::Reference<excel::XRange>();
4306     }
4307 
4308     rtl::Reference<ScVbaRange> pRange = new ScVbaRange( excel::getUnoSheetModuleObj( xSheetRange ), xContext, xSheetRange );
4309     return pRange->Range( Cell1, Cell2, true );
4310 }
4311 
4312 // Helper functions for AutoFilter
lcl_GetDBData_Impl(ScDocShell * pDocShell,sal_Int16 nSheet)4313 static ScDBData* lcl_GetDBData_Impl( ScDocShell* pDocShell, sal_Int16 nSheet )
4314 {
4315     ScDBData* pRet = nullptr;
4316     if (pDocShell)
4317     {
4318         pRet = pDocShell->GetDocument().GetAnonymousDBData(nSheet);
4319     }
4320     return pRet;
4321 }
4322 
lcl_SelectAll(ScDocShell * pDocShell,const ScQueryParam & aParam)4323 static void lcl_SelectAll( ScDocShell* pDocShell, const ScQueryParam& aParam )
4324 {
4325     if ( !pDocShell )
4326         return;
4327 
4328     ScViewData* pViewData = ScDocShell::GetViewData();
4329     if ( !pViewData )
4330     {
4331         ScTabViewShell* pViewSh = pDocShell->GetBestViewShell( true );
4332         pViewData = pViewSh ? &pViewSh->GetViewData() : nullptr;
4333     }
4334 
4335     if ( pViewData )
4336     {
4337         pViewData->GetView()->Query( aParam, nullptr, true );
4338     }
4339 }
4340 
lcl_GetQueryParam(ScDocShell * pDocShell,sal_Int16 nSheet)4341 static ScQueryParam lcl_GetQueryParam( ScDocShell* pDocShell, sal_Int16 nSheet )
4342 {
4343     ScDBData* pDBData = lcl_GetDBData_Impl( pDocShell, nSheet );
4344     ScQueryParam aParam;
4345     if (pDBData)
4346     {
4347         pDBData->GetQueryParam( aParam );
4348     }
4349     return aParam;
4350 }
4351 
lcl_SetAllQueryForField(ScDocShell * pDocShell,SCCOLROW nField,sal_Int16 nSheet)4352 static void lcl_SetAllQueryForField( ScDocShell* pDocShell, SCCOLROW nField, sal_Int16 nSheet )
4353 {
4354     ScQueryParam aParam = lcl_GetQueryParam( pDocShell, nSheet );
4355     aParam.RemoveEntryByField(nField);
4356     lcl_SelectAll( pDocShell, aParam );
4357 }
4358 
4359 // Modifies sCriteria, and nOp depending on the value of sCriteria
lcl_setTableFieldsFromCriteria(OUString & sCriteria1,const uno::Reference<beans::XPropertySet> & xDescProps,sheet::TableFilterField2 & rFilterField)4360 static void lcl_setTableFieldsFromCriteria( OUString& sCriteria1, const uno::Reference< beans::XPropertySet >& xDescProps, sheet::TableFilterField2& rFilterField )
4361 {
4362     // #TODO make this more efficient and cycle through
4363     // sCriteria1 character by character to pick up <,<>,=, * etc.
4364     // right now I am more concerned with just getting it to work right
4365 
4366     sCriteria1 = sCriteria1.trim();
4367     // table of translation of criteria text to FilterOperators
4368     // <>searchtext - NOT_EQUAL
4369     //  =searchtext - EQUAL
4370     //  *searchtext - startwith
4371     //  <>*searchtext - doesn't startwith
4372     //  *searchtext* - contains
4373     //  <>*searchtext* - doesn't contain
4374     // [>|>=|<=|...]searchtext for GREATER_value, GREATER_EQUAL_value etc.
4375     if ( sCriteria1.startsWith( EQUALS ) )
4376     {
4377         if ( o3tl::make_unsigned(sCriteria1.getLength()) == strlen(EQUALS) )
4378             rFilterField.Operator = sheet::FilterOperator2::EMPTY;
4379         else
4380         {
4381             rFilterField.Operator = sheet::FilterOperator2::EQUAL;
4382             sCriteria1 = sCriteria1.copy( strlen(EQUALS) );
4383             sCriteria1 = VBAToRegexp( sCriteria1 );
4384             // UseRegularExpressions
4385             if ( xDescProps.is() )
4386                 xDescProps->setPropertyValue( u"UseRegularExpressions"_ustr, uno::Any( true ) );
4387         }
4388 
4389     }
4390     else if ( sCriteria1.startsWith( NOTEQUALS ) )
4391     {
4392         if ( o3tl::make_unsigned(sCriteria1.getLength()) == strlen(NOTEQUALS) )
4393             rFilterField.Operator = sheet::FilterOperator2::NOT_EMPTY;
4394         else
4395         {
4396             rFilterField.Operator = sheet::FilterOperator2::NOT_EQUAL;
4397             sCriteria1 = sCriteria1.copy( strlen(NOTEQUALS) );
4398             sCriteria1 = VBAToRegexp( sCriteria1 );
4399             // UseRegularExpressions
4400             if ( xDescProps.is() )
4401                 xDescProps->setPropertyValue( u"UseRegularExpressions"_ustr, uno::Any( true ) );
4402         }
4403     }
4404     else if ( sCriteria1.startsWith( GREATERTHAN ) )
4405     {
4406         if ( sCriteria1.startsWith( GREATERTHANEQUALS ) )
4407         {
4408             sCriteria1 = sCriteria1.copy( strlen(GREATERTHANEQUALS) );
4409             rFilterField.Operator = sheet::FilterOperator2::GREATER_EQUAL;
4410         }
4411         else
4412         {
4413             sCriteria1 = sCriteria1.copy( strlen(GREATERTHAN) );
4414             rFilterField.Operator = sheet::FilterOperator2::GREATER;
4415         }
4416 
4417     }
4418     else if ( sCriteria1.startsWith( LESSTHAN ) )
4419     {
4420         if ( sCriteria1.startsWith( LESSTHANEQUALS ) )
4421         {
4422             sCriteria1 = sCriteria1.copy( strlen(LESSTHANEQUALS) );
4423             rFilterField.Operator = sheet::FilterOperator2::LESS_EQUAL;
4424         }
4425         else
4426         {
4427             sCriteria1 = sCriteria1.copy( strlen(LESSTHAN) );
4428             rFilterField.Operator = sheet::FilterOperator2::LESS;
4429         }
4430 
4431     }
4432     else
4433         rFilterField.Operator = sheet::FilterOperator2::EQUAL;
4434 
4435     // tdf#107885 - check if criteria is numeric using locale dependent settings without group separator
4436     // or, if the decimal separator is different from the English locale, without any locale.
4437     sal_Int32 nParseEnd = 0;
4438     rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
4439     double fValue = ScGlobal::getLocaleData().stringToDouble( sCriteria1, false, &eStatus, &nParseEnd );
4440     if ( nParseEnd == sCriteria1.getLength() && eStatus == rtl_math_ConversionStatus_Ok )
4441     {
4442         rFilterField.IsNumeric = true;
4443         rFilterField.NumericValue = fValue;
4444     }
4445     else if ( ScGlobal::getLocaleData().getNumDecimalSep().toChar() != '.' )
4446     {
4447         eStatus = rtl_math_ConversionStatus_Ok;
4448         fValue = ::rtl::math::stringToDouble( sCriteria1, '.', 0, &eStatus, &nParseEnd );
4449         if ( nParseEnd == sCriteria1.getLength() && eStatus == rtl_math_ConversionStatus_Ok )
4450         {
4451             rFilterField.IsNumeric = true;
4452             rFilterField.NumericValue = fValue;
4453         }
4454     }
4455 
4456     rFilterField.StringValue = sCriteria1;
4457 }
4458 
4459 void SAL_CALL
AutoFilter(const uno::Any & aField,const uno::Any & Criteria1,const uno::Any & Operator,const uno::Any & Criteria2,const uno::Any &)4460 ScVbaRange::AutoFilter( const uno::Any& aField, const uno::Any& Criteria1, const uno::Any& Operator, const uno::Any& Criteria2, const uno::Any& /*VisibleDropDown*/ )
4461 {
4462     // Is there an existing autofilter
4463     RangeHelper thisRange( mxRange );
4464     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4465     sal_Int16 nSheet = thisAddress.Sheet;
4466     ScDocShell* pShell = getScDocShell();
4467     bool bHasAuto = false;
4468     uno::Reference< sheet::XDatabaseRange > xDataBaseRange = excel::GetAutoFiltRange( pShell, nSheet );
4469     if ( xDataBaseRange.is() )
4470         bHasAuto = true;
4471 
4472     if ( !bHasAuto )
4473     {
4474         if (  m_Areas->getCount() > 1 )
4475             throw uno::RuntimeException( STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY );
4476 
4477         table::CellRangeAddress autoFiltAddress;
4478         //CurrentRegion()
4479         if ( isSingleCellRange() )
4480         {
4481             uno::Reference< excel::XRange > xCurrent( CurrentRegion() );
4482             if ( xCurrent.is() )
4483             {
4484                 ScVbaRange* pRange = getImplementation( xCurrent );
4485                 if ( pRange )
4486                 {
4487                     if ( pRange->isSingleCellRange() )
4488                        throw uno::RuntimeException(u"Can't create AutoFilter"_ustr );
4489                     RangeHelper currentRegion( pRange->mxRange );
4490                     autoFiltAddress = currentRegion.getCellRangeAddressable()->getRangeAddress();
4491                 }
4492             }
4493         }
4494         else // multi-cell range
4495         {
4496             RangeHelper multiCellRange( mxRange );
4497             autoFiltAddress = multiCellRange.getCellRangeAddressable()->getRangeAddress();
4498             // #163530# Filter box shows only entry of first row
4499             ScDocument* pDocument = ( pShell ? &pShell->GetDocument() : nullptr );
4500             if ( pDocument )
4501             {
4502                 SCCOL nStartCol = autoFiltAddress.StartColumn;
4503                 SCROW nStartRow = autoFiltAddress.StartRow;
4504                 SCCOL nEndCol = autoFiltAddress.EndColumn;
4505                 SCROW nEndRow = autoFiltAddress.EndRow;
4506                 pDocument->GetDataArea( autoFiltAddress.Sheet, nStartCol, nStartRow, nEndCol, nEndRow, true, true );
4507                 autoFiltAddress.StartColumn = nStartCol;
4508                 autoFiltAddress.StartRow = nStartRow;
4509                 autoFiltAddress.EndColumn = nEndCol;
4510                 autoFiltAddress.EndRow = nEndRow;
4511             }
4512         }
4513 
4514         uno::Reference< sheet::XUnnamedDatabaseRanges > xDBRanges = excel::GetUnnamedDataBaseRanges( pShell );
4515         if ( xDBRanges.is() )
4516         {
4517             if ( !xDBRanges->hasByTable( nSheet ) )
4518                 xDBRanges->setByTable( autoFiltAddress );
4519             xDataBaseRange.set( xDBRanges->getByTable(nSheet ), uno::UNO_QUERY_THROW );
4520         }
4521         if ( !xDataBaseRange.is() )
4522             throw uno::RuntimeException(u"Failed to find the autofilter placeholder range"_ustr );
4523 
4524         uno::Reference< beans::XPropertySet > xDBRangeProps( xDataBaseRange, uno::UNO_QUERY_THROW );
4525         // set autofilter
4526         xDBRangeProps->setPropertyValue( u"AutoFilter"_ustr, uno::Any(true) );
4527         // set header (autofilter always need column headers)
4528         uno::Reference< beans::XPropertySet > xFiltProps( xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY_THROW );
4529         xFiltProps->setPropertyValue( u"ContainsHeader"_ustr, uno::Any( true ) );
4530     }
4531 
4532     sal_Int32 nField = 0; // *IS* 1 based
4533     sal_Int32 nOperator = excel::XlAutoFilterOperator::xlAnd;
4534 
4535     sheet::FilterConnection nConn = sheet::FilterConnection_AND;
4536     double nCriteria1 = 0;
4537 
4538     bool bHasCritValue = Criteria1.hasValue();
4539     bool bCritHasNumericValue = false; // not sure if a numeric criteria is possible
4540     if ( bHasCritValue )
4541         bCritHasNumericValue = ( Criteria1 >>= nCriteria1 );
4542 
4543     if (  !aField.hasValue() && ( Criteria1.hasValue() || Operator.hasValue() || Criteria2.hasValue() ) )
4544         throw uno::RuntimeException();
4545     uno::Any Field( aField );
4546     if ( !( Field >>= nField ) )
4547     {
4548         const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
4549         try
4550         {
4551             Field = xConverter->convertTo( aField, cppu::UnoType<sal_Int32>::get() );
4552         }
4553         catch( uno::Exception& )
4554         {
4555         }
4556     }
4557     // Use the normal uno api, sometimes e.g. when you want to use ALL as the filter
4558     // we can't use refresh as the uno interface doesn't have a concept of ALL
4559     // in this case we just call the core calc functionality -
4560     if ( Field >>= nField )
4561     {
4562         uno::Reference< sheet::XSheetFilterDescriptor2 > xDesc(
4563                 xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY );
4564         if ( xDesc.is() )
4565         {
4566             OUString sCriteria1;
4567             bool bAcceptCriteria2 = true;
4568             bool bAll = false;
4569             uno::Sequence< sheet::TableFilterField2 > sTabFilts;
4570             sheet::TableFilterField2* pTabFilts = nullptr;
4571             uno::Reference< beans::XPropertySet > xDescProps( xDesc, uno::UNO_QUERY_THROW );
4572             if ( Criteria1.hasValue() )
4573             {
4574                 sTabFilts.realloc( 1 );
4575                 pTabFilts = sTabFilts.getArray();
4576                 pTabFilts[0].Operator = sheet::FilterOperator2::EQUAL;// sensible default
4577                 if ( !bCritHasNumericValue )
4578                 {
4579                     Criteria1 >>= sCriteria1;
4580                     if ( sCriteria1.isEmpty() )
4581                     {
4582                         uno::Sequence< OUString > aCriteria1;
4583                         Criteria1 >>= aCriteria1;
4584                         sal_uInt16 nLength = aCriteria1.getLength();
4585                         if ( nLength )
4586                         {
4587                             // When sequence is provided for Criteria1 don't care about Criteria2
4588                             bAcceptCriteria2 = false;
4589 
4590                             auto pCriteria1 = aCriteria1.getArray();
4591                             sTabFilts.realloc( nLength );
4592                             pTabFilts = sTabFilts.getArray();
4593                             for ( sal_uInt16 i = 0; i < nLength; ++i )
4594                             {
4595                                 lcl_setTableFieldsFromCriteria( pCriteria1[i], xDescProps, pTabFilts[i] );
4596                                 pTabFilts[i].Connection = sheet::FilterConnection_OR;
4597                                 pTabFilts[i].Field = (nField - 1);
4598                             }
4599                         }
4600                         else
4601                             bAll = true;
4602                     }
4603                     else
4604                     {
4605                         pTabFilts[0].IsNumeric = bCritHasNumericValue;
4606                         if ( bHasCritValue && !sCriteria1.isEmpty() )
4607                             lcl_setTableFieldsFromCriteria( sCriteria1, xDescProps, pTabFilts[0]  );
4608                         else
4609                             bAll = true;
4610                     }
4611                 }
4612                 else // numeric
4613                 {
4614                     pTabFilts[0].IsNumeric = true;
4615                     pTabFilts[0].NumericValue = nCriteria1;
4616                 }
4617             }
4618             else // no value specified
4619                 bAll = true;
4620             // not sure what the relationship between Criteria1 and Operator is,
4621             // e.g. can you have an Operator without a Criteria? In LibreOffice it
4622             if ( Operator.hasValue()  && ( Operator >>= nOperator ) )
4623             {
4624                 assert(pTabFilts);
4625                 // if it's a bottom/top Ten(Percent/Value) and there
4626                 // is no value specified for criteria1 set it to 10
4627                 if ( !bCritHasNumericValue && sCriteria1.isEmpty() && ( nOperator != excel::XlAutoFilterOperator::xlOr ) && ( nOperator != excel::XlAutoFilterOperator::xlAnd ) )
4628                 {
4629                     pTabFilts[0].IsNumeric = true;
4630                     pTabFilts[0].NumericValue = 10;
4631                     bAll = false;
4632                 }
4633                 switch ( nOperator )
4634                 {
4635                     case excel::XlAutoFilterOperator::xlBottom10Items:
4636                         pTabFilts[0].Operator = sheet::FilterOperator2::BOTTOM_VALUES;
4637                         break;
4638                     case excel::XlAutoFilterOperator::xlBottom10Percent:
4639                         pTabFilts[0].Operator = sheet::FilterOperator2::BOTTOM_PERCENT;
4640                         break;
4641                     case excel::XlAutoFilterOperator::xlTop10Items:
4642                         pTabFilts[0].Operator = sheet::FilterOperator2::TOP_VALUES;
4643                         break;
4644                     case excel::XlAutoFilterOperator::xlTop10Percent:
4645                         pTabFilts[0].Operator = sheet::FilterOperator2::TOP_PERCENT;
4646                         break;
4647                     case excel::XlAutoFilterOperator::xlOr:
4648                         nConn = sheet::FilterConnection_OR;
4649                         break;
4650                     case excel::XlAutoFilterOperator::xlAnd:
4651                         nConn = sheet::FilterConnection_AND;
4652                         break;
4653                     default:
4654                         throw uno::RuntimeException(u"UnknownOption"_ustr );
4655 
4656                 }
4657 
4658             }
4659             if ( !bAll && bAcceptCriteria2 )
4660             {
4661                 assert(pTabFilts);
4662                 pTabFilts[0].Connection = sheet::FilterConnection_AND;
4663                 pTabFilts[0].Field = (nField - 1);
4664 
4665                 uno::Sequence< OUString > aCriteria2;
4666                 if ( Criteria2.hasValue() ) // there is a Criteria2
4667                 {
4668                     sTabFilts.realloc(2);
4669                     pTabFilts = sTabFilts.getArray();
4670                     pTabFilts[1].Field = sTabFilts[0].Field;
4671                     pTabFilts[1].Connection = nConn;
4672 
4673                     OUString sCriteria2;
4674                     if ( Criteria2 >>= sCriteria2 )
4675                     {
4676                         if ( !sCriteria2.isEmpty() )
4677                         {
4678                             uno::Reference< beans::XPropertySet > xProps;
4679                             lcl_setTableFieldsFromCriteria( sCriteria2, xProps,  pTabFilts[1] );
4680                             pTabFilts[1].IsNumeric = false;
4681                         }
4682                     }
4683                     else if ( Criteria2 >>= aCriteria2 )
4684                     {
4685                         sal_uInt16 nLength = aCriteria2.getLength();
4686                         if ( nLength )
4687                         {
4688                             // For compatibility use only the last value from the sequence
4689                             lcl_setTableFieldsFromCriteria( aCriteria2.getArray()[nLength - 1], xDescProps, pTabFilts[1] );
4690                         }
4691                     }
4692                     else // numeric
4693                     {
4694                         Criteria2 >>= pTabFilts[1].NumericValue;
4695                         pTabFilts[1].IsNumeric = true;
4696                         pTabFilts[1].Operator = sheet::FilterOperator2::EQUAL;
4697                     }
4698                 }
4699             }
4700 
4701             xDesc->setFilterFields2( sTabFilts );
4702             if ( !bAll )
4703             {
4704                 xDataBaseRange->refresh();
4705             }
4706             else
4707                 // was 0 based now seems to be 1
4708                 lcl_SetAllQueryForField( pShell, nField, nSheet );
4709         }
4710     }
4711     else
4712     {
4713         // this is just to toggle autofilter on and off ( not to be confused with
4714         // a VisibleDropDown option combined with a field, in that case just the
4715         // button should be disabled ) - currently we don't support that
4716         uno::Reference< beans::XPropertySet > xDBRangeProps( xDataBaseRange, uno::UNO_QUERY_THROW );
4717         if ( bHasAuto )
4718         {
4719             // find the any field with the query and select all
4720             ScQueryParam aParam = lcl_GetQueryParam( pShell, nSheet );
4721             for (SCSIZE i = 0; i< aParam.GetEntryCount(); ++i)
4722             {
4723                 ScQueryEntry& rEntry = aParam.GetEntry(i);
4724                 if ( rEntry.bDoQuery )
4725                     lcl_SetAllQueryForField( pShell, rEntry.nField, nSheet );
4726             }
4727             // remove existing filters
4728             uno::Reference< sheet::XSheetFilterDescriptor2 > xSheetFilterDescriptor(
4729                     xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY );
4730             if( xSheetFilterDescriptor.is() )
4731                 xSheetFilterDescriptor->setFilterFields2( uno::Sequence< sheet::TableFilterField2 >() );
4732         }
4733         xDBRangeProps->setPropertyValue( u"AutoFilter"_ustr, uno::Any(!bHasAuto) );
4734 
4735     }
4736 }
4737 
4738 void SAL_CALL
Insert(const uno::Any & Shift,const uno::Any &)4739 ScVbaRange::Insert( const uno::Any& Shift, const uno::Any& /*CopyOrigin*/ )
4740 {
4741     // It appears (from the web) that the undocumented CopyOrigin
4742     // param should contain member of enum XlInsertFormatOrigin
4743     // which can have values xlFormatFromLeftOrAbove or xlFormatFromRightOrBelow
4744     // #TODO investigate resultant behaviour using these constants
4745     // currently just processing Shift
4746 
4747     sheet::CellInsertMode mode = sheet::CellInsertMode_NONE;
4748     if ( Shift.hasValue() )
4749     {
4750         sal_Int32 nShift = 0;
4751         Shift >>= nShift;
4752         switch ( nShift )
4753         {
4754             case excel::XlInsertShiftDirection::xlShiftToRight:
4755                 mode = sheet::CellInsertMode_RIGHT;
4756                 break;
4757             case excel::XlInsertShiftDirection::xlShiftDown:
4758                 mode = sheet::CellInsertMode_DOWN;
4759                 break;
4760             default:
4761                 throw uno::RuntimeException(u"Illegal parameter "_ustr );
4762         }
4763     }
4764     else
4765     {
4766         if ( getRow() >=  getColumn() )
4767             mode = sheet::CellInsertMode_DOWN;
4768         else
4769             mode = sheet::CellInsertMode_RIGHT;
4770     }
4771     RangeHelper thisRange( mxRange );
4772     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4773     uno::Reference< sheet::XCellRangeMovement > xCellRangeMove( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
4774     xCellRangeMove->insertCells( thisAddress, mode );
4775 
4776     // Paste from clipboard only if the clipboard content was copied via VBA, and not already pasted via VBA again.
4777     // "Insert" behavior should not depend on random clipboard content previously copied by the user.
4778     ScDocShell* pDocShell = getDocShellFromRange( mxRange );
4779     const ScTransferObj* pClipObj = pDocShell ? ScTransferObj::GetOwnClipboard(pDocShell->GetClipData()) : nullptr;
4780     if ( pClipObj && pClipObj->GetUseInApi() )
4781     {
4782         // After the insert ( this range ) actually has moved
4783         ScRange aRange( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCROW >( thisAddress.StartRow ), static_cast< SCTAB >( thisAddress.Sheet ), static_cast< SCCOL >( thisAddress.EndColumn ), static_cast< SCROW >( thisAddress.EndRow ), static_cast< SCTAB >( thisAddress.Sheet ) );
4784         uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getDocShellFromRange( mxRange ) , aRange ) );
4785         uno::Reference< excel::XRange > xVbaRange( new ScVbaRange( mxParent, mxContext, xRange, mbIsRows, mbIsColumns ) );
4786         xVbaRange->PasteSpecial( uno::Any(), uno::Any(), uno::Any(), uno::Any() );
4787     }
4788 }
4789 
4790 void SAL_CALL
Autofit()4791 ScVbaRange::Autofit()
4792 {
4793     sal_Int32 nLen = m_Areas->getCount();
4794     if ( nLen > 1 )
4795     {
4796         for ( sal_Int32 index = 1; index != nLen; ++index )
4797         {
4798             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
4799             xRange->Autofit();
4800         }
4801         return;
4802     }
4803 
4804     // if the range is a not a row or column range autofit will
4805     // throw an error
4806     if ( !( mbIsColumns || mbIsRows ) )
4807             DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
4808     ScDocShell* pDocShell = getDocShellFromRange( mxRange );
4809     if ( !pDocShell )
4810         return;
4811 
4812     RangeHelper thisRange( mxRange );
4813     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4814 
4815     std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(thisAddress.StartColumn,thisAddress.EndColumn));
4816     bool bDirection = true;
4817     if ( mbIsRows )
4818     {
4819         bDirection = false;
4820         aColArr[0].mnStart = thisAddress.StartRow;
4821         aColArr[0].mnEnd = thisAddress.EndRow;
4822     }
4823     pDocShell->GetDocFunc().SetWidthOrHeight(
4824         bDirection, aColArr, thisAddress.Sheet, SC_SIZE_OPTIMAL, 0, true, true);
4825 }
4826 
4827 uno::Any SAL_CALL
Hyperlinks(const uno::Any & aIndex)4828 ScVbaRange::Hyperlinks( const uno::Any& aIndex )
4829 {
4830     /*  The range object always returns a new Hyperlinks object containing a
4831         fixed list of existing hyperlinks in the range.
4832         See vbahyperlinks.hxx for more details. */
4833 
4834     // get the global hyperlink object of the sheet (sheet should always be the parent of a Range object)
4835     uno::Reference< excel::XWorksheet > xWorksheet( getParent(), uno::UNO_QUERY_THROW );
4836     uno::Reference< excel::XHyperlinks > xSheetHlinks( xWorksheet->Hyperlinks( uno::Any() ), uno::UNO_QUERY_THROW );
4837     ScVbaHyperlinksRef xScSheetHlinks( dynamic_cast< ScVbaHyperlinks* >( xSheetHlinks.get() ) );
4838     if( !xScSheetHlinks.is() )
4839         throw uno::RuntimeException(u"Cannot obtain hyperlinks implementation object"_ustr );
4840 
4841     // create a new local hyperlinks object based on the sheet hyperlinks
4842     ScVbaHyperlinksRef xHlinks( new ScVbaHyperlinks( getParent(), mxContext, xScSheetHlinks, getScRangeList() ) );
4843     if( aIndex.hasValue() )
4844         return xHlinks->Item( aIndex, uno::Any() );
4845     return uno::Any( uno::Reference< excel::XHyperlinks >( xHlinks ) );
4846 }
4847 
4848 css::uno::Reference< excel::XValidation > SAL_CALL
getValidation()4849 ScVbaRange::getValidation()
4850 {
4851     if ( !m_xValidation.is() )
4852         m_xValidation = new ScVbaValidation( this, mxContext, mxRange );
4853     return m_xValidation;
4854 }
4855 
4856 namespace {
4857 
4858 /// @throws uno::RuntimeException
lclGetPrefixChar(const uno::Reference<table::XCell> & rxCell)4859 sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCell >& rxCell )
4860 {
4861     /*  TODO/FIXME: We need an apostroph-prefix property at the cell to
4862         implement this correctly. For now, return an apostroph for every text
4863         cell.
4864 
4865         TODO/FIXME: When Application.TransitionNavigKeys is supported and true,
4866         this function needs to inspect the cell formatting and return different
4867         prefixes according to the horizontal cell alignment.
4868      */
4869     return (rxCell->getType() == table::CellContentType_TEXT) ? '\'' : 0;
4870 }
4871 
4872 /// @throws uno::RuntimeException
lclGetPrefixChar(const uno::Reference<table::XCellRange> & rxRange)4873 sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCellRange >& rxRange )
4874 {
4875     /*  This implementation is able to handle different prefixes (needed if
4876         Application.TransitionNavigKeys is true). The function lclGetPrefixChar
4877         for single cells called from here may return any prefix. If that
4878         function returns an empty prefix (NUL character) or different non-empty
4879         prefixes for two cells, this function returns 0.
4880      */
4881     sal_Unicode cCurrPrefix = 0;
4882     table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxRange );
4883     sal_Int32 nEndCol = aRangeAddr.EndColumn - aRangeAddr.StartColumn;
4884     sal_Int32 nEndRow = aRangeAddr.EndRow - aRangeAddr.StartRow;
4885     for( sal_Int32 nRow = 0; nRow <= nEndRow; ++nRow )
4886     {
4887         for( sal_Int32 nCol = 0; nCol <= nEndCol; ++nCol )
4888         {
4889             uno::Reference< table::XCell > xCell( rxRange->getCellByPosition( nCol, nRow ), uno::UNO_SET_THROW );
4890             sal_Unicode cNewPrefix = lclGetPrefixChar( xCell );
4891             if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) )
4892                 return 0;
4893             cCurrPrefix = cNewPrefix;
4894         }
4895     }
4896     // all cells contain the same prefix - return it
4897     return cCurrPrefix;
4898 }
4899 
4900 /// @throws uno::RuntimeException
lclGetPrefixChar(const uno::Reference<sheet::XSheetCellRangeContainer> & rxRanges)4901 sal_Unicode lclGetPrefixChar( const uno::Reference< sheet::XSheetCellRangeContainer >& rxRanges )
4902 {
4903     sal_Unicode cCurrPrefix = 0;
4904     uno::Reference< container::XEnumerationAccess > xRangesEA( rxRanges, uno::UNO_QUERY_THROW );
4905     uno::Reference< container::XEnumeration > xRangesEnum( xRangesEA->createEnumeration(), uno::UNO_SET_THROW );
4906     while( xRangesEnum->hasMoreElements() )
4907     {
4908         uno::Reference< table::XCellRange > xRange( xRangesEnum->nextElement(), uno::UNO_QUERY_THROW );
4909         sal_Unicode cNewPrefix = lclGetPrefixChar( xRange );
4910         if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) )
4911             return 0;
4912         cCurrPrefix = cNewPrefix;
4913     }
4914     // all ranges contain the same prefix - return it
4915     return cCurrPrefix;
4916 }
4917 
lclGetPrefixVariant(sal_Unicode cPrefixChar)4918 uno::Any lclGetPrefixVariant( sal_Unicode cPrefixChar )
4919 {
4920     return uno::Any( (cPrefixChar == 0) ? OUString() : OUString( cPrefixChar ) );
4921 }
4922 
4923 } // namespace
4924 
getPrefixCharacter()4925 uno::Any SAL_CALL ScVbaRange::getPrefixCharacter()
4926 {
4927     /*  (1) If Application.TransitionNavigKeys is false, this function returns
4928         an apostroph character if the text cell begins with an apostroph
4929         character (formula return values are not taken into account); otherwise
4930         an empty string.
4931 
4932         (2) If Application.TransitionNavigKeys is true, this function returns
4933         an apostroph character, if the cell is left-aligned; a double-quote
4934         character, if the cell is right-aligned; a circumflex character, if the
4935         cell is centered; a backslash character, if the cell is set to filled;
4936         or an empty string, if nothing of the above.
4937 
4938         If a range or a list of ranges contains texts with leading apostroph
4939         character as well as other cells, this function returns an empty
4940         string.
4941      */
4942 
4943     if( mxRange.is() )
4944         return lclGetPrefixVariant( lclGetPrefixChar( mxRange ) );
4945     if( mxRanges.is() )
4946         return lclGetPrefixVariant( lclGetPrefixChar( mxRanges ) );
4947     throw uno::RuntimeException(u"Unexpected empty Range object"_ustr );
4948 }
4949 
getShowDetail()4950 uno::Any ScVbaRange::getShowDetail()
4951 {
4952     // #FIXME, If the specified range is in a PivotTable report
4953 
4954     // In MSO VBA, the specified range must be a single summary column or row in an outline. otherwise throw exception
4955     if( m_Areas->getCount() > 1 )
4956         throw uno::RuntimeException(u"Can not get Range.ShowDetail attribute "_ustr );
4957 
4958     RangeHelper helper( mxRange );
4959     uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = helper.getSheetCellCursor();
4960     xSheetCellCursor->collapseToCurrentRegion();
4961     uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
4962     table::CellRangeAddress aOutlineAddress = xCellRangeAddressable->getRangeAddress();
4963 
4964     // check if the specified range is a single summary column or row.
4965     table::CellRangeAddress thisAddress = helper.getCellRangeAddressable()->getRangeAddress();
4966     if( (thisAddress.StartRow != thisAddress.EndRow || thisAddress.EndRow != aOutlineAddress.EndRow ) &&
4967         (thisAddress.StartColumn != thisAddress.EndColumn || thisAddress.EndColumn != aOutlineAddress.EndColumn ))
4968     {
4969         throw uno::RuntimeException(u"Can not set Range.ShowDetail attribute"_ustr );
4970     }
4971 
4972     bool bColumn = thisAddress.StartRow != thisAddress.EndRow;
4973     ScDocument& rDoc = getDocumentFromRange( mxRange );
4974     ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable(static_cast<SCTAB>(thisAddress.Sheet), true);
4975     const ScOutlineArray& rOutlineArray =  bColumn ? pOutlineTable->GetColArray(): pOutlineTable->GetRowArray();
4976     SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(thisAddress.EndColumn-1):static_cast<SCCOLROW>(thisAddress.EndRow-1);
4977     const ScOutlineEntry* pEntry = rOutlineArray.GetEntryByPos( 0, nPos );
4978     if( pEntry )
4979     {
4980         const bool bShowDetail = !pEntry->IsHidden();
4981         return uno::Any( bShowDetail );
4982     }
4983 
4984     return aNULL();
4985 }
4986 
setShowDetail(const uno::Any & aShowDetail)4987 void ScVbaRange::setShowDetail(const uno::Any& aShowDetail)
4988 {
4989     // #FIXME, If the specified range is in a PivotTable report
4990 
4991     // In MSO VBA, the specified range must be a single summary column or row in an outline. otherwise throw exception
4992     if( m_Areas->getCount() > 1 )
4993         throw uno::RuntimeException(u"Can not set Range.ShowDetail attribute"_ustr );
4994 
4995     bool bShowDetail = extractBoolFromAny( aShowDetail );
4996 
4997     RangeHelper helper( mxRange );
4998     uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = helper.getSheetCellCursor();
4999     xSheetCellCursor->collapseToCurrentRegion();
5000     uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
5001     table::CellRangeAddress aOutlineAddress = xCellRangeAddressable->getRangeAddress();
5002 
5003     // check if the specified range is a single summary column or row.
5004     table::CellRangeAddress thisAddress = helper.getCellRangeAddressable()->getRangeAddress();
5005     if( (thisAddress.StartRow != thisAddress.EndRow || thisAddress.EndRow != aOutlineAddress.EndRow ) &&
5006         (thisAddress.StartColumn != thisAddress.EndColumn || thisAddress.EndColumn != aOutlineAddress.EndColumn ))
5007     {
5008         throw uno::RuntimeException(u"Can not set Range.ShowDetail attribute"_ustr );
5009     }
5010 
5011     // #FIXME, seems there is a different behavior between MSO and OOo.
5012     //  In OOo, the showDetail will show all the level entries, while only show the first level entry in MSO
5013     uno::Reference< sheet::XSheetOutline > xSheetOutline( helper.getSpreadSheet(), uno::UNO_QUERY_THROW );
5014     if( bShowDetail )
5015         xSheetOutline->showDetail( aOutlineAddress );
5016     else
5017         xSheetOutline->hideDetail( aOutlineAddress );
5018 
5019 }
5020 
5021 uno::Reference< excel::XRange > SAL_CALL
MergeArea()5022 ScVbaRange::MergeArea()
5023 {
5024     uno::Reference< sheet::XSheetCellRange > xMergeShellCellRange(mxRange->getCellRangeByPosition(0,0,0,0), uno::UNO_QUERY_THROW);
5025     uno::Reference< sheet::XSheetCellCursor > xMergeSheetCursor(xMergeShellCellRange->getSpreadsheet()->createCursorByRange( xMergeShellCellRange ), uno::UNO_SET_THROW);
5026     if( xMergeSheetCursor.is() )
5027     {
5028         xMergeSheetCursor->collapseToMergedArea();
5029         uno::Reference<sheet::XCellRangeAddressable> xMergeCellAddress(xMergeSheetCursor, uno::UNO_QUERY_THROW);
5030         table::CellRangeAddress aCellAddress = xMergeCellAddress->getRangeAddress();
5031         if( aCellAddress.StartColumn ==0 && aCellAddress.EndColumn==0 &&
5032             aCellAddress.StartRow==0 && aCellAddress.EndRow==0)
5033         {
5034             return new ScVbaRange( mxParent,mxContext,mxRange );
5035         }
5036         else
5037         {
5038             ScRange refRange( static_cast< SCCOL >( aCellAddress.StartColumn ), static_cast< SCROW >( aCellAddress.StartRow ), static_cast< SCTAB >( aCellAddress.Sheet ),
5039                               static_cast< SCCOL >( aCellAddress.EndColumn ), static_cast< SCROW >( aCellAddress.EndRow ), static_cast< SCTAB >( aCellAddress.Sheet ) );
5040             uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell() , refRange ) );
5041             return new ScVbaRange( mxParent, mxContext,xRange );
5042         }
5043     }
5044     return new ScVbaRange( mxParent, mxContext, mxRange );
5045 }
5046 
5047 void SAL_CALL
PrintOut(const uno::Any & From,const uno::Any & To,const uno::Any & Copies,const uno::Any & Preview,const uno::Any & ActivePrinter,const uno::Any & PrintToFile,const uno::Any & Collate,const uno::Any & PrToFileName)5048 ScVbaRange::PrintOut( const uno::Any& From, const uno::Any& To, const uno::Any& Copies, const uno::Any& Preview, const uno::Any& ActivePrinter, const uno::Any& PrintToFile, const uno::Any& Collate, const uno::Any& PrToFileName )
5049 {
5050     ScDocShell* pShell = nullptr;
5051 
5052     sal_Int32 nItems = m_Areas->getCount();
5053     uno::Sequence<  table::CellRangeAddress > printAreas( nItems );
5054     auto printAreasRange = asNonConstRange(printAreas);
5055     uno::Reference< sheet::XPrintAreas > xPrintAreas;
5056     for ( sal_Int32 index=1; index <= nItems; ++index )
5057     {
5058         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5059 
5060         RangeHelper thisRange( xRange->getCellRange() );
5061         table::CellRangeAddress rangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5062         if ( index == 1 )
5063         {
5064             ScVbaRange* pRange = getImplementation( xRange );
5065             // initialise the doc shell and the printareas
5066             pShell = getDocShellFromRange( pRange->mxRange );
5067             xPrintAreas.set( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5068         }
5069         printAreasRange[ index - 1 ] = rangeAddress;
5070     }
5071     if ( pShell && xPrintAreas.is() )
5072     {
5073         xPrintAreas->setPrintAreas( printAreas );
5074         uno::Reference< frame::XModel > xModel = pShell->GetModel();
5075         PrintOutHelper( excel::getBestViewShell( xModel ), From, To, Copies, Preview, ActivePrinter, PrintToFile, Collate, PrToFileName, true );
5076     }
5077 }
5078 
5079 void SAL_CALL
AutoFill(const uno::Reference<excel::XRange> & Destination,const uno::Any & Type)5080 ScVbaRange::AutoFill(  const uno::Reference< excel::XRange >& Destination, const uno::Any& Type )
5081 {
5082     uno::Reference< excel::XRange > xDest( Destination, uno::UNO_SET_THROW );
5083     ScVbaRange* pRange = getImplementation( xDest );
5084     RangeHelper destRangeHelper( pRange->mxRange );
5085     table::CellRangeAddress destAddress = destRangeHelper.getCellRangeAddressable()->getRangeAddress();
5086 
5087     RangeHelper thisRange( mxRange );
5088     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5089     ScRange sourceRange;
5090     ScRange destRange;
5091 
5092     ScUnoConversion::FillScRange( destRange, destAddress );
5093     ScUnoConversion::FillScRange( sourceRange, thisAddress );
5094 
5095     FillDir eDir = FILL_TO_BOTTOM;
5096     double fStep = 1.0;
5097 
5098     ScRange aRange( destRange );
5099     ScRange aSourceRange( destRange );
5100 
5101     // default to include the number of Rows in the source range;
5102     SCCOLROW nSourceCount = ( sourceRange.aEnd.Row() - sourceRange.aStart.Row() ) + 1;
5103     SCCOLROW nCount = 0;
5104 
5105     if ( sourceRange != destRange )
5106     {
5107         // Find direction of fill, vertical or horizontal
5108         if ( sourceRange.aStart == destRange.aStart )
5109         {
5110             if ( sourceRange.aEnd.Row() == destRange.aEnd.Row() )
5111             {
5112                 nSourceCount = ( sourceRange.aEnd.Col() - sourceRange.aStart.Col() + 1 );
5113                 aSourceRange.aEnd.SetCol( static_cast<SCCOL>( aSourceRange.aStart.Col() + nSourceCount - 1 ) );
5114                 eDir = FILL_TO_RIGHT;
5115                 nCount = aRange.aEnd.Col() - aSourceRange.aEnd.Col();
5116             }
5117             else if ( sourceRange.aEnd.Col() == destRange.aEnd.Col() )
5118             {
5119                 aSourceRange.aEnd.SetRow( static_cast<SCROW>( aSourceRange.aStart.Row() + nSourceCount ) - 1 );
5120                 nCount = aRange.aEnd.Row() - aSourceRange.aEnd.Row();
5121                 eDir = FILL_TO_BOTTOM;
5122             }
5123         }
5124 
5125         else if ( aSourceRange.aEnd == destRange.aEnd )
5126         {
5127             if ( sourceRange.aStart.Col() == destRange.aStart.Col() )
5128             {
5129                 aSourceRange.aStart.SetRow( static_cast<SCROW>( aSourceRange.aEnd.Row() - nSourceCount + 1 ) );
5130                 nCount = aSourceRange.aStart.Row() - aRange.aStart.Row();
5131                 eDir = FILL_TO_TOP;
5132                 fStep = -fStep;
5133             }
5134             else if ( sourceRange.aStart.Row() == destRange.aStart.Row() )
5135             {
5136                 nSourceCount = ( sourceRange.aEnd.Col() - sourceRange.aStart.Col() ) + 1;
5137                 aSourceRange.aStart.SetCol( static_cast<SCCOL>( aSourceRange.aEnd.Col() - nSourceCount + 1 ) );
5138                 nCount = aSourceRange.aStart.Col() - aRange.aStart.Col();
5139                 eDir = FILL_TO_LEFT;
5140                 fStep = -fStep;
5141             }
5142         }
5143     }
5144 
5145     FillCmd eCmd = FILL_AUTO;
5146     FillDateCmd eDateCmd = FILL_DAY;
5147 
5148     if ( Type.hasValue() )
5149     {
5150         sal_Int16 nFillType = excel::XlAutoFillType::xlFillDefault;
5151         Type >>= nFillType;
5152         switch ( nFillType )
5153         {
5154             case excel::XlAutoFillType::xlFillCopy:
5155                 eCmd =  FILL_SIMPLE;
5156                 fStep = 0.0;
5157                 break;
5158             case excel::XlAutoFillType::xlFillDays:
5159                 eCmd = FILL_DATE;
5160                 break;
5161             case excel::XlAutoFillType::xlFillMonths:
5162                 eCmd = FILL_DATE;
5163                 eDateCmd = FILL_MONTH;
5164                 break;
5165             case excel::XlAutoFillType::xlFillWeekdays:
5166                 eCmd = FILL_DATE;
5167                 eDateCmd = FILL_WEEKDAY;
5168                 break;
5169             case excel::XlAutoFillType::xlFillYears:
5170                 eCmd = FILL_DATE;
5171                 eDateCmd = FILL_YEAR;
5172                 break;
5173             case excel::XlAutoFillType::xlGrowthTrend:
5174                 eCmd = FILL_GROWTH;
5175                 break;
5176             case excel::XlAutoFillType::xlFillFormats:
5177                 throw uno::RuntimeException(u"xlFillFormat not supported for AutoFill"_ustr );
5178             case excel::XlAutoFillType::xlFillValues:
5179             case excel::XlAutoFillType::xlFillSeries:
5180             case excel::XlAutoFillType::xlLinearTrend:
5181                 eCmd = FILL_LINEAR;
5182                 break;
5183             case excel::XlAutoFillType::xlFillDefault:
5184             default:
5185                 eCmd =  FILL_AUTO;
5186                 break;
5187         }
5188     }
5189     ScDocShell* pDocSh = getDocShellFromRange( mxRange );
5190     pDocSh->GetDocFunc().FillAuto( aSourceRange, nullptr, eDir, eCmd, eDateCmd,
5191                                    nCount, fStep, MAXDOUBLE/*fEndValue*/, true, true );
5192 }
5193 sal_Bool SAL_CALL
GoalSeek(const uno::Any & Goal,const uno::Reference<excel::XRange> & ChangingCell)5194 ScVbaRange::GoalSeek( const uno::Any& Goal, const uno::Reference< excel::XRange >& ChangingCell )
5195 {
5196     ScDocShell* pDocShell = getScDocShell();
5197     bool bRes = true;
5198     ScVbaRange* pRange = static_cast< ScVbaRange* >( ChangingCell.get() );
5199     if ( pDocShell && pRange )
5200     {
5201         RangeHelper thisRange( mxRange );
5202         table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5203         RangeHelper changingCellRange( pRange->mxRange );
5204         table::CellRangeAddress changingCellAddr = changingCellRange.getCellRangeAddressable()->getRangeAddress();
5205         OUString sGoal = getAnyAsString( Goal );
5206         table::CellAddress thisCell( thisAddress.Sheet, thisAddress.StartColumn, thisAddress.StartRow );
5207         table::CellAddress changingCell( changingCellAddr.Sheet, changingCellAddr.StartColumn, changingCellAddr.StartRow );
5208         sheet::GoalResult res = pDocShell->GetModel()->seekGoal( thisCell, changingCell, sGoal );
5209         ChangingCell->setValue( uno::Any( res.Result ) );
5210 
5211         // openoffice behaves differently, result is 0 if the divergence is too great
5212                 // but... if it detects 0 is the value it requires then it will use that
5213         // e.g. divergence & result both = 0.0 does NOT mean there is an error
5214         if ( ( res.Divergence != 0.0 ) && ( res.Result == 0.0 ) )
5215             bRes = false;
5216     }
5217     else
5218         bRes = false;
5219     return bRes;
5220 }
5221 
5222 void
Calculate()5223 ScVbaRange::Calculate(  )
5224 {
5225     getWorksheet()->Calculate();
5226 }
5227 
5228 uno::Reference< excel::XRange > SAL_CALL
Item(const uno::Any & row,const uno::Any & column)5229 ScVbaRange::Item( const uno::Any& row, const uno::Any& column )
5230 {
5231     if ( mbIsRows || mbIsColumns )
5232     {
5233         if ( column.hasValue() )
5234             DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5235         uno::Reference< excel::XRange > xRange;
5236         if ( mbIsColumns )
5237             xRange = Columns( row );
5238         else
5239             xRange = Rows( row );
5240         return xRange;
5241     }
5242     return Cells( row, column );
5243 }
5244 
5245 void
AutoOutline()5246 ScVbaRange::AutoOutline(  )
5247 {
5248     // #TODO #FIXME needs to check for summary row/col ( whatever they are )
5249     // not valid for multi Area Addresses
5250     if ( m_Areas->getCount() > 1 )
5251         DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY);
5252     // So needs to either span an entire Row or a just be a single cell
5253     // ( that contains a summary RowColumn )
5254     // also the Single cell cause doesn't seem to be handled specially in
5255     // this code ( ported from the helperapi RangeImpl.java,
5256     // RangeRowsImpl.java, RangesImpl.java, RangeSingleCellImpl.java
5257     RangeHelper thisRange( mxRange );
5258     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5259 
5260     if ( isSingleCellRange() || mbIsRows )
5261     {
5262         uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5263         xSheetOutline->autoOutline( thisAddress );
5264     }
5265     else
5266         DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
5267 }
5268 
5269 void SAL_CALL
ClearOutline()5270 ScVbaRange:: ClearOutline(  )
5271 {
5272     if ( m_Areas->getCount() > 1 )
5273     {
5274         sal_Int32 nItems = m_Areas->getCount();
5275         for ( sal_Int32 index=1; index <= nItems; ++index )
5276         {
5277             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5278             xRange->ClearOutline();
5279         }
5280         return;
5281     }
5282     RangeHelper thisRange( mxRange );
5283     uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5284     xSheetOutline->clearOutline();
5285 }
5286 
5287 void
groupUnGroup(bool bUnGroup)5288 ScVbaRange::groupUnGroup( bool bUnGroup )
5289 {
5290     if ( m_Areas->getCount() > 1 )
5291          DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY);
5292     table::TableOrientation nOrient = table::TableOrientation_ROWS;
5293     if ( mbIsColumns )
5294         nOrient = table::TableOrientation_COLUMNS;
5295     RangeHelper thisRange( mxRange );
5296     table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5297     uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5298     if ( bUnGroup )
5299             xSheetOutline->ungroup( thisAddress, nOrient );
5300     else
5301             xSheetOutline->group( thisAddress, nOrient );
5302 }
5303 
5304 void SAL_CALL
Group()5305 ScVbaRange::Group(  )
5306 {
5307     groupUnGroup(false);
5308 }
5309 void SAL_CALL
Ungroup()5310 ScVbaRange::Ungroup(  )
5311 {
5312     groupUnGroup(true);
5313 }
5314 
5315 /// @throws uno::RuntimeException
lcl_mergeCellsOfRange(const uno::Reference<table::XCellRange> & xCellRange,bool _bMerge)5316 static void lcl_mergeCellsOfRange( const uno::Reference< table::XCellRange >& xCellRange, bool _bMerge )
5317 {
5318         uno::Reference< util::XMergeable > xMergeable( xCellRange, uno::UNO_QUERY_THROW );
5319         xMergeable->merge(_bMerge);
5320 }
5321 void SAL_CALL
Merge(const uno::Any & Across)5322 ScVbaRange::Merge( const uno::Any& Across )
5323 {
5324     if ( m_Areas->getCount() > 1 )
5325     {
5326         sal_Int32 nItems = m_Areas->getCount();
5327         for ( sal_Int32 index=1; index <= nItems; ++index )
5328         {
5329             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5330             xRange->Merge(Across);
5331         }
5332         return;
5333     }
5334     bool bAcross = false;
5335     Across >>= bAcross;
5336     if ( !bAcross )
5337         lcl_mergeCellsOfRange( mxRange, true );
5338     else
5339     {
5340         uno::Reference< excel::XRange > oRangeRowsImpl = Rows( uno::Any() );
5341         // #TODO #FIXME this seems incredibly lame, this can't be right
5342         for (sal_Int32 i=1; i <= oRangeRowsImpl->getCount();i++)
5343         {
5344             oRangeRowsImpl->Cells( uno::Any( i ), uno::Any() )->Merge( uno::Any( false ) );
5345         }
5346     }
5347 }
5348 
5349 void SAL_CALL
UnMerge()5350 ScVbaRange::UnMerge(  )
5351 {
5352     if ( m_Areas->getCount() > 1 )
5353     {
5354         sal_Int32 nItems = m_Areas->getCount();
5355         for ( sal_Int32 index=1; index <= nItems; ++index )
5356         {
5357             uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5358             xRange->UnMerge();
5359         }
5360         return;
5361     }
5362     lcl_mergeCellsOfRange( mxRange, false);
5363 }
5364 
5365 uno::Any SAL_CALL
getStyle()5366 ScVbaRange::getStyle()
5367 {
5368     if ( m_Areas->getCount() > 1 )
5369     {
5370         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW  );
5371         return xRange->getStyle();
5372     }
5373     uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
5374     OUString sStyleName;
5375     xProps->getPropertyValue( CELLSTYLE ) >>= sStyleName;
5376     ScDocShell* pShell = getScDocShell();
5377     uno::Reference< frame::XModel > xModel( pShell->GetModel() );
5378     uno::Reference< excel::XStyle > xStyle = new ScVbaStyle( this, mxContext,  sStyleName, xModel );
5379     return uno::Any( xStyle );
5380 }
5381 void SAL_CALL
setStyle(const uno::Any & _style)5382 ScVbaRange::setStyle( const uno::Any& _style )
5383 {
5384     if ( m_Areas->getCount() > 1 )
5385     {
5386         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW );
5387         xRange->setStyle( _style );
5388         return;
5389     }
5390     uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
5391     uno::Reference< excel::XStyle > xStyle;
5392     _style >>= xStyle;
5393     if ( xStyle.is() )
5394         xProps->setPropertyValue( CELLSTYLE, uno::Any( xStyle->getName() ) );
5395 }
5396 
5397 uno::Reference< excel::XRange >
PreviousNext(bool bIsPrevious)5398 ScVbaRange::PreviousNext( bool bIsPrevious )
5399 {
5400     ScMarkData markedRange(getScDocument().GetSheetLimits());
5401     ScRange refRange;
5402     RangeHelper thisRange( mxRange );
5403 
5404     ScUnoConversion::FillScRange( refRange, thisRange.getCellRangeAddressable()->getRangeAddress());
5405     markedRange. SetMarkArea( refRange );
5406     short nMove = bIsPrevious ? -1 : 1;
5407 
5408     SCCOL nNewX = refRange.aStart.Col();
5409     SCROW nNewY = refRange.aStart.Row();
5410     SCTAB nTab = refRange.aStart.Tab();
5411 
5412     ScDocument& rDoc = getScDocument();
5413     rDoc.GetNextPos( nNewX,nNewY, nTab, nMove,0, true,true, markedRange );
5414     refRange.aStart.SetCol( nNewX );
5415     refRange.aStart.SetRow( nNewY );
5416     refRange.aStart.SetTab( nTab );
5417     refRange.aEnd.SetCol( nNewX );
5418     refRange.aEnd.SetRow( nNewY );
5419     refRange.aEnd.SetTab( nTab );
5420 
5421     uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell() , refRange ) );
5422 
5423     return new ScVbaRange( mxParent, mxContext, xRange );
5424 }
5425 
5426 uno::Reference< excel::XRange > SAL_CALL
Next()5427 ScVbaRange::Next()
5428 {
5429     if ( m_Areas->getCount() > 1 )
5430     {
5431         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ) , uno::UNO_QUERY_THROW  );
5432         return xRange->Next();
5433     }
5434     return PreviousNext( false );
5435 }
5436 
5437 uno::Reference< excel::XRange > SAL_CALL
Previous()5438 ScVbaRange::Previous()
5439 {
5440     if ( m_Areas->getCount() > 1 )
5441     {
5442         uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW  );
5443         return xRange->Previous();
5444     }
5445     return PreviousNext( true );
5446 }
5447 
5448 uno::Reference< excel::XRange > SAL_CALL
SpecialCells(const uno::Any & _oType,const uno::Any & _oValue)5449 ScVbaRange::SpecialCells( const uno::Any& _oType, const uno::Any& _oValue)
5450 {
5451     bool bIsSingleCell = isSingleCellRange();
5452     bool bIsMultiArea = ( m_Areas->getCount() > 1 );
5453     ScVbaRange* pRangeToUse = this;
5454     uno::Reference< excel::XRange > xUsedRange( getWorksheet()->getUsedRange() );
5455     sal_Int32 nType = 0;
5456     if ( !( _oType >>= nType ) )
5457         DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5458     switch(nType)
5459     {
5460         case excel::XlCellType::xlCellTypeSameFormatConditions:
5461         case excel::XlCellType::xlCellTypeAllValidation:
5462         case excel::XlCellType::xlCellTypeSameValidation:
5463             DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
5464             break;
5465         case excel::XlCellType::xlCellTypeBlanks:
5466         case excel::XlCellType::xlCellTypeComments:
5467         case excel::XlCellType::xlCellTypeConstants:
5468         case excel::XlCellType::xlCellTypeFormulas:
5469         case excel::XlCellType::xlCellTypeVisible:
5470         case excel::XlCellType::xlCellTypeLastCell:
5471         {
5472             if ( bIsMultiArea )
5473             {
5474                 // need to process each area, gather the results and
5475                 // create a new range from those
5476                 std::vector< table::CellRangeAddress > rangeResults;
5477                 sal_Int32 nItems = m_Areas->getCount() + 1;
5478                 for ( sal_Int32 index=1; index <= nItems; ++index )
5479                 {
5480                     uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5481                     xRange = xRange->SpecialCells( _oType,  _oValue);
5482                     ScVbaRange* pRange = getImplementation( xRange );
5483                     if ( xRange.is() && pRange )
5484                     {
5485                         sal_Int32 nElems = pRange->m_Areas->getCount() + 1;
5486                         for ( sal_Int32 nArea = 1; nArea < nElems; ++nArea )
5487                         {
5488                             uno::Reference< excel::XRange > xTmpRange( m_Areas->Item( uno::Any( nArea ), uno::Any() ), uno::UNO_QUERY_THROW );
5489                             RangeHelper rHelper( xTmpRange->getCellRange() );
5490                             rangeResults.push_back( rHelper.getCellRangeAddressable()->getRangeAddress() );
5491                         }
5492                     }
5493                 }
5494                 ScRangeList aCellRanges;
5495                 for ( const auto& rRangeResult : rangeResults )
5496                 {
5497                     ScRange refRange;
5498                     ScUnoConversion::FillScRange( refRange, rRangeResult );
5499                     aCellRanges.push_back( refRange );
5500                 }
5501                 // Single range
5502                 if ( aCellRanges.size() == 1 )
5503                 {
5504                     uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell(), aCellRanges.front() ) );
5505                     return new ScVbaRange( mxParent, mxContext, xRange );
5506                 }
5507                 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( getScDocShell(), aCellRanges ) );
5508 
5509                 return new ScVbaRange( mxParent, mxContext, xRanges );
5510             }
5511             else if ( bIsSingleCell )
5512             {
5513                 pRangeToUse = static_cast< ScVbaRange* >( xUsedRange.get() );
5514             }
5515 
5516             break;
5517         }
5518         default:
5519         DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5520             break;
5521     }
5522     if ( !pRangeToUse )
5523         DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
5524     return pRangeToUse->SpecialCellsImpl( nType, _oValue );
5525 }
5526 
getContentResultFlags(const uno::Any & aValue)5527 static sal_Int32 getContentResultFlags(const uno::Any& aValue)
5528 {
5529     if (sal_Int32 aType; aValue >>= aType)
5530     {
5531         switch (aType)
5532         {
5533             case excel::XlSpecialCellsValue::xlNumbers:
5534                 return sheet::CellFlags::VALUE | sheet::CellFlags::DATETIME;
5535             case excel::XlSpecialCellsValue::xlTextValues:
5536                 return sheet::CellFlags::STRING;
5537             case excel::XlSpecialCellsValue::xlLogical:
5538                 return sheet::CellFlags::VALUE | sheet::CellFlags::DATETIME;
5539             case excel::XlSpecialCellsValue::xlErrors:
5540                 return 0;
5541             default:
5542                 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {});
5543         }
5544     }
5545     return sheet::CellFlags::VALUE | sheet::CellFlags::STRING | sheet::CellFlags::DATETIME;
5546 }
5547 
5548 /// @throws script::BasicErrorException
lcl_getFormulaResultFlags(const uno::Any & aType)5549 static sal_Int32 lcl_getFormulaResultFlags(const uno::Any& aType)
5550 {
5551     sal_Int32 nType = excel::XlSpecialCellsValue::xlNumbers;
5552     aType >>= nType;
5553     sal_Int32 nRes = sheet::FormulaResult::VALUE;
5554 
5555     switch(nType)
5556     {
5557         case excel::XlSpecialCellsValue::xlErrors:
5558             nRes= sheet::FormulaResult::ERROR;
5559             break;
5560         case excel::XlSpecialCellsValue::xlLogical:
5561             //TODO bc93774: ask NN if this is really an appropriate substitute
5562             nRes = sheet::FormulaResult::VALUE;
5563             break;
5564         case excel::XlSpecialCellsValue::xlNumbers:
5565             nRes = sheet::FormulaResult::VALUE;
5566             break;
5567         case excel::XlSpecialCellsValue::xlTextValues:
5568             nRes = sheet::FormulaResult::STRING;
5569             break;
5570         default:
5571             DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5572     }
5573     return nRes;
5574 }
5575 
5576 uno::Reference< excel::XRange >
SpecialCellsImpl(sal_Int32 nType,const uno::Any & _oValue)5577 ScVbaRange::SpecialCellsImpl( sal_Int32 nType, const uno::Any& _oValue)
5578 {
5579     uno::Reference< excel::XRange > xRange;
5580     try
5581     {
5582         uno::Reference< sheet::XCellRangesQuery > xQuery( mxRange, uno::UNO_QUERY_THROW );
5583         uno::Reference< sheet::XSheetCellRanges > xLocSheetCellRanges;
5584         switch(nType)
5585         {
5586             case excel::XlCellType::xlCellTypeAllFormatConditions:
5587             case excel::XlCellType::xlCellTypeSameFormatConditions:
5588             case excel::XlCellType::xlCellTypeAllValidation:
5589             case excel::XlCellType::xlCellTypeSameValidation:
5590                 // Shouldn't get here ( should be filtered out by
5591                 // ScVbaRange::SpecialCells()
5592                 DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
5593                 break;
5594             case excel::XlCellType::xlCellTypeBlanks:
5595                 xLocSheetCellRanges = xQuery->queryEmptyCells();
5596                 break;
5597             case excel::XlCellType::xlCellTypeComments:
5598                 xLocSheetCellRanges = xQuery->queryContentCells(sheet::CellFlags::ANNOTATION);
5599                 break;
5600             case excel::XlCellType::xlCellTypeConstants:
5601                 xLocSheetCellRanges = xQuery->queryContentCells(getContentResultFlags(_oValue));
5602                 break;
5603             case excel::XlCellType::xlCellTypeFormulas:
5604             {
5605                 sal_Int32 nFormulaResult = lcl_getFormulaResultFlags(_oValue);
5606                 xLocSheetCellRanges = xQuery->queryFormulaCells(nFormulaResult);
5607                 break;
5608             }
5609             case excel::XlCellType::xlCellTypeLastCell:
5610                 xRange = Cells( uno::Any( getCount() ), uno::Any() );
5611                 [[fallthrough]]; //TODO ???
5612             case excel::XlCellType::xlCellTypeVisible:
5613                 xLocSheetCellRanges = xQuery->queryVisibleCells();
5614                 break;
5615             default:
5616                 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5617                 break;
5618         }
5619         if (xLocSheetCellRanges.is())
5620         {
5621             xRange = lcl_makeXRangeFromSheetCellRanges( getParent(), mxContext, xLocSheetCellRanges, getScDocShell() );
5622         }
5623     }
5624     catch (uno::Exception& )
5625     {
5626         DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, u"No cells were found");
5627     }
5628     return xRange;
5629 }
5630 
5631 void SAL_CALL
RemoveSubtotal()5632 ScVbaRange::RemoveSubtotal(  )
5633 {
5634     uno::Reference< sheet::XSubTotalCalculatable > xSub( mxRange, uno::UNO_QUERY_THROW );
5635     xSub->removeSubTotals();
5636 }
5637 
5638 void SAL_CALL
Subtotal(::sal_Int32 _nGroupBy,::sal_Int32 _nFunction,const uno::Sequence<::sal_Int32> & _nTotalList,const uno::Any & aReplace,const uno::Any & PageBreaks,const uno::Any &)5639 ScVbaRange::Subtotal( ::sal_Int32 _nGroupBy, ::sal_Int32 _nFunction, const uno::Sequence< ::sal_Int32 >& _nTotalList, const uno::Any& aReplace, const uno::Any& PageBreaks, const uno::Any& /*SummaryBelowData*/ )
5640 {
5641     try
5642     {
5643         bool bDoReplace = false;
5644         aReplace >>= bDoReplace;
5645         bool bAddPageBreaks = false;
5646         PageBreaks >>= bAddPageBreaks;
5647 
5648         uno::Reference< sheet::XSubTotalCalculatable> xSub(mxRange, uno::UNO_QUERY_THROW );
5649         uno::Reference< sheet::XSubTotalDescriptor > xSubDesc = xSub->createSubTotalDescriptor(true);
5650         uno::Reference< beans::XPropertySet > xSubDescPropertySet( xSubDesc, uno::UNO_QUERY_THROW );
5651         xSubDescPropertySet->setPropertyValue(u"InsertPageBreaks"_ustr, uno::Any( bAddPageBreaks));
5652         sal_Int32 nLen = _nTotalList.getLength();
5653         uno::Sequence< sheet::SubTotalColumn > aColumns( nLen );
5654         auto aColumnsRange = asNonConstRange(aColumns);
5655         for (int i = 0; i < nLen; i++)
5656         {
5657             aColumnsRange[i].Column = _nTotalList[i] - 1;
5658             switch (_nFunction)
5659             {
5660                 case excel::XlConsolidationFunction::xlAverage:
5661                     aColumnsRange[i].Function = sheet::GeneralFunction_AVERAGE;
5662                     break;
5663                 case excel::XlConsolidationFunction::xlCount:
5664                     aColumnsRange[i].Function = sheet::GeneralFunction_COUNT;
5665                     break;
5666                 case excel::XlConsolidationFunction::xlCountNums:
5667                     aColumnsRange[i].Function = sheet::GeneralFunction_COUNTNUMS;
5668                     break;
5669                 case excel::XlConsolidationFunction::xlMax:
5670                     aColumnsRange[i].Function = sheet::GeneralFunction_MAX;
5671                     break;
5672                 case excel::XlConsolidationFunction::xlMin:
5673                     aColumnsRange[i].Function = sheet::GeneralFunction_MIN;
5674                     break;
5675                 case excel::XlConsolidationFunction::xlProduct:
5676                     aColumnsRange[i].Function = sheet::GeneralFunction_PRODUCT;
5677                     break;
5678                 case excel::XlConsolidationFunction::xlStDev:
5679                     aColumnsRange[i].Function = sheet::GeneralFunction_STDEV;
5680                     break;
5681                 case excel::XlConsolidationFunction::xlStDevP:
5682                     aColumnsRange[i].Function = sheet::GeneralFunction_STDEVP;
5683                     break;
5684                 case excel::XlConsolidationFunction::xlSum:
5685                     aColumnsRange[i].Function = sheet::GeneralFunction_SUM;
5686                     break;
5687                 case excel::XlConsolidationFunction::xlUnknown:
5688                     aColumnsRange[i].Function = sheet::GeneralFunction_NONE;
5689                     break;
5690                 case excel::XlConsolidationFunction::xlVar:
5691                     aColumnsRange[i].Function = sheet::GeneralFunction_VAR;
5692                     break;
5693                 case excel::XlConsolidationFunction::xlVarP:
5694                     aColumnsRange[i].Function = sheet::GeneralFunction_VARP;
5695                     break;
5696                 default:
5697                     DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {}) ;
5698                     return;
5699             }
5700         }
5701         xSubDesc->addNew(aColumns, _nGroupBy - 1);
5702         xSub->applySubTotals(xSubDesc, bDoReplace);
5703     }
5704     catch (const uno::Exception&)
5705     {
5706         DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
5707     }
5708 }
5709 
5710 void SAL_CALL
ExportAsFixedFormat(const css::uno::Any & Type,const css::uno::Any & FileName,const css::uno::Any & Quality,const css::uno::Any & IncludeDocProperties,const css::uno::Any &,const css::uno::Any & From,const css::uno::Any & To,const css::uno::Any & OpenAfterPublish,const css::uno::Any &)5711 ScVbaRange::ExportAsFixedFormat(const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
5712     const css::uno::Any& IncludeDocProperties, const css::uno::Any& /*IgnorePrintAreas*/, const css::uno::Any& From,
5713     const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& /*FixedFormatExtClassPtr*/)
5714 {
5715     ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
5716     if (!pUnoRangesBase)
5717         throw uno::RuntimeException(u"Failed to access underlying uno range object"_ustr);
5718     ScDocShell* pShell = pUnoRangesBase->GetDocShell();
5719     if (!pShell)
5720         return;
5721 
5722     uno::Reference< frame::XModel > xModel(pShell->GetModel(), uno::UNO_SET_THROW);
5723     uno::Reference< excel::XApplication > xApplication(Application(), uno::UNO_QUERY_THROW);
5724 
5725     excel::ExportAsFixedFormatHelper(xModel, xApplication, Type, FileName, Quality,
5726         IncludeDocProperties, From, To, OpenAfterPublish);
5727 }
5728 
5729 OUString
getServiceImplName()5730 ScVbaRange::getServiceImplName()
5731 {
5732     return u"ScVbaRange"_ustr;
5733 }
5734 
5735 uno::Sequence< OUString >
getServiceNames()5736 ScVbaRange::getServiceNames()
5737 {
5738     return { u"ooo.vba.excel.Range"_ustr };
5739 }
5740 
5741 sal_Bool SAL_CALL
hasError()5742 ScVbaRange::hasError()
5743 {
5744     double dResult = 0.0;
5745     uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
5746     uno::Reference< script::XInvocation > xInvoc( xApplication->WorksheetFunction(), uno::UNO_QUERY_THROW );
5747 
5748     uno::Reference< excel::XRange > aRange( this );
5749     uno::Sequence< uno::Any > Params{ uno::Any(aRange) };
5750     uno::Sequence< sal_Int16 > OutParamIndex;
5751     uno::Sequence< uno::Any > OutParam;
5752     xInvoc->invoke( u"IsError"_ustr, Params, OutParamIndex, OutParam ) >>= dResult;
5753     return dResult > 0.0;
5754 }
5755 
5756 
5757 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_ScVbaRange_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const & args)5758 Calc_ScVbaRange_get_implementation(
5759     css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
5760 {
5761     return cppu::acquire(new ScVbaRange(args, context));
5762 }
5763 
5764 
5765 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5766