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