1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sc.hxx" 30 31 32 33 // INCLUDE --------------------------------------------------------------- 34 35 #include "externalrefmgr.hxx" 36 #include "document.hxx" 37 #include "token.hxx" 38 #include "tokenarray.hxx" 39 #include "address.hxx" 40 #include "tablink.hxx" 41 #include "docsh.hxx" 42 #include "scextopt.hxx" 43 #include "rangenam.hxx" 44 #include "cell.hxx" 45 #include "viewdata.hxx" 46 #include "tabvwsh.hxx" 47 #include "sc.hrc" 48 49 #include "sfx2/app.hxx" 50 #include "sfx2/docfilt.hxx" 51 #include "sfx2/docfile.hxx" 52 #include "sfx2/fcontnr.hxx" 53 #include "sfx2/sfxsids.hrc" 54 #include "sfx2/objsh.hxx" 55 #include "svl/broadcast.hxx" 56 #include "svl/smplhint.hxx" 57 #include "svl/itemset.hxx" 58 #include "svl/stritem.hxx" 59 #include "svl/urihelper.hxx" 60 #include "svl/zformat.hxx" 61 #include "sfx2/linkmgr.hxx" 62 #include "tools/urlobj.hxx" 63 #include "unotools/ucbhelper.hxx" 64 #include "unotools/localfilehelper.hxx" 65 66 #include <memory> 67 #include <algorithm> 68 69 #include <boost/scoped_ptr.hpp> 70 71 using ::std::auto_ptr; 72 using ::com::sun::star::uno::Any; 73 using ::rtl::OUString; 74 using ::std::vector; 75 using ::std::find; 76 using ::std::find_if; 77 using ::std::distance; 78 using ::std::pair; 79 using ::std::list; 80 using ::std::unary_function; 81 using namespace formula; 82 83 #define SRCDOC_LIFE_SPAN 6000 // 1 minute (in 100th of a sec) 84 #define SRCDOC_SCAN_INTERVAL 1000*5 // every 5 seconds (in msec) 85 86 namespace { 87 88 class TabNameSearchPredicate : public unary_function<bool, ScExternalRefCache::TableName> 89 { 90 public: 91 explicit TabNameSearchPredicate(const String& rSearchName) : 92 maSearchName(ScGlobal::pCharClass->upper(rSearchName)) 93 { 94 } 95 96 bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const 97 { 98 // Ok, I'm doing case insensitive search here. 99 return rTabNameSet.maUpperName.Equals(maSearchName); 100 } 101 102 private: 103 String maSearchName; 104 }; 105 106 class FindSrcFileByName : public unary_function<ScExternalRefManager::SrcFileData, bool> 107 { 108 public: 109 FindSrcFileByName(const String& rMatchName) : 110 mrMatchName(rMatchName) 111 { 112 } 113 114 bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const 115 { 116 return rSrcData.maFileName.Equals(mrMatchName); 117 } 118 119 private: 120 const String& mrMatchName; 121 }; 122 123 class NotifyLinkListener : public unary_function<ScExternalRefManager::LinkListener*, void> 124 { 125 public: 126 NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) : 127 mnFileId(nFileId), meType(eType) {} 128 129 NotifyLinkListener(const NotifyLinkListener& r) : 130 mnFileId(r.mnFileId), meType(r.meType) {} 131 132 void operator() (ScExternalRefManager::LinkListener* p) const 133 { 134 p->notify(mnFileId, meType); 135 } 136 private: 137 sal_uInt16 mnFileId; 138 ScExternalRefManager::LinkUpdateType meType; 139 }; 140 141 struct UpdateFormulaCell : public unary_function<ScFormulaCell*, void> 142 { 143 void operator() (ScFormulaCell* pCell) const 144 { 145 // Check to make sure the cell really contains ocExternalRef. 146 // External names, external cell and range references all have a 147 // ocExternalRef token. 148 const ScTokenArray* pCode = pCell->GetCode(); 149 if (!pCode->HasOpCode( ocExternalRef)) 150 return; 151 152 ScTokenArray* pArray = pCell->GetCode(); 153 if (pArray) 154 // Clear the error code, or a cell with error won't get re-compiled. 155 pArray->SetCodeError(0); 156 157 pCell->SetCompile(true); 158 pCell->CompileTokenArray(); 159 pCell->SetDirty(); 160 } 161 }; 162 163 class RemoveFormulaCell : public unary_function<pair<const sal_uInt16, ScExternalRefManager::RefCellSet>, void> 164 { 165 public: 166 explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {} 167 void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const 168 { 169 r.second.erase(mpCell); 170 } 171 private: 172 ScFormulaCell* mpCell; 173 }; 174 175 class ConvertFormulaToStatic : public unary_function<ScFormulaCell*, void> 176 { 177 public: 178 explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {} 179 void operator() (ScFormulaCell* pCell) const 180 { 181 ScAddress aPos = pCell->aPos; 182 183 // We don't check for empty cells because empty external cells are 184 // treated as having a value of 0. 185 186 if (pCell->IsValue()) 187 { 188 // Turn this into value cell. 189 double fVal = pCell->GetValue(); 190 mpDoc->PutCell(aPos, new ScValueCell(fVal)); 191 } 192 else 193 { 194 // string cell otherwise. 195 String aVal; 196 pCell->GetString(aVal); 197 mpDoc->PutCell(aPos, new ScStringCell(aVal)); 198 } 199 } 200 private: 201 ScDocument* mpDoc; 202 }; 203 204 } 205 206 // ============================================================================ 207 208 ScExternalRefCache::Table::Table() 209 : meReferenced( REFERENCED_MARKED ) 210 // Prevent accidental data loss due to lack of knowledge. 211 { 212 } 213 214 ScExternalRefCache::Table::~Table() 215 { 216 } 217 218 void ScExternalRefCache::Table::setReferencedFlag( ScExternalRefCache::Table::ReferencedFlag eFlag ) 219 { 220 meReferenced = eFlag; 221 } 222 223 void ScExternalRefCache::Table::setReferenced( bool bReferenced ) 224 { 225 if (meReferenced != REFERENCED_PERMANENT) 226 meReferenced = (bReferenced ? REFERENCED_MARKED : UNREFERENCED); 227 } 228 229 ScExternalRefCache::Table::ReferencedFlag ScExternalRefCache::Table::getReferencedFlag() const 230 { 231 return meReferenced; 232 } 233 234 bool ScExternalRefCache::Table::isReferenced() const 235 { 236 return meReferenced != UNREFERENCED; 237 } 238 239 void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef pToken, sal_uInt32 nFmtIndex, bool bSetCacheRange) 240 { 241 using ::std::pair; 242 RowsDataType::iterator itrRow = maRows.find(nRow); 243 if (itrRow == maRows.end()) 244 { 245 // This row does not exist yet. 246 pair<RowsDataType::iterator, bool> res = maRows.insert( 247 RowsDataType::value_type(nRow, RowDataType())); 248 249 if (!res.second) 250 return; 251 252 itrRow = res.first; 253 } 254 255 // Insert this token into the specified column location. I don't need to 256 // check for existing data. Just overwrite it. 257 RowDataType& rRow = itrRow->second; 258 ScExternalRefCache::Cell aCell; 259 aCell.mxToken = pToken; 260 aCell.mnFmtIndex = nFmtIndex; 261 rRow.insert(RowDataType::value_type(nCol, aCell)); 262 if (bSetCacheRange) 263 setCachedCell(nCol, nRow); 264 } 265 266 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const 267 { 268 RowsDataType::const_iterator itrTable = maRows.find(nRow); 269 if (itrTable == maRows.end()) 270 { 271 // this table doesn't have the specified row. 272 return getEmptyOrNullToken(nCol, nRow); 273 } 274 275 const RowDataType& rRowData = itrTable->second; 276 RowDataType::const_iterator itrRow = rRowData.find(nCol); 277 if (itrRow == rRowData.end()) 278 { 279 // this row doesn't have the specified column. 280 return getEmptyOrNullToken(nCol, nRow); 281 } 282 283 const Cell& rCell = itrRow->second; 284 if (pnFmtIndex) 285 *pnFmtIndex = rCell.mnFmtIndex; 286 287 return rCell.mxToken; 288 } 289 290 bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const 291 { 292 RowsDataType::const_iterator itrRow = maRows.find(nRow); 293 return itrRow != maRows.end(); 294 } 295 296 void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const 297 { 298 vector<SCROW> aRows; 299 aRows.reserve(maRows.size()); 300 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end(); 301 for (; itr != itrEnd; ++itr) 302 if (nLow <= itr->first && itr->first <= nHigh) 303 aRows.push_back(itr->first); 304 305 // hash map is not ordered, so we need to explicitly sort it. 306 ::std::sort(aRows.begin(), aRows.end()); 307 rRows.swap(aRows); 308 } 309 310 ::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const 311 { 312 ::std::pair< SCROW, SCROW > aRange( 0, 0 ); 313 if( !maRows.empty() ) 314 { 315 // iterate over entire container (hash map is not sorted by key) 316 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end(); 317 aRange.first = itr->first; 318 aRange.second = itr->first + 1; 319 while( ++itr != itrEnd ) 320 { 321 if( itr->first < aRange.first ) 322 aRange.first = itr->first; 323 else if( itr->first >= aRange.second ) 324 aRange.second = itr->first + 1; 325 } 326 } 327 return aRange; 328 } 329 330 void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const 331 { 332 RowsDataType::const_iterator itrRow = maRows.find(nRow); 333 if (itrRow == maRows.end()) 334 // this table doesn't have the specified row. 335 return; 336 337 const RowDataType& rRowData = itrRow->second; 338 vector<SCCOL> aCols; 339 aCols.reserve(rRowData.size()); 340 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end(); 341 for (; itrCol != itrColEnd; ++itrCol) 342 if (nLow <= itrCol->first && itrCol->first <= nHigh) 343 aCols.push_back(itrCol->first); 344 345 // hash map is not ordered, so we need to explicitly sort it. 346 ::std::sort(aCols.begin(), aCols.end()); 347 rCols.swap(aCols); 348 } 349 350 ::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const 351 { 352 ::std::pair< SCCOL, SCCOL > aRange( 0, 0 ); 353 354 RowsDataType::const_iterator itrRow = maRows.find( nRow ); 355 if (itrRow == maRows.end()) 356 // this table doesn't have the specified row. 357 return aRange; 358 359 const RowDataType& rRowData = itrRow->second; 360 if( !rRowData.empty() ) 361 { 362 // iterate over entire container (hash map is not sorted by key) 363 RowDataType::const_iterator itr = rRowData.begin(), itrEnd = rRowData.end(); 364 aRange.first = itr->first; 365 aRange.second = itr->first + 1; 366 while( ++itr != itrEnd ) 367 { 368 if( itr->first < aRange.first ) 369 aRange.first = itr->first; 370 else if( itr->first >= aRange.second ) 371 aRange.second = itr->first + 1; 372 } 373 } 374 return aRange; 375 } 376 377 void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const 378 { 379 RowsDataType::const_iterator itrRow = maRows.begin(), itrRowEnd = maRows.end(); 380 for (; itrRow != itrRowEnd; ++itrRow) 381 { 382 const RowDataType& rRowData = itrRow->second; 383 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end(); 384 for (; itrCol != itrColEnd; ++itrCol) 385 { 386 const Cell& rCell = itrCol->second; 387 rNumFmts.push_back(rCell.mnFmtIndex); 388 } 389 } 390 } 391 392 const ScRangeList& ScExternalRefCache::Table::getCachedRanges() const 393 { 394 return maCachedRanges; 395 } 396 397 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const 398 { 399 return maCachedRanges.In(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0)); 400 } 401 402 void ScExternalRefCache::Table::setCachedCell(SCCOL nCol, SCROW nRow) 403 { 404 setCachedCellRange(nCol, nRow, nCol, nRow); 405 } 406 407 void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) 408 { 409 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0); 410 if (!maCachedRanges.Count()) 411 maCachedRanges.Append(aRange); 412 else 413 maCachedRanges.Join(aRange); 414 415 String aStr; 416 maCachedRanges.Format(aStr, SCA_VALID); 417 } 418 419 void ScExternalRefCache::Table::setWholeTableCached() 420 { 421 setCachedCellRange(0, 0, MAXCOL, MAXROW); 422 } 423 424 bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol, SCROW nRow) const 425 { 426 return maCachedRanges.In(ScRange(nCol, nRow, 0, nCol, nRow, 0)); 427 } 428 429 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getEmptyOrNullToken( 430 SCCOL nCol, SCROW nRow) const 431 { 432 if (isInCachedRanges(nCol, nRow)) 433 { 434 TokenRef p(new ScEmptyCellToken(false, false)); 435 return p; 436 } 437 return TokenRef(); 438 } 439 440 // ---------------------------------------------------------------------------- 441 442 ScExternalRefCache::TableName::TableName(const String& rUpper, const String& rReal) : 443 maUpperName(rUpper), maRealName(rReal) 444 { 445 } 446 447 // ---------------------------------------------------------------------------- 448 449 ScExternalRefCache::CellFormat::CellFormat() : 450 mbIsSet(false), mnType(NUMBERFORMAT_ALL), mnIndex(0) 451 { 452 } 453 454 // ---------------------------------------------------------------------------- 455 456 ScExternalRefCache::ScExternalRefCache() 457 { 458 } 459 ScExternalRefCache::~ScExternalRefCache() 460 { 461 } 462 463 const String* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const String& rTabName) const 464 { 465 DocDataType::const_iterator itrDoc = maDocs.find(nFileId); 466 if (itrDoc == maDocs.end()) 467 { 468 // specified document is not cached. 469 return NULL; 470 } 471 472 const DocItem& rDoc = itrDoc->second; 473 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find( 474 ScGlobal::pCharClass->upper(rTabName)); 475 if (itrTabId == rDoc.maTableNameIndex.end()) 476 { 477 // the specified table is not in cache. 478 return NULL; 479 } 480 481 return &rDoc.maTableNames[itrTabId->second].maRealName; 482 } 483 484 const String* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const String& rRangeName) const 485 { 486 DocDataType::const_iterator itrDoc = maDocs.find(nFileId); 487 if (itrDoc == maDocs.end()) 488 { 489 // specified document is not cached. 490 return NULL; 491 } 492 493 const DocItem& rDoc = itrDoc->second; 494 NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find( 495 ScGlobal::pCharClass->upper(rRangeName)); 496 if (itr == rDoc.maRealRangeNameMap.end()) 497 // range name not found. 498 return NULL; 499 500 return &itr->second; 501 } 502 503 ScExternalRefCache::TokenRef ScExternalRefCache::getCellData( 504 sal_uInt16 nFileId, const String& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) 505 { 506 DocDataType::const_iterator itrDoc = maDocs.find(nFileId); 507 if (itrDoc == maDocs.end()) 508 { 509 // specified document is not cached. 510 return TokenRef(); 511 } 512 513 const DocItem& rDoc = itrDoc->second; 514 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find( 515 ScGlobal::pCharClass->upper(rTabName)); 516 if (itrTabId == rDoc.maTableNameIndex.end()) 517 { 518 // the specified table is not in cache. 519 return TokenRef(); 520 } 521 522 const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second]; 523 if (!pTableData.get()) 524 { 525 // the table data is not instantiated yet. 526 return TokenRef(); 527 } 528 529 return pTableData->getCell(nCol, nRow, pnFmtIndex); 530 } 531 532 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData( 533 sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange) 534 { 535 DocDataType::iterator itrDoc = maDocs.find(nFileId); 536 if (itrDoc == maDocs.end()) 537 // specified document is not cached. 538 return TokenArrayRef(); 539 540 DocItem& rDoc = itrDoc->second; 541 542 TableNameIndexMap::iterator itrTabId = rDoc.maTableNameIndex.find( 543 ScGlobal::pCharClass->upper(rTabName)); 544 if (itrTabId == rDoc.maTableNameIndex.end()) 545 // the specified table is not in cache. 546 return TokenArrayRef(); 547 548 const ScAddress& s = rRange.aStart; 549 const ScAddress& e = rRange.aEnd; 550 551 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); 552 SCCOL nCol1 = s.Col(), nCol2 = e.Col(); 553 SCROW nRow1 = s.Row(), nRow2 = e.Row(); 554 555 // Make sure I have all the tables cached. 556 size_t nTabFirstId = itrTabId->second; 557 size_t nTabLastId = nTabFirstId + nTab2 - nTab1; 558 if (nTabLastId >= rDoc.maTables.size()) 559 // not all tables are cached. 560 return TokenArrayRef(); 561 562 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId)); 563 564 RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange); 565 if (itrRange != rDoc.maRangeArrays.end()) 566 // Cache hit! 567 return itrRange->second; 568 569 ::boost::scoped_ptr<ScRange> pNewRange; 570 TokenArrayRef pArray; 571 bool bFirstTab = true; 572 for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab) 573 { 574 TableTypeRef pTab = rDoc.maTables[nTab]; 575 if (!pTab.get()) 576 return TokenArrayRef(); 577 578 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2; 579 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2; 580 581 if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2)) 582 { 583 // specified range is not entirely within cached ranges. 584 return TokenArrayRef(); 585 } 586 587 ScMatrixRef xMat = new ScMatrix( 588 static_cast<SCSIZE>(nDataCol2-nDataCol1+1), static_cast<SCSIZE>(nDataRow2-nDataRow1+1)); 589 590 #if 0 591 // TODO: Switch to this code block once we have support for sparsely-filled 592 // matrices in ScMatrix. 593 594 // Only fill non-empty cells, for better performance. 595 vector<SCROW> aRows; 596 pTab->getAllRows(aRows, nDataRow1, nDataRow2); 597 for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) 598 { 599 SCROW nRow = *itr; 600 vector<SCCOL> aCols; 601 pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2); 602 for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol) 603 { 604 SCCOL nCol = *itrCol; 605 TokenRef pToken = pTab->getCell(nCol, nRow); 606 if (!pToken) 607 // This should never happen! 608 return TokenArrayRef(); 609 610 SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1; 611 switch (pToken->GetType()) 612 { 613 case svDouble: 614 xMat->PutDouble(pToken->GetDouble(), nC, nR); 615 break; 616 case svString: 617 xMat->PutString(pToken->GetString(), nC, nR); 618 break; 619 default: 620 ; 621 } 622 } 623 } 624 #else 625 vector<SCROW> aRows; 626 pTab->getAllRows(aRows, nDataRow1, nDataRow2); 627 if (aRows.empty()) 628 // Cache is empty. 629 return TokenArrayRef(); 630 else 631 // Trim the column below the last non-empty row. 632 nDataRow2 = aRows.back(); 633 634 // Empty all matrix elements first, and fill only non-empty elements. 635 for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) 636 { 637 for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) 638 { 639 TokenRef pToken = pTab->getCell(nCol, nRow); 640 SCSIZE nC = nCol - nCol1, nR = nRow - nRow1; 641 if (!pToken) 642 return TokenArrayRef(); 643 644 switch (pToken->GetType()) 645 { 646 case svDouble: 647 xMat->PutDouble(pToken->GetDouble(), nC, nR); 648 break; 649 case svString: 650 xMat->PutString(pToken->GetString(), nC, nR); 651 break; 652 default: 653 xMat->PutEmpty(nC, nR); 654 } 655 } 656 } 657 #endif 658 659 if (!bFirstTab) 660 pArray->AddOpCode(ocSep); 661 662 ScMatrix* pMat2 = xMat; 663 ScMatrixToken aToken(pMat2); 664 if (!pArray) 665 pArray.reset(new ScTokenArray); 666 pArray->AddToken(aToken); 667 668 bFirstTab = false; 669 670 if (!pNewRange) 671 pNewRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 672 else 673 pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 674 } 675 676 if (pNewRange) 677 rDoc.maRangeArrays.insert( RangeArrayMap::value_type(*pNewRange, pArray)); 678 return pArray; 679 } 680 681 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const String& rName) 682 { 683 DocItem* pDoc = getDocItem(nFileId); 684 if (!pDoc) 685 return TokenArrayRef(); 686 687 RangeNameMap& rMap = pDoc->maRangeNames; 688 RangeNameMap::const_iterator itr = rMap.find( 689 ScGlobal::pCharClass->upper(rName)); 690 if (itr == rMap.end()) 691 return TokenArrayRef(); 692 693 return itr->second; 694 } 695 696 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const String& rName, TokenArrayRef pArray) 697 { 698 DocItem* pDoc = getDocItem(nFileId); 699 if (!pDoc) 700 return; 701 702 String aUpperName = ScGlobal::pCharClass->upper(rName); 703 RangeNameMap& rMap = pDoc->maRangeNames; 704 rMap.insert(RangeNameMap::value_type(aUpperName, pArray)); 705 pDoc->maRealRangeNameMap.insert(NamePairMap::value_type(aUpperName, rName)); 706 } 707 708 void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const String& rTabName, SCCOL nCol, SCROW nRow, 709 TokenRef pToken, sal_uInt32 nFmtIndex) 710 { 711 if (!isDocInitialized(nFileId)) 712 return; 713 714 using ::std::pair; 715 DocItem* pDocItem = getDocItem(nFileId); 716 if (!pDocItem) 717 return; 718 719 DocItem& rDoc = *pDocItem; 720 721 // See if the table by this name already exists. 722 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find( 723 ScGlobal::pCharClass->upper(rTabName)); 724 if (itrTabName == rDoc.maTableNameIndex.end()) 725 // Table not found. Maybe the table name or the file id is wrong ??? 726 return; 727 728 TableTypeRef& pTableData = rDoc.maTables[itrTabName->second]; 729 if (!pTableData.get()) 730 pTableData.reset(new Table); 731 732 pTableData->setCell(nCol, nRow, pToken, nFmtIndex); 733 pTableData->setCachedCell(nCol, nRow); 734 } 735 736 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData, 737 TokenArrayRef pArray) 738 { 739 using ::std::pair; 740 if (rData.empty() || !isDocInitialized(nFileId)) 741 // nothing to cache 742 return; 743 744 // First, get the document item for the given file ID. 745 DocItem* pDocItem = getDocItem(nFileId); 746 if (!pDocItem) 747 return; 748 749 DocItem& rDoc = *pDocItem; 750 751 // Now, find the table position of the first table to cache. 752 const String& rFirstTabName = rData.front().maTableName; 753 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find( 754 ScGlobal::pCharClass->upper(rFirstTabName)); 755 if (itrTabName == rDoc.maTableNameIndex.end()) 756 { 757 // table index not found. 758 return; 759 } 760 761 size_t nTabFirstId = itrTabName->second; 762 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row(); 763 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col(); 764 vector<SingleRangeData>::const_iterator itrDataBeg = rData.begin(), itrDataEnd = rData.end(); 765 for (vector<SingleRangeData>::const_iterator itrData = itrDataBeg; itrData != itrDataEnd; ++itrData) 766 { 767 size_t i = nTabFirstId + ::std::distance(itrDataBeg, itrData); 768 TableTypeRef& pTabData = rDoc.maTables[i]; 769 if (!pTabData.get()) 770 pTabData.reset(new Table); 771 772 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 773 { 774 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 775 { 776 SCSIZE nC = nCol - nCol1, nR = nRow - nRow1; 777 TokenRef pToken; 778 const ScMatrixRef& pMat = itrData->mpRangeData; 779 if (pMat->IsEmpty(nC, nR)) 780 // Don't cache empty cells. 781 continue; 782 783 if (pMat->IsValue(nC, nR)) 784 pToken.reset(new formula::FormulaDoubleToken(pMat->GetDouble(nC, nR))); 785 else if (pMat->IsString(nC, nR)) 786 pToken.reset(new formula::FormulaStringToken(pMat->GetString(nC, nR))); 787 788 if (pToken) 789 // Don't mark this cell 'cached' here, for better performance. 790 pTabData->setCell(nCol, nRow, pToken, 0, false); 791 } 792 } 793 // Mark the whole range 'cached'. 794 pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2); 795 } 796 797 size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab(); 798 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId)); 799 800 rDoc.maRangeArrays.insert( RangeArrayMap::value_type( aCacheRange, pArray)); 801 } 802 803 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId) 804 { 805 DocItem* pDoc = getDocItem(nFileId); 806 if (!pDoc) 807 return false; 808 809 return pDoc->mbInitFromSource; 810 } 811 812 static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const String& rName, size_t& rIndex) 813 { 814 ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName); 815 if (itr == rMap.end()) 816 return false; 817 818 rIndex = itr->second; 819 return true; 820 } 821 822 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<String>& rTabNames) 823 { 824 DocItem* pDoc = getDocItem(nFileId); 825 if (!pDoc) 826 return; 827 828 size_t n = rTabNames.size(); 829 830 // table name list - the list must include all table names in the source 831 // document and only to be populated when loading the source document, not 832 // when loading cached data from, say, Excel XCT/CRN records. 833 vector<TableName> aNewTabNames; 834 aNewTabNames.reserve(n); 835 for (vector<String>::const_iterator itr = rTabNames.begin(), itrEnd = rTabNames.end(); 836 itr != itrEnd; ++itr) 837 { 838 TableName aNameItem(ScGlobal::pCharClass->upper(*itr), *itr); 839 aNewTabNames.push_back(aNameItem); 840 } 841 pDoc->maTableNames.swap(aNewTabNames); 842 843 // data tables - preserve any existing data that may have been set during 844 // file import. 845 vector<TableTypeRef> aNewTables(n); 846 for (size_t i = 0; i < n; ++i) 847 { 848 size_t nIndex; 849 if (lcl_getTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex)) 850 { 851 aNewTables[i] = pDoc->maTables[nIndex]; 852 } 853 } 854 pDoc->maTables.swap(aNewTables); 855 856 // name index map 857 TableNameIndexMap aNewNameIndex; 858 for (size_t i = 0; i < n; ++i) 859 aNewNameIndex.insert(TableNameIndexMap::value_type(pDoc->maTableNames[i].maUpperName, i)); 860 pDoc->maTableNameIndex.swap(aNewNameIndex); 861 862 pDoc->mbInitFromSource = true; 863 } 864 865 String ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const 866 { 867 if( DocItem* pDoc = getDocItem( nFileId ) ) 868 if( nCacheId < pDoc->maTableNames.size() ) 869 return pDoc->maTableNames[ nCacheId ].maRealName; 870 return EMPTY_STRING; 871 } 872 873 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<String>& rTabNames) const 874 { 875 rTabNames.clear(); 876 DocItem* pDoc = getDocItem(nFileId); 877 if (!pDoc) 878 return; 879 880 size_t n = pDoc->maTableNames.size(); 881 rTabNames.reserve(n); 882 for (vector<TableName>::const_iterator itr = pDoc->maTableNames.begin(), itrEnd = pDoc->maTableNames.end(); 883 itr != itrEnd; ++itr) 884 rTabNames.push_back(itr->maRealName); 885 } 886 887 SCsTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const String& rStartTabName, const String& rEndTabName ) const 888 { 889 DocItem* pDoc = getDocItem(nFileId); 890 if (!pDoc) 891 return -1; 892 893 vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin(); 894 vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end(); 895 896 vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd, 897 TabNameSearchPredicate( rStartTabName)); 898 if (itrStartTab == itrEnd) 899 return -1; 900 901 vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd, 902 TabNameSearchPredicate( rEndTabName)); 903 if (itrEndTab == itrEnd) 904 return 0; 905 906 size_t nStartDist = ::std::distance( itrBeg, itrStartTab); 907 size_t nEndDist = ::std::distance( itrBeg, itrEndTab); 908 return nStartDist <= nEndDist ? static_cast<SCsTAB>(nEndDist - nStartDist + 1) : -static_cast<SCsTAB>(nStartDist - nEndDist + 1); 909 } 910 911 void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const 912 { 913 using ::std::sort; 914 using ::std::unique; 915 916 vector<sal_uInt32> aNumFmts; 917 for (DocDataType::const_iterator itrDoc = maDocs.begin(), itrDocEnd = maDocs.end(); 918 itrDoc != itrDocEnd; ++itrDoc) 919 { 920 const vector<TableTypeRef>& rTables = itrDoc->second.maTables; 921 for (vector<TableTypeRef>::const_iterator itrTab = rTables.begin(), itrTabEnd = rTables.end(); 922 itrTab != itrTabEnd; ++itrTab) 923 { 924 TableTypeRef pTab = *itrTab; 925 if (!pTab) 926 continue; 927 928 pTab->getAllNumberFormats(aNumFmts); 929 } 930 } 931 932 // remove duplicates. 933 sort(aNumFmts.begin(), aNumFmts.end()); 934 aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end()); 935 rNumFmts.swap(aNumFmts); 936 } 937 938 bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId ) 939 { 940 DocItem* pDocItem = getDocItem(nFileId); 941 if (!pDocItem) 942 return areAllCacheTablesReferenced(); 943 944 for (::std::vector<TableTypeRef>::iterator itrTab = pDocItem->maTables.begin(); 945 itrTab != pDocItem->maTables.end(); ++itrTab) 946 { 947 if ((*itrTab).get()) 948 (*itrTab)->setReferenced( true); 949 } 950 addCacheDocToReferenced( nFileId); 951 return areAllCacheTablesReferenced(); 952 } 953 954 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const String& rTabName, size_t nSheets, bool bPermanent ) 955 { 956 DocItem* pDoc = getDocItem(nFileId); 957 if (pDoc) 958 { 959 size_t nIndex = 0; 960 String aTabNameUpper = ScGlobal::pCharClass->upper( rTabName); 961 if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex)) 962 { 963 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size()); 964 for (size_t i = nIndex; i < nStop; ++i) 965 { 966 TableTypeRef pTab = pDoc->maTables[i]; 967 if (pTab.get()) 968 { 969 Table::ReferencedFlag eNewFlag = (bPermanent ? 970 Table::REFERENCED_PERMANENT : 971 Table::REFERENCED_MARKED); 972 Table::ReferencedFlag eOldFlag = pTab->getReferencedFlag(); 973 if (eOldFlag != Table::REFERENCED_PERMANENT && eNewFlag != eOldFlag) 974 { 975 pTab->setReferencedFlag( eNewFlag); 976 addCacheTableToReferenced( nFileId, i); 977 } 978 } 979 } 980 } 981 } 982 return areAllCacheTablesReferenced(); 983 } 984 985 void ScExternalRefCache::setCacheTableReferencedPermanently( sal_uInt16 nFileId, const String& rTabName, size_t nSheets ) 986 { 987 DocItem* pDoc = getDocItem(nFileId); 988 if (pDoc) 989 { 990 size_t nIndex = 0; 991 String aTabNameUpper = ScGlobal::pCharClass->upper( rTabName); 992 if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex)) 993 { 994 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size()); 995 for (size_t i = nIndex; i < nStop; ++i) 996 { 997 TableTypeRef pTab = pDoc->maTables[i]; 998 if (pTab.get()) 999 pTab->setReferencedFlag( Table::REFERENCED_PERMANENT); 1000 } 1001 } 1002 } 1003 } 1004 1005 void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced ) 1006 { 1007 if (bReferenced) 1008 { 1009 maReferenced.reset(0); 1010 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc) 1011 { 1012 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second; 1013 for (::std::vector<TableTypeRef>::iterator itrTab = rDocItem.maTables.begin(); 1014 itrTab != rDocItem.maTables.end(); ++itrTab) 1015 { 1016 if ((*itrTab).get()) 1017 (*itrTab)->setReferenced( true); 1018 } 1019 } 1020 } 1021 else 1022 { 1023 size_t nDocs = 0; 1024 for (DocDataType::const_iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc) 1025 { 1026 if (nDocs <= (*itrDoc).first) 1027 nDocs = (*itrDoc).first + 1; 1028 } 1029 maReferenced.reset( nDocs); 1030 1031 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc) 1032 { 1033 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second; 1034 sal_uInt16 nFileId = (*itrDoc).first; 1035 size_t nTables = rDocItem.maTables.size(); 1036 ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId]; 1037 // All referenced => non-existing tables evaluate as completed. 1038 rDocReferenced.maTables.resize( nTables, true); 1039 for (size_t i=0; i < nTables; ++i) 1040 { 1041 TableTypeRef & xTab = rDocItem.maTables[i]; 1042 if (xTab.get()) 1043 { 1044 if (xTab->getReferencedFlag() == Table::REFERENCED_PERMANENT) 1045 addCacheTableToReferenced( nFileId, i); 1046 else 1047 { 1048 xTab->setReferencedFlag( Table::UNREFERENCED); 1049 rDocReferenced.maTables[i] = false; 1050 rDocReferenced.mbAllTablesReferenced = false; 1051 // An addCacheTableToReferenced() actually may have 1052 // resulted in mbAllReferenced been set. Clear it. 1053 maReferenced.mbAllReferenced = false; 1054 } 1055 } 1056 } 1057 } 1058 } 1059 } 1060 1061 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex ) 1062 { 1063 if (nFileId >= maReferenced.maDocs.size()) 1064 return; 1065 1066 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables; 1067 size_t nTables = rTables.size(); 1068 if (nIndex >= nTables) 1069 return; 1070 1071 if (!rTables[nIndex]) 1072 { 1073 rTables[nIndex] = true; 1074 size_t i = 0; 1075 while (i < nTables && rTables[i]) 1076 ++i; 1077 if (i == nTables) 1078 { 1079 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true; 1080 maReferenced.checkAllDocs(); 1081 } 1082 } 1083 } 1084 1085 void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId ) 1086 { 1087 if (nFileId >= maReferenced.maDocs.size()) 1088 return; 1089 1090 if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced) 1091 { 1092 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables; 1093 size_t nSize = rTables.size(); 1094 for (size_t i=0; i < nSize; ++i) 1095 rTables[i] = true; 1096 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true; 1097 maReferenced.checkAllDocs(); 1098 } 1099 } 1100 1101 bool ScExternalRefCache::areAllCacheTablesReferenced() const 1102 { 1103 return maReferenced.mbAllReferenced; 1104 } 1105 1106 ScExternalRefCache::ReferencedStatus::ReferencedStatus() : 1107 mbAllReferenced(false) 1108 { 1109 reset(0); 1110 } 1111 1112 ScExternalRefCache::ReferencedStatus::ReferencedStatus( size_t nDocs ) : 1113 mbAllReferenced(false) 1114 { 1115 reset( nDocs); 1116 } 1117 1118 void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs ) 1119 { 1120 if (nDocs) 1121 { 1122 mbAllReferenced = false; 1123 DocReferencedVec aRefs( nDocs); 1124 maDocs.swap( aRefs); 1125 } 1126 else 1127 { 1128 mbAllReferenced = true; 1129 DocReferencedVec aRefs; 1130 maDocs.swap( aRefs); 1131 } 1132 } 1133 1134 void ScExternalRefCache::ReferencedStatus::checkAllDocs() 1135 { 1136 for (DocReferencedVec::const_iterator itr = maDocs.begin(); itr != maDocs.end(); ++itr) 1137 { 1138 if (!(*itr).mbAllTablesReferenced) 1139 return; 1140 } 1141 mbAllReferenced = true; 1142 } 1143 1144 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const 1145 { 1146 DocItem* pDoc = getDocItem(nFileId); 1147 if (!pDoc || nTabIndex >= pDoc->maTables.size()) 1148 return TableTypeRef(); 1149 1150 return pDoc->maTables[nTabIndex]; 1151 } 1152 1153 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const String& rTabName, bool bCreateNew, size_t* pnIndex) 1154 { 1155 // In API, the index is transported as cached sheet ID of type sal_Int32 in 1156 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet 1157 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively 1158 // being 0xffffffff 1159 const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1)); 1160 1161 DocItem* pDoc = getDocItem(nFileId); 1162 if (!pDoc) 1163 { 1164 if (pnIndex) *pnIndex = nNotAvailable; 1165 return TableTypeRef(); 1166 } 1167 1168 DocItem& rDoc = *pDoc; 1169 1170 size_t nIndex; 1171 String aTabNameUpper = ScGlobal::pCharClass->upper(rTabName); 1172 if (lcl_getTableDataIndex(rDoc.maTableNameIndex, aTabNameUpper, nIndex)) 1173 { 1174 // specified table found. 1175 if( pnIndex ) *pnIndex = nIndex; 1176 if (bCreateNew && !rDoc.maTables[nIndex]) 1177 rDoc.maTables[nIndex].reset(new Table); 1178 1179 return rDoc.maTables[nIndex]; 1180 } 1181 1182 if (!bCreateNew) 1183 { 1184 if (pnIndex) *pnIndex = nNotAvailable; 1185 return TableTypeRef(); 1186 } 1187 1188 // Specified table doesn't exist yet. Create one. 1189 nIndex = rDoc.maTables.size(); 1190 if( pnIndex ) *pnIndex = nIndex; 1191 TableTypeRef pTab(new Table); 1192 rDoc.maTables.push_back(pTab); 1193 rDoc.maTableNames.push_back(TableName(aTabNameUpper, rTabName)); 1194 rDoc.maTableNameIndex.insert( 1195 TableNameIndexMap::value_type(aTabNameUpper, nIndex)); 1196 return pTab; 1197 } 1198 1199 void ScExternalRefCache::clearCache(sal_uInt16 nFileId) 1200 { 1201 maDocs.erase(nFileId); 1202 } 1203 1204 ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const 1205 { 1206 using ::std::pair; 1207 DocDataType::iterator itrDoc = maDocs.find(nFileId); 1208 if (itrDoc == maDocs.end()) 1209 { 1210 // specified document is not cached. 1211 pair<DocDataType::iterator, bool> res = maDocs.insert( 1212 DocDataType::value_type(nFileId, DocItem())); 1213 1214 if (!res.second) 1215 // insertion failed. 1216 return NULL; 1217 1218 itrDoc = res.first; 1219 } 1220 1221 return &itrDoc->second; 1222 } 1223 1224 // ============================================================================ 1225 1226 ScExternalRefLink::ScExternalRefLink(ScDocument* pDoc, sal_uInt16 nFileId, const String& rFilter) : 1227 ::sfx2::SvBaseLink(::sfx2::LINKUPDATE_ONCALL, FORMAT_FILE), 1228 mnFileId(nFileId), 1229 maFilterName(rFilter), 1230 mpDoc(pDoc), 1231 mbDoRefresh(true) 1232 { 1233 } 1234 1235 ScExternalRefLink::~ScExternalRefLink() 1236 { 1237 } 1238 1239 void ScExternalRefLink::Closed() 1240 { 1241 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager(); 1242 pMgr->breakLink(mnFileId); 1243 } 1244 1245 void ScExternalRefLink::DataChanged(const String& /*rMimeType*/, const Any& /*rValue*/) 1246 { 1247 if (!mbDoRefresh) 1248 return; 1249 1250 String aFile, aFilter; 1251 mpDoc->GetLinkManager()->GetDisplayNames(this, NULL, &aFile, NULL, &aFilter); 1252 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager(); 1253 const String* pCurFile = pMgr->getExternalFileName(mnFileId); 1254 if (!pCurFile) 1255 return; 1256 1257 if (pCurFile->Equals(aFile)) 1258 { 1259 // Refresh the current source document. 1260 pMgr->refreshNames(mnFileId); 1261 } 1262 else 1263 { 1264 // The source document has changed. 1265 ScDocShell* pDocShell = ScDocShell::GetViewData()->GetDocShell(); 1266 ScDocShellModificator aMod(*pDocShell); 1267 pMgr->switchSrcFile(mnFileId, aFile, aFilter); 1268 maFilterName = aFilter; 1269 aMod.SetDocumentModified(); 1270 } 1271 } 1272 1273 void ScExternalRefLink::Edit(Window* pParent, const Link& /*rEndEditHdl*/) 1274 { 1275 SvBaseLink::Edit(pParent, LINK(this, ScExternalRefLink, ExternalRefEndEditHdl)); 1276 } 1277 1278 void ScExternalRefLink::SetDoReferesh(bool b) 1279 { 1280 mbDoRefresh = b; 1281 } 1282 1283 IMPL_LINK( ScExternalRefLink, ExternalRefEndEditHdl, ::sfx2::SvBaseLink*, EMPTYARG ) 1284 { 1285 return 0; 1286 } 1287 1288 // ============================================================================ 1289 1290 static FormulaToken* lcl_convertToToken(ScBaseCell* pCell) 1291 { 1292 if (!pCell || pCell->HasEmptyData()) 1293 { 1294 bool bInherited = (pCell && pCell->GetCellType() == CELLTYPE_FORMULA); 1295 return new ScEmptyCellToken( bInherited, false); 1296 } 1297 1298 switch (pCell->GetCellType()) 1299 { 1300 case CELLTYPE_EDIT: 1301 { 1302 String aStr; 1303 static_cast<ScEditCell*>(pCell)->GetString(aStr); 1304 return new formula::FormulaStringToken(aStr); 1305 } 1306 //break; 1307 case CELLTYPE_STRING: 1308 { 1309 String aStr; 1310 static_cast<ScStringCell*>(pCell)->GetString(aStr); 1311 return new formula::FormulaStringToken(aStr); 1312 } 1313 //break; 1314 case CELLTYPE_VALUE: 1315 { 1316 double fVal = static_cast<ScValueCell*>(pCell)->GetValue(); 1317 return new formula::FormulaDoubleToken(fVal); 1318 } 1319 //break; 1320 case CELLTYPE_FORMULA: 1321 { 1322 ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); 1323 sal_uInt16 nError = pFCell->GetErrCode(); 1324 if (nError) 1325 return new FormulaErrorToken( nError); 1326 else if (pFCell->IsValue()) 1327 { 1328 double fVal = pFCell->GetValue(); 1329 return new formula::FormulaDoubleToken(fVal); 1330 } 1331 else 1332 { 1333 String aStr; 1334 pFCell->GetString(aStr); 1335 return new formula::FormulaStringToken(aStr); 1336 } 1337 } 1338 //break; 1339 default: 1340 DBG_ERROR("attempted to convert an unknown cell type."); 1341 } 1342 1343 return NULL; 1344 } 1345 1346 static ScTokenArray* lcl_convertToTokenArray(ScDocument* pSrcDoc, ScRange& rRange, 1347 vector<ScExternalRefCache::SingleRangeData>& rCacheData) 1348 { 1349 ScAddress& s = rRange.aStart; 1350 ScAddress& e = rRange.aEnd; 1351 1352 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); 1353 SCCOL nCol1 = s.Col(), nCol2 = e.Col(); 1354 SCROW nRow1 = s.Row(), nRow2 = e.Row(); 1355 1356 if (nTab2 != nTab1) 1357 // For now, we don't support multi-sheet ranges intentionally because 1358 // we don't have a way to express them in a single token. In the 1359 // future we can introduce a new stack variable type svMatrixList with 1360 // a new token type that can store a 3D matrix value and convert a 3D 1361 // range to it. 1362 return NULL; 1363 1364 ::boost::scoped_ptr<ScRange> pUsedRange; 1365 1366 auto_ptr<ScTokenArray> pArray(new ScTokenArray); 1367 bool bFirstTab = true; 1368 vector<ScExternalRefCache::SingleRangeData>::iterator 1369 itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end(); 1370 1371 for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache) 1372 { 1373 // Only loop within the data area. 1374 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2; 1375 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2; 1376 bool bShrunk; 1377 if (!pSrcDoc->ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false)) 1378 // no data within specified range. 1379 continue; 1380 1381 if (pUsedRange.get()) 1382 // Make sure the used area only grows, not shrinks. 1383 pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 1384 else 1385 pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 1386 1387 ScMatrixRef xMat = new ScMatrix( 1388 static_cast<SCSIZE>(nDataCol2-nDataCol1+1), 1389 static_cast<SCSIZE>(nDataRow2-nDataRow1+1)); 1390 1391 for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) 1392 { 1393 for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) 1394 { 1395 SCSIZE nC = nCol - nCol1, nR = nRow - nRow1; 1396 ScBaseCell* pCell; 1397 pSrcDoc->GetCell(nCol, nRow, nTab, pCell); 1398 if (!pCell || pCell->HasEmptyData()) 1399 xMat->PutEmpty(nC, nR); 1400 else 1401 { 1402 switch (pCell->GetCellType()) 1403 { 1404 case CELLTYPE_EDIT: 1405 { 1406 String aStr; 1407 static_cast<ScEditCell*>(pCell)->GetString(aStr); 1408 xMat->PutString(aStr, nC, nR); 1409 } 1410 break; 1411 case CELLTYPE_STRING: 1412 { 1413 String aStr; 1414 static_cast<ScStringCell*>(pCell)->GetString(aStr); 1415 xMat->PutString(aStr, nC, nR); 1416 } 1417 break; 1418 case CELLTYPE_VALUE: 1419 { 1420 double fVal = static_cast<ScValueCell*>(pCell)->GetValue(); 1421 xMat->PutDouble(fVal, nC, nR); 1422 } 1423 break; 1424 case CELLTYPE_FORMULA: 1425 { 1426 ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); 1427 sal_uInt16 nError = pFCell->GetErrCode(); 1428 if (nError) 1429 xMat->PutDouble( CreateDoubleError( nError), nC, nR); 1430 else if (pFCell->IsValue()) 1431 { 1432 double fVal = pFCell->GetValue(); 1433 xMat->PutDouble(fVal, nC, nR); 1434 } 1435 else 1436 { 1437 String aStr; 1438 pFCell->GetString(aStr); 1439 xMat->PutString(aStr, nC, nR); 1440 } 1441 } 1442 break; 1443 default: 1444 DBG_ERROR("attempted to convert an unknown cell type."); 1445 } 1446 } 1447 } 1448 } 1449 if (!bFirstTab) 1450 pArray->AddOpCode(ocSep); 1451 1452 ScMatrix* pMat2 = xMat; 1453 ScMatrixToken aToken(pMat2); 1454 pArray->AddToken(aToken); 1455 1456 itrCache->mpRangeData = xMat; 1457 1458 bFirstTab = false; 1459 } 1460 1461 if (!pUsedRange.get()) 1462 return NULL; 1463 1464 s.SetCol(pUsedRange->aStart.Col()); 1465 s.SetRow(pUsedRange->aStart.Row()); 1466 e.SetCol(pUsedRange->aEnd.Col()); 1467 e.SetRow(pUsedRange->aEnd.Row()); 1468 1469 return pArray.release(); 1470 } 1471 1472 static ScTokenArray* lcl_fillEmptyMatrix(const ScRange& rRange) 1473 { 1474 SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1); 1475 SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1); 1476 ScMatrixRef xMat = new ScMatrix(nC, nR); 1477 for (SCSIZE i = 0; i < nC; ++i) 1478 for (SCSIZE j = 0; j < nR; ++j) 1479 xMat->PutEmpty(i, j); 1480 1481 ScMatrix* pMat2 = xMat; 1482 ScMatrixToken aToken(pMat2); 1483 auto_ptr<ScTokenArray> pArray(new ScTokenArray); 1484 pArray->AddToken(aToken); 1485 return pArray.release(); 1486 } 1487 1488 ScExternalRefManager::ScExternalRefManager(ScDocument* pDoc) : 1489 mpDoc(pDoc), 1490 mbInReferenceMarking(false), 1491 mbUserInteractionEnabled(true) 1492 { 1493 maSrcDocTimer.SetTimeoutHdl( LINK(this, ScExternalRefManager, TimeOutHdl) ); 1494 maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL); 1495 } 1496 1497 ScExternalRefManager::~ScExternalRefManager() 1498 { 1499 clear(); 1500 } 1501 1502 String ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const 1503 { 1504 return maRefCache.getTableName(nFileId, nTabIndex); 1505 } 1506 1507 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const 1508 { 1509 return maRefCache.getCacheTable(nFileId, nTabIndex); 1510 } 1511 1512 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, const String& rTabName, bool bCreateNew, size_t* pnIndex) 1513 { 1514 return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex); 1515 } 1516 1517 // ============================================================================ 1518 1519 ScExternalRefManager::LinkListener::LinkListener() 1520 { 1521 } 1522 1523 ScExternalRefManager::LinkListener::~LinkListener() 1524 { 1525 } 1526 1527 // ---------------------------------------------------------------------------- 1528 1529 ScExternalRefManager::ApiGuard::ApiGuard(ScDocument* pDoc) : 1530 mpMgr(pDoc->GetExternalRefManager()), 1531 mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled) 1532 { 1533 // We don't want user interaction handled in the API. 1534 mpMgr->mbUserInteractionEnabled = false; 1535 } 1536 1537 ScExternalRefManager::ApiGuard::~ApiGuard() 1538 { 1539 // Restore old value. 1540 mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled; 1541 } 1542 1543 // ---------------------------------------------------------------------------- 1544 1545 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<String>& rTabNames) const 1546 { 1547 maRefCache.getAllTableNames(nFileId, rTabNames); 1548 } 1549 1550 SCsTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const String& rStartTabName, const String& rEndTabName ) const 1551 { 1552 return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName); 1553 } 1554 1555 void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const 1556 { 1557 maRefCache.getAllNumberFormats(rNumFmts); 1558 } 1559 1560 sal_uInt16 ScExternalRefManager::getExternalFileCount() const 1561 { 1562 return static_cast< sal_uInt16 >( maSrcFiles.size() ); 1563 } 1564 1565 bool ScExternalRefManager::markUsedByLinkListeners() 1566 { 1567 bool bAllMarked = false; 1568 for (LinkListenerMap::const_iterator itr = maLinkListeners.begin(); 1569 itr != maLinkListeners.end() && !bAllMarked; ++itr) 1570 { 1571 if (!(*itr).second.empty()) 1572 bAllMarked = maRefCache.setCacheDocReferenced( (*itr).first); 1573 /* TODO: LinkListeners should remember the table they're listening to. 1574 * As is, listening to one table will mark all tables of the document 1575 * being referenced. */ 1576 } 1577 return bAllMarked; 1578 } 1579 1580 bool ScExternalRefManager::markUsedExternalRefCells() 1581 { 1582 RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end(); 1583 for (; itr != itrEnd; ++itr) 1584 { 1585 RefCellSet::iterator itrCell = itr->second.begin(), itrCellEnd = itr->second.end(); 1586 for (; itrCell != itrCellEnd; ++itrCell) 1587 { 1588 ScFormulaCell* pCell = *itrCell; 1589 bool bUsed = pCell->MarkUsedExternalReferences(); 1590 if (bUsed) 1591 // Return true when at least one cell references external docs. 1592 return true; 1593 } 1594 } 1595 return false; 1596 } 1597 1598 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const String& rTabName, size_t nSheets ) 1599 { 1600 return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, false); 1601 } 1602 1603 void ScExternalRefManager::setCacheTableReferencedPermanently( sal_uInt16 nFileId, const String& rTabName, size_t nSheets ) 1604 { 1605 if (isInReferenceMarking()) 1606 // Do all maintenance work. 1607 maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, true); 1608 else 1609 // Set only the permanent flag. 1610 maRefCache.setCacheTableReferencedPermanently( nFileId, rTabName, nSheets); 1611 } 1612 1613 void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced ) 1614 { 1615 mbInReferenceMarking = !bReferenced; 1616 maRefCache.setAllCacheTableReferencedStati( bReferenced ); 1617 } 1618 1619 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const String& rName, const ScTokenArray& rArray) 1620 { 1621 ScExternalRefCache::TokenArrayRef pArray(rArray.Clone()); 1622 maRefCache.setRangeNameTokens(nFileId, rName, pArray); 1623 } 1624 1625 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken( 1626 sal_uInt16 nFileId, const String& rTabName, const ScAddress& rCell, 1627 const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt) 1628 { 1629 if (pCurPos) 1630 insertRefCell(nFileId, *pCurPos); 1631 1632 maybeLinkExternalFile(nFileId); 1633 1634 if (pTab) 1635 *pTab = -1; 1636 1637 if (pFmt) 1638 pFmt->mbIsSet = false; 1639 1640 // Check if the given table name and the cell position is cached. 1641 sal_uInt32 nFmtIndex = 0; 1642 ScExternalRefCache::TokenRef pToken = maRefCache.getCellData( 1643 nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex); 1644 if (pToken) 1645 { 1646 // Cache hit ! 1647 if (pFmt) 1648 { 1649 short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex); 1650 if (nFmtType != NUMBERFORMAT_UNDEFINED) 1651 { 1652 pFmt->mbIsSet = true; 1653 pFmt->mnIndex = nFmtIndex; 1654 pFmt->mnType = nFmtType; 1655 } 1656 } 1657 return pToken; 1658 } 1659 1660 // reference not cached. read from the source document. 1661 ScDocument* pSrcDoc = getSrcDocument(nFileId); 1662 if (!pSrcDoc) 1663 { 1664 // Source document not reachable. Throw a reference error. 1665 pToken.reset(new FormulaErrorToken(errNoRef)); 1666 return pToken; 1667 } 1668 1669 ScBaseCell* pCell = NULL; 1670 SCTAB nTab; 1671 if (!pSrcDoc->GetTable(rTabName, nTab)) 1672 { 1673 // specified table name doesn't exist in the source document. 1674 pToken.reset(new FormulaErrorToken(errNoRef)); 1675 return pToken; 1676 } 1677 1678 if (pTab) 1679 *pTab = nTab; 1680 1681 SCCOL nDataCol1 = 0, nDataCol2 = MAXCOL; 1682 SCROW nDataRow1 = 0, nDataRow2 = MAXROW; 1683 bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2); 1684 if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row()) 1685 { 1686 // requested cell is outside the data area. Don't even bother caching 1687 // this data, but add it to the cached range to prevent accessing the 1688 // source document time and time again. 1689 ScExternalRefCache::TableTypeRef pCacheTab = 1690 maRefCache.getCacheTable(nFileId, rTabName, true, NULL); 1691 if (pCacheTab) 1692 pCacheTab->setCachedCell(rCell.Col(), rCell.Row()); 1693 1694 pToken.reset(new ScEmptyCellToken(false, false)); 1695 return pToken; 1696 } 1697 1698 pSrcDoc->GetCell(rCell.Col(), rCell.Row(), nTab, pCell); 1699 ScExternalRefCache::TokenRef pTok(lcl_convertToToken(pCell)); 1700 1701 pSrcDoc->GetNumberFormat(rCell.Col(), rCell.Row(), nTab, nFmtIndex); 1702 nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, pSrcDoc); 1703 if (pFmt) 1704 { 1705 short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex); 1706 if (nFmtType != NUMBERFORMAT_UNDEFINED) 1707 { 1708 pFmt->mbIsSet = true; 1709 pFmt->mnIndex = nFmtIndex; 1710 pFmt->mnType = nFmtType; 1711 } 1712 } 1713 1714 if (!pTok.get()) 1715 { 1716 // Generate an error for unresolvable cells. 1717 pTok.reset( new FormulaErrorToken( errNoValue)); 1718 } 1719 1720 // Now, insert the token into cache table but don't cache empty cells. 1721 if (pTok->GetType() != formula::svEmptyCell) 1722 maRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pTok, nFmtIndex); 1723 1724 return pTok; 1725 } 1726 1727 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens( 1728 sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange, const ScAddress* pCurPos) 1729 { 1730 if (pCurPos) 1731 insertRefCell(nFileId, *pCurPos); 1732 1733 maybeLinkExternalFile(nFileId); 1734 1735 // Check if the given table name and the cell position is cached. 1736 ScExternalRefCache::TokenArrayRef pArray = 1737 maRefCache.getCellRangeData(nFileId, rTabName, rRange); 1738 if (pArray) 1739 // Cache hit ! 1740 return pArray; 1741 1742 ScDocument* pSrcDoc = getSrcDocument(nFileId); 1743 if (!pSrcDoc) 1744 { 1745 // Source document is not reachable. Throw a reference error. 1746 pArray.reset(new ScTokenArray); 1747 pArray->AddToken(FormulaErrorToken(errNoRef)); 1748 return pArray; 1749 } 1750 1751 SCTAB nTab1; 1752 if (!pSrcDoc->GetTable(rTabName, nTab1)) 1753 { 1754 // specified table name doesn't exist in the source document. 1755 pArray.reset(new ScTokenArray); 1756 pArray->AddToken(FormulaErrorToken(errNoRef)); 1757 return pArray; 1758 } 1759 1760 ScRange aRange(rRange); 1761 SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab(); 1762 1763 vector<ScExternalRefCache::SingleRangeData> aCacheData; 1764 aCacheData.reserve(nTabSpan+1); 1765 aCacheData.push_back(ScExternalRefCache::SingleRangeData()); 1766 aCacheData.back().maTableName = ScGlobal::pCharClass->upper(rTabName); 1767 1768 for (SCTAB i = 1; i < nTabSpan + 1; ++i) 1769 { 1770 String aTabName; 1771 if (!pSrcDoc->GetName(nTab1 + 1, aTabName)) 1772 // source document doesn't have any table by the specified name. 1773 break; 1774 1775 aCacheData.push_back(ScExternalRefCache::SingleRangeData()); 1776 aCacheData.back().maTableName = ScGlobal::pCharClass->upper(aTabName); 1777 } 1778 1779 aRange.aStart.SetTab(nTab1); 1780 aRange.aEnd.SetTab(nTab1 + nTabSpan); 1781 1782 pArray.reset(lcl_convertToTokenArray(pSrcDoc, aRange, aCacheData)); 1783 1784 if (pArray) 1785 // Cache these values. 1786 maRefCache.setCellRangeData(nFileId, aRange, aCacheData, pArray); 1787 else 1788 { 1789 // Array is empty. Fill it with an empty matrix of the required size. 1790 pArray.reset(lcl_fillEmptyMatrix(rRange)); 1791 1792 // Make sure to set this range 'cached', to prevent unnecessarily 1793 // accessing the src document time and time again. 1794 ScExternalRefCache::TableTypeRef pCacheTab = 1795 maRefCache.getCacheTable(nFileId, rTabName, true, NULL); 1796 if (pCacheTab) 1797 pCacheTab->setCachedCellRange( 1798 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); 1799 } 1800 1801 return pArray; 1802 } 1803 1804 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(sal_uInt16 nFileId, const String& rName, const ScAddress* pCurPos) 1805 { 1806 if (pCurPos) 1807 insertRefCell(nFileId, *pCurPos); 1808 1809 maybeLinkExternalFile(nFileId); 1810 1811 ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName); 1812 if (pArray.get()) 1813 return pArray; 1814 1815 ScDocument* pSrcDoc = getSrcDocument(nFileId); 1816 if (!pSrcDoc) 1817 return ScExternalRefCache::TokenArrayRef(); 1818 1819 ScRangeName* pExtNames = pSrcDoc->GetRangeName(); 1820 String aUpperName = ScGlobal::pCharClass->upper(rName); 1821 sal_uInt16 n; 1822 bool bRes = pExtNames->SearchNameUpper(aUpperName, n); 1823 if (!bRes) 1824 return ScExternalRefCache::TokenArrayRef(); 1825 1826 ScRangeData* pRangeData = (*pExtNames)[n]; 1827 if (!pRangeData) 1828 return ScExternalRefCache::TokenArrayRef(); 1829 1830 // Parse all tokens in this external range data, and replace each absolute 1831 // reference token with an external reference token, and cache them. Also 1832 // register the source document with the link manager if it's a new 1833 // source. 1834 1835 ScExternalRefCache::TokenArrayRef pNew(new ScTokenArray); 1836 1837 ScTokenArray* pCode = pRangeData->GetCode(); 1838 for (FormulaToken* pToken = pCode->First(); pToken; pToken = pCode->Next()) 1839 { 1840 bool bTokenAdded = false; 1841 switch (pToken->GetType()) 1842 { 1843 case svSingleRef: 1844 { 1845 const ScSingleRefData& rRef = static_cast<ScToken*>(pToken)->GetSingleRef(); 1846 String aTabName; 1847 pSrcDoc->GetName(rRef.nTab, aTabName); 1848 ScExternalSingleRefToken aNewToken(nFileId, aTabName, static_cast<ScToken*>(pToken)->GetSingleRef()); 1849 pNew->AddToken(aNewToken); 1850 bTokenAdded = true; 1851 } 1852 break; 1853 case svDoubleRef: 1854 { 1855 const ScSingleRefData& rRef = static_cast<ScToken*>(pToken)->GetSingleRef(); 1856 String aTabName; 1857 pSrcDoc->GetName(rRef.nTab, aTabName); 1858 ScExternalDoubleRefToken aNewToken(nFileId, aTabName, static_cast<ScToken*>(pToken)->GetDoubleRef()); 1859 pNew->AddToken(aNewToken); 1860 bTokenAdded = true; 1861 } 1862 break; 1863 default: 1864 ; // nothing 1865 } 1866 1867 if (!bTokenAdded) 1868 pNew->AddToken(*pToken); 1869 } 1870 1871 // Make sure to pass the correctly-cased range name here. 1872 maRefCache.setRangeNameTokens(nFileId, pRangeData->GetName(), pNew); 1873 return pNew; 1874 } 1875 1876 void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId) 1877 { 1878 RefCellMap::iterator itrFile = maRefCells.find(nFileId); 1879 if (itrFile == maRefCells.end()) 1880 return; 1881 1882 RefCellSet& rRefCells = itrFile->second; 1883 for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell()); 1884 1885 ScViewData* pViewData = ScDocShell::GetViewData(); 1886 if (!pViewData) 1887 return; 1888 1889 ScTabViewShell* pVShell = pViewData->GetViewShell(); 1890 if (!pVShell) 1891 return; 1892 1893 // Repainting the grid also repaints the texts, but is there a better way 1894 // to refresh texts? 1895 pVShell->Invalidate(FID_REPAINT); 1896 pVShell->PaintGrid(); 1897 } 1898 1899 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell) 1900 { 1901 RefCellMap::iterator itr = maRefCells.find(nFileId); 1902 if (itr == maRefCells.end()) 1903 { 1904 RefCellSet aRefCells; 1905 pair<RefCellMap::iterator, bool> r = maRefCells.insert( 1906 RefCellMap::value_type(nFileId, aRefCells)); 1907 if (!r.second) 1908 // insertion failed. 1909 return; 1910 1911 itr = r.first; 1912 } 1913 1914 ScBaseCell* pCell = mpDoc->GetCell(rCell); 1915 if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) 1916 itr->second.insert(static_cast<ScFormulaCell*>(pCell)); 1917 } 1918 1919 ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId) 1920 { 1921 if (!mpDoc->IsExecuteLinkEnabled()) 1922 return NULL; 1923 1924 DocShellMap::iterator itrEnd = maDocShells.end(); 1925 DocShellMap::iterator itr = maDocShells.find(nFileId); 1926 1927 if (itr != itrEnd) 1928 { 1929 // document already loaded. 1930 1931 // TODO: Find out a way to access a document that's already open in 1932 // memory and re-use that instance, instead of loading it from the 1933 // disk again. 1934 1935 SfxObjectShell* p = itr->second.maShell; 1936 itr->second.maLastAccess = Time(); 1937 return static_cast<ScDocShell*>(p)->GetDocument(); 1938 } 1939 1940 const String* pFile = getExternalFileName(nFileId); 1941 if (!pFile) 1942 // no file name associated with this ID. 1943 return NULL; 1944 1945 String aFilter; 1946 SrcShell aSrcDoc; 1947 aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter); 1948 if (!aSrcDoc.maShell.Is()) 1949 { 1950 // source document could not be loaded. 1951 return NULL; 1952 } 1953 1954 if (maDocShells.empty()) 1955 { 1956 // If this is the first source document insertion, start up the timer. 1957 maSrcDocTimer.Start(); 1958 } 1959 1960 maDocShells.insert(DocShellMap::value_type(nFileId, aSrcDoc)); 1961 SfxObjectShell* p = aSrcDoc.maShell; 1962 ScDocument* pSrcDoc = static_cast<ScDocShell*>(p)->GetDocument(); 1963 1964 SCTAB nTabCount = pSrcDoc->GetTableCount(); 1965 if (!maRefCache.isDocInitialized(nFileId) && nTabCount) 1966 { 1967 // Populate the cache with all table names in the source document. 1968 vector<String> aTabNames; 1969 aTabNames.reserve(nTabCount); 1970 for (SCTAB i = 0; i < nTabCount; ++i) 1971 { 1972 String aName; 1973 pSrcDoc->GetName(i, aName); 1974 aTabNames.push_back(aName); 1975 } 1976 maRefCache.initializeDoc(nFileId, aTabNames); 1977 } 1978 return pSrcDoc; 1979 } 1980 1981 SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, String& rFilter) 1982 { 1983 const SrcFileData* pFileData = getExternalFileData(nFileId); 1984 if (!pFileData) 1985 return NULL; 1986 1987 // Always load the document by using the path created from the relative 1988 // path. If the referenced document is not there, simply exit. The 1989 // original file name should be used only when the relative path is not 1990 // given. 1991 String aFile = pFileData->maFileName; 1992 maybeCreateRealFileName(nFileId); 1993 if (pFileData->maRealFileName.Len()) 1994 aFile = pFileData->maRealFileName; 1995 1996 if (!isFileLoadable(aFile)) 1997 return NULL; 1998 1999 String aOptions( pFileData->maFilterOptions ); 2000 if ( pFileData->maFilterName.Len() ) 2001 rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter 2002 else 2003 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false); 2004 const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter); 2005 2006 if (!pFileData->maRelativeName.Len()) 2007 { 2008 // Generate a relative file path. 2009 INetURLObject aBaseURL(getOwnDocumentName()); 2010 aBaseURL.insertName(OUString::createFromAscii("content.xml")); 2011 2012 String aStr = URIHelper::simpleNormalizedMakeRelative( 2013 aBaseURL.GetMainURL(INetURLObject::NO_DECODE), aFile); 2014 2015 setRelativeFileName(nFileId, aStr); 2016 } 2017 2018 SfxItemSet* pSet = new SfxAllItemSet(SFX_APP()->GetPool()); 2019 if (aOptions.Len()) 2020 pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions)); 2021 2022 // make medium hidden to prevent assertion from progress bar 2023 pSet->Put( SfxBoolItem( SID_HIDDEN, sal_True ) ); 2024 2025 auto_ptr<SfxMedium> pMedium(new SfxMedium(aFile, STREAM_STD_READ, false, pFilter, pSet)); 2026 if (pMedium->GetError() != ERRCODE_NONE) 2027 return NULL; 2028 2029 // To load encrypted documents with password, user interaction needs to be enabled. 2030 pMedium->UseInteractionHandler(mbUserInteractionEnabled); 2031 2032 ScDocShell* pNewShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL); 2033 SfxObjectShellRef aRef = pNewShell; 2034 2035 // increment the recursive link count of the source document. 2036 ScExtDocOptions* pExtOpt = mpDoc->GetExtDocOptions(); 2037 sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0; 2038 ScDocument* pSrcDoc = pNewShell->GetDocument(); 2039 pSrcDoc->EnableExecuteLink(false); // to prevent circular access of external references. 2040 pSrcDoc->EnableUndo(false); 2041 pSrcDoc->EnableAdjustHeight(false); 2042 2043 ScExtDocOptions* pExtOptNew = pSrcDoc->GetExtDocOptions(); 2044 if (!pExtOptNew) 2045 { 2046 pExtOptNew = new ScExtDocOptions; 2047 pSrcDoc->SetExtDocOptions(pExtOptNew); 2048 } 2049 pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1; 2050 2051 pNewShell->DoLoad(pMedium.release()); 2052 2053 // with UseInteractionHandler, options may be set by dialog during DoLoad 2054 String aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium()); 2055 if (aNew.Len() && aNew != aOptions) 2056 aOptions = aNew; 2057 setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options 2058 2059 return aRef; 2060 } 2061 2062 bool ScExternalRefManager::isFileLoadable(const String& rFile) const 2063 { 2064 if (!rFile.Len()) 2065 return false; 2066 2067 if (isOwnDocument(rFile)) 2068 return false; 2069 2070 String aPhysical; 2071 if (utl::LocalFileHelper::ConvertURLToPhysicalName(rFile, aPhysical) && aPhysical.Len()) 2072 { 2073 // #i114504# try IsFolder/Exists only for file URLs 2074 2075 if (utl::UCBContentHelper::IsFolder(rFile)) 2076 return false; 2077 2078 return utl::UCBContentHelper::Exists(rFile); 2079 } 2080 else 2081 return true; // for http and others, Exists doesn't work, but the URL can still be opened 2082 } 2083 2084 void ScExternalRefManager::maybeLinkExternalFile(sal_uInt16 nFileId) 2085 { 2086 if (maLinkedDocs.count(nFileId)) 2087 // file alerady linked, or the link has been broken. 2088 return; 2089 2090 // Source document not linked yet. Link it now. 2091 const String* pFileName = getExternalFileName(nFileId); 2092 if (!pFileName) 2093 return; 2094 2095 String aFilter, aOptions; 2096 const SrcFileData* pFileData = getExternalFileData(nFileId); 2097 if (pFileData) 2098 { 2099 aFilter = pFileData->maFilterName; 2100 aOptions = pFileData->maFilterOptions; 2101 } 2102 // If a filter was already set (for example, loading the cached table), 2103 // don't call GetFilterName which has to access the source file. 2104 if (!aFilter.Len()) 2105 ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false); 2106 sfx2::LinkManager* pLinkMgr = mpDoc->GetLinkManager(); 2107 ScExternalRefLink* pLink = new ScExternalRefLink(mpDoc, nFileId, aFilter); 2108 DBG_ASSERT(pFileName, "ScExternalRefManager::insertExternalFileLink: file name pointer is NULL"); 2109 pLinkMgr->InsertFileLink(*pLink, OBJECT_CLIENT_FILE, *pFileName, &aFilter); 2110 2111 pLink->SetDoReferesh(false); 2112 pLink->Update(); 2113 pLink->SetDoReferesh(true); 2114 2115 maLinkedDocs.insert(LinkedDocMap::value_type(nFileId, true)); 2116 } 2117 2118 void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const String& rOwnDocName) 2119 { 2120 if (!maRelativeName.Len()) 2121 // No relative path given. Nothing to do. 2122 return; 2123 2124 if (maRealFileName.Len()) 2125 // Real file name already created. Nothing to do. 2126 return; 2127 2128 // Formulate the absolute file path from the relative path. 2129 const String& rRelPath = maRelativeName; 2130 INetURLObject aBaseURL(rOwnDocName); 2131 aBaseURL.insertName(OUString::createFromAscii("content.xml")); 2132 bool bWasAbs = false; 2133 maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::NO_DECODE); 2134 } 2135 2136 void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId) 2137 { 2138 if (nFileId >= maSrcFiles.size()) 2139 return; 2140 2141 maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName()); 2142 } 2143 2144 const String& ScExternalRefManager::getOwnDocumentName() const 2145 { 2146 SfxObjectShell* pShell = mpDoc->GetDocumentShell(); 2147 if (!pShell) 2148 // This should not happen! 2149 return EMPTY_STRING; 2150 2151 SfxMedium* pMed = pShell->GetMedium(); 2152 if (!pMed) 2153 return EMPTY_STRING; 2154 2155 return pMed->GetName(); 2156 } 2157 2158 bool ScExternalRefManager::isOwnDocument(const String& rFile) const 2159 { 2160 return getOwnDocumentName().Equals(rFile); 2161 } 2162 2163 void ScExternalRefManager::convertToAbsName(String& rFile) const 2164 { 2165 SfxObjectShell* pDocShell = mpDoc->GetDocumentShell(); 2166 rFile = ScGlobal::GetAbsDocName(rFile, pDocShell); 2167 } 2168 2169 sal_uInt16 ScExternalRefManager::getExternalFileId(const String& rFile) 2170 { 2171 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2172 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile)); 2173 if (itr != itrEnd) 2174 { 2175 size_t nId = distance(itrBeg, itr); 2176 return static_cast<sal_uInt16>(nId); 2177 } 2178 2179 SrcFileData aData; 2180 aData.maFileName = rFile; 2181 maSrcFiles.push_back(aData); 2182 return static_cast<sal_uInt16>(maSrcFiles.size() - 1); 2183 } 2184 2185 const String* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal) 2186 { 2187 if (nFileId >= maSrcFiles.size()) 2188 return NULL; 2189 2190 if (bForceOriginal) 2191 return &maSrcFiles[nFileId].maFileName; 2192 2193 maybeCreateRealFileName(nFileId); 2194 2195 if (maSrcFiles[nFileId].maRealFileName.Len()) 2196 return &maSrcFiles[nFileId].maRealFileName; 2197 else 2198 return &maSrcFiles[nFileId].maFileName; 2199 } 2200 2201 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const 2202 { 2203 return nFileId < maSrcFiles.size(); 2204 } 2205 2206 bool ScExternalRefManager::hasExternalFile(const String& rFile) const 2207 { 2208 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2209 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile)); 2210 return itr != itrEnd; 2211 } 2212 2213 const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const 2214 { 2215 if (nFileId >= maSrcFiles.size()) 2216 return NULL; 2217 2218 return &maSrcFiles[nFileId]; 2219 } 2220 2221 const String* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const String& rTabName) const 2222 { 2223 return maRefCache.getRealTableName(nFileId, rTabName); 2224 } 2225 2226 const String* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const String& rRangeName) const 2227 { 2228 return maRefCache.getRealRangeName(nFileId, rRangeName); 2229 } 2230 2231 template<typename MapContainer> 2232 void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap) 2233 { 2234 typename MapContainer::iterator itr = rMap.find(nFileId); 2235 if (itr != rMap.end()) 2236 rMap.erase(itr); 2237 } 2238 2239 void ScExternalRefManager::refreshNames(sal_uInt16 nFileId) 2240 { 2241 maRefCache.clearCache(nFileId); 2242 lcl_removeByFileId(nFileId, maDocShells); 2243 2244 if (maDocShells.empty()) 2245 maSrcDocTimer.Stop(); 2246 2247 // Update all cells containing names from this source document. 2248 refreshAllRefCells(nFileId); 2249 2250 notifyAllLinkListeners(nFileId, LINK_MODIFIED); 2251 } 2252 2253 void ScExternalRefManager::breakLink(sal_uInt16 nFileId) 2254 { 2255 // Turn all formula cells referencing this external document into static 2256 // cells. 2257 RefCellMap::iterator itrRefs = maRefCells.find(nFileId); 2258 if (itrRefs != maRefCells.end()) 2259 { 2260 // Make a copy because removing the formula cells below will modify 2261 // the original container. 2262 RefCellSet aSet = itrRefs->second; 2263 for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(mpDoc)); 2264 maRefCells.erase(nFileId); 2265 } 2266 2267 lcl_removeByFileId(nFileId, maDocShells); 2268 2269 if (maDocShells.empty()) 2270 maSrcDocTimer.Stop(); 2271 2272 LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId); 2273 if (itr != maLinkedDocs.end()) 2274 itr->second = false; 2275 2276 notifyAllLinkListeners(nFileId, LINK_BROKEN); 2277 } 2278 2279 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const String& rNewFile, const String& rNewFilter) 2280 { 2281 maSrcFiles[nFileId].maFileName = rNewFile; 2282 maSrcFiles[nFileId].maRelativeName.Erase(); 2283 maSrcFiles[nFileId].maRealFileName.Erase(); 2284 if (!maSrcFiles[nFileId].maFilterName.Equals(rNewFilter)) 2285 { 2286 // Filter type has changed. 2287 maSrcFiles[nFileId].maFilterName = rNewFilter; 2288 maSrcFiles[nFileId].maFilterOptions.Erase(); 2289 } 2290 refreshNames(nFileId); 2291 } 2292 2293 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const String& rRelUrl) 2294 { 2295 if (nFileId >= maSrcFiles.size()) 2296 return; 2297 maSrcFiles[nFileId].maRelativeName = rRelUrl; 2298 } 2299 2300 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const String& rFilterName, const String& rOptions) 2301 { 2302 if (nFileId >= maSrcFiles.size()) 2303 return; 2304 maSrcFiles[nFileId].maFilterName = rFilterName; 2305 maSrcFiles[nFileId].maFilterOptions = rOptions; 2306 } 2307 2308 void ScExternalRefManager::clear() 2309 { 2310 DocShellMap::iterator itrEnd = maDocShells.end(); 2311 for (DocShellMap::iterator itr = maDocShells.begin(); itr != itrEnd; ++itr) 2312 itr->second.maShell->DoClose(); 2313 2314 maDocShells.clear(); 2315 maSrcDocTimer.Stop(); 2316 } 2317 2318 bool ScExternalRefManager::hasExternalData() const 2319 { 2320 return !maSrcFiles.empty(); 2321 } 2322 2323 void ScExternalRefManager::resetSrcFileData(const String& rBaseFileUrl) 2324 { 2325 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2326 itr != itrEnd; ++itr) 2327 { 2328 // Re-generate relative file name from the absolute file name. 2329 String aAbsName = itr->maRealFileName; 2330 if (!aAbsName.Len()) 2331 aAbsName = itr->maFileName; 2332 2333 itr->maRelativeName = URIHelper::simpleNormalizedMakeRelative( 2334 rBaseFileUrl, aAbsName); 2335 } 2336 } 2337 2338 void ScExternalRefManager::updateAbsAfterLoad() 2339 { 2340 String aOwn( getOwnDocumentName() ); 2341 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2342 itr != itrEnd; ++itr) 2343 { 2344 // update maFileName to the real file name, 2345 // to be called when the original name is no longer needed (after CompileXML) 2346 2347 itr->maybeCreateRealFileName( aOwn ); 2348 String aReal = itr->maRealFileName; 2349 if (aReal.Len()) 2350 itr->maFileName = aReal; 2351 } 2352 } 2353 2354 void ScExternalRefManager::removeRefCell(ScFormulaCell* pCell) 2355 { 2356 for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell)); 2357 } 2358 2359 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener) 2360 { 2361 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId); 2362 if (itr == maLinkListeners.end()) 2363 { 2364 pair<LinkListenerMap::iterator, bool> r = maLinkListeners.insert( 2365 LinkListenerMap::value_type(nFileId, LinkListeners())); 2366 if (!r.second) 2367 { 2368 DBG_ERROR("insertion of new link listener list failed"); 2369 return; 2370 } 2371 2372 itr = r.first; 2373 } 2374 2375 LinkListeners& rList = itr->second; 2376 rList.insert(pListener); 2377 } 2378 2379 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener) 2380 { 2381 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId); 2382 if (itr == maLinkListeners.end()) 2383 // no listeners for a specified file. 2384 return; 2385 2386 LinkListeners& rList = itr->second; 2387 rList.erase(pListener); 2388 2389 if (rList.empty()) 2390 // No more listeners for this file. Remove its entry. 2391 maLinkListeners.erase(itr); 2392 } 2393 2394 void ScExternalRefManager::removeLinkListener(LinkListener* pListener) 2395 { 2396 LinkListenerMap::iterator itr = maLinkListeners.begin(), itrEnd = maLinkListeners.end(); 2397 for (; itr != itrEnd; ++itr) 2398 itr->second.erase(pListener); 2399 } 2400 2401 void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType) 2402 { 2403 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId); 2404 if (itr == maLinkListeners.end()) 2405 // no listeners for a specified file. 2406 return; 2407 2408 LinkListeners& rList = itr->second; 2409 for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType)); 2410 } 2411 2412 void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut) 2413 { 2414 DocShellMap aNewDocShells; 2415 DocShellMap::iterator itr = maDocShells.begin(), itrEnd = maDocShells.end(); 2416 for (; itr != itrEnd; ++itr) 2417 { 2418 // in 100th of a second. 2419 sal_Int32 nSinceLastAccess = (Time() - itr->second.maLastAccess).GetTime(); 2420 if (nSinceLastAccess < nTimeOut) 2421 aNewDocShells.insert(*itr); 2422 } 2423 maDocShells.swap(aNewDocShells); 2424 2425 if (maDocShells.empty()) 2426 maSrcDocTimer.Stop(); 2427 } 2428 2429 sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, ScDocument* pSrcDoc) 2430 { 2431 NumFmtMap::iterator itr = maNumFormatMap.find(nFileId); 2432 if (itr == maNumFormatMap.end()) 2433 { 2434 // Number formatter map is not initialized for this external document. 2435 pair<NumFmtMap::iterator, bool> r = maNumFormatMap.insert( 2436 NumFmtMap::value_type(nFileId, SvNumberFormatterMergeMap())); 2437 2438 if (!r.second) 2439 // insertion failed. 2440 return nNumFmt; 2441 2442 itr = r.first; 2443 mpDoc->GetFormatTable()->MergeFormatter( *pSrcDoc->GetFormatTable()); 2444 SvNumberFormatterMergeMap aMap = mpDoc->GetFormatTable()->ConvertMergeTableToMap(); 2445 itr->second.swap(aMap); 2446 } 2447 const SvNumberFormatterMergeMap& rMap = itr->second; 2448 SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt); 2449 if (itrNumFmt != rMap.end()) 2450 // mapped value found. 2451 return itrNumFmt->second; 2452 2453 return nNumFmt; 2454 } 2455 2456 IMPL_LINK(ScExternalRefManager, TimeOutHdl, AutoTimer*, pTimer) 2457 { 2458 if (pTimer == &maSrcDocTimer) 2459 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN); 2460 2461 return 0; 2462 } 2463 2464