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 <sheetdatabuffer.hxx>
21 
22 #include <algorithm>
23 #include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
24 #include <com/sun/star/util/DateTime.hpp>
25 #include <com/sun/star/util/NumberFormat.hpp>
26 #include <com/sun/star/util/XNumberFormatTypes.hpp>
27 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
28 #include <sal/log.hxx>
29 #include <osl/diagnose.h>
30 #include <editeng/boxitem.hxx>
31 #include <oox/helper/containerhelper.hxx>
32 #include <oox/helper/propertyset.hxx>
33 #include <oox/token/properties.hxx>
34 #include <oox/token/tokens.hxx>
35 #include <addressconverter.hxx>
36 #include <formulaparser.hxx>
37 #include <sharedstringsbuffer.hxx>
38 #include <unitconverter.hxx>
39 #include <rangelst.hxx>
40 #include <document.hxx>
41 #include <scitems.hxx>
42 #include <docpool.hxx>
43 #include <paramisc.hxx>
44 #include <patattr.hxx>
45 #include <documentimport.hxx>
46 #include <formulabuffer.hxx>
47 #include <numformat.hxx>
48 #include <sax/tools/converter.hxx>
49 
50 namespace oox {
51 namespace xls {
52 
53 using namespace ::com::sun::star::lang;
54 using namespace ::com::sun::star::sheet;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::util;
57 
CellModel()58 CellModel::CellModel() :
59     mnCellType( XML_TOKEN_INVALID ),
60     mnXfId( -1 ),
61     mbShowPhonetic( false )
62 {
63 }
64 
CellFormulaModel()65 CellFormulaModel::CellFormulaModel() :
66     mnFormulaType( XML_TOKEN_INVALID ),
67     mnSharedId( -1 )
68 {
69 }
70 
isValidArrayRef(const ScAddress & rCellAddr)71 bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
72 {
73     return (maFormulaRef.aStart == rCellAddr );
74 }
75 
isValidSharedRef(const ScAddress & rCellAddr)76 bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr )
77 {
78     return
79         (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) &&
80         (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) &&
81         (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row());
82 }
83 
DataTableModel()84 DataTableModel::DataTableModel() :
85     mb2dTable( false ),
86     mbRowTable( false ),
87     mbRef1Deleted( false ),
88     mbRef2Deleted( false )
89 {
90 }
91 
CellBlockBuffer(const WorksheetHelper & rHelper)92 CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
93     WorksheetHelper( rHelper ),
94     mnCurrRow( -1 )
95 {
96 }
97 
setColSpans(sal_Int32 nRow,const ValueRangeSet & rColSpans)98 void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
99 {
100     OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
101     OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
102     if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) )
103     {
104         maColSpans[ nRow ] = rColSpans.getRanges();
105         mnCurrRow = nRow;
106     }
107 }
108 
SheetDataBuffer(const WorksheetHelper & rHelper)109 SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
110     WorksheetHelper( rHelper ),
111     maCellBlocks( rHelper ),
112     mbPendingSharedFmla( false )
113 {
114 }
115 
setColSpans(sal_Int32 nRow,const ValueRangeSet & rColSpans)116 void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
117 {
118     maCellBlocks.setColSpans( nRow, rColSpans );
119 }
120 
setBlankCell(const CellModel & rModel)121 void SheetDataBuffer::setBlankCell( const CellModel& rModel )
122 {
123     setCellFormat( rModel );
124 }
125 
setValueCell(const CellModel & rModel,double fValue)126 void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
127 {
128     getDocImport().setNumericCell(rModel.maCellAddr, fValue);
129     setCellFormat( rModel );
130 }
131 
setStringCell(const CellModel & rModel,const OUString & rText)132 void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
133 {
134     if (!rText.isEmpty())
135         getDocImport().setStringCell(rModel.maCellAddr, rText);
136 
137     setCellFormat( rModel );
138 }
139 
setStringCell(const CellModel & rModel,const RichStringRef & rxString)140 void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
141 {
142     OSL_ENSURE( rxString.get(), "SheetDataBuffer::setStringCell - missing rich string object" );
143     const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
144     OUString aText;
145     if( rxString->extractPlainString( aText, pFirstPortionFont ) )
146     {
147         setStringCell( rModel, aText );
148     }
149     else
150     {
151         putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
152         setCellFormat( rModel );
153     }
154 }
155 
setStringCell(const CellModel & rModel,sal_Int32 nStringId)156 void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
157 {
158     RichStringRef xString = getSharedStrings().getString( nStringId );
159     if( xString.get() )
160         setStringCell( rModel, xString );
161     else
162         setBlankCell( rModel );
163 }
164 
setDateTimeCell(const CellModel & rModel,const css::util::DateTime & rDateTime)165 void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
166 {
167     // write serial date/time value into the cell
168     double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
169     setValueCell( rModel, fSerial );
170     // set appropriate number format
171     using namespace ::com::sun::star::util::NumberFormat;
172     sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
173     // set number format
174     try
175     {
176         Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
177         Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
178         sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
179         PropertySet aPropSet( getCell( rModel.maCellAddr ) );
180         aPropSet.setProperty( PROP_NumberFormat, nIndex );
181     }
182     catch( Exception& )
183     {
184     }
185 }
186 
setBooleanCell(const CellModel & rModel,bool bValue)187 void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
188 {
189     getFormulaBuffer().setCellFormula(
190         rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
191 
192     // #108770# set 'Standard' number format for all Boolean cells
193     setCellFormat( rModel );
194 }
195 
setErrorCell(const CellModel & rModel,const OUString & rErrorCode)196 void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
197 {
198     // Using the formula compiler now we can simply pass on the error string.
199     getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
200     setCellFormat( rModel );
201 }
202 
setErrorCell(const CellModel & rModel,sal_uInt8 nErrorCode)203 void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
204 {
205     setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
206 }
207 
setDateCell(const CellModel & rModel,const OUString & rDateString)208 void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
209 {
210     css::util::DateTime aDateTime;
211     if (!sax::Converter::parseDateTime( aDateTime, rDateString))
212     {
213         SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
214         // At least don't lose data.
215         setStringCell( rModel, rDateString);
216         return;
217     }
218 
219     double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
220     setValueCell( rModel, fSerial);
221 }
222 
createSharedFormula(const ScAddress & rAddr,const ApiTokenSequence & rTokens)223 void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
224 {
225     BinAddress aAddr(rAddr);
226     maSharedFormulas[aAddr] = rTokens;
227     if( mbPendingSharedFmla )
228         setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
229 }
230 
setFormulaCell(const CellModel & rModel,const ApiTokenSequence & rTokens)231 void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
232 {
233     mbPendingSharedFmla = false;
234     ApiTokenSequence aTokens;
235 
236     /*  Detect special token passed as placeholder for array formulas, shared
237         formulas, and table operations. In BIFF, these formulas are represented
238         by a single tExp resp. tTbl token. If the formula parser finds these
239         tokens, it puts a single OPCODE_BAD token with the base address and
240         formula type into the token sequence. This information will be
241         extracted here, and in case of a shared formula, the shared formula
242         buffer will generate the resulting formula token array. */
243     ApiSpecialTokenInfo aTokenInfo;
244     if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
245     {
246         /*  The second member of the token info is set to true, if the formula
247             represents a table operation, which will be skipped. In BIFF12 it
248             is not possible to distinguish array and shared formulas
249             (BIFF5/BIFF8 provide this information with a special flag in the
250             FORMULA record). */
251         if( !aTokenInfo.Second )
252         {
253             /*  Construct the token array representing the shared formula. If
254                 the returned sequence is empty, the definition of the shared
255                 formula has not been loaded yet, or the cell is part of an
256                 array formula. In this case, the cell will be remembered. After
257                 reading the formula definition it will be retried to insert the
258                 formula via retryPendingSharedFormulaCell(). */
259             ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
260             aTokens = resolveSharedFormula( aTokenAddr );
261             if( !aTokens.hasElements() )
262             {
263                 maSharedFmlaAddr = rModel.maCellAddr;
264                 maSharedBaseAddr = aTokenAddr;
265                 mbPendingSharedFmla = true;
266             }
267         }
268     }
269     else
270     {
271         // simple formula, use the passed token array
272         aTokens = rTokens;
273     }
274 
275     setCellFormula( rModel.maCellAddr, aTokens );
276     setCellFormat( rModel );
277 }
278 
createArrayFormula(const ScRange & rRange,const ApiTokenSequence & rTokens)279 void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
280 {
281     /*  Array formulas will be inserted later in finalizeImport(). This is
282         needed to not disturb collecting all the cells, which will be put into
283         the sheet in large blocks to increase performance. */
284     maArrayFormulas.emplace_back( rRange, rTokens );
285 }
286 
createTableOperation(const ScRange & rRange,const DataTableModel & rModel)287 void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
288 {
289     /*  Table operations will be inserted later in finalizeImport(). This is
290         needed to not disturb collecting all the cells, which will be put into
291         the sheet in large blocks to increase performance. */
292     maTableOperations.emplace_back( rRange, rModel );
293 }
294 
setRowFormat(sal_Int32 nRow,sal_Int32 nXfId,bool bCustomFormat)295 void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
296 {
297     // set row formatting
298     if( bCustomFormat )
299     {
300         // try to expand cached row range, if formatting is equal
301         if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
302         {
303 
304             maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
305             maXfIdRowRange.set( nRow, nXfId );
306         }
307     }
308     else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
309     {
310         // finish last cached row range
311         maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
312         maXfIdRowRange.set( -1, -1 );
313     }
314 }
315 
setMergedRange(const ScRange & rRange)316 void SheetDataBuffer::setMergedRange( const ScRange& rRange )
317 {
318     maMergedRanges.emplace_back( rRange );
319 }
320 
321 typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
322 
addIfNotInMyMap(const StylesBuffer & rStyles,std::map<FormatKeyPair,ScRangeList> & rMap,sal_Int32 nXfId,sal_Int32 nFormatId,const ScRangeList & rRangeList)323 static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
324 {
325     Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
326     if ( pXf1 )
327     {
328         auto it = std::find_if(rMap.begin(), rMap.end(),
329             [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
330                 if (rEntry.first.second != nFormatId)
331                     return false;
332                 Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
333                 return *pXf1 == *pXf2;
334             });
335         if (it != rMap.end()) // already exists
336         {
337             // add ranges from the rangelist to the existing rangelist for the
338             // matching style ( should we check if they overlap ? )
339             for (size_t i = 0, nSize = rRangeList.size(); i < nSize; ++i)
340                 it->second.push_back(rRangeList[i]);
341             return;
342         }
343         rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
344     }
345 }
346 
addColXfStyle(sal_Int32 nXfId,sal_Int32 nFormatId,const ScRange & rAddress,bool bProcessRowRange)347 void SheetDataBuffer::addColXfStyle( sal_Int32 nXfId, sal_Int32 nFormatId, const ScRange& rAddress, bool bProcessRowRange )
348 {
349     RowRangeStyle aStyleRows;
350     aStyleRows.mnNumFmt.first = nXfId;
351     aStyleRows.mnNumFmt.second = nFormatId;
352     aStyleRows.mnStartRow = rAddress.aStart.Row();
353     aStyleRows.mnEndRow = rAddress.aEnd.Row();
354     for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
355     {
356         if ( !bProcessRowRange )
357             maStylesPerColumn[ nCol ].insert( aStyleRows );
358         else
359         {
360             RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
361             // Reset row range for each column
362             aStyleRows.mnStartRow = rAddress.aStart.Row();
363             aStyleRows.mnEndRow = rAddress.aEnd.Row();
364 
365             // If aStyleRows includes rows already allocated to a style
366             // in rRowStyles, then we need to split it into parts.
367             // ( to occupy only rows that have no style definition)
368 
369             // Start iterating at the first element that is not completely before aStyleRows
370             RowStyles::iterator rows_it = rRowStyles.lower_bound(aStyleRows);
371             RowStyles::iterator rows_end = rRowStyles.end();
372             bool bAddRange = true;
373             for ( ; rows_it != rows_end; ++rows_it )
374             {
375                 const RowRangeStyle& r = *rows_it;
376 
377                 // Add the part of aStyleRows that does not overlap with r
378                 if ( aStyleRows.mnStartRow < r.mnStartRow )
379                 {
380                     RowRangeStyle aSplit = aStyleRows;
381                     aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, r.mnStartRow - 1);
382                     // Insert with hint that aSplit comes directly before the current position
383                     rRowStyles.insert( rows_it, aSplit );
384                 }
385 
386                 // Done if no part of aStyleRows extends beyond r
387                 if ( aStyleRows.mnEndRow <= r.mnEndRow )
388                 {
389                     bAddRange = false;
390                     break;
391                 }
392 
393                 // Cut off the part aStyleRows that was handled above
394                 aStyleRows.mnStartRow = r.mnEndRow + 1;
395             }
396             if ( bAddRange )
397                 rRowStyles.insert( aStyleRows );
398         }
399     }
400 }
401 
finalizeImport()402 void SheetDataBuffer::finalizeImport()
403 {
404     // create all array formulas
405     for( const auto& [rRange, rTokens] : maArrayFormulas )
406         finalizeArrayFormula( rRange, rTokens );
407 
408     // create all table operations
409     for( const auto& [rRange, rModel] : maTableOperations )
410         finalizeTableOperation( rRange, rModel );
411 
412     // write default formatting of remaining row range
413     maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
414 
415     std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
416     for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
417     {
418         addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
419     }
420     // gather all ranges that have the same style and apply them in bulk
421     for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
422     {
423         for (size_t i = 0, nSize = rRanges.size(); i < nSize; ++i)
424             addColXfStyle( rFormatKeyPair.first, rFormatKeyPair.second, rRanges[i]);
425     }
426 
427     for ( const auto& [rXfId, rRowRangeList] : maXfIdRowRangeList )
428     {
429         if ( rXfId == -1 ) // it's a dud skip it
430             continue;
431         AddressConverter& rAddrConv = getAddressConverter();
432         // get all row ranges for id
433         for ( const auto& rRange : rRowRangeList )
434         {
435             ScRange aRange( 0, rRange.mnFirst, getSheetIndex(),
436                             rAddrConv.getMaxApiAddress().Col(), rRange.mnLast, getSheetIndex() );
437 
438             addColXfStyle( rXfId, -1, aRange, true );
439         }
440     }
441 
442     ScDocumentImport& rDocImport = getDocImport();
443     ScDocument& rDoc = rDocImport.getDoc();
444     StylesBuffer& rStyles = getStyles();
445     for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
446     {
447         SCCOL nScCol = static_cast< SCCOL >( rCol );
448 
449         // tdf#91567 Get pattern from the first row without AutoFilter
450         const ScPatternAttr* pDefPattern = nullptr;
451         bool bAutoFilter = true;
452         SCROW nScRow = 0;
453         while ( bAutoFilter && nScRow < rDoc.MaxRow() )
454         {
455             pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
456             if ( pDefPattern )
457             {
458                 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
459                 bAutoFilter = pAttr->HasAutoFilter();
460             }
461             else
462                 break;
463             nScRow++;
464         }
465         if ( !pDefPattern || nScRow == rDoc.MaxRow() )
466             pDefPattern = rDoc.GetDefPattern();
467 
468         Xf::AttrList aAttrs(pDefPattern);
469         for ( const auto& rRowStyle : rRowStyles )
470         {
471              Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
472 
473              if ( pXf )
474                  pXf->applyPatternToAttrList( aAttrs,  rRowStyle.mnStartRow,  rRowStyle.mnEndRow,  rRowStyle.mnNumFmt.second );
475         }
476         if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
477         {
478             ScAttrEntry aEntry;
479             aEntry.nEndRow = rDoc.MaxRow();
480             aEntry.pPattern = pDefPattern;
481             rDoc.GetPool()->Put(*aEntry.pPattern);
482             aAttrs.maAttrs.push_back(aEntry);
483 
484             if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
485                 aAttrs.mbLatinNumFmtOnly = false;
486         }
487 
488         ScDocumentImport::Attrs aAttrParam;
489         aAttrParam.mvData.swap(aAttrs.maAttrs);
490         aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
491 
492         rDocImport.setAttrEntries(getSheetIndex(), nScCol, std::move(aAttrParam));
493     }
494 
495     // merge all cached merged ranges and update right/bottom cell borders
496     for( const auto& rMergedRange : maMergedRanges )
497         applyCellMerging( rMergedRange.maRange );
498     for( const auto& rCenterFillRange : maCenterFillRanges )
499         applyCellMerging( rCenterFillRange.maRange );
500 }
501 
502 // private --------------------------------------------------------------------
503 
XfIdRowRange()504 SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
505     maRowRange( -1 ),
506     mnXfId( -1 )
507 {
508 }
509 
set(sal_Int32 nRow,sal_Int32 nXfId)510 void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
511 {
512     maRowRange = ValueRange( nRow );
513     mnXfId = nXfId;
514 }
515 
tryExpand(sal_Int32 nRow,sal_Int32 nXfId)516 bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
517 {
518     if( mnXfId == nXfId )
519     {
520         if( maRowRange.mnLast + 1 == nRow )
521         {
522             ++maRowRange.mnLast;
523             return true;
524         }
525         if( maRowRange.mnFirst == nRow + 1 )
526         {
527             --maRowRange.mnFirst;
528             return true;
529         }
530     }
531     return false;
532 }
533 
MergedRange(const ScRange & rRange)534 SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
535     maRange( rRange ),
536     mnHorAlign( XML_TOKEN_INVALID )
537 {
538 }
539 
MergedRange(const ScAddress & rAddress,sal_Int32 nHorAlign)540 SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
541     maRange( rAddress, rAddress ),
542     mnHorAlign( nHorAlign )
543 {
544 }
545 
tryExpand(const ScAddress & rAddress,sal_Int32 nHorAlign)546 bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
547 {
548     if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
549         (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
550     {
551         maRange.aEnd.IncCol();
552         return true;
553     }
554     return false;
555 }
556 
setCellFormula(const ScAddress & rCellAddr,const ApiTokenSequence & rTokens)557 void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
558 {
559     if( rTokens.hasElements() )
560     {
561         putFormulaTokens( rCellAddr, rTokens );
562     }
563 }
564 
565 
resolveSharedFormula(const ScAddress & rAddr) const566 ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
567 {
568     BinAddress aAddr(rAddr);
569     ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
570     return aTokens;
571 }
572 
finalizeArrayFormula(const ScRange & rRange,const ApiTokenSequence & rTokens) const573 void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
574 {
575     Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
576     OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
577     if( xTokens.is() )
578         xTokens->setArrayTokens( rTokens );
579 }
580 
finalizeTableOperation(const ScRange & rRange,const DataTableModel & rModel)581 void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
582 {
583     if (rModel.mbRef1Deleted)
584         return;
585 
586     if (rModel.maRef1.isEmpty())
587         return;
588 
589     if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
590         return;
591 
592     sal_Int16 nSheet = getSheetIndex();
593 
594     ScAddress aRef1( 0, 0, 0 );
595     if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
596         return;
597 
598     ScDocumentImport& rDoc = getDocImport();
599     ScTabOpParam aParam;
600 
601     ScRange aScRange(rRange);
602 
603     if (rModel.mb2dTable)
604     {
605         // Two-variable data table.
606         if (rModel.mbRef2Deleted)
607             return;
608 
609         if (rModel.maRef2.isEmpty())
610             return;
611 
612         ScAddress aRef2( 0, 0, 0 );
613         if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
614             return;
615 
616         aParam.meMode = ScTabOpParam::Both;
617 
618         aScRange.aStart.IncCol(-1);
619         aScRange.aStart.IncRow(-1);
620 
621         aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
622         aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
623 
624         // Ref1 is row input cell and Ref2 is column input cell.
625         aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
626         aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
627         rDoc.setTableOpCells(aScRange, aParam);
628 
629         return;
630     }
631 
632     // One-variable data table.
633 
634     if (rModel.mbRowTable)
635     {
636         // One-variable row input cell (horizontal).
637         aParam.meMode = ScTabOpParam::Row;
638         aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
639         aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
640         aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
641         aScRange.aStart.IncRow(-1);
642         rDoc.setTableOpCells(aScRange, aParam);
643     }
644     else
645     {
646         // One-variable column input cell (vertical).
647         aParam.meMode = ScTabOpParam::Column;
648         aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
649         aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
650         aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
651         aScRange.aStart.IncCol(-1);
652         rDoc.setTableOpCells(aScRange, aParam);
653     }
654 }
655 
setCellFormat(const CellModel & rModel)656 void SheetDataBuffer::setCellFormat( const CellModel& rModel )
657 {
658     if( rModel.mnXfId >= 0 )
659     {
660         ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
661         ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
662         /* The xlsx sheet data contains row wise information.
663          * It is sufficient to check if the row range size is one
664          */
665         if (!rRangeList.empty() &&
666             *pLastRange == rModel.maCellAddr)
667             ; // do nothing - this probably bad data
668         else if (!rRangeList.empty() &&
669             pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
670             pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
671             pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
672             pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
673         {
674             pLastRange->aEnd.IncCol();       // Expand Column
675         }
676         else
677         {
678             rRangeList.push_back(ScRange(rModel.maCellAddr));
679             pLastRange = &rRangeList.back();
680         }
681 
682         if (rRangeList.size() > 1)
683         {
684             for (size_t i = rRangeList.size() - 1; i != 0; --i)
685             {
686                 ScRange& rMergeRange = rRangeList[i - 1];
687                 if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
688                     break;
689 
690                 /* Try to merge this with the previous range */
691                 if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
692                     pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
693                     pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
694                 {
695                     rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
696                     rRangeList.Remove(rRangeList.size() - 1);
697                     break;
698                 }
699                 else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
700                     break; // Un-necessary to check with any other rows
701             }
702         }
703         // update merged ranges for 'center across selection' and 'fill'
704         if( const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get() )
705         {
706             sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
707             if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
708             {
709                 /*  start new merged range, if cell is not empty (#108781#),
710                     or try to expand last range with empty cell */
711                 if( rModel.mnCellType != XML_TOKEN_INVALID )
712                     maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
713                 else if( !maCenterFillRanges.empty() )
714                     maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
715             }
716         }
717     }
718 }
719 
lcl_SetBorderLine(ScDocument & rDoc,const ScRange & rRange,SCTAB nScTab,SvxBoxItemLine nLine)720 static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
721 {
722     SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
723     SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
724 
725     const SvxBoxItem* pFromItem =
726         rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
727     const SvxBoxItem* pToItem =
728         rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
729 
730     SvxBoxItem aNewItem( *pToItem );
731     aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
732     rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
733 }
734 
applyCellMerging(const ScRange & rRange)735 void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
736 {
737     bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
738     bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
739 
740     const ScAddress& rStart = rRange.aStart;
741     const ScAddress& rEnd = rRange.aEnd;
742     ScDocument& rDoc = getScDocument();
743     // set correct right border
744     if( bMultiCol )
745         lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
746         // set correct lower border
747     if( bMultiRow )
748         lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
749     // do merge
750     if( bMultiCol || bMultiRow )
751         rDoc.DoMerge( getSheetIndex(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row() );
752 }
753 
754 } // namespace xls
755 } // namespace oox
756 
757 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
758