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