LibreOffice Module sc (master) 1
sheetdatabuffer.cxx
Go to the documentation of this file.
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#include <patterncache.hxx>
22
23#include <algorithm>
24#include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
25#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
26#include <com/sun/star/table/XCell.hpp>
27#include <com/sun/star/table/XCellRange.hpp>
28#include <com/sun/star/util/DateTime.hpp>
29#include <com/sun/star/util/NumberFormat.hpp>
30#include <com/sun/star/util/XNumberFormatTypes.hpp>
31#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
32#include <sal/log.hxx>
33#include <osl/diagnose.h>
34#include <editeng/boxitem.hxx>
37#include <oox/token/properties.hxx>
38#include <oox/token/tokens.hxx>
39#include <addressconverter.hxx>
40#include <formulaparser.hxx>
42#include <unitconverter.hxx>
43#include <rangelst.hxx>
44#include <document.hxx>
45#include <scitems.hxx>
46#include <docpool.hxx>
47#include <paramisc.hxx>
48#include <patattr.hxx>
49#include <documentimport.hxx>
50#include <formulabuffer.hxx>
51#include <numformat.hxx>
53
54namespace oox::xls {
55
56using namespace ::com::sun::star::lang;
57using namespace ::com::sun::star::sheet;
58using namespace ::com::sun::star::uno;
59using namespace ::com::sun::star::util;
60
62 mnCellType( XML_TOKEN_INVALID ),
63 mnXfId( -1 ),
64 mbShowPhonetic( false )
65{
66}
67
69 mnFormulaType( XML_TOKEN_INVALID ),
70 mnSharedId( -1 )
71{
72}
73
75{
76 return (maFormulaRef.aStart == rCellAddr );
77}
78
80{
81 return
82 (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) &&
83 (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) &&
84 (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row());
85}
86
88 mb2dTable( false ),
89 mbRowTable( false ),
90 mbRef1Deleted( false ),
91 mbRef2Deleted( false )
92{
93}
94
96 WorksheetHelper( rHelper ),
97 mbPendingSharedFmla( false )
98{
99}
100
102{
103 setCellFormat( rModel );
104}
105
106void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
107{
108 getDocImport().setNumericCell(rModel.maCellAddr, fValue);
109 setCellFormat( rModel );
110}
111
112void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
113{
114 if (!rText.isEmpty())
115 getDocImport().setStringCell(rModel.maCellAddr, rText);
116
117 setCellFormat( rModel );
118}
119
120void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
121{
122 OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" );
123 const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
124 const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
125 bool bSingleLine = pXf ? !rxString->isPreserveSpace() && !pXf->getAlignment().getModel().mbWrapText : false;
126 OUString aText;
127 if( rxString->extractPlainString( aText, pFirstPortionFont ) )
128 {
129 setStringCell( rModel, aText );
130 }
131 else
132 {
133 putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont, bSingleLine );
134 setCellFormat( rModel );
135 }
136}
137
138void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
139{
140 RichStringRef xString = getSharedStrings().getString( nStringId );
141 if( xString )
142 setStringCell( rModel, xString );
143 else
144 setBlankCell( rModel );
145}
146
147void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
148{
149 // write serial date/time value into the cell
150 double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
151 setValueCell( rModel, fSerial );
152 // set appropriate number format
153 using namespace ::com::sun::star::util::NumberFormat;
154 sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
155 // set number format
156 try
157 {
158 Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
159 Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
160 sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
161 PropertySet aPropSet( getCell( rModel.maCellAddr ) );
163 }
164 catch( Exception& )
165 {
166 }
167}
168
169void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
170{
172 rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
173
174 // #108770# set 'Standard' number format for all Boolean cells
175 setCellFormat( rModel );
176}
177
178void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
179{
180 // Using the formula compiler now we can simply pass on the error string.
181 getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
182 setCellFormat( rModel );
183}
184
185void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
186{
187 setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
188}
189
190void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
191{
192 css::util::DateTime aDateTime;
193 if (!sax::Converter::parseDateTime( aDateTime, rDateString))
194 {
195 SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
196 // At least don't lose data.
197 setStringCell( rModel, rDateString);
198 return;
199 }
200
201 double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
202 setValueCell( rModel, fSerial);
203}
204
206{
207 BinAddress aAddr(rAddr);
208 maSharedFormulas[aAddr] = rTokens;
211}
212
213void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
214{
215 mbPendingSharedFmla = false;
216 ApiTokenSequence aTokens;
217
218 /* Detect special token passed as placeholder for array formulas, shared
219 formulas, and table operations. In BIFF, these formulas are represented
220 by a single tExp resp. tTbl token. If the formula parser finds these
221 tokens, it puts a single OPCODE_BAD token with the base address and
222 formula type into the token sequence. This information will be
223 extracted here, and in case of a shared formula, the shared formula
224 buffer will generate the resulting formula token array. */
225 ApiSpecialTokenInfo aTokenInfo;
226 if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
227 {
228 /* The second member of the token info is set to true, if the formula
229 represents a table operation, which will be skipped. In BIFF12 it
230 is not possible to distinguish array and shared formulas
231 (BIFF5/BIFF8 provide this information with a special flag in the
232 FORMULA record). */
233 if( !aTokenInfo.Second )
234 {
235 /* Construct the token array representing the shared formula. If
236 the returned sequence is empty, the definition of the shared
237 formula has not been loaded yet, or the cell is part of an
238 array formula. In this case, the cell will be remembered. After
239 reading the formula definition it will be retried to insert the
240 formula via retryPendingSharedFormulaCell(). */
241 ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
242 aTokens = resolveSharedFormula( aTokenAddr );
243 if( !aTokens.hasElements() )
244 {
246 maSharedBaseAddr = aTokenAddr;
247 mbPendingSharedFmla = true;
248 }
249 }
250 }
251 else
252 {
253 // simple formula, use the passed token array
254 aTokens = rTokens;
255 }
256
257 setCellFormula( rModel.maCellAddr, aTokens );
258 setCellFormat( rModel );
259}
260
262{
263 /* Array formulas will be inserted later in finalizeImport(). This is
264 needed to not disturb collecting all the cells, which will be put into
265 the sheet in large blocks to increase performance. */
266 maArrayFormulas.emplace_back( rRange, rTokens );
267}
268
270{
271 /* Table operations will be inserted later in finalizeImport(). This is
272 needed to not disturb collecting all the cells, which will be put into
273 the sheet in large blocks to increase performance. */
274 maTableOperations.emplace_back( rRange, rModel );
275}
276
277void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
278{
279 // set row formatting
280 if( bCustomFormat )
281 {
282 // try to expand cached row range, if formatting is equal
283 if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
284 {
285
287 maXfIdRowRange.set( nRow, nXfId );
288 }
289 }
290 else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
291 {
292 // finish last cached row range
294 maXfIdRowRange.set( -1, -1 );
295 }
296}
297
299{
300 maMergedRanges.emplace_back( rRange );
301}
302
303typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
304
305static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
306{
307 Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
308 if ( !pXf1 )
309 return;
310
311 auto it = std::find_if(rMap.begin(), rMap.end(),
312 [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
313 if (rEntry.first.second != nFormatId)
314 return false;
315 Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
316 return *pXf1 == *pXf2;
317 });
318 if (it != rMap.end()) // already exists
319 {
320 // add ranges from the rangelist to the existing rangelist for the
321 // matching style ( should we check if they overlap ? )
322 it->second.insert(it->second.end(), rRangeList.begin(), rRangeList.end());
323 return;
324 }
325 rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
326}
327
329{
330 std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
331 for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
332 {
333 addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
334 }
335 // gather all ranges that have the same style and apply them in bulk
336 // Collect data in unsorted vectors and sort them just once at the end
337 // instead of possibly slow repeated inserts.
338 TmpColStyles tmpStylesPerColumn;
339 for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
340 {
341 for (const ScRange & rAddress : rRanges)
342 {
343 RowRangeStyle aStyleRows;
344 aStyleRows.mnNumFmt.first = rFormatKeyPair.first;
345 aStyleRows.mnNumFmt.second = rFormatKeyPair.second;
346 aStyleRows.mnStartRow = rAddress.aStart.Row();
347 aStyleRows.mnEndRow = rAddress.aEnd.Row();
348 for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
349 tmpStylesPerColumn[ nCol ].push_back( aStyleRows );
350 }
351 }
352 for( auto& rowStyles : tmpStylesPerColumn )
353 {
354 TmpRowStyles& s = rowStyles.second;
355 std::sort( s.begin(), s.end(), StyleRowRangeComp());
356 s.erase( std::unique( s.begin(), s.end(),
357 [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
358 // Synthetize operator== from operator < . Do not create an actual operator==
359 // as operator< is somewhat specific (see StyleRowRangeComp).
360 { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
361 s.end());
362 // Broken documents may have overlapping ranges that cause problems, repeat once more.
363 if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
364 {
365 std::sort( s.begin(), s.end(), StyleRowRangeComp());
366 s.erase( std::unique( s.begin(), s.end(),
367 [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
368 { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
369 s.end());
370 }
371 maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( std::move( s ));
372 }
373}
374
376{
377 // count the number of row-range-styles we have
379 int cnt = 0;
380 for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
381 {
382 if ( nXfId == -1 ) // it's a dud skip it
383 continue;
384 cnt += rRowRangeList.size();
385 }
386 // pre-allocate space in the sorted_vector
387 for ( sal_Int32 nCol = 0; nCol <= rAddrConv.getMaxApiAddress().Col(); ++nCol )
388 {
389 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
390 rRowStyles.reserve(rRowStyles.size() + cnt);
391 }
392 const auto nMaxCol = rAddrConv.getMaxApiAddress().Col();
393 for ( sal_Int32 nCol = 0; nCol <= nMaxCol; ++nCol )
394 {
395 RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
396 for ( auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
397 {
398 if ( nXfId == -1 ) // it's a dud skip it
399 continue;
400 // sort the row ranges, so we spend less time moving data around
401 // when we insert into aStyleRows
402 std::sort(rRowRangeList.begin(), rRowRangeList.end(),
403 [](const ValueRange& lhs, const ValueRange& rhs)
404 {
405 return lhs.mnFirst < rhs.mnFirst;
406 });
407 // get all row ranges for id
408 for ( const auto& rRange : rRowRangeList )
409 {
410 RowRangeStyle aStyleRows;
411 aStyleRows.mnNumFmt.first = nXfId;
412 aStyleRows.mnNumFmt.second = -1;
413
414 // Reset row range for each column
415 aStyleRows.mnStartRow = rRange.mnFirst;
416 aStyleRows.mnEndRow = rRange.mnLast;
417
418 // If aStyleRows includes rows already allocated to a style
419 // in rRowStyles, then we need to split it into parts.
420 // ( to occupy only rows that have no style definition)
421
422 // Start iterating at the first element that is not completely before aStyleRows
423 RowStyles::const_iterator rows_it = rRowStyles.lower_bound(aStyleRows);
424 bool bAddRange = true;
425 for ( ; rows_it != rRowStyles.end(); ++rows_it )
426 {
427 // Add the part of aStyleRows that does not overlap with r
428 if ( aStyleRows.mnStartRow < rows_it->mnStartRow )
429 {
430 RowRangeStyle aSplit = aStyleRows;
431 aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, rows_it->mnStartRow - 1);
432 rows_it = rRowStyles.insert( aSplit ).first;
433 }
434
435 // Done if no part of aStyleRows extends beyond r
436 if ( aStyleRows.mnEndRow <= rows_it->mnEndRow )
437 {
438 bAddRange = false;
439 break;
440 }
441
442 // Cut off the part aStyleRows that was handled above
443 aStyleRows.mnStartRow = rows_it->mnEndRow + 1;
444 }
445 if ( bAddRange )
446 rRowStyles.insert( aStyleRows );
447 }
448 }
449 }
450}
451
453{
454 ScDocumentImport& rDocImport = getDocImport();
455
456 SCTAB nStartTabInvalidatedIters(SCTAB_MAX);
457 SCTAB nEndTabInvalidatedIters(0);
458
459 // create all array formulas
460 for( const auto& [rRange, rTokens] : maArrayFormulas )
461 {
462 finalizeArrayFormula(rRange, rTokens);
463
464 nStartTabInvalidatedIters = std::min(rRange.aStart.Tab(), nStartTabInvalidatedIters);
465 nEndTabInvalidatedIters = std::max(rRange.aEnd.Tab(), nEndTabInvalidatedIters);
466 }
467
468 for (SCTAB nTab = nStartTabInvalidatedIters; nTab <= nEndTabInvalidatedIters; ++nTab)
469 rDocImport.invalidateBlockPositionSet(nTab);
470
471 // create all table operations
472 for( const auto& [rRange, rModel] : maTableOperations )
473 finalizeTableOperation( rRange, rModel );
474
475 // write default formatting of remaining row range
477
479
481
482 ScDocument& rDoc = rDocImport.getDoc();
483 StylesBuffer& rStyles = getStyles();
484 ScDocumentImport::Attrs aPendingAttrParam;
485 SCCOL pendingColStart = -1;
486 SCCOL pendingColEnd = -1;
487 ScPatternCache aPatternCache;
488
489 for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
490 {
491 SCCOL nScCol = static_cast< SCCOL >( rCol );
492
493 // tdf#91567 Get pattern from the first row without AutoFilter
494 const ScPatternAttr* pDefPattern = nullptr;
495 bool bAutoFilter = true;
496 SCROW nScRow = 0;
497 while ( bAutoFilter && nScRow < rDoc.MaxRow() )
498 {
499 pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
500 if ( pDefPattern )
501 {
502 const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
503 bAutoFilter = pAttr->HasAutoFilter();
504 }
505 else
506 break;
507 nScRow++;
508 }
509 if ( !pDefPattern || nScRow == rDoc.MaxRow() )
510 pDefPattern = rDoc.GetDefPattern();
511
512 Xf::AttrList aAttrs(pDefPattern);
513 for ( const auto& rRowStyle : rRowStyles )
514 {
515 Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
516
517 if ( pXf )
518 pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow,
519 rRowStyle.mnNumFmt.first, rRowStyle.mnNumFmt.second, aPatternCache );
520 }
521 if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
522 {
523 ScAttrEntry aEntry;
524 aEntry.nEndRow = rDoc.MaxRow();
525 aEntry.pPattern = pDefPattern;
526 rDoc.GetPool()->Put(*aEntry.pPattern);
527 aAttrs.maAttrs.push_back(aEntry);
528
529 if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
530 aAttrs.mbLatinNumFmtOnly = false;
531 }
532
533 ScDocumentImport::Attrs aAttrParam;
534 aAttrParam.mvData.swap(aAttrs.maAttrs);
535 aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
536
537 // Compress setting the attributes, set the same set in one call.
538 if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
539 ++pendingColEnd;
540 else
541 {
542 if( pendingColStart != -1 )
543 rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
544 pendingColStart = pendingColEnd = nScCol;
545 aPendingAttrParam = std::move( aAttrParam );
546 }
547 }
548 if( pendingColStart != -1 )
549 rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
550
551 // merge all cached merged ranges and update right/bottom cell borders
552 for( const auto& rMergedRange : maMergedRanges )
553 applyCellMerging( rMergedRange.maRange );
554 for( const auto& rCenterFillRange : maCenterFillRanges )
555 applyCellMerging( rCenterFillRange.maRange );
556}
557
558// private --------------------------------------------------------------------
559
561 maRowRange( -1 ),
562 mnXfId( -1 )
563{
564}
565
566void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
567{
568 maRowRange = ValueRange( nRow );
569 mnXfId = nXfId;
570}
571
572bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
573{
574 if( mnXfId == nXfId )
575 {
576 if( maRowRange.mnLast + 1 == nRow )
577 {
578 ++maRowRange.mnLast;
579 return true;
580 }
581 if( maRowRange.mnFirst == nRow + 1 )
582 {
583 --maRowRange.mnFirst;
584 return true;
585 }
586 }
587 return false;
588}
589
591 maRange( rRange ),
592 mnHorAlign( XML_TOKEN_INVALID )
593{
594}
595
596SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
597 maRange( rAddress, rAddress ),
598 mnHorAlign( nHorAlign )
599{
600}
601
602bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
603{
604 if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
605 (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
606 {
607 maRange.aEnd.IncCol();
608 return true;
609 }
610 return false;
611}
612
613void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
614{
615 if( rTokens.hasElements() )
616 {
617 putFormulaTokens( rCellAddr, rTokens );
618 }
619}
620
621
623{
624 BinAddress aAddr(rAddr);
626 return aTokens;
627}
628
629void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
630{
631 Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
632 OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
633 if( xTokens.is() )
634 xTokens->setArrayTokens( rTokens );
635}
636
638{
639 if (rModel.mbRef1Deleted)
640 return;
641
642 if (rModel.maRef1.isEmpty())
643 return;
644
645 if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
646 return;
647
648 sal_Int16 nSheet = getSheetIndex();
649
650 ScAddress aRef1( 0, 0, 0 );
651 if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
652 return;
653
655 ScTabOpParam aParam;
656
657 ScRange aScRange(rRange);
658
659 if (rModel.mb2dTable)
660 {
661 // Two-variable data table.
662 if (rModel.mbRef2Deleted)
663 return;
664
665 if (rModel.maRef2.isEmpty())
666 return;
667
668 ScAddress aRef2( 0, 0, 0 );
669 if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
670 return;
671
672 aParam.meMode = ScTabOpParam::Both;
673
674 aScRange.aStart.IncCol(-1);
675 aScRange.aStart.IncRow(-1);
676
677 aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
678 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
679
680 // Ref1 is row input cell and Ref2 is column input cell.
681 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
682 aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
683 rDoc.setTableOpCells(aScRange, aParam);
684
685 return;
686 }
687
688 // One-variable data table.
689
690 if (rModel.mbRowTable)
691 {
692 // One-variable row input cell (horizontal).
693 aParam.meMode = ScTabOpParam::Row;
694 aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
695 aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
696 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
697 aScRange.aStart.IncRow(-1);
698 rDoc.setTableOpCells(aScRange, aParam);
699 }
700 else
701 {
702 // One-variable column input cell (vertical).
704 aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
705 aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
706 aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
707 aScRange.aStart.IncCol(-1);
708 rDoc.setTableOpCells(aScRange, aParam);
709 }
710}
711
713{
714 if( rModel.mnXfId < 0 )
715 return;
716
717 ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
718 ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
719 /* The xlsx sheet data contains row wise information.
720 * It is sufficient to check if the row range size is one
721 */
722 if (!rRangeList.empty() &&
723 *pLastRange == rModel.maCellAddr)
724 ; // do nothing - this probably bad data
725 else if (!rRangeList.empty() &&
726 pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
727 pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
728 pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
729 pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
730 {
731 pLastRange->aEnd.IncCol(); // Expand Column
732 }
733 else
734 {
735 rRangeList.push_back(ScRange(rModel.maCellAddr));
736 pLastRange = &rRangeList.back();
737 }
738
739 if (rRangeList.size() > 1)
740 {
741 for (size_t i = rRangeList.size() - 1; i != 0; --i)
742 {
743 ScRange& rMergeRange = rRangeList[i - 1];
744 if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
745 break;
746
747 /* Try to merge this with the previous range */
748 if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
749 pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
750 pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
751 {
752 rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
753 rRangeList.Remove(rRangeList.size() - 1);
754 break;
755 }
756 else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
757 break; // Un-necessary to check with any other rows
758 }
759 }
760 // update merged ranges for 'center across selection' and 'fill'
761 const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
762 if( !pXf )
763 return;
764
765 sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
766 if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
767 {
768 /* start new merged range, if cell is not empty (#108781#),
769 or try to expand last range with empty cell */
770 if( rModel.mnCellType != XML_TOKEN_INVALID )
771 maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
772 else if( !maCenterFillRanges.empty() )
773 maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
774 }
775}
776
777static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
778{
779 SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
780 SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
781
782 const SvxBoxItem* pFromItem =
783 rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
784 const SvxBoxItem* pToItem =
785 rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
786
787 SvxBoxItem aNewItem( *pToItem );
788 aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
789 rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
790}
791
793{
794 bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
795 bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
796
797 const ScAddress& rStart = rRange.aStart;
798 const ScAddress& rEnd = rRange.aEnd;
799 ScDocument& rDoc = getScDocument();
800 // set correct right border
801 if( bMultiCol )
802 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
803 // set correct lower border
804 if( bMultiRow )
805 lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
806 // do merge
807 if( bMultiCol || bMultiRow )
808 rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), getSheetIndex() );
809}
810
811} // namespace oox
812
813/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const SCTAB SCTAB_MAX
Definition: address.hxx:57
B2DRange maRange
SvxBoxItemLine
SCTAB Tab() const
Definition: address.hxx:283
void IncCol(SCCOL nDelta=1)
Definition: address.hxx:316
SCROW Row() const
Definition: address.hxx:274
void SetRow(SCROW nRowP)
Definition: address.hxx:287
void IncRow(SCROW nDelta=1)
Definition: address.hxx:312
SCCOL Col() const
Definition: address.hxx:279
Accessor class to ScDocument.
void setStringCell(const ScAddress &rPos, const OUString &rStr)
void setTableOpCells(const ScRange &rRange, const ScTabOpParam &rParam)
ScDocument & getDoc()
void invalidateBlockPositionSet(SCTAB nTab)
void setAttrEntries(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, Attrs &&rAttrs)
Set an array of cell attributes to specified range of columns.
void setNumericCell(const ScAddress &rPos, double fVal)
SC_DLLPUBLIC ScPatternAttr * GetDefPattern() const
Definition: document.cxx:6045
SC_DLLPUBLIC ScDocumentPool * GetPool()
Definition: document.cxx:6050
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:893
SC_DLLPUBLIC void DoMerge(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, bool bDeleteCaptions=true)
Definition: documen3.cxx:2063
SC_DLLPUBLIC void ApplyAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem &rAttr)
Definition: document.cxx:4741
SC_DLLPUBLIC const SfxPoolItem * GetAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich) const
Definition: document.cxx:4684
SC_DLLPUBLIC const ScPatternAttr * GetPattern(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:4719
bool HasAutoFilter() const
Definition: attrib.hxx:106
SfxItemSet & GetItemSet()
Definition: patattr.hxx:192
::std::vector< ScRange >::const_iterator begin() const
Definition: rangelst.hxx:97
::std::vector< ScRange >::const_iterator end() const
Definition: rangelst.hxx:98
bool empty() const
Definition: rangelst.hxx:88
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1137
size_t size() const
Definition: rangelst.hxx:89
void Remove(size_t nPos)
Definition: rangelst.cxx:1093
ScRange & back()
Definition: rangelst.hxx:94
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
void Set(const ScAddress &rAdr, bool bNewRelCol, bool bNewRelRow, bool bNewRelTab)
Definition: address.hxx:914
const T & Put(std::unique_ptr< T > xItem, sal_uInt16 nWhich=0)
const SfxPoolItem * GetItem(sal_uInt16 nWhich, bool bSearchInParent=true) const
const editeng::SvxBorderLine * GetLine(SvxBoxItemLine nLine) const
void SetLine(const editeng::SvxBorderLine *pNew, SvxBoxItemLine nLine)
void reserve(size_type amount)
std::vector< Value >::const_iterator const_iterator
const_iterator end() const
size_type size() const
std::pair< const_iterator, bool > insert(Value &&x)
const_iterator lower_bound(const Value &x) const
static const MapType::mapped_type * getMapElement(const MapType &rMap, const typename MapType::key_type &rKey)
bool setProperty(sal_Int32 nPropId, const Type &rValue)
Converter for cell addresses and cell ranges for OOXML and BIFF filters.
const ScAddress & getMaxApiAddress() const
Returns the biggest valid cell address in the own Calc document.
const AlignmentModel & getModel() const
Returns the alignment model structure.
void setCellFormula(const ScAddress &rAddress, const OUString &)
RichStringRef getString(sal_Int32 nStringId) const
Returns the specified string.
void setErrorCell(const CellModel &rModel, const OUString &rErrorCode)
Inserts an error cell from the passed error code into the sheet.
void setBlankCell(const CellModel &rModel)
Inserts a blank cell (with formatting) into the sheet.
std::map< sal_Int32, std::vector< ValueRange > > maXfIdRowRangeList
True = maSharedFmlaAddr and maSharedBaseAddr are valid.
void createTableOperation(const ScRange &rRange, const DataTableModel &rModel)
Sets a multiple table operation to the passed range.
::std::map< BinAddress, ApiTokenSequence > maSharedFormulas
All table operations in the sheet.
std::vector< TableOperation > maTableOperations
All array formulas in the sheet.
ScAddress maSharedBaseAddr
Address of a cell containing a pending shared formula.
void setRowFormat(sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat)
Sets default cell formatting for the specified range of rows.
void setDateCell(const CellModel &rModel, const OUString &rDateString)
Inserts an ISO 8601 date cell into the sheet.
void setMergedRange(const ScRange &rRange)
Merges the cells in the passed cell range.
ScAddress maSharedFmlaAddr
Maps shared formula base address to defined name token index.
void finalizeArrayFormula(const ScRange &rRange, const ApiTokenSequence &rTokens) const
Inserts the passed array formula into the sheet.
SheetDataBuffer(const WorksheetHelper &rHelper)
void setCellFormat(const CellModel &rModel)
Processes the cell formatting data of the passed cell.
void finalizeTableOperation(const ScRange &rRange, const DataTableModel &rModel)
Inserts the passed table operation into the sheet.
void setValueCell(const CellModel &rModel, double fValue)
Inserts a value cell into the sheet.
void createSharedFormula(const ScAddress &rRange, const ApiTokenSequence &rTokens)
::std::pair< sal_Int32, sal_Int32 > XfIdNumFmtKey
void setCellFormula(const ScAddress &rCellAddr, const ApiTokenSequence &rTokens)
Sets the passed formula token array into a cell.
void setDateTimeCell(const CellModel &rModel, const css::util::DateTime &rDateTime)
Inserts a date/time cell into the sheet and adjusts number format.
void setFormulaCell(const CellModel &rModel, const ApiTokenSequence &rTokens)
Inserts a formula cell into the sheet.
void setBooleanCell(const CellModel &rModel, bool bValue)
Inserts a boolean cell into the sheet and adjusts number format.
void setStringCell(const CellModel &rModel, const OUString &rText)
Inserts a simple string cell into the sheet.
MergedRangeVector maCenterFillRanges
Merged cell ranges.
void applyCellMerging(const ScRange &rRange)
Writes all cell formatting attributes to the passed cell range list.
::std::vector< RowRangeStyle > TmpRowStyles
void finalizeImport()
Final processing after the sheet has been imported.
void createArrayFormula(const ScRange &rRange, const ApiTokenSequence &rTokens)
Inserts the passed token array as array formula.
bool mbPendingSharedFmla
Merged cell ranges from 'center across' or 'fill' alignment.
ArrayFormulaVector maArrayFormulas
Stores cell styles by column ( in row ranges )
XfIdRowRange maXfIdRowRange
Base address of the pending shared formula.
::std::map< sal_Int32, TmpRowStyles > TmpColStyles
MergedRangeVector maMergedRanges
Collected XF identifiers for cell rangelists.
std::map< XfIdNumFmtKey, ScRangeList > maXfIdRangeLists
Cached XF identifier for a range of rows.
ApiTokenSequence resolveSharedFormula(const ScAddress &rMapKey) const
Creates a formula token array representing the shared formula with the passed identifier.
FontRef getFontFromCellXf(sal_Int32 nXfId) const
Returns the font object of the specified cell XF.
XfRef getCellXf(sal_Int32 nXfId) const
Returns the specified cell format object.
double calcSerialFromDateTime(const css::util::DateTime &rDateTime) const
Returns the serial value of the passed datetime, based on current nulldate.
SharedStringsBuffer & getSharedStrings() const
Returns the shared strings read from the shared strings substream.
AddressConverter & getAddressConverter() const
Returns the converter for string to cell address/range conversion.
StylesBuffer & getStyles() const
Returns all cell formatting objects read from the styles substream.
FormulaBuffer & getFormulaBuffer() const
ScDocumentImport & getDocImport()
UnitConverter & getUnitConverter() const
Returns the measurement unit converter.
const css::uno::Reference< css::sheet::XSpreadsheetDocument > & getDocument() const
Returns a reference to the source/target spreadsheet document model.
FormulaParser & getFormulaParser() const
Returns a shared import formula parser (import filter only!).
SCTAB getSheetIndex() const
Returns the index of the current sheet.
void putRichString(const ScAddress &rAddress, RichString &rString, const oox::xls::Font *pFirstPortionFont, bool bSingleLine=false)
Inserts a rich-string cell directly into the Calc sheet.
void putFormulaTokens(const ScAddress &rAddress, const ApiTokenSequence &rTokens)
Inserts a formula cell directly into the Calc sheet.
css::uno::Reference< css::table::XCell > getCell(const ScAddress &rAddress) const
Returns the XCell interface for the passed cell address.
css::uno::Reference< css::table::XCellRange > getCellRange(const ScRange &rRange) const
Returns the XCellRange interface for the passed cell range address.
Represents a cell format or a cell style (called XF, extended format).
void applyPatternToAttrList(AttrList &rAttrs, SCROW nRow1, SCROW nRow2, sal_Int32 nXfId, sal_Int32 nForceScNumFmt, ScPatternCache &rCache)
const Alignment & getAlignment() const
Returns the alignment data of this style.
static bool parseDateTime(css::util::DateTime &rDateTime, std::u16string_view rString)
static bool isLatinScript(const ScPatternAttr &rPat, ScDocument &rDoc)
Check if the attribute pattern has a number format that only produces latin script output.
Definition: numformat.cxx:32
sal_Int32 nIndex
#define SAL_WARN(area, stream)
@ Exception
int i
std::pair< sal_Int32, sal_Int32 > FormatKeyPair
css::uno::Sequence< ApiToken > ApiTokenSequence
css::beans::Pair< css::table::CellAddress, sal_Bool > ApiSpecialTokenInfo
Contains the base address and type of a special token representing an array formula or a shared formu...
static void addIfNotInMyMap(const StylesBuffer &rStyles, std::map< FormatKeyPair, ScRangeList > &rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList &rRangeList)
static void lcl_SetBorderLine(ScDocument &rDoc, const ScRange &rRange, SCTAB nScTab, SvxBoxItemLine nLine)
std::shared_ptr< RichString > RichStringRef
Definition: richstring.hxx:263
XML_TOKEN_INVALID
constexpr TypedWhichId< ScMergeFlagAttr > ATTR_MERGE_FLAG(145)
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
SCROW nEndRow
Definition: attarray.hxx:83
const ScPatternAttr * pPattern
Definition: attarray.hxx:84
std::vector< ScAttrEntry > mvData
Parameter for data table aka multiple operations.
Definition: paramisc.hxx:46
ScRefAddress aRefFormulaEnd
Definition: paramisc.hxx:50
ScRefAddress aRefColCell
Definition: paramisc.hxx:52
ScRefAddress aRefFormulaCell
Definition: paramisc.hxx:49
ScRefAddress aRefRowCell
Definition: paramisc.hxx:51
sal_Int32 mnLast
bool mbWrapText
Indentation.
A 2D cell address struct for binary filters.
bool isValidArrayRef(const ScAddress &rCellAddr)
Returns true, if the passed cell address is valid for an array formula.
bool isValidSharedRef(const ScAddress &rCellAddr)
Returns true, if the passed cell address is valid for a shared formula.
CellFormulaModel()
Identifier of a shared formula (OOXML only).
Stores basic data about cell values and formatting.
CellModel()
True = show phonetic text.
sal_Int32 mnXfId
Data type of the cell value.
sal_Int32 mnCellType
The address of the current cell.
Stores data about table operations.
DataTableModel()
True = second reference cell deleted.
bool mbRef2Deleted
True = first reference cell deleted.
bool mb2dTable
Second reference cell for table operations.
OUString maRef2
First reference cell for table operations.
bool mbRowTable
True = 2-dimensional data table.
bool mbRef1Deleted
True = row oriented data table.
bool tryExpand(const ScAddress &rAddress, sal_Int32 nHorAlign)
MergedRange(const ScRange &rRange)
Horizontal alignment in the range.
void set(sal_Int32 nRow, sal_Int32 nXfId)
sal_Int32 mnXfId
Indexes of first and last row.
XfIdRowRange()
XF identifier for the row range.
bool tryExpand(sal_Int32 nRow, sal_Int32 nXfId)
std::vector< ScAttrEntry > maAttrs
unsigned char sal_uInt8
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17
constexpr OUStringLiteral PROP_NumberFormat