Bump version to 4.3-4
[LibreOffice.git] / sc / source / ui / docshell / externalrefmgr.cxx
blob325cfb2544d73c497a4c43818324ff205de510ca
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "externalrefmgr.hxx"
21 #include "document.hxx"
22 #include "token.hxx"
23 #include "tokenarray.hxx"
24 #include "address.hxx"
25 #include "tablink.hxx"
26 #include "docsh.hxx"
27 #include "scextopt.hxx"
28 #include "rangenam.hxx"
29 #include "formulacell.hxx"
30 #include "viewdata.hxx"
31 #include "tabvwsh.hxx"
32 #include "sc.hrc"
33 #include "globstr.hrc"
34 #include "cellvalue.hxx"
36 #include <sfx2/app.hxx>
37 #include <sfx2/docfilt.hxx>
38 #include <sfx2/docfile.hxx>
39 #include <sfx2/fcontnr.hxx>
40 #include <sfx2/sfxsids.hrc>
41 #include <sfx2/objsh.hxx>
42 #include <svl/broadcast.hxx>
43 #include <svl/smplhint.hxx>
44 #include <svl/itemset.hxx>
45 #include <svl/stritem.hxx>
46 #include <svl/urihelper.hxx>
47 #include <svl/zformat.hxx>
48 #include <svl/sharedstringpool.hxx>
49 #include <sfx2/linkmgr.hxx>
50 #include <tools/urlobj.hxx>
51 #include <unotools/ucbhelper.hxx>
52 #include <unotools/localfilehelper.hxx>
53 #include <vcl/msgbox.hxx>
54 #include "stringutil.hxx"
55 #include "scmatrix.hxx"
56 #include <columnspanset.hxx>
57 #include <column.hxx>
59 #include <memory>
60 #include <algorithm>
62 #include <boost/scoped_ptr.hpp>
64 using ::std::auto_ptr;
65 using ::com::sun::star::uno::Any;
66 using ::std::vector;
67 using ::std::find;
68 using ::std::find_if;
69 using ::std::remove_if;
70 using ::std::distance;
71 using ::std::pair;
72 using ::std::list;
73 using ::std::unary_function;
74 using namespace formula;
76 #define SRCDOC_LIFE_SPAN 30000 // 5 minutes (in 100th of a sec)
77 #define SRCDOC_SCAN_INTERVAL 1000*30 // every 30 seconds (in msec)
79 namespace {
81 class TabNameSearchPredicate : public unary_function<ScExternalRefCache::TableName, bool>
83 public:
84 explicit TabNameSearchPredicate(const OUString& rSearchName) :
85 maSearchName(ScGlobal::pCharClass->uppercase(rSearchName))
89 bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const
91 // Ok, I'm doing case insensitive search here.
92 return rTabNameSet.maUpperName.equals(maSearchName);
95 private:
96 OUString maSearchName;
99 class FindSrcFileByName : public unary_function<ScExternalRefManager::SrcFileData, bool>
101 public:
102 FindSrcFileByName(const OUString& rMatchName) :
103 mrMatchName(rMatchName)
107 bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const
109 return rSrcData.maFileName.equals(mrMatchName);
112 private:
113 const OUString& mrMatchName;
116 class NotifyLinkListener : public unary_function<ScExternalRefManager::LinkListener*, void>
118 public:
119 NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) :
120 mnFileId(nFileId), meType(eType) {}
122 NotifyLinkListener(const NotifyLinkListener& r) :
123 mnFileId(r.mnFileId), meType(r.meType) {}
125 void operator() (ScExternalRefManager::LinkListener* p) const
127 p->notify(mnFileId, meType);
129 private:
130 sal_uInt16 mnFileId;
131 ScExternalRefManager::LinkUpdateType meType;
134 struct UpdateFormulaCell : public unary_function<ScFormulaCell*, void>
136 void operator() (ScFormulaCell* pCell) const
138 // Check to make sure the cell really contains ocExternalRef.
139 // External names, external cell and range references all have a
140 // ocExternalRef token.
141 ScTokenArray* pCode = pCell->GetCode();
142 if (!pCode->HasExternalRef())
143 return;
145 if (pCode->GetCodeError())
147 // Clear the error code, or a cell with error won't get re-compiled.
148 pCode->SetCodeError(0);
149 pCell->SetCompile(true);
150 pCell->CompileTokenArray();
153 pCell->SetDirty();
157 class RemoveFormulaCell : public unary_function<pair<const sal_uInt16, ScExternalRefManager::RefCellSet>, void>
159 public:
160 explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {}
161 void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const
163 r.second.erase(mpCell);
165 private:
166 ScFormulaCell* mpCell;
169 class ConvertFormulaToStatic : public unary_function<ScFormulaCell*, void>
171 public:
172 explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {}
173 void operator() (ScFormulaCell* pCell) const
175 ScAddress aPos = pCell->aPos;
177 // We don't check for empty cells because empty external cells are
178 // treated as having a value of 0.
180 if (pCell->IsValue())
182 // Turn this into value cell.
183 mpDoc->SetValue(aPos, pCell->GetValue());
185 else
187 // string cell otherwise.
188 ScSetStringParam aParam;
189 aParam.setTextInput();
190 mpDoc->SetString(aPos, pCell->GetString().getString(), &aParam);
193 private:
194 ScDocument* mpDoc;
198 * Check whether a named range contains an external reference to a
199 * particular document.
201 bool hasRefsToSrcDoc(ScRangeData& rData, sal_uInt16 nFileId)
203 ScTokenArray* pArray = rData.GetCode();
204 if (!pArray)
205 return false;
207 pArray->Reset();
208 ScToken* p = static_cast<ScToken*>(pArray->GetNextReference());
209 for (; p; p = static_cast<ScToken*>(pArray->GetNextReference()))
211 if (!p->IsExternalRef())
212 continue;
214 if (p->GetIndex() == nFileId)
215 return true;
217 return false;
220 class EraseRangeByIterator : unary_function<ScRangeName::iterator, void>
222 ScRangeName& mrRanges;
223 public:
224 EraseRangeByIterator(ScRangeName& rRanges) : mrRanges(rRanges) {}
225 void operator() (const ScRangeName::iterator& itr)
227 mrRanges.erase(itr);
232 * Remove all named ranges that contain references to specified source
233 * document.
235 void removeRangeNamesBySrcDoc(ScRangeName& rRanges, sal_uInt16 nFileId)
237 ScRangeName::iterator itr = rRanges.begin(), itrEnd = rRanges.end();
238 vector<ScRangeName::iterator> v;
239 for (; itr != itrEnd; ++itr)
241 if (hasRefsToSrcDoc(*itr->second, nFileId))
242 v.push_back(itr);
244 for_each(v.begin(), v.end(), EraseRangeByIterator(rRanges));
249 ScExternalRefCache::Table::Table()
250 : meReferenced( REFERENCED_MARKED )
251 // Prevent accidental data loss due to lack of knowledge.
255 ScExternalRefCache::Table::~Table()
259 void ScExternalRefCache::Table::clear()
261 maRows.clear();
262 maCachedRanges.RemoveAll();
263 meReferenced = REFERENCED_MARKED;
266 void ScExternalRefCache::Table::setReferencedFlag( ScExternalRefCache::Table::ReferencedFlag eFlag )
268 meReferenced = eFlag;
271 void ScExternalRefCache::Table::setReferenced( bool bReferenced )
273 if (meReferenced != REFERENCED_PERMANENT)
274 meReferenced = (bReferenced ? REFERENCED_MARKED : UNREFERENCED);
277 ScExternalRefCache::Table::ReferencedFlag ScExternalRefCache::Table::getReferencedFlag() const
279 return meReferenced;
282 bool ScExternalRefCache::Table::isReferenced() const
284 return meReferenced != UNREFERENCED;
287 void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef pToken, sal_uLong nFmtIndex, bool bSetCacheRange)
289 using ::std::pair;
290 RowsDataType::iterator itrRow = maRows.find(nRow);
291 if (itrRow == maRows.end())
293 // This row does not exist yet.
294 pair<RowsDataType::iterator, bool> res = maRows.insert(
295 RowsDataType::value_type(nRow, RowDataType()));
297 if (!res.second)
298 return;
300 itrRow = res.first;
303 // Insert this token into the specified column location. I don't need to
304 // check for existing data. Just overwrite it.
305 RowDataType& rRow = itrRow->second;
306 ScExternalRefCache::Cell aCell;
307 aCell.mxToken = pToken;
308 aCell.mnFmtIndex = nFmtIndex;
309 rRow.insert(RowDataType::value_type(nCol, aCell));
310 if (bSetCacheRange)
311 setCachedCell(nCol, nRow);
314 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const
316 RowsDataType::const_iterator itrTable = maRows.find(nRow);
317 if (itrTable == maRows.end())
319 // this table doesn't have the specified row.
320 return getEmptyOrNullToken(nCol, nRow);
323 const RowDataType& rRowData = itrTable->second;
324 RowDataType::const_iterator itrRow = rRowData.find(nCol);
325 if (itrRow == rRowData.end())
327 // this row doesn't have the specified column.
328 return getEmptyOrNullToken(nCol, nRow);
331 const Cell& rCell = itrRow->second;
332 if (pnFmtIndex)
333 *pnFmtIndex = rCell.mnFmtIndex;
335 return rCell.mxToken;
338 bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const
340 RowsDataType::const_iterator itrRow = maRows.find(nRow);
341 return itrRow != maRows.end();
344 void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const
346 vector<SCROW> aRows;
347 aRows.reserve(maRows.size());
348 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end();
349 for (; itr != itrEnd; ++itr)
350 if (nLow <= itr->first && itr->first <= nHigh)
351 aRows.push_back(itr->first);
353 // hash map is not ordered, so we need to explicitly sort it.
354 ::std::sort(aRows.begin(), aRows.end());
355 rRows.swap(aRows);
358 ::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const
360 ::std::pair< SCROW, SCROW > aRange( 0, 0 );
361 if( !maRows.empty() )
363 // iterate over entire container (hash map is not sorted by key)
364 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end();
365 aRange.first = itr->first;
366 aRange.second = itr->first + 1;
367 while( ++itr != itrEnd )
369 if( itr->first < aRange.first )
370 aRange.first = itr->first;
371 else if( itr->first >= aRange.second )
372 aRange.second = itr->first + 1;
375 return aRange;
378 void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const
380 RowsDataType::const_iterator itrRow = maRows.find(nRow);
381 if (itrRow == maRows.end())
382 // this table doesn't have the specified row.
383 return;
385 const RowDataType& rRowData = itrRow->second;
386 vector<SCCOL> aCols;
387 aCols.reserve(rRowData.size());
388 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
389 for (; itrCol != itrColEnd; ++itrCol)
390 if (nLow <= itrCol->first && itrCol->first <= nHigh)
391 aCols.push_back(itrCol->first);
393 // hash map is not ordered, so we need to explicitly sort it.
394 ::std::sort(aCols.begin(), aCols.end());
395 rCols.swap(aCols);
398 ::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const
400 ::std::pair< SCCOL, SCCOL > aRange( 0, 0 );
402 RowsDataType::const_iterator itrRow = maRows.find( nRow );
403 if (itrRow == maRows.end())
404 // this table doesn't have the specified row.
405 return aRange;
407 const RowDataType& rRowData = itrRow->second;
408 if( !rRowData.empty() )
410 // iterate over entire container (hash map is not sorted by key)
411 RowDataType::const_iterator itr = rRowData.begin(), itrEnd = rRowData.end();
412 aRange.first = itr->first;
413 aRange.second = itr->first + 1;
414 while( ++itr != itrEnd )
416 if( itr->first < aRange.first )
417 aRange.first = itr->first;
418 else if( itr->first >= aRange.second )
419 aRange.second = itr->first + 1;
422 return aRange;
425 void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
427 RowsDataType::const_iterator itrRow = maRows.begin(), itrRowEnd = maRows.end();
428 for (; itrRow != itrRowEnd; ++itrRow)
430 const RowDataType& rRowData = itrRow->second;
431 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
432 for (; itrCol != itrColEnd; ++itrCol)
434 const Cell& rCell = itrCol->second;
435 rNumFmts.push_back(rCell.mnFmtIndex);
440 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
442 return maCachedRanges.In(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
445 void ScExternalRefCache::Table::setCachedCell(SCCOL nCol, SCROW nRow)
447 setCachedCellRange(nCol, nRow, nCol, nRow);
450 void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
452 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
453 if ( maCachedRanges.empty() )
454 maCachedRanges.Append(aRange);
455 else
456 maCachedRanges.Join(aRange);
459 void ScExternalRefCache::Table::setWholeTableCached()
461 setCachedCellRange(0, 0, MAXCOL, MAXROW);
464 bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol, SCROW nRow) const
466 return maCachedRanges.In(ScRange(nCol, nRow, 0, nCol, nRow, 0));
469 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getEmptyOrNullToken(
470 SCCOL nCol, SCROW nRow) const
472 if (isInCachedRanges(nCol, nRow))
474 TokenRef p(new ScEmptyCellToken(false, false));
475 return p;
477 return TokenRef();
480 ScExternalRefCache::TableName::TableName(const OUString& rUpper, const OUString& rReal) :
481 maUpperName(rUpper), maRealName(rReal)
485 ScExternalRefCache::CellFormat::CellFormat() :
486 mbIsSet(false), mnType(NUMBERFORMAT_ALL), mnIndex(0)
490 ScExternalRefCache::ScExternalRefCache() {}
492 ScExternalRefCache::~ScExternalRefCache() {}
494 const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
496 osl::MutexGuard aGuard(&maMtxDocs);
498 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
499 if (itrDoc == maDocs.end())
501 // specified document is not cached.
502 return NULL;
505 const DocItem& rDoc = itrDoc->second;
506 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
507 ScGlobal::pCharClass->uppercase(rTabName));
508 if (itrTabId == rDoc.maTableNameIndex.end())
510 // the specified table is not in cache.
511 return NULL;
514 return &rDoc.maTableNames[itrTabId->second].maRealName;
517 const OUString* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
519 osl::MutexGuard aGuard(&maMtxDocs);
521 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
522 if (itrDoc == maDocs.end())
524 // specified document is not cached.
525 return NULL;
528 const DocItem& rDoc = itrDoc->second;
529 NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find(
530 ScGlobal::pCharClass->uppercase(rRangeName));
531 if (itr == rDoc.maRealRangeNameMap.end())
532 // range name not found.
533 return NULL;
535 return &itr->second;
538 ScExternalRefCache::TokenRef ScExternalRefCache::getCellData(
539 sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex)
541 osl::MutexGuard aGuard(&maMtxDocs);
543 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
544 if (itrDoc == maDocs.end())
546 // specified document is not cached.
547 return TokenRef();
550 const DocItem& rDoc = itrDoc->second;
551 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
552 ScGlobal::pCharClass->uppercase(rTabName));
553 if (itrTabId == rDoc.maTableNameIndex.end())
555 // the specified table is not in cache.
556 return TokenRef();
559 const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
560 if (!pTableData.get())
562 // the table data is not instantiated yet.
563 return TokenRef();
566 return pTableData->getCell(nCol, nRow, pnFmtIndex);
569 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData(
570 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange)
572 osl::MutexGuard aGuard(&maMtxDocs);
574 DocDataType::iterator itrDoc = maDocs.find(nFileId);
575 if (itrDoc == maDocs.end())
576 // specified document is not cached.
577 return TokenArrayRef();
579 DocItem& rDoc = itrDoc->second;
581 TableNameIndexMap::iterator itrTabId = rDoc.maTableNameIndex.find(
582 ScGlobal::pCharClass->uppercase(rTabName));
583 if (itrTabId == rDoc.maTableNameIndex.end())
584 // the specified table is not in cache.
585 return TokenArrayRef();
587 const ScAddress& s = rRange.aStart;
588 const ScAddress& e = rRange.aEnd;
590 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
591 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
592 SCROW nRow1 = s.Row(), nRow2 = e.Row();
594 // Make sure I have all the tables cached.
595 size_t nTabFirstId = itrTabId->second;
596 size_t nTabLastId = nTabFirstId + nTab2 - nTab1;
597 if (nTabLastId >= rDoc.maTables.size())
598 // not all tables are cached.
599 return TokenArrayRef();
601 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
603 RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
604 if (itrRange != rDoc.maRangeArrays.end())
605 // Cache hit!
606 return itrRange->second;
608 ::boost::scoped_ptr<ScRange> pNewRange;
609 TokenArrayRef pArray;
610 bool bFirstTab = true;
611 for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
613 TableTypeRef pTab = rDoc.maTables[nTab];
614 if (!pTab.get())
615 return TokenArrayRef();
617 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
618 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
620 if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2))
622 // specified range is not entirely within cached ranges.
623 return TokenArrayRef();
626 ScMatrixRef xMat = new ScMatrix(
627 static_cast<SCSIZE>(nDataCol2-nDataCol1+1), static_cast<SCSIZE>(nDataRow2-nDataRow1+1));
629 // Only fill non-empty cells, for better performance.
630 vector<SCROW> aRows;
631 pTab->getAllRows(aRows, nDataRow1, nDataRow2);
632 for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr)
634 SCROW nRow = *itr;
635 vector<SCCOL> aCols;
636 pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2);
637 for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol)
639 SCCOL nCol = *itrCol;
640 TokenRef pToken = pTab->getCell(nCol, nRow);
641 if (!pToken)
642 // This should never happen!
643 return TokenArrayRef();
645 SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1;
646 switch (pToken->GetType())
648 case svDouble:
649 xMat->PutDouble(pToken->GetDouble(), nC, nR);
650 break;
651 case svString:
652 xMat->PutString(pToken->GetString(), nC, nR);
653 break;
654 default:
660 if (!bFirstTab)
661 pArray->AddOpCode(ocSep);
663 ScMatrixToken aToken(xMat);
664 if (!pArray)
665 pArray.reset(new ScTokenArray);
666 pArray->AddToken(aToken);
668 bFirstTab = false;
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));
676 if (pNewRange)
677 rDoc.maRangeArrays.insert( RangeArrayMap::value_type(*pNewRange, pArray));
678 return pArray;
681 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const OUString& rName)
683 osl::MutexGuard aGuard(&maMtxDocs);
685 DocItem* pDoc = getDocItem(nFileId);
686 if (!pDoc)
687 return TokenArrayRef();
689 RangeNameMap& rMap = pDoc->maRangeNames;
690 RangeNameMap::const_iterator itr = rMap.find(
691 ScGlobal::pCharClass->uppercase(rName));
692 if (itr == rMap.end())
693 return TokenArrayRef();
695 return itr->second;
698 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, TokenArrayRef pArray)
700 osl::MutexGuard aGuard(&maMtxDocs);
702 DocItem* pDoc = getDocItem(nFileId);
703 if (!pDoc)
704 return;
706 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
707 RangeNameMap& rMap = pDoc->maRangeNames;
708 rMap.insert(RangeNameMap::value_type(aUpperName, pArray));
709 pDoc->maRealRangeNameMap.insert(NamePairMap::value_type(aUpperName, rName));
712 bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId, const OUString& rName) const
714 osl::MutexGuard aGuard(&maMtxDocs);
716 DocItem* pDoc = getDocItem(nFileId);
717 if (!pDoc)
718 return false;
720 const RangeNameMap& rMap = pDoc->maRangeNames;
721 return rMap.count(rName) > 0;
724 void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow,
725 TokenRef pToken, sal_uLong nFmtIndex)
727 if (!isDocInitialized(nFileId))
728 return;
730 using ::std::pair;
731 DocItem* pDocItem = getDocItem(nFileId);
732 if (!pDocItem)
733 return;
735 DocItem& rDoc = *pDocItem;
737 // See if the table by this name already exists.
738 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
739 ScGlobal::pCharClass->uppercase(rTabName));
740 if (itrTabName == rDoc.maTableNameIndex.end())
741 // Table not found. Maybe the table name or the file id is wrong ???
742 return;
744 TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
745 if (!pTableData.get())
746 pTableData.reset(new Table);
748 pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
749 pTableData->setCachedCell(nCol, nRow);
752 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
753 const TokenArrayRef& pArray)
755 using ::std::pair;
756 if (rData.empty() || !isDocInitialized(nFileId))
757 // nothing to cache
758 return;
760 // First, get the document item for the given file ID.
761 DocItem* pDocItem = getDocItem(nFileId);
762 if (!pDocItem)
763 return;
765 DocItem& rDoc = *pDocItem;
767 // Now, find the table position of the first table to cache.
768 const OUString& rFirstTabName = rData.front().maTableName;
769 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
770 ScGlobal::pCharClass->uppercase(rFirstTabName));
771 if (itrTabName == rDoc.maTableNameIndex.end())
773 // table index not found.
774 return;
777 size_t nTabFirstId = itrTabName->second;
778 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
779 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
780 vector<SingleRangeData>::const_iterator itrDataBeg = rData.begin(), itrDataEnd = rData.end();
781 for (vector<SingleRangeData>::const_iterator itrData = itrDataBeg; itrData != itrDataEnd; ++itrData)
783 size_t i = nTabFirstId + ::std::distance(itrDataBeg, itrData);
784 TableTypeRef& pTabData = rDoc.maTables[i];
785 if (!pTabData.get())
786 pTabData.reset(new Table);
788 const ScMatrixRef& pMat = itrData->mpRangeData;
789 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
791 const SCSIZE nR = nRow - nRow1;
792 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
794 const SCSIZE nC = nCol - nCol1;
796 ScMatrixValue value = pMat->Get(nC, nR);
798 TokenRef pToken;
800 switch (value.nType) {
801 case SC_MATVAL_VALUE:
802 case SC_MATVAL_BOOLEAN:
803 pToken.reset(new formula::FormulaDoubleToken(value.fVal));
804 break;
805 case SC_MATVAL_STRING:
806 pToken.reset(new formula::FormulaStringToken(value.aStr));
807 break;
808 default:
809 // Don't cache empty cells.
810 break;
813 if (pToken)
814 // Don't mark this cell 'cached' here, for better performance.
815 pTabData->setCell(nCol, nRow, pToken, 0, false);
818 // Mark the whole range 'cached'.
819 pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2);
822 size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab();
823 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
825 rDoc.maRangeArrays.insert( RangeArrayMap::value_type( aCacheRange, pArray));
828 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId)
830 DocItem* pDoc = getDocItem(nFileId);
831 if (!pDoc)
832 return false;
834 return pDoc->mbInitFromSource;
837 static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex)
839 ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
840 if (itr == rMap.end())
841 return false;
843 rIndex = itr->second;
844 return true;
847 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames)
849 DocItem* pDoc = getDocItem(nFileId);
850 if (!pDoc)
851 return;
853 size_t n = rTabNames.size();
855 // table name list - the list must include all table names in the source
856 // document and only to be populated when loading the source document, not
857 // when loading cached data from, say, Excel XCT/CRN records.
858 vector<TableName> aNewTabNames;
859 aNewTabNames.reserve(n);
860 for (vector<OUString>::const_iterator itr = rTabNames.begin(), itrEnd = rTabNames.end();
861 itr != itrEnd; ++itr)
863 TableName aNameItem(ScGlobal::pCharClass->uppercase(*itr), *itr);
864 aNewTabNames.push_back(aNameItem);
866 pDoc->maTableNames.swap(aNewTabNames);
868 // data tables - preserve any existing data that may have been set during
869 // file import.
870 vector<TableTypeRef> aNewTables(n);
871 for (size_t i = 0; i < n; ++i)
873 size_t nIndex;
874 if (lcl_getTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
876 aNewTables[i] = pDoc->maTables[nIndex];
879 pDoc->maTables.swap(aNewTables);
881 // name index map
882 TableNameIndexMap aNewNameIndex;
883 for (size_t i = 0; i < n; ++i)
884 aNewNameIndex.insert(TableNameIndexMap::value_type(pDoc->maTableNames[i].maUpperName, i));
885 pDoc->maTableNameIndex.swap(aNewNameIndex);
887 pDoc->mbInitFromSource = true;
890 OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
892 if( DocItem* pDoc = getDocItem( nFileId ) )
893 if( nCacheId < pDoc->maTableNames.size() )
894 return pDoc->maTableNames[ nCacheId ].maRealName;
895 return EMPTY_OUSTRING;
898 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
900 rTabNames.clear();
901 DocItem* pDoc = getDocItem(nFileId);
902 if (!pDoc)
903 return;
905 size_t n = pDoc->maTableNames.size();
906 rTabNames.reserve(n);
907 for (vector<TableName>::const_iterator itr = pDoc->maTableNames.begin(), itrEnd = pDoc->maTableNames.end();
908 itr != itrEnd; ++itr)
909 rTabNames.push_back(itr->maRealName);
912 SCsTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
914 DocItem* pDoc = getDocItem(nFileId);
915 if (!pDoc)
916 return -1;
918 vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
919 vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
921 vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
922 TabNameSearchPredicate( rStartTabName));
923 if (itrStartTab == itrEnd)
924 return -1;
926 vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
927 TabNameSearchPredicate( rEndTabName));
928 if (itrEndTab == itrEnd)
929 return 0;
931 size_t nStartDist = ::std::distance( itrBeg, itrStartTab);
932 size_t nEndDist = ::std::distance( itrBeg, itrEndTab);
933 return nStartDist <= nEndDist ? static_cast<SCsTAB>(nEndDist - nStartDist + 1) : -static_cast<SCsTAB>(nStartDist - nEndDist + 1);
936 void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
938 osl::MutexGuard aGuard(&maMtxDocs);
940 using ::std::sort;
941 using ::std::unique;
943 vector<sal_uInt32> aNumFmts;
944 for (DocDataType::const_iterator itrDoc = maDocs.begin(), itrDocEnd = maDocs.end();
945 itrDoc != itrDocEnd; ++itrDoc)
947 const vector<TableTypeRef>& rTables = itrDoc->second.maTables;
948 for (vector<TableTypeRef>::const_iterator itrTab = rTables.begin(), itrTabEnd = rTables.end();
949 itrTab != itrTabEnd; ++itrTab)
951 TableTypeRef pTab = *itrTab;
952 if (!pTab)
953 continue;
955 pTab->getAllNumberFormats(aNumFmts);
959 // remove duplicates.
960 sort(aNumFmts.begin(), aNumFmts.end());
961 aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
962 rNumFmts.swap(aNumFmts);
965 bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId )
967 DocItem* pDocItem = getDocItem(nFileId);
968 if (!pDocItem)
969 return areAllCacheTablesReferenced();
971 for (::std::vector<TableTypeRef>::iterator itrTab = pDocItem->maTables.begin();
972 itrTab != pDocItem->maTables.end(); ++itrTab)
974 if ((*itrTab).get())
975 (*itrTab)->setReferenced( true);
977 addCacheDocToReferenced( nFileId);
978 return areAllCacheTablesReferenced();
981 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets, bool bPermanent )
983 DocItem* pDoc = getDocItem(nFileId);
984 if (pDoc)
986 size_t nIndex = 0;
987 OUString aTabNameUpper = ScGlobal::pCharClass->uppercase( rTabName);
988 if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex))
990 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
991 for (size_t i = nIndex; i < nStop; ++i)
993 TableTypeRef pTab = pDoc->maTables[i];
994 if (pTab.get())
996 Table::ReferencedFlag eNewFlag = (bPermanent ?
997 Table::REFERENCED_PERMANENT :
998 Table::REFERENCED_MARKED);
999 Table::ReferencedFlag eOldFlag = pTab->getReferencedFlag();
1000 if (eOldFlag != Table::REFERENCED_PERMANENT && eNewFlag != eOldFlag)
1002 pTab->setReferencedFlag( eNewFlag);
1003 addCacheTableToReferenced( nFileId, i);
1009 return areAllCacheTablesReferenced();
1012 void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced )
1014 osl::MutexGuard aGuard(&maMtxDocs);
1016 if (bReferenced)
1018 maReferenced.reset(0);
1019 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1021 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
1022 for (::std::vector<TableTypeRef>::iterator itrTab = rDocItem.maTables.begin();
1023 itrTab != rDocItem.maTables.end(); ++itrTab)
1025 if ((*itrTab).get())
1026 (*itrTab)->setReferenced( true);
1030 else
1032 size_t nDocs = 0;
1033 for (DocDataType::const_iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1035 if (nDocs <= (*itrDoc).first)
1036 nDocs = (*itrDoc).first + 1;
1038 maReferenced.reset( nDocs);
1040 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1042 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
1043 sal_uInt16 nFileId = (*itrDoc).first;
1044 size_t nTables = rDocItem.maTables.size();
1045 ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId];
1046 // All referenced => non-existing tables evaluate as completed.
1047 rDocReferenced.maTables.resize( nTables, true);
1048 for (size_t i=0; i < nTables; ++i)
1050 TableTypeRef & xTab = rDocItem.maTables[i];
1051 if (xTab.get())
1053 if (xTab->getReferencedFlag() == Table::REFERENCED_PERMANENT)
1054 addCacheTableToReferenced( nFileId, i);
1055 else
1057 xTab->setReferencedFlag( Table::UNREFERENCED);
1058 rDocReferenced.maTables[i] = false;
1059 rDocReferenced.mbAllTablesReferenced = false;
1060 // An addCacheTableToReferenced() actually may have
1061 // resulted in mbAllReferenced been set. Clear it.
1062 maReferenced.mbAllReferenced = false;
1070 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
1072 if (nFileId >= maReferenced.maDocs.size())
1073 return;
1075 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1076 size_t nTables = rTables.size();
1077 if (nIndex >= nTables)
1078 return;
1080 if (!rTables[nIndex])
1082 rTables[nIndex] = true;
1083 size_t i = 0;
1084 while (i < nTables && rTables[i])
1085 ++i;
1086 if (i == nTables)
1088 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1089 maReferenced.checkAllDocs();
1094 void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId )
1096 if (nFileId >= maReferenced.maDocs.size())
1097 return;
1099 if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
1101 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1102 size_t nSize = rTables.size();
1103 for (size_t i=0; i < nSize; ++i)
1104 rTables[i] = true;
1105 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1106 maReferenced.checkAllDocs();
1110 bool ScExternalRefCache::areAllCacheTablesReferenced() const
1112 return maReferenced.mbAllReferenced;
1115 void ScExternalRefCache::getAllCachedDataSpans( sal_uInt16 nFileId, sc::ColumnSpanSet& rSet ) const
1117 const DocItem* pDocItem = getDocItem(nFileId);
1118 if (!pDocItem)
1119 // This document is not cached.
1120 return;
1122 const std::vector<TableTypeRef>& rTables = pDocItem->maTables;
1123 for (size_t nTab = 0, nTabCount = rTables.size(); nTab < nTabCount; ++nTab)
1125 TableTypeRef pTab = rTables[nTab];
1126 if (!pTab)
1127 continue;
1129 std::vector<SCROW> aRows;
1130 pTab->getAllRows(aRows);
1131 std::vector<SCROW>::const_iterator itRow = aRows.begin(), itRowEnd = aRows.end();
1132 for (; itRow != itRowEnd; ++itRow)
1134 SCROW nRow = *itRow;
1135 std::vector<SCCOL> aCols;
1136 pTab->getAllCols(nRow, aCols);
1137 std::vector<SCCOL>::const_iterator itCol = aCols.begin(), itColEnd = aCols.end();
1138 for (; itCol != itColEnd; ++itCol)
1140 SCCOL nCol = *itCol;
1141 rSet.set(nTab, nCol, nRow, true);
1147 ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
1148 mbAllReferenced(false)
1150 reset(0);
1153 void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs )
1155 if (nDocs)
1157 mbAllReferenced = false;
1158 DocReferencedVec aRefs( nDocs);
1159 maDocs.swap( aRefs);
1161 else
1163 mbAllReferenced = true;
1164 DocReferencedVec aRefs;
1165 maDocs.swap( aRefs);
1169 void ScExternalRefCache::ReferencedStatus::checkAllDocs()
1171 for (DocReferencedVec::const_iterator itr = maDocs.begin(); itr != maDocs.end(); ++itr)
1173 if (!(*itr).mbAllTablesReferenced)
1174 return;
1176 mbAllReferenced = true;
1179 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1181 DocItem* pDoc = getDocItem(nFileId);
1182 if (!pDoc || nTabIndex >= pDoc->maTables.size())
1183 return TableTypeRef();
1185 return pDoc->maTables[nTabIndex];
1188 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex)
1190 // In API, the index is transported as cached sheet ID of type sal_Int32 in
1191 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
1192 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
1193 // being 0xffffffff
1194 const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
1196 DocItem* pDoc = getDocItem(nFileId);
1197 if (!pDoc)
1199 if (pnIndex) *pnIndex = nNotAvailable;
1200 return TableTypeRef();
1203 DocItem& rDoc = *pDoc;
1205 size_t nIndex;
1206 OUString aTabNameUpper = ScGlobal::pCharClass->uppercase(rTabName);
1207 if (lcl_getTableDataIndex(rDoc.maTableNameIndex, aTabNameUpper, nIndex))
1209 // specified table found.
1210 if( pnIndex ) *pnIndex = nIndex;
1211 if (bCreateNew && !rDoc.maTables[nIndex])
1212 rDoc.maTables[nIndex].reset(new Table);
1214 return rDoc.maTables[nIndex];
1217 if (!bCreateNew)
1219 if (pnIndex) *pnIndex = nNotAvailable;
1220 return TableTypeRef();
1223 // Specified table doesn't exist yet. Create one.
1224 nIndex = rDoc.maTables.size();
1225 if( pnIndex ) *pnIndex = nIndex;
1226 TableTypeRef pTab(new Table);
1227 rDoc.maTables.push_back(pTab);
1228 rDoc.maTableNames.push_back(TableName(aTabNameUpper, rTabName));
1229 rDoc.maTableNameIndex.insert(
1230 TableNameIndexMap::value_type(aTabNameUpper, nIndex));
1231 return pTab;
1234 void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
1236 osl::MutexGuard aGuard(&maMtxDocs);
1237 maDocs.erase(nFileId);
1240 void ScExternalRefCache::clearCacheTables(sal_uInt16 nFileId)
1242 osl::MutexGuard aGuard(&maMtxDocs);
1243 DocItem* pDocItem = getDocItem(nFileId);
1244 if (!pDocItem)
1245 // This document is not cached at all.
1246 return;
1248 // Clear all cache table content, but keep the tables.
1249 std::vector<TableTypeRef>& rTabs = pDocItem->maTables;
1250 for (size_t i = 0, n = rTabs.size(); i < n; ++i)
1252 TableTypeRef pTab = rTabs[i];
1253 if (!pTab)
1254 continue;
1256 pTab->clear();
1259 // Clear the external range name caches.
1260 pDocItem->maRangeNames.clear();
1261 pDocItem->maRangeArrays.clear();
1262 pDocItem->maRealRangeNameMap.clear();
1265 ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const
1267 osl::MutexGuard aGuard(&maMtxDocs);
1269 using ::std::pair;
1270 DocDataType::iterator itrDoc = maDocs.find(nFileId);
1271 if (itrDoc == maDocs.end())
1273 // specified document is not cached.
1274 pair<DocDataType::iterator, bool> res = maDocs.insert(
1275 DocDataType::value_type(nFileId, DocItem()));
1277 if (!res.second)
1278 // insertion failed.
1279 return NULL;
1281 itrDoc = res.first;
1284 return &itrDoc->second;
1287 ScExternalRefLink::ScExternalRefLink(ScDocument* pDoc, sal_uInt16 nFileId, const OUString& rFilter) :
1288 ::sfx2::SvBaseLink(::sfx2::LINKUPDATE_ONCALL, FORMAT_FILE),
1289 mnFileId(nFileId),
1290 maFilterName(rFilter),
1291 mpDoc(pDoc),
1292 mbDoRefresh(true)
1296 ScExternalRefLink::~ScExternalRefLink()
1300 void ScExternalRefLink::Closed()
1302 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
1303 pMgr->breakLink(mnFileId);
1306 ::sfx2::SvBaseLink::UpdateResult ScExternalRefLink::DataChanged(const OUString& /*rMimeType*/, const Any& /*rValue*/)
1308 if (!mbDoRefresh)
1309 return SUCCESS;
1311 OUString aFile, aFilter;
1312 mpDoc->GetLinkManager()->GetDisplayNames(this, NULL, &aFile, NULL, &aFilter);
1313 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
1315 if (!pMgr->isFileLoadable(aFile))
1316 return ERROR_GENERAL;
1318 const OUString* pCurFile = pMgr->getExternalFileName(mnFileId);
1319 if (!pCurFile)
1320 return ERROR_GENERAL;
1322 if (pCurFile->equals(aFile))
1324 // Refresh the current source document.
1325 if (!pMgr->refreshSrcDocument(mnFileId))
1326 return ERROR_GENERAL;
1328 else
1330 // The source document has changed.
1331 ScDocShell* pDocShell = ScDocShell::GetViewData()->GetDocShell();
1332 ScDocShellModificator aMod(*pDocShell);
1333 pMgr->switchSrcFile(mnFileId, aFile, aFilter);
1334 maFilterName = aFilter;
1335 aMod.SetDocumentModified();
1338 return SUCCESS;
1341 void ScExternalRefLink::Edit(Window* pParent, const Link& /*rEndEditHdl*/)
1343 SvBaseLink::Edit(pParent, LINK(this, ScExternalRefLink, ExternalRefEndEditHdl));
1346 void ScExternalRefLink::SetDoReferesh(bool b)
1348 mbDoRefresh = b;
1351 IMPL_LINK_NOARG(ScExternalRefLink, ExternalRefEndEditHdl)
1353 return 0;
1356 static FormulaToken* convertToToken( ScDocument* pHostDoc, ScDocument* pSrcDoc, ScRefCellValue& rCell )
1358 if (rCell.hasEmptyValue())
1360 bool bInherited = (rCell.meType == CELLTYPE_FORMULA);
1361 return new ScEmptyCellToken(bInherited, false);
1364 switch (rCell.meType)
1366 case CELLTYPE_EDIT:
1367 case CELLTYPE_STRING:
1369 OUString aStr = rCell.getString(pSrcDoc);
1370 svl::SharedString aSS = pHostDoc->GetSharedStringPool().intern(aStr);
1371 return new formula::FormulaStringToken(aSS);
1373 case CELLTYPE_VALUE:
1374 return new formula::FormulaDoubleToken(rCell.mfValue);
1375 case CELLTYPE_FORMULA:
1377 ScFormulaCell* pFCell = rCell.mpFormula;
1378 sal_uInt16 nError = pFCell->GetErrCode();
1379 if (nError)
1380 return new FormulaErrorToken( nError);
1381 else if (pFCell->IsValue())
1383 double fVal = pFCell->GetValue();
1384 return new formula::FormulaDoubleToken(fVal);
1386 else
1388 svl::SharedString aStr = pFCell->GetString();
1389 return new formula::FormulaStringToken(aStr);
1392 default:
1393 OSL_FAIL("attempted to convert an unknown cell type.");
1396 return NULL;
1399 template<class T>
1400 struct ColumnBatch
1402 ScDocument* mpHostDoc;
1403 ScDocument* mpSrcDoc;
1405 std::vector<T> maStorage;
1406 CellType meType1;
1407 CellType meType2;
1408 SCROW mnRowStart;
1410 ColumnBatch( ScDocument* pHostDoc, ScDocument* pSrcDoc, CellType eType1, CellType eType2 ) :
1411 mpHostDoc(pHostDoc),
1412 mpSrcDoc(pSrcDoc),
1413 meType1(eType1),
1414 meType2(eType2),
1415 mnRowStart(-1) {}
1417 void update(ScRefCellValue& raCell, const SCCOL nCol, const SCROW nRow, ScMatrixRef& xMat)
1419 if (raCell.meType == meType1 || raCell.meType == meType2)
1421 if (mnRowStart < 0)
1422 mnRowStart = nRow;
1423 maStorage.push_back(getValue(raCell));
1425 else
1427 flush(nCol, xMat);
1431 void flush(const SCCOL nCol, ScMatrixRef& xMat)
1433 if (maStorage.empty())
1434 return;
1435 putValues(xMat, nCol);
1436 mnRowStart = -1;
1437 maStorage.clear();
1440 T getValue(ScRefCellValue& raCell) const;
1441 void putValues(ScMatrixRef& xMat, const SCCOL nCol) const;
1444 template<>
1445 inline svl::SharedString ColumnBatch<svl::SharedString>::getValue(ScRefCellValue& rCell) const
1447 OUString aStr = rCell.getString(mpSrcDoc);
1448 return mpHostDoc->GetSharedStringPool().intern(aStr);
1451 template<class T>
1452 inline T ColumnBatch<T>::getValue(ScRefCellValue& raCell) const
1454 return raCell.mfValue;
1457 template<>
1458 inline void ColumnBatch<svl::SharedString>::putValues(ScMatrixRef& xMat, const SCCOL nCol) const
1460 xMat->PutString(&maStorage.front(), maStorage.size(), nCol, mnRowStart);
1463 template<class T>
1464 inline void ColumnBatch<T>::putValues(ScMatrixRef& xMat, const SCCOL nCol) const
1466 xMat->PutDouble(&maStorage.front(), maStorage.size(), nCol, mnRowStart);
1469 static ScTokenArray* convertToTokenArray(
1470 ScDocument* pHostDoc, ScDocument* pSrcDoc, ScRange& rRange, vector<ScExternalRefCache::SingleRangeData>& rCacheData )
1472 ScAddress& s = rRange.aStart;
1473 ScAddress& e = rRange.aEnd;
1475 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1476 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
1477 SCROW nRow1 = s.Row(), nRow2 = e.Row();
1479 if (nTab2 != nTab1)
1480 // For now, we don't support multi-sheet ranges intentionally because
1481 // we don't have a way to express them in a single token. In the
1482 // future we can introduce a new stack variable type svMatrixList with
1483 // a new token type that can store a 3D matrix value and convert a 3D
1484 // range to it.
1485 return NULL;
1487 ::boost::scoped_ptr<ScRange> pUsedRange;
1489 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1490 auto_ptr<ScTokenArray> pArray(new ScTokenArray);
1491 SAL_WNODEPRECATED_DECLARATIONS_POP
1492 bool bFirstTab = true;
1493 vector<ScExternalRefCache::SingleRangeData>::iterator
1494 itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
1496 for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
1498 // Only loop within the data area.
1499 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
1500 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
1501 bool bShrunk;
1502 if (!pSrcDoc->ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false))
1503 // no data within specified range.
1504 continue;
1506 if (pUsedRange.get())
1507 // Make sure the used area only grows, not shrinks.
1508 pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1509 else
1510 pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1512 ScMatrixRef xMat = new ScMatrix(
1513 static_cast<SCSIZE>(nCol2-nCol1+1), static_cast<SCSIZE>(nRow2-nRow1+1));
1515 ScRefCellValue aCell;
1516 ColumnBatch<svl::SharedString> aStringBatch(pHostDoc, pSrcDoc, CELLTYPE_STRING, CELLTYPE_EDIT);
1517 ColumnBatch<double> aDoubleBatch(pHostDoc, pSrcDoc, CELLTYPE_VALUE, CELLTYPE_VALUE);
1519 for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol)
1521 const SCSIZE nC = nCol - nCol1;
1522 for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow)
1524 const SCSIZE nR = nRow - nRow1;
1526 aCell.assign(*pSrcDoc, ScAddress(nCol, nRow, nTab));
1528 aStringBatch.update(aCell, nC, nR, xMat);
1529 aDoubleBatch.update(aCell, nC, nR, xMat);
1531 if (aCell.hasEmptyValue())
1532 // Skip empty cells. Matrix's default values are empty elements.
1533 continue;
1535 switch (aCell.meType)
1537 case CELLTYPE_FORMULA:
1539 ScFormulaCell* pFCell = aCell.mpFormula;
1540 sal_uInt16 nError = pFCell->GetErrCode();
1541 if (nError)
1542 xMat->PutDouble( CreateDoubleError( nError), nC, nR);
1543 else if (pFCell->IsValue())
1545 double fVal = pFCell->GetValue();
1546 xMat->PutDouble(fVal, nC, nR);
1548 else
1550 svl::SharedString aStr = pFCell->GetString();
1551 aStr = pHostDoc->GetSharedStringPool().intern(aStr.getString());
1552 xMat->PutString(aStr, nC, nR);
1555 break;
1556 default:
1557 OSL_FAIL("attempted to convert an unknown cell type.");
1561 aStringBatch.flush(nC, xMat);
1562 aDoubleBatch.flush(nC, xMat);
1564 if (!bFirstTab)
1565 pArray->AddOpCode(ocSep);
1567 ScMatrixToken aToken(xMat);
1568 pArray->AddToken(aToken);
1570 itrCache->mpRangeData = xMat;
1572 bFirstTab = false;
1575 if (!pUsedRange.get())
1576 return NULL;
1578 s.SetCol(pUsedRange->aStart.Col());
1579 s.SetRow(pUsedRange->aStart.Row());
1580 e.SetCol(pUsedRange->aEnd.Col());
1581 e.SetRow(pUsedRange->aEnd.Row());
1583 return pArray.release();
1586 static ScTokenArray* lcl_fillEmptyMatrix(const ScRange& rRange)
1588 SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1);
1589 SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1);
1590 ScMatrixRef xMat = new ScMatrix(nC, nR);
1592 ScMatrixToken aToken(xMat);
1593 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1594 auto_ptr<ScTokenArray> pArray(new ScTokenArray);
1595 SAL_WNODEPRECATED_DECLARATIONS_POP
1596 pArray->AddToken(aToken);
1597 return pArray.release();
1600 ScExternalRefManager::ScExternalRefManager(ScDocument* pDoc) :
1601 mpDoc(pDoc),
1602 mbInReferenceMarking(false),
1603 mbUserInteractionEnabled(true),
1604 mbDocTimerEnabled(true)
1606 maSrcDocTimer.SetTimeoutHdl( LINK(this, ScExternalRefManager, TimeOutHdl) );
1607 maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL);
1610 ScExternalRefManager::~ScExternalRefManager()
1612 clear();
1615 OUString ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
1617 return maRefCache.getTableName(nFileId, nTabIndex);
1620 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1622 return maRefCache.getCacheTable(nFileId, nTabIndex);
1625 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(
1626 sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex)
1628 return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex);
1631 ScExternalRefManager::LinkListener::LinkListener()
1635 ScExternalRefManager::LinkListener::~LinkListener()
1639 ScExternalRefManager::ApiGuard::ApiGuard(ScDocument* pDoc) :
1640 mpMgr(pDoc->GetExternalRefManager()),
1641 mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled)
1643 // We don't want user interaction handled in the API.
1644 mpMgr->mbUserInteractionEnabled = false;
1647 ScExternalRefManager::ApiGuard::~ApiGuard()
1649 // Restore old value.
1650 mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled;
1653 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1655 maRefCache.getAllTableNames(nFileId, rTabNames);
1658 SCsTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1660 return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
1663 void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
1665 maRefCache.getAllNumberFormats(rNumFmts);
1668 sal_uInt16 ScExternalRefManager::getExternalFileCount() const
1670 return static_cast< sal_uInt16 >( maSrcFiles.size() );
1673 bool ScExternalRefManager::markUsedByLinkListeners()
1675 bool bAllMarked = false;
1676 for (LinkListenerMap::const_iterator itr = maLinkListeners.begin();
1677 itr != maLinkListeners.end() && !bAllMarked; ++itr)
1679 if (!(*itr).second.empty())
1680 bAllMarked = maRefCache.setCacheDocReferenced( (*itr).first);
1681 /* TODO: LinkListeners should remember the table they're listening to.
1682 * As is, listening to one table will mark all tables of the document
1683 * being referenced. */
1685 return bAllMarked;
1688 bool ScExternalRefManager::markUsedExternalRefCells()
1690 RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end();
1691 for (; itr != itrEnd; ++itr)
1693 RefCellSet::iterator itrCell = itr->second.begin(), itrCellEnd = itr->second.end();
1694 for (; itrCell != itrCellEnd; ++itrCell)
1696 ScFormulaCell* pCell = *itrCell;
1697 bool bUsed = pCell->MarkUsedExternalReferences();
1698 if (bUsed)
1699 // Return true when at least one cell references external docs.
1700 return true;
1703 return false;
1706 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1708 return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, false);
1711 void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced )
1713 mbInReferenceMarking = !bReferenced;
1714 maRefCache.setAllCacheTableReferencedStati( bReferenced );
1717 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, const ScTokenArray& rArray)
1719 ScExternalRefCache::TokenArrayRef pArray(rArray.Clone());
1720 maRefCache.setRangeNameTokens(nFileId, rName, pArray);
1723 namespace {
1726 * Put a single cell data into internal cache table.
1728 * @param pFmt optional cell format index that may need to be stored with
1729 * the cell value.
1731 void putCellDataIntoCache(
1732 ScExternalRefCache& rRefCache, const ScExternalRefCache::TokenRef& pToken,
1733 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1734 const ScExternalRefCache::CellFormat* pFmt)
1736 // Now, insert the token into cache table but don't cache empty cells.
1737 if (pToken->GetType() != formula::svEmptyCell)
1739 sal_uLong nFmtIndex = (pFmt && pFmt->mbIsSet) ? pFmt->mnIndex : 0;
1740 rRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pToken, nFmtIndex);
1745 * Put the data into our internal cache table.
1747 * @param rRefCache cache table set.
1748 * @param pArray single range data to be returned.
1749 * @param nFileId external file ID
1750 * @param rTabName name of the table where the data should be cached.
1751 * @param rCacheData range data to be cached.
1752 * @param rCacheRange original cache range, including the empty region if
1753 * any.
1754 * @param rDataRange reduced cache range that includes only the non-empty
1755 * data area.
1757 void putRangeDataIntoCache(
1758 ScExternalRefCache& rRefCache, ScExternalRefCache::TokenArrayRef& pArray,
1759 sal_uInt16 nFileId, const OUString& rTabName,
1760 const vector<ScExternalRefCache::SingleRangeData>& rCacheData,
1761 const ScRange& rCacheRange, const ScRange& rDataRange)
1763 if (pArray)
1764 // Cache these values.
1765 rRefCache.setCellRangeData(nFileId, rDataRange, rCacheData, pArray);
1766 else
1768 // Array is empty. Fill it with an empty matrix of the required size.
1769 pArray.reset(lcl_fillEmptyMatrix(rCacheRange));
1771 // Make sure to set this range 'cached', to prevent unnecessarily
1772 // accessing the src document time and time again.
1773 ScExternalRefCache::TableTypeRef pCacheTab =
1774 rRefCache.getCacheTable(nFileId, rTabName, true, NULL);
1775 if (pCacheTab)
1776 pCacheTab->setCachedCellRange(
1777 rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row());
1782 * When accessing an external document for the first time, we need to
1783 * populate the cache with all its sheet names (whether they are referenced
1784 * or not) in the correct order. Many client codes that use external
1785 * references make this assumption.
1787 * @param rRefCache cache table set.
1788 * @param pSrcDoc source document instance.
1789 * @param nFileId external file ID associated with the source document.
1791 void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sal_uInt16 nFileId)
1793 if (!pSrcDoc)
1794 return;
1796 if (rRefCache.isDocInitialized(nFileId))
1797 // Already initialized. No need to do this twice.
1798 return;
1800 SCTAB nTabCount = pSrcDoc->GetTableCount();
1801 if (nTabCount)
1803 // Populate the cache with all table names in the source document.
1804 vector<OUString> aTabNames;
1805 aTabNames.reserve(nTabCount);
1806 for (SCTAB i = 0; i < nTabCount; ++i)
1808 OUString aName;
1809 pSrcDoc->GetName(i, aName);
1810 aTabNames.push_back(aName);
1812 rRefCache.initializeDoc(nFileId, aTabNames);
1818 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken(
1819 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1820 const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
1822 if (pCurPos)
1823 insertRefCell(nFileId, *pCurPos);
1825 maybeLinkExternalFile(nFileId);
1827 if (pTab)
1828 *pTab = -1;
1830 if (pFmt)
1831 pFmt->mbIsSet = false;
1833 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1834 if (pSrcDoc)
1836 // source document already loaded in memory. Re-use this instance.
1837 SCTAB nTab;
1838 if (!pSrcDoc->GetTable(rTabName, nTab))
1840 // specified table name doesn't exist in the source document.
1841 ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(errNoRef));
1842 return pToken;
1845 if (pTab)
1846 *pTab = nTab;
1848 ScExternalRefCache::TokenRef pToken =
1849 getSingleRefTokenFromSrcDoc(
1850 nFileId, pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1852 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1853 return pToken;
1856 // Check if the given table name and the cell position is cached.
1857 sal_uInt32 nFmtIndex = 0;
1858 ScExternalRefCache::TokenRef pToken = maRefCache.getCellData(
1859 nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex);
1860 if (pToken)
1862 // Cache hit !
1863 fillCellFormat(nFmtIndex, pFmt);
1864 return pToken;
1867 // reference not cached. read from the source document.
1868 pSrcDoc = getSrcDocument(nFileId);
1869 if (!pSrcDoc)
1871 // Source document not reachable. Throw a reference error.
1872 pToken.reset(new FormulaErrorToken(errNoRef));
1873 return pToken;
1876 SCTAB nTab;
1877 if (!pSrcDoc->GetTable(rTabName, nTab))
1879 // specified table name doesn't exist in the source document.
1880 pToken.reset(new FormulaErrorToken(errNoRef));
1881 return pToken;
1884 if (pTab)
1885 *pTab = nTab;
1887 SCCOL nDataCol1 = 0, nDataCol2 = MAXCOL;
1888 SCROW nDataRow1 = 0, nDataRow2 = MAXROW;
1889 bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2);
1890 if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row())
1892 // requested cell is outside the data area. Don't even bother caching
1893 // this data, but add it to the cached range to prevent accessing the
1894 // source document time and time again.
1895 ScExternalRefCache::TableTypeRef pCacheTab =
1896 maRefCache.getCacheTable(nFileId, rTabName, true, NULL);
1897 if (pCacheTab)
1898 pCacheTab->setCachedCell(rCell.Col(), rCell.Row());
1900 pToken.reset(new ScEmptyCellToken(false, false));
1901 return pToken;
1904 pToken = getSingleRefTokenFromSrcDoc(
1905 nFileId, pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1907 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1908 return pToken;
1911 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens(
1912 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
1914 if (pCurPos)
1915 insertRefCell(nFileId, *pCurPos);
1917 maybeLinkExternalFile(nFileId);
1919 ScRange aDataRange(rRange);
1920 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1921 if (pSrcDoc)
1923 // Document already loaded in memory.
1924 vector<ScExternalRefCache::SingleRangeData> aCacheData;
1925 ScExternalRefCache::TokenArrayRef pArray =
1926 getDoubleRefTokensFromSrcDoc(pSrcDoc, rTabName, aDataRange, aCacheData);
1928 // Put the data into cache.
1929 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1930 return pArray;
1933 // Check if the given table name and the cell position is cached.
1934 ScExternalRefCache::TokenArrayRef pArray =
1935 maRefCache.getCellRangeData(nFileId, rTabName, rRange);
1936 if (pArray)
1937 // Cache hit !
1938 return pArray;
1940 pSrcDoc = getSrcDocument(nFileId);
1941 if (!pSrcDoc)
1943 // Source document is not reachable. Throw a reference error.
1944 pArray.reset(new ScTokenArray);
1945 pArray->AddToken(FormulaErrorToken(errNoRef));
1946 return pArray;
1949 vector<ScExternalRefCache::SingleRangeData> aCacheData;
1950 pArray = getDoubleRefTokensFromSrcDoc(pSrcDoc, rTabName, aDataRange, aCacheData);
1952 // Put the data into cache.
1953 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1954 return pArray;
1957 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(
1958 sal_uInt16 nFileId, const OUString& rName, const ScAddress* pCurPos)
1960 if (pCurPos)
1961 insertRefCell(nFileId, *pCurPos);
1963 maybeLinkExternalFile(nFileId);
1965 OUString aName = rName; // make a copy to have the casing corrected.
1966 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1967 if (pSrcDoc)
1969 // Document already loaded in memory.
1970 ScExternalRefCache::TokenArrayRef pArray =
1971 getRangeNameTokensFromSrcDoc(nFileId, pSrcDoc, aName);
1973 if (pArray)
1974 // Cache this range name array.
1975 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
1977 return pArray;
1980 ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName);
1981 if (pArray.get())
1982 // This range name is cached.
1983 return pArray;
1985 pSrcDoc = getSrcDocument(nFileId);
1986 if (!pSrcDoc)
1987 // failed to load document from disk.
1988 return ScExternalRefCache::TokenArrayRef();
1990 pArray = getRangeNameTokensFromSrcDoc(nFileId, pSrcDoc, aName);
1992 if (pArray)
1993 // Cache this range name array.
1994 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
1996 return pArray;
1999 namespace {
2001 bool hasRangeName(ScDocument& rDoc, const OUString& rName)
2003 ScRangeName* pExtNames = rDoc.GetRangeName();
2004 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
2005 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2006 return pRangeData != NULL;
2011 bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId, const OUString& rName)
2013 maybeLinkExternalFile(nFileId);
2014 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2015 if (pSrcDoc)
2017 // Only check the presence of the name.
2018 return hasRangeName(*pSrcDoc, rName);
2021 if (maRefCache.isValidRangeName(nFileId, rName))
2022 // Range name is cached.
2023 return true;
2025 pSrcDoc = getSrcDocument(nFileId);
2026 if (!pSrcDoc)
2027 // failed to load document from disk.
2028 return false;
2030 return hasRangeName(*pSrcDoc, rName);
2033 void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId)
2035 RefCellMap::iterator itrFile = maRefCells.find(nFileId);
2036 if (itrFile == maRefCells.end())
2037 return;
2039 RefCellSet& rRefCells = itrFile->second;
2040 for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell());
2042 ScViewData* pViewData = ScDocShell::GetViewData();
2043 if (!pViewData)
2044 return;
2046 ScTabViewShell* pVShell = pViewData->GetViewShell();
2047 if (!pVShell)
2048 return;
2050 // Repainting the grid also repaints the texts, but is there a better way
2051 // to refresh texts?
2052 pVShell->Invalidate(FID_REPAINT);
2053 pVShell->PaintGrid();
2056 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
2058 RefCellMap::iterator itr = maRefCells.find(nFileId);
2059 if (itr == maRefCells.end())
2061 RefCellSet aRefCells;
2062 pair<RefCellMap::iterator, bool> r = maRefCells.insert(
2063 RefCellMap::value_type(nFileId, aRefCells));
2064 if (!r.second)
2065 // insertion failed.
2066 return;
2068 itr = r.first;
2071 ScFormulaCell* pCell = mpDoc->GetFormulaCell(rCell);
2072 if (pCell)
2073 itr->second.insert(pCell);
2076 void ScExternalRefManager::enableDocTimer( bool bEnable )
2078 if (mbDocTimerEnabled == bEnable)
2079 return;
2081 mbDocTimerEnabled = bEnable;
2082 if (mbDocTimerEnabled)
2084 if (!maDocShells.empty())
2086 DocShellMap::iterator it = maDocShells.begin(), itEnd = maDocShells.end();
2087 for (; it != itEnd; ++it)
2088 it->second.maLastAccess = Time(Time::SYSTEM);
2090 maSrcDocTimer.Start();
2093 else
2094 maSrcDocTimer.Stop();
2097 void ScExternalRefManager::fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat* pFmt) const
2099 if (!pFmt)
2100 return;
2102 short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex);
2103 if (nFmtType != NUMBERFORMAT_UNDEFINED)
2105 pFmt->mbIsSet = true;
2106 pFmt->mnIndex = nFmtIndex;
2107 pFmt->mnType = nFmtType;
2111 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefTokenFromSrcDoc(
2112 sal_uInt16 nFileId, ScDocument* pSrcDoc, const ScAddress& rPos,
2113 ScExternalRefCache::CellFormat* pFmt)
2115 // Get the cell from src doc, and convert it into a token.
2116 ScRefCellValue aCell;
2117 aCell.assign(*pSrcDoc, rPos);
2118 ScExternalRefCache::TokenRef pToken(convertToToken(mpDoc, pSrcDoc, aCell));
2120 if (!pToken.get())
2122 // Generate an error for unresolvable cells.
2123 pToken.reset( new FormulaErrorToken( errNoValue));
2126 // Get number format information.
2127 sal_uInt32 nFmtIndex = 0;
2128 pSrcDoc->GetNumberFormat(rPos.Col(), rPos.Row(), rPos.Tab(), nFmtIndex);
2129 nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, pSrcDoc);
2130 fillCellFormat(nFmtIndex, pFmt);
2131 return pToken;
2134 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokensFromSrcDoc(
2135 ScDocument* pSrcDoc, const OUString& rTabName, ScRange& rRange,
2136 vector<ScExternalRefCache::SingleRangeData>& rCacheData)
2138 ScExternalRefCache::TokenArrayRef pArray;
2139 SCTAB nTab1;
2141 if (!pSrcDoc->GetTable(rTabName, nTab1))
2143 // specified table name doesn't exist in the source document.
2144 pArray.reset(new ScTokenArray);
2145 pArray->AddToken(FormulaErrorToken(errNoRef));
2146 return pArray;
2149 ScRange aRange(rRange);
2150 aRange.Justify();
2151 SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
2153 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2154 aCacheData.reserve(nTabSpan+1);
2155 aCacheData.push_back(ScExternalRefCache::SingleRangeData());
2156 aCacheData.back().maTableName = ScGlobal::pCharClass->uppercase(rTabName);
2158 for (SCTAB i = 1; i < nTabSpan + 1; ++i)
2160 OUString aTabName;
2161 if (!pSrcDoc->GetName(nTab1 + 1, aTabName))
2162 // source document doesn't have any table by the specified name.
2163 break;
2165 aCacheData.push_back(ScExternalRefCache::SingleRangeData());
2166 aCacheData.back().maTableName = ScGlobal::pCharClass->uppercase(aTabName);
2169 aRange.aStart.SetTab(nTab1);
2170 aRange.aEnd.SetTab(nTab1 + nTabSpan);
2172 pArray.reset(convertToTokenArray(mpDoc, pSrcDoc, aRange, aCacheData));
2173 rRange = aRange;
2174 rCacheData.swap(aCacheData);
2175 return pArray;
2178 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokensFromSrcDoc(
2179 sal_uInt16 nFileId, ScDocument* pSrcDoc, OUString& rName)
2181 ScRangeName* pExtNames = pSrcDoc->GetRangeName();
2182 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
2183 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2184 if (!pRangeData)
2185 return ScExternalRefCache::TokenArrayRef();
2187 // Parse all tokens in this external range data, and replace each absolute
2188 // reference token with an external reference token, and cache them. Also
2189 // register the source document with the link manager if it's a new
2190 // source.
2192 ScExternalRefCache::TokenArrayRef pNew(new ScTokenArray);
2194 ScTokenArray aCode(*pRangeData->GetCode());
2195 for (const FormulaToken* pToken = aCode.First(); pToken; pToken = aCode.Next())
2197 bool bTokenAdded = false;
2198 switch (pToken->GetType())
2200 case svSingleRef:
2202 const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
2203 OUString aTabName;
2204 pSrcDoc->GetName(rRef.Tab(), aTabName);
2205 ScExternalSingleRefToken aNewToken(nFileId, aTabName, static_cast<const ScToken*>(pToken)->GetSingleRef());
2206 pNew->AddToken(aNewToken);
2207 bTokenAdded = true;
2209 break;
2210 case svDoubleRef:
2212 const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
2213 OUString aTabName;
2214 pSrcDoc->GetName(rRef.Tab(), aTabName);
2215 ScExternalDoubleRefToken aNewToken(nFileId, aTabName, static_cast<const ScToken*>(pToken)->GetDoubleRef());
2216 pNew->AddToken(aNewToken);
2217 bTokenAdded = true;
2219 break;
2220 default:
2221 ; // nothing
2224 if (!bTokenAdded)
2225 pNew->AddToken(*pToken);
2228 rName = pRangeData->GetName(); // Get the correctly-cased name.
2229 return pNew;
2232 ScDocument* ScExternalRefManager::getInMemorySrcDocument(sal_uInt16 nFileId)
2234 const OUString* pFileName = getExternalFileName(nFileId);
2235 if (!pFileName)
2236 return NULL;
2238 ScDocument* pSrcDoc = NULL;
2239 TypeId aType(TYPE(ScDocShell));
2240 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
2241 while (pShell)
2243 SfxMedium* pMedium = pShell->GetMedium();
2244 if (pMedium && !pMedium->GetName().isEmpty())
2246 // TODO: We should make the case sensitivity platform dependent.
2247 if (pFileName->equalsIgnoreAsciiCase(pMedium->GetName()))
2249 // Found !
2250 pSrcDoc = pShell->GetDocument();
2251 break;
2254 else
2256 // handle unsaved documents here
2257 OUString aName = pShell->GetName();
2258 if (pFileName->equalsIgnoreAsciiCase(aName))
2260 // Found !
2261 SrcShell aSrcDoc;
2262 aSrcDoc.maShell = pShell;
2263 maUnsavedDocShells.insert(DocShellMap::value_type(nFileId, aSrcDoc));
2264 StartListening(*pShell);
2265 pSrcDoc = pShell->GetDocument();
2266 break;
2269 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
2272 initDocInCache(maRefCache, pSrcDoc, nFileId);
2273 return pSrcDoc;
2276 ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId)
2278 if (!mpDoc->IsExecuteLinkEnabled())
2279 return NULL;
2281 DocShellMap::iterator itrEnd = maDocShells.end();
2282 DocShellMap::iterator itr = maDocShells.find(nFileId);
2284 if (itr != itrEnd)
2286 // document already loaded.
2288 SfxObjectShell* p = itr->second.maShell;
2289 itr->second.maLastAccess = Time( Time::SYSTEM );
2290 return static_cast<ScDocShell*>(p)->GetDocument();
2293 itrEnd = maUnsavedDocShells.end();
2294 itr = maUnsavedDocShells.find(nFileId);
2295 if (itr != itrEnd)
2297 //document is unsaved document
2299 SfxObjectShell* p = itr->second.maShell;
2300 itr->second.maLastAccess = Time( Time::SYSTEM );
2301 return static_cast<ScDocShell*>(p)->GetDocument();
2304 const OUString* pFile = getExternalFileName(nFileId);
2305 if (!pFile)
2306 // no file name associated with this ID.
2307 return NULL;
2309 OUString aFilter;
2310 SrcShell aSrcDoc;
2313 aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
2315 catch (const css::uno::Exception&)
2318 if (!aSrcDoc.maShell.Is())
2320 // source document could not be loaded.
2321 return NULL;
2324 return cacheNewDocShell(nFileId, aSrcDoc);
2327 SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUString& rFilter)
2329 const SrcFileData* pFileData = getExternalFileData(nFileId);
2330 if (!pFileData)
2331 return NULL;
2333 // Always load the document by using the path created from the relative
2334 // path. If the referenced document is not there, simply exit. The
2335 // original file name should be used only when the relative path is not
2336 // given.
2337 OUString aFile = pFileData->maFileName;
2338 maybeCreateRealFileName(nFileId);
2339 if (!pFileData->maRealFileName.isEmpty())
2340 aFile = pFileData->maRealFileName;
2342 if (!isFileLoadable(aFile))
2343 return NULL;
2345 OUString aOptions = pFileData->maFilterOptions;
2346 if ( !pFileData->maFilterName.isEmpty() )
2347 rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter
2348 else
2349 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);;
2350 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
2351 const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
2353 if (pFileData->maRelativeName.isEmpty())
2355 // Generate a relative file path.
2356 INetURLObject aBaseURL(getOwnDocumentName());
2357 aBaseURL.insertName(OUString("content.xml"));
2359 OUString aStr = URIHelper::simpleNormalizedMakeRelative(
2360 aBaseURL.GetMainURL(INetURLObject::NO_DECODE), aFile);
2362 setRelativeFileName(nFileId, aStr);
2365 SfxItemSet* pSet = new SfxAllItemSet(SFX_APP()->GetPool());
2366 if (!aOptions.isEmpty())
2367 pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
2369 // make medium hidden to prevent assertion from progress bar
2370 pSet->Put( SfxBoolItem(SID_HIDDEN, true) );
2372 SAL_WNODEPRECATED_DECLARATIONS_PUSH
2373 auto_ptr<SfxMedium> pMedium(new SfxMedium(aFile, STREAM_STD_READ, pFilter, pSet));
2374 SAL_WNODEPRECATED_DECLARATIONS_POP
2375 if (pMedium->GetError() != ERRCODE_NONE)
2376 return NULL;
2378 // To load encrypted documents with password, user interaction needs to be enabled.
2379 pMedium->UseInteractionHandler(mbUserInteractionEnabled);
2381 ScDocShell* pNewShell = new ScDocShell(SFXMODEL_EXTERNAL_LINK);
2382 SfxObjectShellRef aRef = pNewShell;
2384 // increment the recursive link count of the source document.
2385 ScExtDocOptions* pExtOpt = mpDoc->GetExtDocOptions();
2386 sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0;
2387 ScDocument* pSrcDoc = pNewShell->GetDocument();
2388 pSrcDoc->EnableExecuteLink(false); // to prevent circular access of external references.
2389 pSrcDoc->EnableUndo(false);
2390 pSrcDoc->EnableAdjustHeight(false);
2391 pSrcDoc->EnableUserInteraction(false);
2393 ScExtDocOptions* pExtOptNew = pSrcDoc->GetExtDocOptions();
2394 if (!pExtOptNew)
2396 pExtOptNew = new ScExtDocOptions;
2397 pSrcDoc->SetExtDocOptions(pExtOptNew);
2399 pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
2401 if (!pNewShell->DoLoad(pMedium.release()))
2403 aRef->DoClose();
2404 aRef.Clear();
2405 return aRef;
2408 // with UseInteractionHandler, options may be set by dialog during DoLoad
2409 OUString aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium());
2410 if (!aNew.isEmpty() && aNew != aOptions)
2411 aOptions = aNew;
2412 setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options
2414 return aRef;
2417 ScDocument* ScExternalRefManager::cacheNewDocShell( sal_uInt16 nFileId, SrcShell& rSrcShell )
2419 if (mbDocTimerEnabled && maDocShells.empty())
2420 // If this is the first source document insertion, start up the timer.
2421 maSrcDocTimer.Start();
2423 maDocShells.insert(DocShellMap::value_type(nFileId, rSrcShell));
2424 SfxObjectShell& rShell = *rSrcShell.maShell;
2425 ScDocument* pSrcDoc = static_cast<ScDocShell&>(rShell).GetDocument();
2426 initDocInCache(maRefCache, pSrcDoc, nFileId);
2427 return pSrcDoc;
2430 bool ScExternalRefManager::isFileLoadable(const OUString& rFile) const
2432 if (rFile.isEmpty())
2433 return false;
2435 if (isOwnDocument(rFile))
2436 return false;
2437 OUString aPhysical;
2438 if (utl::LocalFileHelper::ConvertURLToPhysicalName(rFile, aPhysical) && !aPhysical.isEmpty())
2440 // #i114504# try IsFolder/Exists only for file URLs
2442 if (utl::UCBContentHelper::IsFolder(rFile))
2443 return false;
2445 return utl::UCBContentHelper::Exists(rFile);
2447 else
2448 return true; // for http and others, Exists doesn't work, but the URL can still be opened
2451 void ScExternalRefManager::maybeLinkExternalFile(sal_uInt16 nFileId)
2453 if (maLinkedDocs.count(nFileId))
2454 // file already linked, or the link has been broken.
2455 return;
2457 // Source document not linked yet. Link it now.
2458 const OUString* pFileName = getExternalFileName(nFileId);
2459 if (!pFileName)
2460 return;
2462 OUString aFilter, aOptions;
2463 const SrcFileData* pFileData = getExternalFileData(nFileId);
2464 if (pFileData)
2466 aFilter = pFileData->maFilterName;
2467 aOptions = pFileData->maFilterOptions;
2469 // If a filter was already set (for example, loading the cached table),
2470 // don't call GetFilterName which has to access the source file.
2471 if (aFilter.isEmpty())
2472 ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false);
2473 sfx2::LinkManager* pLinkMgr = mpDoc->GetLinkManager();
2474 if (!pLinkMgr)
2476 SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL");
2477 return;
2479 ScExternalRefLink* pLink = new ScExternalRefLink(mpDoc, nFileId, aFilter);
2480 OSL_ENSURE(pFileName, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL");
2481 OUString aTmp = aFilter;
2482 pLinkMgr->InsertFileLink(*pLink, OBJECT_CLIENT_FILE, *pFileName, &aTmp);
2484 pLink->SetDoReferesh(false);
2485 pLink->Update();
2486 pLink->SetDoReferesh(true);
2488 maLinkedDocs.insert(LinkedDocMap::value_type(nFileId, true));
2491 void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const OUString& rOwnDocName)
2493 if (maRelativeName.isEmpty())
2494 // No relative path given. Nothing to do.
2495 return;
2497 if (!maRealFileName.isEmpty())
2498 // Real file name already created. Nothing to do.
2499 return;
2501 // Formulate the absolute file path from the relative path.
2502 const OUString& rRelPath = maRelativeName;
2503 INetURLObject aBaseURL(rOwnDocName);
2504 aBaseURL.insertName(OUString("content.xml"));
2505 bool bWasAbs = false;
2506 maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::NO_DECODE);
2509 void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId)
2511 if (nFileId >= maSrcFiles.size())
2512 return;
2514 maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
2517 OUString ScExternalRefManager::getOwnDocumentName() const
2519 SfxObjectShell* pShell = mpDoc->GetDocumentShell();
2520 if (!pShell)
2521 // This should not happen!
2522 return OUString();
2524 SfxMedium* pMed = pShell->GetMedium();
2525 if (!pMed)
2526 return OUString();
2528 return pMed->GetName();
2531 bool ScExternalRefManager::isOwnDocument(const OUString& rFile) const
2533 return getOwnDocumentName().equals(rFile);
2536 void ScExternalRefManager::convertToAbsName(OUString& rFile) const
2538 // unsaved documents have no AbsName
2539 TypeId aType(TYPE(ScDocShell));
2540 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
2541 while (pShell)
2543 if (rFile == pShell->GetName())
2544 return;
2546 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
2549 SfxObjectShell* pDocShell = mpDoc->GetDocumentShell();
2550 rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
2553 sal_uInt16 ScExternalRefManager::getExternalFileId(const OUString& rFile)
2555 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2556 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2557 if (itr != itrEnd)
2559 size_t nId = distance(itrBeg, itr);
2560 return static_cast<sal_uInt16>(nId);
2563 SrcFileData aData;
2564 aData.maFileName = rFile;
2565 maSrcFiles.push_back(aData);
2566 return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
2569 const OUString* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
2571 if (nFileId >= maSrcFiles.size())
2572 return NULL;
2574 if (bForceOriginal)
2575 return &maSrcFiles[nFileId].maFileName;
2577 maybeCreateRealFileName(nFileId);
2579 if (!maSrcFiles[nFileId].maRealFileName.isEmpty())
2580 return &maSrcFiles[nFileId].maRealFileName;
2582 return &maSrcFiles[nFileId].maFileName;
2585 std::vector<OUString> ScExternalRefManager::getAllCachedExternalFileNames() const
2587 std::vector<OUString> aNames;
2588 aNames.reserve(maSrcFiles.size());
2589 std::vector<SrcFileData>::const_iterator it = maSrcFiles.begin(), itEnd = maSrcFiles.end();
2590 for (; it != itEnd; ++it)
2592 const SrcFileData& rData = *it;
2593 aNames.push_back(rData.maFileName);
2596 return aNames;
2599 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
2601 return nFileId < maSrcFiles.size();
2604 bool ScExternalRefManager::hasExternalFile(const OUString& rFile) const
2606 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2607 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2608 return itr != itrEnd;
2611 const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const
2613 if (nFileId >= maSrcFiles.size())
2614 return NULL;
2616 return &maSrcFiles[nFileId];
2619 const OUString* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
2621 return maRefCache.getRealTableName(nFileId, rTabName);
2624 const OUString* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
2626 return maRefCache.getRealRangeName(nFileId, rRangeName);
2629 template<typename MapContainer>
2630 static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
2632 typename MapContainer::iterator itr = rMap.find(nFileId);
2633 if (itr != rMap.end())
2635 // Close this document shell.
2636 itr->second.maShell->DoClose();
2637 rMap.erase(itr);
2641 void ScExternalRefManager::clearCache(sal_uInt16 nFileId)
2643 maRefCache.clearCache(nFileId);
2646 namespace {
2648 class RefCacheFiller : public sc::ColumnSpanSet::ColumnAction
2650 svl::SharedStringPool& mrStrPool;
2652 ScExternalRefCache& mrRefCache;
2653 ScExternalRefCache::TableTypeRef mpRefTab;
2654 sal_uInt16 mnFileId;
2655 ScColumn* mpCurCol;
2656 sc::ColumnBlockConstPosition maBlockPos;
2658 public:
2659 RefCacheFiller( svl::SharedStringPool& rStrPool, ScExternalRefCache& rRefCache, sal_uInt16 nFileId ) :
2660 mrStrPool(rStrPool), mrRefCache(rRefCache), mnFileId(nFileId), mpCurCol(NULL) {}
2662 virtual void startColumn( ScColumn* pCol ) SAL_OVERRIDE
2664 mpCurCol = pCol;
2665 if (!mpCurCol)
2666 return;
2668 mpCurCol->InitBlockPosition(maBlockPos);
2669 mpRefTab = mrRefCache.getCacheTable(mnFileId, mpCurCol->GetTab());
2672 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) SAL_OVERRIDE
2674 if (!mpCurCol || !bVal)
2675 return;
2677 if (!mpRefTab)
2678 return;
2680 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2682 ScExternalRefCache::TokenRef pTok;
2683 ScRefCellValue aCell = mpCurCol->GetCellValue(maBlockPos, nRow);
2684 switch (aCell.meType)
2686 case CELLTYPE_STRING:
2687 case CELLTYPE_EDIT:
2689 OUString aStr = aCell.getString(&mpCurCol->GetDoc());
2690 svl::SharedString aSS = mrStrPool.intern(aStr);
2691 pTok.reset(new formula::FormulaStringToken(aSS));
2693 break;
2694 case CELLTYPE_VALUE:
2695 pTok.reset(new formula::FormulaDoubleToken(aCell.mfValue));
2696 break;
2697 case CELLTYPE_FORMULA:
2699 sc::FormulaResultValue aRes = aCell.mpFormula->GetResult();
2700 switch (aRes.meType)
2702 case sc::FormulaResultValue::Value:
2703 pTok.reset(new formula::FormulaDoubleToken(aRes.mfValue));
2704 break;
2705 case sc::FormulaResultValue::String:
2707 // Re-intern the string to the host document pool.
2708 svl::SharedString aInterned = mrStrPool.intern(aRes.maString.getString());
2709 pTok.reset(new formula::FormulaStringToken(aInterned));
2711 break;
2712 case sc::FormulaResultValue::Error:
2713 case sc::FormulaResultValue::Invalid:
2714 default:
2715 pTok.reset(new FormulaErrorToken(errNoValue));
2718 break;
2719 default:
2720 pTok.reset(new FormulaErrorToken(errNoValue));
2723 if (pTok)
2725 // Cache this cell.
2726 mpRefTab->setCell(mpCurCol->GetCol(), nRow, pTok, mpCurCol->GetNumberFormat(nRow));
2727 mpRefTab->setCachedCell(mpCurCol->GetCol(), nRow);
2735 bool ScExternalRefManager::refreshSrcDocument(sal_uInt16 nFileId)
2737 sc::ColumnSpanSet aCachedArea(false);
2738 maRefCache.getAllCachedDataSpans(nFileId, aCachedArea);
2740 OUString aFilter;
2741 SfxObjectShellRef xDocShell;
2744 xDocShell = loadSrcDocument(nFileId, aFilter);
2746 catch ( const css::uno::Exception& ) {}
2748 if (!xDocShell.Is())
2749 // Failed to load the document. Bail out.
2750 return false;
2752 ScDocShell& rDocSh = static_cast<ScDocShell&>(*xDocShell);
2753 ScDocument* pSrcDoc = rDocSh.GetDocument();
2755 // Clear the existing cache, and refill it. Make sure we keep the
2756 // existing cache table instances here.
2757 maRefCache.clearCacheTables(nFileId);
2758 RefCacheFiller aAction(mpDoc->GetSharedStringPool(), maRefCache, nFileId);
2759 aCachedArea.executeColumnAction(*pSrcDoc, aAction);
2761 DocShellMap::iterator it = maDocShells.find(nFileId);
2762 if (it != maDocShells.end())
2764 it->second.maShell->DoClose();
2765 it->second.maShell = xDocShell;
2766 it->second.maLastAccess = Time(Time::SYSTEM);
2768 else
2770 SrcShell aSrcDoc;
2771 aSrcDoc.maShell = xDocShell;
2772 aSrcDoc.maLastAccess = Time(Time::SYSTEM);
2773 cacheNewDocShell(nFileId, aSrcDoc);
2776 // Update all cells containing names from this source document.
2777 refreshAllRefCells(nFileId);
2779 notifyAllLinkListeners(nFileId, LINK_MODIFIED);
2781 return true;
2784 void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
2786 // Turn all formula cells referencing this external document into static
2787 // cells.
2788 RefCellMap::iterator itrRefs = maRefCells.find(nFileId);
2789 if (itrRefs != maRefCells.end())
2791 // Make a copy because removing the formula cells below will modify
2792 // the original container.
2793 RefCellSet aSet = itrRefs->second;
2794 for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(mpDoc));
2795 maRefCells.erase(nFileId);
2798 // Remove all named ranges that reference this document.
2800 // Global named ranges.
2801 ScRangeName* pRanges = mpDoc->GetRangeName();
2802 if (pRanges)
2803 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2805 // Sheet-local named ranges.
2806 for (SCTAB i = 0, n = mpDoc->GetTableCount(); i < n; ++i)
2808 pRanges = mpDoc->GetRangeName(i);
2809 if (pRanges)
2810 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2813 clearCache(nFileId);
2814 lcl_removeByFileId(nFileId, maDocShells);
2816 if (maDocShells.empty())
2817 maSrcDocTimer.Stop();
2819 LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
2820 if (itr != maLinkedDocs.end())
2821 itr->second = false;
2823 notifyAllLinkListeners(nFileId, LINK_BROKEN);
2826 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const OUString& rNewFile, const OUString& rNewFilter)
2828 maSrcFiles[nFileId].maFileName = rNewFile;
2829 maSrcFiles[nFileId].maRelativeName = OUString();
2830 maSrcFiles[nFileId].maRealFileName = OUString();
2831 if (!maSrcFiles[nFileId].maFilterName.equals(rNewFilter))
2833 // Filter type has changed.
2834 maSrcFiles[nFileId].maFilterName = rNewFilter;
2835 maSrcFiles[nFileId].maFilterOptions = OUString();
2837 refreshSrcDocument(nFileId);
2840 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const OUString& rRelUrl)
2842 if (nFileId >= maSrcFiles.size())
2843 return;
2844 maSrcFiles[nFileId].maRelativeName = rRelUrl;
2847 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const OUString& rFilterName, const OUString& rOptions)
2849 if (nFileId >= maSrcFiles.size())
2850 return;
2851 maSrcFiles[nFileId].maFilterName = rFilterName;
2852 maSrcFiles[nFileId].maFilterOptions = rOptions;
2855 void ScExternalRefManager::clear()
2857 DocShellMap::iterator itrEnd = maDocShells.end();
2858 for (DocShellMap::iterator itr = maDocShells.begin(); itr != itrEnd; ++itr)
2859 itr->second.maShell->DoClose();
2861 maDocShells.clear();
2862 maSrcDocTimer.Stop();
2865 bool ScExternalRefManager::hasExternalData() const
2867 return !maSrcFiles.empty();
2870 void ScExternalRefManager::resetSrcFileData(const OUString& rBaseFileUrl)
2872 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2873 itr != itrEnd; ++itr)
2875 // Re-generate relative file name from the absolute file name.
2876 OUString aAbsName = itr->maRealFileName;
2877 if (aAbsName.isEmpty())
2878 aAbsName = itr->maFileName;
2880 itr->maRelativeName = URIHelper::simpleNormalizedMakeRelative(
2881 rBaseFileUrl, aAbsName);
2885 void ScExternalRefManager::updateAbsAfterLoad()
2887 OUString aOwn( getOwnDocumentName() );
2888 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2889 itr != itrEnd; ++itr)
2891 // update maFileName to the real file name,
2892 // to be called when the original name is no longer needed (after CompileXML)
2894 itr->maybeCreateRealFileName( aOwn );
2895 OUString aReal = itr->maRealFileName;
2896 if (!aReal.isEmpty())
2897 itr->maFileName = aReal;
2901 void ScExternalRefManager::removeRefCell(ScFormulaCell* pCell)
2903 for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell));
2906 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
2908 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2909 if (itr == maLinkListeners.end())
2911 pair<LinkListenerMap::iterator, bool> r = maLinkListeners.insert(
2912 LinkListenerMap::value_type(nFileId, LinkListeners()));
2913 if (!r.second)
2915 OSL_FAIL("insertion of new link listener list failed");
2916 return;
2919 itr = r.first;
2922 LinkListeners& rList = itr->second;
2923 rList.insert(pListener);
2926 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
2928 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2929 if (itr == maLinkListeners.end())
2930 // no listeners for a specified file.
2931 return;
2933 LinkListeners& rList = itr->second;
2934 rList.erase(pListener);
2936 if (rList.empty())
2937 // No more listeners for this file. Remove its entry.
2938 maLinkListeners.erase(itr);
2941 void ScExternalRefManager::removeLinkListener(LinkListener* pListener)
2943 LinkListenerMap::iterator itr = maLinkListeners.begin(), itrEnd = maLinkListeners.end();
2944 for (; itr != itrEnd; ++itr)
2945 itr->second.erase(pListener);
2948 void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
2950 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2951 if (itr == maLinkListeners.end())
2952 // no listeners for a specified file.
2953 return;
2955 LinkListeners& rList = itr->second;
2956 for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
2959 void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut)
2961 // To avoid potentially freezing Calc, we close one stale document at a time.
2962 DocShellMap::iterator itr = maDocShells.begin(), itrEnd = maDocShells.end();
2963 for (; itr != itrEnd; ++itr)
2965 // in 100th of a second.
2966 sal_Int32 nSinceLastAccess = (Time( Time::SYSTEM ) - itr->second.maLastAccess).GetTime();
2967 if (nSinceLastAccess >= nTimeOut)
2969 // Timed out. Let's close this, and exit the loop.
2970 itr->second.maShell->DoClose();
2971 maDocShells.erase(itr);
2972 break;
2976 if (maDocShells.empty())
2977 maSrcDocTimer.Stop();
2980 sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument* pSrcDoc)
2982 NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
2983 if (itr == maNumFormatMap.end())
2985 // Number formatter map is not initialized for this external document.
2986 pair<NumFmtMap::iterator, bool> r = maNumFormatMap.insert(
2987 NumFmtMap::value_type(nFileId, SvNumberFormatterMergeMap()));
2989 if (!r.second)
2990 // insertion failed.
2991 return nNumFmt;
2993 itr = r.first;
2994 mpDoc->GetFormatTable()->MergeFormatter( *pSrcDoc->GetFormatTable());
2995 SvNumberFormatterMergeMap aMap = mpDoc->GetFormatTable()->ConvertMergeTableToMap();
2996 itr->second.swap(aMap);
2998 const SvNumberFormatterMergeMap& rMap = itr->second;
2999 SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
3000 if (itrNumFmt != rMap.end())
3001 // mapped value found.
3002 return itrNumFmt->second;
3004 return nNumFmt;
3007 void ScExternalRefManager::transformUnsavedRefToSavedRef( SfxObjectShell* pShell )
3009 DocShellMap::iterator itr = maUnsavedDocShells.begin();
3010 while( itr != maUnsavedDocShells.end() )
3012 if (&(itr->second.maShell) == pShell)
3014 // found that the shell is marked as unsaved
3015 OUString aFileURL = pShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DECODE_TO_IURI);
3016 switchSrcFile(itr->first, aFileURL, OUString());
3017 EndListening(*pShell);
3018 maUnsavedDocShells.erase(itr++);
3023 void ScExternalRefManager::Notify( SfxBroadcaster&, const SfxHint& rHint )
3025 if ( rHint.ISA( SfxEventHint ) )
3027 sal_uLong nEventId = ((SfxEventHint&)rHint).GetEventId();
3028 switch ( nEventId )
3030 case SFX_EVENT_PREPARECLOSEDOC:
3032 SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
3033 ScDocShell* pDocShell = static_cast< ScDocShell* >( pObjShell );
3034 WarningBox aBox( pDocShell->GetActiveDialogParent(), WinBits( WB_OK ),
3035 ScGlobal::GetRscString( STR_CLOSE_WITH_UNSAVED_REFS ) );
3036 aBox.Execute();
3038 break;
3039 case SFX_EVENT_SAVEDOCDONE:
3040 case SFX_EVENT_SAVEASDOCDONE:
3042 SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
3043 transformUnsavedRefToSavedRef(pObjShell);
3045 break;
3046 default:
3047 break;
3052 IMPL_LINK(ScExternalRefManager, TimeOutHdl, AutoTimer*, pTimer)
3054 if (pTimer == &maSrcDocTimer)
3055 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN);
3057 return 0;
3060 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */