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