xref: /core/sc/source/filter/oox/worksheethelper.cxx (revision 75036ee9)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <worksheethelper.hxx>
22 
23 #include <algorithm>
24 #include <utility>
25 #include <com/sun/star/awt/Point.hpp>
26 #include <com/sun/star/awt/Size.hpp>
27 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
28 #include <com/sun/star/sheet/ConditionOperator2.hpp>
29 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
30 #include <com/sun/star/sheet/ValidationType.hpp>
31 #include <com/sun/star/sheet/ValidationAlertStyle.hpp>
32 #include <com/sun/star/sheet/XCellAddressable.hpp>
33 #include <com/sun/star/sheet/XMultiFormulaTokens.hpp>
34 #include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
35 #include <com/sun/star/sheet/XSheetCondition2.hpp>
36 #include <com/sun/star/sheet/XSheetOutline.hpp>
37 #include <com/sun/star/sheet/XSpreadsheet.hpp>
38 #include <com/sun/star/table/XColumnRowRange.hpp>
39 #include <com/sun/star/text/WritingMode2.hpp>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <osl/diagnose.h>
42 #include <rtl/ustrbuf.hxx>
43 #include <oox/core/filterbase.hxx>
44 #include <oox/helper/propertyset.hxx>
45 #include <oox/token/properties.hxx>
46 #include <oox/token/tokens.hxx>
47 #include <addressconverter.hxx>
48 #include <autofilterbuffer.hxx>
49 #include <commentsbuffer.hxx>
50 #include <condformatbuffer.hxx>
51 #include <document.hxx>
52 #include <drawingfragment.hxx>
53 #include <pagesettings.hxx>
54 #include <querytablebuffer.hxx>
55 #include <sheetdatabuffer.hxx>
56 #include <stylesbuffer.hxx>
57 #include <tokenuno.hxx>
58 #include <unitconverter.hxx>
59 #include <viewsettings.hxx>
60 #include <worksheetbuffer.hxx>
61 #include <worksheetsettings.hxx>
62 #include <formulabuffer.hxx>
63 #include <scitems.hxx>
64 #include <editutil.hxx>
65 #include <tokenarray.hxx>
66 #include <tablebuffer.hxx>
67 #include <documentimport.hxx>
68 #include <stlsheet.hxx>
69 #include <stlpool.hxx>
70 #include <cellvalue.hxx>
71 #include <columnspanset.hxx>
72 #include <dbdata.hxx>
73 
74 #include <svl/stritem.hxx>
75 #include <editeng/eeitem.hxx>
76 #include <editeng/editobj.hxx>
77 #include <editeng/flditem.hxx>
78 #include <tools/UnitConversion.hxx>
79 
80 namespace oox::xls {
81 
82 using namespace ::com::sun::star;
83 using namespace ::com::sun::star::beans;
84 using namespace ::com::sun::star::drawing;
85 using namespace ::com::sun::star::lang;
86 using namespace ::com::sun::star::sheet;
87 using namespace ::com::sun::star::table;
88 using namespace ::com::sun::star::text;
89 using namespace ::com::sun::star::uno;
90 
91 namespace {
92 
93 void lclUpdateProgressBar( const ISegmentProgressBarRef& rxProgressBar, double fPosition )
94 {
95     if( rxProgressBar )
96         rxProgressBar->setPosition( fPosition );
97 }
98 
99 } // namespace
100 
101 ColumnModel::ColumnModel() :
102     maRange( -1 ),
103     mfWidth( 0.0 ),
104     mnXfId( -1 ),
105     mnLevel( 0 ),
106     mbShowPhonetic( false ),
107     mbHidden( false ),
108     mbCollapsed( false )
109 {
110 }
111 
112 bool ColumnModel::isMergeable( const ColumnModel& rModel ) const
113 {
114     return
115         (maRange.mnFirst        <= rModel.maRange.mnFirst) &&
116         (rModel.maRange.mnFirst <= maRange.mnLast + 1) &&
117         (mfWidth                == rModel.mfWidth) &&
118         // ignore mnXfId, cell formatting is always set directly
119         (mnLevel                == rModel.mnLevel) &&
120         (mbHidden               == rModel.mbHidden) &&
121         (mbCollapsed            == rModel.mbCollapsed);
122 }
123 
124 RowModel::RowModel() :
125     mnRow( -1 ),
126     mfHeight( 0.0 ),
127     mnXfId( -1 ),
128     mnLevel( 0 ),
129     mbCustomHeight( false ),
130     mbCustomFormat( false ),
131     mbShowPhonetic( false ),
132     mbHidden( false ),
133     mbCollapsed( false ),
134     mbThickTop( false ),
135     mbThickBottom( false )
136 {
137 }
138 
139 void RowModel::insertColSpan( const ValueRange& rColSpan )
140 {
141     if( (0 <= rColSpan.mnFirst) && (rColSpan.mnFirst <= rColSpan.mnLast) )
142         maColSpans.insert( rColSpan );
143 }
144 
145 bool RowModel::isMergeable( const RowModel& rModel ) const
146 {
147     return
148         // ignore maColSpans - is handled separately in SheetDataBuffer class
149         (mfHeight       == rModel.mfHeight) &&
150         // ignore mnXfId, mbCustomFormat, mbShowPhonetic - cell formatting is always set directly
151         (mnLevel        == rModel.mnLevel) &&
152         (mbCustomHeight == rModel.mbCustomHeight) &&
153         (mbHidden       == rModel.mbHidden) &&
154         (mbCollapsed    == rModel.mbCollapsed);
155 }
156 
157 PageBreakModel::PageBreakModel()
158     : mnColRow(0)
159     , mnMin(0)
160     , mnMax(0)
161     , mbManual(false)
162 {
163 }
164 
165 HyperlinkModel::HyperlinkModel()
166 {
167 }
168 
169 ValidationModel::ValidationModel() :
170     mnType( XML_none ),
171     mnOperator( XML_between ),
172     mnErrorStyle( XML_stop ),
173     mbShowInputMsg( false ),
174     mbShowErrorMsg( false ),
175     mbNoDropDown( false ),
176     mbAllowBlank( false )
177 {
178 }
179 
180 void ValidationModel::setBiffType( sal_uInt8 nType )
181 {
182     static const sal_Int32 spnTypeIds[] = {
183         XML_none, XML_whole, XML_decimal, XML_list, XML_date, XML_time, XML_textLength, XML_custom };
184     mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_none );
185 }
186 
187 void ValidationModel::setBiffOperator( sal_uInt8 nOperator )
188 {
189     static const sal_Int32 spnOperators[] = {
190         XML_between, XML_notBetween, XML_equal, XML_notEqual,
191         XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual };
192     mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
193 }
194 
195 void ValidationModel::setBiffErrorStyle( sal_uInt8 nErrorStyle )
196 {
197     static const sal_Int32 spnErrorStyles[] = { XML_stop, XML_warning, XML_information };
198     mnErrorStyle = STATIC_ARRAY_SELECT( spnErrorStyles, nErrorStyle, XML_stop );
199 }
200 
201 class WorksheetGlobals : public WorkbookHelper, public IWorksheetProgress
202 {
203 public:
204     explicit            WorksheetGlobals(
205                             const WorkbookHelper& rHelper,
206                             const ISegmentProgressBarRef& rxProgressBar,
207                             WorksheetType eSheetType,
208                             SCTAB nSheet );
209 
210     /** Returns true, if this helper refers to an existing Calc sheet. */
211     bool         isValidSheet() const { return mxSheet.is(); }
212 
213     /** Returns the type of this sheet. */
214     WorksheetType getSheetType() const { return meSheetType; }
215     /** Returns the index of the current sheet. */
216     SCTAB        getSheetIndex() const { return maUsedArea.aStart.Tab(); }
217     /** Returns the XSpreadsheet interface of the current sheet. */
218     const Reference< XSpreadsheet >& getSheet() const { return mxSheet; }
219 
220     /** Returns the XCell interface for the passed cell address. */
221     Reference< XCell >  getCell( const ScAddress& rAddress ) const;
222     /** Returns the XCellRange interface for the passed cell range address. */
223     Reference< XCellRange > getCellRange( const ScRange& rRange ) const;
224     /** Returns the XSheetCellRanges interface for the passed cell range addresses. */
225     Reference< XSheetCellRanges > getCellRangeList( const ScRangeList& rRanges ) const;
226 
227     /** Returns the XCellRange interface for a column. */
228     Reference< XCellRange > getColumn( sal_Int32 nCol ) const;
229     /** Returns the XCellRange interface for a row. */
230     Reference< XCellRange > getRow( sal_Int32 nRow ) const;
231 
232     /** Returns the XDrawPage interface of the draw page of the current sheet. */
233     Reference< XDrawPage > getDrawPage() const;
234     /** Returns the size of the entire drawing page in 1/100 mm. */
235     const awt::Size&         getDrawPageSize() const;
236 
237     /** Returns the absolute position of the top-left corner of the cell in 1/100 mm. */
238     awt::Point               getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const;
239 
240     /** Returns the address of the cell that contains the passed point in 1/100 mm. */
241     ScAddress                getCellAddressFromPosition( const awt::Point& rPosition ) const;
242     /** Returns the cell range address that contains the passed rectangle in 1/100 mm. */
243     ScRange                  getCellRangeFromRectangle( const awt::Rectangle& rRect ) const;
244 
245     /** Returns the buffer for cell contents and cell formatting. */
246     SheetDataBuffer& getSheetData() { return maSheetData; }
247     /** Returns the conditional formatting in this sheet. */
248     CondFormatBuffer& getCondFormats() { return maCondFormats; }
249     /** Returns the buffer for all cell comments in this sheet. */
250     CommentsBuffer& getComments() { return maComments; }
251     /** Returns the auto filters for the sheet. */
252     AutoFilterBuffer& getAutoFilters() { return maAutoFilters; }
253     /** Returns the buffer for all web query tables in this sheet. */
254     QueryTableBuffer& getQueryTables() { return maQueryTables; }
255     /** Returns the worksheet settings object. */
256     WorksheetSettings& getWorksheetSettings() { return maSheetSett; }
257     /** Returns the page/print settings for this sheet. */
258     PageSettings& getPageSettings() { return maPageSett; }
259     /** Returns the view settings for this sheet. */
260     SheetViewSettings& getSheetViewSettings() { return maSheetViewSett; }
261     /** Returns the VML drawing page for this sheet (OOXML/BIFF12 only). */
262     VmlDrawing&  getVmlDrawing() { return *mxVmlDrawing; }
263     /** returns the ExtLst entries that need to be filled */
264     ExtLst&      getExtLst() { return maExtLst; }
265 
266     /** Sets a column or row page break described in the passed struct. */
267     void                setPageBreak( const PageBreakModel& rModel, bool bRowBreak );
268     /** Inserts the hyperlink URL into the spreadsheet. */
269     void                setHyperlink( const HyperlinkModel& rModel );
270     /** Inserts the data validation settings into the spreadsheet. */
271     void                setValidation( const ValidationModel& rModel );
272     /** Sets the path to the DrawingML fragment of this sheet. */
273     void                setDrawingPath( const OUString& rDrawingPath );
274     /** Sets the path to the legacy VML drawing fragment of this sheet. */
275     void                setVmlDrawingPath( const OUString& rVmlDrawingPath );
276 
277     /** Extends the used area of this sheet by the passed cell position. */
278     void                extendUsedArea( const ScAddress& rAddress );
279 
280     /** Extends the used area of this sheet by the passed cell range. */
281     void                extendUsedArea( const ScRange& rRange );
282     /** Extends the shape bounding box by the position and size of the passed rectangle. */
283     void                extendShapeBoundingBox( const awt::Rectangle& rShapeRect );
284 
285     /** Sets base width for all columns (without padding pixels). This value
286         is only used, if base width has not been set with setDefaultColumnWidth(). */
287     void                setBaseColumnWidth( sal_Int32 nWidth );
288     /** Sets default width for all columns. This function overrides the base
289         width set with the setBaseColumnWidth() function. */
290     void                setDefaultColumnWidth( double fWidth );
291     /** Sets column settings for a specific column range.
292         @descr  Column default formatting is converted directly, other settings
293         are cached and converted in the finalizeImport() call. */
294     void                setColumnModel( const ColumnModel& rModel );
295     /** Converts column default cell formatting. */
296     void convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId );
297 
298     /** Sets default height and hidden state for all unused rows in the sheet. */
299     void                setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom );
300     /** Sets row settings for a specific row.
301         @descr  Row default formatting is converted directly, other settings
302         are cached and converted in the finalizeImport() call. */
303     void                setRowModel( const RowModel& rModel );
304 
305     /** Initial conversion before importing the worksheet. */
306     void                initializeWorksheetImport();
307     /** Final conversion after importing the worksheet. */
308     void                finalizeWorksheetImport();
309 
310     void finalizeDrawingImport();
311 
312     /// Allow the threaded importer to override our progress bar impl.
313     virtual ISegmentProgressBarRef getRowProgress() override
314     {
315         return mxRowProgress;
316     }
317     virtual void setCustomRowProgress( const ISegmentProgressBarRef &rxRowProgress ) override
318     {
319         mxRowProgress = rxRowProgress;
320         mbFastRowProgress = true;
321     }
322 
323 private:
324     typedef ::std::vector< sal_Int32 >                  OutlineLevelVec;
325     typedef ::std::pair< ColumnModel, sal_Int32 >       ColumnModelRange;
326     typedef ::std::map< sal_Int32, ColumnModelRange >   ColumnModelRangeMap;
327     typedef ::std::pair< RowModel, sal_Int32 >          RowModelRange;
328     typedef ::std::map< sal_Int32, RowModelRange >      RowModelRangeMap;
329 
330     /** Inserts all imported hyperlinks into their cell ranges. */
331     void finalizeHyperlinkRanges();
332     /** Generates the final URL for the passed hyperlink. */
333     OUString            getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const;
334     /** Inserts a hyperlinks into the specified cell. */
335     void insertHyperlink( const ScAddress& rAddress, const OUString& rUrl );
336 
337     /** Inserts all imported data validations into their cell ranges. */
338     void                finalizeValidationRanges() const;
339 
340     /** Converts column properties for all columns in the sheet. */
341     void                convertColumns();
342     /** Converts column properties. */
343     void                convertColumns( OutlineLevelVec& orColLevels, const ValueRange& rColRange, const ColumnModel& rModel );
344 
345     /** Converts row properties for all rows in the sheet. */
346     void                convertRows(const std::vector<sc::ColRowSpan>& rSpans);
347     /** Converts row properties. */
348     void                convertRows(OutlineLevelVec& orRowLevels, const ValueRange& rRowRange,
349                                     const RowModel& rModel,
350                                     const std::vector<sc::ColRowSpan>& rSpans,
351                                     double fDefHeight = -1.0);
352 
353     /** Converts outline grouping for the passed column or row. */
354     void                convertOutlines( OutlineLevelVec& orLevels, sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows );
355     /** Groups columns or rows for the given range. */
356     void                groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapsed, bool bRows );
357 
358     /** Imports the drawings of the sheet (DML, VML, DFF) and updates the used area. */
359     void                finalizeDrawings();
360 
361     /** Update the row import progress bar */
362     void UpdateRowProgress( const ScRange& rUsedArea, SCROW nRow );
363 
364 private:
365     typedef ::std::unique_ptr< VmlDrawing >       VmlDrawingPtr;
366 
367     const ScAddress&    mrMaxApiPos;        /// Reference to maximum Calc cell address from address converter.
368     ScRange             maUsedArea;         /// Used area of the sheet, and sheet index of the sheet.
369     ColumnModel         maDefColModel;      /// Default column formatting.
370     ColumnModelRangeMap maColModels;        /// Ranges of columns sorted by first column index.
371     RowModel            maDefRowModel;      /// Default row formatting.
372     RowModelRangeMap    maRowModels;        /// Ranges of rows sorted by first row index.
373     std::vector< HyperlinkModel > maHyperlinks;       /// Cell ranges containing hyperlinks.
374     std::vector< ValidationModel > maValidations;      /// Cell ranges containing data validation settings.
375     SheetDataBuffer     maSheetData;        /// Buffer for cell contents and cell formatting.
376     CondFormatBuffer    maCondFormats;      /// Buffer for conditional formatting.
377     CommentsBuffer      maComments;         /// Buffer for all cell comments in this sheet.
378     AutoFilterBuffer    maAutoFilters;      /// Sheet auto filters (not associated to a table).
379     QueryTableBuffer    maQueryTables;      /// Buffer for all web query tables in this sheet.
380     WorksheetSettings   maSheetSett;        /// Global settings for this sheet.
381     PageSettings        maPageSett;         /// Page/print settings for this sheet.
382     SheetViewSettings   maSheetViewSett;    /// View settings for this sheet.
383     VmlDrawingPtr       mxVmlDrawing;       /// Collection of all VML shapes.
384     ExtLst              maExtLst;           /// List of extended elements
385     OUString            maDrawingPath;      /// Path to DrawingML fragment.
386     OUString            maVmlDrawingPath;   /// Path to legacy VML drawing fragment.
387     awt::Size                maDrawPageSize;     /// Current size of the drawing page in 1/100 mm.
388     awt::Rectangle           maShapeBoundingBox; /// Bounding box for all shapes from all drawings.
389     ISegmentProgressBarRef mxProgressBar;   /// Sheet progress bar.
390     bool                   mbFastRowProgress; /// Do we have a progress bar thread ?
391     ISegmentProgressBarRef mxRowProgress;   /// Progress bar for row/cell processing.
392     ISegmentProgressBarRef mxFinalProgress; /// Progress bar for finalization.
393     WorksheetType       meSheetType;        /// Type of this sheet.
394     Reference< XSpreadsheet > mxSheet;      /// Reference to the current sheet.
395     bool                mbHasDefWidth;      /// True = default column width is set from defaultColWidth attribute.
396 };
397 
398 constexpr OUStringLiteral gaSheetCellRanges( u"com.sun.star.sheet.SheetCellRanges" ); /// Service name for a SheetCellRanges object.
399 
400 WorksheetGlobals::WorksheetGlobals( const WorkbookHelper& rHelper, const ISegmentProgressBarRef& rxProgressBar, WorksheetType eSheetType, SCTAB nSheet ) :
401     WorkbookHelper( rHelper ),
402     mrMaxApiPos( rHelper.getAddressConverter().getMaxApiAddress() ),
403     maUsedArea( SCCOL_MAX, SCROW_MAX, nSheet, -1, -1, nSheet ), // Set start address to largest possible value, and end address to smallest
404     maSheetData( *this ),
405     maCondFormats( *this ),
406     maComments( *this ),
407     maAutoFilters( *this ),
408     maQueryTables( *this ),
409     maSheetSett( *this ),
410     maPageSett( *this ),
411     maSheetViewSett( *this ),
412     mxProgressBar( rxProgressBar ),
413     mbFastRowProgress( false ),
414     meSheetType( eSheetType ),
415     mxSheet(getSheetFromDoc( nSheet )),
416     mbHasDefWidth( false )
417 {
418     if( !mxSheet.is() )
419         maUsedArea.aStart.SetTab( -1 );
420 
421     // default column settings (width and hidden state may be updated later)
422     maDefColModel.mfWidth = 8.5;
423     maDefColModel.mnXfId = -1;
424     maDefColModel.mnLevel = 0;
425     maDefColModel.mbHidden = false;
426     maDefColModel.mbCollapsed = false;
427 
428     // default row settings (height and hidden state may be updated later)
429     maDefRowModel.mfHeight = 0.0;
430     maDefRowModel.mnXfId = -1;
431     maDefRowModel.mnLevel = 0;
432     maDefRowModel.mbCustomHeight = false;
433     maDefRowModel.mbCustomFormat = false;
434     maDefRowModel.mbShowPhonetic = false;
435     maDefRowModel.mbHidden = false;
436     maDefRowModel.mbCollapsed = false;
437 
438     // buffers
439     mxVmlDrawing.reset( new VmlDrawing( *this ) );
440 
441     // prepare progress bars
442     if( mxProgressBar )
443     {
444         mxRowProgress = mxProgressBar->createSegment( 0.5 );
445         mxFinalProgress = mxProgressBar->createSegment( 0.5 );
446     }
447 }
448 
449 Reference< XCell > WorksheetGlobals::getCell( const ScAddress& rAddress ) const
450 {
451     Reference< XCell > xCell;
452     if( mxSheet.is() ) try
453     {
454         xCell = mxSheet->getCellByPosition( rAddress.Col(), rAddress.Row() );
455     }
456     catch( Exception& )
457     {
458     }
459     return xCell;
460 }
461 
462 Reference< XCellRange > WorksheetGlobals::getCellRange( const ScRange& rRange ) const
463 {
464     Reference< XCellRange > xRange;
465     if( mxSheet.is() ) try
466     {
467         xRange = mxSheet->getCellRangeByPosition( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
468     }
469     catch( Exception& )
470     {
471     }
472     return xRange;
473 }
474 
475 Reference< XSheetCellRanges > WorksheetGlobals::getCellRangeList( const ScRangeList& rRanges ) const
476 {
477     Reference< XSheetCellRanges > xRanges;
478     if( mxSheet.is() && !rRanges.empty() ) try
479     {
480         xRanges.set( getBaseFilter().getModelFactory()->createInstance( gaSheetCellRanges ), UNO_QUERY_THROW );
481         Reference< XSheetCellRangeContainer > xRangeCont( xRanges, UNO_QUERY_THROW );
482         xRangeCont->addRangeAddresses( AddressConverter::toApiSequence(rRanges), false );
483     }
484     catch( Exception& )
485     {
486     }
487     return xRanges;
488 }
489 
490 Reference< XCellRange > WorksheetGlobals::getColumn( sal_Int32 nCol ) const
491 {
492     Reference< XCellRange > xColumn;
493     try
494     {
495         Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
496         Reference< XTableColumns > xColumns( xColRowRange->getColumns(), UNO_SET_THROW );
497         xColumn.set( xColumns->getByIndex( nCol ), UNO_QUERY );
498     }
499     catch( Exception& )
500     {
501     }
502     return xColumn;
503 }
504 
505 Reference< XCellRange > WorksheetGlobals::getRow( sal_Int32 nRow ) const
506 {
507     Reference< XCellRange > xRow;
508     try
509     {
510         Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
511         Reference< XTableRows > xRows( xColRowRange->getRows(), UNO_SET_THROW );
512         xRow.set( xRows->getByIndex( nRow ), UNO_QUERY );
513     }
514     catch( Exception& )
515     {
516     }
517     return xRow;
518 }
519 
520 Reference< XDrawPage > WorksheetGlobals::getDrawPage() const
521 {
522     Reference< XDrawPage > xDrawPage;
523     try
524     {
525         xDrawPage = Reference< XDrawPageSupplier >( mxSheet, UNO_QUERY_THROW )->getDrawPage();
526     }
527     catch( Exception& )
528     {
529     }
530     return xDrawPage;
531 }
532 
533 const awt::Size& WorksheetGlobals::getDrawPageSize() const
534 {
535     OSL_ENSURE( (maDrawPageSize.Width > 0) && (maDrawPageSize.Height > 0), "WorksheetGlobals::getDrawPageSize - called too early, size invalid" );
536     return maDrawPageSize;
537 }
538 
539 awt::Point WorksheetGlobals::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
540 {
541     awt::Point aPoint;
542     PropertySet aCellProp( getCell( ScAddress( nCol, nRow, getSheetIndex() ) ) );
543     aCellProp.getProperty( aPoint, PROP_Position );
544     return aPoint;
545 }
546 
547 namespace {
548 
549 sal_Int32 lclGetMidAddr( sal_Int32 nBegAddr, sal_Int32 nEndAddr, sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
550 {
551     // use sal_Int64 to prevent integer overflow
552     return nBegAddr + 1 + static_cast< sal_Int32 >( static_cast< sal_Int64 >( nEndAddr - nBegAddr - 2 ) * (nSearchPos - nBegPos) / (nEndPos - nBegPos) );
553 }
554 
555 bool lclPrepareInterval( sal_Int32 nBegAddr, sal_Int32& rnMidAddr, sal_Int32 nEndAddr,
556         sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
557 {
558     // searched position before nBegPos -> use nBegAddr
559     if( nSearchPos <= nBegPos )
560     {
561         rnMidAddr = nBegAddr;
562         return false;
563     }
564 
565     // searched position after nEndPos, or begin next to end -> use nEndAddr
566     if( (nSearchPos >= nEndPos) || (nBegAddr + 1 >= nEndAddr) )
567     {
568         rnMidAddr = nEndAddr;
569         return false;
570     }
571 
572     /*  Otherwise find mid address according to position. lclGetMidAddr() will
573         return an address between nBegAddr and nEndAddr. */
574     rnMidAddr = lclGetMidAddr( nBegAddr, nEndAddr, nBegPos, nEndPos, nSearchPos );
575     return true;
576 }
577 
578 bool lclUpdateInterval( sal_Int32& rnBegAddr, sal_Int32& rnMidAddr, sal_Int32& rnEndAddr,
579         sal_Int32& rnBegPos, sal_Int32 nMidPos, sal_Int32& rnEndPos, sal_Int32 nSearchPos )
580 {
581     // nSearchPos < nMidPos: use the interval [begin,mid] in the next iteration
582     if( nSearchPos < nMidPos )
583     {
584         // if rnBegAddr is next to rnMidAddr, the latter is the column/row in question
585         if( rnBegAddr + 1 >= rnMidAddr )
586             return false;
587         // otherwise, set interval end to mid
588         rnEndPos = nMidPos;
589         rnEndAddr = rnMidAddr;
590         rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
591         return true;
592     }
593 
594     // nSearchPos > nMidPos: use the interval [mid,end] in the next iteration
595     if( nSearchPos > nMidPos )
596     {
597         // if rnMidAddr is next to rnEndAddr, the latter is the column/row in question
598         if( rnMidAddr + 1 >= rnEndAddr )
599         {
600             rnMidAddr = rnEndAddr;
601             return false;
602         }
603         // otherwise, set interval start to mid
604         rnBegPos = nMidPos;
605         rnBegAddr = rnMidAddr;
606         rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
607         return true;
608     }
609 
610     // nSearchPos == nMidPos: rnMidAddr is the column/row in question, do not loop anymore
611     return false;
612 }
613 
614 } // namespace
615 
616 ScAddress WorksheetGlobals::getCellAddressFromPosition( const awt::Point& rPosition ) const
617 {
618     // starting cell address and its position in drawing layer (top-left edge)
619     sal_Int32 nBegCol = 0;
620     sal_Int32 nBegRow = 0;
621     awt::Point aBegPos( 0, 0 );
622 
623     // end cell address and its position in drawing layer (bottom-right edge)
624     sal_Int32 nEndCol = mrMaxApiPos.Col() + 1;
625     sal_Int32 nEndRow = mrMaxApiPos.Row() + 1;
626     awt::Point aEndPos( maDrawPageSize.Width, maDrawPageSize.Height );
627 
628     // starting point for interval search
629     sal_Int32 nMidCol, nMidRow;
630     bool bLoopCols = lclPrepareInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aEndPos.X, rPosition.X );
631     bool bLoopRows = lclPrepareInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aEndPos.Y, rPosition.Y );
632     awt::Point aMidPos = getCellPosition( nMidCol, nMidRow );
633 
634     /*  The loop will find the column/row index of the cell right of/below
635         the cell containing the passed point, unless the point is located at
636         the top or left border of the containing cell. */
637     while( bLoopCols || bLoopRows )
638     {
639         bLoopCols = bLoopCols && lclUpdateInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aMidPos.X, aEndPos.X, rPosition.X );
640         bLoopRows = bLoopRows && lclUpdateInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aMidPos.Y, aEndPos.Y, rPosition.Y );
641         aMidPos = getCellPosition( nMidCol, nMidRow );
642     }
643 
644     /*  The cell left of/above the current search position contains the passed
645         point, unless the point is located on the top/left border of the cell,
646         or the last column/row of the sheet has been reached. */
647     if( aMidPos.X > rPosition.X ) --nMidCol;
648     if( aMidPos.Y > rPosition.Y ) --nMidRow;
649     return ScAddress( nMidCol, nMidRow, getSheetIndex() );
650 }
651 
652 ScRange WorksheetGlobals::getCellRangeFromRectangle( const awt::Rectangle& rRect ) const
653 {
654     ScAddress aStartAddr = getCellAddressFromPosition( awt::Point( rRect.X, rRect.Y ) );
655     awt::Point aBotRight( rRect.X + rRect.Width, rRect.Y + rRect.Height );
656     ScAddress aEndAddr = getCellAddressFromPosition( aBotRight );
657     bool bMultiCols = aStartAddr.Col() < aEndAddr.Col();
658     bool bMultiRows = aStartAddr.Row() < aEndAddr.Row();
659     if( bMultiCols || bMultiRows )
660     {
661         /*  Reduce end position of the cell range to previous column or row, if
662             the rectangle ends exactly between two columns or rows. */
663         awt::Point aEndPos = getCellPosition( aEndAddr.Col(), aEndAddr.Row() );
664         if( bMultiCols && (aBotRight.X <= aEndPos.X) )
665             aEndAddr.IncCol(-1);
666         if( bMultiRows && (aBotRight.Y <= aEndPos.Y) )
667             aEndAddr.IncRow(-1);
668     }
669     return ScRange( aStartAddr.Col(), aStartAddr.Row(), getSheetIndex(),
670                     aEndAddr.Col(), aEndAddr.Row(), getSheetIndex() );
671 }
672 
673 void WorksheetGlobals::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
674 {
675     if( rModel.mbManual && (rModel.mnColRow > 0) )
676     {
677         PropertySet aPropSet( bRowBreak ? getRow( rModel.mnColRow ) : getColumn( rModel.mnColRow ) );
678         aPropSet.setProperty( PROP_IsStartOfNewPage, true );
679     }
680 }
681 
682 void WorksheetGlobals::setHyperlink( const HyperlinkModel& rModel )
683 {
684     maHyperlinks.push_back( rModel );
685 }
686 
687 void WorksheetGlobals::setValidation( const ValidationModel& rModel )
688 {
689     maValidations.push_back( rModel );
690 }
691 
692 void WorksheetGlobals::setDrawingPath( const OUString& rDrawingPath )
693 {
694     maDrawingPath = rDrawingPath;
695 }
696 
697 void WorksheetGlobals::setVmlDrawingPath( const OUString& rVmlDrawingPath )
698 {
699     maVmlDrawingPath = rVmlDrawingPath;
700 }
701 
702 void WorksheetGlobals::extendUsedArea( const ScAddress& rAddress )
703 {
704     maUsedArea.aStart.SetCol( ::std::min( maUsedArea.aStart.Col(), rAddress.Col() ) );
705     maUsedArea.aStart.SetRow( ::std::min( maUsedArea.aStart.Row(), rAddress.Row() ) );
706     maUsedArea.aEnd.SetCol( ::std::max( maUsedArea.aEnd.Col(), rAddress.Col() ) );
707     maUsedArea.aEnd.SetRow( ::std::max( maUsedArea.aEnd.Row(), rAddress.Row() ) );
708 }
709 
710 void WorksheetGlobals::extendUsedArea( const ScRange& rRange )
711 {
712     extendUsedArea( rRange.aStart );
713     extendUsedArea( rRange.aEnd );
714 }
715 
716 void WorksheetHelper::extendUsedArea( const ScRange& rRange )
717 {
718     extendUsedArea( rRange.aStart );
719     extendUsedArea( rRange.aEnd );
720 }
721 
722 void WorksheetGlobals::extendShapeBoundingBox( const awt::Rectangle& rShapeRect )
723 {
724     if( (maShapeBoundingBox.Width == 0) && (maShapeBoundingBox.Height == 0) )
725     {
726         // width and height of maShapeBoundingBox are assumed to be zero on first cell
727         maShapeBoundingBox = rShapeRect;
728     }
729     else
730     {
731         sal_Int32 nEndX = ::std::max( maShapeBoundingBox.X + maShapeBoundingBox.Width, rShapeRect.X + rShapeRect.Width );
732         sal_Int32 nEndY = ::std::max( maShapeBoundingBox.Y + maShapeBoundingBox.Height, rShapeRect.Y + rShapeRect.Height );
733         maShapeBoundingBox.X = ::std::min( maShapeBoundingBox.X, rShapeRect.X );
734         maShapeBoundingBox.Y = ::std::min( maShapeBoundingBox.Y, rShapeRect.Y );
735         maShapeBoundingBox.Width = nEndX - maShapeBoundingBox.X;
736         maShapeBoundingBox.Height = nEndY - maShapeBoundingBox.Y;
737     }
738 }
739 
740 void WorksheetGlobals::setBaseColumnWidth( sal_Int32 nWidth )
741 {
742     // do not modify width, if setDefaultColumnWidth() has been used
743     if( !mbHasDefWidth && (nWidth > 0) )
744     {
745         // #i3006# add 5 pixels padding to the width
746         maDefColModel.mfWidth = nWidth + getUnitConverter().scaleValue( 5, Unit::ScreenX, Unit::Digit );
747     }
748 }
749 
750 void WorksheetGlobals::setDefaultColumnWidth( double fWidth )
751 {
752     // overrides a width set with setBaseColumnWidth()
753     if( fWidth > 0.0 )
754     {
755         maDefColModel.mfWidth = fWidth;
756         mbHasDefWidth = true;
757     }
758 }
759 
760 void WorksheetGlobals::setColumnModel( const ColumnModel& rModel )
761 {
762     // convert 1-based OOXML column indexes to 0-based API column indexes
763     sal_Int32 nFirstCol = rModel.maRange.mnFirst - 1;
764     sal_Int32 nLastCol = rModel.maRange.mnLast - 1;
765     if( !(getAddressConverter().checkCol( nFirstCol, true ) && (nFirstCol <= nLastCol)) )
766         return;
767 
768     // Validate last column index.
769     // If last column is equal to last possible column, Excel adds one
770     // more. We do that also in XclExpColinfo::SaveXml() and for 1024 end
771     // up with 1025 instead, which would lead to excess columns in
772     // checkCol(). Cater for this oddity.
773     if (nLastCol == mrMaxApiPos.Col() + 1)
774         --nLastCol;
775     // This is totally fouled up. If we saved 1025 and the file is saved
776     // with Excel again, it increments the value to 1026.
777     else if (nLastCol == mrMaxApiPos.Col() + 2)
778         nLastCol -= 2;
779     // Excel may add a column range for the remaining columns (with
780     // <cols><col .../></cols>), even if not used or only used to grey out
781     // columns in page break view. Don't let that trigger overflow warning,
782     // so check for the last possible column. If there really is content in
783     // the range that should be caught anyway.
784     else if (nLastCol == getAddressConverter().getMaxXlsAddress().Col())
785         nLastCol = mrMaxApiPos.Col();
786     // User may have applied custom column widths to arbitrary excess
787     // columns. Ignore those and don't track as overflow columns (false).
788     // Effectively this does the same as the above cases, just keep them
789     // for explanation.
790     // Actual data present should trigger the overflow detection later.
791     else if( !getAddressConverter().checkCol( nLastCol, false ) )
792         nLastCol = mrMaxApiPos.Col();
793     // try to find entry in column model map that is able to merge with the passed model
794     bool bInsertModel = true;
795     if( !maColModels.empty() )
796     {
797         // find first column model range following nFirstCol (nFirstCol < aIt->first), or end of map
798         ColumnModelRangeMap::iterator aIt = maColModels.upper_bound( nFirstCol );
799         OSL_ENSURE( aIt == maColModels.end(), "WorksheetGlobals::setColModel - columns are unsorted" );
800         // if inserting before another column model, get last free column
801         OSL_ENSURE( (aIt == maColModels.end()) || (nLastCol < aIt->first), "WorksheetGlobals::setColModel - multiple models of the same column" );
802         if( aIt != maColModels.end() )
803             nLastCol = ::std::min( nLastCol, aIt->first - 1 );
804         if( aIt != maColModels.begin() )
805         {
806             // go to previous map element (which may be able to merge with the passed model)
807             --aIt;
808             // the usage of upper_bound() above ensures that aIt->first is less than or equal to nFirstCol now
809             sal_Int32& rnLastMapCol = aIt->second.second;
810             OSL_ENSURE( rnLastMapCol < nFirstCol, "WorksheetGlobals::setColModel - multiple models of the same column" );
811             nFirstCol = ::std::max( rnLastMapCol + 1, nFirstCol );
812             if( (rnLastMapCol + 1 == nFirstCol) && (nFirstCol <= nLastCol) && aIt->second.first.isMergeable( rModel ) )
813             {
814                 // can merge with existing model, update last column index
815                 rnLastMapCol = nLastCol;
816                 bInsertModel = false;
817             }
818         }
819     }
820     if( nFirstCol <= nLastCol )
821     {
822         // insert the column model, if it has not been merged with another
823         if( bInsertModel )
824             maColModels[ nFirstCol ] = ColumnModelRange( rModel, nLastCol );
825         // set column formatting directly
826         convertColumnFormat( nFirstCol, nLastCol, rModel.mnXfId );
827     }
828 }
829 
830 void WorksheetGlobals::convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId )
831 {
832     ScRange aRange( nFirstCol, 0, getSheetIndex(), nLastCol, mrMaxApiPos.Row(), getSheetIndex() );
833     if( getAddressConverter().validateCellRange( aRange, true, false ) )
834     {
835         const StylesBuffer& rStyles = getStyles();
836 
837         // Set cell styles via direct API - the preferred approach.
838         ScDocumentImport& rDoc = getDocImport();
839         rStyles.writeCellXfToDoc(rDoc, aRange, nXfId);
840     }
841 }
842 
843 void WorksheetGlobals::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
844 {
845     maDefRowModel.mfHeight = fHeight;
846     maDefRowModel.mbCustomHeight = bCustomHeight;
847     maDefRowModel.mbHidden = bHidden;
848     maDefRowModel.mbThickTop = bThickTop;
849     maDefRowModel.mbThickBottom = bThickBottom;
850 }
851 
852 void WorksheetGlobals::setRowModel( const RowModel& rModel )
853 {
854     // convert 1-based OOXML row index to 0-based API row index
855     sal_Int32 nRow = rModel.mnRow - 1;
856     if( getAddressConverter().checkRow( nRow, true ) )
857     {
858         // try to find entry in row model map that is able to merge with the passed model
859         bool bInsertModel = true;
860         bool bUnusedRow = true;
861         if( !maRowModels.empty() )
862         {
863             // find first row model range following nRow (nRow < aIt->first), or end of map
864             RowModelRangeMap::iterator aIt = maRowModels.upper_bound( nRow );
865             OSL_ENSURE( aIt == maRowModels.end(), "WorksheetGlobals::setRowModel - rows are unsorted" );
866             if( aIt != maRowModels.begin() )
867             {
868                 // go to previous map element (which may be able to merge with the passed model)
869                 --aIt;
870                 // the usage of upper_bound() above ensures that aIt->first is less than or equal to nRow now
871                 sal_Int32& rnLastMapRow = aIt->second.second;
872                 bUnusedRow = rnLastMapRow < nRow;
873                 OSL_ENSURE( bUnusedRow, "WorksheetGlobals::setRowModel - multiple models of the same row" );
874                 if( (rnLastMapRow + 1 == nRow) && aIt->second.first.isMergeable( rModel ) )
875                 {
876                     // can merge with existing model, update last row index
877                     ++rnLastMapRow;
878                     bInsertModel = false;
879                 }
880             }
881         }
882         if( bUnusedRow )
883         {
884             // insert the row model, if it has not been merged with another
885             if( bInsertModel )
886                 maRowModels[ nRow ] = RowModelRange( rModel, nRow );
887             // set row formatting
888             maSheetData.setRowFormat( nRow, rModel.mnXfId, rModel.mbCustomFormat );
889             // set column spans
890             maSheetData.setColSpans( nRow, rModel.maColSpans );
891         }
892     }
893 
894     UpdateRowProgress( maUsedArea, nRow );
895 }
896 
897 // This is called at a higher frequency inside the (threaded) inner loop.
898 void WorksheetGlobals::UpdateRowProgress( const ScRange& rUsedArea, SCROW nRow )
899 {
900     if (!mxRowProgress || nRow < rUsedArea.aStart.Row() || rUsedArea.aEnd.Row() < nRow)
901         return;
902 
903     double fNewPos = static_cast<double>(nRow - rUsedArea.aStart.Row() + 1.0) / (rUsedArea.aEnd.Row() - rUsedArea.aStart.Row() + 1.0);
904 
905     if (mbFastRowProgress)
906         mxRowProgress->setPosition(fNewPos);
907     else
908     {
909         double fCurPos = mxRowProgress->getPosition();
910         if (fCurPos < fNewPos && (fNewPos - fCurPos) > 0.3)
911             // Try not to re-draw progress bar too frequently.
912             mxRowProgress->setPosition(fNewPos);
913     }
914 }
915 
916 void WorksheetGlobals::initializeWorksheetImport()
917 {
918     // set default cell style for unused cells
919     ScDocumentImport& rDoc = getDocImport();
920 
921     ScStyleSheet* pStyleSheet =
922         static_cast<ScStyleSheet*>(rDoc.getDoc().GetStyleSheetPool()->Find(
923             getStyles().getDefaultStyleName(), SfxStyleFamily::Para));
924 
925     if (pStyleSheet)
926         rDoc.setCellStyleToSheet(getSheetIndex(), *pStyleSheet);
927 
928     /*  Remember the current sheet index in global data, needed by global
929         objects, e.g. the chart converter. */
930     setCurrentSheetIndex( getSheetIndex() );
931 }
932 
933 void WorksheetGlobals::finalizeWorksheetImport()
934 {
935     lclUpdateProgressBar( mxRowProgress, 1.0 );
936     maSheetData.finalizeImport();
937     // assumes getTables().finalizeImport ( which creates the DatabaseRanges )
938     // has been called already
939     getTables().applyAutoFilters();
940 
941     getCondFormats().finalizeImport();
942     lclUpdateProgressBar( mxFinalProgress, 0.25 );
943     finalizeHyperlinkRanges();
944     finalizeValidationRanges();
945     maAutoFilters.finalizeImport( getSheetIndex() );
946     maQueryTables.finalizeImport();
947     maSheetSett.finalizeImport();
948     maPageSett.finalizeImport();
949     maSheetViewSett.finalizeImport();
950 
951     lclUpdateProgressBar( mxFinalProgress, 0.5 );
952     convertColumns();
953 
954     // tdf#99913 rows hidden by filter need extra flag
955     ScDocument& rDoc = getScDocument();
956     std::vector<sc::ColRowSpan> aSpans;
957     SCTAB nTab = getSheetIndex();
958     ScDBData* pDBData = rDoc.GetAnonymousDBData(nTab);
959     if (pDBData && pDBData->HasAutoFilter())
960     {
961         ScRange aRange;
962         pDBData->GetArea(aRange);
963         SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
964         SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
965         aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
966     }
967     ScDBCollection* pDocColl = rDoc.GetDBCollection();
968     if (!pDocColl->empty())
969     {
970         ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
971         for (const auto& rxDB : rDBs)
972         {
973             if (rxDB->GetTab() == nTab && rxDB->HasAutoFilter())
974             {
975                 ScRange aRange;
976                 rxDB->GetArea(aRange);
977                 SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
978                 SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
979                 aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
980             }
981         }
982     }
983     convertRows(aSpans);
984     lclUpdateProgressBar( mxFinalProgress, 1.0 );
985 }
986 
987 void WorksheetGlobals::finalizeDrawingImport()
988 {
989     finalizeDrawings();
990 
991     // forget current sheet index in global data
992     setCurrentSheetIndex( -1 );
993 }
994 
995 // private --------------------------------------------------------------------
996 
997 void WorksheetGlobals::finalizeHyperlinkRanges()
998 {
999     for (auto const& link : maHyperlinks)
1000     {
1001         OUString aUrl = getHyperlinkUrl(link);
1002         // try to insert URL into each cell of the range
1003         if( !aUrl.isEmpty() )
1004             for( ScAddress aAddress(link.maRange.aStart.Col(), link.maRange.aStart.Row(), getSheetIndex() ); aAddress.Row() <= link.maRange.aEnd.Row(); aAddress.IncRow() )
1005                 for( aAddress.SetCol(link.maRange.aStart.Col()); aAddress.Col() <= link.maRange.aEnd.Col(); aAddress.IncCol() )
1006                     insertHyperlink( aAddress, aUrl );
1007     }
1008 }
1009 
1010 OUString WorksheetGlobals::getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const
1011 {
1012     OUStringBuffer aUrlBuffer;
1013     if( !rHyperlink.maTarget.isEmpty() )
1014         aUrlBuffer.append( getBaseFilter().getAbsoluteUrl( rHyperlink.maTarget ) );
1015     if( !rHyperlink.maLocation.isEmpty() )
1016         aUrlBuffer.append( '#' ).append( rHyperlink.maLocation );
1017     OUString aUrl = aUrlBuffer.makeStringAndClear();
1018 
1019     if( aUrl.startsWith("#") )
1020     {
1021         sal_Int32 nSepPos = aUrl.lastIndexOf( '!' );
1022         if( nSepPos > 0 )
1023         {
1024             // Do not attempt to blindly convert '#SheetName!A1' to
1025             // '#SheetName.A1', it can be #SheetName!R1C1 as well. Hyperlink
1026             // handler has to handle all, but prefer '#SheetName.A1' if
1027             // possible.
1028             if (nSepPos < aUrl.getLength() - 1)
1029             {
1030                 ScRange aRange;
1031                 const ScDocumentImport& rDoc = getDocImport();
1032                 if ((aRange.ParseAny( aUrl.copy( nSepPos + 1 ), rDoc.getDoc(),
1033                                 formula::FormulaGrammar::CONV_XL_R1C1)
1034                       & ScRefFlags::VALID) == ScRefFlags::ZERO)
1035                     aUrl = aUrl.replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ) );
1036             }
1037             // #i66592# convert sheet names that have been renamed on import
1038             OUString aSheetName = aUrl.copy( 1, nSepPos - 1 );
1039             OUString aCalcName = getWorksheets().getCalcSheetName( aSheetName );
1040             if( !aCalcName.isEmpty() )
1041                 aUrl = aUrl.replaceAt( 1, nSepPos - 1, aCalcName );
1042         }
1043     }
1044 
1045     return aUrl;
1046 }
1047 
1048 void WorksheetGlobals::insertHyperlink( const ScAddress& rAddress, const OUString& rUrl )
1049 {
1050     ScDocumentImport& rDoc = getDocImport();
1051     ScRefCellValue aCell(rDoc.getDoc(), rAddress);
1052 
1053     if (aCell.meType == CELLTYPE_STRING || aCell.meType == CELLTYPE_EDIT)
1054     {
1055         OUString aStr = aCell.getString(&rDoc.getDoc());
1056         ScFieldEditEngine& rEE = rDoc.getDoc().GetEditEngine();
1057         rEE.Clear();
1058 
1059         SvxURLField aURLField(rUrl, aStr, SvxURLFormat::Repr);
1060         SvxFieldItem aURLItem(aURLField, EE_FEATURE_FIELD);
1061         rEE.QuickInsertField(aURLItem, ESelection());
1062 
1063         rDoc.setEditCell(rAddress, rEE.CreateTextObject());
1064     }
1065     else
1066     {
1067         // Handle other cell types e.g. formulas ( and ? ) that have associated
1068         // hyperlinks.
1069         // Ideally all hyperlinks should be treated  as below. For the moment,
1070         // given the current absence of ods support lets just handle what we
1071         // previously didn't handle the new way.
1072         // Unfortunately we won't be able to preserve such hyperlinks when
1073         // saving to ods. Note: when we are able to save such hyperlinks to ods
1074         // we should handle *all* imported hyperlinks as below ( e.g. as cell
1075         // attribute ) for better interoperability.
1076 
1077         SfxStringItem aItem(ATTR_HYPERLINK, rUrl);
1078         rDoc.getDoc().ApplyAttr(rAddress.Col(), rAddress.Row(), rAddress.Tab(), aItem);
1079     }
1080 }
1081 
1082 void WorksheetGlobals::finalizeValidationRanges() const
1083 {
1084     for (auto const& validation : maValidations)
1085     {
1086         PropertySet aPropSet( getCellRangeList(validation.maRanges) );
1087 
1088         Reference< XPropertySet > xValidation( aPropSet.getAnyProperty( PROP_Validation ), UNO_QUERY );
1089         if( xValidation.is() )
1090         {
1091             PropertySet aValProps( xValidation );
1092 
1093             try
1094             {
1095                 const OUString aToken = validation.msRef.getToken( 0, ' ' );
1096 
1097                 Reference<XSpreadsheet> xSheet = getSheetFromDoc( getCurrentSheetIndex() );
1098                 Reference<XCellRange> xDBCellRange;
1099                 Reference<XCell> xCell;
1100                 xDBCellRange = xSheet->getCellRangeByName( aToken );
1101 
1102                 xCell = xDBCellRange->getCellByPosition( 0, 0 );
1103                 Reference<XCellAddressable> xCellAddressable( xCell, UNO_QUERY_THROW );
1104                 CellAddress aFirstCell = xCellAddressable->getCellAddress();
1105                 Reference<XSheetCondition> xCondition( xValidation, UNO_QUERY_THROW );
1106                 xCondition->setSourcePosition( aFirstCell );
1107             }
1108             catch(const Exception&)
1109             {
1110             }
1111 
1112             // convert validation type to API enum
1113             ValidationType eType = ValidationType_ANY;
1114             switch( validation.mnType )
1115             {
1116                 case XML_custom:        eType = ValidationType_CUSTOM;      break;
1117                 case XML_date:          eType = ValidationType_DATE;        break;
1118                 case XML_decimal:       eType = ValidationType_DECIMAL;     break;
1119                 case XML_list:          eType = ValidationType_LIST;        break;
1120                 case XML_none:          eType = ValidationType_ANY;         break;
1121                 case XML_textLength:    eType = ValidationType_TEXT_LEN;    break;
1122                 case XML_time:          eType = ValidationType_TIME;        break;
1123                 case XML_whole:         eType = ValidationType_WHOLE;       break;
1124                 default:    OSL_FAIL( "WorksheetData::finalizeValidationRanges - unknown validation type" );
1125             }
1126             aValProps.setProperty( PROP_Type, eType );
1127 
1128             // convert error alert style to API enum
1129             ValidationAlertStyle eAlertStyle = ValidationAlertStyle_STOP;
1130             switch( validation.mnErrorStyle )
1131             {
1132                 case XML_information:   eAlertStyle = ValidationAlertStyle_INFO;    break;
1133                 case XML_stop:          eAlertStyle = ValidationAlertStyle_STOP;    break;
1134                 case XML_warning:       eAlertStyle = ValidationAlertStyle_WARNING; break;
1135                 default:    OSL_FAIL( "WorksheetData::finalizeValidationRanges - unknown error style" );
1136             }
1137             aValProps.setProperty( PROP_ErrorAlertStyle, eAlertStyle );
1138 
1139             // convert dropdown style to API visibility constants
1140             sal_Int16 nVisibility = validation.mbNoDropDown ? TableValidationVisibility::INVISIBLE : TableValidationVisibility::UNSORTED;
1141             aValProps.setProperty( PROP_ShowList, nVisibility );
1142 
1143             // messages
1144             aValProps.setProperty( PROP_ShowInputMessage, validation.mbShowInputMsg );
1145             aValProps.setProperty( PROP_InputTitle, validation.maInputTitle );
1146             aValProps.setProperty( PROP_InputMessage, validation.maInputMessage );
1147             aValProps.setProperty( PROP_ShowErrorMessage, validation.mbShowErrorMsg );
1148             aValProps.setProperty( PROP_ErrorTitle, validation.maErrorTitle );
1149             aValProps.setProperty( PROP_ErrorMessage, validation.maErrorMessage );
1150 
1151             // allow blank cells
1152             aValProps.setProperty( PROP_IgnoreBlankCells, validation.mbAllowBlank );
1153 
1154             try
1155             {
1156                 // condition operator
1157                 Reference< XSheetCondition2 > xSheetCond( xValidation, UNO_QUERY_THROW );
1158                 if( eType == ValidationType_CUSTOM )
1159                     xSheetCond->setConditionOperator( ConditionOperator2::FORMULA );
1160                 else
1161                     xSheetCond->setConditionOperator( CondFormatBuffer::convertToApiOperator( validation.mnOperator ) );
1162 
1163                 // condition formulas
1164                 Reference< XMultiFormulaTokens > xTokens( xValidation, UNO_QUERY_THROW );
1165                 xTokens->setTokens( 0, validation.maTokens1 );
1166                 xTokens->setTokens( 1, validation.maTokens2 );
1167             }
1168             catch( Exception& )
1169             {
1170             }
1171 
1172             // write back validation settings to cell range(s)
1173             aPropSet.setProperty( PROP_Validation, xValidation );
1174         }
1175     }
1176 }
1177 
1178 void WorksheetGlobals::convertColumns()
1179 {
1180     sal_Int32 nNextCol = 0;
1181     sal_Int32 nMaxCol = mrMaxApiPos.Col();
1182     // stores first grouped column index for each level
1183     OutlineLevelVec aColLevels;
1184 
1185     for (auto const& colModel : maColModels)
1186     {
1187         // column indexes are stored 0-based in maColModels
1188         ValueRange aColRange( ::std::max( colModel.first, nNextCol ), ::std::min( colModel.second.second, nMaxCol ) );
1189         // process gap between two column models, use default column model
1190         if( nNextCol < aColRange.mnFirst )
1191             convertColumns( aColLevels, ValueRange( nNextCol, aColRange.mnFirst - 1 ), maDefColModel );
1192         // process the column model
1193         convertColumns( aColLevels, aColRange, colModel.second.first );
1194         // cache next column to be processed
1195         nNextCol = aColRange.mnLast + 1;
1196     }
1197 
1198     // remaining default columns to end of sheet
1199     convertColumns( aColLevels, ValueRange( nNextCol, nMaxCol ), maDefColModel );
1200     // close remaining column outlines spanning to end of sheet
1201     convertOutlines( aColLevels, nMaxCol + 1, 0, false, false );
1202 }
1203 
1204 void WorksheetGlobals::convertColumns( OutlineLevelVec& orColLevels,
1205         const ValueRange& rColRange, const ColumnModel& rModel )
1206 {
1207     // column width: convert 'number of characters' to column width in twips
1208     sal_Int32 nWidth = std::round(getUnitConverter().scaleValue( rModel.mfWidth, Unit::Digit, Unit::Twip ));
1209 
1210     SCTAB nTab = getSheetIndex();
1211     ScDocument& rDoc = getScDocument();
1212     SCCOL nStartCol = rColRange.mnFirst;
1213     SCCOL nEndCol = rColRange.mnLast;
1214 
1215     if( nWidth > 0 )
1216     {
1217         // macro sheets have double width
1218         if( meSheetType == WorksheetType::Macro )
1219             nWidth *= 2;
1220 
1221         for( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
1222         {
1223             rDoc.SetColWidthOnly(nCol, nTab, nWidth);
1224         }
1225     }
1226 
1227     // hidden columns: TODO: #108683# hide columns later?
1228     if( rModel.mbHidden )
1229     {
1230         rDoc.SetColHidden( nStartCol, nEndCol, nTab, true );
1231     }
1232 
1233     // outline settings for this column range
1234     convertOutlines( orColLevels, rColRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, false );
1235 }
1236 
1237 void WorksheetGlobals::convertRows(const std::vector<sc::ColRowSpan>& rSpans)
1238 {
1239     sal_Int32 nNextRow = 0;
1240     sal_Int32 nMaxRow = mrMaxApiPos.Row();
1241     // stores first grouped row index for each level
1242     OutlineLevelVec aRowLevels;
1243 
1244     for (auto const& rowModel : maRowModels)
1245     {
1246         // row indexes are stored 0-based in maRowModels
1247         ValueRange aRowRange( ::std::max( rowModel.first, nNextRow ), ::std::min( rowModel.second.second, nMaxRow ) );
1248         // process gap between two row models, use default row model
1249         if( nNextRow < aRowRange.mnFirst )
1250             convertRows(aRowLevels, ValueRange(nNextRow, aRowRange.mnFirst - 1), maDefRowModel,
1251                         rSpans);
1252         // process the row model
1253         convertRows(aRowLevels, aRowRange, rowModel.second.first, rSpans, maDefRowModel.mfHeight);
1254         // cache next row to be processed
1255         nNextRow = aRowRange.mnLast + 1;
1256     }
1257 
1258     // remaining default rows to end of sheet
1259     convertRows(aRowLevels, ValueRange(nNextRow, nMaxRow), maDefRowModel, rSpans);
1260     // close remaining row outlines spanning to end of sheet
1261     convertOutlines( aRowLevels, nMaxRow + 1, 0, false, true );
1262 }
1263 
1264 void WorksheetGlobals::convertRows(OutlineLevelVec& orRowLevels, const ValueRange& rRowRange,
1265                                    const RowModel& rModel,
1266                                    const std::vector<sc::ColRowSpan>& rSpans, double fDefHeight)
1267 {
1268     // row height: convert points to row height in twips
1269     double fHeight = (rModel.mfHeight >= 0.0) ? rModel.mfHeight : fDefHeight;
1270     sal_Int32 nHeight = std::round(o3tl::toTwips( fHeight, o3tl::Length::pt ));
1271     SCROW nStartRow = rRowRange.mnFirst;
1272     SCROW nEndRow = rRowRange.mnLast;
1273     SCTAB nTab = getSheetIndex();
1274     if( nHeight > 0 )
1275     {
1276         /* always import the row height, ensures better layout */
1277         ScDocument& rDoc = getScDocument();
1278         rDoc.SetRowHeightOnly(nStartRow, nEndRow, nTab, nHeight);
1279         if(rModel.mbCustomHeight)
1280             rDoc.SetManualHeight( nStartRow, nEndRow, nTab, true );
1281     }
1282 
1283     // hidden rows: TODO: #108683# hide rows later?
1284     if( rModel.mbHidden )
1285     {
1286         ScDocument& rDoc = getScDocument();
1287         rDoc.SetRowHidden( nStartRow, nEndRow, nTab, true );
1288         for (const auto& rSpan : rSpans)
1289         {
1290             // tdf#99913 rows hidden by filter need extra flag
1291             if (rSpan.mnStart <= nStartRow && nStartRow <= rSpan.mnEnd)
1292             {
1293                 SCROW nLast = ::std::min(nEndRow, rSpan.mnEnd);
1294                 rDoc.SetRowFiltered(nStartRow, nLast, nTab, true);
1295                 break;
1296             }
1297         }
1298     }
1299 
1300     // outline settings for this row range
1301     convertOutlines( orRowLevels, rRowRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, true );
1302 }
1303 
1304 void WorksheetGlobals::convertOutlines( OutlineLevelVec& orLevels,
1305         sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows )
1306 {
1307     /*  It is ensured from caller functions, that this function is called
1308         without any gaps between the processed column or row ranges. */
1309 
1310     OSL_ENSURE( nLevel >= 0, "WorksheetGlobals::convertOutlines - negative outline level" );
1311     nLevel = ::std::max< sal_Int32 >( nLevel, 0 );
1312 
1313     sal_Int32 nSize = orLevels.size();
1314     if( nSize < nLevel )
1315     {
1316         // Outline level increased. Push the begin column position.
1317         orLevels.insert(orLevels.end(), nLevel - nSize, nColRow);
1318     }
1319     else if( nLevel < nSize )
1320     {
1321         // Outline level decreased. Pop them all out.
1322         for( sal_Int32 nIndex = nLevel; nIndex < nSize; ++nIndex )
1323         {
1324             sal_Int32 nFirstInLevel = orLevels.back();
1325             orLevels.pop_back();
1326             groupColumnsOrRows( nFirstInLevel, nColRow - 1, bCollapsed, bRows );
1327             bCollapsed = false; // collapse only once
1328         }
1329     }
1330 }
1331 
1332 void WorksheetGlobals::groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapse, bool bRows )
1333 {
1334     try
1335     {
1336         Reference< XSheetOutline > xOutline( mxSheet, UNO_QUERY_THROW );
1337         if( bRows )
1338         {
1339             CellRangeAddress aRange( getSheetIndex(), 0, nFirstColRow, 0, nLastColRow );
1340             xOutline->group( aRange, TableOrientation_ROWS );
1341             if( bCollapse )
1342                 xOutline->hideDetail( aRange );
1343         }
1344         else
1345         {
1346             CellRangeAddress aRange( getSheetIndex(), nFirstColRow, 0, nLastColRow, 0 );
1347             xOutline->group( aRange, TableOrientation_COLUMNS );
1348             if( bCollapse )
1349                 xOutline->hideDetail( aRange );
1350         }
1351     }
1352     catch( Exception& )
1353     {
1354     }
1355 }
1356 
1357 void WorksheetGlobals::finalizeDrawings()
1358 {
1359     // calculate the current drawing page size (after rows/columns are imported)
1360     PropertySet aRangeProp( getCellRange( ScRange( 0, 0, getSheetIndex(), mrMaxApiPos.Col(), mrMaxApiPos.Row(), getSheetIndex() ) ) );
1361     aRangeProp.getProperty( maDrawPageSize, PROP_Size );
1362 
1363     // import DML and VML
1364     if( !maDrawingPath.isEmpty() )
1365         importOoxFragment( new DrawingFragment( *this, maDrawingPath ) );
1366     if( !maVmlDrawingPath.isEmpty() )
1367         importOoxFragment( new VmlDrawingFragment( *this, maVmlDrawingPath ) );
1368 
1369     // comments (after callout shapes have been imported from VML/DFF)
1370     maComments.finalizeImport();
1371 
1372     /*  Extend used area of the sheet by cells covered with drawing objects.
1373         Needed if the imported document is inserted as "OLE object from file"
1374         and thus does not provide an OLE size property by itself. */
1375     if( (maShapeBoundingBox.Width > 0) || (maShapeBoundingBox.Height > 0) )
1376         extendUsedArea( getCellRangeFromRectangle( maShapeBoundingBox ) );
1377 
1378     // if no used area is set, default to A1
1379     if( maUsedArea.aStart.Col() > maUsedArea.aEnd.Col() )
1380     {
1381         maUsedArea.aStart.SetCol( 0 );
1382         maUsedArea.aEnd.SetCol( 0 );
1383     }
1384 
1385     if( maUsedArea.aStart.Row() > maUsedArea.aEnd.Row() )
1386     {
1387         maUsedArea.aStart.SetRow( 0 );
1388         maUsedArea.aEnd.SetRow( 0 );
1389     }
1390 
1391     /*  Register the used area of this sheet in global view settings. The
1392         global view settings will set the visible area if this document is an
1393         embedded OLE object. */
1394     getViewSettings().setSheetUsedArea( maUsedArea );
1395 
1396     /*  #i103686# Set right-to-left sheet layout. Must be done after all
1397         drawing shapes to simplify calculation of shape coordinates. */
1398     if( maSheetViewSett.isSheetRightToLeft() )
1399     {
1400         PropertySet aPropSet( mxSheet );
1401         aPropSet.setProperty( PROP_TableLayout, WritingMode2::RL_TB );
1402     }
1403 }
1404 
1405 WorksheetHelper::WorksheetHelper( WorksheetGlobals& rSheetGlob ) :
1406     WorkbookHelper( rSheetGlob ),
1407     mrSheetGlob( rSheetGlob )
1408 {
1409 }
1410 
1411 ScDocument& WorksheetHelper::getScDocument()
1412 {
1413     return getDocImport().getDoc();
1414 }
1415 
1416 /*static*/ WorksheetGlobalsRef WorksheetHelper::constructGlobals( const WorkbookHelper& rHelper,
1417         const ISegmentProgressBarRef& rxProgressBar, WorksheetType eSheetType, SCTAB nSheet )
1418 {
1419     WorksheetGlobalsRef xSheetGlob = std::make_shared<WorksheetGlobals>( rHelper, rxProgressBar, eSheetType, nSheet );
1420     if( !xSheetGlob->isValidSheet() )
1421         xSheetGlob.reset();
1422     return xSheetGlob;
1423 }
1424 
1425 /* static */ IWorksheetProgress *WorksheetHelper::getWorksheetInterface( const WorksheetGlobalsRef &xRef )
1426 {
1427     return static_cast< IWorksheetProgress *>( xRef.get() );
1428 }
1429 
1430 WorksheetType WorksheetHelper::getSheetType() const
1431 {
1432     return mrSheetGlob.getSheetType();
1433 }
1434 
1435 SCTAB WorksheetHelper::getSheetIndex() const
1436 {
1437     return mrSheetGlob.getSheetIndex();
1438 }
1439 
1440 const Reference< XSpreadsheet >& WorksheetHelper::getSheet() const
1441 {
1442     return mrSheetGlob.getSheet();
1443 }
1444 
1445 Reference< XCell > WorksheetHelper::getCell( const ScAddress& rAddress ) const
1446 {
1447     return mrSheetGlob.getCell( rAddress );
1448 }
1449 
1450 Reference< XCellRange > WorksheetHelper::getCellRange( const ScRange& rRange ) const
1451 {
1452     return mrSheetGlob.getCellRange( rRange );
1453 }
1454 
1455 Reference< XDrawPage > WorksheetHelper::getDrawPage() const
1456 {
1457     return mrSheetGlob.getDrawPage();
1458 }
1459 
1460 awt::Point WorksheetHelper::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
1461 {
1462     return mrSheetGlob.getCellPosition( nCol, nRow );
1463 }
1464 
1465 const awt::Size& WorksheetHelper::getDrawPageSize() const
1466 {
1467     return mrSheetGlob.getDrawPageSize();
1468 }
1469 
1470 SheetDataBuffer& WorksheetHelper::getSheetData() const
1471 {
1472     return mrSheetGlob.getSheetData();
1473 }
1474 
1475 CondFormatBuffer& WorksheetHelper::getCondFormats() const
1476 {
1477     return mrSheetGlob.getCondFormats();
1478 }
1479 
1480 CommentsBuffer& WorksheetHelper::getComments() const
1481 {
1482     return mrSheetGlob.getComments();
1483 }
1484 
1485 AutoFilterBuffer& WorksheetHelper::getAutoFilters() const
1486 {
1487     return mrSheetGlob.getAutoFilters();
1488 }
1489 
1490 QueryTableBuffer& WorksheetHelper::getQueryTables() const
1491 {
1492     return mrSheetGlob.getQueryTables();
1493 }
1494 
1495 WorksheetSettings& WorksheetHelper::getWorksheetSettings() const
1496 {
1497     return mrSheetGlob.getWorksheetSettings();
1498 }
1499 
1500 PageSettings& WorksheetHelper::getPageSettings() const
1501 {
1502     return mrSheetGlob.getPageSettings();
1503 }
1504 
1505 SheetViewSettings& WorksheetHelper::getSheetViewSettings() const
1506 {
1507     return mrSheetGlob.getSheetViewSettings();
1508 }
1509 
1510 VmlDrawing& WorksheetHelper::getVmlDrawing() const
1511 {
1512     return mrSheetGlob.getVmlDrawing();
1513 }
1514 
1515 ExtLst& WorksheetHelper::getExtLst() const
1516 {
1517     return mrSheetGlob.getExtLst();
1518 }
1519 
1520 void WorksheetHelper::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
1521 {
1522     mrSheetGlob.setPageBreak( rModel, bRowBreak );
1523 }
1524 
1525 void WorksheetHelper::setHyperlink( const HyperlinkModel& rModel )
1526 {
1527     mrSheetGlob.setHyperlink( rModel );
1528 }
1529 
1530 void WorksheetHelper::setValidation( const ValidationModel& rModel )
1531 {
1532     mrSheetGlob.setValidation( rModel );
1533 }
1534 
1535 void WorksheetHelper::setDrawingPath( const OUString& rDrawingPath )
1536 {
1537     mrSheetGlob.setDrawingPath( rDrawingPath );
1538 }
1539 
1540 void WorksheetHelper::setVmlDrawingPath( const OUString& rVmlDrawingPath )
1541 {
1542     mrSheetGlob.setVmlDrawingPath( rVmlDrawingPath );
1543 }
1544 
1545 void WorksheetHelper::extendUsedArea( const ScAddress& rAddress )
1546 {
1547     mrSheetGlob.extendUsedArea( rAddress );
1548 }
1549 
1550 void WorksheetHelper::extendShapeBoundingBox( const awt::Rectangle& rShapeRect )
1551 {
1552     mrSheetGlob.extendShapeBoundingBox( rShapeRect );
1553 }
1554 
1555 void WorksheetHelper::setBaseColumnWidth( sal_Int32 nWidth )
1556 {
1557     mrSheetGlob.setBaseColumnWidth( nWidth );
1558 }
1559 
1560 void WorksheetHelper::setDefaultColumnWidth( double fWidth )
1561 {
1562     mrSheetGlob.setDefaultColumnWidth( fWidth );
1563 }
1564 
1565 void WorksheetHelper::setColumnModel( const ColumnModel& rModel )
1566 {
1567     mrSheetGlob.setColumnModel( rModel );
1568 }
1569 
1570 void WorksheetHelper::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
1571 {
1572     mrSheetGlob.setDefaultRowSettings( fHeight, bCustomHeight, bHidden, bThickTop, bThickBottom );
1573 }
1574 
1575 void WorksheetHelper::setRowModel( const RowModel& rModel )
1576 {
1577     mrSheetGlob.setRowModel( rModel );
1578 }
1579 
1580 void WorksheetHelper::setCellFormulaValue(
1581     const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType )
1582 {
1583     getFormulaBuffer().setCellFormulaValue(rAddress, rValueStr, nCellType);
1584 }
1585 
1586 void WorksheetHelper::putRichString( const ScAddress& rAddress, const RichString& rString, const oox::xls::Font* pFirstPortionFont )
1587 {
1588     ScEditEngineDefaulter& rEE = getEditEngine();
1589 
1590     // The cell will own the text object instance returned from convert().
1591     getDocImport().setEditCell(rAddress, rString.convert(rEE, pFirstPortionFont));
1592 }
1593 
1594 void WorksheetHelper::putFormulaTokens( const ScAddress& rAddress, const ApiTokenSequence& rTokens )
1595 {
1596     ScDocumentImport& rDoc = getDocImport();
1597     std::unique_ptr<ScTokenArray> pTokenArray(new ScTokenArray(rDoc.getDoc()));
1598     ScTokenConversion::ConvertToTokenArray(rDoc.getDoc(), *pTokenArray, rTokens);
1599     rDoc.setFormulaCell(rAddress, std::move(pTokenArray));
1600 }
1601 
1602 void WorksheetHelper::initializeWorksheetImport()
1603 {
1604     mrSheetGlob.initializeWorksheetImport();
1605 }
1606 
1607 void WorksheetHelper::finalizeWorksheetImport()
1608 {
1609     mrSheetGlob.finalizeWorksheetImport();
1610 }
1611 
1612 void WorksheetHelper::finalizeDrawingImport()
1613 {
1614     mrSheetGlob.finalizeDrawingImport();
1615 }
1616 
1617 void WorksheetHelper::setCellFormula( const ScAddress& rTokenAddress, const OUString& rTokenStr )
1618 {
1619     getFormulaBuffer().setCellFormula( rTokenAddress,  rTokenStr );
1620 }
1621 
1622 void WorksheetHelper::setCellFormula(
1623     const ScAddress& rAddr, sal_Int32 nSharedId,
1624     const OUString& rCellValue, sal_Int32 nValueType )
1625 {
1626     getFormulaBuffer().setCellFormula(rAddr, nSharedId, rCellValue, nValueType);
1627 }
1628 
1629 void WorksheetHelper::setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr )
1630 {
1631     getFormulaBuffer().setCellArrayFormula( rRangeAddress,  rTokenAddress, rTokenStr );
1632 }
1633 
1634 void WorksheetHelper::createSharedFormulaMapEntry(
1635     const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rTokens )
1636 {
1637     getFormulaBuffer().createSharedFormulaMapEntry(rAddress, nSharedId, rTokens);
1638 }
1639 
1640 } // namespace oox
1641 
1642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1643