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