fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / ui / docshell / externalrefmgr.cxx
blob8c557af0c8d5fa833621cb4d439530a9ec8bda99
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>
58 #include <com/sun/star/document/MacroExecMode.hpp>
60 #include <memory>
61 #include <algorithm>
63 #include <boost/scoped_ptr.hpp>
65 using ::std::unique_ptr;
66 using ::com::sun::star::uno::Any;
67 using ::std::vector;
68 using ::std::find;
69 using ::std::find_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 formula::FormulaToken* p = pArray->GetNextReference();
209 for (; p; p = 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 bool ScExternalRefCache::Table::isReferenced() const
279 return meReferenced != UNREFERENCED;
282 void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef pToken, sal_uLong nFmtIndex, bool bSetCacheRange)
284 using ::std::pair;
285 RowsDataType::iterator itrRow = maRows.find(nRow);
286 if (itrRow == maRows.end())
288 // This row does not exist yet.
289 pair<RowsDataType::iterator, bool> res = maRows.insert(
290 RowsDataType::value_type(nRow, RowDataType()));
292 if (!res.second)
293 return;
295 itrRow = res.first;
298 // Insert this token into the specified column location. I don't need to
299 // check for existing data. Just overwrite it.
300 RowDataType& rRow = itrRow->second;
301 ScExternalRefCache::Cell aCell;
302 aCell.mxToken = pToken;
303 aCell.mnFmtIndex = nFmtIndex;
304 rRow.insert(RowDataType::value_type(nCol, aCell));
305 if (bSetCacheRange)
306 setCachedCell(nCol, nRow);
309 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const
311 RowsDataType::const_iterator itrTable = maRows.find(nRow);
312 if (itrTable == maRows.end())
314 // this table doesn't have the specified row.
315 return getEmptyOrNullToken(nCol, nRow);
318 const RowDataType& rRowData = itrTable->second;
319 RowDataType::const_iterator itrRow = rRowData.find(nCol);
320 if (itrRow == rRowData.end())
322 // this row doesn't have the specified column.
323 return getEmptyOrNullToken(nCol, nRow);
326 const Cell& rCell = itrRow->second;
327 if (pnFmtIndex)
328 *pnFmtIndex = rCell.mnFmtIndex;
330 return rCell.mxToken;
333 bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const
335 RowsDataType::const_iterator itrRow = maRows.find(nRow);
336 return itrRow != maRows.end();
339 void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const
341 vector<SCROW> aRows;
342 aRows.reserve(maRows.size());
343 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end();
344 for (; itr != itrEnd; ++itr)
345 if (nLow <= itr->first && itr->first <= nHigh)
346 aRows.push_back(itr->first);
348 // hash map is not ordered, so we need to explicitly sort it.
349 ::std::sort(aRows.begin(), aRows.end());
350 rRows.swap(aRows);
353 ::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const
355 ::std::pair< SCROW, SCROW > aRange( 0, 0 );
356 if( !maRows.empty() )
358 // iterate over entire container (hash map is not sorted by key)
359 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end();
360 aRange.first = itr->first;
361 aRange.second = itr->first + 1;
362 while( ++itr != itrEnd )
364 if( itr->first < aRange.first )
365 aRange.first = itr->first;
366 else if( itr->first >= aRange.second )
367 aRange.second = itr->first + 1;
370 return aRange;
373 void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const
375 RowsDataType::const_iterator itrRow = maRows.find(nRow);
376 if (itrRow == maRows.end())
377 // this table doesn't have the specified row.
378 return;
380 const RowDataType& rRowData = itrRow->second;
381 vector<SCCOL> aCols;
382 aCols.reserve(rRowData.size());
383 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
384 for (; itrCol != itrColEnd; ++itrCol)
385 if (nLow <= itrCol->first && itrCol->first <= nHigh)
386 aCols.push_back(itrCol->first);
388 // hash map is not ordered, so we need to explicitly sort it.
389 ::std::sort(aCols.begin(), aCols.end());
390 rCols.swap(aCols);
393 ::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const
395 ::std::pair< SCCOL, SCCOL > aRange( 0, 0 );
397 RowsDataType::const_iterator itrRow = maRows.find( nRow );
398 if (itrRow == maRows.end())
399 // this table doesn't have the specified row.
400 return aRange;
402 const RowDataType& rRowData = itrRow->second;
403 if( !rRowData.empty() )
405 // iterate over entire container (hash map is not sorted by key)
406 RowDataType::const_iterator itr = rRowData.begin(), itrEnd = rRowData.end();
407 aRange.first = itr->first;
408 aRange.second = itr->first + 1;
409 while( ++itr != itrEnd )
411 if( itr->first < aRange.first )
412 aRange.first = itr->first;
413 else if( itr->first >= aRange.second )
414 aRange.second = itr->first + 1;
417 return aRange;
420 void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
422 RowsDataType::const_iterator itrRow = maRows.begin(), itrRowEnd = maRows.end();
423 for (; itrRow != itrRowEnd; ++itrRow)
425 const RowDataType& rRowData = itrRow->second;
426 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
427 for (; itrCol != itrColEnd; ++itrCol)
429 const Cell& rCell = itrCol->second;
430 rNumFmts.push_back(rCell.mnFmtIndex);
435 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
437 return maCachedRanges.In(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
440 void ScExternalRefCache::Table::setCachedCell(SCCOL nCol, SCROW nRow)
442 setCachedCellRange(nCol, nRow, nCol, nRow);
445 void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
447 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
448 if ( maCachedRanges.empty() )
449 maCachedRanges.Append(aRange);
450 else
451 maCachedRanges.Join(aRange);
454 void ScExternalRefCache::Table::setWholeTableCached()
456 setCachedCellRange(0, 0, MAXCOL, MAXROW);
459 bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol, SCROW nRow) const
461 return maCachedRanges.In(ScRange(nCol, nRow, 0, nCol, nRow, 0));
464 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getEmptyOrNullToken(
465 SCCOL nCol, SCROW nRow) const
467 if (isInCachedRanges(nCol, nRow))
469 TokenRef p(new ScEmptyCellToken(false, false));
470 return p;
472 return TokenRef();
475 ScExternalRefCache::TableName::TableName(const OUString& rUpper, const OUString& rReal) :
476 maUpperName(rUpper), maRealName(rReal)
480 ScExternalRefCache::CellFormat::CellFormat() :
481 mbIsSet(false), mnType(css::util::NumberFormat::ALL), mnIndex(0)
485 ScExternalRefCache::ScExternalRefCache() {}
487 ScExternalRefCache::~ScExternalRefCache() {}
489 const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
491 osl::MutexGuard aGuard(&maMtxDocs);
493 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
494 if (itrDoc == maDocs.end())
496 // specified document is not cached.
497 return NULL;
500 const DocItem& rDoc = itrDoc->second;
501 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
502 ScGlobal::pCharClass->uppercase(rTabName));
503 if (itrTabId == rDoc.maTableNameIndex.end())
505 // the specified table is not in cache.
506 return NULL;
509 return &rDoc.maTableNames[itrTabId->second].maRealName;
512 const OUString* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
514 osl::MutexGuard aGuard(&maMtxDocs);
516 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
517 if (itrDoc == maDocs.end())
519 // specified document is not cached.
520 return NULL;
523 const DocItem& rDoc = itrDoc->second;
524 NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find(
525 ScGlobal::pCharClass->uppercase(rRangeName));
526 if (itr == rDoc.maRealRangeNameMap.end())
527 // range name not found.
528 return NULL;
530 return &itr->second;
533 ScExternalRefCache::TokenRef ScExternalRefCache::getCellData(
534 sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex)
536 osl::MutexGuard aGuard(&maMtxDocs);
538 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
539 if (itrDoc == maDocs.end())
541 // specified document is not cached.
542 return TokenRef();
545 const DocItem& rDoc = itrDoc->second;
546 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
547 ScGlobal::pCharClass->uppercase(rTabName));
548 if (itrTabId == rDoc.maTableNameIndex.end())
550 // the specified table is not in cache.
551 return TokenRef();
554 const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
555 if (!pTableData.get())
557 // the table data is not instantiated yet.
558 return TokenRef();
561 return pTableData->getCell(nCol, nRow, pnFmtIndex);
564 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData(
565 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange)
567 osl::MutexGuard aGuard(&maMtxDocs);
569 DocDataType::iterator itrDoc = maDocs.find(nFileId);
570 if (itrDoc == maDocs.end())
571 // specified document is not cached.
572 return TokenArrayRef();
574 DocItem& rDoc = itrDoc->second;
576 TableNameIndexMap::iterator itrTabId = rDoc.maTableNameIndex.find(
577 ScGlobal::pCharClass->uppercase(rTabName));
578 if (itrTabId == rDoc.maTableNameIndex.end())
579 // the specified table is not in cache.
580 return TokenArrayRef();
582 const ScAddress& s = rRange.aStart;
583 const ScAddress& e = rRange.aEnd;
585 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
586 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
587 SCROW nRow1 = s.Row(), nRow2 = e.Row();
589 // Make sure I have all the tables cached.
590 size_t nTabFirstId = itrTabId->second;
591 size_t nTabLastId = nTabFirstId + nTab2 - nTab1;
592 if (nTabLastId >= rDoc.maTables.size())
593 // not all tables are cached.
594 return TokenArrayRef();
596 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
598 RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
599 if (itrRange != rDoc.maRangeArrays.end())
600 // Cache hit!
601 return itrRange->second;
603 ::boost::scoped_ptr<ScRange> pNewRange;
604 TokenArrayRef pArray;
605 bool bFirstTab = true;
606 for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
608 TableTypeRef pTab = rDoc.maTables[nTab];
609 if (!pTab.get())
610 return TokenArrayRef();
612 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
613 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
615 if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2))
617 // specified range is not entirely within cached ranges.
618 return TokenArrayRef();
621 ScMatrixRef xMat = new ScMatrix(
622 static_cast<SCSIZE>(nDataCol2-nDataCol1+1), static_cast<SCSIZE>(nDataRow2-nDataRow1+1));
624 // Only fill non-empty cells, for better performance.
625 vector<SCROW> aRows;
626 pTab->getAllRows(aRows, nDataRow1, nDataRow2);
627 for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr)
629 SCROW nRow = *itr;
630 vector<SCCOL> aCols;
631 pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2);
632 for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol)
634 SCCOL nCol = *itrCol;
635 TokenRef pToken = pTab->getCell(nCol, nRow);
636 if (!pToken)
637 // This should never happen!
638 return TokenArrayRef();
640 SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1;
641 switch (pToken->GetType())
643 case svDouble:
644 xMat->PutDouble(pToken->GetDouble(), nC, nR);
645 break;
646 case svString:
647 xMat->PutString(pToken->GetString(), nC, nR);
648 break;
649 default:
655 if (!bFirstTab)
656 pArray->AddOpCode(ocSep);
658 ScMatrixToken aToken(xMat);
659 if (!pArray)
660 pArray.reset(new ScTokenArray);
661 pArray->AddToken(aToken);
663 bFirstTab = false;
665 if (!pNewRange)
666 pNewRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
667 else
668 pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
671 if (pNewRange)
672 rDoc.maRangeArrays.insert( RangeArrayMap::value_type(*pNewRange, pArray));
673 return pArray;
676 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const OUString& rName)
678 osl::MutexGuard aGuard(&maMtxDocs);
680 DocItem* pDoc = getDocItem(nFileId);
681 if (!pDoc)
682 return TokenArrayRef();
684 RangeNameMap& rMap = pDoc->maRangeNames;
685 RangeNameMap::const_iterator itr = rMap.find(
686 ScGlobal::pCharClass->uppercase(rName));
687 if (itr == rMap.end())
688 return TokenArrayRef();
690 return itr->second;
693 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, TokenArrayRef pArray)
695 osl::MutexGuard aGuard(&maMtxDocs);
697 DocItem* pDoc = getDocItem(nFileId);
698 if (!pDoc)
699 return;
701 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
702 RangeNameMap& rMap = pDoc->maRangeNames;
703 rMap.insert(RangeNameMap::value_type(aUpperName, pArray));
704 pDoc->maRealRangeNameMap.insert(NamePairMap::value_type(aUpperName, rName));
707 bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId, const OUString& rName) const
709 osl::MutexGuard aGuard(&maMtxDocs);
711 DocItem* pDoc = getDocItem(nFileId);
712 if (!pDoc)
713 return false;
715 const RangeNameMap& rMap = pDoc->maRangeNames;
716 return rMap.count(rName) > 0;
719 void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow,
720 TokenRef pToken, sal_uLong nFmtIndex)
722 if (!isDocInitialized(nFileId))
723 return;
725 using ::std::pair;
726 DocItem* pDocItem = getDocItem(nFileId);
727 if (!pDocItem)
728 return;
730 DocItem& rDoc = *pDocItem;
732 // See if the table by this name already exists.
733 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
734 ScGlobal::pCharClass->uppercase(rTabName));
735 if (itrTabName == rDoc.maTableNameIndex.end())
736 // Table not found. Maybe the table name or the file id is wrong ???
737 return;
739 TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
740 if (!pTableData.get())
741 pTableData.reset(new Table);
743 pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
744 pTableData->setCachedCell(nCol, nRow);
747 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
748 const TokenArrayRef& pArray)
750 using ::std::pair;
751 if (rData.empty() || !isDocInitialized(nFileId))
752 // nothing to cache
753 return;
755 // First, get the document item for the given file ID.
756 DocItem* pDocItem = getDocItem(nFileId);
757 if (!pDocItem)
758 return;
760 DocItem& rDoc = *pDocItem;
762 // Now, find the table position of the first table to cache.
763 const OUString& rFirstTabName = rData.front().maTableName;
764 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
765 ScGlobal::pCharClass->uppercase(rFirstTabName));
766 if (itrTabName == rDoc.maTableNameIndex.end())
768 // table index not found.
769 return;
772 size_t nTabFirstId = itrTabName->second;
773 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
774 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
775 vector<SingleRangeData>::const_iterator itrDataBeg = rData.begin(), itrDataEnd = rData.end();
776 for (vector<SingleRangeData>::const_iterator itrData = itrDataBeg; itrData != itrDataEnd; ++itrData)
778 size_t i = nTabFirstId + ::std::distance(itrDataBeg, itrData);
779 TableTypeRef& pTabData = rDoc.maTables[i];
780 if (!pTabData.get())
781 pTabData.reset(new Table);
783 const ScMatrixRef& pMat = itrData->mpRangeData;
784 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
786 const SCSIZE nR = nRow - nRow1;
787 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
789 const SCSIZE nC = nCol - nCol1;
791 ScMatrixValue value = pMat->Get(nC, nR);
793 TokenRef pToken;
795 switch (value.nType) {
796 case SC_MATVAL_VALUE:
797 case SC_MATVAL_BOOLEAN:
798 pToken.reset(new formula::FormulaDoubleToken(value.fVal));
799 break;
800 case SC_MATVAL_STRING:
801 pToken.reset(new formula::FormulaStringToken(value.aStr));
802 break;
803 default:
804 // Don't cache empty cells.
805 break;
808 if (pToken)
809 // Don't mark this cell 'cached' here, for better performance.
810 pTabData->setCell(nCol, nRow, pToken, 0, false);
813 // Mark the whole range 'cached'.
814 pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2);
817 size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab();
818 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
820 rDoc.maRangeArrays.insert( RangeArrayMap::value_type( aCacheRange, pArray));
823 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId)
825 DocItem* pDoc = getDocItem(nFileId);
826 if (!pDoc)
827 return false;
829 return pDoc->mbInitFromSource;
832 static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex)
834 ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
835 if (itr == rMap.end())
836 return false;
838 rIndex = itr->second;
839 return true;
842 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames)
844 DocItem* pDoc = getDocItem(nFileId);
845 if (!pDoc)
846 return;
848 size_t n = rTabNames.size();
850 // table name list - the list must include all table names in the source
851 // document and only to be populated when loading the source document, not
852 // when loading cached data from, say, Excel XCT/CRN records.
853 vector<TableName> aNewTabNames;
854 aNewTabNames.reserve(n);
855 for (vector<OUString>::const_iterator itr = rTabNames.begin(), itrEnd = rTabNames.end();
856 itr != itrEnd; ++itr)
858 TableName aNameItem(ScGlobal::pCharClass->uppercase(*itr), *itr);
859 aNewTabNames.push_back(aNameItem);
861 pDoc->maTableNames.swap(aNewTabNames);
863 // data tables - preserve any existing data that may have been set during
864 // file import.
865 vector<TableTypeRef> aNewTables(n);
866 for (size_t i = 0; i < n; ++i)
868 size_t nIndex;
869 if (lcl_getTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
871 aNewTables[i] = pDoc->maTables[nIndex];
874 pDoc->maTables.swap(aNewTables);
876 // name index map
877 TableNameIndexMap aNewNameIndex;
878 for (size_t i = 0; i < n; ++i)
879 aNewNameIndex.insert(TableNameIndexMap::value_type(pDoc->maTableNames[i].maUpperName, i));
880 pDoc->maTableNameIndex.swap(aNewNameIndex);
882 pDoc->mbInitFromSource = true;
885 OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
887 if( DocItem* pDoc = getDocItem( nFileId ) )
888 if( nCacheId < pDoc->maTableNames.size() )
889 return pDoc->maTableNames[ nCacheId ].maRealName;
890 return EMPTY_OUSTRING;
893 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
895 rTabNames.clear();
896 DocItem* pDoc = getDocItem(nFileId);
897 if (!pDoc)
898 return;
900 size_t n = pDoc->maTableNames.size();
901 rTabNames.reserve(n);
902 for (vector<TableName>::const_iterator itr = pDoc->maTableNames.begin(), itrEnd = pDoc->maTableNames.end();
903 itr != itrEnd; ++itr)
904 rTabNames.push_back(itr->maRealName);
907 SCsTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
909 DocItem* pDoc = getDocItem(nFileId);
910 if (!pDoc)
911 return -1;
913 vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
914 vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
916 vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
917 TabNameSearchPredicate( rStartTabName));
918 if (itrStartTab == itrEnd)
919 return -1;
921 vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
922 TabNameSearchPredicate( rEndTabName));
923 if (itrEndTab == itrEnd)
924 return 0;
926 size_t nStartDist = ::std::distance( itrBeg, itrStartTab);
927 size_t nEndDist = ::std::distance( itrBeg, itrEndTab);
928 return nStartDist <= nEndDist ? static_cast<SCsTAB>(nEndDist - nStartDist + 1) : -static_cast<SCsTAB>(nStartDist - nEndDist + 1);
931 void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
933 osl::MutexGuard aGuard(&maMtxDocs);
935 using ::std::sort;
936 using ::std::unique;
938 vector<sal_uInt32> aNumFmts;
939 for (DocDataType::const_iterator itrDoc = maDocs.begin(), itrDocEnd = maDocs.end();
940 itrDoc != itrDocEnd; ++itrDoc)
942 const vector<TableTypeRef>& rTables = itrDoc->second.maTables;
943 for (vector<TableTypeRef>::const_iterator itrTab = rTables.begin(), itrTabEnd = rTables.end();
944 itrTab != itrTabEnd; ++itrTab)
946 TableTypeRef pTab = *itrTab;
947 if (!pTab)
948 continue;
950 pTab->getAllNumberFormats(aNumFmts);
954 // remove duplicates.
955 sort(aNumFmts.begin(), aNumFmts.end());
956 aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
957 rNumFmts.swap(aNumFmts);
960 bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId )
962 DocItem* pDocItem = getDocItem(nFileId);
963 if (!pDocItem)
964 return areAllCacheTablesReferenced();
966 for (::std::vector<TableTypeRef>::iterator itrTab = pDocItem->maTables.begin();
967 itrTab != pDocItem->maTables.end(); ++itrTab)
969 if ((*itrTab).get())
970 (*itrTab)->setReferenced( true);
972 addCacheDocToReferenced( nFileId);
973 return areAllCacheTablesReferenced();
976 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets, bool bPermanent )
978 DocItem* pDoc = getDocItem(nFileId);
979 if (pDoc)
981 size_t nIndex = 0;
982 OUString aTabNameUpper = ScGlobal::pCharClass->uppercase( rTabName);
983 if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex))
985 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
986 for (size_t i = nIndex; i < nStop; ++i)
988 TableTypeRef pTab = pDoc->maTables[i];
989 if (pTab.get())
991 Table::ReferencedFlag eNewFlag = (bPermanent ?
992 Table::REFERENCED_PERMANENT :
993 Table::REFERENCED_MARKED);
994 Table::ReferencedFlag eOldFlag = pTab->getReferencedFlag();
995 if (eOldFlag != Table::REFERENCED_PERMANENT && eNewFlag != eOldFlag)
997 pTab->setReferencedFlag( eNewFlag);
998 addCacheTableToReferenced( nFileId, i);
1004 return areAllCacheTablesReferenced();
1007 void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced )
1009 osl::MutexGuard aGuard(&maMtxDocs);
1011 if (bReferenced)
1013 maReferenced.reset(0);
1014 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1016 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
1017 for (::std::vector<TableTypeRef>::iterator itrTab = rDocItem.maTables.begin();
1018 itrTab != rDocItem.maTables.end(); ++itrTab)
1020 if ((*itrTab).get())
1021 (*itrTab)->setReferenced( true);
1025 else
1027 size_t nDocs = 0;
1028 for (DocDataType::const_iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1030 if (nDocs <= (*itrDoc).first)
1031 nDocs = (*itrDoc).first + 1;
1033 maReferenced.reset( nDocs);
1035 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1037 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
1038 sal_uInt16 nFileId = (*itrDoc).first;
1039 size_t nTables = rDocItem.maTables.size();
1040 ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId];
1041 // All referenced => non-existing tables evaluate as completed.
1042 rDocReferenced.maTables.resize( nTables, true);
1043 for (size_t i=0; i < nTables; ++i)
1045 TableTypeRef & xTab = rDocItem.maTables[i];
1046 if (xTab.get())
1048 if (xTab->getReferencedFlag() == Table::REFERENCED_PERMANENT)
1049 addCacheTableToReferenced( nFileId, i);
1050 else
1052 xTab->setReferencedFlag( Table::UNREFERENCED);
1053 rDocReferenced.maTables[i] = false;
1054 rDocReferenced.mbAllTablesReferenced = false;
1055 // An addCacheTableToReferenced() actually may have
1056 // resulted in mbAllReferenced been set. Clear it.
1057 maReferenced.mbAllReferenced = false;
1065 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
1067 if (nFileId >= maReferenced.maDocs.size())
1068 return;
1070 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1071 size_t nTables = rTables.size();
1072 if (nIndex >= nTables)
1073 return;
1075 if (!rTables[nIndex])
1077 rTables[nIndex] = true;
1078 size_t i = 0;
1079 while (i < nTables && rTables[i])
1080 ++i;
1081 if (i == nTables)
1083 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1084 maReferenced.checkAllDocs();
1089 void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId )
1091 if (nFileId >= maReferenced.maDocs.size())
1092 return;
1094 if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
1096 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1097 size_t nSize = rTables.size();
1098 for (size_t i=0; i < nSize; ++i)
1099 rTables[i] = true;
1100 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1101 maReferenced.checkAllDocs();
1105 void ScExternalRefCache::getAllCachedDataSpans( sal_uInt16 nFileId, sc::ColumnSpanSet& rSet ) const
1107 const DocItem* pDocItem = getDocItem(nFileId);
1108 if (!pDocItem)
1109 // This document is not cached.
1110 return;
1112 const std::vector<TableTypeRef>& rTables = pDocItem->maTables;
1113 for (size_t nTab = 0, nTabCount = rTables.size(); nTab < nTabCount; ++nTab)
1115 TableTypeRef pTab = rTables[nTab];
1116 if (!pTab)
1117 continue;
1119 std::vector<SCROW> aRows;
1120 pTab->getAllRows(aRows);
1121 std::vector<SCROW>::const_iterator itRow = aRows.begin(), itRowEnd = aRows.end();
1122 for (; itRow != itRowEnd; ++itRow)
1124 SCROW nRow = *itRow;
1125 std::vector<SCCOL> aCols;
1126 pTab->getAllCols(nRow, aCols);
1127 std::vector<SCCOL>::const_iterator itCol = aCols.begin(), itColEnd = aCols.end();
1128 for (; itCol != itColEnd; ++itCol)
1130 SCCOL nCol = *itCol;
1131 rSet.set(nTab, nCol, nRow, true);
1137 ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
1138 mbAllReferenced(false)
1140 reset(0);
1143 void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs )
1145 if (nDocs)
1147 mbAllReferenced = false;
1148 DocReferencedVec aRefs( nDocs);
1149 maDocs.swap( aRefs);
1151 else
1153 mbAllReferenced = true;
1154 DocReferencedVec aRefs;
1155 maDocs.swap( aRefs);
1159 void ScExternalRefCache::ReferencedStatus::checkAllDocs()
1161 for (DocReferencedVec::const_iterator itr = maDocs.begin(); itr != maDocs.end(); ++itr)
1163 if (!(*itr).mbAllTablesReferenced)
1164 return;
1166 mbAllReferenced = true;
1169 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1171 DocItem* pDoc = getDocItem(nFileId);
1172 if (!pDoc || nTabIndex >= pDoc->maTables.size())
1173 return TableTypeRef();
1175 return pDoc->maTables[nTabIndex];
1178 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex)
1180 // In API, the index is transported as cached sheet ID of type sal_Int32 in
1181 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
1182 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
1183 // being 0xffffffff
1184 const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
1186 DocItem* pDoc = getDocItem(nFileId);
1187 if (!pDoc)
1189 if (pnIndex) *pnIndex = nNotAvailable;
1190 return TableTypeRef();
1193 DocItem& rDoc = *pDoc;
1195 size_t nIndex;
1196 OUString aTabNameUpper = ScGlobal::pCharClass->uppercase(rTabName);
1197 if (lcl_getTableDataIndex(rDoc.maTableNameIndex, aTabNameUpper, nIndex))
1199 // specified table found.
1200 if( pnIndex ) *pnIndex = nIndex;
1201 if (bCreateNew && !rDoc.maTables[nIndex])
1202 rDoc.maTables[nIndex].reset(new Table);
1204 return rDoc.maTables[nIndex];
1207 if (!bCreateNew)
1209 if (pnIndex) *pnIndex = nNotAvailable;
1210 return TableTypeRef();
1213 // Specified table doesn't exist yet. Create one.
1214 nIndex = rDoc.maTables.size();
1215 if( pnIndex ) *pnIndex = nIndex;
1216 TableTypeRef pTab(new Table);
1217 rDoc.maTables.push_back(pTab);
1218 rDoc.maTableNames.push_back(TableName(aTabNameUpper, rTabName));
1219 rDoc.maTableNameIndex.insert(
1220 TableNameIndexMap::value_type(aTabNameUpper, nIndex));
1221 return pTab;
1224 void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
1226 osl::MutexGuard aGuard(&maMtxDocs);
1227 maDocs.erase(nFileId);
1230 void ScExternalRefCache::clearCacheTables(sal_uInt16 nFileId)
1232 osl::MutexGuard aGuard(&maMtxDocs);
1233 DocItem* pDocItem = getDocItem(nFileId);
1234 if (!pDocItem)
1235 // This document is not cached at all.
1236 return;
1238 // Clear all cache table content, but keep the tables.
1239 std::vector<TableTypeRef>& rTabs = pDocItem->maTables;
1240 for (size_t i = 0, n = rTabs.size(); i < n; ++i)
1242 TableTypeRef pTab = rTabs[i];
1243 if (!pTab)
1244 continue;
1246 pTab->clear();
1249 // Clear the external range name caches.
1250 pDocItem->maRangeNames.clear();
1251 pDocItem->maRangeArrays.clear();
1252 pDocItem->maRealRangeNameMap.clear();
1255 ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const
1257 osl::MutexGuard aGuard(&maMtxDocs);
1259 using ::std::pair;
1260 DocDataType::iterator itrDoc = maDocs.find(nFileId);
1261 if (itrDoc == maDocs.end())
1263 // specified document is not cached.
1264 pair<DocDataType::iterator, bool> res = maDocs.insert(
1265 DocDataType::value_type(nFileId, DocItem()));
1267 if (!res.second)
1268 // insertion failed.
1269 return NULL;
1271 itrDoc = res.first;
1274 return &itrDoc->second;
1277 ScExternalRefLink::ScExternalRefLink(ScDocument* pDoc, sal_uInt16 nFileId, const OUString& rFilter) :
1278 ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE),
1279 mnFileId(nFileId),
1280 maFilterName(rFilter),
1281 mpDoc(pDoc),
1282 mbDoRefresh(true)
1286 ScExternalRefLink::~ScExternalRefLink()
1290 void ScExternalRefLink::Closed()
1292 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
1293 pMgr->breakLink(mnFileId);
1296 ::sfx2::SvBaseLink::UpdateResult ScExternalRefLink::DataChanged(const OUString& /*rMimeType*/, const Any& /*rValue*/)
1298 if (!mbDoRefresh)
1299 return SUCCESS;
1301 OUString aFile, aFilter;
1302 sfx2::LinkManager::GetDisplayNames(this, NULL, &aFile, NULL, &aFilter);
1303 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
1305 if (!pMgr->isFileLoadable(aFile))
1306 return ERROR_GENERAL;
1308 const OUString* pCurFile = pMgr->getExternalFileName(mnFileId);
1309 if (!pCurFile)
1310 return ERROR_GENERAL;
1312 if (pCurFile->equals(aFile))
1314 // Refresh the current source document.
1315 if (!pMgr->refreshSrcDocument(mnFileId))
1316 return ERROR_GENERAL;
1318 else
1320 // The source document has changed.
1321 ScDocShell* pDocShell = ScDocShell::GetViewData()->GetDocShell();
1322 ScDocShellModificator aMod(*pDocShell);
1323 pMgr->switchSrcFile(mnFileId, aFile, aFilter);
1324 maFilterName = aFilter;
1325 aMod.SetDocumentModified();
1328 return SUCCESS;
1331 void ScExternalRefLink::Edit(vcl::Window* pParent, const Link<>& /*rEndEditHdl*/)
1333 SvBaseLink::Edit(pParent, LINK(this, ScExternalRefLink, ExternalRefEndEditHdl));
1336 void ScExternalRefLink::SetDoReferesh(bool b)
1338 mbDoRefresh = b;
1341 IMPL_STATIC_LINK_NOARG(ScExternalRefLink, ExternalRefEndEditHdl)
1343 return 0;
1346 static FormulaToken* convertToToken( ScDocument* pHostDoc, ScDocument* pSrcDoc, ScRefCellValue& rCell )
1348 if (rCell.hasEmptyValue())
1350 bool bInherited = (rCell.meType == CELLTYPE_FORMULA);
1351 return new ScEmptyCellToken(bInherited, false);
1354 switch (rCell.meType)
1356 case CELLTYPE_EDIT:
1357 case CELLTYPE_STRING:
1359 OUString aStr = rCell.getString(pSrcDoc);
1360 svl::SharedString aSS = pHostDoc->GetSharedStringPool().intern(aStr);
1361 return new formula::FormulaStringToken(aSS);
1363 case CELLTYPE_VALUE:
1364 return new formula::FormulaDoubleToken(rCell.mfValue);
1365 case CELLTYPE_FORMULA:
1367 ScFormulaCell* pFCell = rCell.mpFormula;
1368 sal_uInt16 nError = pFCell->GetErrCode();
1369 if (nError)
1370 return new FormulaErrorToken( nError);
1371 else if (pFCell->IsValue())
1373 double fVal = pFCell->GetValue();
1374 return new formula::FormulaDoubleToken(fVal);
1376 else
1378 svl::SharedString aStr = pFCell->GetString();
1379 return new formula::FormulaStringToken(aStr);
1382 default:
1383 OSL_FAIL("attempted to convert an unknown cell type.");
1386 return NULL;
1389 template<class T>
1390 struct ColumnBatch
1392 ScDocument* mpHostDoc;
1393 ScDocument* mpSrcDoc;
1395 std::vector<T> maStorage;
1396 CellType meType1;
1397 CellType meType2;
1398 SCROW mnRowStart;
1400 ColumnBatch( ScDocument* pHostDoc, ScDocument* pSrcDoc, CellType eType1, CellType eType2 ) :
1401 mpHostDoc(pHostDoc),
1402 mpSrcDoc(pSrcDoc),
1403 meType1(eType1),
1404 meType2(eType2),
1405 mnRowStart(-1) {}
1407 void update(ScRefCellValue& raCell, const SCCOL nCol, const SCROW nRow, ScMatrixRef& xMat)
1409 if (raCell.meType == meType1 || raCell.meType == meType2)
1411 if (mnRowStart < 0)
1412 mnRowStart = nRow;
1413 maStorage.push_back(getValue(raCell));
1415 else
1417 flush(nCol, xMat);
1421 void flush(const SCCOL nCol, ScMatrixRef& xMat)
1423 if (maStorage.empty())
1424 return;
1425 putValues(xMat, nCol);
1426 mnRowStart = -1;
1427 maStorage.clear();
1430 T getValue(ScRefCellValue& raCell) const;
1431 void putValues(ScMatrixRef& xMat, const SCCOL nCol) const;
1434 template<>
1435 inline svl::SharedString ColumnBatch<svl::SharedString>::getValue(ScRefCellValue& rCell) const
1437 OUString aStr = rCell.getString(mpSrcDoc);
1438 return mpHostDoc->GetSharedStringPool().intern(aStr);
1441 template<class T>
1442 inline T ColumnBatch<T>::getValue(ScRefCellValue& raCell) const
1444 return raCell.mfValue;
1447 template<>
1448 inline void ColumnBatch<svl::SharedString>::putValues(ScMatrixRef& xMat, const SCCOL nCol) const
1450 xMat->PutString(&maStorage.front(), maStorage.size(), nCol, mnRowStart);
1453 template<class T>
1454 inline void ColumnBatch<T>::putValues(ScMatrixRef& xMat, const SCCOL nCol) const
1456 xMat->PutDouble(&maStorage.front(), maStorage.size(), nCol, mnRowStart);
1459 static ScTokenArray* convertToTokenArray(
1460 ScDocument* pHostDoc, ScDocument* pSrcDoc, ScRange& rRange, vector<ScExternalRefCache::SingleRangeData>& rCacheData )
1462 ScAddress& s = rRange.aStart;
1463 ScAddress& e = rRange.aEnd;
1465 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1466 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
1467 SCROW nRow1 = s.Row(), nRow2 = e.Row();
1469 if (nTab2 != nTab1)
1470 // For now, we don't support multi-sheet ranges intentionally because
1471 // we don't have a way to express them in a single token. In the
1472 // future we can introduce a new stack variable type svMatrixList with
1473 // a new token type that can store a 3D matrix value and convert a 3D
1474 // range to it.
1475 return NULL;
1477 ::boost::scoped_ptr<ScRange> pUsedRange;
1479 unique_ptr<ScTokenArray> pArray(new ScTokenArray);
1480 bool bFirstTab = true;
1481 vector<ScExternalRefCache::SingleRangeData>::iterator
1482 itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
1484 for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
1486 // Only loop within the data area.
1487 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
1488 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
1489 bool bShrunk;
1490 if (!pSrcDoc->ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false))
1491 // no data within specified range.
1492 continue;
1494 if (pUsedRange.get())
1495 // Make sure the used area only grows, not shrinks.
1496 pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1497 else
1498 pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1500 ScMatrixRef xMat = new ScMatrix(
1501 static_cast<SCSIZE>(nCol2-nCol1+1), static_cast<SCSIZE>(nRow2-nRow1+1));
1503 ScRefCellValue aCell;
1504 ColumnBatch<svl::SharedString> aStringBatch(pHostDoc, pSrcDoc, CELLTYPE_STRING, CELLTYPE_EDIT);
1505 ColumnBatch<double> aDoubleBatch(pHostDoc, pSrcDoc, CELLTYPE_VALUE, CELLTYPE_VALUE);
1507 for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol)
1509 const SCSIZE nC = nCol - nCol1;
1510 for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow)
1512 const SCSIZE nR = nRow - nRow1;
1514 aCell.assign(*pSrcDoc, ScAddress(nCol, nRow, nTab));
1516 aStringBatch.update(aCell, nC, nR, xMat);
1517 aDoubleBatch.update(aCell, nC, nR, xMat);
1519 if (aCell.hasEmptyValue())
1520 // Skip empty cells. Matrix's default values are empty elements.
1521 continue;
1523 switch (aCell.meType)
1525 case CELLTYPE_FORMULA:
1527 ScFormulaCell* pFCell = aCell.mpFormula;
1528 sal_uInt16 nError = pFCell->GetErrCode();
1529 if (nError)
1530 xMat->PutDouble( CreateDoubleError( nError), nC, nR);
1531 else if (pFCell->IsValue())
1533 double fVal = pFCell->GetValue();
1534 xMat->PutDouble(fVal, nC, nR);
1536 else
1538 svl::SharedString aStr = pFCell->GetString();
1539 aStr = pHostDoc->GetSharedStringPool().intern(aStr.getString());
1540 xMat->PutString(aStr, nC, nR);
1543 break;
1544 // These are handled in batch:
1545 case CELLTYPE_VALUE:
1546 case CELLTYPE_STRING:
1547 case CELLTYPE_EDIT:
1548 break;
1549 default:
1550 OSL_FAIL("attempted to convert an unknown cell type.");
1554 aStringBatch.flush(nC, xMat);
1555 aDoubleBatch.flush(nC, xMat);
1557 if (!bFirstTab)
1558 pArray->AddOpCode(ocSep);
1560 ScMatrixToken aToken(xMat);
1561 pArray->AddToken(aToken);
1563 itrCache->mpRangeData = xMat;
1565 bFirstTab = false;
1568 if (!pUsedRange.get())
1569 return NULL;
1571 s.SetCol(pUsedRange->aStart.Col());
1572 s.SetRow(pUsedRange->aStart.Row());
1573 e.SetCol(pUsedRange->aEnd.Col());
1574 e.SetRow(pUsedRange->aEnd.Row());
1576 return pArray.release();
1579 static ScTokenArray* lcl_fillEmptyMatrix(const ScRange& rRange)
1581 SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1);
1582 SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1);
1583 ScMatrixRef xMat = new ScMatrix(nC, nR);
1585 ScMatrixToken aToken(xMat);
1586 unique_ptr<ScTokenArray> pArray(new ScTokenArray);
1587 pArray->AddToken(aToken);
1588 return pArray.release();
1591 ScExternalRefManager::ScExternalRefManager(ScDocument* pDoc) :
1592 mpDoc(pDoc),
1593 mbInReferenceMarking(false),
1594 mbUserInteractionEnabled(true),
1595 mbDocTimerEnabled(true)
1597 maSrcDocTimer.SetTimeoutHdl( LINK(this, ScExternalRefManager, TimeOutHdl) );
1598 maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL);
1601 ScExternalRefManager::~ScExternalRefManager()
1603 clear();
1606 OUString ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
1608 return maRefCache.getTableName(nFileId, nTabIndex);
1611 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1613 return maRefCache.getCacheTable(nFileId, nTabIndex);
1616 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(
1617 sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex)
1619 return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex);
1622 ScExternalRefManager::LinkListener::LinkListener()
1626 ScExternalRefManager::LinkListener::~LinkListener()
1630 ScExternalRefManager::ApiGuard::ApiGuard(ScDocument* pDoc) :
1631 mpMgr(pDoc->GetExternalRefManager()),
1632 mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled)
1634 // We don't want user interaction handled in the API.
1635 mpMgr->mbUserInteractionEnabled = false;
1638 ScExternalRefManager::ApiGuard::~ApiGuard()
1640 // Restore old value.
1641 mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled;
1644 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1646 maRefCache.getAllTableNames(nFileId, rTabNames);
1649 SCsTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1651 return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
1654 void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
1656 maRefCache.getAllNumberFormats(rNumFmts);
1659 sal_uInt16 ScExternalRefManager::getExternalFileCount() const
1661 return static_cast< sal_uInt16 >( maSrcFiles.size() );
1664 bool ScExternalRefManager::markUsedByLinkListeners()
1666 bool bAllMarked = false;
1667 for (LinkListenerMap::const_iterator itr = maLinkListeners.begin();
1668 itr != maLinkListeners.end() && !bAllMarked; ++itr)
1670 if (!(*itr).second.empty())
1671 bAllMarked = maRefCache.setCacheDocReferenced( (*itr).first);
1672 /* TODO: LinkListeners should remember the table they're listening to.
1673 * As is, listening to one table will mark all tables of the document
1674 * being referenced. */
1676 return bAllMarked;
1679 bool ScExternalRefManager::markUsedExternalRefCells()
1681 RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end();
1682 for (; itr != itrEnd; ++itr)
1684 RefCellSet::iterator itrCell = itr->second.begin(), itrCellEnd = itr->second.end();
1685 for (; itrCell != itrCellEnd; ++itrCell)
1687 ScFormulaCell* pCell = *itrCell;
1688 bool bUsed = pCell->MarkUsedExternalReferences();
1689 if (bUsed)
1690 // Return true when at least one cell references external docs.
1691 return true;
1694 return false;
1697 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1699 return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, false);
1702 void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced )
1704 mbInReferenceMarking = !bReferenced;
1705 maRefCache.setAllCacheTableReferencedStati( bReferenced );
1708 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, const ScTokenArray& rArray)
1710 ScExternalRefCache::TokenArrayRef pArray(rArray.Clone());
1711 maRefCache.setRangeNameTokens(nFileId, rName, pArray);
1714 namespace {
1717 * Put a single cell data into internal cache table.
1719 * @param pFmt optional cell format index that may need to be stored with
1720 * the cell value.
1722 void putCellDataIntoCache(
1723 ScExternalRefCache& rRefCache, const ScExternalRefCache::TokenRef& pToken,
1724 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1725 const ScExternalRefCache::CellFormat* pFmt)
1727 // Now, insert the token into cache table but don't cache empty cells.
1728 if (pToken->GetType() != formula::svEmptyCell)
1730 sal_uLong nFmtIndex = (pFmt && pFmt->mbIsSet) ? pFmt->mnIndex : 0;
1731 rRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pToken, nFmtIndex);
1736 * Put the data into our internal cache table.
1738 * @param rRefCache cache table set.
1739 * @param pArray single range data to be returned.
1740 * @param nFileId external file ID
1741 * @param rTabName name of the table where the data should be cached.
1742 * @param rCacheData range data to be cached.
1743 * @param rCacheRange original cache range, including the empty region if
1744 * any.
1745 * @param rDataRange reduced cache range that includes only the non-empty
1746 * data area.
1748 void putRangeDataIntoCache(
1749 ScExternalRefCache& rRefCache, ScExternalRefCache::TokenArrayRef& pArray,
1750 sal_uInt16 nFileId, const OUString& rTabName,
1751 const vector<ScExternalRefCache::SingleRangeData>& rCacheData,
1752 const ScRange& rCacheRange, const ScRange& rDataRange)
1754 if (pArray)
1755 // Cache these values.
1756 rRefCache.setCellRangeData(nFileId, rDataRange, rCacheData, pArray);
1757 else
1759 // Array is empty. Fill it with an empty matrix of the required size.
1760 pArray.reset(lcl_fillEmptyMatrix(rCacheRange));
1762 // Make sure to set this range 'cached', to prevent unnecessarily
1763 // accessing the src document time and time again.
1764 ScExternalRefCache::TableTypeRef pCacheTab =
1765 rRefCache.getCacheTable(nFileId, rTabName, true, NULL);
1766 if (pCacheTab)
1767 pCacheTab->setCachedCellRange(
1768 rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row());
1773 * When accessing an external document for the first time, we need to
1774 * populate the cache with all its sheet names (whether they are referenced
1775 * or not) in the correct order. Many client codes that use external
1776 * references make this assumption.
1778 * @param rRefCache cache table set.
1779 * @param pSrcDoc source document instance.
1780 * @param nFileId external file ID associated with the source document.
1782 void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sal_uInt16 nFileId)
1784 if (!pSrcDoc)
1785 return;
1787 if (rRefCache.isDocInitialized(nFileId))
1788 // Already initialized. No need to do this twice.
1789 return;
1791 SCTAB nTabCount = pSrcDoc->GetTableCount();
1792 if (nTabCount)
1794 // Populate the cache with all table names in the source document.
1795 vector<OUString> aTabNames;
1796 aTabNames.reserve(nTabCount);
1797 for (SCTAB i = 0; i < nTabCount; ++i)
1799 OUString aName;
1800 pSrcDoc->GetName(i, aName);
1801 aTabNames.push_back(aName);
1803 rRefCache.initializeDoc(nFileId, aTabNames);
1809 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken(
1810 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1811 const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
1813 if (pCurPos)
1814 insertRefCell(nFileId, *pCurPos);
1816 maybeLinkExternalFile(nFileId);
1818 if (pTab)
1819 *pTab = -1;
1821 if (pFmt)
1822 pFmt->mbIsSet = false;
1824 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1825 if (pSrcDoc)
1827 // source document already loaded in memory. Re-use this instance.
1828 SCTAB nTab;
1829 if (!pSrcDoc->GetTable(rTabName, nTab))
1831 // specified table name doesn't exist in the source document.
1832 ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(errNoRef));
1833 return pToken;
1836 if (pTab)
1837 *pTab = nTab;
1839 ScExternalRefCache::TokenRef pToken =
1840 getSingleRefTokenFromSrcDoc(
1841 nFileId, pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1843 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1844 return pToken;
1847 // Check if the given table name and the cell position is cached.
1848 sal_uInt32 nFmtIndex = 0;
1849 ScExternalRefCache::TokenRef pToken = maRefCache.getCellData(
1850 nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex);
1851 if (pToken)
1853 // Cache hit !
1854 fillCellFormat(nFmtIndex, pFmt);
1855 return pToken;
1858 // reference not cached. read from the source document.
1859 pSrcDoc = getSrcDocument(nFileId);
1860 if (!pSrcDoc)
1862 // Source document not reachable. Throw a reference error.
1863 pToken.reset(new FormulaErrorToken(errNoRef));
1864 return pToken;
1867 SCTAB nTab;
1868 if (!pSrcDoc->GetTable(rTabName, nTab))
1870 // specified table name doesn't exist in the source document.
1871 pToken.reset(new FormulaErrorToken(errNoRef));
1872 return pToken;
1875 if (pTab)
1876 *pTab = nTab;
1878 SCCOL nDataCol1 = 0, nDataCol2 = MAXCOL;
1879 SCROW nDataRow1 = 0, nDataRow2 = MAXROW;
1880 bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2);
1881 if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row())
1883 // requested cell is outside the data area. Don't even bother caching
1884 // this data, but add it to the cached range to prevent accessing the
1885 // source document time and time again.
1886 ScExternalRefCache::TableTypeRef pCacheTab =
1887 maRefCache.getCacheTable(nFileId, rTabName, true, NULL);
1888 if (pCacheTab)
1889 pCacheTab->setCachedCell(rCell.Col(), rCell.Row());
1891 pToken.reset(new ScEmptyCellToken(false, false));
1892 return pToken;
1895 pToken = getSingleRefTokenFromSrcDoc(
1896 nFileId, pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1898 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1899 return pToken;
1902 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens(
1903 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
1905 if (pCurPos)
1906 insertRefCell(nFileId, *pCurPos);
1908 maybeLinkExternalFile(nFileId);
1910 ScRange aDataRange(rRange);
1911 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1912 if (pSrcDoc)
1914 // Document already loaded in memory.
1915 vector<ScExternalRefCache::SingleRangeData> aCacheData;
1916 ScExternalRefCache::TokenArrayRef pArray =
1917 getDoubleRefTokensFromSrcDoc(pSrcDoc, rTabName, aDataRange, aCacheData);
1919 // Put the data into cache.
1920 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1921 return pArray;
1924 // Check if the given table name and the cell position is cached.
1925 ScExternalRefCache::TokenArrayRef pArray =
1926 maRefCache.getCellRangeData(nFileId, rTabName, rRange);
1927 if (pArray)
1928 // Cache hit !
1929 return pArray;
1931 pSrcDoc = getSrcDocument(nFileId);
1932 if (!pSrcDoc)
1934 // Source document is not reachable. Throw a reference error.
1935 pArray.reset(new ScTokenArray);
1936 pArray->AddToken(FormulaErrorToken(errNoRef));
1937 return pArray;
1940 vector<ScExternalRefCache::SingleRangeData> aCacheData;
1941 pArray = getDoubleRefTokensFromSrcDoc(pSrcDoc, rTabName, aDataRange, aCacheData);
1943 // Put the data into cache.
1944 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1945 return pArray;
1948 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(
1949 sal_uInt16 nFileId, const OUString& rName, const ScAddress* pCurPos)
1951 if (pCurPos)
1952 insertRefCell(nFileId, *pCurPos);
1954 maybeLinkExternalFile(nFileId);
1956 OUString aName = rName; // make a copy to have the casing corrected.
1957 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1958 if (pSrcDoc)
1960 // Document already loaded in memory.
1961 ScExternalRefCache::TokenArrayRef pArray =
1962 getRangeNameTokensFromSrcDoc(nFileId, pSrcDoc, aName);
1964 if (pArray)
1965 // Cache this range name array.
1966 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
1968 return pArray;
1971 ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName);
1972 if (pArray.get())
1973 // This range name is cached.
1974 return pArray;
1976 pSrcDoc = getSrcDocument(nFileId);
1977 if (!pSrcDoc)
1978 // failed to load document from disk.
1979 return ScExternalRefCache::TokenArrayRef();
1981 pArray = getRangeNameTokensFromSrcDoc(nFileId, pSrcDoc, aName);
1983 if (pArray)
1984 // Cache this range name array.
1985 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
1987 return pArray;
1990 namespace {
1992 bool hasRangeName(ScDocument& rDoc, const OUString& rName)
1994 ScRangeName* pExtNames = rDoc.GetRangeName();
1995 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
1996 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
1997 return pRangeData != NULL;
2002 bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId, const OUString& rName)
2004 maybeLinkExternalFile(nFileId);
2005 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2006 if (pSrcDoc)
2008 // Only check the presence of the name.
2009 return hasRangeName(*pSrcDoc, rName);
2012 if (maRefCache.isValidRangeName(nFileId, rName))
2013 // Range name is cached.
2014 return true;
2016 pSrcDoc = getSrcDocument(nFileId);
2017 if (!pSrcDoc)
2018 // failed to load document from disk.
2019 return false;
2021 return hasRangeName(*pSrcDoc, rName);
2024 void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId)
2026 RefCellMap::iterator itrFile = maRefCells.find(nFileId);
2027 if (itrFile == maRefCells.end())
2028 return;
2030 RefCellSet& rRefCells = itrFile->second;
2031 for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell());
2033 ScViewData* pViewData = ScDocShell::GetViewData();
2034 if (!pViewData)
2035 return;
2037 ScTabViewShell* pVShell = pViewData->GetViewShell();
2038 if (!pVShell)
2039 return;
2041 // Repainting the grid also repaints the texts, but is there a better way
2042 // to refresh texts?
2043 pVShell->Invalidate(FID_REPAINT);
2044 pVShell->PaintGrid();
2047 namespace {
2049 void insertRefCellByIterator(
2050 ScExternalRefManager::RefCellMap::iterator& itr, ScFormulaCell* pCell)
2052 if (pCell)
2054 itr->second.insert(pCell);
2055 pCell->SetIsExtRef();
2061 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
2063 RefCellMap::iterator itr = maRefCells.find(nFileId);
2064 if (itr == maRefCells.end())
2066 RefCellSet aRefCells;
2067 pair<RefCellMap::iterator, bool> r = maRefCells.insert(
2068 RefCellMap::value_type(nFileId, aRefCells));
2069 if (!r.second)
2070 // insertion failed.
2071 return;
2073 itr = r.first;
2076 insertRefCellByIterator(itr, mpDoc->GetFormulaCell(rCell));
2079 void ScExternalRefManager::insertRefCellFromTemplate( ScFormulaCell* pTemplateCell, ScFormulaCell* pCell )
2081 if (!pTemplateCell || !pCell)
2082 return;
2084 for (RefCellMap::iterator itr = maRefCells.begin(); itr != maRefCells.end(); ++itr)
2086 if (itr->second.find(pTemplateCell) != itr->second.end())
2087 insertRefCellByIterator(itr, pCell);
2091 bool ScExternalRefManager::hasCellExternalReference(const ScAddress& rCell)
2093 ScFormulaCell* pCell = mpDoc->GetFormulaCell(rCell);
2095 if (pCell)
2096 for (RefCellMap::iterator itr = maRefCells.begin(); itr != maRefCells.end(); ++itr)
2098 if (itr->second.find(pCell) != itr->second.end())
2099 return true;
2102 return false;
2105 void ScExternalRefManager::enableDocTimer( bool bEnable )
2107 if (mbDocTimerEnabled == bEnable)
2108 return;
2110 mbDocTimerEnabled = bEnable;
2111 if (mbDocTimerEnabled)
2113 if (!maDocShells.empty())
2115 DocShellMap::iterator it = maDocShells.begin(), itEnd = maDocShells.end();
2116 for (; it != itEnd; ++it)
2117 it->second.maLastAccess = tools::Time(tools::Time::SYSTEM);
2119 maSrcDocTimer.Start();
2122 else
2123 maSrcDocTimer.Stop();
2126 void ScExternalRefManager::fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat* pFmt) const
2128 if (!pFmt)
2129 return;
2131 short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex);
2132 if (nFmtType != css::util::NumberFormat::UNDEFINED)
2134 pFmt->mbIsSet = true;
2135 pFmt->mnIndex = nFmtIndex;
2136 pFmt->mnType = nFmtType;
2140 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefTokenFromSrcDoc(
2141 sal_uInt16 nFileId, ScDocument* pSrcDoc, const ScAddress& rPos,
2142 ScExternalRefCache::CellFormat* pFmt)
2144 // Get the cell from src doc, and convert it into a token.
2145 ScRefCellValue aCell;
2146 aCell.assign(*pSrcDoc, rPos);
2147 ScExternalRefCache::TokenRef pToken(convertToToken(mpDoc, pSrcDoc, aCell));
2149 if (!pToken.get())
2151 // Generate an error for unresolvable cells.
2152 pToken.reset( new FormulaErrorToken( errNoValue));
2155 // Get number format information.
2156 sal_uInt32 nFmtIndex = 0;
2157 pSrcDoc->GetNumberFormat(rPos.Col(), rPos.Row(), rPos.Tab(), nFmtIndex);
2158 nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, pSrcDoc);
2159 fillCellFormat(nFmtIndex, pFmt);
2160 return pToken;
2163 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokensFromSrcDoc(
2164 ScDocument* pSrcDoc, const OUString& rTabName, ScRange& rRange,
2165 vector<ScExternalRefCache::SingleRangeData>& rCacheData)
2167 ScExternalRefCache::TokenArrayRef pArray;
2168 SCTAB nTab1;
2170 if (!pSrcDoc->GetTable(rTabName, nTab1))
2172 // specified table name doesn't exist in the source document.
2173 pArray.reset(new ScTokenArray);
2174 pArray->AddToken(FormulaErrorToken(errNoRef));
2175 return pArray;
2178 ScRange aRange(rRange);
2179 aRange.Justify();
2180 SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
2182 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2183 aCacheData.reserve(nTabSpan+1);
2184 aCacheData.push_back(ScExternalRefCache::SingleRangeData());
2185 aCacheData.back().maTableName = ScGlobal::pCharClass->uppercase(rTabName);
2187 for (SCTAB i = 1; i < nTabSpan + 1; ++i)
2189 OUString aTabName;
2190 if (!pSrcDoc->GetName(nTab1 + 1, aTabName))
2191 // source document doesn't have any table by the specified name.
2192 break;
2194 aCacheData.push_back(ScExternalRefCache::SingleRangeData());
2195 aCacheData.back().maTableName = ScGlobal::pCharClass->uppercase(aTabName);
2198 aRange.aStart.SetTab(nTab1);
2199 aRange.aEnd.SetTab(nTab1 + nTabSpan);
2201 pArray.reset(convertToTokenArray(mpDoc, pSrcDoc, aRange, aCacheData));
2202 rRange = aRange;
2203 rCacheData.swap(aCacheData);
2204 return pArray;
2207 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokensFromSrcDoc(
2208 sal_uInt16 nFileId, ScDocument* pSrcDoc, OUString& rName)
2210 ScRangeName* pExtNames = pSrcDoc->GetRangeName();
2211 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
2212 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2213 if (!pRangeData)
2214 return ScExternalRefCache::TokenArrayRef();
2216 // Parse all tokens in this external range data, and replace each absolute
2217 // reference token with an external reference token, and cache them. Also
2218 // register the source document with the link manager if it's a new
2219 // source.
2221 ScExternalRefCache::TokenArrayRef pNew(new ScTokenArray);
2223 ScTokenArray aCode(*pRangeData->GetCode());
2224 for (const FormulaToken* pToken = aCode.First(); pToken; pToken = aCode.Next())
2226 bool bTokenAdded = false;
2227 switch (pToken->GetType())
2229 case svSingleRef:
2231 const ScSingleRefData& rRef = *pToken->GetSingleRef();
2232 OUString aTabName;
2233 pSrcDoc->GetName(rRef.Tab(), aTabName);
2234 ScExternalSingleRefToken aNewToken(nFileId, aTabName, *pToken->GetSingleRef());
2235 pNew->AddToken(aNewToken);
2236 bTokenAdded = true;
2238 break;
2239 case svDoubleRef:
2241 const ScSingleRefData& rRef = *pToken->GetSingleRef();
2242 OUString aTabName;
2243 pSrcDoc->GetName(rRef.Tab(), aTabName);
2244 ScExternalDoubleRefToken aNewToken(nFileId, aTabName, *pToken->GetDoubleRef());
2245 pNew->AddToken(aNewToken);
2246 bTokenAdded = true;
2248 break;
2249 default:
2250 ; // nothing
2253 if (!bTokenAdded)
2254 pNew->AddToken(*pToken);
2257 rName = pRangeData->GetName(); // Get the correctly-cased name.
2258 return pNew;
2261 ScDocument* ScExternalRefManager::getInMemorySrcDocument(sal_uInt16 nFileId)
2263 const OUString* pFileName = getExternalFileName(nFileId);
2264 if (!pFileName)
2265 return NULL;
2267 ScDocument* pSrcDoc = NULL;
2268 TypeId aType(TYPE(ScDocShell));
2269 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
2270 while (pShell)
2272 SfxMedium* pMedium = pShell->GetMedium();
2273 if (pMedium && !pMedium->GetName().isEmpty())
2275 // TODO: We should make the case sensitivity platform dependent.
2276 if (pFileName->equalsIgnoreAsciiCase(pMedium->GetName()))
2278 // Found !
2279 pSrcDoc = &pShell->GetDocument();
2280 break;
2283 else
2285 // handle unsaved documents here
2286 OUString aName = pShell->GetName();
2287 if (pFileName->equalsIgnoreAsciiCase(aName))
2289 // Found !
2290 SrcShell aSrcDoc;
2291 aSrcDoc.maShell = pShell;
2292 maUnsavedDocShells.insert(DocShellMap::value_type(nFileId, aSrcDoc));
2293 StartListening(*pShell);
2294 pSrcDoc = &pShell->GetDocument();
2295 break;
2298 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
2301 initDocInCache(maRefCache, pSrcDoc, nFileId);
2302 return pSrcDoc;
2305 ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId)
2307 if (!mpDoc->IsExecuteLinkEnabled())
2308 return NULL;
2310 DocShellMap::iterator itrEnd = maDocShells.end();
2311 DocShellMap::iterator itr = maDocShells.find(nFileId);
2313 if (itr != itrEnd)
2315 // document already loaded.
2317 SfxObjectShell* p = itr->second.maShell;
2318 itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2319 return &static_cast<ScDocShell*>(p)->GetDocument();
2322 itrEnd = maUnsavedDocShells.end();
2323 itr = maUnsavedDocShells.find(nFileId);
2324 if (itr != itrEnd)
2326 //document is unsaved document
2328 SfxObjectShell* p = itr->second.maShell;
2329 itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2330 return &static_cast<ScDocShell*>(p)->GetDocument();
2333 const OUString* pFile = getExternalFileName(nFileId);
2334 if (!pFile)
2335 // no file name associated with this ID.
2336 return NULL;
2338 OUString aFilter;
2339 SrcShell aSrcDoc;
2342 aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
2344 catch (const css::uno::Exception&)
2347 if (!aSrcDoc.maShell.Is())
2349 // source document could not be loaded.
2350 return NULL;
2353 return &cacheNewDocShell(nFileId, aSrcDoc);
2356 SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUString& rFilter)
2358 const SrcFileData* pFileData = getExternalFileData(nFileId);
2359 if (!pFileData)
2360 return NULL;
2362 // Always load the document by using the path created from the relative
2363 // path. If the referenced document is not there, simply exit. The
2364 // original file name should be used only when the relative path is not
2365 // given.
2366 OUString aFile = pFileData->maFileName;
2367 maybeCreateRealFileName(nFileId);
2368 if (!pFileData->maRealFileName.isEmpty())
2369 aFile = pFileData->maRealFileName;
2371 if (!isFileLoadable(aFile))
2372 return NULL;
2374 OUString aOptions = pFileData->maFilterOptions;
2375 if ( !pFileData->maFilterName.isEmpty() )
2376 rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter
2377 else
2378 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
2379 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
2380 const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
2382 if (pFileData->maRelativeName.isEmpty())
2384 // Generate a relative file path.
2385 INetURLObject aBaseURL(getOwnDocumentName());
2386 aBaseURL.insertName(OUString("content.xml"));
2388 OUString aStr = URIHelper::simpleNormalizedMakeRelative(
2389 aBaseURL.GetMainURL(INetURLObject::NO_DECODE), aFile);
2391 setRelativeFileName(nFileId, aStr);
2394 SfxItemSet* pSet = new SfxAllItemSet(SfxGetpApp()->GetPool());
2395 if (!aOptions.isEmpty())
2396 pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
2398 // make medium hidden to prevent assertion from progress bar
2399 pSet->Put( SfxBoolItem(SID_HIDDEN, true) );
2401 // If the current document is allowed to execute macros then the referenced
2402 // document may execute macros according to the security configuration.
2403 SfxObjectShell* pShell = mpDoc->GetDocumentShell();
2404 if (pShell)
2406 SfxMedium* pMedium = pShell->GetMedium();
2407 if (pMedium)
2409 const SfxPoolItem* pItem;
2410 if (pMedium->GetItemSet()->GetItemState( SID_MACROEXECMODE, false, &pItem ) == SfxItemState::SET &&
2411 static_cast<const SfxUInt16Item*>(pItem)->GetValue() != css::document::MacroExecMode::NEVER_EXECUTE)
2412 pSet->Put( SfxUInt16Item( SID_MACROEXECMODE, css::document::MacroExecMode::USE_CONFIG));
2416 unique_ptr<SfxMedium> pMedium(new SfxMedium(aFile, STREAM_STD_READ, pFilter, pSet));
2417 if (pMedium->GetError() != ERRCODE_NONE)
2418 return NULL;
2420 // To load encrypted documents with password, user interaction needs to be enabled.
2421 pMedium->UseInteractionHandler(mbUserInteractionEnabled);
2423 ScDocShell* pNewShell = new ScDocShell(SfxModelFlags::EXTERNAL_LINK);
2424 SfxObjectShellRef aRef = pNewShell;
2426 // increment the recursive link count of the source document.
2427 ScExtDocOptions* pExtOpt = mpDoc->GetExtDocOptions();
2428 sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0;
2429 ScDocument& rSrcDoc = pNewShell->GetDocument();
2430 rSrcDoc.EnableExecuteLink(false); // to prevent circular access of external references.
2431 rSrcDoc.EnableUndo(false);
2432 rSrcDoc.EnableAdjustHeight(false);
2433 rSrcDoc.EnableUserInteraction(false);
2435 ScExtDocOptions* pExtOptNew = rSrcDoc.GetExtDocOptions();
2436 if (!pExtOptNew)
2438 pExtOptNew = new ScExtDocOptions;
2439 rSrcDoc.SetExtDocOptions(pExtOptNew);
2441 pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
2443 if (!pNewShell->DoLoad(pMedium.release()))
2445 aRef->DoClose();
2446 aRef.Clear();
2447 return aRef;
2450 // with UseInteractionHandler, options may be set by dialog during DoLoad
2451 OUString aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium());
2452 if (!aNew.isEmpty() && aNew != aOptions)
2453 aOptions = aNew;
2454 setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options
2456 return aRef;
2459 ScDocument& ScExternalRefManager::cacheNewDocShell( sal_uInt16 nFileId, SrcShell& rSrcShell )
2461 if (mbDocTimerEnabled && maDocShells.empty())
2462 // If this is the first source document insertion, start up the timer.
2463 maSrcDocTimer.Start();
2465 maDocShells.insert(DocShellMap::value_type(nFileId, rSrcShell));
2466 SfxObjectShell& rShell = *rSrcShell.maShell;
2467 ScDocument& rSrcDoc = static_cast<ScDocShell&>(rShell).GetDocument();
2468 initDocInCache(maRefCache, &rSrcDoc, nFileId);
2469 return rSrcDoc;
2472 bool ScExternalRefManager::isFileLoadable(const OUString& rFile) const
2474 if (rFile.isEmpty())
2475 return false;
2477 if (isOwnDocument(rFile))
2478 return false;
2479 OUString aPhysical;
2480 if (utl::LocalFileHelper::ConvertURLToPhysicalName(rFile, aPhysical) && !aPhysical.isEmpty())
2482 // #i114504# try IsFolder/Exists only for file URLs
2484 if (utl::UCBContentHelper::IsFolder(rFile))
2485 return false;
2487 return utl::UCBContentHelper::Exists(rFile);
2489 else
2490 return true; // for http and others, Exists doesn't work, but the URL can still be opened
2493 void ScExternalRefManager::maybeLinkExternalFile(sal_uInt16 nFileId)
2495 if (maLinkedDocs.count(nFileId))
2496 // file already linked, or the link has been broken.
2497 return;
2499 // Source document not linked yet. Link it now.
2500 const OUString* pFileName = getExternalFileName(nFileId);
2501 if (!pFileName)
2502 return;
2504 OUString aFilter, aOptions;
2505 const SrcFileData* pFileData = getExternalFileData(nFileId);
2506 if (pFileData)
2508 aFilter = pFileData->maFilterName;
2509 aOptions = pFileData->maFilterOptions;
2511 // If a filter was already set (for example, loading the cached table),
2512 // don't call GetFilterName which has to access the source file.
2513 if (aFilter.isEmpty())
2514 ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false);
2515 sfx2::LinkManager* pLinkMgr = mpDoc->GetLinkManager();
2516 if (!pLinkMgr)
2518 SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL");
2519 return;
2521 ScExternalRefLink* pLink = new ScExternalRefLink(mpDoc, nFileId, aFilter);
2522 OSL_ENSURE(pFileName, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL");
2523 OUString aTmp = aFilter;
2524 pLinkMgr->InsertFileLink(*pLink, OBJECT_CLIENT_FILE, *pFileName, &aTmp);
2526 pLink->SetDoReferesh(false);
2527 pLink->Update();
2528 pLink->SetDoReferesh(true);
2530 maLinkedDocs.insert(LinkedDocMap::value_type(nFileId, true));
2533 void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const OUString& rOwnDocName)
2535 if (maRelativeName.isEmpty())
2536 // No relative path given. Nothing to do.
2537 return;
2539 if (!maRealFileName.isEmpty())
2540 // Real file name already created. Nothing to do.
2541 return;
2543 // Formulate the absolute file path from the relative path.
2544 const OUString& rRelPath = maRelativeName;
2545 INetURLObject aBaseURL(rOwnDocName);
2546 aBaseURL.insertName(OUString("content.xml"));
2547 bool bWasAbs = false;
2548 maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::NO_DECODE);
2551 void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId)
2553 if (nFileId >= maSrcFiles.size())
2554 return;
2556 maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
2559 OUString ScExternalRefManager::getOwnDocumentName() const
2561 SfxObjectShell* pShell = mpDoc->GetDocumentShell();
2562 if (!pShell)
2563 // This should not happen!
2564 return OUString();
2566 SfxMedium* pMed = pShell->GetMedium();
2567 if (!pMed)
2568 return OUString();
2570 return pMed->GetName();
2573 bool ScExternalRefManager::isOwnDocument(const OUString& rFile) const
2575 return getOwnDocumentName().equals(rFile);
2578 void ScExternalRefManager::convertToAbsName(OUString& rFile) const
2580 // unsaved documents have no AbsName
2581 TypeId aType(TYPE(ScDocShell));
2582 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
2583 while (pShell)
2585 if (rFile == pShell->GetName())
2586 return;
2588 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
2591 SfxObjectShell* pDocShell = mpDoc->GetDocumentShell();
2592 rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
2595 sal_uInt16 ScExternalRefManager::getExternalFileId(const OUString& rFile)
2597 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2598 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2599 if (itr != itrEnd)
2601 size_t nId = distance(itrBeg, itr);
2602 return static_cast<sal_uInt16>(nId);
2605 SrcFileData aData;
2606 aData.maFileName = rFile;
2607 maSrcFiles.push_back(aData);
2608 return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
2611 const OUString* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
2613 if (nFileId >= maSrcFiles.size())
2614 return NULL;
2616 if (bForceOriginal)
2617 return &maSrcFiles[nFileId].maFileName;
2619 maybeCreateRealFileName(nFileId);
2621 if (!maSrcFiles[nFileId].maRealFileName.isEmpty())
2622 return &maSrcFiles[nFileId].maRealFileName;
2624 return &maSrcFiles[nFileId].maFileName;
2627 std::vector<OUString> ScExternalRefManager::getAllCachedExternalFileNames() const
2629 std::vector<OUString> aNames;
2630 aNames.reserve(maSrcFiles.size());
2631 std::vector<SrcFileData>::const_iterator it = maSrcFiles.begin(), itEnd = maSrcFiles.end();
2632 for (; it != itEnd; ++it)
2634 const SrcFileData& rData = *it;
2635 aNames.push_back(rData.maFileName);
2638 return aNames;
2641 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
2643 return nFileId < maSrcFiles.size();
2646 bool ScExternalRefManager::hasExternalFile(const OUString& rFile) const
2648 return ::std::any_of(maSrcFiles.begin(), maSrcFiles.end(), FindSrcFileByName(rFile));
2651 const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const
2653 if (nFileId >= maSrcFiles.size())
2654 return NULL;
2656 return &maSrcFiles[nFileId];
2659 const OUString* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
2661 return maRefCache.getRealTableName(nFileId, rTabName);
2664 const OUString* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
2666 return maRefCache.getRealRangeName(nFileId, rRangeName);
2669 template<typename MapContainer>
2670 static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
2672 typename MapContainer::iterator itr = rMap.find(nFileId);
2673 if (itr != rMap.end())
2675 // Close this document shell.
2676 itr->second.maShell->DoClose();
2677 rMap.erase(itr);
2681 void ScExternalRefManager::clearCache(sal_uInt16 nFileId)
2683 maRefCache.clearCache(nFileId);
2686 namespace {
2688 class RefCacheFiller : public sc::ColumnSpanSet::ColumnAction
2690 svl::SharedStringPool& mrStrPool;
2692 ScExternalRefCache& mrRefCache;
2693 ScExternalRefCache::TableTypeRef mpRefTab;
2694 sal_uInt16 mnFileId;
2695 ScColumn* mpCurCol;
2696 sc::ColumnBlockConstPosition maBlockPos;
2698 public:
2699 RefCacheFiller( svl::SharedStringPool& rStrPool, ScExternalRefCache& rRefCache, sal_uInt16 nFileId ) :
2700 mrStrPool(rStrPool), mrRefCache(rRefCache), mnFileId(nFileId), mpCurCol(NULL) {}
2702 virtual void startColumn( ScColumn* pCol ) SAL_OVERRIDE
2704 mpCurCol = pCol;
2705 if (!mpCurCol)
2706 return;
2708 mpCurCol->InitBlockPosition(maBlockPos);
2709 mpRefTab = mrRefCache.getCacheTable(mnFileId, mpCurCol->GetTab());
2712 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) SAL_OVERRIDE
2714 if (!mpCurCol || !bVal)
2715 return;
2717 if (!mpRefTab)
2718 return;
2720 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2722 ScExternalRefCache::TokenRef pTok;
2723 ScRefCellValue aCell = mpCurCol->GetCellValue(maBlockPos, nRow);
2724 switch (aCell.meType)
2726 case CELLTYPE_STRING:
2727 case CELLTYPE_EDIT:
2729 OUString aStr = aCell.getString(&mpCurCol->GetDoc());
2730 svl::SharedString aSS = mrStrPool.intern(aStr);
2731 pTok.reset(new formula::FormulaStringToken(aSS));
2733 break;
2734 case CELLTYPE_VALUE:
2735 pTok.reset(new formula::FormulaDoubleToken(aCell.mfValue));
2736 break;
2737 case CELLTYPE_FORMULA:
2739 sc::FormulaResultValue aRes = aCell.mpFormula->GetResult();
2740 switch (aRes.meType)
2742 case sc::FormulaResultValue::Value:
2743 pTok.reset(new formula::FormulaDoubleToken(aRes.mfValue));
2744 break;
2745 case sc::FormulaResultValue::String:
2747 // Re-intern the string to the host document pool.
2748 svl::SharedString aInterned = mrStrPool.intern(aRes.maString.getString());
2749 pTok.reset(new formula::FormulaStringToken(aInterned));
2751 break;
2752 case sc::FormulaResultValue::Error:
2753 case sc::FormulaResultValue::Invalid:
2754 default:
2755 pTok.reset(new FormulaErrorToken(errNoValue));
2758 break;
2759 default:
2760 pTok.reset(new FormulaErrorToken(errNoValue));
2763 if (pTok)
2765 // Cache this cell.
2766 mpRefTab->setCell(mpCurCol->GetCol(), nRow, pTok, mpCurCol->GetNumberFormat(nRow));
2767 mpRefTab->setCachedCell(mpCurCol->GetCol(), nRow);
2775 bool ScExternalRefManager::refreshSrcDocument(sal_uInt16 nFileId)
2777 sc::ColumnSpanSet aCachedArea(false);
2778 maRefCache.getAllCachedDataSpans(nFileId, aCachedArea);
2780 OUString aFilter;
2781 SfxObjectShellRef xDocShell;
2784 xDocShell = loadSrcDocument(nFileId, aFilter);
2786 catch ( const css::uno::Exception& ) {}
2788 if (!xDocShell.Is())
2789 // Failed to load the document. Bail out.
2790 return false;
2792 ScDocShell& rDocSh = static_cast<ScDocShell&>(*xDocShell);
2793 ScDocument& rSrcDoc = rDocSh.GetDocument();
2795 // Clear the existing cache, and refill it. Make sure we keep the
2796 // existing cache table instances here.
2797 maRefCache.clearCacheTables(nFileId);
2798 RefCacheFiller aAction(mpDoc->GetSharedStringPool(), maRefCache, nFileId);
2799 aCachedArea.executeColumnAction(rSrcDoc, aAction);
2801 DocShellMap::iterator it = maDocShells.find(nFileId);
2802 if (it != maDocShells.end())
2804 it->second.maShell->DoClose();
2805 it->second.maShell = xDocShell;
2806 it->second.maLastAccess = tools::Time(tools::Time::SYSTEM);
2808 else
2810 SrcShell aSrcDoc;
2811 aSrcDoc.maShell = xDocShell;
2812 aSrcDoc.maLastAccess = tools::Time(tools::Time::SYSTEM);
2813 cacheNewDocShell(nFileId, aSrcDoc);
2816 // Update all cells containing names from this source document.
2817 refreshAllRefCells(nFileId);
2819 notifyAllLinkListeners(nFileId, LINK_MODIFIED);
2821 return true;
2824 void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
2826 // Turn all formula cells referencing this external document into static
2827 // cells.
2828 RefCellMap::iterator itrRefs = maRefCells.find(nFileId);
2829 if (itrRefs != maRefCells.end())
2831 // Make a copy because removing the formula cells below will modify
2832 // the original container.
2833 RefCellSet aSet = itrRefs->second;
2834 for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(mpDoc));
2835 maRefCells.erase(nFileId);
2838 // Remove all named ranges that reference this document.
2840 // Global named ranges.
2841 ScRangeName* pRanges = mpDoc->GetRangeName();
2842 if (pRanges)
2843 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2845 // Sheet-local named ranges.
2846 for (SCTAB i = 0, n = mpDoc->GetTableCount(); i < n; ++i)
2848 pRanges = mpDoc->GetRangeName(i);
2849 if (pRanges)
2850 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2853 clearCache(nFileId);
2854 lcl_removeByFileId(nFileId, maDocShells);
2856 if (maDocShells.empty())
2857 maSrcDocTimer.Stop();
2859 LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
2860 if (itr != maLinkedDocs.end())
2861 itr->second = false;
2863 notifyAllLinkListeners(nFileId, LINK_BROKEN);
2866 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const OUString& rNewFile, const OUString& rNewFilter)
2868 maSrcFiles[nFileId].maFileName = rNewFile;
2869 maSrcFiles[nFileId].maRelativeName.clear();
2870 maSrcFiles[nFileId].maRealFileName.clear();
2871 if (!maSrcFiles[nFileId].maFilterName.equals(rNewFilter))
2873 // Filter type has changed.
2874 maSrcFiles[nFileId].maFilterName = rNewFilter;
2875 maSrcFiles[nFileId].maFilterOptions.clear();
2877 refreshSrcDocument(nFileId);
2880 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const OUString& rRelUrl)
2882 if (nFileId >= maSrcFiles.size())
2883 return;
2884 maSrcFiles[nFileId].maRelativeName = rRelUrl;
2887 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const OUString& rFilterName, const OUString& rOptions)
2889 if (nFileId >= maSrcFiles.size())
2890 return;
2891 maSrcFiles[nFileId].maFilterName = rFilterName;
2892 maSrcFiles[nFileId].maFilterOptions = rOptions;
2895 void ScExternalRefManager::clear()
2897 DocShellMap::iterator itrEnd = maDocShells.end();
2898 for (DocShellMap::iterator itr = maDocShells.begin(); itr != itrEnd; ++itr)
2899 itr->second.maShell->DoClose();
2901 maDocShells.clear();
2902 maSrcDocTimer.Stop();
2905 bool ScExternalRefManager::hasExternalData() const
2907 return !maSrcFiles.empty();
2910 void ScExternalRefManager::resetSrcFileData(const OUString& rBaseFileUrl)
2912 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2913 itr != itrEnd; ++itr)
2915 // Re-generate relative file name from the absolute file name.
2916 OUString aAbsName = itr->maRealFileName;
2917 if (aAbsName.isEmpty())
2918 aAbsName = itr->maFileName;
2920 itr->maRelativeName = URIHelper::simpleNormalizedMakeRelative(
2921 rBaseFileUrl, aAbsName);
2925 void ScExternalRefManager::updateAbsAfterLoad()
2927 OUString aOwn( getOwnDocumentName() );
2928 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2929 itr != itrEnd; ++itr)
2931 // update maFileName to the real file name,
2932 // to be called when the original name is no longer needed (after CompileXML)
2934 itr->maybeCreateRealFileName( aOwn );
2935 OUString aReal = itr->maRealFileName;
2936 if (!aReal.isEmpty())
2937 itr->maFileName = aReal;
2941 void ScExternalRefManager::removeRefCell(ScFormulaCell* pCell)
2943 for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell));
2946 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
2948 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2949 if (itr == maLinkListeners.end())
2951 pair<LinkListenerMap::iterator, bool> r = maLinkListeners.insert(
2952 LinkListenerMap::value_type(nFileId, LinkListeners()));
2953 if (!r.second)
2955 OSL_FAIL("insertion of new link listener list failed");
2956 return;
2959 itr = r.first;
2962 LinkListeners& rList = itr->second;
2963 rList.insert(pListener);
2966 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
2968 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2969 if (itr == maLinkListeners.end())
2970 // no listeners for a specified file.
2971 return;
2973 LinkListeners& rList = itr->second;
2974 rList.erase(pListener);
2976 if (rList.empty())
2977 // No more listeners for this file. Remove its entry.
2978 maLinkListeners.erase(itr);
2981 void ScExternalRefManager::removeLinkListener(LinkListener* pListener)
2983 LinkListenerMap::iterator itr = maLinkListeners.begin(), itrEnd = maLinkListeners.end();
2984 for (; itr != itrEnd; ++itr)
2985 itr->second.erase(pListener);
2988 void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
2990 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2991 if (itr == maLinkListeners.end())
2992 // no listeners for a specified file.
2993 return;
2995 LinkListeners& rList = itr->second;
2996 for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
2999 void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut)
3001 // To avoid potentially freezing Calc, we close one stale document at a time.
3002 DocShellMap::iterator itr = maDocShells.begin(), itrEnd = maDocShells.end();
3003 for (; itr != itrEnd; ++itr)
3005 // in 100th of a second.
3006 sal_Int32 nSinceLastAccess = (tools::Time( tools::Time::SYSTEM ) - itr->second.maLastAccess).GetTime();
3007 if (nSinceLastAccess >= nTimeOut)
3009 // Timed out. Let's close this, and exit the loop.
3010 itr->second.maShell->DoClose();
3011 maDocShells.erase(itr);
3012 break;
3016 if (maDocShells.empty())
3017 maSrcDocTimer.Stop();
3020 sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument* pSrcDoc)
3022 NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
3023 if (itr == maNumFormatMap.end())
3025 // Number formatter map is not initialized for this external document.
3026 pair<NumFmtMap::iterator, bool> r = maNumFormatMap.insert(
3027 NumFmtMap::value_type(nFileId, SvNumberFormatterMergeMap()));
3029 if (!r.second)
3030 // insertion failed.
3031 return nNumFmt;
3033 itr = r.first;
3034 mpDoc->GetFormatTable()->MergeFormatter( *pSrcDoc->GetFormatTable());
3035 SvNumberFormatterMergeMap aMap = mpDoc->GetFormatTable()->ConvertMergeTableToMap();
3036 itr->second.swap(aMap);
3038 const SvNumberFormatterMergeMap& rMap = itr->second;
3039 SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
3040 if (itrNumFmt != rMap.end())
3041 // mapped value found.
3042 return itrNumFmt->second;
3044 return nNumFmt;
3047 void ScExternalRefManager::transformUnsavedRefToSavedRef( SfxObjectShell* pShell )
3049 DocShellMap::iterator itr = maUnsavedDocShells.begin();
3050 while( itr != maUnsavedDocShells.end() )
3052 if (&(itr->second.maShell) == pShell)
3054 // found that the shell is marked as unsaved
3055 OUString aFileURL = pShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DECODE_TO_IURI);
3056 switchSrcFile(itr->first, aFileURL, OUString());
3057 EndListening(*pShell);
3058 maUnsavedDocShells.erase(itr++);
3063 void ScExternalRefManager::Notify( SfxBroadcaster&, const SfxHint& rHint )
3065 const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
3066 if ( pEventHint )
3068 sal_uLong nEventId = pEventHint->GetEventId();
3069 switch ( nEventId )
3071 case SFX_EVENT_PREPARECLOSEDOC:
3073 ScopedVclPtrInstance<WarningBox> aBox( ScDocShell::GetActiveDialogParent(), WinBits( WB_OK ),
3074 ScGlobal::GetRscString( STR_CLOSE_WITH_UNSAVED_REFS ) );
3075 aBox->Execute();
3077 break;
3078 case SFX_EVENT_SAVEDOCDONE:
3079 case SFX_EVENT_SAVEASDOCDONE:
3081 SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
3082 transformUnsavedRefToSavedRef(pObjShell);
3084 break;
3085 default:
3086 break;
3091 IMPL_LINK_TYPED(ScExternalRefManager, TimeOutHdl, Timer*, pTimer, void)
3093 if (pTimer == &maSrcDocTimer)
3094 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN);
3097 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */