tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / vba / vbarange.cxx
blob433b069fa6d0513e59c85198eb4fe184b064e4a0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "vbarange.hxx"
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>
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>
80 #include <com/sun/star/sheet/XSubTotalCalculatable.hpp>
81 #include <com/sun/star/sheet/XSubTotalDescriptor.hpp>
82 #include <com/sun/star/sheet/GeneralFunction.hpp>
84 #include <com/sun/star/sheet/XSheetAnnotationsSupplier.hpp>
85 #include <com/sun/star/sheet/XSheetAnnotations.hpp>
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>
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>
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>
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"
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>
149 #include <global.hxx>
151 #include "vbastyle.hxx"
152 #include "vbaname.hxx"
153 #include <utility>
154 #include <vector>
155 #include <vbahelper/vbacollectionimpl.hxx>
157 #include <com/sun/star/bridge/oleautomation/Date.hpp>
158 #include <tokenarray.hxx>
159 #include <tokenuno.hxx>
161 #include <memory>
163 using namespace ::ooo::vba;
164 using namespace ::com::sun::star;
165 using ::std::vector;
167 // difference between VBA and file format width, in character units
168 const double fExtraWidth = 182.0 / 256.0;
170 const sal_Int16 supportedIndexTable[] = { excel::XlBordersIndex::xlEdgeLeft, excel::XlBordersIndex::xlEdgeTop, excel::XlBordersIndex::xlEdgeBottom, excel::XlBordersIndex::xlEdgeRight, excel::XlBordersIndex::xlDiagonalDown, excel::XlBordersIndex::xlDiagonalUp, excel::XlBordersIndex::xlInsideVertical, excel::XlBordersIndex::xlInsideHorizontal };
172 static sal_uInt16 lcl_pointsToTwips( double nVal )
174 nVal = nVal * static_cast<double>(20);
175 short nTwips = static_cast<short>(nVal);
176 return nTwips;
178 static double lcl_TwipsToPoints( sal_uInt16 nVal )
180 double nPoints = nVal;
181 return nPoints / 20;
184 static double lcl_Round2DecPlaces( double nVal )
186 nVal = (nVal * double(100));
187 tools::Long tmp = static_cast<tools::Long>(nVal);
188 if ( ( nVal - tmp ) >= 0.5 )
189 ++tmp;
190 nVal = double(tmp)/100;
191 return nVal;
194 static uno::Any lcl_makeRange( const uno::Reference< XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Any& rAny, bool bIsRows, bool bIsColumns )
196 uno::Reference< table::XCellRange > xCellRange(rAny, uno::UNO_QUERY_THROW);
197 return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( rParent, rContext, xCellRange, bIsRows, bIsColumns ) ) );
200 static uno::Reference< excel::XRange > lcl_makeXRangeFromSheetCellRanges( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRanges >& xLocSheetCellRanges, ScDocShell* pDoc )
202 uno::Reference< excel::XRange > xRange;
203 const uno::Sequence< table::CellRangeAddress > sAddresses = xLocSheetCellRanges->getRangeAddresses();
204 ScRangeList aCellRanges;
205 if ( sAddresses.hasElements() )
207 for ( const auto& rAddress : sAddresses )
209 ScRange refRange;
210 ScUnoConversion::FillScRange( refRange, rAddress );
211 aCellRanges.push_back( refRange );
213 // Single range
214 if ( aCellRanges.size() == 1 )
216 uno::Reference< table::XCellRange > xTmpRange( new ScCellRangeObj( pDoc, aCellRanges.front() ) );
217 xRange = new ScVbaRange( xParent, xContext, xTmpRange );
219 else
221 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDoc, aCellRanges ) );
222 xRange = new ScVbaRange( xParent, xContext, xRanges );
225 return xRange;
228 ScCellRangesBase* ScVbaRange::getCellRangesBase()
230 if( mxRanges.is() )
231 return dynamic_cast<ScCellRangesBase*>( mxRanges.get() );
232 if( mxRange.is() )
233 return dynamic_cast<ScCellRangesBase*>( mxRange.get() );
234 throw uno::RuntimeException(u"General Error creating range - Unknown"_ustr );
237 ScCellRangeObj* ScVbaRange::getCellRangeObj()
239 return dynamic_cast< ScCellRangeObj* >( getCellRangesBase() );
242 SfxItemSet* ScVbaRange::getCurrentDataSet( )
244 SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( getCellRangesBase() );
245 if ( !pDataSet )
246 throw uno::RuntimeException(u"Can't access Itemset for range"_ustr );
247 return pDataSet;
250 void ScVbaRange::fireChangeEvent()
252 if( !ScVbaApplication::getDocumentEventsEnabled() )
253 return;
255 ScDocument& rDoc = getScDocument();
256 const uno::Reference< script::vba::XVBAEventProcessor >& xVBAEvents = rDoc.GetVbaEventProcessor();
257 if( xVBAEvents.is() ) try
259 uno::Sequence< uno::Any > aArgs{ uno::Any(uno::Reference< excel::XRange >( this )) };
260 xVBAEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_CHANGE, aArgs );
262 catch( uno::Exception& )
267 namespace {
269 class SingleRangeEnumeration : public EnumerationHelper_BASE
271 uno::Reference< table::XCellRange > m_xRange;
272 bool bHasMore;
273 public:
274 /// @throws uno::RuntimeException
275 explicit SingleRangeEnumeration( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )), bHasMore( true ) { }
276 virtual sal_Bool SAL_CALL hasMoreElements( ) override { return bHasMore; }
277 virtual uno::Any SAL_CALL nextElement( ) override
279 if ( !bHasMore )
280 throw container::NoSuchElementException();
281 bHasMore = false;
282 return uno::Any( m_xRange );
286 // very simple class to pass to ScVbaCollectionBaseImpl containing
287 // just one item
289 class SingleRangeIndexAccess : public ::cppu::WeakImplHelper< container::XIndexAccess,
290 container::XEnumerationAccess >
292 private:
293 uno::Reference< table::XCellRange > m_xRange;
295 public:
296 explicit SingleRangeIndexAccess( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )) {}
297 // XIndexAccess
298 virtual ::sal_Int32 SAL_CALL getCount() override { return 1; }
299 virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
301 if ( Index != 0 )
302 throw lang::IndexOutOfBoundsException();
303 return uno::Any( m_xRange );
305 // XElementAccess
306 virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<table::XCellRange>::get(); }
307 virtual sal_Bool SAL_CALL hasElements() override { return true; }
308 // XEnumerationAccess
309 virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override { return new SingleRangeEnumeration( m_xRange ); }
313 class RangesEnumerationImpl : public EnumerationHelperImpl
315 bool mbIsRows;
316 bool mbIsColumns;
317 public:
318 /// @throws uno::RuntimeException
319 RangesEnumerationImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, bool bIsRows, bool bIsColumns ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), mbIsRows( bIsRows ), mbIsColumns( bIsColumns ) {}
320 virtual uno::Any SAL_CALL nextElement( ) override
322 return lcl_makeRange( m_xParent, m_xContext, m_xEnumeration->nextElement(), mbIsRows, mbIsColumns );
328 class ScVbaRangeAreas : public ScVbaCollectionBaseImpl
330 bool mbIsRows;
331 bool mbIsColumns;
332 public:
333 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 ) {}
335 // XEnumerationAccess
336 virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override;
338 // XElementAccess
339 virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<excel::XRange>::get(); }
341 virtual uno::Any createCollectionObject( const uno::Any& aSource ) override;
343 virtual OUString getServiceImplName() override { return OUString(); }
345 virtual uno::Sequence< OUString > getServiceNames() override { return uno::Sequence< OUString >(); }
349 uno::Reference< container::XEnumeration > SAL_CALL
350 ScVbaRangeAreas::createEnumeration()
352 uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
353 return new RangesEnumerationImpl( mxParent, mxContext, xEnumAccess->createEnumeration(), mbIsRows, mbIsColumns );
356 uno::Any
357 ScVbaRangeAreas::createCollectionObject( const uno::Any& aSource )
359 return lcl_makeRange( mxParent, mxContext, aSource, mbIsRows, mbIsColumns );
362 // assume that xIf is in fact a ScCellRangesBase
363 /// @throws uno::RuntimeException
364 static ScDocShell*
365 getDocShellFromIf( const uno::Reference< uno::XInterface >& xIf )
367 ScCellRangesBase* pUno = dynamic_cast<ScCellRangesBase*>( xIf.get() );
368 if ( !pUno )
369 throw uno::RuntimeException(u"Failed to access underlying uno range object"_ustr );
370 return pUno->GetDocShell();
373 /// @throws uno::RuntimeException
374 static ScDocShell*
375 getDocShellFromRange( const uno::Reference< table::XCellRange >& xRange )
377 // need the ScCellRangesBase to get docshell
378 uno::Reference< uno::XInterface > xIf( xRange );
379 return getDocShellFromIf(xIf );
382 /// @throws uno::RuntimeException
383 static ScDocShell*
384 getDocShellFromRanges( const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges )
386 // need the ScCellRangesBase to get docshell
387 uno::Reference< uno::XInterface > xIf( xRanges );
388 return getDocShellFromIf(xIf );
391 /// @throws uno::RuntimeException
392 static uno::Reference< frame::XModel > getModelFromXIf( const uno::Reference< uno::XInterface >& xIf )
394 ScDocShell* pDocShell = getDocShellFromIf(xIf );
395 return pDocShell->GetModel();
398 /// @throws uno::RuntimeException
399 static uno::Reference< frame::XModel > getModelFromRange( const uno::Reference< table::XCellRange >& xRange )
401 // the XInterface for getImplementation can be any derived interface, no need for queryInterface
402 uno::Reference< uno::XInterface > xIf( xRange );
403 return getModelFromXIf( xIf );
406 static ScDocument&
407 getDocumentFromRange( const uno::Reference< table::XCellRange >& xRange )
409 ScDocShell* pDocShell = getDocShellFromRange( xRange );
410 if ( !pDocShell )
411 throw uno::RuntimeException(u"Failed to access underlying docshell from uno range object"_ustr );
412 ScDocument& rDoc = pDocShell->GetDocument();
413 return rDoc;
416 ScDocument&
417 ScVbaRange::getScDocument()
419 if ( mxRanges.is() )
421 uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
422 uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
423 return getDocumentFromRange( xRange );
425 return getDocumentFromRange( mxRange );
428 ScDocShell*
429 ScVbaRange::getScDocShell()
431 if ( mxRanges.is() )
433 uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
434 uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
435 return getDocShellFromRange( xRange );
437 return getDocShellFromRange( mxRange );
440 ScVbaRange* ScVbaRange::getImplementation( const uno::Reference< excel::XRange >& rxRange )
442 // FIXME: always save to use dynamic_cast? Or better to (implement and) use XTunnel?
443 return dynamic_cast< ScVbaRange* >( rxRange.get() );
446 uno::Reference< frame::XModel > ScVbaRange::getUnoModel()
448 if( ScDocShell* pDocShell = getScDocShell() )
449 return pDocShell->GetModel();
450 throw uno::RuntimeException();
453 uno::Reference< frame::XModel > ScVbaRange::getUnoModel( const uno::Reference< excel::XRange >& rxRange )
455 if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
456 return pScVbaRange->getUnoModel();
457 throw uno::RuntimeException();
460 const ScRangeList& ScVbaRange::getScRangeList()
462 if( ScCellRangesBase* pScRangesBase = getCellRangesBase() )
463 return pScRangesBase->GetRangeList();
464 throw uno::RuntimeException(u"Cannot obtain UNO range implementation object"_ustr );
467 const ScRangeList& ScVbaRange::getScRangeList( const uno::Reference< excel::XRange >& rxRange )
469 if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
470 return pScVbaRange->getScRangeList();
471 throw uno::RuntimeException(u"Cannot obtain VBA range implementation object"_ustr );
474 namespace {
476 class NumFormatHelper
478 uno::Reference< util::XNumberFormatsSupplier > mxSupplier;
479 uno::Reference< beans::XPropertySet > mxRangeProps;
480 uno::Reference< util::XNumberFormats > mxFormats;
481 public:
482 explicit NumFormatHelper( const uno::Reference< table::XCellRange >& xRange )
484 mxSupplier.set( getModelFromRange( xRange ), uno::UNO_QUERY_THROW );
485 mxRangeProps.set( xRange, uno::UNO_QUERY_THROW);
486 mxFormats = mxSupplier->getNumberFormats();
488 uno::Reference< beans::XPropertySet > getNumberProps()
490 tools::Long nIndexKey = 0;
491 uno::Any aValue = mxRangeProps->getPropertyValue( u"NumberFormat"_ustr );
492 aValue >>= nIndexKey;
494 if ( mxFormats.is() )
495 return mxFormats->getByKey( nIndexKey );
496 return uno::Reference< beans::XPropertySet > ();
499 bool isBooleanType()
502 return (getNumberFormat() & util::NumberFormat::LOGICAL) != 0;
505 bool isDateType()
507 sal_Int16 nType = getNumberFormat();
508 return ( nType & util::NumberFormat::DATETIME ) != 0;
511 OUString getNumberFormatString()
513 uno::Reference< uno::XInterface > xIf( mxRangeProps, uno::UNO_QUERY_THROW );
514 ScCellRangesBase* pUnoCellRange = dynamic_cast<ScCellRangesBase*>( xIf.get() );
515 if ( pUnoCellRange )
518 SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( pUnoCellRange );
519 SfxItemState eState = pDataSet->GetItemState( ATTR_VALUE_FORMAT);
520 // one of the cells in the range is not like the other ;-)
521 // so return a zero length format to indicate that
522 if ( eState == SfxItemState::INVALID )
523 return OUString();
526 uno::Reference< beans::XPropertySet > xNumberProps( getNumberProps(), uno::UNO_SET_THROW );
527 OUString aFormatString;
528 uno::Any aString = xNumberProps->getPropertyValue( u"FormatString"_ustr );
529 aString >>= aFormatString;
530 return aFormatString;
533 sal_Int16 getNumberFormat()
535 uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
536 sal_Int16 nType = ::comphelper::getINT16(
537 xNumberProps->getPropertyValue( u"Type"_ustr ) );
538 return nType;
541 void setNumberFormat( const OUString& rFormat )
543 // #163288# treat "General" as "Standard" format
544 sal_Int32 nNewIndex = 0;
545 if( !rFormat.equalsIgnoreAsciiCase( "General" ) )
547 lang::Locale aLocale;
548 uno::Reference< beans::XPropertySet > xNumProps = getNumberProps();
549 xNumProps->getPropertyValue( u"Locale"_ustr ) >>= aLocale;
550 nNewIndex = mxFormats->queryKey( rFormat, aLocale, false );
551 if ( nNewIndex == -1 ) // format not defined
552 nNewIndex = mxFormats->addNew( rFormat, aLocale );
554 mxRangeProps->setPropertyValue( u"NumberFormat"_ustr, uno::Any( nNewIndex ) );
557 void setNumberFormat( sal_Int16 nType )
559 uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
560 lang::Locale aLocale;
561 xNumberProps->getPropertyValue( u"Locale"_ustr ) >>= aLocale;
562 uno::Reference<util::XNumberFormatTypes> xTypes( mxFormats, uno::UNO_QUERY );
563 if ( xTypes.is() )
565 sal_Int32 nNewIndex = xTypes->getStandardFormat( nType, aLocale );
566 mxRangeProps->setPropertyValue( u"NumberFormat"_ustr, uno::Any( nNewIndex ) );
572 struct CellPos
574 CellPos( sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nArea ):m_nRow(nRow), m_nCol(nCol), m_nArea( nArea ) {};
575 sal_Int32 m_nRow;
576 sal_Int32 m_nCol;
577 sal_Int32 m_nArea;
582 typedef ::cppu::WeakImplHelper< container::XEnumeration > CellsEnumeration_BASE;
583 typedef ::std::vector< CellPos > vCellPos;
585 namespace {
587 // #FIXME - QUICK
588 // we could probably could and should modify CellsEnumeration below
589 // to handle rows and columns (but I do this separately for now
590 // and... this class only handles single areas (does it have to handle
591 // multi area ranges??)
592 class ColumnsRowEnumeration: public CellsEnumeration_BASE
594 uno::Reference< excel::XRange > mxRange;
595 sal_Int32 mMaxElems;
596 sal_Int32 mCurElem;
598 public:
599 ColumnsRowEnumeration( uno::Reference< excel::XRange > xRange, sal_Int32 nElems ) : mxRange(std::move( xRange )), mMaxElems( nElems ), mCurElem( 0 )
603 virtual sal_Bool SAL_CALL hasMoreElements() override { return mCurElem < mMaxElems; }
605 virtual uno::Any SAL_CALL nextElement() override
607 if ( !hasMoreElements() )
608 throw container::NoSuchElementException();
609 sal_Int32 vbaIndex = 1 + mCurElem++;
610 return uno::Any( mxRange->Item( uno::Any( vbaIndex ), uno::Any() ) );
614 class CellsEnumeration : public CellsEnumeration_BASE
616 uno::WeakReference< XHelperInterface > mxParent;
617 uno::Reference< uno::XComponentContext > mxContext;
618 uno::Reference< XCollection > m_xAreas;
619 vCellPos m_CellPositions;
620 vCellPos::const_iterator m_it;
622 /// @throws uno::RuntimeException
623 uno::Reference< table::XCellRange > getArea( sal_Int32 nVBAIndex )
625 if ( nVBAIndex < 1 || nVBAIndex > m_xAreas->getCount() )
626 throw uno::RuntimeException();
627 uno::Reference< excel::XRange > xRange( m_xAreas->Item( uno::Any(nVBAIndex), uno::Any() ), uno::UNO_QUERY_THROW );
628 uno::Reference< table::XCellRange > xCellRange( ScVbaRange::getCellRange( xRange ), uno::UNO_QUERY_THROW );
629 return xCellRange;
632 void populateArea( sal_Int32 nVBAIndex )
634 uno::Reference< table::XCellRange > xRange = getArea( nVBAIndex );
635 uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, uno::UNO_QUERY_THROW );
636 sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
637 sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
638 for ( sal_Int32 i=0; i<nRowCount; ++i )
640 for ( sal_Int32 j=0; j<nColCount; ++j )
641 m_CellPositions.emplace_back( i,j,nVBAIndex );
644 public:
645 CellsEnumeration( const uno::Reference< XHelperInterface >& xParent, uno::Reference< uno::XComponentContext > xContext, uno::Reference< XCollection > xAreas ): mxParent( xParent ), mxContext(std::move( xContext )), m_xAreas(std::move( xAreas ))
647 sal_Int32 nItems = m_xAreas->getCount();
648 for ( sal_Int32 index=1; index <= nItems; ++index )
650 populateArea( index );
652 m_it = m_CellPositions.begin();
654 virtual sal_Bool SAL_CALL hasMoreElements() override { return m_it != m_CellPositions.end(); }
656 virtual uno::Any SAL_CALL nextElement() override
658 if ( !hasMoreElements() )
659 throw container::NoSuchElementException();
660 CellPos aPos = *m_it++;
662 uno::Reference< table::XCellRange > xRangeArea = getArea( aPos.m_nArea );
663 uno::Reference< table::XCellRange > xCellRange( xRangeArea->getCellByPosition( aPos.m_nCol, aPos.m_nRow ), uno::UNO_QUERY_THROW );
664 return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( mxParent, mxContext, xCellRange ) ) );
671 constexpr OUString ISVISIBLE = u"IsVisible"_ustr;
672 const char EQUALS[] = "=";
673 const char NOTEQUALS[] = "<>";
674 const char GREATERTHAN[] = ">";
675 const char GREATERTHANEQUALS[] = ">=";
676 const char LESSTHAN[] = "<";
677 const char LESSTHANEQUALS[] = "<=";
678 constexpr OUString STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY(u"The command you chose cannot be performed with multiple selections.\nSelect a single range and click the command again"_ustr);
679 constexpr OUString CELLSTYLE = u"CellStyle"_ustr;
681 namespace {
683 class CellValueSetter : public ValueSetter
685 protected:
686 uno::Any maValue;
687 public:
688 explicit CellValueSetter( uno::Any aValue );
689 virtual bool processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell ) override;
690 virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
696 CellValueSetter::CellValueSetter( uno::Any aValue ): maValue(std::move( aValue )) {}
698 void
699 CellValueSetter::visitNode( sal_Int32 /*i*/, sal_Int32 /*j*/, const uno::Reference< table::XCell >& xCell )
701 processValue( maValue, xCell );
704 bool
705 CellValueSetter::processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell )
708 bool isExtracted = false;
709 switch ( aValue.getValueTypeClass() )
711 case uno::TypeClass_BOOLEAN:
713 bool bState = false;
714 if ( aValue >>= bState )
716 uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
717 if ( bState )
718 xCell->setValue( double(1) );
719 else
720 xCell->setValue( double(0) );
721 NumFormatHelper cellNumFormat( xRange );
722 cellNumFormat.setNumberFormat( util::NumberFormat::LOGICAL );
724 break;
726 case uno::TypeClass_STRING:
728 OUString aString;
729 if ( aValue >>= aString )
731 // The required behavior for a string value is:
732 // 1. If the first character is a single quote, use the rest as a string cell, regardless of the cell's number format.
733 // 2. Otherwise, if the cell's number format is "text", use the string value as a string cell.
734 // 3. Otherwise, parse the string value in English locale, and apply a corresponding number format with the cell's locale
735 // if the cell's number format was "General".
736 // Case 1 is handled here, the rest in ScCellObj::InputEnglishString
738 if ( aString.toChar() == '\'' ) // case 1 - handle with XTextRange
740 OUString aRemainder( aString.copy(1) ); // strip the quote
741 uno::Reference< text::XTextRange > xTextRange( xCell, uno::UNO_QUERY_THROW );
742 xTextRange->setString( aRemainder );
744 else
746 // call implementation method InputEnglishString
747 ScCellObj* pCellObj = dynamic_cast< ScCellObj* >( xCell.get() );
748 if ( pCellObj )
749 pCellObj->InputEnglishString( aString );
752 else
753 isExtracted = false;
754 break;
756 default:
758 double nDouble = 0.0;
759 if ( aValue >>= nDouble )
761 uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
762 NumFormatHelper cellFormat( xRange );
763 // If we are setting a number and the cell types was logical
764 // then we need to reset the logical format. ( see case uno::TypeClass_BOOLEAN:
765 // handling above )
766 if ( cellFormat.isBooleanType() )
767 cellFormat.setNumberFormat(u"General"_ustr);
768 xCell->setValue( nDouble );
770 else
771 isExtracted = false;
772 break;
775 return isExtracted;
779 namespace {
781 class CellValueGetter : public ValueGetter
783 protected:
784 RangeValueType meValueType;
785 uno::Any maValue;
786 public:
787 CellValueGetter(RangeValueType eValueType) { meValueType = eValueType; }
788 virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
789 virtual void processValue( const uno::Any& aValue ) override;
790 const uno::Any& getValue() const override { return maValue; }
795 void
796 CellValueGetter::processValue( const uno::Any& aValue )
798 maValue = aValue;
800 void CellValueGetter::visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell )
802 uno::Any aValue;
803 table::CellContentType eCellContentType = xCell->getType();
804 if( eCellContentType == table::CellContentType_VALUE || eCellContentType == table::CellContentType_FORMULA )
806 if ( eCellContentType == table::CellContentType_FORMULA )
809 OUString sFormula = xCell->getFormula();
810 if ( sFormula == "=TRUE()" )
811 aValue <<= true;
812 else if ( sFormula == "=FALSE()" )
813 aValue <<= false;
814 else
816 uno::Reference< beans::XPropertySet > xProp( xCell, uno::UNO_QUERY_THROW );
818 sal_Int32 nResultType = sheet::FormulaResult::VALUE;
819 // some formulas give textual results
820 xProp->getPropertyValue( u"FormulaResultType2"_ustr ) >>= nResultType;
822 if ( nResultType == sheet::FormulaResult::STRING )
824 uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
825 aValue <<= xTextRange->getString();
827 else
828 aValue <<= xCell->getValue();
831 else
833 uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
834 NumFormatHelper cellFormat( xRange );
835 if ( cellFormat.isBooleanType() )
836 aValue <<= ( xCell->getValue() != 0.0 );
837 else if ( cellFormat.isDateType() && meValueType == RangeValueType::value)
838 aValue <<= bridge::oleautomation::Date( xCell->getValue() );
839 else
840 aValue <<= xCell->getValue();
843 if( eCellContentType == table::CellContentType_TEXT )
845 uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
846 aValue <<= xTextRange->getString();
848 processValue( aValue );
851 namespace {
853 class CellFormulaValueSetter : public CellValueSetter
855 private:
856 ScDocument& m_rDoc;
857 formula::FormulaGrammar::Grammar m_eGrammar;
858 public:
859 CellFormulaValueSetter( const uno::Any& aValue, ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ):CellValueSetter( aValue ), m_rDoc( rDoc ), m_eGrammar( eGram ){}
860 protected:
861 bool processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell ) override
863 OUString sFormula;
864 double aDblValue = 0.0;
865 if ( aValue >>= sFormula )
867 // convert to GRAM_API style grammar because XCell::setFormula
868 // always compile it in that grammar. Perhaps
869 // css.sheet.FormulaParser should be used in future to directly
870 // pass formula tokens when that API stabilizes.
871 if ( m_eGrammar != formula::FormulaGrammar::GRAM_API && ( o3tl::starts_with(o3tl::trim(sFormula), u"=") ) )
873 uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
874 ScCellRangesBase* pUnoRangesBase
875 = dynamic_cast< ScCellRangesBase* >( xIf.get() );
876 if ( pUnoRangesBase )
878 const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
879 if (!rCellRanges.empty())
881 ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, m_eGrammar );
882 // compile the string in the format passed in
883 std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sFormula));
884 // convert to API grammar
885 aCompiler.SetGrammar( formula::FormulaGrammar::GRAM_API );
886 OUString sConverted;
887 aCompiler.CreateStringFromTokenArray(sConverted);
888 sFormula = EQUALS + sConverted;
893 xCell->setFormula( sFormula );
894 return true;
896 else if ( aValue >>= aDblValue )
898 xCell->setValue( aDblValue );
899 return true;
901 return false;
906 class CellFormulaValueGetter : public CellValueGetter
908 private:
909 ScDocument& m_rDoc;
910 formula::FormulaGrammar::Grammar m_eGrammar;
911 public:
912 CellFormulaValueGetter(ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ) :
913 CellValueGetter( RangeValueType::value ), m_rDoc( rDoc ), m_eGrammar( eGram ) {}
914 virtual void visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell ) override
916 uno::Any aValue;
917 aValue <<= xCell->getFormula();
918 // XCell::getFormula() returns the formula in API grammar, convert.
919 if ((xCell->getType() == table::CellContentType_FORMULA)
920 && m_eGrammar != formula::FormulaGrammar::GRAM_API)
922 uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
923 ScCellRangesBase* pUnoRangesBase
924 = dynamic_cast< ScCellRangesBase* >( xIf.get() );
925 if (pUnoRangesBase)
927 OUString sVal;
928 aValue >>= sVal;
929 const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
930 if (!rCellRanges.empty())
932 // Compile string from API grammar.
933 ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, formula::FormulaGrammar::GRAM_API );
934 std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sVal));
935 // Convert to desired grammar.
936 aCompiler.SetGrammar( m_eGrammar );
937 OUString sConverted;
938 aCompiler.CreateStringFromTokenArray(sConverted);
939 sVal = EQUALS + sConverted;
940 aValue <<= sVal;
945 processValue( aValue );
950 class Dim2ArrayValueGetter : public ArrayVisitor
952 protected:
953 uno::Any maValue;
954 ValueGetter& mValueGetter;
955 void processValue( sal_Int32 x, sal_Int32 y, const uno::Any& aValue )
957 uno::Sequence< uno::Sequence< uno::Any > >& aMatrix = const_cast<css::uno::Sequence<css::uno::Sequence<css::uno::Any>> &>(*o3tl::doAccess<uno::Sequence<uno::Sequence<uno::Any>>>(maValue));
958 aMatrix.getArray()[x].getArray()[y] = aValue;
961 public:
962 Dim2ArrayValueGetter(sal_Int32 nRowCount, sal_Int32 nColCount, ValueGetter& rValueGetter ): mValueGetter(rValueGetter)
964 uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
965 aMatrix.realloc( nRowCount );
966 auto pMatrix = aMatrix.getArray();
967 for ( sal_Int32 index = 0; index < nRowCount; ++index )
968 pMatrix[index].realloc( nColCount );
969 maValue <<= aMatrix;
971 void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
974 mValueGetter.visitNode( x, y, xCell );
975 processValue( x, y, mValueGetter.getValue() );
977 const uno::Any& getValue() const { return maValue; }
983 constexpr OUString sNA = u"#N/A"_ustr;
985 namespace {
987 class Dim1ArrayValueSetter : public ArrayVisitor
989 uno::Sequence< uno::Any > aMatrix;
990 sal_Int32 nColCount;
991 ValueSetter& mCellValueSetter;
992 public:
993 Dim1ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ):mCellValueSetter( rCellValueSetter )
995 aValue >>= aMatrix;
996 nColCount = aMatrix.getLength();
998 virtual void visitNode( sal_Int32 /*x*/, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
1000 if ( y < nColCount )
1001 mCellValueSetter.processValue( aMatrix[ y ], xCell );
1002 else
1003 mCellValueSetter.processValue( uno::Any( sNA ), xCell );
1007 class Dim2ArrayValueSetter : public ArrayVisitor
1009 uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
1010 ValueSetter& mCellValueSetter;
1011 sal_Int32 nRowCount;
1012 sal_Int32 nColCount;
1013 public:
1014 Dim2ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ) : mCellValueSetter( rCellValueSetter )
1016 aValue >>= aMatrix;
1017 nRowCount = aMatrix.getLength();
1018 nColCount = aMatrix[0].getLength();
1021 virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
1023 if ( x < nRowCount && y < nColCount )
1024 mCellValueSetter.processValue( aMatrix[ x ][ y ], xCell );
1025 else
1026 mCellValueSetter.processValue( uno::Any( sNA ), xCell );
1031 class RangeProcessor
1033 public:
1034 virtual void process( const uno::Reference< excel::XRange >& xRange ) = 0;
1036 protected:
1037 ~RangeProcessor() {}
1040 class RangeValueProcessor : public RangeProcessor
1042 const uno::Any& m_aVal;
1043 public:
1044 explicit RangeValueProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
1045 virtual ~RangeValueProcessor() {}
1046 virtual void process( const uno::Reference< excel::XRange >& xRange ) override
1048 xRange->setValue( m_aVal );
1052 class RangeFormulaProcessor : public RangeProcessor
1054 const uno::Any& m_aVal;
1055 public:
1056 explicit RangeFormulaProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
1057 virtual ~RangeFormulaProcessor() {}
1058 virtual void process( const uno::Reference< excel::XRange >& xRange ) override
1060 xRange->setFormula( m_aVal );
1064 class RangeCountProcessor : public RangeProcessor
1066 sal_Int32 nCount;
1067 public:
1068 RangeCountProcessor():nCount(0){}
1069 virtual ~RangeCountProcessor() {}
1070 virtual void process( const uno::Reference< excel::XRange >& xRange ) override
1072 nCount = nCount + xRange->getCount();
1074 sal_Int32 value() { return nCount; }
1076 class AreasVisitor
1078 private:
1079 uno::Reference< XCollection > m_Areas;
1080 public:
1081 explicit AreasVisitor( uno::Reference< XCollection > xAreas ):m_Areas(std::move( xAreas )){}
1083 void visit( RangeProcessor& processor )
1085 if ( m_Areas.is() )
1087 sal_Int32 nItems = m_Areas->getCount();
1088 for ( sal_Int32 index=1; index <= nItems; ++index )
1090 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
1091 processor.process( xRange );
1097 class RangeHelper
1099 uno::Reference< table::XCellRange > m_xCellRange;
1101 public:
1102 /// @throws uno::RuntimeException
1103 explicit RangeHelper( uno::Reference< table::XCellRange > xCellRange ) : m_xCellRange(std::move( xCellRange ))
1105 if ( !m_xCellRange.is() )
1106 throw uno::RuntimeException();
1108 /// @throws uno::RuntimeException
1109 explicit RangeHelper( const uno::Any& rCellRange )
1111 m_xCellRange.set(rCellRange, uno::UNO_QUERY_THROW);
1113 /// @throws uno::RuntimeException
1114 uno::Reference< sheet::XSheetCellRange > getSheetCellRange() const
1116 return uno::Reference< sheet::XSheetCellRange >(m_xCellRange, uno::UNO_QUERY_THROW);
1118 /// @throws uno::RuntimeException
1119 uno::Reference< sheet::XSpreadsheet > getSpreadSheet() const
1121 return getSheetCellRange()->getSpreadsheet();
1124 /// @throws uno::RuntimeException
1125 uno::Reference< table::XCellRange > getCellRangeFromSheet() const
1127 return uno::Reference< table::XCellRange >(getSpreadSheet(), uno::UNO_QUERY_THROW );
1130 /// @throws uno::RuntimeException
1131 uno::Reference< sheet::XCellRangeAddressable > getCellRangeAddressable() const
1133 return uno::Reference< sheet::XCellRangeAddressable >(m_xCellRange, ::uno::UNO_QUERY_THROW);
1137 /// @throws uno::RuntimeException
1138 uno::Reference< sheet::XSheetCellCursor > getSheetCellCursor() const
1140 return uno::Reference< sheet::XSheetCellCursor >( getSpreadSheet()->createCursorByRange( getSheetCellRange() ), uno::UNO_SET_THROW );
1143 static uno::Reference< excel::XRange > createRangeFromRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference<uno::XComponentContext >& xContext,
1144 const uno::Reference< table::XCellRange >& xRange, const uno::Reference< sheet::XCellRangeAddressable >& xCellRangeAddressable )
1146 const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
1147 return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext,
1148 xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow)));
1155 bool
1156 ScVbaRange::getCellRangesForAddress( ScRefFlags& rResFlags, std::u16string_view sAddress, ScDocShell* pDocSh, ScRangeList& rCellRanges, formula::FormulaGrammar::AddressConvention eConv, char cDelimiter )
1159 if ( pDocSh )
1161 ScDocument& rDoc = pDocSh->GetDocument();
1162 rResFlags = rCellRanges.Parse( sAddress, rDoc, eConv, 0, cDelimiter );
1163 if ( rResFlags & ScRefFlags::VALID )
1165 return true;
1168 return false;
1171 bool getScRangeListForAddress( const OUString& sName, ScDocShell* pDocSh, const ScRange& refRange, ScRangeList& aCellRanges, formula::FormulaGrammar::AddressConvention aConv )
1173 // see if there is a match with a named range
1174 uno::Reference< container::XNameAccess > xNameAccess( pDocSh->GetModel()->getPropertyValue( u"NamedRanges"_ustr ), uno::UNO_QUERY_THROW );
1175 // Strange enough you can have Range( "namedRange1, namedRange2, etc," )
1176 // loop around each ',' separated name
1177 std::vector< OUString > vNames;
1178 sal_Int32 nIndex = 0;
1181 OUString aToken = sName.getToken( 0, ',', nIndex );
1182 vNames.push_back( aToken );
1183 } while ( nIndex >= 0 );
1185 if ( vNames.empty() )
1186 vNames.push_back( sName );
1188 for ( const auto& rName : vNames )
1190 formula::FormulaGrammar::AddressConvention eConv = aConv;
1191 // spaces are illegal ( but the user of course can enter them )
1192 OUString sAddress = rName.trim();
1193 // if a local name ( on the active sheet ) exists this will
1194 // take precedence over a global with the same name
1195 if ( !xNameAccess->hasByName( sAddress ) )
1197 // try a local name
1198 ScDocument& rDoc = pDocSh->GetDocument();
1199 SCTAB nCurTab = ScDocShell::GetCurTab();
1200 ScRangeName* pRangeName = rDoc.GetRangeName(nCurTab);
1201 if (pRangeName)
1203 // TODO: Handle local names correctly:
1204 // bool bLocalName = pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sAddress)) != nullptr;
1207 char aChar = 0;
1208 if ( xNameAccess->hasByName( sAddress ) )
1210 uno::Reference< sheet::XNamedRange > xNamed( xNameAccess->getByName( sAddress ), uno::UNO_QUERY_THROW );
1211 sAddress = xNamed->getContent();
1212 // As the address comes from OOO, the addressing
1213 // style is may not be XL_A1
1214 eConv = pDocSh->GetDocument().GetAddressConvention();
1215 aChar = ';';
1218 ScRefFlags nFlags = ScRefFlags::ZERO;
1219 if ( !ScVbaRange::getCellRangesForAddress( nFlags, sAddress, pDocSh, aCellRanges, eConv, aChar ) )
1220 return false;
1222 bool bTabFromReferrer = !( nFlags & ScRefFlags::TAB_3D );
1224 for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
1226 ScRange & rRange = aCellRanges[ i ];
1227 rRange.aStart.SetCol( refRange.aStart.Col() + rRange.aStart.Col() );
1228 rRange.aStart.SetRow( refRange.aStart.Row() + rRange.aStart.Row() );
1229 rRange.aStart.SetTab( bTabFromReferrer ? refRange.aStart.Tab() : rRange.aStart.Tab() );
1230 rRange.aEnd.SetCol( refRange.aStart.Col() + rRange.aEnd.Col() );
1231 rRange.aEnd.SetRow( refRange.aStart.Row() + rRange.aEnd.Row() );
1232 rRange.aEnd.SetTab( bTabFromReferrer ? refRange.aEnd.Tab() : rRange.aEnd.Tab() );
1235 return true;
1238 /// @throws uno::RuntimeException
1239 static rtl::Reference<ScVbaRange>
1240 getRangeForName( const uno::Reference< uno::XComponentContext >& xContext, const OUString& sName, ScDocShell* pDocSh, const table::CellRangeAddress& pAddr, formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_XL_A1 )
1242 ScRangeList aCellRanges;
1243 ScRange refRange;
1244 ScUnoConversion::FillScRange( refRange, pAddr );
1245 if ( !getScRangeListForAddress ( sName, pDocSh, refRange, aCellRanges, eConv ) )
1246 throw uno::RuntimeException();
1247 // Single range
1248 if ( aCellRanges.size() == 1 )
1250 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pDocSh, aCellRanges.front() ) );
1251 uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRange );
1252 return new ScVbaRange( xFixThisParent, xContext, xRange );
1254 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDocSh, aCellRanges ) );
1256 uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRanges );
1257 return new ScVbaRange( xFixThisParent, xContext, xRanges );
1260 namespace {
1262 /// @throws uno::RuntimeException
1263 template< typename RangeType >
1264 table::CellRangeAddress lclGetRangeAddress( const uno::Reference< RangeType >& rxCellRange )
1266 return uno::Reference< sheet::XCellRangeAddressable >( rxCellRange, uno::UNO_QUERY_THROW )->getRangeAddress();
1269 /// @throws uno::RuntimeException
1270 void lclClearRange( const uno::Reference< table::XCellRange >& rxCellRange )
1272 using namespace ::com::sun::star::sheet::CellFlags;
1273 sal_Int32 const nFlags = VALUE | DATETIME | STRING | ANNOTATION | FORMULA | HARDATTR | STYLES | EDITATTR | FORMATTED;
1274 uno::Reference< sheet::XSheetOperation > xSheetOperation( rxCellRange, uno::UNO_QUERY_THROW );
1275 xSheetOperation->clearContents( nFlags );
1278 /// @throws uno::RuntimeException
1279 uno::Reference< sheet::XSheetCellRange > lclExpandToMerged( const uno::Reference< table::XCellRange >& rxCellRange, bool bRecursive )
1281 uno::Reference< sheet::XSheetCellRange > xNewCellRange( rxCellRange, uno::UNO_QUERY_THROW );
1282 uno::Reference< sheet::XSpreadsheet > xSheet( xNewCellRange->getSpreadsheet(), uno::UNO_SET_THROW );
1283 table::CellRangeAddress aNewAddress = lclGetRangeAddress( xNewCellRange );
1284 table::CellRangeAddress aOldAddress;
1285 // expand as long as there are new merged ranges included
1288 aOldAddress = aNewAddress;
1289 uno::Reference< sheet::XSheetCellCursor > xCursor( xSheet->createCursorByRange( xNewCellRange ), uno::UNO_SET_THROW );
1290 if (xCursor.is())
1292 xCursor->collapseToMergedArea();
1293 xNewCellRange.set( xCursor, uno::UNO_QUERY_THROW );
1294 aNewAddress = lclGetRangeAddress( xNewCellRange );
1297 while( bRecursive && (aOldAddress != aNewAddress) );
1298 return xNewCellRange;
1301 /// @throws uno::RuntimeException
1302 uno::Reference< sheet::XSheetCellRangeContainer > lclExpandToMerged( const uno::Reference< sheet::XSheetCellRangeContainer >& rxCellRanges )
1304 if( !rxCellRanges.is() )
1305 throw uno::RuntimeException(u"Missing cell ranges object"_ustr );
1306 sal_Int32 nCount = rxCellRanges->getCount();
1307 if( nCount < 1 )
1308 throw uno::RuntimeException(u"Missing cell ranges object"_ustr );
1310 ScRangeList aScRanges;
1311 for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
1313 uno::Reference< table::XCellRange > xRange( rxCellRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
1314 table::CellRangeAddress aRangeAddr = lclGetRangeAddress( lclExpandToMerged( xRange, /*bRecursive*/true ) );
1315 ScRange aScRange;
1316 ScUnoConversion::FillScRange( aScRange, aRangeAddr );
1317 aScRanges.push_back( aScRange );
1319 return new ScCellRangesObj( getDocShellFromRanges( rxCellRanges ), aScRanges );
1322 /// @throws uno::RuntimeException
1323 void lclExpandAndMerge( const uno::Reference< table::XCellRange >& rxCellRange, bool bMerge )
1325 uno::Reference< util::XMergeable > xMerge( lclExpandToMerged( rxCellRange, true ), uno::UNO_QUERY_THROW );
1326 // Calc cannot merge over merged ranges, always unmerge first
1327 xMerge->merge( false );
1328 if( !bMerge )
1329 return;
1331 // clear all contents of the covered cells (not the top-left cell)
1332 table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
1333 sal_Int32 nLastColIdx = aRangeAddr.EndColumn - aRangeAddr.StartColumn;
1334 sal_Int32 nLastRowIdx = aRangeAddr.EndRow - aRangeAddr.StartRow;
1335 // clear cells of top row, right of top-left cell
1336 if( nLastColIdx > 0 )
1337 lclClearRange( rxCellRange->getCellRangeByPosition( 1, 0, nLastColIdx, 0 ) );
1338 // clear all rows below top row
1339 if( nLastRowIdx > 0 )
1340 lclClearRange( rxCellRange->getCellRangeByPosition( 0, 1, nLastColIdx, nLastRowIdx ) );
1341 // merge the range
1342 xMerge->merge( true );
1345 /// @throws uno::RuntimeException
1346 util::TriState lclGetMergedState( const uno::Reference< table::XCellRange >& rxCellRange )
1348 /* 1) Check if range is completely inside one single merged range. To do
1349 this, try to extend from top-left cell only (not from entire range).
1350 This will exclude cases where this range consists of several merged
1351 ranges (or parts of them). */
1352 table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
1353 uno::Reference< table::XCellRange > xTopLeft( rxCellRange->getCellRangeByPosition( 0, 0, 0, 0 ), uno::UNO_SET_THROW );
1354 uno::Reference< sheet::XSheetCellRange > xExpanded( lclExpandToMerged( xTopLeft, false ), uno::UNO_SET_THROW );
1355 table::CellRangeAddress aExpAddr = lclGetRangeAddress( xExpanded );
1356 // check that expanded range has more than one cell (really merged)
1357 if( ((aExpAddr.StartColumn < aExpAddr.EndColumn) || (aExpAddr.StartRow < aExpAddr.EndRow)) && ScUnoConversion::Contains( aExpAddr, aRangeAddr ) )
1358 return util::TriState_YES;
1360 /* 2) Check if this range contains any merged cells (completely or
1361 partly). This seems to be hardly possible via API, as
1362 XMergeable::getIsMerged() returns only true, if the top-left cell of a
1363 merged range is part of this range, so cases where just the lower part
1364 of a merged range is part of this range are not covered. */
1365 ScRange aScRange;
1366 ScUnoConversion::FillScRange( aScRange, aRangeAddr );
1367 bool bHasMerged = getDocumentFromRange( rxCellRange ).HasAttrib( aScRange, HasAttrFlags::Merged | HasAttrFlags::Overlapped );
1368 return bHasMerged ? util::TriState_INDETERMINATE : util::TriState_NO;
1371 } // namespace
1373 css::uno::Reference< excel::XRange >
1374 ScVbaRange::getRangeObjectForName(
1375 const uno::Reference< uno::XComponentContext >& xContext, const OUString& sRangeName,
1376 ScDocShell* pDocSh, formula::FormulaGrammar::AddressConvention eConv )
1378 table::CellRangeAddress refAddr;
1379 return getRangeForName( xContext, sRangeName, pDocSh, refAddr, eConv );
1382 /// @throws uno::RuntimeException
1383 static table::CellRangeAddress getCellRangeAddressForVBARange( const uno::Any& aParam, ScDocShell* pDocSh )
1385 uno::Reference< table::XCellRange > xRangeParam;
1386 switch ( aParam.getValueTypeClass() )
1388 case uno::TypeClass_STRING:
1390 OUString rString;
1391 aParam >>= rString;
1392 ScRangeList aCellRanges;
1393 ScRange refRange;
1394 if ( getScRangeListForAddress ( rString, pDocSh, refRange, aCellRanges ) )
1396 if ( aCellRanges.size() == 1 )
1398 table::CellRangeAddress aRangeAddress;
1399 ScUnoConversion::FillApiRange( aRangeAddress, aCellRanges.front() );
1400 return aRangeAddress;
1404 break;
1406 case uno::TypeClass_INTERFACE:
1408 uno::Reference< excel::XRange > xRange;
1409 aParam >>= xRange;
1410 if ( xRange.is() )
1411 xRange->getCellRange() >>= xRangeParam;
1413 break;
1415 default:
1416 throw uno::RuntimeException(u"Can't extract CellRangeAddress from type"_ustr );
1418 return lclGetRangeAddress( xRangeParam );
1421 /// @throws uno::RuntimeException
1422 static uno::Reference< XCollection >
1423 lcl_setupBorders( const uno::Reference< excel::XRange >& xParentRange, const uno::Reference<uno::XComponentContext>& xContext, const uno::Reference< table::XCellRange >& xRange )
1425 uno::Reference< XHelperInterface > xParent( xParentRange, uno::UNO_QUERY_THROW );
1426 ScDocument& rDoc = getDocumentFromRange(xRange);
1427 ScVbaPalette aPalette( rDoc.GetDocumentShell() );
1428 uno::Reference< XCollection > borders( new ScVbaBorders( xParent, xContext, xRange, aPalette ) );
1429 return borders;
1432 ScVbaRange::ScVbaRange( uno::Sequence< uno::Any> const & args,
1433 uno::Reference< uno::XComponentContext> const & xContext ) : ScVbaRange_BASE( getXSomethingFromArgs< XHelperInterface >( args, 0 ), xContext, getXSomethingFromArgs< beans::XPropertySet >( args, 1, false ), getModelFromXIf( getXSomethingFromArgs< uno::XInterface >( args, 1 ) ), true ), mbIsRows( false ), mbIsColumns( false )
1435 mxRange.set( mxPropertySet, uno::UNO_QUERY );
1436 mxRanges.set( mxPropertySet, uno::UNO_QUERY );
1437 uno::Reference< container::XIndexAccess > xIndex;
1438 if ( mxRange.is() )
1440 xIndex = new SingleRangeIndexAccess( mxRange );
1442 else if ( mxRanges.is() )
1444 xIndex.set( mxRanges, uno::UNO_QUERY_THROW );
1446 m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
1449 ScVbaRange::ScVbaRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< table::XCellRange >& xRange, bool bIsRows, bool bIsColumns )
1450 : ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRange, uno::UNO_QUERY_THROW ), getModelFromRange( xRange), true ), mxRange( xRange ),
1451 mbIsRows( bIsRows ),
1452 mbIsColumns( bIsColumns )
1454 if ( !xContext.is() )
1455 throw lang::IllegalArgumentException(u"context is not set "_ustr, uno::Reference< uno::XInterface >() , 1 );
1456 if ( !xRange.is() )
1457 throw lang::IllegalArgumentException(u"range is not set "_ustr, uno::Reference< uno::XInterface >() , 1 );
1459 uno::Reference< container::XIndexAccess > xIndex( new SingleRangeIndexAccess( xRange ) );
1460 m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
1464 ScVbaRange::ScVbaRange(const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges, bool bIsRows, bool bIsColumns)
1465 : ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRanges, uno::UNO_QUERY_THROW ), getModelFromXIf( uno::Reference< uno::XInterface >( xRanges, uno::UNO_QUERY_THROW ) ), true ), mxRanges( xRanges ),mbIsRows( bIsRows ), mbIsColumns( bIsColumns )
1468 uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
1469 m_Areas = new ScVbaRangeAreas( xParent, mxContext, xIndex, mbIsRows, mbIsColumns );
1473 ScVbaRange::~ScVbaRange()
1477 uno::Reference< XCollection >& ScVbaRange::getBorders()
1479 if ( !m_Borders.is() )
1481 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
1482 m_Borders = lcl_setupBorders( this, mxContext, uno::Reference< table::XCellRange >( xRange->getCellRange(), uno::UNO_QUERY_THROW ) );
1484 return m_Borders;
1487 void
1488 ScVbaRange::visitArray( ArrayVisitor& visitor )
1490 ScDocShell* pDocSh = nullptr;
1491 if(ScCellRangeObj* range = dynamic_cast<ScCellRangeObj*>(mxRange.get()))
1492 pDocSh = range->GetDocShell();
1493 if ( pDocSh )
1494 pDocSh->LockPaint();
1495 table::CellRangeAddress aRangeAddr = lclGetRangeAddress( mxRange );
1496 sal_Int32 nRowCount = aRangeAddr.EndRow - aRangeAddr.StartRow + 1;
1497 sal_Int32 nColCount = aRangeAddr.EndColumn - aRangeAddr.StartColumn + 1;
1498 for ( sal_Int32 i=0; i<nRowCount; ++i )
1500 for ( sal_Int32 j=0; j<nColCount; ++j )
1502 uno::Reference< table::XCell > xCell( mxRange->getCellByPosition( j, i ), uno::UNO_SET_THROW );
1504 visitor.visitNode( i, j, xCell );
1507 if ( pDocSh )
1508 pDocSh->UnlockPaint();
1511 uno::Any
1512 ScVbaRange::getValue( ValueGetter& valueGetter)
1514 uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
1515 // single cell range
1516 if ( isSingleCellRange() )
1518 visitArray( valueGetter );
1519 return valueGetter.getValue();
1521 sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
1522 sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
1523 // multi cell range ( return array )
1524 Dim2ArrayValueGetter arrayGetter( nRowCount, nColCount, valueGetter );
1525 visitArray( arrayGetter );
1526 return uno::Any( script::ArrayWrapper( false, arrayGetter.getValue() ) );
1529 css::uno::Any ScVbaRange::DoGetValue( RangeValueType eValueType )
1531 // #TODO code within the test below "if ( m_Areas... " can be removed
1532 // Test is performed only because m_xRange is NOT set to be
1533 // the first range in m_Areas ( to force failure while
1534 // the implementations for each method are being updated )
1535 if ( m_Areas->getCount() > 1 )
1537 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1538 return xRange->getValue();
1541 CellValueGetter valueGetter( eValueType );
1542 return getValue( valueGetter );
1545 uno::Any SAL_CALL
1546 ScVbaRange::getValue()
1548 return DoGetValue( RangeValueType::value );
1551 uno::Any SAL_CALL
1552 ScVbaRange::getValue2()
1554 return DoGetValue( RangeValueType::value2 );
1558 void
1559 ScVbaRange::setValue( const uno::Any& aValue, ValueSetter& valueSetter )
1561 uno::TypeClass aClass = aValue.getValueTypeClass();
1562 if ( aClass == uno::TypeClass_SEQUENCE )
1564 const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
1565 uno::Any aConverted;
1568 // test for single dimension, could do
1569 // with a better test than this
1570 if ( aValue.getValueTypeName().indexOf('[') == aValue.getValueTypeName().lastIndexOf('[') )
1572 aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Any >>::get() );
1573 Dim1ArrayValueSetter setter( aConverted, valueSetter );
1574 visitArray( setter );
1576 else
1578 aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() );
1579 Dim2ArrayValueSetter setter( aConverted, valueSetter );
1580 visitArray( setter );
1583 catch ( const uno::Exception& )
1585 TOOLS_WARN_EXCEPTION("sc", "Bahhh, caught" );
1588 else
1590 visitArray( valueSetter );
1592 fireChangeEvent();
1595 void SAL_CALL
1596 ScVbaRange::setValue( const uno::Any &aValue )
1598 // If this is a multiple selection apply setValue over all areas
1599 if ( m_Areas->getCount() > 1 )
1601 AreasVisitor aVisitor( m_Areas );
1602 RangeValueProcessor valueProcessor( aValue );
1603 aVisitor.visit( valueProcessor );
1604 return;
1606 CellValueSetter valueSetter( aValue );
1607 setValue( aValue, valueSetter );
1610 void SAL_CALL
1611 ScVbaRange::setValue2( const uno::Any &aValue )
1613 return setValue( aValue );
1617 void SAL_CALL
1618 ScVbaRange::Clear()
1620 using namespace ::com::sun::star::sheet::CellFlags;
1621 sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA | HARDATTR | EDITATTR | FORMATTED;
1622 ClearContents( nFlags, true );
1625 //helper ClearContent
1626 void
1627 ScVbaRange::ClearContents( sal_Int32 nFlags, bool bFireEvent )
1629 // #TODO code within the test below "if ( m_Areas... " can be removed
1630 // Test is performed only because m_xRange is NOT set to be
1631 // the first range in m_Areas ( to force failure while
1632 // the implementations for each method are being updated )
1633 if ( m_Areas->getCount() > 1 )
1635 sal_Int32 nItems = m_Areas->getCount();
1636 for ( sal_Int32 index=1; index <= nItems; ++index )
1638 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
1639 ScVbaRange* pRange = getImplementation( xRange );
1640 if ( pRange )
1641 pRange->ClearContents( nFlags, false ); // do not fire for single ranges
1643 // fire change event for the entire range list
1644 if( bFireEvent ) fireChangeEvent();
1645 return;
1648 uno::Reference< sheet::XSheetOperation > xSheetOperation(mxRange, uno::UNO_QUERY_THROW);
1649 xSheetOperation->clearContents( nFlags );
1650 if( bFireEvent ) fireChangeEvent();
1653 void SAL_CALL
1654 ScVbaRange::ClearComments()
1656 ClearContents( sheet::CellFlags::ANNOTATION, false );
1659 void SAL_CALL
1660 ScVbaRange::ClearContents()
1662 using namespace ::com::sun::star::sheet::CellFlags;
1663 sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA;
1664 ClearContents( nFlags, true );
1667 void SAL_CALL
1668 ScVbaRange::ClearFormats()
1670 // FIXME: need to check if we need to combine FORMATTED
1671 using namespace ::com::sun::star::sheet::CellFlags;
1672 sal_Int32 const nFlags = HARDATTR | FORMATTED | EDITATTR;
1673 ClearContents( nFlags, false );
1676 void
1677 ScVbaRange::setFormulaValue( const uno::Any& rFormula, formula::FormulaGrammar::Grammar eGram )
1679 // If this is a multiple selection apply setFormula over all areas
1680 if ( m_Areas->getCount() > 1 )
1682 AreasVisitor aVisitor( m_Areas );
1683 RangeFormulaProcessor valueProcessor( rFormula );
1684 aVisitor.visit( valueProcessor );
1685 return;
1687 CellFormulaValueSetter formulaValueSetter( rFormula, getScDocument(), eGram );
1688 setValue( rFormula, formulaValueSetter );
1691 uno::Any
1692 ScVbaRange::getFormulaValue( formula::FormulaGrammar::Grammar eGram )
1694 // #TODO code within the test below "if ( m_Areas... " can be removed
1695 // Test is performed only because m_xRange is NOT set to be
1696 // the first range in m_Areas ( to force failure while
1697 // the implementations for each method are being updated )
1698 if ( m_Areas->getCount() > 1 )
1700 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1701 return xRange->getFormula();
1703 CellFormulaValueGetter valueGetter( getScDocument(), eGram );
1704 return getValue( valueGetter );
1708 uno::Any
1709 ScVbaRange::getFormula()
1711 return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
1714 void
1715 ScVbaRange::setFormula(const uno::Any &rFormula )
1717 setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
1720 uno::Any
1721 ScVbaRange::getFormulaR1C1()
1723 return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
1726 void
1727 ScVbaRange::setFormulaR1C1(const uno::Any& rFormula )
1729 setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
1732 uno::Any
1733 ScVbaRange::getFormulaLocal()
1735 return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
1738 void
1739 ScVbaRange::setFormulaLocal(const uno::Any &rFormula )
1741 setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
1744 uno::Any
1745 ScVbaRange::getFormulaR1C1Local()
1747 return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
1750 void
1751 ScVbaRange::setFormulaR1C1Local(const uno::Any& rFormula )
1753 setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
1756 sal_Int32
1757 ScVbaRange::getCount()
1759 // If this is a multiple selection apply setValue over all areas
1760 if ( m_Areas->getCount() > 1 )
1762 AreasVisitor aVisitor( m_Areas );
1763 RangeCountProcessor valueProcessor;
1764 aVisitor.visit( valueProcessor );
1765 return valueProcessor.value();
1767 sal_Int32 rowCount = 0;
1768 sal_Int32 colCount = 0;
1769 uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
1770 rowCount = xColumnRowRange->getRows()->getCount();
1771 colCount = xColumnRowRange->getColumns()->getCount();
1773 if( mbIsRows )
1774 return rowCount;
1775 if( mbIsColumns )
1776 return colCount;
1777 return rowCount * colCount;
1780 sal_Int32
1781 ScVbaRange::getRow()
1783 // #TODO code within the test below "if ( m_Areas... " can be removed
1784 // Test is performed only because m_xRange is NOT set to be
1785 // the first range in m_Areas ( to force failure while
1786 // the implementations for each method are being updated )
1787 if ( m_Areas->getCount() > 1 )
1789 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1790 return xRange->getRow();
1792 uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
1793 return xCellAddressable->getCellAddress().Row + 1; // Zero value indexing
1796 sal_Int32
1797 ScVbaRange::getColumn()
1799 // #TODO code within the test below "if ( m_Areas... " can be removed
1800 // Test is performed only because m_xRange is NOT set to be
1801 // the first range in m_Areas ( to force failure while
1802 // the implementations for each method are being updated )
1803 if ( m_Areas->getCount() > 1 )
1805 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1806 return xRange->getColumn();
1808 uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
1809 return xCellAddressable->getCellAddress().Column + 1; // Zero value indexing
1812 uno::Any
1813 ScVbaRange::HasFormula()
1815 if ( m_Areas->getCount() > 1 )
1817 sal_Int32 nItems = m_Areas->getCount();
1818 uno::Any aResult = aNULL();
1819 for ( sal_Int32 index=1; index <= nItems; ++index )
1821 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
1822 // if the HasFormula for any area is different to another
1823 // return null
1824 if ( index > 1 )
1825 if ( aResult != xRange->HasFormula() )
1826 return aNULL();
1827 aResult = xRange->HasFormula();
1828 if ( aNULL() == aResult )
1829 return aNULL();
1831 return aResult;
1833 uno::Reference< uno::XInterface > xIf( mxRange, uno::UNO_QUERY_THROW );
1834 ScCellRangesBase* pThisRanges = dynamic_cast< ScCellRangesBase* > ( xIf.get() );
1835 if ( pThisRanges )
1837 uno::Reference<uno::XInterface> xRanges( pThisRanges->queryFormulaCells( sheet::FormulaResult::ERROR | sheet::FormulaResult::VALUE | sheet::FormulaResult::STRING ), uno::UNO_QUERY_THROW );
1838 ScCellRangesBase* pFormulaRanges
1839 = dynamic_cast< ScCellRangesBase* > ( xRanges.get() );
1840 assert(pFormulaRanges);
1841 // check if there are no formula cell, return false
1842 if ( pFormulaRanges->GetRangeList().empty() )
1843 return uno::Any(false);
1845 // check if there are holes (where some cells are not formulas)
1846 // or returned range is not equal to this range
1847 if ( ( pFormulaRanges->GetRangeList().size() > 1 )
1848 || ( pFormulaRanges->GetRangeList().front().aStart != pThisRanges->GetRangeList().front().aStart )
1849 || ( pFormulaRanges->GetRangeList().front().aEnd != pThisRanges->GetRangeList().front().aEnd )
1851 return aNULL(); // should return aNULL;
1853 return uno::Any( true );
1855 void
1856 ScVbaRange::fillSeries( sheet::FillDirection nFillDirection, sheet::FillMode nFillMode, sheet::FillDateMode nFillDateMode, double fStep, double fEndValue )
1858 if ( m_Areas->getCount() > 1 )
1860 // Multi-Area Range
1861 uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
1862 for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
1864 uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
1865 ScVbaRange* pThisRange = getImplementation( xRange );
1866 pThisRange->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
1869 return;
1872 uno::Reference< sheet::XCellSeries > xCellSeries(mxRange, uno::UNO_QUERY_THROW );
1873 xCellSeries->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
1874 fireChangeEvent();
1877 void
1878 ScVbaRange::FillLeft()
1880 fillSeries(sheet::FillDirection_TO_LEFT,
1881 sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1884 void
1885 ScVbaRange::FillRight()
1887 fillSeries(sheet::FillDirection_TO_RIGHT,
1888 sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1891 void
1892 ScVbaRange::FillUp()
1894 fillSeries(sheet::FillDirection_TO_TOP,
1895 sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1898 void
1899 ScVbaRange::FillDown()
1901 fillSeries(sheet::FillDirection_TO_BOTTOM,
1902 sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
1905 OUString
1906 ScVbaRange::getText()
1908 // #TODO code within the test below "if ( m_Areas... " can be removed
1909 // Test is performed only because m_xRange is NOT set to be
1910 // the first range in m_Areas ( to force failure while
1911 // the implementations for each method are being updated )
1912 if ( m_Areas->getCount() > 1 )
1914 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1915 return xRange->getText();
1917 uno::Reference< text::XTextRange > xTextRange(mxRange->getCellByPosition(0,0), uno::UNO_QUERY_THROW );
1918 return xTextRange->getString();
1921 uno::Reference< excel::XRange >
1922 ScVbaRange::Offset( const ::uno::Any &nRowOff, const uno::Any &nColOff )
1924 SCROW nRowOffset = 0;
1925 SCCOL nColOffset = 0;
1926 bool bIsRowOffset = ( nRowOff >>= nRowOffset );
1927 bool bIsColumnOffset = ( nColOff >>= nColOffset );
1928 ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
1930 ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
1932 for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
1934 ScRange & rRange = aCellRanges[ i ];
1935 if ( bIsColumnOffset )
1937 rRange.aStart.SetCol( rRange.aStart.Col() + nColOffset );
1938 rRange.aEnd.SetCol( rRange.aEnd.Col() + nColOffset );
1940 if ( bIsRowOffset )
1942 rRange.aStart.SetRow( rRange.aStart.Row() + nRowOffset );
1943 rRange.aEnd.SetRow( rRange.aEnd.Row() + nRowOffset );
1947 if ( aCellRanges.size() > 1 ) // Multi-Area
1949 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
1950 return new ScVbaRange( mxParent, mxContext, xRanges );
1952 // normal range
1953 const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
1954 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
1955 return new ScVbaRange( mxParent, mxContext, xRange );
1958 uno::Reference< excel::XRange >
1959 ScVbaRange::CurrentRegion()
1961 // #TODO code within the test below "if ( m_Areas... " can be removed
1962 // Test is performed only because m_xRange is NOT set to be
1963 // the first range in m_Areas ( to force failure while
1964 // the implementations for each method are being updated )
1965 if ( m_Areas->getCount() > 1 )
1967 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1968 return xRange->CurrentRegion();
1971 RangeHelper helper( mxRange );
1972 uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
1973 helper.getSheetCellCursor();
1974 xSheetCellCursor->collapseToCurrentRegion();
1975 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
1976 return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
1979 uno::Reference< excel::XRange >
1980 ScVbaRange::CurrentArray()
1982 // #TODO code within the test below "if ( m_Areas... " can be removed
1983 // Test is performed only because m_xRange is NOT set to be
1984 // the first range in m_Areas ( to force failure while
1985 // the implementations for each method are being updated )
1986 if ( m_Areas->getCount() > 1 )
1988 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
1989 return xRange->CurrentArray();
1991 RangeHelper helper( mxRange );
1992 uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
1993 helper.getSheetCellCursor();
1994 xSheetCellCursor->collapseToCurrentArray();
1995 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
1996 return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
1999 uno::Any
2000 ScVbaRange::getFormulaArray()
2002 // #TODO code within the test below "if ( m_Areas... " can be removed
2003 // Test is performed only because m_xRange is NOT set to be
2004 // the first range in m_Areas ( to force failure while
2005 // the implementations for each method are being updated )
2006 if ( m_Areas->getCount() > 1 )
2008 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
2009 return xRange->getFormulaArray();
2012 // return a formula if there is one or else an array
2013 // still not sure when the return as array code should run
2014 // ( I think it is if there is more than one formula ) at least
2015 // that is what the doc says ( but I am not even sure how to detect that )
2016 // for the moment any tests we have pass
2017 uno::Reference< sheet::XArrayFormulaRange> xFormulaArray( mxRange, uno::UNO_QUERY_THROW );
2018 if ( !xFormulaArray->getArrayFormula().isEmpty() )
2019 return uno::Any( xFormulaArray->getArrayFormula() );
2021 uno::Reference< sheet::XCellRangeFormula> xCellRangeFormula( mxRange, uno::UNO_QUERY_THROW );
2022 const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
2023 uno::Any aSingleValueOrMatrix;
2024 // When dealing with a single element ( embedded in the sequence of sequence ) unwrap and return
2025 // that value
2026 uno::Sequence< uno::Sequence<OUString> > aTmpSeq = xCellRangeFormula->getFormulaArray();
2027 if ( aTmpSeq.getLength() == 1 )
2029 if ( aTmpSeq[ 0 ].getLength() == 1 )
2030 aSingleValueOrMatrix <<= aTmpSeq[ 0 ][ 0 ];
2032 else
2033 aSingleValueOrMatrix = xConverter->convertTo( uno::Any( aTmpSeq ) , cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() ) ;
2034 return aSingleValueOrMatrix;
2037 void
2038 ScVbaRange::setFormulaArray(const uno::Any& rFormula)
2040 // #TODO code within the test below "if ( m_Areas... " can be removed
2041 // Test is performed only because m_xRange is NOT set to be
2042 // the first range in m_Areas ( to force failure while
2043 // the implementations for each method are being updated )
2044 if ( m_Areas->getCount() > 1 )
2046 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
2047 return xRange->setFormulaArray( rFormula );
2049 // #TODO need to distinguish between getFormula and getFormulaArray e.g. (R1C1)
2050 // but for the moment it's just easier to treat them the same for setting
2051 // seems
2052 uno::Reference< lang::XMultiServiceFactory > xModelFactory( getUnoModel(), uno::UNO_QUERY_THROW );
2053 uno::Reference< sheet::XFormulaParser > xParser( xModelFactory->createInstance( u"com.sun.star.sheet.FormulaParser"_ustr ), uno::UNO_QUERY_THROW );
2054 uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY_THROW);
2056 table::CellRangeAddress aRangeAddress = xSource->getRangeAddress();
2057 // #TODO check if api orders the address
2058 // e.g. do we need to order the RangeAddress to get the topleft ( or can we assume it
2059 // is in the correct order )
2060 table::CellAddress aAddress;
2061 aAddress.Sheet = aRangeAddress.Sheet;
2062 aAddress.Column = aRangeAddress.StartColumn;
2063 aAddress.Row = aRangeAddress.StartRow;
2064 OUString sFormula;
2065 rFormula >>= sFormula;
2066 uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( sFormula, aAddress );
2067 ScTokenArray aTokenArray(getScDocument());
2068 (void)ScTokenConversion::ConvertToTokenArray( getScDocument(), aTokenArray, aTokens );
2070 getScDocShell()->GetDocFunc().EnterMatrix( getScRangeList()[0], nullptr, &aTokenArray, OUString(), true, true, OUString(), formula::FormulaGrammar::GRAM_API );
2073 OUString
2074 ScVbaRange::Characters(const uno::Any& Start, const uno::Any& Length)
2076 // #TODO code within the test below "if ( m_Areas... " can be removed
2077 // Test is performed only because m_xRange is NOT set to be
2078 // the first range in m_Areas ( to force failure while
2079 // the implementations for each method are being updated )
2080 if ( m_Areas->getCount() > 1 )
2082 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
2083 return xRange->Characters( Start, Length );
2086 tools::Long nIndex = 0, nCount = 0;
2087 OUString rString;
2088 uno::Reference< text::XTextRange > xTextRange(mxRange, ::uno::UNO_QUERY_THROW );
2089 rString = xTextRange->getString();
2090 if( !( Start >>= nIndex ) && !( Length >>= nCount ) )
2091 return rString;
2092 if(!( Start >>= nIndex ) )
2093 nIndex = 1;
2094 if(!( Length >>= nCount ) )
2095 nIndex = rString.getLength();
2096 return rString.copy( --nIndex, nCount ); // Zero value indexing
2099 OUString
2100 ScVbaRange::Address( const uno::Any& RowAbsolute, const uno::Any& ColumnAbsolute, const uno::Any& ReferenceStyle, const uno::Any& External, const uno::Any& RelativeTo )
2102 if ( m_Areas->getCount() > 1 )
2104 // Multi-Area Range
2105 OUStringBuffer sAddress;
2106 uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
2107 uno::Any aExternalCopy = External;
2108 for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
2110 uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
2111 if ( index > 1 )
2113 sAddress.append(",");
2114 // force external to be false
2115 // only first address should have the
2116 // document and sheet specifications
2117 aExternalCopy <<= false;
2119 sAddress.append(xRange->Address( RowAbsolute, ColumnAbsolute, ReferenceStyle, aExternalCopy, RelativeTo ));
2121 return sAddress.makeStringAndClear();
2124 ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2125 if ( ReferenceStyle.hasValue() )
2127 sal_Int32 refStyle = excel::XlReferenceStyle::xlA1;
2128 ReferenceStyle >>= refStyle;
2129 if ( refStyle == excel::XlReferenceStyle::xlR1C1 )
2130 dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, 0, 0 );
2132 // default
2133 ScRefFlags nFlags = ScRefFlags::RANGE_ABS;
2134 ScDocShell* pDocShell = getScDocShell();
2135 ScDocument& rDoc = pDocShell->GetDocument();
2137 RangeHelper thisRange( mxRange );
2138 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
2139 ScRange aRange( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCROW >( thisAddress.StartRow ), static_cast< SCTAB >( thisAddress.Sheet ), static_cast< SCCOL >( thisAddress.EndColumn ), static_cast< SCROW >( thisAddress.EndRow ), static_cast< SCTAB >( thisAddress.Sheet ) );
2140 constexpr ScRefFlags ROW_ABS = ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
2141 constexpr ScRefFlags COL_ABS = ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
2143 if ( RowAbsolute.hasValue() )
2145 bool bVal = true;
2146 RowAbsolute >>= bVal;
2147 if ( !bVal )
2148 nFlags &= ~ROW_ABS;
2150 if ( ColumnAbsolute.hasValue() )
2152 bool bVal = true;
2153 ColumnAbsolute >>= bVal;
2154 if ( !bVal )
2155 nFlags &= ~COL_ABS;
2157 if ( External.hasValue() )
2159 bool bLocal = false;
2160 External >>= bLocal;
2161 if ( bLocal )
2162 nFlags |= ScRefFlags::TAB_3D | ScRefFlags::FORCE_DOC;
2164 if ( RelativeTo.hasValue() )
2166 // #TODO should I throw an error if R1C1 is not set?
2168 table::CellRangeAddress refAddress = getCellRangeAddressForVBARange( RelativeTo, pDocShell );
2169 dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, static_cast< SCROW >( refAddress.StartRow ), static_cast< SCCOL >( refAddress.StartColumn ) );
2171 return aRange.Format(rDoc, nFlags, dDetails);
2174 uno::Reference < excel::XFont >
2175 ScVbaRange::Font()
2177 uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY );
2178 ScDocument& rDoc = getScDocument();
2179 if ( mxRange.is() )
2180 xProps.set(mxRange, ::uno::UNO_QUERY );
2181 else if ( mxRanges.is() )
2182 xProps.set(mxRanges, ::uno::UNO_QUERY );
2184 ScVbaPalette aPalette( rDoc.GetDocumentShell() );
2185 ScCellRangeObj* pRangeObj = nullptr;
2188 pRangeObj = getCellRangeObj();
2190 catch( uno::Exception& )
2193 return new ScVbaFont( this, mxContext, aPalette, xProps, pRangeObj );
2196 uno::Reference< excel::XRange >
2197 ScVbaRange::Cells( const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
2199 // #TODO code within the test below "if ( m_Areas... " can be removed
2200 // Test is performed only because m_xRange is NOT set to be
2201 // the first range in m_Areas ( to force failure while
2202 // the implementations for each method are being updated )
2203 if ( m_Areas->getCount() > 1 )
2205 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
2206 return xRange->Cells( nRowIndex, nColumnIndex );
2209 // Performance: Use a common helper method for ScVbaRange::Cells and ScVbaWorksheet::Cells,
2210 // instead of creating a new ScVbaRange object in often-called ScVbaWorksheet::Cells
2211 return CellsHelper( getScDocument(), mxParent, mxContext, mxRange, nRowIndex, nColumnIndex );
2214 // static
2215 uno::Reference< excel::XRange >
2216 ScVbaRange::CellsHelper( const ScDocument& rDoc,
2217 const uno::Reference< ov::XHelperInterface >& xParent,
2218 const uno::Reference< uno::XComponentContext >& xContext,
2219 const uno::Reference< css::table::XCellRange >& xRange,
2220 const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
2222 sal_Int32 nRow = 0, nColumn = 0;
2224 bool bIsIndex = nRowIndex.hasValue();
2225 bool bIsColumnIndex = nColumnIndex.hasValue();
2227 // Sometimes we might get a float or a double or whatever
2228 // set in the Any, we should convert as appropriate
2229 // #FIXME - perhaps worth turning this into some sort of
2230 // conversion routine e.g. bSuccess = getValueFromAny( nRow, nRowIndex, cppu::UnoType<sal_Int32>::get() )
2231 if ( nRowIndex.hasValue() && !( nRowIndex >>= nRow ) )
2233 const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
2234 uno::Any aConverted;
2237 aConverted = xConverter->convertTo( nRowIndex, cppu::UnoType<sal_Int32>::get() );
2238 bIsIndex = ( aConverted >>= nRow );
2240 catch( uno::Exception& ) {} // silence any errors
2243 if ( bIsColumnIndex )
2245 // Column index can be a col address e.g Cells( 1, "B" ) etc.
2246 OUString sCol;
2247 if ( nColumnIndex >>= sCol )
2249 ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2250 ScRange tmpRange;
2251 ScRefFlags flags = tmpRange.ParseCols( rDoc, sCol, dDetails );
2252 if ( (flags & ScRefFlags::COL_VALID) == ScRefFlags::ZERO )
2253 throw uno::RuntimeException();
2254 nColumn = tmpRange.aStart.Col() + 1;
2256 else
2258 if ( !( nColumnIndex >>= nColumn ) )
2260 const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
2261 uno::Any aConverted;
2264 aConverted = xConverter->convertTo( nColumnIndex, cppu::UnoType<sal_Int32>::get() );
2265 bIsColumnIndex = ( aConverted >>= nColumn );
2267 catch( uno::Exception& ) {} // silence any errors
2271 RangeHelper thisRange( xRange );
2272 table::CellRangeAddress thisRangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
2273 uno::Reference< table::XCellRange > xSheetRange = thisRange.getCellRangeFromSheet();
2274 if( !bIsIndex && !bIsColumnIndex ) // .Cells
2275 // #FIXME needs proper parent ( Worksheet )
2276 return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext, xRange ) );
2278 sal_Int32 nIndex = --nRow;
2279 if( bIsIndex && !bIsColumnIndex ) // .Cells(n)
2281 uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, ::uno::UNO_QUERY_THROW);
2282 sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
2284 if ( !nIndex || nIndex < 0 )
2285 nRow = 0;
2286 else
2287 nRow = nIndex / nColCount;
2288 nColumn = nIndex % nColCount;
2290 else
2291 --nColumn;
2292 nRow = nRow + thisRangeAddress.StartRow;
2293 nColumn = nColumn + thisRangeAddress.StartColumn;
2294 return new ScVbaRange( xParent, xContext, xSheetRange->getCellRangeByPosition( nColumn, nRow, nColumn, nRow ) );
2297 void
2298 ScVbaRange::Select()
2300 ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2301 if ( !pUnoRangesBase )
2302 throw uno::RuntimeException(u"Failed to access underlying uno range object"_ustr );
2303 ScDocShell* pShell = pUnoRangesBase->GetDocShell();
2304 if ( !pShell )
2305 return;
2307 uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_SET_THROW );
2308 uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
2309 if ( mxRanges.is() )
2310 xSelection->select( uno::Any( lclExpandToMerged( mxRanges ) ) );
2311 else
2312 xSelection->select( uno::Any( lclExpandToMerged( mxRange, true ) ) );
2313 // set focus on document e.g.
2314 // ThisComponent.CurrentController.Frame.getContainerWindow.SetFocus
2317 uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW );
2318 uno::Reference< frame::XFrame > xFrame( xController->getFrame(), uno::UNO_SET_THROW );
2319 uno::Reference< awt::XWindow > xWin( xFrame->getContainerWindow(), uno::UNO_SET_THROW );
2320 xWin->setFocus();
2322 catch( uno::Exception& )
2327 static bool cellInRange( const table::CellRangeAddress& rAddr, sal_Int32 nCol, sal_Int32 nRow )
2329 return nCol >= rAddr.StartColumn && nCol <= rAddr.EndColumn &&
2330 nRow >= rAddr.StartRow && nRow <= rAddr.EndRow;
2333 static void setCursor( SCCOL nCol, SCROW nRow, const uno::Reference< frame::XModel >& xModel, bool bInSel = true )
2335 ScTabViewShell* pShell = excel::getBestViewShell( xModel );
2336 if ( pShell )
2338 if ( bInSel )
2339 pShell->SetCursor( nCol, nRow );
2340 else
2341 pShell->MoveCursorAbs( nCol, nRow, SC_FOLLOW_NONE, false, false, true );
2345 void
2346 ScVbaRange::Activate()
2348 // get first cell of current range
2349 uno::Reference< table::XCellRange > xCellRange;
2350 if ( mxRanges.is() )
2352 uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
2353 xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
2355 else
2356 xCellRange.set( mxRange, uno::UNO_SET_THROW );
2358 RangeHelper thisRange( xCellRange );
2359 uno::Reference< sheet::XCellRangeAddressable > xThisRangeAddress = thisRange.getCellRangeAddressable();
2360 table::CellRangeAddress thisRangeAddress = xThisRangeAddress->getRangeAddress();
2361 uno::Reference< frame::XModel > xModel;
2362 ScDocShell* pShell = getScDocShell();
2364 if ( pShell )
2365 xModel = pShell->GetModel();
2367 if ( !xModel.is() )
2368 throw uno::RuntimeException();
2370 // get current selection
2371 uno::Reference< sheet::XCellRangeAddressable > xRange( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
2373 uno::Reference< sheet::XSheetCellRanges > xRanges( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
2375 if ( xRanges.is() )
2377 const uno::Sequence< table::CellRangeAddress > nAddrs = xRanges->getRangeAddresses();
2378 for ( const auto& rAddr : nAddrs )
2380 if ( cellInRange( rAddr, thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
2382 setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
2383 return;
2389 if ( xRange.is() && cellInRange( xRange->getRangeAddress(), thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
2390 setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
2391 else
2393 // if this range is multi cell select the range other
2394 // wise just position the cell at this single range position
2395 if ( isSingleCellRange() )
2396 // This top-leftmost cell of this Range is not in the current
2397 // selection so just select this range
2398 setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel, false );
2399 else
2400 Select();
2405 ScRange ScVbaRange::obtainRangeEvenIfRangeListIsEmpty( const ScRangeList& rCellRanges ) const
2407 // XXX It may be that using the current range list was never correct, but
2408 // always the initial sheet range would be instead, history is unclear.
2410 if (!rCellRanges.empty())
2411 return rCellRanges.front();
2413 table::CellRangeAddress aRA( lclGetRangeAddress( mxRange ));
2414 return ScRange( aRA.StartColumn, aRA.StartRow, aRA.Sheet, aRA.EndColumn, aRA.EndRow, aRA.Sheet);
2417 uno::Reference< excel::XRange >
2418 ScVbaRange::Rows(const uno::Any& aIndex )
2420 if ( aIndex.hasValue() )
2422 ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2423 ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
2425 sal_Int32 nValue = 0;
2426 OUString sAddress;
2427 if( aIndex >>= nValue )
2429 aRange.aStart.SetRow( aRange.aStart.Row() + --nValue );
2430 aRange.aEnd.SetRow( aRange.aStart.Row() );
2432 else if ( aIndex >>= sAddress )
2434 ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2435 ScRange tmpRange;
2436 tmpRange.ParseRows( getScDocument(), sAddress, dDetails );
2437 SCROW nStartRow = tmpRange.aStart.Row();
2438 SCROW nEndRow = tmpRange.aEnd.Row();
2440 aRange.aStart.SetRow( aRange.aStart.Row() + nStartRow );
2441 aRange.aEnd.SetRow( aRange.aStart.Row() + ( nEndRow - nStartRow ));
2443 else
2444 throw uno::RuntimeException(u"Illegal param"_ustr );
2446 if ( aRange.aStart.Row() < 0 || aRange.aEnd.Row() < 0 )
2447 throw uno::RuntimeException(u"Internal failure, illegal param"_ustr );
2448 // return a normal range ( even for multi-selection
2449 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
2450 return new ScVbaRange( mxParent, mxContext, xRange, true );
2452 // Rows() - no params
2453 if ( m_Areas->getCount() > 1 )
2454 return new ScVbaRange( mxParent, mxContext, mxRanges, true );
2455 return new ScVbaRange( mxParent, mxContext, mxRange, true );
2458 uno::Reference< excel::XRange >
2459 ScVbaRange::Columns(const uno::Any& aIndex )
2461 ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2462 ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
2464 if ( aIndex.hasValue() )
2466 OUString sAddress;
2467 sal_Int32 nValue = 0;
2468 if ( aIndex >>= nValue )
2470 aRange.aStart.SetCol( aRange.aStart.Col() + static_cast< SCCOL > ( --nValue ) );
2471 aRange.aEnd.SetCol( aRange.aStart.Col() );
2474 else if ( aIndex >>= sAddress )
2476 ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
2477 ScRange tmpRange;
2478 tmpRange.ParseCols( getScDocument(), sAddress, dDetails );
2479 SCCOL nStartCol = tmpRange.aStart.Col();
2480 SCCOL nEndCol = tmpRange.aEnd.Col();
2482 aRange.aStart.SetCol( aRange.aStart.Col() + nStartCol );
2483 aRange.aEnd.SetCol( aRange.aStart.Col() + ( nEndCol - nStartCol ));
2485 else
2486 throw uno::RuntimeException(u"Illegal param"_ustr );
2488 if ( aRange.aStart.Col() < 0 || aRange.aEnd.Col() < 0 )
2489 throw uno::RuntimeException(u"Internal failure, illegal param"_ustr );
2491 // Columns() - no params
2492 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
2493 return new ScVbaRange( mxParent, mxContext, xRange, false, true );
2496 void
2497 ScVbaRange::setMergeCells( const uno::Any& aIsMerged )
2499 bool bMerge = extractBoolFromAny( aIsMerged );
2501 if( mxRanges.is() )
2503 sal_Int32 nCount = mxRanges->getCount();
2505 // VBA does nothing (no error) if the own ranges overlap somehow
2506 ::std::vector< table::CellRangeAddress > aList;
2507 for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
2509 uno::Reference< sheet::XCellRangeAddressable > xRangeAddr( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
2510 table::CellRangeAddress aAddress = xRangeAddr->getRangeAddress();
2511 if (std::any_of(aList.begin(), aList.end(),
2512 [&aAddress](const table::CellRangeAddress& rAddress)
2513 { return ScUnoConversion::Intersects( rAddress, aAddress ); }))
2514 return;
2515 aList.push_back( aAddress );
2518 // (un)merge every range after it has been extended to intersecting merged ranges from sheet
2519 for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
2521 uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
2522 lclExpandAndMerge( xRange, bMerge );
2524 return;
2527 // otherwise, merge single range
2528 lclExpandAndMerge( mxRange, bMerge );
2531 uno::Any
2532 ScVbaRange::getMergeCells()
2534 if( mxRanges.is() )
2536 sal_Int32 nCount = mxRanges->getCount();
2537 for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
2539 uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
2540 util::TriState eMerged = lclGetMergedState( xRange );
2541 /* Excel always returns NULL, if one range of the range list is
2542 partly or completely merged. Even if all ranges are completely
2543 merged, the return value is still NULL. */
2544 if( eMerged != util::TriState_NO )
2545 return aNULL();
2547 // no range is merged anyhow, return false
2548 return uno::Any( false );
2551 // otherwise, check single range
2552 switch( lclGetMergedState( mxRange ) )
2554 case util::TriState_YES: return uno::Any( true );
2555 case util::TriState_NO: return uno::Any( false );
2556 default: return aNULL();
2560 void
2561 ScVbaRange::Copy(const ::uno::Any& Destination)
2563 if ( Destination.hasValue() )
2565 // TODO copy with multiple selections should work here too
2566 if ( m_Areas->getCount() > 1 )
2567 throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
2568 uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
2569 uno::Any aRange = xRange->getCellRange();
2570 uno::Reference< table::XCellRange > xCellRange;
2571 aRange >>= xCellRange;
2572 uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW);
2573 uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
2574 uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
2575 uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
2576 uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
2577 xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY_THROW );
2578 uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
2579 xMover->copyRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
2580 if ( ScVbaRange* pRange = getImplementation( xRange ) )
2581 pRange->fireChangeEvent();
2583 else
2585 Select();
2586 excel::implnCopy(getUnoModel());
2590 void
2591 ScVbaRange::Cut(const ::uno::Any& Destination)
2593 if ( m_Areas->getCount() > 1 )
2594 throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
2595 if (Destination.hasValue())
2597 uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
2598 uno::Reference< table::XCellRange > xCellRange( xRange->getCellRange(), uno::UNO_QUERY_THROW );
2599 uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW );
2600 uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
2601 uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
2602 uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
2603 uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
2604 xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY);
2605 uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
2606 xMover->moveRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
2608 else
2610 uno::Reference< frame::XModel > xModel = getModelFromRange( mxRange );
2611 Select();
2612 excel::implnCut( xModel );
2616 void
2617 ScVbaRange::setNumberFormat( const uno::Any& aFormat )
2619 OUString sFormat;
2620 aFormat >>= sFormat;
2621 if ( m_Areas->getCount() > 1 )
2623 sal_Int32 nItems = m_Areas->getCount();
2624 for ( sal_Int32 index=1; index <= nItems; ++index )
2626 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2627 xRange->setNumberFormat( aFormat );
2629 return;
2631 NumFormatHelper numFormat( mxRange );
2632 numFormat.setNumberFormat( sFormat );
2635 uno::Any
2636 ScVbaRange::getNumberFormat()
2639 if ( m_Areas->getCount() > 1 )
2641 sal_Int32 nItems = m_Areas->getCount();
2642 uno::Any aResult = aNULL();
2643 for ( sal_Int32 index=1; index <= nItems; ++index )
2645 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2646 // if the numberformat of one area is different to another
2647 // return null
2648 if ( index > 1 )
2649 if ( aResult != xRange->getNumberFormat() )
2650 return aNULL();
2651 aResult = xRange->getNumberFormat();
2652 if ( aNULL() == aResult )
2653 return aNULL();
2655 return aResult;
2657 NumFormatHelper numFormat( mxRange );
2658 OUString sFormat = numFormat.getNumberFormatString();
2659 if ( !sFormat.isEmpty() )
2660 return uno::Any( sFormat );
2661 return aNULL();
2664 uno::Reference< excel::XRange >
2665 ScVbaRange::Resize( const uno::Any &RowSize, const uno::Any &ColumnSize )
2667 tools::Long nRowSize = 0, nColumnSize = 0;
2668 bool bIsRowChanged = ( RowSize >>= nRowSize ), bIsColumnChanged = ( ColumnSize >>= nColumnSize );
2669 uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, ::uno::UNO_QUERY_THROW);
2670 uno::Reference< sheet::XSheetCellRange > xSheetRange(mxRange, ::uno::UNO_QUERY_THROW);
2671 uno::Reference< sheet::XSheetCellCursor > xCursor( xSheetRange->getSpreadsheet()->createCursorByRange(xSheetRange), ::uno::UNO_SET_THROW );
2673 if( !bIsRowChanged )
2674 nRowSize = xColumnRowRange->getRows()->getCount();
2675 if( !bIsColumnChanged )
2676 nColumnSize = xColumnRowRange->getColumns()->getCount();
2678 xCursor->collapseToSize( nColumnSize, nRowSize );
2679 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xCursor, ::uno::UNO_QUERY_THROW );
2680 uno::Reference< table::XCellRange > xRange( xSheetRange->getSpreadsheet(), ::uno::UNO_QUERY_THROW );
2681 const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
2682 return new ScVbaRange( mxParent, mxContext, xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow));
2685 void
2686 ScVbaRange::setWrapText( const uno::Any& aIsWrapped )
2688 if ( m_Areas->getCount() > 1 )
2690 sal_Int32 nItems = m_Areas->getCount();
2691 for ( sal_Int32 index=1; index <= nItems; ++index )
2693 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2694 xRange->setWrapText( aIsWrapped );
2696 return;
2699 uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
2700 bool bIsWrapped = extractBoolFromAny( aIsWrapped );
2701 xProps->setPropertyValue( u"IsTextWrapped"_ustr, uno::Any( bIsWrapped ) );
2704 uno::Any
2705 ScVbaRange::getWrapText()
2707 if ( m_Areas->getCount() > 1 )
2709 sal_Int32 nItems = m_Areas->getCount();
2710 uno::Any aResult;
2711 for ( sal_Int32 index=1; index <= nItems; ++index )
2713 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
2714 if ( index > 1 )
2715 if ( aResult != xRange->getWrapText() )
2716 return aNULL();
2717 aResult = xRange->getWrapText();
2719 return aResult;
2722 SfxItemSet* pDataSet = getCurrentDataSet();
2724 SfxItemState eState = pDataSet->GetItemState( ATTR_LINEBREAK);
2725 if ( eState == SfxItemState::INVALID )
2726 return aNULL();
2728 uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
2729 uno::Any aValue = xProps->getPropertyValue( u"IsTextWrapped"_ustr );
2730 return aValue;
2733 uno::Reference< excel::XInterior > ScVbaRange::Interior( )
2735 uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
2736 return new ScVbaInterior ( this, mxContext, xProps, &getScDocument() );
2738 uno::Reference< excel::XRange >
2739 ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2 )
2741 return Range( Cell1, Cell2, false );
2743 uno::Reference< excel::XRange >
2744 ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2, bool bForceUseInpuRangeTab )
2747 uno::Reference< table::XCellRange > xCellRange = mxRange;
2749 if ( m_Areas->getCount() > 1 )
2751 uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
2752 xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
2754 else
2755 xCellRange.set( mxRange );
2757 RangeHelper thisRange( xCellRange );
2758 uno::Reference< table::XCellRange > xRanges = thisRange.getCellRangeFromSheet();
2759 uno::Reference< sheet::XCellRangeAddressable > xAddressable( xRanges, uno::UNO_QUERY_THROW );
2761 uno::Reference< table::XCellRange > xReferrer =
2762 xRanges->getCellRangeByPosition( getColumn()-1, getRow()-1,
2763 xAddressable->getRangeAddress().EndColumn,
2764 xAddressable->getRangeAddress().EndRow );
2765 // xAddressable now for this range
2766 xAddressable.set( xReferrer, uno::UNO_QUERY_THROW );
2768 if( !Cell1.hasValue() )
2769 throw uno::RuntimeException( u"Invalid Argument"_ustr );
2771 table::CellRangeAddress parentRangeAddress = xAddressable->getRangeAddress();
2773 ScRange aRange;
2774 // Cell1 defined only
2775 if ( !Cell2.hasValue() )
2777 OUString sName;
2778 Cell1 >>= sName;
2779 RangeHelper referRange( xReferrer );
2780 table::CellRangeAddress referAddress = referRange.getCellRangeAddressable()->getRangeAddress();
2781 return getRangeForName( mxContext, sName, getScDocShell(), referAddress );
2784 else
2786 table::CellRangeAddress cell1, cell2;
2787 cell1 = getCellRangeAddressForVBARange( Cell1, getScDocShell() );
2788 // Cell1 & Cell2 defined
2789 // Excel seems to combine the range as the range defined by
2790 // the combination of Cell1 & Cell2
2792 cell2 = getCellRangeAddressForVBARange( Cell2, getScDocShell() );
2794 table::CellRangeAddress resultAddress;
2795 resultAddress.StartColumn = ( cell1.StartColumn < cell2.StartColumn ) ? cell1.StartColumn : cell2.StartColumn;
2796 resultAddress.StartRow = ( cell1.StartRow < cell2.StartRow ) ? cell1.StartRow : cell2.StartRow;
2797 resultAddress.EndColumn = std::max( cell1.EndColumn, cell2.EndColumn );
2798 resultAddress.EndRow = std::max( cell1.EndRow, cell2.EndRow );
2799 if ( bForceUseInpuRangeTab )
2801 // this is a call from Application.Range( x,y )
2802 // it's possible for x or y to specify a different sheet from
2803 // the current or active on ( but they must be the same )
2804 if ( cell1.Sheet != cell2.Sheet )
2805 throw uno::RuntimeException();
2806 parentRangeAddress.Sheet = cell1.Sheet;
2808 else
2810 // this is not a call from Application.Range( x,y )
2811 // if a different sheet from this range is specified it's
2812 // an error
2813 if ( parentRangeAddress.Sheet != cell1.Sheet
2814 || parentRangeAddress.Sheet != cell2.Sheet
2816 throw uno::RuntimeException();
2819 ScUnoConversion::FillScRange( aRange, resultAddress );
2821 ScRange parentAddress;
2822 ScUnoConversion::FillScRange( parentAddress, parentRangeAddress);
2823 if ( aRange.aStart.Col() >= 0 && aRange.aStart.Row() >= 0 && aRange.aEnd.Col() >= 0 && aRange.aEnd.Row() >= 0 )
2825 sal_Int32 nStartX = parentAddress.aStart.Col() + aRange.aStart.Col();
2826 sal_Int32 nStartY = parentAddress.aStart.Row() + aRange.aStart.Row();
2827 sal_Int32 nEndX = parentAddress.aStart.Col() + aRange.aEnd.Col();
2828 sal_Int32 nEndY = parentAddress.aStart.Row() + aRange.aEnd.Row();
2830 if ( nStartX <= nEndX && nEndX <= parentAddress.aEnd.Col() &&
2831 nStartY <= nEndY && nEndY <= parentAddress.aEnd.Row() )
2833 ScRange aNew( static_cast<SCCOL>(nStartX), static_cast<SCROW>(nStartY), parentAddress.aStart.Tab(),
2834 static_cast<SCCOL>(nEndX), static_cast<SCROW>(nEndY), parentAddress.aEnd.Tab() );
2835 xCellRange = new ScCellRangeObj( getScDocShell(), aNew );
2839 return new ScVbaRange( mxParent, mxContext, xCellRange );
2843 // Allow access to underlying openoffice uno api ( useful for debugging
2844 // with openoffice basic )
2845 uno::Any SAL_CALL ScVbaRange::getCellRange( )
2847 uno::Any aAny;
2848 if ( mxRanges.is() )
2849 aAny <<= mxRanges;
2850 else if ( mxRange.is() )
2851 aAny <<= mxRange;
2852 return aAny;
2855 uno::Any ScVbaRange::getCellRange( const uno::Reference< excel::XRange >& rxRange )
2857 if( ScVbaRange* pVbaRange = getImplementation( rxRange ) )
2858 return pVbaRange->getCellRange();
2859 throw uno::RuntimeException();
2862 static InsertDeleteFlags getPasteFlags (sal_Int32 Paste)
2864 InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
2865 switch (Paste) {
2866 case excel::XlPasteType::xlPasteComments:
2867 nFlags = InsertDeleteFlags::NOTE;break;
2868 case excel::XlPasteType::xlPasteFormats:
2869 nFlags = InsertDeleteFlags::ATTRIB;break;
2870 case excel::XlPasteType::xlPasteFormulas:
2871 nFlags = InsertDeleteFlags::FORMULA;break;
2872 case excel::XlPasteType::xlPasteFormulasAndNumberFormats :
2873 case excel::XlPasteType::xlPasteValues:
2874 nFlags = ( InsertDeleteFlags::VALUE | InsertDeleteFlags::DATETIME | InsertDeleteFlags::STRING | InsertDeleteFlags::SPECIAL_BOOLEAN ); break;
2875 case excel::XlPasteType::xlPasteValuesAndNumberFormats:
2876 nFlags = InsertDeleteFlags::VALUE | InsertDeleteFlags::ATTRIB; break;
2877 case excel::XlPasteType::xlPasteColumnWidths:
2878 case excel::XlPasteType::xlPasteValidation:
2879 nFlags = InsertDeleteFlags::NONE;break;
2880 case excel::XlPasteType::xlPasteAll:
2881 case excel::XlPasteType::xlPasteAllExceptBorders:
2882 default:
2883 nFlags = InsertDeleteFlags::ALL;break;
2885 return nFlags;
2888 static ScPasteFunc
2889 getPasteFormulaBits( sal_Int32 Operation)
2891 ScPasteFunc nFormulaBits = ScPasteFunc::NONE;
2892 switch (Operation)
2894 case excel::XlPasteSpecialOperation::xlPasteSpecialOperationAdd:
2895 nFormulaBits = ScPasteFunc::ADD; break;
2896 case excel::XlPasteSpecialOperation::xlPasteSpecialOperationSubtract:
2897 nFormulaBits = ScPasteFunc::SUB;break;
2898 case excel::XlPasteSpecialOperation::xlPasteSpecialOperationMultiply:
2899 nFormulaBits = ScPasteFunc::MUL;break;
2900 case excel::XlPasteSpecialOperation::xlPasteSpecialOperationDivide:
2901 nFormulaBits = ScPasteFunc::DIV;break;
2903 case excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone:
2904 default:
2905 nFormulaBits = ScPasteFunc::NONE; break;
2908 return nFormulaBits;
2910 void SAL_CALL
2911 ScVbaRange::PasteSpecial( const uno::Any& Paste, const uno::Any& Operation, const uno::Any& SkipBlanks, const uno::Any& Transpose )
2913 if ( m_Areas->getCount() > 1 )
2914 throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
2915 ScDocShell* pShell = getScDocShell();
2917 if (!pShell)
2918 throw uno::RuntimeException(u"That command cannot be used with no ScDocShell"_ustr );
2920 uno::Reference< frame::XModel > xModel(pShell->GetModel(), uno::UNO_SET_THROW);
2921 uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
2922 // select this range
2923 xSelection->select( uno::Any( mxRange ) );
2924 // set up defaults
2925 sal_Int32 nPaste = excel::XlPasteType::xlPasteAll;
2926 sal_Int32 nOperation = excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone;
2927 bool bTranspose = false;
2928 bool bSkipBlanks = false;
2930 if ( Paste.hasValue() )
2931 Paste >>= nPaste;
2932 if ( Operation.hasValue() )
2933 Operation >>= nOperation;
2934 if ( SkipBlanks.hasValue() )
2935 SkipBlanks >>= bSkipBlanks;
2936 if ( Transpose.hasValue() )
2937 Transpose >>= bTranspose;
2939 InsertDeleteFlags nFlags = getPasteFlags(nPaste);
2940 ScPasteFunc nFormulaBits = getPasteFormulaBits(nOperation);
2942 excel::implnPasteSpecial(xModel, nFlags, nFormulaBits, bSkipBlanks, bTranspose);
2945 uno::Reference< excel::XRange >
2946 ScVbaRange::getEntireColumnOrRow( bool bColumn )
2948 ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
2949 // copy the range list
2950 ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
2951 ScDocument& rDoc = getScDocument();
2953 for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
2955 ScRange & rRange = aCellRanges[ i ];
2956 if ( bColumn )
2958 rRange.aStart.SetRow( 0 );
2959 rRange.aEnd.SetRow( rDoc.MaxRow() );
2961 else
2963 rRange.aStart.SetCol( 0 );
2964 rRange.aEnd.SetCol( rDoc.MaxCol() );
2967 if ( aCellRanges.size() > 1 ) // Multi-Area
2969 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
2971 return new ScVbaRange( mxParent, mxContext, xRanges, !bColumn, bColumn );
2973 const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
2974 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
2975 return new ScVbaRange( mxParent, mxContext, xRange, !bColumn, bColumn );
2978 uno::Reference< excel::XRange > SAL_CALL
2979 ScVbaRange::getEntireRow()
2981 return getEntireColumnOrRow(false);
2984 uno::Reference< excel::XRange > SAL_CALL
2985 ScVbaRange::getEntireColumn()
2987 return getEntireColumnOrRow(true);
2990 uno::Reference< excel::XComment > SAL_CALL
2991 ScVbaRange::AddComment( const uno::Any& Text )
2993 // if there is already a comment in the top-left cell then throw
2994 if( getComment().is() )
2995 throw uno::RuntimeException();
2997 // workaround: Excel allows to create empty comment, Calc does not
2998 OUString aNoteText;
2999 if( Text.hasValue() && !(Text >>= aNoteText) )
3000 throw uno::RuntimeException();
3001 if( aNoteText.isEmpty() )
3002 aNoteText = " ";
3004 // try to create a new annotation
3005 table::CellRangeAddress aRangePos = lclGetRangeAddress( mxRange );
3006 table::CellAddress aNotePos( aRangePos.Sheet, aRangePos.StartColumn, aRangePos.StartRow );
3007 uno::Reference< sheet::XSheetCellRange > xCellRange( mxRange, uno::UNO_QUERY_THROW );
3008 uno::Reference< sheet::XSheetAnnotationsSupplier > xAnnosSupp( xCellRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
3009 uno::Reference< sheet::XSheetAnnotations > xAnnos( xAnnosSupp->getAnnotations(), uno::UNO_SET_THROW );
3010 xAnnos->insertNew( aNotePos, aNoteText );
3011 return new ScVbaComment( this, mxContext, getUnoModel(), mxRange );
3014 uno::Reference< excel::XComment > SAL_CALL
3015 ScVbaRange::getComment()
3017 // intentional behavior to return a null object if no
3018 // comment defined
3019 uno::Reference< excel::XComment > xComment( new ScVbaComment( this, mxContext, getUnoModel(), mxRange ) );
3020 if ( xComment->Text( uno::Any(), uno::Any(), uno::Any() ).isEmpty() )
3021 return nullptr;
3022 return xComment;
3026 /// @throws uno::RuntimeException
3027 static uno::Reference< beans::XPropertySet >
3028 getRowOrColumnProps( const uno::Reference< table::XCellRange >& xCellRange, bool bRows )
3030 uno::Reference< table::XColumnRowRange > xColRow( xCellRange, uno::UNO_QUERY_THROW );
3031 uno::Reference< beans::XPropertySet > xProps;
3032 if ( bRows )
3033 xProps.set( xColRow->getRows(), uno::UNO_QUERY_THROW );
3034 else
3035 xProps.set( xColRow->getColumns(), uno::UNO_QUERY_THROW );
3036 return xProps;
3039 uno::Any SAL_CALL
3040 ScVbaRange::getHidden()
3042 // if multi-area result is the result of the
3043 // first area
3044 if ( m_Areas->getCount() > 1 )
3046 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(sal_Int32(1)), uno::Any() ), uno::UNO_QUERY_THROW );
3047 return xRange->getHidden();
3049 bool bIsVisible = false;
3052 uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
3053 if ( !( xProps->getPropertyValue( ISVISIBLE ) >>= bIsVisible ) )
3054 throw uno::RuntimeException(u"Failed to get IsVisible property"_ustr );
3056 catch( const uno::Exception& e )
3058 css::uno::Any anyEx = cppu::getCaughtException();
3059 throw css::lang::WrappedTargetRuntimeException( e.Message,
3060 nullptr, anyEx );
3062 return uno::Any( !bIsVisible );
3065 void SAL_CALL
3066 ScVbaRange::setHidden( const uno::Any& _hidden )
3068 if ( m_Areas->getCount() > 1 )
3070 sal_Int32 nItems = m_Areas->getCount();
3071 for ( sal_Int32 index=1; index <= nItems; ++index )
3073 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
3074 xRange->setHidden( _hidden );
3076 return;
3079 bool bHidden = extractBoolFromAny( _hidden );
3082 uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
3083 xProps->setPropertyValue( ISVISIBLE, uno::Any( !bHidden ) );
3085 catch( const uno::Exception& e )
3087 css::uno::Any anyEx = cppu::getCaughtException();
3088 throw css::lang::WrappedTargetRuntimeException( e.Message,
3089 nullptr, anyEx );
3093 sal_Bool SAL_CALL
3094 ScVbaRange::Replace( const OUString& What, const OUString& Replacement, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& MatchCase, const uno::Any& MatchByte, const uno::Any& SearchFormat, const uno::Any& ReplaceFormat )
3096 if ( m_Areas->getCount() > 1 )
3098 for ( sal_Int32 index = 1; index <= m_Areas->getCount(); ++index )
3100 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
3101 xRange->Replace( What, Replacement, LookAt, SearchOrder, MatchCase, MatchByte, SearchFormat, ReplaceFormat );
3103 return true; // seems to return true always ( or at least I haven't found the trick of
3106 // sanity check required params
3107 if ( What.isEmpty() )
3108 throw uno::RuntimeException(u"Range::Replace, missing params"_ustr );
3109 // #TODO #FIXME SearchFormat & ReplacesFormat are not processed
3110 // What do we do about MatchByte... we don't seem to support that
3111 const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
3112 SvxSearchItem newOptions( globalSearchOptions );
3114 uno::Reference< util::XReplaceable > xReplace( mxRange, uno::UNO_QUERY );
3115 if ( xReplace.is() )
3117 uno::Reference< util::XReplaceDescriptor > xDescriptor =
3118 xReplace->createReplaceDescriptor();
3120 xDescriptor->setSearchString(What);
3121 xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
3122 xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
3123 xDescriptor->setReplaceString( Replacement);
3124 if ( LookAt.hasValue() )
3126 // sets SearchWords ( true is Cell match )
3127 sal_Int16 nLook = ::comphelper::getINT16( LookAt );
3128 bool bSearchWords = false;
3129 if ( nLook == excel::XlLookAt::xlPart )
3130 bSearchWords = false;
3131 else if ( nLook == excel::XlLookAt::xlWhole )
3132 bSearchWords = true;
3133 else
3134 throw uno::RuntimeException(u"Range::Replace, illegal value for LookAt"_ustr );
3135 // set global search props ( affects the find dialog
3136 // and of course the defaults for this method
3137 newOptions.SetWordOnly( bSearchWords );
3138 xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
3140 // sets SearchByRow ( true for Rows )
3141 if ( SearchOrder.hasValue() )
3143 sal_Int16 nSearchOrder = ::comphelper::getINT16( SearchOrder );
3144 bool bSearchByRow = false;
3145 if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
3146 bSearchByRow = false;
3147 else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
3148 bSearchByRow = true;
3149 else
3150 throw uno::RuntimeException(u"Range::Replace, illegal value for SearchOrder"_ustr );
3152 newOptions.SetRowDirection( bSearchByRow );
3153 xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
3155 if ( MatchCase.hasValue() )
3157 bool bMatchCase = false;
3159 // SearchCaseSensitive
3160 MatchCase >>= bMatchCase;
3161 xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
3164 ScGlobal::SetSearchItem( newOptions );
3165 // ignore MatchByte for the moment, it's not supported in
3166 // OOo.org afaik
3168 uno::Reference< container::XIndexAccess > xIndexAccess = xReplace->findAll( xDescriptor );
3169 xReplace->replaceAll( xDescriptor );
3170 if ( xIndexAccess.is() && xIndexAccess->getCount() > 0 )
3172 for ( sal_Int32 i = 0; i < xIndexAccess->getCount(); ++i )
3174 uno::Reference< table::XCellRange > xCellRange( xIndexAccess->getByIndex( i ), uno::UNO_QUERY );
3175 if ( xCellRange.is() )
3177 uno::Reference< excel::XRange > xRange( new ScVbaRange( mxParent, mxContext, xCellRange ) );
3178 uno::Reference< container::XEnumerationAccess > xEnumAccess( xRange, uno::UNO_QUERY_THROW );
3179 uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
3180 while ( xEnum->hasMoreElements() )
3182 uno::Reference< excel::XRange > xNextRange( xEnum->nextElement(), uno::UNO_QUERY_THROW );
3183 ScVbaRange* pRange = dynamic_cast< ScVbaRange * > ( xNextRange.get() );
3184 if ( pRange )
3185 pRange->fireChangeEvent();
3191 return true; // always
3194 uno::Reference< excel::XRange > SAL_CALL
3195 ScVbaRange::Find( const uno::Any& What, const uno::Any& After, const uno::Any& LookIn, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& SearchDirection, const uno::Any& MatchCase, const uno::Any& /*MatchByte*/, const uno::Any& /*SearchFormat*/ )
3197 // return a Range object that represents the first cell where that information is found.
3198 OUString sWhat;
3199 sal_Int32 nWhat = 0;
3200 double fWhat = 0.0;
3202 // string.
3203 if( What >>= sWhat )
3205 else if( What >>= nWhat )
3207 sWhat = OUString::number( nWhat );
3209 else if( What >>= fWhat )
3211 sWhat = OUString::number( fWhat );
3213 else
3214 throw uno::RuntimeException(u"Range::Find, missing search-for-what param"_ustr );
3216 const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
3217 SvxSearchItem newOptions( globalSearchOptions );
3219 uno::Reference< util::XSearchable > xSearch( mxRange, uno::UNO_QUERY );
3220 if( xSearch.is() )
3222 uno::Reference< util::XSearchDescriptor > xDescriptor = xSearch->createSearchDescriptor();
3223 xDescriptor->setSearchString(sWhat);
3224 xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
3225 xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
3227 uno::Reference< excel::XRange > xAfterRange;
3228 uno::Reference< table::XCellRange > xStartCell;
3229 if( After >>= xAfterRange )
3231 // After must be a single cell in the range
3232 if( xAfterRange->getCount() > 1 )
3233 throw uno::RuntimeException(u"After must be a single cell."_ustr );
3234 uno::Reference< excel::XRange > xCell( Cells( uno::Any( xAfterRange->getRow() ), uno::Any( xAfterRange->getColumn() ) ), uno::UNO_SET_THROW );
3235 xStartCell.set( xAfterRange->getCellRange(), uno::UNO_QUERY_THROW );
3238 // LookIn
3239 if( LookIn.hasValue() )
3241 sal_Int32 nLookIn = 0;
3242 if( LookIn >>= nLookIn )
3244 SvxSearchCellType nSearchType;
3245 switch( nLookIn )
3247 case excel::XlFindLookIn::xlComments :
3248 nSearchType = SvxSearchCellType::NOTE; // Notes
3249 break;
3250 case excel::XlFindLookIn::xlFormulas :
3251 nSearchType = SvxSearchCellType::FORMULA;
3252 break;
3253 case excel::XlFindLookIn::xlValues :
3254 nSearchType = SvxSearchCellType::VALUE;
3255 break;
3256 default:
3257 throw uno::RuntimeException(u"Range::Find, illegal value for LookIn."_ustr );
3259 newOptions.SetCellType( nSearchType );
3260 xDescriptor->setPropertyValue( u"SearchType"_ustr, uno::Any( static_cast<sal_uInt16>(nSearchType) ) );
3264 // LookAt
3265 if ( LookAt.hasValue() )
3267 sal_Int16 nLookAt = ::comphelper::getINT16( LookAt );
3268 bool bSearchWords = false;
3269 if ( nLookAt == excel::XlLookAt::xlPart )
3270 bSearchWords = false;
3271 else if ( nLookAt == excel::XlLookAt::xlWhole )
3272 bSearchWords = true;
3273 else
3274 throw uno::RuntimeException(u"Range::Find, illegal value for LookAt"_ustr );
3275 newOptions.SetWordOnly( bSearchWords );
3276 xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
3279 // SearchOrder
3280 if ( SearchOrder.hasValue() )
3282 sal_Int16 nSearchOrder = ::comphelper::getINT16( SearchOrder );
3283 bool bSearchByRow = false;
3284 if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
3285 bSearchByRow = false;
3286 else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
3287 bSearchByRow = true;
3288 else
3289 throw uno::RuntimeException(u"Range::Find, illegal value for SearchOrder"_ustr );
3291 newOptions.SetRowDirection( bSearchByRow );
3292 xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
3295 // SearchDirection
3296 if ( SearchDirection.hasValue() )
3298 sal_Int32 nSearchDirection = 0;
3299 if( SearchDirection >>= nSearchDirection )
3301 bool bSearchBackwards = false;
3302 if ( nSearchDirection == excel::XlSearchDirection::xlNext )
3303 bSearchBackwards = false;
3304 else if( nSearchDirection == excel::XlSearchDirection::xlPrevious )
3305 bSearchBackwards = true;
3306 else
3307 throw uno::RuntimeException(u"Range::Find, illegal value for SearchDirection"_ustr );
3308 newOptions.SetBackward( bSearchBackwards );
3309 xDescriptor->setPropertyValue( u"SearchBackwards"_ustr, uno::Any( bSearchBackwards ) );
3313 // MatchCase
3314 bool bMatchCase = false;
3315 if ( MatchCase.hasValue() )
3317 // SearchCaseSensitive
3318 if( !( MatchCase >>= bMatchCase ) )
3319 throw uno::RuntimeException(u"Range::Find illegal value for MatchCase"_ustr );
3321 xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
3323 // MatchByte
3324 // SearchFormat
3325 // ignore
3327 ScGlobal::SetSearchItem( newOptions );
3329 uno::Reference< uno::XInterface > xInterface = xStartCell.is() ? xSearch->findNext( xStartCell, xDescriptor) : xSearch->findFirst( xDescriptor );
3330 uno::Reference< table::XCellRange > xCellRange( xInterface, uno::UNO_QUERY );
3331 // if we are searching from a starting cell and failed to find a match
3332 // then try from the beginning
3333 if ( !xCellRange.is() && xStartCell.is() )
3335 xInterface = xSearch->findFirst( xDescriptor );
3336 xCellRange.set( xInterface, uno::UNO_QUERY );
3338 if ( xCellRange.is() )
3340 uno::Reference< excel::XRange > xResultRange = new ScVbaRange( mxParent, mxContext, xCellRange );
3341 if( xResultRange.is() )
3343 return xResultRange;
3349 return uno::Reference< excel::XRange >();
3352 static uno::Reference< table::XCellRange > processKey( const uno::Any& Key, const uno::Reference< uno::XComponentContext >& xContext, ScDocShell* pDocSh )
3354 uno::Reference< excel::XRange > xKeyRange;
3355 if (Key.getValueTypeClass() == css::uno::TypeClass_INTERFACE)
3357 xKeyRange.set( Key, uno::UNO_QUERY_THROW );
3359 else if ( Key.getValueType() == ::cppu::UnoType<OUString>::get() )
3362 OUString sRangeName = ::comphelper::getString( Key );
3363 table::CellRangeAddress aRefAddr;
3364 if ( !pDocSh )
3365 throw uno::RuntimeException(u"Range::Sort no docshell to calculate key param"_ustr );
3366 xKeyRange = getRangeForName( xContext, sRangeName, pDocSh, aRefAddr );
3368 else
3369 throw uno::RuntimeException(u"Range::Sort illegal type value for key param"_ustr );
3370 uno::Reference< table::XCellRange > xKey;
3371 xKey.set( xKeyRange->getCellRange(), uno::UNO_QUERY_THROW );
3372 return xKey;
3375 // helper method for Sort
3376 /// @throws uno::RuntimeException
3377 static sal_Int32 findSortPropertyIndex( const uno::Sequence< beans::PropertyValue >& props,
3378 const OUString& sPropName )
3380 const beans::PropertyValue* pProp = std::find_if(props.begin(), props.end(),
3381 [&sPropName](const beans::PropertyValue& rProp) { return rProp.Name == sPropName; });
3383 if ( pProp == props.end() )
3384 throw uno::RuntimeException(u"Range::Sort unknown sort property"_ustr );
3385 return static_cast<sal_Int32>(std::distance(props.begin(), pProp));
3388 // helper method for Sort
3389 /// @throws uno::RuntimeException
3390 static void updateTableSortField( const uno::Reference< table::XCellRange >& xParentRange,
3391 const uno::Reference< table::XCellRange >& xColRowKey, sal_Int16 nOrder,
3392 table::TableSortField& aTableField, bool bIsSortColumn, bool bMatchCase )
3394 RangeHelper parentRange( xParentRange );
3395 RangeHelper colRowRange( xColRowKey );
3397 table::CellRangeAddress parentRangeAddress = parentRange.getCellRangeAddressable()->getRangeAddress();
3399 table::CellRangeAddress colRowKeyAddress = colRowRange.getCellRangeAddressable()->getRangeAddress();
3401 // make sure that upper left point of key range is within the
3402 // parent range
3403 if (
3404 ( bIsSortColumn || colRowKeyAddress.StartColumn < parentRangeAddress.StartColumn ||
3405 colRowKeyAddress.StartColumn > parentRangeAddress.EndColumn )
3407 ( !bIsSortColumn || colRowKeyAddress.StartRow < parentRangeAddress.StartRow ||
3408 colRowKeyAddress.StartRow > parentRangeAddress.EndRow )
3410 throw uno::RuntimeException(u"Illegal Key param"_ustr );
3412 //determine col/row index
3413 if ( bIsSortColumn )
3414 aTableField.Field = colRowKeyAddress.StartRow - parentRangeAddress.StartRow;
3415 else
3416 aTableField.Field = colRowKeyAddress.StartColumn - parentRangeAddress.StartColumn;
3417 aTableField.IsCaseSensitive = bMatchCase;
3419 if ( nOrder == excel::XlSortOrder::xlAscending )
3420 aTableField.IsAscending = true;
3421 else
3422 aTableField.IsAscending = false;
3427 void SAL_CALL
3428 ScVbaRange::Sort( const uno::Any& Key1, const uno::Any& Order1, const uno::Any& Key2, const uno::Any& /*Type*/, const uno::Any& Order2, const uno::Any& Key3, const uno::Any& Order3, const uno::Any& Header, const uno::Any& OrderCustom, const uno::Any& MatchCase, const uno::Any& Orientation, const uno::Any& SortMethod, const uno::Any& DataOption1, const uno::Any& DataOption2, const uno::Any& DataOption3 )
3430 // #TODO# #FIXME# can we do something with Type
3431 if ( m_Areas->getCount() > 1 )
3432 throw uno::RuntimeException(u"That command cannot be used on multiple selections"_ustr );
3434 sal_Int16 nDataOption1 = excel::XlSortDataOption::xlSortNormal;
3435 sal_Int16 nDataOption2 = excel::XlSortDataOption::xlSortNormal;
3436 sal_Int16 nDataOption3 = excel::XlSortDataOption::xlSortNormal;
3438 ScDocument& rDoc = getScDocument();
3440 uno::Reference< table::XCellRange > xRangeCurrent;
3441 if (isSingleCellRange())
3443 // Expand to CurrentRegion
3444 uno::Reference< excel::XRange > xCurrent( CurrentRegion());
3445 if (xCurrent.is())
3447 const ScVbaRange* pRange = getImplementation( xCurrent );
3448 if (pRange)
3449 xRangeCurrent = pRange->mxRange;
3452 if (!xRangeCurrent.is())
3453 xRangeCurrent = mxRange;
3454 RangeHelper thisRange( xRangeCurrent );
3455 table::CellRangeAddress thisRangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3457 ScSortParam aSortParam;
3458 SCTAB nTab = thisRangeAddress.Sheet;
3459 rDoc.GetSortParam( aSortParam, nTab );
3461 if ( DataOption1.hasValue() )
3462 DataOption1 >>= nDataOption1;
3463 if ( DataOption2.hasValue() )
3464 DataOption2 >>= nDataOption2;
3465 if ( DataOption3.hasValue() )
3466 DataOption3 >>= nDataOption3;
3468 // 1) #TODO #FIXME need to process DataOption[1..3] not used currently
3469 // 2) #TODO #FIXME need to refactor this ( below ) into an IsSingleCell() method
3470 uno::Reference< table::XColumnRowRange > xColumnRowRange(xRangeCurrent, uno::UNO_QUERY_THROW );
3472 // set up defaults
3474 sal_Int16 nOrder1 = aSortParam.maKeyState[0].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
3475 sal_Int16 nOrder2 = aSortParam.maKeyState[1].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
3476 sal_Int16 nOrder3 = aSortParam.maKeyState[2].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
3478 sal_Int16 nCustom = aSortParam.nUserIndex;
3479 sal_Int16 nSortMethod = excel::XlSortMethod::xlPinYin;
3480 bool bMatchCase = aSortParam.bCaseSens;
3482 // seems to work opposite to expected, see below
3483 sal_Int16 nOrientation = aSortParam.bByRow ? excel::XlSortOrientation::xlSortColumns : excel::XlSortOrientation::xlSortRows;
3485 if ( Orientation.hasValue() )
3487 // Documentation says xlSortRows is default but that doesn't appear to be
3488 // the case. Also it appears that xlSortColumns is the default which
3489 // strangely enough sorts by Row
3490 nOrientation = ::comphelper::getINT16( Orientation );
3491 // persist new option to be next calls default
3492 if ( nOrientation == excel::XlSortOrientation::xlSortRows )
3493 aSortParam.bByRow = false;
3494 else
3495 aSortParam.bByRow = true;
3499 bool bIsSortColumns=false; // sort by row
3501 if ( nOrientation == excel::XlSortOrientation::xlSortRows )
3502 bIsSortColumns = true;
3503 sal_Int16 nHeader = aSortParam.nCompatHeader;
3504 bool bContainsHeader = false;
3506 if ( Header.hasValue() )
3508 nHeader = ::comphelper::getINT16( Header );
3509 aSortParam.nCompatHeader = nHeader;
3512 if ( nHeader == excel::XlYesNoGuess::xlGuess )
3514 bool bHasColHeader = rDoc.HasColHeader( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), static_cast< SCCOL >( thisRangeAddress.EndColumn ), static_cast< SCROW >( thisRangeAddress.EndRow ), static_cast< SCTAB >( thisRangeAddress.Sheet ));
3515 bool bHasRowHeader = rDoc.HasRowHeader( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), static_cast< SCCOL >( thisRangeAddress.EndColumn ), static_cast< SCROW >( thisRangeAddress.EndRow ), static_cast< SCTAB >( thisRangeAddress.Sheet ) );
3516 if ( bHasColHeader || bHasRowHeader )
3517 nHeader = excel::XlYesNoGuess::xlYes;
3518 else
3519 nHeader = excel::XlYesNoGuess::xlNo;
3520 aSortParam.nCompatHeader = nHeader;
3523 if ( nHeader == excel::XlYesNoGuess::xlYes )
3524 bContainsHeader = true;
3526 if ( SortMethod.hasValue() )
3528 nSortMethod = ::comphelper::getINT16( SortMethod );
3531 if ( OrderCustom.hasValue() )
3533 OrderCustom >>= nCustom;
3534 --nCustom; // 0-based in OOo
3535 aSortParam.nUserIndex = nCustom;
3538 if ( MatchCase.hasValue() )
3540 MatchCase >>= bMatchCase;
3541 aSortParam.bCaseSens = bMatchCase;
3544 if ( Order1.hasValue() )
3546 nOrder1 = ::comphelper::getINT16(Order1);
3547 if ( nOrder1 == excel::XlSortOrder::xlAscending )
3548 aSortParam.maKeyState[0].bAscending = true;
3549 else
3550 aSortParam.maKeyState[0].bAscending = false;
3553 if ( Order2.hasValue() )
3555 nOrder2 = ::comphelper::getINT16(Order2);
3556 if ( nOrder2 == excel::XlSortOrder::xlAscending )
3557 aSortParam.maKeyState[1].bAscending = true;
3558 else
3559 aSortParam.maKeyState[1].bAscending = false;
3561 if ( Order3.hasValue() )
3563 nOrder3 = ::comphelper::getINT16(Order3);
3564 if ( nOrder3 == excel::XlSortOrder::xlAscending )
3565 aSortParam.maKeyState[2].bAscending = true;
3566 else
3567 aSortParam.maKeyState[2].bAscending = false;
3570 uno::Reference< table::XCellRange > xKey1;
3571 uno::Reference< table::XCellRange > xKey2;
3572 uno::Reference< table::XCellRange > xKey3;
3573 ScDocShell* pDocShell = getScDocShell();
3574 xKey1 = processKey( Key1, mxContext, pDocShell );
3575 if ( !xKey1.is() )
3576 throw uno::RuntimeException(u"Range::Sort needs a key1 param"_ustr );
3578 if ( Key2.hasValue() )
3579 xKey2 = processKey( Key2, mxContext, pDocShell );
3580 if ( Key3.hasValue() )
3581 xKey3 = processKey( Key3, mxContext, pDocShell );
3583 uno::Reference< util::XSortable > xSort( xRangeCurrent, uno::UNO_QUERY_THROW );
3584 uno::Sequence< beans::PropertyValue > sortDescriptor = xSort->createSortDescriptor();
3585 auto psortDescriptor = sortDescriptor.getArray();
3586 sal_Int32 nTableSortFieldIndex = findSortPropertyIndex( sortDescriptor, u"SortFields"_ustr );
3588 uno::Sequence< table::TableSortField > sTableFields(1);
3589 sal_Int32 nTableIndex = 0;
3590 updateTableSortField( xRangeCurrent, xKey1, nOrder1, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
3592 if ( xKey2.is() )
3594 sTableFields.realloc( sTableFields.getLength() + 1 );
3595 updateTableSortField( xRangeCurrent, xKey2, nOrder2, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
3597 if ( xKey3.is() )
3599 sTableFields.realloc( sTableFields.getLength() + 1 );
3600 updateTableSortField( xRangeCurrent, xKey3, nOrder3, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
3602 psortDescriptor[ nTableSortFieldIndex ].Value <<= sTableFields;
3604 sal_Int32 nIndex = findSortPropertyIndex( sortDescriptor, u"IsSortColumns"_ustr );
3605 psortDescriptor[ nIndex ].Value <<= bIsSortColumns;
3607 nIndex = findSortPropertyIndex( sortDescriptor, u"ContainsHeader"_ustr );
3608 psortDescriptor[ nIndex ].Value <<= bContainsHeader;
3610 rDoc.SetSortParam( aSortParam, nTab );
3611 xSort->sort( sortDescriptor );
3613 // #FIXME #TODO
3614 // The SortMethod param is not processed ( not sure what its all about, need to
3615 (void)nSortMethod;
3618 uno::Reference< excel::XRange > SAL_CALL
3619 ScVbaRange::End( ::sal_Int32 Direction )
3621 if ( m_Areas->getCount() > 1 )
3623 uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
3624 return xRange->End( Direction );
3627 // #FIXME #TODO
3628 // euch! found my orig implementation sucked, so
3629 // trying this even sucker one (really need to use/expose code in
3630 // around ScTabView::MoveCursorArea(), that's the bit that calculates
3631 // where the cursor should go)
3632 // Main problem with this method is the ultra hacky attempt to preserve
3633 // the ActiveCell, there should be no need to go to these extremes
3635 // Save ActiveSheet/ActiveCell pos (to restore later)
3636 uno::Any aDft;
3637 uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
3638 uno::Reference< excel::XWorksheet > sActiveSheet = xApplication->getActiveSheet();
3639 OUString sActiveCell = xApplication->getActiveCell()->Address(aDft, aDft, aDft, aDft, aDft );
3641 // position current cell upper left of this range
3642 Cells( uno::Any( sal_Int32(1) ), uno::Any( sal_Int32(1) ) )->Select();
3644 uno::Reference< frame::XModel > xModel = getModelFromRange( mxRange );
3646 SfxViewFrame* pViewFrame = excel::getViewFrame( xModel );
3647 if ( pViewFrame )
3649 SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
3650 // Hoping this will make sure this slot is called
3651 // synchronously
3652 SfxBoolItem sfxAsync( SID_ASYNCHRON, false );
3653 aArgs.Put( sfxAsync );
3654 SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher();
3656 sal_uInt16 nSID = 0;
3658 switch( Direction )
3660 case excel::XlDirection::xlDown:
3661 nSID = SID_CURSORBLKDOWN;
3662 break;
3663 case excel::XlDirection::xlUp:
3664 nSID = SID_CURSORBLKUP;
3665 break;
3666 case excel::XlDirection::xlToLeft:
3667 nSID = SID_CURSORBLKLEFT;
3668 break;
3669 case excel::XlDirection::xlToRight:
3670 nSID = SID_CURSORBLKRIGHT;
3671 break;
3672 default:
3673 throw uno::RuntimeException(u": Invalid ColumnIndex"_ustr );
3675 if ( pDispatcher )
3677 pDispatcher->Execute( nSID, SfxCallMode::SYNCHRON, aArgs );
3681 // result is the ActiveCell
3682 OUString sMoved = xApplication->getActiveCell()->Address(aDft, aDft, aDft, aDft, aDft );
3684 uno::Any aVoid;
3685 uno::Reference< excel::XRange > resultCell;
3686 resultCell.set( xApplication->getActiveSheet()->Range( uno::Any( sMoved ), aVoid ), uno::UNO_SET_THROW );
3688 // restore old ActiveCell
3689 uno::Reference< excel::XRange > xOldActiveCell( sActiveSheet->Range( uno::Any( sActiveCell ), aVoid ), uno::UNO_SET_THROW );
3690 xOldActiveCell->Select();
3693 // return result
3694 return resultCell;
3697 bool
3698 ScVbaRange::isSingleCellRange() const
3700 uno::Reference< sheet::XCellRangeAddressable > xAddressable( mxRange, uno::UNO_QUERY );
3701 if ( xAddressable.is() )
3703 table::CellRangeAddress aRangeAddr = xAddressable->getRangeAddress();
3704 return ( aRangeAddr.EndColumn == aRangeAddr.StartColumn && aRangeAddr.EndRow == aRangeAddr.StartRow );
3706 return false;
3709 uno::Reference< excel::XCharacters > SAL_CALL
3710 ScVbaRange::characters( const uno::Any& Start, const uno::Any& Length )
3712 if ( !isSingleCellRange() )
3713 throw uno::RuntimeException(u"Can't create Characters property for multicell range "_ustr );
3714 uno::Reference< text::XSimpleText > xSimple(mxRange->getCellByPosition(0,0) , uno::UNO_QUERY_THROW );
3715 ScDocument& rDoc = getDocumentFromRange(mxRange);
3717 ScVbaPalette aPalette( rDoc.GetDocumentShell() );
3718 return new ScVbaCharacters( this, mxContext, aPalette, xSimple, Start, Length );
3721 void SAL_CALL
3722 ScVbaRange::Delete( const uno::Any& Shift )
3724 if ( m_Areas->getCount() > 1 )
3726 sal_Int32 nItems = m_Areas->getCount();
3727 for ( sal_Int32 index=1; index <= nItems; ++index )
3729 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
3730 xRange->Delete( Shift );
3732 return;
3734 sheet::CellDeleteMode mode = sheet::CellDeleteMode_NONE ;
3735 RangeHelper thisRange( mxRange );
3736 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3737 if ( Shift.hasValue() )
3739 sal_Int32 nShift = 0;
3740 Shift >>= nShift;
3741 switch ( nShift )
3743 case excel::XlDeleteShiftDirection::xlShiftUp:
3744 mode = sheet::CellDeleteMode_UP;
3745 break;
3746 case excel::XlDeleteShiftDirection::xlShiftToLeft:
3747 mode = sheet::CellDeleteMode_LEFT;
3748 break;
3749 default:
3750 throw uno::RuntimeException(u"Illegal parameter "_ustr );
3753 else
3755 ScDocument& rDoc = getScDocument();
3756 bool bFullRow = ( thisAddress.StartColumn == 0 && thisAddress.EndColumn == rDoc.MaxCol() );
3757 sal_Int32 nCols = thisAddress.EndColumn - thisAddress.StartColumn;
3758 sal_Int32 nRows = thisAddress.EndRow - thisAddress.StartRow;
3759 if ( mbIsRows || bFullRow || ( nCols >= nRows ) )
3760 mode = sheet::CellDeleteMode_UP;
3761 else
3762 mode = sheet::CellDeleteMode_LEFT;
3764 uno::Reference< sheet::XCellRangeMovement > xCellRangeMove( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
3765 xCellRangeMove->removeRange( thisAddress, mode );
3769 //XElementAccess
3770 sal_Bool SAL_CALL
3771 ScVbaRange::hasElements()
3773 uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY );
3774 if ( xColumnRowRange.is() )
3775 if ( xColumnRowRange->getRows()->getCount() ||
3776 xColumnRowRange->getColumns()->getCount() )
3777 return true;
3778 return false;
3781 // XEnumerationAccess
3782 uno::Reference< container::XEnumeration > SAL_CALL
3783 ScVbaRange::createEnumeration()
3785 if ( mbIsColumns || mbIsRows )
3787 uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY );
3788 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
3789 sal_Int32 nElems = 0;
3790 if ( mbIsColumns )
3791 nElems = xColumnRowRange->getColumns()->getCount();
3792 else
3793 nElems = xColumnRowRange->getRows()->getCount();
3794 return new ColumnsRowEnumeration( xRange, nElems );
3797 return new CellsEnumeration( mxParent, mxContext, m_Areas );
3800 OUString SAL_CALL
3801 ScVbaRange::getDefaultMethodName( )
3803 return u"Item"_ustr;
3806 // returns calc internal col. width ( in points )
3807 double
3808 ScVbaRange::getCalcColWidth(const table::CellRangeAddress& rAddress)
3810 ScDocument& rDoc = getScDocument();
3811 sal_uInt16 nWidth = rDoc.GetOriginalWidth( static_cast< SCCOL >( rAddress.StartColumn ), static_cast< SCTAB >( rAddress.Sheet ) );
3812 double nPoints = lcl_TwipsToPoints( nWidth );
3813 nPoints = lcl_Round2DecPlaces( nPoints );
3814 return nPoints;
3817 double
3818 ScVbaRange::getCalcRowHeight(const table::CellRangeAddress& rAddress)
3820 ScDocument& rDoc = getDocumentFromRange( mxRange );
3821 sal_uInt16 nWidth = rDoc.GetOriginalHeight( rAddress.StartRow, rAddress.Sheet );
3822 double nPoints = lcl_TwipsToPoints( nWidth );
3823 nPoints = lcl_Round2DecPlaces( nPoints );
3824 return nPoints;
3827 // return Char Width in points
3828 static double getDefaultCharWidth( ScDocShell* pDocShell )
3830 ScDocument& rDoc = pDocShell->GetDocument();
3831 OutputDevice* pRefDevice = rDoc.GetRefDevice();
3832 vcl::Font aDefFont;
3833 rDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aDefFont, pRefDevice);
3834 pRefDevice->SetFont(aDefFont);
3835 tools::Long nCharWidth = pRefDevice->GetTextWidth( OUString( '0' ) ); // 1/100th mm
3836 return o3tl::convert<double>(nCharWidth, o3tl::Length::mm100, o3tl::Length::pt);
3839 uno::Any SAL_CALL
3840 ScVbaRange::getColumnWidth()
3842 sal_Int32 nLen = m_Areas->getCount();
3843 if ( nLen > 1 )
3845 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
3846 return xRange->getColumnWidth();
3849 double nColWidth = 0;
3850 ScDocShell* pShell = getScDocShell();
3851 if ( pShell )
3853 double defaultCharWidth = getDefaultCharWidth( pShell );
3854 RangeHelper thisRange( mxRange );
3855 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3856 sal_Int32 nStartCol = thisAddress.StartColumn;
3857 sal_Int32 nEndCol = thisAddress.EndColumn;
3858 sal_uInt16 nColTwips = 0;
3859 for( sal_Int32 nCol = nStartCol ; nCol <= nEndCol; ++nCol )
3861 thisAddress.StartColumn = nCol;
3862 sal_uInt16 nCurTwips = pShell->GetDocument().GetOriginalWidth( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCTAB >( thisAddress.Sheet ) );
3863 if ( nCol == nStartCol )
3864 nColTwips = nCurTwips;
3865 if ( nColTwips != nCurTwips )
3866 return aNULL();
3868 nColWidth = lcl_TwipsToPoints( nColTwips );
3869 if ( nColWidth != 0.0 )
3870 nColWidth = ( nColWidth / defaultCharWidth ) - fExtraWidth;
3872 nColWidth = lcl_Round2DecPlaces( nColWidth );
3873 return uno::Any( nColWidth );
3876 void SAL_CALL
3877 ScVbaRange::setColumnWidth( const uno::Any& _columnwidth )
3879 sal_Int32 nLen = m_Areas->getCount();
3880 if ( nLen > 1 )
3882 for ( sal_Int32 index = 1; index != nLen; ++index )
3884 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
3885 xRange->setColumnWidth( _columnwidth );
3887 return;
3889 double nColWidth = 0;
3890 _columnwidth >>= nColWidth;
3891 nColWidth = lcl_Round2DecPlaces( nColWidth );
3892 ScDocShell* pDocShell = getScDocShell();
3893 if ( !pDocShell )
3894 return;
3896 if ( nColWidth != 0.0 )
3897 nColWidth = ( nColWidth + fExtraWidth ) * getDefaultCharWidth( pDocShell );
3898 RangeHelper thisRange( mxRange );
3899 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
3900 sal_uInt16 nTwips = lcl_pointsToTwips( nColWidth );
3902 std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(thisAddress.StartColumn, thisAddress.EndColumn));
3903 // #163561# use mode SC_SIZE_DIRECT: hide for width 0, show for other values
3904 pDocShell->GetDocFunc().SetWidthOrHeight(
3905 true, aColArr, thisAddress.Sheet, SC_SIZE_DIRECT, nTwips, true, true);
3908 uno::Any SAL_CALL
3909 ScVbaRange::getWidth()
3911 if ( m_Areas->getCount() > 1 )
3913 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
3914 return xRange->getWidth();
3916 uno::Reference< table::XColumnRowRange > xColRowRange( mxRange, uno::UNO_QUERY_THROW );
3917 uno::Reference< container::XIndexAccess > xIndexAccess( xColRowRange->getColumns(), uno::UNO_QUERY_THROW );
3918 sal_Int32 nElems = xIndexAccess->getCount();
3919 double nWidth = 0;
3920 for ( sal_Int32 index=0; index<nElems; ++index )
3922 uno::Reference< sheet::XCellRangeAddressable > xAddressable( xIndexAccess->getByIndex( index ), uno::UNO_QUERY_THROW );
3923 double nTmpWidth = getCalcColWidth( xAddressable->getRangeAddress() );
3924 nWidth += nTmpWidth;
3926 return uno::Any( nWidth );
3929 uno::Any SAL_CALL
3930 ScVbaRange::Areas( const uno::Any& item)
3932 if ( !item.hasValue() )
3933 return uno::Any( uno::Reference< ov::XCollection >(m_Areas) );
3934 return m_Areas->Item( item, uno::Any() );
3937 uno::Reference< excel::XRange >
3938 ScVbaRange::getArea( sal_Int32 nIndex )
3940 if ( !m_Areas.is() )
3941 throw uno::RuntimeException(u"No areas available"_ustr );
3942 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( ++nIndex ), uno::Any() ), uno::UNO_QUERY_THROW );
3943 return xRange;
3946 uno::Any
3947 ScVbaRange::Borders( const uno::Any& item )
3949 if ( !item.hasValue() )
3950 return uno::Any( getBorders() );
3951 return getBorders()->Item( item, uno::Any() );
3954 uno::Any SAL_CALL
3955 ScVbaRange::BorderAround( const css::uno::Any& LineStyle, const css::uno::Any& Weight,
3956 const css::uno::Any& ColorIndex, const css::uno::Any& Color )
3958 sal_Int32 nCount = getBorders()->getCount();
3960 for( sal_Int32 i = 0; i < nCount; i++ )
3962 const sal_Int32 nLineType = supportedIndexTable[i];
3963 switch( nLineType )
3965 case excel::XlBordersIndex::xlEdgeLeft:
3966 case excel::XlBordersIndex::xlEdgeTop:
3967 case excel::XlBordersIndex::xlEdgeBottom:
3968 case excel::XlBordersIndex::xlEdgeRight:
3970 uno::Reference< excel::XBorder > xBorder( m_Borders->Item( uno::Any( nLineType ), uno::Any() ), uno::UNO_QUERY_THROW );
3971 if( LineStyle.hasValue() )
3973 xBorder->setLineStyle( LineStyle );
3975 if( Weight.hasValue() )
3977 xBorder->setWeight( Weight );
3979 if( ColorIndex.hasValue() )
3981 xBorder->setColorIndex( ColorIndex );
3983 if( Color.hasValue() )
3985 xBorder->setColor( Color );
3987 break;
3989 case excel::XlBordersIndex::xlInsideVertical:
3990 case excel::XlBordersIndex::xlInsideHorizontal:
3991 case excel::XlBordersIndex::xlDiagonalDown:
3992 case excel::XlBordersIndex::xlDiagonalUp:
3993 break;
3994 default:
3995 return uno::Any( false );
3998 return uno::Any( true );
4001 uno::Any SAL_CALL
4002 ScVbaRange::getRowHeight()
4004 sal_Int32 nLen = m_Areas->getCount();
4005 if ( nLen > 1 )
4007 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
4008 return xRange->getRowHeight();
4011 // if any row's RowHeight in the
4012 // range is different from any other, then return NULL
4013 RangeHelper thisRange( mxRange );
4014 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4016 sal_Int32 nStartRow = thisAddress.StartRow;
4017 sal_Int32 nEndRow = thisAddress.EndRow;
4018 sal_uInt16 nRowTwips = 0;
4019 // #TODO probably possible to use the SfxItemSet (and see if
4020 // SfxItemState::INVALID is set) to improve performance
4021 // #CHECKME looks like this is general behaviour not just row Range specific
4022 // if ( mbIsRows )
4023 ScDocShell* pShell = getScDocShell();
4024 if ( pShell )
4026 for ( sal_Int32 nRow = nStartRow ; nRow <= nEndRow; ++nRow )
4028 thisAddress.StartRow = nRow;
4029 sal_uInt16 nCurTwips = pShell->GetDocument().GetOriginalHeight( thisAddress.StartRow, thisAddress.Sheet );
4030 if ( nRow == nStartRow )
4031 nRowTwips = nCurTwips;
4032 if ( nRowTwips != nCurTwips )
4033 return aNULL();
4036 double nHeight = lcl_Round2DecPlaces( lcl_TwipsToPoints( nRowTwips ) );
4037 return uno::Any( nHeight );
4040 void SAL_CALL
4041 ScVbaRange::setRowHeight( const uno::Any& _rowheight)
4043 sal_Int32 nLen = m_Areas->getCount();
4044 if ( nLen > 1 )
4046 for ( sal_Int32 index = 1; index != nLen; ++index )
4048 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
4049 xRange->setRowHeight( _rowheight );
4051 return;
4053 double nHeight = 0; // Incoming height is in points
4054 _rowheight >>= nHeight;
4055 nHeight = lcl_Round2DecPlaces( nHeight );
4056 RangeHelper thisRange( mxRange );
4057 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4058 sal_uInt16 nTwips = lcl_pointsToTwips( nHeight );
4060 ScDocShell* pDocShell = getDocShellFromRange( mxRange );
4061 std::vector<sc::ColRowSpan> aRowArr(1, sc::ColRowSpan(thisAddress.StartRow, thisAddress.EndRow));
4062 pDocShell->GetDocFunc().SetWidthOrHeight(
4063 false, aRowArr, thisAddress.Sheet, SC_SIZE_ORIGINAL, nTwips, true, true);
4066 uno::Any SAL_CALL
4067 ScVbaRange::getPageBreak()
4069 sal_Int32 nPageBreak = excel::XlPageBreak::xlPageBreakNone;
4070 ScDocShell* pShell = getDocShellFromRange( mxRange );
4071 if ( pShell )
4073 RangeHelper thisRange( mxRange );
4074 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4075 bool bColumn = false;
4077 if (thisAddress.StartRow==0)
4078 bColumn = true;
4080 uno::Reference< frame::XModel > xModel = pShell->GetModel();
4081 if ( xModel.is() )
4083 ScDocument& rDoc = getDocumentFromRange( mxRange );
4085 ScBreakType nBreak = ScBreakType::NONE;
4086 if ( !bColumn )
4087 nBreak = rDoc.HasRowBreak(thisAddress.StartRow, thisAddress.Sheet);
4088 else
4089 nBreak = rDoc.HasColBreak(thisAddress.StartColumn, thisAddress.Sheet);
4091 if (nBreak & ScBreakType::Page)
4092 nPageBreak = excel::XlPageBreak::xlPageBreakAutomatic;
4094 if (nBreak & ScBreakType::Manual)
4095 nPageBreak = excel::XlPageBreak::xlPageBreakManual;
4099 return uno::Any( nPageBreak );
4102 void SAL_CALL
4103 ScVbaRange::setPageBreak( const uno::Any& _pagebreak)
4105 sal_Int32 nPageBreak = 0;
4106 _pagebreak >>= nPageBreak;
4108 ScDocShell* pShell = getDocShellFromRange( mxRange );
4109 if ( !pShell )
4110 return;
4112 RangeHelper thisRange( mxRange );
4113 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4114 if ((thisAddress.StartColumn==0) && (thisAddress.StartRow==0))
4115 return;
4116 bool bColumn = false;
4118 if (thisAddress.StartRow==0)
4119 bColumn = true;
4121 ScAddress aAddr( static_cast<SCCOL>(thisAddress.StartColumn), thisAddress.StartRow, thisAddress.Sheet );
4122 uno::Reference< frame::XModel > xModel = pShell->GetModel();
4123 if ( xModel.is() )
4125 ScTabViewShell* pViewShell = excel::getBestViewShell( xModel );
4126 if ( nPageBreak == excel::XlPageBreak::xlPageBreakManual )
4127 pViewShell->InsertPageBreak( bColumn, true, &aAddr);
4128 else if ( nPageBreak == excel::XlPageBreak::xlPageBreakNone )
4129 pViewShell->DeletePageBreak( bColumn, true, &aAddr);
4133 uno::Any SAL_CALL
4134 ScVbaRange::getHeight()
4136 if ( m_Areas->getCount() > 1 )
4138 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
4139 return xRange->getHeight();
4142 uno::Reference< table::XColumnRowRange > xColRowRange( mxRange, uno::UNO_QUERY_THROW );
4143 uno::Reference< container::XIndexAccess > xIndexAccess( xColRowRange->getRows(), uno::UNO_QUERY_THROW );
4144 sal_Int32 nElems = xIndexAccess->getCount();
4145 double nHeight = 0;
4146 for ( sal_Int32 index=0; index<nElems; ++index )
4148 uno::Reference< sheet::XCellRangeAddressable > xAddressable( xIndexAccess->getByIndex( index ), uno::UNO_QUERY_THROW );
4149 nHeight += getCalcRowHeight(xAddressable->getRangeAddress() );
4151 return uno::Any( nHeight );
4154 awt::Point
4155 ScVbaRange::getPosition() const
4157 awt::Point aPoint;
4158 uno::Reference< beans::XPropertySet > xProps;
4159 if ( mxRange.is() )
4160 xProps.set( mxRange, uno::UNO_QUERY_THROW );
4161 else
4162 xProps.set( mxRanges, uno::UNO_QUERY_THROW );
4163 xProps->getPropertyValue( u"Position"_ustr ) >>= aPoint;
4164 return aPoint;
4166 uno::Any SAL_CALL
4167 ScVbaRange::getLeft()
4169 // helperapi returns the first ranges left ( and top below )
4170 if ( m_Areas->getCount() > 1 )
4171 return getArea( 0 )->getLeft();
4172 awt::Point aPoint = getPosition();
4173 return uno::Any(o3tl::convert<double>(aPoint.X, o3tl::Length::mm100, o3tl::Length::pt));
4176 uno::Any SAL_CALL
4177 ScVbaRange::getTop()
4179 // helperapi returns the first ranges top
4180 if ( m_Areas->getCount() > 1 )
4181 return getArea( 0 )->getTop();
4182 awt::Point aPoint= getPosition();
4183 return uno::Any(o3tl::convert<double>(aPoint.Y, o3tl::Length::mm100, o3tl::Length::pt));
4186 static uno::Reference< sheet::XCellRangeReferrer > getNamedRange( const uno::Reference< uno::XInterface >& xIf, const uno::Reference< table::XCellRange >& thisRange )
4188 uno::Reference< beans::XPropertySet > xProps( xIf, uno::UNO_QUERY_THROW );
4189 uno::Reference< container::XNameAccess > xNameAccess( xProps->getPropertyValue( u"NamedRanges"_ustr ), uno::UNO_QUERY_THROW );
4191 const uno::Sequence< OUString > sNames = xNameAccess->getElementNames();
4192 // uno::Reference< table::XCellRange > thisRange( getCellRange(), uno::UNO_QUERY_THROW );
4193 uno::Reference< sheet::XCellRangeReferrer > xNamedRange;
4194 for ( const auto& rName : sNames )
4196 uno::Reference< sheet::XCellRangeReferrer > xName( xNameAccess->getByName( rName ), uno::UNO_QUERY );
4197 if ( xName.is() )
4199 if ( thisRange == xName->getReferredCells() )
4201 xNamedRange = std::move(xName);
4202 break;
4206 return xNamedRange;
4209 uno::Reference< excel::XName >
4210 ScVbaRange::getName()
4212 uno::Reference< beans::XPropertySet > xProps( getUnoModel(), uno::UNO_QUERY );
4213 uno::Reference< table::XCellRange > thisRange( getCellRange(), uno::UNO_QUERY_THROW );
4214 // Application range
4215 uno::Reference< sheet::XCellRangeReferrer > xNamedRange = getNamedRange( xProps, thisRange );
4217 if ( !xNamedRange.is() )
4219 // not in application range then assume it might be in
4220 // sheet namedranges
4221 RangeHelper aRange( thisRange );
4222 uno::Reference< sheet::XSpreadsheet > xSheet = aRange.getSpreadSheet();
4223 xProps.set( xSheet, uno::UNO_QUERY );
4224 // impl here
4225 xNamedRange = getNamedRange( xProps, thisRange );
4227 if ( xProps.is() && xNamedRange.is() )
4229 uno::Reference< sheet::XNamedRanges > xNamedRanges( xProps, uno::UNO_QUERY_THROW );
4230 uno::Reference< sheet::XNamedRange > xName( xNamedRange, uno::UNO_QUERY_THROW );
4231 return new ScVbaName( mxParent, mxContext, xName, xNamedRanges, getUnoModel() );
4233 return uno::Reference< excel::XName >();
4236 uno::Reference< excel::XWorksheet >
4237 ScVbaRange::getWorksheet()
4239 // #TODO #FIXME parent should always be set up ( currently that's not
4240 // the case )
4241 uno::Reference< excel::XWorksheet > xSheet( getParent(), uno::UNO_QUERY );
4242 if ( !xSheet.is() )
4244 uno::Reference< table::XCellRange > xRange = mxRange;
4246 if ( mxRanges.is() ) // assign xRange to first range
4248 uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
4249 xRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
4251 ScDocShell* pDocShell = getDocShellFromRange(xRange);
4252 RangeHelper rHelper(xRange);
4253 // parent should be Thisworkbook
4254 xSheet.set( new ScVbaWorksheet( uno::Reference< XHelperInterface >(), mxContext,rHelper.getSpreadSheet(),pDocShell->GetModel()) );
4256 return xSheet;
4259 // #TODO remove this ugly application processing
4260 // Process an application Range request e.g. 'Range("a1,b2,a4:b6")
4261 uno::Reference< excel::XRange >
4262 ScVbaRange::ApplicationRange( const uno::Reference< uno::XComponentContext >& xContext, const css::uno::Any &Cell1, const css::uno::Any &Cell2 )
4264 // Although the documentation seems clear that Range without a
4265 // qualifier then it's a shortcut for ActiveSheet.Range
4266 // however, similarly Application.Range is apparently also a
4267 // shortcut for ActiveSheet.Range
4268 // The is however a subtle behavioural difference I've come across
4269 // wrt to named ranges.
4270 // If a named range "test" exists { Sheet1!$A1 } and the active sheet
4271 // is Sheet2 then the following will fail
4272 // msgbox ActiveSheet.Range("test").Address ' fails
4273 // msgbox WorkSheets("Sheet2").Range("test").Address
4274 // but!!!
4275 // msgbox Range("test").Address ' works
4276 // msgbox Application.Range("test").Address ' works
4278 // Single param Range
4279 OUString sRangeName;
4280 Cell1 >>= sRangeName;
4281 if ( Cell1.hasValue() && !Cell2.hasValue() && !sRangeName.isEmpty() )
4283 uno::Reference< beans::XPropertySet > xPropSet( getCurrentExcelDoc(xContext), uno::UNO_QUERY_THROW );
4285 uno::Reference< container::XNameAccess > xNamed( xPropSet->getPropertyValue( u"NamedRanges"_ustr ), uno::UNO_QUERY_THROW );
4286 uno::Reference< sheet::XCellRangeReferrer > xReferrer;
4289 xReferrer.set ( xNamed->getByName( sRangeName ), uno::UNO_QUERY );
4291 catch( uno::Exception& /*e*/ )
4293 // do nothing
4295 if ( xReferrer.is() )
4297 uno::Reference< table::XCellRange > xRange = xReferrer->getReferredCells();
4298 if ( xRange.is() )
4300 uno::Reference< excel::XRange > xVbRange = new ScVbaRange( excel::getUnoSheetModuleObj( xRange ), xContext, xRange );
4301 return xVbRange;
4306 uno::Reference<table::XCellRange> xSheetRange;
4310 uno::Reference<sheet::XSpreadsheetView> xView(
4311 getCurrentExcelDoc(xContext)->getCurrentController(), uno::UNO_QUERY_THROW);
4313 xSheetRange.set(xView->getActiveSheet(), uno::UNO_QUERY_THROW);
4315 catch (const uno::Exception&)
4317 return uno::Reference<excel::XRange>();
4320 rtl::Reference<ScVbaRange> pRange = new ScVbaRange( excel::getUnoSheetModuleObj( xSheetRange ), xContext, xSheetRange );
4321 return pRange->Range( Cell1, Cell2, true );
4324 // Helper functions for AutoFilter
4325 static ScDBData* lcl_GetDBData_Impl( ScDocShell* pDocShell, sal_Int16 nSheet )
4327 ScDBData* pRet = nullptr;
4328 if (pDocShell)
4330 pRet = pDocShell->GetDocument().GetAnonymousDBData(nSheet);
4332 return pRet;
4335 static void lcl_SelectAll( ScDocShell* pDocShell, const ScQueryParam& aParam )
4337 if ( !pDocShell )
4338 return;
4340 ScViewData* pViewData = ScDocShell::GetViewData();
4341 if ( !pViewData )
4343 ScTabViewShell* pViewSh = pDocShell->GetBestViewShell( true );
4344 pViewData = pViewSh ? &pViewSh->GetViewData() : nullptr;
4347 if ( pViewData )
4349 pViewData->GetView()->Query( aParam, nullptr, true );
4353 static ScQueryParam lcl_GetQueryParam( ScDocShell* pDocShell, sal_Int16 nSheet )
4355 ScDBData* pDBData = lcl_GetDBData_Impl( pDocShell, nSheet );
4356 ScQueryParam aParam;
4357 if (pDBData)
4359 pDBData->GetQueryParam( aParam );
4361 return aParam;
4364 static void lcl_SetAllQueryForField( ScDocShell* pDocShell, SCCOLROW nField, sal_Int16 nSheet )
4366 ScQueryParam aParam = lcl_GetQueryParam( pDocShell, nSheet );
4367 aParam.RemoveEntryByField(nField);
4368 lcl_SelectAll( pDocShell, aParam );
4371 // Modifies sCriteria, and nOp depending on the value of sCriteria
4372 static void lcl_setTableFieldsFromCriteria( OUString& sCriteria1, const uno::Reference< beans::XPropertySet >& xDescProps, sheet::TableFilterField2& rFilterField )
4374 // #TODO make this more efficient and cycle through
4375 // sCriteria1 character by character to pick up <,<>,=, * etc.
4376 // right now I am more concerned with just getting it to work right
4378 sCriteria1 = sCriteria1.trim();
4379 // table of translation of criteria text to FilterOperators
4380 // <>searchtext - NOT_EQUAL
4381 // =searchtext - EQUAL
4382 // *searchtext - startwith
4383 // <>*searchtext - doesn't startwith
4384 // *searchtext* - contains
4385 // <>*searchtext* - doesn't contain
4386 // [>|>=|<=|...]searchtext for GREATER_value, GREATER_EQUAL_value etc.
4387 if ( sCriteria1.startsWith( EQUALS ) )
4389 if ( o3tl::make_unsigned(sCriteria1.getLength()) == strlen(EQUALS) )
4390 rFilterField.Operator = sheet::FilterOperator2::EMPTY;
4391 else
4393 rFilterField.Operator = sheet::FilterOperator2::EQUAL;
4394 sCriteria1 = sCriteria1.copy( strlen(EQUALS) );
4395 sCriteria1 = VBAToRegexp( sCriteria1 );
4396 // UseRegularExpressions
4397 if ( xDescProps.is() )
4398 xDescProps->setPropertyValue( u"UseRegularExpressions"_ustr, uno::Any( true ) );
4402 else if ( sCriteria1.startsWith( NOTEQUALS ) )
4404 if ( o3tl::make_unsigned(sCriteria1.getLength()) == strlen(NOTEQUALS) )
4405 rFilterField.Operator = sheet::FilterOperator2::NOT_EMPTY;
4406 else
4408 rFilterField.Operator = sheet::FilterOperator2::NOT_EQUAL;
4409 sCriteria1 = sCriteria1.copy( strlen(NOTEQUALS) );
4410 sCriteria1 = VBAToRegexp( sCriteria1 );
4411 // UseRegularExpressions
4412 if ( xDescProps.is() )
4413 xDescProps->setPropertyValue( u"UseRegularExpressions"_ustr, uno::Any( true ) );
4416 else if ( sCriteria1.startsWith( GREATERTHAN ) )
4418 if ( sCriteria1.startsWith( GREATERTHANEQUALS ) )
4420 sCriteria1 = sCriteria1.copy( strlen(GREATERTHANEQUALS) );
4421 rFilterField.Operator = sheet::FilterOperator2::GREATER_EQUAL;
4423 else
4425 sCriteria1 = sCriteria1.copy( strlen(GREATERTHAN) );
4426 rFilterField.Operator = sheet::FilterOperator2::GREATER;
4430 else if ( sCriteria1.startsWith( LESSTHAN ) )
4432 if ( sCriteria1.startsWith( LESSTHANEQUALS ) )
4434 sCriteria1 = sCriteria1.copy( strlen(LESSTHANEQUALS) );
4435 rFilterField.Operator = sheet::FilterOperator2::LESS_EQUAL;
4437 else
4439 sCriteria1 = sCriteria1.copy( strlen(LESSTHAN) );
4440 rFilterField.Operator = sheet::FilterOperator2::LESS;
4444 else
4445 rFilterField.Operator = sheet::FilterOperator2::EQUAL;
4447 // tdf#107885 - check if criteria is numeric using locale dependent settings without group separator
4448 // or, if the decimal separator is different from the English locale, without any locale.
4449 sal_Int32 nParseEnd = 0;
4450 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
4451 double fValue = ScGlobal::getLocaleData().stringToDouble( sCriteria1, false, &eStatus, &nParseEnd );
4452 if ( nParseEnd == sCriteria1.getLength() && eStatus == rtl_math_ConversionStatus_Ok )
4454 rFilterField.IsNumeric = true;
4455 rFilterField.NumericValue = fValue;
4457 else if ( ScGlobal::getLocaleData().getNumDecimalSep().toChar() != '.' )
4459 eStatus = rtl_math_ConversionStatus_Ok;
4460 fValue = ::rtl::math::stringToDouble( sCriteria1, '.', 0, &eStatus, &nParseEnd );
4461 if ( nParseEnd == sCriteria1.getLength() && eStatus == rtl_math_ConversionStatus_Ok )
4463 rFilterField.IsNumeric = true;
4464 rFilterField.NumericValue = fValue;
4468 rFilterField.StringValue = sCriteria1;
4471 void SAL_CALL
4472 ScVbaRange::AutoFilter( const uno::Any& aField, const uno::Any& Criteria1, const uno::Any& Operator, const uno::Any& Criteria2, const uno::Any& /*VisibleDropDown*/ )
4474 // Is there an existing autofilter
4475 RangeHelper thisRange( mxRange );
4476 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4477 sal_Int16 nSheet = thisAddress.Sheet;
4478 ScDocShell* pShell = getScDocShell();
4479 bool bHasAuto = false;
4480 uno::Reference< sheet::XDatabaseRange > xDataBaseRange = excel::GetAutoFiltRange( pShell, nSheet );
4481 if ( xDataBaseRange.is() )
4482 bHasAuto = true;
4484 if ( !bHasAuto )
4486 if ( m_Areas->getCount() > 1 )
4487 throw uno::RuntimeException( STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY );
4489 table::CellRangeAddress autoFiltAddress;
4490 //CurrentRegion()
4491 if ( isSingleCellRange() )
4493 uno::Reference< excel::XRange > xCurrent( CurrentRegion() );
4494 if ( xCurrent.is() )
4496 ScVbaRange* pRange = getImplementation( xCurrent );
4497 if ( pRange )
4499 if ( pRange->isSingleCellRange() )
4500 throw uno::RuntimeException(u"Can't create AutoFilter"_ustr );
4501 RangeHelper currentRegion( pRange->mxRange );
4502 autoFiltAddress = currentRegion.getCellRangeAddressable()->getRangeAddress();
4506 else // multi-cell range
4508 RangeHelper multiCellRange( mxRange );
4509 autoFiltAddress = multiCellRange.getCellRangeAddressable()->getRangeAddress();
4510 // #163530# Filter box shows only entry of first row
4511 ScDocument* pDocument = ( pShell ? &pShell->GetDocument() : nullptr );
4512 if ( pDocument )
4514 SCCOL nStartCol = autoFiltAddress.StartColumn;
4515 SCROW nStartRow = autoFiltAddress.StartRow;
4516 SCCOL nEndCol = autoFiltAddress.EndColumn;
4517 SCROW nEndRow = autoFiltAddress.EndRow;
4518 pDocument->GetDataArea( autoFiltAddress.Sheet, nStartCol, nStartRow, nEndCol, nEndRow, true, true );
4519 autoFiltAddress.StartColumn = nStartCol;
4520 autoFiltAddress.StartRow = nStartRow;
4521 autoFiltAddress.EndColumn = nEndCol;
4522 autoFiltAddress.EndRow = nEndRow;
4526 uno::Reference< sheet::XUnnamedDatabaseRanges > xDBRanges = excel::GetUnnamedDataBaseRanges( pShell );
4527 if ( xDBRanges.is() )
4529 if ( !xDBRanges->hasByTable( nSheet ) )
4530 xDBRanges->setByTable( autoFiltAddress );
4531 xDataBaseRange.set( xDBRanges->getByTable(nSheet ), uno::UNO_QUERY_THROW );
4533 if ( !xDataBaseRange.is() )
4534 throw uno::RuntimeException(u"Failed to find the autofilter placeholder range"_ustr );
4536 uno::Reference< beans::XPropertySet > xDBRangeProps( xDataBaseRange, uno::UNO_QUERY_THROW );
4537 // set autofilter
4538 xDBRangeProps->setPropertyValue( u"AutoFilter"_ustr, uno::Any(true) );
4539 // set header (autofilter always need column headers)
4540 uno::Reference< beans::XPropertySet > xFiltProps( xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY_THROW );
4541 xFiltProps->setPropertyValue( u"ContainsHeader"_ustr, uno::Any( true ) );
4544 sal_Int32 nField = 0; // *IS* 1 based
4545 sal_Int32 nOperator = excel::XlAutoFilterOperator::xlAnd;
4547 sheet::FilterConnection nConn = sheet::FilterConnection_AND;
4548 double nCriteria1 = 0;
4550 bool bHasCritValue = Criteria1.hasValue();
4551 bool bCritHasNumericValue = false; // not sure if a numeric criteria is possible
4552 if ( bHasCritValue )
4553 bCritHasNumericValue = ( Criteria1 >>= nCriteria1 );
4555 if ( !aField.hasValue() && ( Criteria1.hasValue() || Operator.hasValue() || Criteria2.hasValue() ) )
4556 throw uno::RuntimeException();
4557 uno::Any Field( aField );
4558 if ( !( Field >>= nField ) )
4560 const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
4563 Field = xConverter->convertTo( aField, cppu::UnoType<sal_Int32>::get() );
4565 catch( uno::Exception& )
4569 // Use the normal uno api, sometimes e.g. when you want to use ALL as the filter
4570 // we can't use refresh as the uno interface doesn't have a concept of ALL
4571 // in this case we just call the core calc functionality -
4572 if ( Field >>= nField )
4574 uno::Reference< sheet::XSheetFilterDescriptor2 > xDesc(
4575 xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY );
4576 if ( xDesc.is() )
4578 OUString sCriteria1;
4579 bool bAcceptCriteria2 = true;
4580 bool bAll = false;
4581 uno::Sequence< sheet::TableFilterField2 > sTabFilts;
4582 sheet::TableFilterField2* pTabFilts = nullptr;
4583 uno::Reference< beans::XPropertySet > xDescProps( xDesc, uno::UNO_QUERY_THROW );
4584 if ( Criteria1.hasValue() )
4586 sTabFilts.realloc( 1 );
4587 pTabFilts = sTabFilts.getArray();
4588 pTabFilts[0].Operator = sheet::FilterOperator2::EQUAL;// sensible default
4589 if ( !bCritHasNumericValue )
4591 Criteria1 >>= sCriteria1;
4592 if ( sCriteria1.isEmpty() )
4594 uno::Sequence< OUString > aCriteria1;
4595 Criteria1 >>= aCriteria1;
4596 sal_uInt16 nLength = aCriteria1.getLength();
4597 if ( nLength )
4599 // When sequence is provided for Criteria1 don't care about Criteria2
4600 bAcceptCriteria2 = false;
4602 auto pCriteria1 = aCriteria1.getArray();
4603 sTabFilts.realloc( nLength );
4604 pTabFilts = sTabFilts.getArray();
4605 for ( sal_uInt16 i = 0; i < nLength; ++i )
4607 lcl_setTableFieldsFromCriteria( pCriteria1[i], xDescProps, pTabFilts[i] );
4608 pTabFilts[i].Connection = sheet::FilterConnection_OR;
4609 pTabFilts[i].Field = (nField - 1);
4612 else
4613 bAll = true;
4615 else
4617 pTabFilts[0].IsNumeric = bCritHasNumericValue;
4618 if ( bHasCritValue && !sCriteria1.isEmpty() )
4619 lcl_setTableFieldsFromCriteria( sCriteria1, xDescProps, pTabFilts[0] );
4620 else
4621 bAll = true;
4624 else // numeric
4626 pTabFilts[0].IsNumeric = true;
4627 pTabFilts[0].NumericValue = nCriteria1;
4630 else // no value specified
4631 bAll = true;
4632 // not sure what the relationship between Criteria1 and Operator is,
4633 // e.g. can you have an Operator without a Criteria? In LibreOffice it
4634 if ( Operator.hasValue() && ( Operator >>= nOperator ) )
4636 assert(pTabFilts);
4637 // if it's a bottom/top Ten(Percent/Value) and there
4638 // is no value specified for criteria1 set it to 10
4639 if ( !bCritHasNumericValue && sCriteria1.isEmpty() && ( nOperator != excel::XlAutoFilterOperator::xlOr ) && ( nOperator != excel::XlAutoFilterOperator::xlAnd ) )
4641 pTabFilts[0].IsNumeric = true;
4642 pTabFilts[0].NumericValue = 10;
4643 bAll = false;
4645 switch ( nOperator )
4647 case excel::XlAutoFilterOperator::xlBottom10Items:
4648 pTabFilts[0].Operator = sheet::FilterOperator2::BOTTOM_VALUES;
4649 break;
4650 case excel::XlAutoFilterOperator::xlBottom10Percent:
4651 pTabFilts[0].Operator = sheet::FilterOperator2::BOTTOM_PERCENT;
4652 break;
4653 case excel::XlAutoFilterOperator::xlTop10Items:
4654 pTabFilts[0].Operator = sheet::FilterOperator2::TOP_VALUES;
4655 break;
4656 case excel::XlAutoFilterOperator::xlTop10Percent:
4657 pTabFilts[0].Operator = sheet::FilterOperator2::TOP_PERCENT;
4658 break;
4659 case excel::XlAutoFilterOperator::xlOr:
4660 nConn = sheet::FilterConnection_OR;
4661 break;
4662 case excel::XlAutoFilterOperator::xlAnd:
4663 nConn = sheet::FilterConnection_AND;
4664 break;
4665 default:
4666 throw uno::RuntimeException(u"UnknownOption"_ustr );
4671 if ( !bAll && bAcceptCriteria2 )
4673 assert(pTabFilts);
4674 pTabFilts[0].Connection = sheet::FilterConnection_AND;
4675 pTabFilts[0].Field = (nField - 1);
4677 uno::Sequence< OUString > aCriteria2;
4678 if ( Criteria2.hasValue() ) // there is a Criteria2
4680 sTabFilts.realloc(2);
4681 pTabFilts = sTabFilts.getArray();
4682 pTabFilts[1].Field = sTabFilts[0].Field;
4683 pTabFilts[1].Connection = nConn;
4685 OUString sCriteria2;
4686 if ( Criteria2 >>= sCriteria2 )
4688 if ( !sCriteria2.isEmpty() )
4690 uno::Reference< beans::XPropertySet > xProps;
4691 lcl_setTableFieldsFromCriteria( sCriteria2, xProps, pTabFilts[1] );
4692 pTabFilts[1].IsNumeric = false;
4695 else if ( Criteria2 >>= aCriteria2 )
4697 sal_uInt16 nLength = aCriteria2.getLength();
4698 if ( nLength )
4700 // For compatibility use only the last value from the sequence
4701 lcl_setTableFieldsFromCriteria( aCriteria2.getArray()[nLength - 1], xDescProps, pTabFilts[1] );
4704 else // numeric
4706 Criteria2 >>= pTabFilts[1].NumericValue;
4707 pTabFilts[1].IsNumeric = true;
4708 pTabFilts[1].Operator = sheet::FilterOperator2::EQUAL;
4713 xDesc->setFilterFields2( sTabFilts );
4714 if ( !bAll )
4716 xDataBaseRange->refresh();
4718 else
4719 // was 0 based now seems to be 1
4720 lcl_SetAllQueryForField( pShell, nField, nSheet );
4723 else
4725 // this is just to toggle autofilter on and off ( not to be confused with
4726 // a VisibleDropDown option combined with a field, in that case just the
4727 // button should be disabled ) - currently we don't support that
4728 uno::Reference< beans::XPropertySet > xDBRangeProps( xDataBaseRange, uno::UNO_QUERY_THROW );
4729 if ( bHasAuto )
4731 // find the any field with the query and select all
4732 ScQueryParam aParam = lcl_GetQueryParam( pShell, nSheet );
4733 for (SCSIZE i = 0; i< aParam.GetEntryCount(); ++i)
4735 ScQueryEntry& rEntry = aParam.GetEntry(i);
4736 if ( rEntry.bDoQuery )
4737 lcl_SetAllQueryForField( pShell, rEntry.nField, nSheet );
4739 // remove existing filters
4740 uno::Reference< sheet::XSheetFilterDescriptor2 > xSheetFilterDescriptor(
4741 xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY );
4742 if( xSheetFilterDescriptor.is() )
4743 xSheetFilterDescriptor->setFilterFields2( uno::Sequence< sheet::TableFilterField2 >() );
4745 xDBRangeProps->setPropertyValue( u"AutoFilter"_ustr, uno::Any(!bHasAuto) );
4750 void SAL_CALL
4751 ScVbaRange::Insert( const uno::Any& Shift, const uno::Any& /*CopyOrigin*/ )
4753 // It appears (from the web) that the undocumented CopyOrigin
4754 // param should contain member of enum XlInsertFormatOrigin
4755 // which can have values xlFormatFromLeftOrAbove or xlFormatFromRightOrBelow
4756 // #TODO investigate resultant behaviour using these constants
4757 // currently just processing Shift
4759 sheet::CellInsertMode mode = sheet::CellInsertMode_NONE;
4760 if ( Shift.hasValue() )
4762 sal_Int32 nShift = 0;
4763 Shift >>= nShift;
4764 switch ( nShift )
4766 case excel::XlInsertShiftDirection::xlShiftToRight:
4767 mode = sheet::CellInsertMode_RIGHT;
4768 break;
4769 case excel::XlInsertShiftDirection::xlShiftDown:
4770 mode = sheet::CellInsertMode_DOWN;
4771 break;
4772 default:
4773 throw uno::RuntimeException(u"Illegal parameter "_ustr );
4776 else
4778 if ( getRow() >= getColumn() )
4779 mode = sheet::CellInsertMode_DOWN;
4780 else
4781 mode = sheet::CellInsertMode_RIGHT;
4783 RangeHelper thisRange( mxRange );
4784 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4785 uno::Reference< sheet::XCellRangeMovement > xCellRangeMove( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
4786 xCellRangeMove->insertCells( thisAddress, mode );
4788 // Paste from clipboard only if the clipboard content was copied via VBA, and not already pasted via VBA again.
4789 // "Insert" behavior should not depend on random clipboard content previously copied by the user.
4790 ScDocShell* pDocShell = getDocShellFromRange( mxRange );
4791 const ScTransferObj* pClipObj = pDocShell ? ScTransferObj::GetOwnClipboard(pDocShell->GetClipData()) : nullptr;
4792 if ( pClipObj && pClipObj->GetUseInApi() )
4794 // After the insert ( this range ) actually has moved
4795 ScRange aRange( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCROW >( thisAddress.StartRow ), static_cast< SCTAB >( thisAddress.Sheet ), static_cast< SCCOL >( thisAddress.EndColumn ), static_cast< SCROW >( thisAddress.EndRow ), static_cast< SCTAB >( thisAddress.Sheet ) );
4796 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getDocShellFromRange( mxRange ) , aRange ) );
4797 uno::Reference< excel::XRange > xVbaRange( new ScVbaRange( mxParent, mxContext, xRange, mbIsRows, mbIsColumns ) );
4798 xVbaRange->PasteSpecial( uno::Any(), uno::Any(), uno::Any(), uno::Any() );
4802 void SAL_CALL
4803 ScVbaRange::Autofit()
4805 sal_Int32 nLen = m_Areas->getCount();
4806 if ( nLen > 1 )
4808 for ( sal_Int32 index = 1; index != nLen; ++index )
4810 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
4811 xRange->Autofit();
4813 return;
4816 // if the range is a not a row or column range autofit will
4817 // throw an error
4818 if ( !( mbIsColumns || mbIsRows ) )
4819 DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
4820 ScDocShell* pDocShell = getDocShellFromRange( mxRange );
4821 if ( !pDocShell )
4822 return;
4824 RangeHelper thisRange( mxRange );
4825 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
4827 std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(thisAddress.StartColumn,thisAddress.EndColumn));
4828 bool bDirection = true;
4829 if ( mbIsRows )
4831 bDirection = false;
4832 aColArr[0].mnStart = thisAddress.StartRow;
4833 aColArr[0].mnEnd = thisAddress.EndRow;
4835 pDocShell->GetDocFunc().SetWidthOrHeight(
4836 bDirection, aColArr, thisAddress.Sheet, SC_SIZE_OPTIMAL, 0, true, true);
4839 uno::Any SAL_CALL
4840 ScVbaRange::Hyperlinks( const uno::Any& aIndex )
4842 /* The range object always returns a new Hyperlinks object containing a
4843 fixed list of existing hyperlinks in the range.
4844 See vbahyperlinks.hxx for more details. */
4846 // get the global hyperlink object of the sheet (sheet should always be the parent of a Range object)
4847 uno::Reference< excel::XWorksheet > xWorksheet( getParent(), uno::UNO_QUERY_THROW );
4848 uno::Reference< excel::XHyperlinks > xSheetHlinks( xWorksheet->Hyperlinks( uno::Any() ), uno::UNO_QUERY_THROW );
4849 ScVbaHyperlinksRef xScSheetHlinks( dynamic_cast< ScVbaHyperlinks* >( xSheetHlinks.get() ) );
4850 if( !xScSheetHlinks.is() )
4851 throw uno::RuntimeException(u"Cannot obtain hyperlinks implementation object"_ustr );
4853 // create a new local hyperlinks object based on the sheet hyperlinks
4854 ScVbaHyperlinksRef xHlinks( new ScVbaHyperlinks( getParent(), mxContext, xScSheetHlinks, getScRangeList() ) );
4855 if( aIndex.hasValue() )
4856 return xHlinks->Item( aIndex, uno::Any() );
4857 return uno::Any( uno::Reference< excel::XHyperlinks >( xHlinks ) );
4860 css::uno::Reference< excel::XValidation > SAL_CALL
4861 ScVbaRange::getValidation()
4863 if ( !m_xValidation.is() )
4864 m_xValidation = new ScVbaValidation( this, mxContext, mxRange );
4865 return m_xValidation;
4868 namespace {
4870 /// @throws uno::RuntimeException
4871 sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCell >& rxCell )
4873 /* TODO/FIXME: We need an apostroph-prefix property at the cell to
4874 implement this correctly. For now, return an apostroph for every text
4875 cell.
4877 TODO/FIXME: When Application.TransitionNavigKeys is supported and true,
4878 this function needs to inspect the cell formatting and return different
4879 prefixes according to the horizontal cell alignment.
4881 return (rxCell->getType() == table::CellContentType_TEXT) ? '\'' : 0;
4884 /// @throws uno::RuntimeException
4885 sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCellRange >& rxRange )
4887 /* This implementation is able to handle different prefixes (needed if
4888 Application.TransitionNavigKeys is true). The function lclGetPrefixChar
4889 for single cells called from here may return any prefix. If that
4890 function returns an empty prefix (NUL character) or different non-empty
4891 prefixes for two cells, this function returns 0.
4893 sal_Unicode cCurrPrefix = 0;
4894 table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxRange );
4895 sal_Int32 nEndCol = aRangeAddr.EndColumn - aRangeAddr.StartColumn;
4896 sal_Int32 nEndRow = aRangeAddr.EndRow - aRangeAddr.StartRow;
4897 for( sal_Int32 nRow = 0; nRow <= nEndRow; ++nRow )
4899 for( sal_Int32 nCol = 0; nCol <= nEndCol; ++nCol )
4901 uno::Reference< table::XCell > xCell( rxRange->getCellByPosition( nCol, nRow ), uno::UNO_SET_THROW );
4902 sal_Unicode cNewPrefix = lclGetPrefixChar( xCell );
4903 if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) )
4904 return 0;
4905 cCurrPrefix = cNewPrefix;
4908 // all cells contain the same prefix - return it
4909 return cCurrPrefix;
4912 /// @throws uno::RuntimeException
4913 sal_Unicode lclGetPrefixChar( const uno::Reference< sheet::XSheetCellRangeContainer >& rxRanges )
4915 sal_Unicode cCurrPrefix = 0;
4916 uno::Reference< container::XEnumerationAccess > xRangesEA( rxRanges, uno::UNO_QUERY_THROW );
4917 uno::Reference< container::XEnumeration > xRangesEnum( xRangesEA->createEnumeration(), uno::UNO_SET_THROW );
4918 while( xRangesEnum->hasMoreElements() )
4920 uno::Reference< table::XCellRange > xRange( xRangesEnum->nextElement(), uno::UNO_QUERY_THROW );
4921 sal_Unicode cNewPrefix = lclGetPrefixChar( xRange );
4922 if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) )
4923 return 0;
4924 cCurrPrefix = cNewPrefix;
4926 // all ranges contain the same prefix - return it
4927 return cCurrPrefix;
4930 uno::Any lclGetPrefixVariant( sal_Unicode cPrefixChar )
4932 return uno::Any( (cPrefixChar == 0) ? OUString() : OUString( cPrefixChar ) );
4935 } // namespace
4937 uno::Any SAL_CALL ScVbaRange::getPrefixCharacter()
4939 /* (1) If Application.TransitionNavigKeys is false, this function returns
4940 an apostroph character if the text cell begins with an apostroph
4941 character (formula return values are not taken into account); otherwise
4942 an empty string.
4944 (2) If Application.TransitionNavigKeys is true, this function returns
4945 an apostroph character, if the cell is left-aligned; a double-quote
4946 character, if the cell is right-aligned; a circumflex character, if the
4947 cell is centered; a backslash character, if the cell is set to filled;
4948 or an empty string, if nothing of the above.
4950 If a range or a list of ranges contains texts with leading apostroph
4951 character as well as other cells, this function returns an empty
4952 string.
4955 if( mxRange.is() )
4956 return lclGetPrefixVariant( lclGetPrefixChar( mxRange ) );
4957 if( mxRanges.is() )
4958 return lclGetPrefixVariant( lclGetPrefixChar( mxRanges ) );
4959 throw uno::RuntimeException(u"Unexpected empty Range object"_ustr );
4962 uno::Any ScVbaRange::getShowDetail()
4964 // #FIXME, If the specified range is in a PivotTable report
4966 // In MSO VBA, the specified range must be a single summary column or row in an outline. otherwise throw exception
4967 if( m_Areas->getCount() > 1 )
4968 throw uno::RuntimeException(u"Can not get Range.ShowDetail attribute "_ustr );
4970 RangeHelper helper( mxRange );
4971 uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = helper.getSheetCellCursor();
4972 xSheetCellCursor->collapseToCurrentRegion();
4973 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
4974 table::CellRangeAddress aOutlineAddress = xCellRangeAddressable->getRangeAddress();
4976 // check if the specified range is a single summary column or row.
4977 table::CellRangeAddress thisAddress = helper.getCellRangeAddressable()->getRangeAddress();
4978 if( (thisAddress.StartRow != thisAddress.EndRow || thisAddress.EndRow != aOutlineAddress.EndRow ) &&
4979 (thisAddress.StartColumn != thisAddress.EndColumn || thisAddress.EndColumn != aOutlineAddress.EndColumn ))
4981 throw uno::RuntimeException(u"Can not set Range.ShowDetail attribute"_ustr );
4984 bool bColumn = thisAddress.StartRow != thisAddress.EndRow;
4985 ScDocument& rDoc = getDocumentFromRange( mxRange );
4986 ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable(static_cast<SCTAB>(thisAddress.Sheet), true);
4987 const ScOutlineArray& rOutlineArray = bColumn ? pOutlineTable->GetColArray(): pOutlineTable->GetRowArray();
4988 SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(thisAddress.EndColumn-1):static_cast<SCCOLROW>(thisAddress.EndRow-1);
4989 const ScOutlineEntry* pEntry = rOutlineArray.GetEntryByPos( 0, nPos );
4990 if( pEntry )
4992 const bool bShowDetail = !pEntry->IsHidden();
4993 return uno::Any( bShowDetail );
4996 return aNULL();
4999 void ScVbaRange::setShowDetail(const uno::Any& aShowDetail)
5001 // #FIXME, If the specified range is in a PivotTable report
5003 // In MSO VBA, the specified range must be a single summary column or row in an outline. otherwise throw exception
5004 if( m_Areas->getCount() > 1 )
5005 throw uno::RuntimeException(u"Can not set Range.ShowDetail attribute"_ustr );
5007 bool bShowDetail = extractBoolFromAny( aShowDetail );
5009 RangeHelper helper( mxRange );
5010 uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = helper.getSheetCellCursor();
5011 xSheetCellCursor->collapseToCurrentRegion();
5012 uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
5013 table::CellRangeAddress aOutlineAddress = xCellRangeAddressable->getRangeAddress();
5015 // check if the specified range is a single summary column or row.
5016 table::CellRangeAddress thisAddress = helper.getCellRangeAddressable()->getRangeAddress();
5017 if( (thisAddress.StartRow != thisAddress.EndRow || thisAddress.EndRow != aOutlineAddress.EndRow ) &&
5018 (thisAddress.StartColumn != thisAddress.EndColumn || thisAddress.EndColumn != aOutlineAddress.EndColumn ))
5020 throw uno::RuntimeException(u"Can not set Range.ShowDetail attribute"_ustr );
5023 // #FIXME, seems there is a different behavior between MSO and OOo.
5024 // In OOo, the showDetail will show all the level entries, while only show the first level entry in MSO
5025 uno::Reference< sheet::XSheetOutline > xSheetOutline( helper.getSpreadSheet(), uno::UNO_QUERY_THROW );
5026 if( bShowDetail )
5027 xSheetOutline->showDetail( aOutlineAddress );
5028 else
5029 xSheetOutline->hideDetail( aOutlineAddress );
5033 uno::Reference< excel::XRange > SAL_CALL
5034 ScVbaRange::MergeArea()
5036 uno::Reference< sheet::XSheetCellRange > xMergeShellCellRange(mxRange->getCellRangeByPosition(0,0,0,0), uno::UNO_QUERY_THROW);
5037 uno::Reference< sheet::XSheetCellCursor > xMergeSheetCursor(xMergeShellCellRange->getSpreadsheet()->createCursorByRange( xMergeShellCellRange ), uno::UNO_SET_THROW);
5038 if( xMergeSheetCursor.is() )
5040 xMergeSheetCursor->collapseToMergedArea();
5041 uno::Reference<sheet::XCellRangeAddressable> xMergeCellAddress(xMergeSheetCursor, uno::UNO_QUERY_THROW);
5042 table::CellRangeAddress aCellAddress = xMergeCellAddress->getRangeAddress();
5043 if( aCellAddress.StartColumn ==0 && aCellAddress.EndColumn==0 &&
5044 aCellAddress.StartRow==0 && aCellAddress.EndRow==0)
5046 return new ScVbaRange( mxParent,mxContext,mxRange );
5048 else
5050 ScRange refRange( static_cast< SCCOL >( aCellAddress.StartColumn ), static_cast< SCROW >( aCellAddress.StartRow ), static_cast< SCTAB >( aCellAddress.Sheet ),
5051 static_cast< SCCOL >( aCellAddress.EndColumn ), static_cast< SCROW >( aCellAddress.EndRow ), static_cast< SCTAB >( aCellAddress.Sheet ) );
5052 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell() , refRange ) );
5053 return new ScVbaRange( mxParent, mxContext,xRange );
5056 return new ScVbaRange( mxParent, mxContext, mxRange );
5059 void SAL_CALL
5060 ScVbaRange::PrintOut( const uno::Any& From, const uno::Any& To, const uno::Any& Copies, const uno::Any& Preview, const uno::Any& ActivePrinter, const uno::Any& PrintToFile, const uno::Any& Collate, const uno::Any& PrToFileName )
5062 ScDocShell* pShell = nullptr;
5064 sal_Int32 nItems = m_Areas->getCount();
5065 uno::Sequence< table::CellRangeAddress > printAreas( nItems );
5066 auto printAreasRange = asNonConstRange(printAreas);
5067 uno::Reference< sheet::XPrintAreas > xPrintAreas;
5068 for ( sal_Int32 index=1; index <= nItems; ++index )
5070 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5072 RangeHelper thisRange( xRange->getCellRange() );
5073 table::CellRangeAddress rangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5074 if ( index == 1 )
5076 ScVbaRange* pRange = getImplementation( xRange );
5077 // initialise the doc shell and the printareas
5078 pShell = getDocShellFromRange( pRange->mxRange );
5079 xPrintAreas.set( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5081 printAreasRange[ index - 1 ] = rangeAddress;
5083 if ( pShell && xPrintAreas.is() )
5085 xPrintAreas->setPrintAreas( printAreas );
5086 uno::Reference< frame::XModel > xModel = pShell->GetModel();
5087 PrintOutHelper( excel::getBestViewShell( xModel ), From, To, Copies, Preview, ActivePrinter, PrintToFile, Collate, PrToFileName, true );
5091 void SAL_CALL
5092 ScVbaRange::AutoFill( const uno::Reference< excel::XRange >& Destination, const uno::Any& Type )
5094 uno::Reference< excel::XRange > xDest( Destination, uno::UNO_SET_THROW );
5095 ScVbaRange* pRange = getImplementation( xDest );
5096 RangeHelper destRangeHelper( pRange->mxRange );
5097 table::CellRangeAddress destAddress = destRangeHelper.getCellRangeAddressable()->getRangeAddress();
5099 RangeHelper thisRange( mxRange );
5100 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5101 ScRange sourceRange;
5102 ScRange destRange;
5104 ScUnoConversion::FillScRange( destRange, destAddress );
5105 ScUnoConversion::FillScRange( sourceRange, thisAddress );
5107 FillDir eDir = FILL_TO_BOTTOM;
5108 double fStep = 1.0;
5110 ScRange aRange( destRange );
5111 ScRange aSourceRange( destRange );
5113 // default to include the number of Rows in the source range;
5114 SCCOLROW nSourceCount = ( sourceRange.aEnd.Row() - sourceRange.aStart.Row() ) + 1;
5115 SCCOLROW nCount = 0;
5117 if ( sourceRange != destRange )
5119 // Find direction of fill, vertical or horizontal
5120 if ( sourceRange.aStart == destRange.aStart )
5122 if ( sourceRange.aEnd.Row() == destRange.aEnd.Row() )
5124 nSourceCount = ( sourceRange.aEnd.Col() - sourceRange.aStart.Col() + 1 );
5125 aSourceRange.aEnd.SetCol( static_cast<SCCOL>( aSourceRange.aStart.Col() + nSourceCount - 1 ) );
5126 eDir = FILL_TO_RIGHT;
5127 nCount = aRange.aEnd.Col() - aSourceRange.aEnd.Col();
5129 else if ( sourceRange.aEnd.Col() == destRange.aEnd.Col() )
5131 aSourceRange.aEnd.SetRow( static_cast<SCROW>( aSourceRange.aStart.Row() + nSourceCount ) - 1 );
5132 nCount = aRange.aEnd.Row() - aSourceRange.aEnd.Row();
5133 eDir = FILL_TO_BOTTOM;
5137 else if ( aSourceRange.aEnd == destRange.aEnd )
5139 if ( sourceRange.aStart.Col() == destRange.aStart.Col() )
5141 aSourceRange.aStart.SetRow( static_cast<SCROW>( aSourceRange.aEnd.Row() - nSourceCount + 1 ) );
5142 nCount = aSourceRange.aStart.Row() - aRange.aStart.Row();
5143 eDir = FILL_TO_TOP;
5144 fStep = -fStep;
5146 else if ( sourceRange.aStart.Row() == destRange.aStart.Row() )
5148 nSourceCount = ( sourceRange.aEnd.Col() - sourceRange.aStart.Col() ) + 1;
5149 aSourceRange.aStart.SetCol( static_cast<SCCOL>( aSourceRange.aEnd.Col() - nSourceCount + 1 ) );
5150 nCount = aSourceRange.aStart.Col() - aRange.aStart.Col();
5151 eDir = FILL_TO_LEFT;
5152 fStep = -fStep;
5157 FillCmd eCmd = FILL_AUTO;
5158 FillDateCmd eDateCmd = FILL_DAY;
5160 if ( Type.hasValue() )
5162 sal_Int16 nFillType = excel::XlAutoFillType::xlFillDefault;
5163 Type >>= nFillType;
5164 switch ( nFillType )
5166 case excel::XlAutoFillType::xlFillCopy:
5167 eCmd = FILL_SIMPLE;
5168 fStep = 0.0;
5169 break;
5170 case excel::XlAutoFillType::xlFillDays:
5171 eCmd = FILL_DATE;
5172 break;
5173 case excel::XlAutoFillType::xlFillMonths:
5174 eCmd = FILL_DATE;
5175 eDateCmd = FILL_MONTH;
5176 break;
5177 case excel::XlAutoFillType::xlFillWeekdays:
5178 eCmd = FILL_DATE;
5179 eDateCmd = FILL_WEEKDAY;
5180 break;
5181 case excel::XlAutoFillType::xlFillYears:
5182 eCmd = FILL_DATE;
5183 eDateCmd = FILL_YEAR;
5184 break;
5185 case excel::XlAutoFillType::xlGrowthTrend:
5186 eCmd = FILL_GROWTH;
5187 break;
5188 case excel::XlAutoFillType::xlFillFormats:
5189 throw uno::RuntimeException(u"xlFillFormat not supported for AutoFill"_ustr );
5190 case excel::XlAutoFillType::xlFillValues:
5191 case excel::XlAutoFillType::xlFillSeries:
5192 case excel::XlAutoFillType::xlLinearTrend:
5193 eCmd = FILL_LINEAR;
5194 break;
5195 case excel::XlAutoFillType::xlFillDefault:
5196 default:
5197 eCmd = FILL_AUTO;
5198 break;
5201 ScDocShell* pDocSh = getDocShellFromRange( mxRange );
5202 pDocSh->GetDocFunc().FillAuto( aSourceRange, nullptr, eDir, eCmd, eDateCmd,
5203 nCount, fStep, MAXDOUBLE/*fEndValue*/, true, true );
5205 sal_Bool SAL_CALL
5206 ScVbaRange::GoalSeek( const uno::Any& Goal, const uno::Reference< excel::XRange >& ChangingCell )
5208 ScDocShell* pDocShell = getScDocShell();
5209 bool bRes = true;
5210 ScVbaRange* pRange = static_cast< ScVbaRange* >( ChangingCell.get() );
5211 if ( pDocShell && pRange )
5213 RangeHelper thisRange( mxRange );
5214 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5215 RangeHelper changingCellRange( pRange->mxRange );
5216 table::CellRangeAddress changingCellAddr = changingCellRange.getCellRangeAddressable()->getRangeAddress();
5217 OUString sGoal = getAnyAsString( Goal );
5218 table::CellAddress thisCell( thisAddress.Sheet, thisAddress.StartColumn, thisAddress.StartRow );
5219 table::CellAddress changingCell( changingCellAddr.Sheet, changingCellAddr.StartColumn, changingCellAddr.StartRow );
5220 sheet::GoalResult res = pDocShell->GetModel()->seekGoal( thisCell, changingCell, sGoal );
5221 ChangingCell->setValue( uno::Any( res.Result ) );
5223 // openoffice behaves differently, result is 0 if the divergence is too great
5224 // but... if it detects 0 is the value it requires then it will use that
5225 // e.g. divergence & result both = 0.0 does NOT mean there is an error
5226 if ( ( res.Divergence != 0.0 ) && ( res.Result == 0.0 ) )
5227 bRes = false;
5229 else
5230 bRes = false;
5231 return bRes;
5234 void
5235 ScVbaRange::Calculate( )
5237 getWorksheet()->Calculate();
5240 uno::Reference< excel::XRange > SAL_CALL
5241 ScVbaRange::Item( const uno::Any& row, const uno::Any& column )
5243 if ( mbIsRows || mbIsColumns )
5245 if ( column.hasValue() )
5246 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5247 uno::Reference< excel::XRange > xRange;
5248 if ( mbIsColumns )
5249 xRange = Columns( row );
5250 else
5251 xRange = Rows( row );
5252 return xRange;
5254 return Cells( row, column );
5257 void
5258 ScVbaRange::AutoOutline( )
5260 // #TODO #FIXME needs to check for summary row/col ( whatever they are )
5261 // not valid for multi Area Addresses
5262 if ( m_Areas->getCount() > 1 )
5263 DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY);
5264 // So needs to either span an entire Row or a just be a single cell
5265 // ( that contains a summary RowColumn )
5266 // also the Single cell cause doesn't seem to be handled specially in
5267 // this code ( ported from the helperapi RangeImpl.java,
5268 // RangeRowsImpl.java, RangesImpl.java, RangeSingleCellImpl.java
5269 RangeHelper thisRange( mxRange );
5270 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5272 if ( isSingleCellRange() || mbIsRows )
5274 uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5275 xSheetOutline->autoOutline( thisAddress );
5277 else
5278 DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
5281 void SAL_CALL
5282 ScVbaRange:: ClearOutline( )
5284 if ( m_Areas->getCount() > 1 )
5286 sal_Int32 nItems = m_Areas->getCount();
5287 for ( sal_Int32 index=1; index <= nItems; ++index )
5289 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5290 xRange->ClearOutline();
5292 return;
5294 RangeHelper thisRange( mxRange );
5295 uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5296 xSheetOutline->clearOutline();
5299 void
5300 ScVbaRange::groupUnGroup( bool bUnGroup )
5302 if ( m_Areas->getCount() > 1 )
5303 DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY);
5304 table::TableOrientation nOrient = table::TableOrientation_ROWS;
5305 if ( mbIsColumns )
5306 nOrient = table::TableOrientation_COLUMNS;
5307 RangeHelper thisRange( mxRange );
5308 table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
5309 uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
5310 if ( bUnGroup )
5311 xSheetOutline->ungroup( thisAddress, nOrient );
5312 else
5313 xSheetOutline->group( thisAddress, nOrient );
5316 void SAL_CALL
5317 ScVbaRange::Group( )
5319 groupUnGroup(false);
5321 void SAL_CALL
5322 ScVbaRange::Ungroup( )
5324 groupUnGroup(true);
5327 /// @throws uno::RuntimeException
5328 static void lcl_mergeCellsOfRange( const uno::Reference< table::XCellRange >& xCellRange, bool _bMerge )
5330 uno::Reference< util::XMergeable > xMergeable( xCellRange, uno::UNO_QUERY_THROW );
5331 xMergeable->merge(_bMerge);
5333 void SAL_CALL
5334 ScVbaRange::Merge( const uno::Any& Across )
5336 if ( m_Areas->getCount() > 1 )
5338 sal_Int32 nItems = m_Areas->getCount();
5339 for ( sal_Int32 index=1; index <= nItems; ++index )
5341 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5342 xRange->Merge(Across);
5344 return;
5346 bool bAcross = false;
5347 Across >>= bAcross;
5348 if ( !bAcross )
5349 lcl_mergeCellsOfRange( mxRange, true );
5350 else
5352 uno::Reference< excel::XRange > oRangeRowsImpl = Rows( uno::Any() );
5353 // #TODO #FIXME this seems incredibly lame, this can't be right
5354 for (sal_Int32 i=1; i <= oRangeRowsImpl->getCount();i++)
5356 oRangeRowsImpl->Cells( uno::Any( i ), uno::Any() )->Merge( uno::Any( false ) );
5361 void SAL_CALL
5362 ScVbaRange::UnMerge( )
5364 if ( m_Areas->getCount() > 1 )
5366 sal_Int32 nItems = m_Areas->getCount();
5367 for ( sal_Int32 index=1; index <= nItems; ++index )
5369 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5370 xRange->UnMerge();
5372 return;
5374 lcl_mergeCellsOfRange( mxRange, false);
5377 uno::Any SAL_CALL
5378 ScVbaRange::getStyle()
5380 if ( m_Areas->getCount() > 1 )
5382 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW );
5383 return xRange->getStyle();
5385 uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
5386 OUString sStyleName;
5387 xProps->getPropertyValue( CELLSTYLE ) >>= sStyleName;
5388 ScDocShell* pShell = getScDocShell();
5389 uno::Reference< frame::XModel > xModel( pShell->GetModel() );
5390 uno::Reference< excel::XStyle > xStyle = new ScVbaStyle( this, mxContext, sStyleName, xModel );
5391 return uno::Any( xStyle );
5393 void SAL_CALL
5394 ScVbaRange::setStyle( const uno::Any& _style )
5396 if ( m_Areas->getCount() > 1 )
5398 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW );
5399 xRange->setStyle( _style );
5400 return;
5402 uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
5403 uno::Reference< excel::XStyle > xStyle;
5404 _style >>= xStyle;
5405 if ( xStyle.is() )
5406 xProps->setPropertyValue( CELLSTYLE, uno::Any( xStyle->getName() ) );
5409 uno::Reference< excel::XRange >
5410 ScVbaRange::PreviousNext( bool bIsPrevious )
5412 ScMarkData markedRange(getScDocument().GetSheetLimits());
5413 ScRange refRange;
5414 RangeHelper thisRange( mxRange );
5416 ScUnoConversion::FillScRange( refRange, thisRange.getCellRangeAddressable()->getRangeAddress());
5417 markedRange. SetMarkArea( refRange );
5418 short nMove = bIsPrevious ? -1 : 1;
5420 SCCOL nNewX = refRange.aStart.Col();
5421 SCROW nNewY = refRange.aStart.Row();
5422 SCTAB nTab = refRange.aStart.Tab();
5424 ScDocument& rDoc = getScDocument();
5425 rDoc.GetNextPos( nNewX,nNewY, nTab, nMove,0, true,true, markedRange );
5426 refRange.aStart.SetCol( nNewX );
5427 refRange.aStart.SetRow( nNewY );
5428 refRange.aStart.SetTab( nTab );
5429 refRange.aEnd.SetCol( nNewX );
5430 refRange.aEnd.SetRow( nNewY );
5431 refRange.aEnd.SetTab( nTab );
5433 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell() , refRange ) );
5435 return new ScVbaRange( mxParent, mxContext, xRange );
5438 uno::Reference< excel::XRange > SAL_CALL
5439 ScVbaRange::Next()
5441 if ( m_Areas->getCount() > 1 )
5443 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ) , uno::UNO_QUERY_THROW );
5444 return xRange->Next();
5446 return PreviousNext( false );
5449 uno::Reference< excel::XRange > SAL_CALL
5450 ScVbaRange::Previous()
5452 if ( m_Areas->getCount() > 1 )
5454 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW );
5455 return xRange->Previous();
5457 return PreviousNext( true );
5460 uno::Reference< excel::XRange > SAL_CALL
5461 ScVbaRange::SpecialCells( const uno::Any& _oType, const uno::Any& _oValue)
5463 bool bIsSingleCell = isSingleCellRange();
5464 bool bIsMultiArea = ( m_Areas->getCount() > 1 );
5465 ScVbaRange* pRangeToUse = this;
5466 uno::Reference< excel::XRange > xUsedRange( getWorksheet()->getUsedRange() );
5467 sal_Int32 nType = 0;
5468 if ( !( _oType >>= nType ) )
5469 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5470 switch(nType)
5472 case excel::XlCellType::xlCellTypeSameFormatConditions:
5473 case excel::XlCellType::xlCellTypeAllValidation:
5474 case excel::XlCellType::xlCellTypeSameValidation:
5475 DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
5476 break;
5477 case excel::XlCellType::xlCellTypeBlanks:
5478 case excel::XlCellType::xlCellTypeComments:
5479 case excel::XlCellType::xlCellTypeConstants:
5480 case excel::XlCellType::xlCellTypeFormulas:
5481 case excel::XlCellType::xlCellTypeVisible:
5482 case excel::XlCellType::xlCellTypeLastCell:
5484 if ( bIsMultiArea )
5486 // need to process each area, gather the results and
5487 // create a new range from those
5488 std::vector< table::CellRangeAddress > rangeResults;
5489 sal_Int32 nItems = m_Areas->getCount() + 1;
5490 for ( sal_Int32 index=1; index <= nItems; ++index )
5492 uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
5493 xRange = xRange->SpecialCells( _oType, _oValue);
5494 ScVbaRange* pRange = getImplementation( xRange );
5495 if ( xRange.is() && pRange )
5497 sal_Int32 nElems = pRange->m_Areas->getCount() + 1;
5498 for ( sal_Int32 nArea = 1; nArea < nElems; ++nArea )
5500 uno::Reference< excel::XRange > xTmpRange( m_Areas->Item( uno::Any( nArea ), uno::Any() ), uno::UNO_QUERY_THROW );
5501 RangeHelper rHelper( xTmpRange->getCellRange() );
5502 rangeResults.push_back( rHelper.getCellRangeAddressable()->getRangeAddress() );
5506 ScRangeList aCellRanges;
5507 for ( const auto& rRangeResult : rangeResults )
5509 ScRange refRange;
5510 ScUnoConversion::FillScRange( refRange, rRangeResult );
5511 aCellRanges.push_back( refRange );
5513 // Single range
5514 if ( aCellRanges.size() == 1 )
5516 uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell(), aCellRanges.front() ) );
5517 return new ScVbaRange( mxParent, mxContext, xRange );
5519 uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( getScDocShell(), aCellRanges ) );
5521 return new ScVbaRange( mxParent, mxContext, xRanges );
5523 else if ( bIsSingleCell )
5525 pRangeToUse = static_cast< ScVbaRange* >( xUsedRange.get() );
5528 break;
5530 default:
5531 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5532 break;
5534 if ( !pRangeToUse )
5535 DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
5536 return pRangeToUse->SpecialCellsImpl( nType, _oValue );
5539 static sal_Int32 getContentResultFlags(const uno::Any& aValue)
5541 if (sal_Int32 aType; aValue >>= aType)
5543 switch (aType)
5545 case excel::XlSpecialCellsValue::xlNumbers:
5546 return sheet::CellFlags::VALUE | sheet::CellFlags::DATETIME;
5547 case excel::XlSpecialCellsValue::xlTextValues:
5548 return sheet::CellFlags::STRING;
5549 case excel::XlSpecialCellsValue::xlLogical:
5550 return sheet::CellFlags::VALUE | sheet::CellFlags::DATETIME;
5551 case excel::XlSpecialCellsValue::xlErrors:
5552 return 0;
5553 default:
5554 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {});
5557 return sheet::CellFlags::VALUE | sheet::CellFlags::STRING | sheet::CellFlags::DATETIME;
5560 /// @throws script::BasicErrorException
5561 static sal_Int32 lcl_getFormulaResultFlags(const uno::Any& aType)
5563 sal_Int32 nType = excel::XlSpecialCellsValue::xlNumbers;
5564 aType >>= nType;
5565 sal_Int32 nRes = sheet::FormulaResult::VALUE;
5567 switch(nType)
5569 case excel::XlSpecialCellsValue::xlErrors:
5570 nRes= sheet::FormulaResult::ERROR;
5571 break;
5572 case excel::XlSpecialCellsValue::xlLogical:
5573 //TODO bc93774: ask NN if this is really an appropriate substitute
5574 nRes = sheet::FormulaResult::VALUE;
5575 break;
5576 case excel::XlSpecialCellsValue::xlNumbers:
5577 nRes = sheet::FormulaResult::VALUE;
5578 break;
5579 case excel::XlSpecialCellsValue::xlTextValues:
5580 nRes = sheet::FormulaResult::STRING;
5581 break;
5582 default:
5583 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5585 return nRes;
5588 uno::Reference< excel::XRange >
5589 ScVbaRange::SpecialCellsImpl( sal_Int32 nType, const uno::Any& _oValue)
5591 uno::Reference< excel::XRange > xRange;
5594 uno::Reference< sheet::XCellRangesQuery > xQuery( mxRange, uno::UNO_QUERY_THROW );
5595 uno::Reference< sheet::XSheetCellRanges > xLocSheetCellRanges;
5596 switch(nType)
5598 case excel::XlCellType::xlCellTypeAllFormatConditions:
5599 case excel::XlCellType::xlCellTypeSameFormatConditions:
5600 case excel::XlCellType::xlCellTypeAllValidation:
5601 case excel::XlCellType::xlCellTypeSameValidation:
5602 // Shouldn't get here ( should be filtered out by
5603 // ScVbaRange::SpecialCells()
5604 DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
5605 break;
5606 case excel::XlCellType::xlCellTypeBlanks:
5607 xLocSheetCellRanges = xQuery->queryEmptyCells();
5608 break;
5609 case excel::XlCellType::xlCellTypeComments:
5610 xLocSheetCellRanges = xQuery->queryContentCells(sheet::CellFlags::ANNOTATION);
5611 break;
5612 case excel::XlCellType::xlCellTypeConstants:
5613 xLocSheetCellRanges = xQuery->queryContentCells(getContentResultFlags(_oValue));
5614 break;
5615 case excel::XlCellType::xlCellTypeFormulas:
5617 sal_Int32 nFormulaResult = lcl_getFormulaResultFlags(_oValue);
5618 xLocSheetCellRanges = xQuery->queryFormulaCells(nFormulaResult);
5619 break;
5621 case excel::XlCellType::xlCellTypeLastCell:
5622 xRange = Cells( uno::Any( getCount() ), uno::Any() );
5623 [[fallthrough]]; //TODO ???
5624 case excel::XlCellType::xlCellTypeVisible:
5625 xLocSheetCellRanges = xQuery->queryVisibleCells();
5626 break;
5627 default:
5628 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
5629 break;
5631 if (xLocSheetCellRanges.is())
5633 xRange = lcl_makeXRangeFromSheetCellRanges( getParent(), mxContext, xLocSheetCellRanges, getScDocShell() );
5636 catch (uno::Exception& )
5638 DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, u"No cells were found");
5640 return xRange;
5643 void SAL_CALL
5644 ScVbaRange::RemoveSubtotal( )
5646 uno::Reference< sheet::XSubTotalCalculatable > xSub( mxRange, uno::UNO_QUERY_THROW );
5647 xSub->removeSubTotals();
5650 void SAL_CALL
5651 ScVbaRange::Subtotal( ::sal_Int32 _nGroupBy, ::sal_Int32 _nFunction, const uno::Sequence< ::sal_Int32 >& _nTotalList, const uno::Any& aReplace, const uno::Any& PageBreaks, const uno::Any& /*SummaryBelowData*/ )
5655 bool bDoReplace = false;
5656 aReplace >>= bDoReplace;
5657 bool bAddPageBreaks = false;
5658 PageBreaks >>= bAddPageBreaks;
5660 uno::Reference< sheet::XSubTotalCalculatable> xSub(mxRange, uno::UNO_QUERY_THROW );
5661 uno::Reference< sheet::XSubTotalDescriptor > xSubDesc = xSub->createSubTotalDescriptor(true);
5662 uno::Reference< beans::XPropertySet > xSubDescPropertySet( xSubDesc, uno::UNO_QUERY_THROW );
5663 xSubDescPropertySet->setPropertyValue(u"InsertPageBreaks"_ustr, uno::Any( bAddPageBreaks));
5664 sal_Int32 nLen = _nTotalList.getLength();
5665 uno::Sequence< sheet::SubTotalColumn > aColumns( nLen );
5666 auto aColumnsRange = asNonConstRange(aColumns);
5667 for (int i = 0; i < nLen; i++)
5669 aColumnsRange[i].Column = _nTotalList[i] - 1;
5670 switch (_nFunction)
5672 case excel::XlConsolidationFunction::xlAverage:
5673 aColumnsRange[i].Function = sheet::GeneralFunction_AVERAGE;
5674 break;
5675 case excel::XlConsolidationFunction::xlCount:
5676 aColumnsRange[i].Function = sheet::GeneralFunction_COUNT;
5677 break;
5678 case excel::XlConsolidationFunction::xlCountNums:
5679 aColumnsRange[i].Function = sheet::GeneralFunction_COUNTNUMS;
5680 break;
5681 case excel::XlConsolidationFunction::xlMax:
5682 aColumnsRange[i].Function = sheet::GeneralFunction_MAX;
5683 break;
5684 case excel::XlConsolidationFunction::xlMin:
5685 aColumnsRange[i].Function = sheet::GeneralFunction_MIN;
5686 break;
5687 case excel::XlConsolidationFunction::xlProduct:
5688 aColumnsRange[i].Function = sheet::GeneralFunction_PRODUCT;
5689 break;
5690 case excel::XlConsolidationFunction::xlStDev:
5691 aColumnsRange[i].Function = sheet::GeneralFunction_STDEV;
5692 break;
5693 case excel::XlConsolidationFunction::xlStDevP:
5694 aColumnsRange[i].Function = sheet::GeneralFunction_STDEVP;
5695 break;
5696 case excel::XlConsolidationFunction::xlSum:
5697 aColumnsRange[i].Function = sheet::GeneralFunction_SUM;
5698 break;
5699 case excel::XlConsolidationFunction::xlUnknown:
5700 aColumnsRange[i].Function = sheet::GeneralFunction_NONE;
5701 break;
5702 case excel::XlConsolidationFunction::xlVar:
5703 aColumnsRange[i].Function = sheet::GeneralFunction_VAR;
5704 break;
5705 case excel::XlConsolidationFunction::xlVarP:
5706 aColumnsRange[i].Function = sheet::GeneralFunction_VARP;
5707 break;
5708 default:
5709 DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {}) ;
5710 return;
5713 xSubDesc->addNew(aColumns, _nGroupBy - 1);
5714 xSub->applySubTotals(xSubDesc, bDoReplace);
5716 catch (const uno::Exception&)
5718 DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
5722 void SAL_CALL
5723 ScVbaRange::ExportAsFixedFormat(const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
5724 const css::uno::Any& IncludeDocProperties, const css::uno::Any& /*IgnorePrintAreas*/, const css::uno::Any& From,
5725 const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& /*FixedFormatExtClassPtr*/)
5727 ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
5728 if (!pUnoRangesBase)
5729 throw uno::RuntimeException(u"Failed to access underlying uno range object"_ustr);
5730 ScDocShell* pShell = pUnoRangesBase->GetDocShell();
5731 if (!pShell)
5732 return;
5734 uno::Reference< frame::XModel > xModel(pShell->GetModel(), uno::UNO_SET_THROW);
5735 uno::Reference< excel::XApplication > xApplication(Application(), uno::UNO_QUERY_THROW);
5737 excel::ExportAsFixedFormatHelper(xModel, xApplication, Type, FileName, Quality,
5738 IncludeDocProperties, From, To, OpenAfterPublish);
5741 OUString
5742 ScVbaRange::getServiceImplName()
5744 return u"ScVbaRange"_ustr;
5747 uno::Sequence< OUString >
5748 ScVbaRange::getServiceNames()
5750 return { u"ooo.vba.excel.Range"_ustr };
5753 sal_Bool SAL_CALL
5754 ScVbaRange::hasError()
5756 double dResult = 0.0;
5757 uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
5758 uno::Reference< script::XInvocation > xInvoc( xApplication->WorksheetFunction(), uno::UNO_QUERY_THROW );
5760 uno::Reference< excel::XRange > aRange( this );
5761 uno::Sequence< uno::Any > Params{ uno::Any(aRange) };
5762 uno::Sequence< sal_Int16 > OutParamIndex;
5763 uno::Sequence< uno::Any > OutParam;
5764 xInvoc->invoke( u"IsError"_ustr, Params, OutParamIndex, OutParam ) >>= dResult;
5765 return dResult > 0.0;
5769 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
5770 Calc_ScVbaRange_get_implementation(
5771 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
5773 return cppu::acquire(new ScVbaRange(args, context));
5777 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */