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