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