Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / ui / docshell / externalrefmgr.cxx
blob4646103a3190670158cc9f0cf82e719ed4ed4695
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"
57 #include <memory>
58 #include <algorithm>
60 #include <boost/scoped_ptr.hpp>
62 using ::std::auto_ptr;
63 using ::com::sun::star::uno::Any;
64 using ::std::vector;
65 using ::std::find;
66 using ::std::find_if;
67 using ::std::remove_if;
68 using ::std::distance;
69 using ::std::pair;
70 using ::std::list;
71 using ::std::unary_function;
72 using namespace formula;
74 #define SRCDOC_LIFE_SPAN 6000 // 1 minute (in 100th of a sec)
75 #define SRCDOC_SCAN_INTERVAL 1000*5 // every 5 seconds (in msec)
77 namespace {
79 class TabNameSearchPredicate : public unary_function<ScExternalRefCache::TableName, bool>
81 public:
82 explicit TabNameSearchPredicate(const OUString& rSearchName) :
83 maSearchName(ScGlobal::pCharClass->uppercase(rSearchName))
87 bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const
89 // Ok, I'm doing case insensitive search here.
90 return rTabNameSet.maUpperName.equals(maSearchName);
93 private:
94 OUString maSearchName;
97 class FindSrcFileByName : public unary_function<ScExternalRefManager::SrcFileData, bool>
99 public:
100 FindSrcFileByName(const OUString& rMatchName) :
101 mrMatchName(rMatchName)
105 bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const
107 return rSrcData.maFileName.equals(mrMatchName);
110 private:
111 const OUString& mrMatchName;
114 class NotifyLinkListener : public unary_function<ScExternalRefManager::LinkListener*, void>
116 public:
117 NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) :
118 mnFileId(nFileId), meType(eType) {}
120 NotifyLinkListener(const NotifyLinkListener& r) :
121 mnFileId(r.mnFileId), meType(r.meType) {}
123 void operator() (ScExternalRefManager::LinkListener* p) const
125 p->notify(mnFileId, meType);
127 private:
128 sal_uInt16 mnFileId;
129 ScExternalRefManager::LinkUpdateType meType;
132 struct UpdateFormulaCell : public unary_function<ScFormulaCell*, void>
134 void operator() (ScFormulaCell* pCell) const
136 // Check to make sure the cell really contains ocExternalRef.
137 // External names, external cell and range references all have a
138 // ocExternalRef token.
139 const ScTokenArray* pCode = pCell->GetCode();
140 if (!pCode->HasExternalRef())
141 return;
143 ScTokenArray* pArray = pCell->GetCode();
144 if (pArray)
145 // Clear the error code, or a cell with error won't get re-compiled.
146 pArray->SetCodeError(0);
148 pCell->SetCompile(true);
149 pCell->CompileTokenArray();
150 pCell->SetDirty();
154 class RemoveFormulaCell : public unary_function<pair<const sal_uInt16, ScExternalRefManager::RefCellSet>, void>
156 public:
157 explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {}
158 void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const
160 r.second.erase(mpCell);
162 private:
163 ScFormulaCell* mpCell;
166 class ConvertFormulaToStatic : public unary_function<ScFormulaCell*, void>
168 public:
169 explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {}
170 void operator() (ScFormulaCell* pCell) const
172 ScAddress aPos = pCell->aPos;
174 // We don't check for empty cells because empty external cells are
175 // treated as having a value of 0.
177 if (pCell->IsValue())
179 // Turn this into value cell.
180 mpDoc->SetValue(aPos, pCell->GetValue());
182 else
184 // string cell otherwise.
185 ScSetStringParam aParam;
186 aParam.setTextInput();
187 mpDoc->SetString(aPos, pCell->GetString().getString(), &aParam);
190 private:
191 ScDocument* mpDoc;
195 * Check whether a named range contains an external reference to a
196 * particular document.
198 bool hasRefsToSrcDoc(ScRangeData& rData, sal_uInt16 nFileId)
200 ScTokenArray* pArray = rData.GetCode();
201 if (!pArray)
202 return false;
204 pArray->Reset();
205 ScToken* p = static_cast<ScToken*>(pArray->GetNextReference());
206 for (; p; p = static_cast<ScToken*>(pArray->GetNextReference()))
208 if (!p->IsExternalRef())
209 continue;
211 if (p->GetIndex() == nFileId)
212 return true;
214 return false;
217 class EraseRangeByIterator : unary_function<ScRangeName::iterator, void>
219 ScRangeName& mrRanges;
220 public:
221 EraseRangeByIterator(ScRangeName& rRanges) : mrRanges(rRanges) {}
222 void operator() (const ScRangeName::iterator& itr)
224 mrRanges.erase(itr);
229 * Remove all named ranges that contain references to specified source
230 * document.
232 void removeRangeNamesBySrcDoc(ScRangeName& rRanges, sal_uInt16 nFileId)
234 ScRangeName::iterator itr = rRanges.begin(), itrEnd = rRanges.end();
235 vector<ScRangeName::iterator> v;
236 for (; itr != itrEnd; ++itr)
238 if (hasRefsToSrcDoc(*itr->second, nFileId))
239 v.push_back(itr);
241 for_each(v.begin(), v.end(), EraseRangeByIterator(rRanges));
246 // ============================================================================
248 ScExternalRefCache::Table::Table()
249 : meReferenced( REFERENCED_MARKED )
250 // Prevent accidental data loss due to lack of knowledge.
254 ScExternalRefCache::Table::~Table()
258 void ScExternalRefCache::Table::setReferencedFlag( ScExternalRefCache::Table::ReferencedFlag eFlag )
260 meReferenced = eFlag;
263 void ScExternalRefCache::Table::setReferenced( bool bReferenced )
265 if (meReferenced != REFERENCED_PERMANENT)
266 meReferenced = (bReferenced ? REFERENCED_MARKED : UNREFERENCED);
269 ScExternalRefCache::Table::ReferencedFlag ScExternalRefCache::Table::getReferencedFlag() const
271 return meReferenced;
274 bool ScExternalRefCache::Table::isReferenced() const
276 return meReferenced != UNREFERENCED;
279 void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef pToken, sal_uLong nFmtIndex, bool bSetCacheRange)
281 using ::std::pair;
282 RowsDataType::iterator itrRow = maRows.find(nRow);
283 if (itrRow == maRows.end())
285 // This row does not exist yet.
286 pair<RowsDataType::iterator, bool> res = maRows.insert(
287 RowsDataType::value_type(nRow, RowDataType()));
289 if (!res.second)
290 return;
292 itrRow = res.first;
295 // Insert this token into the specified column location. I don't need to
296 // check for existing data. Just overwrite it.
297 RowDataType& rRow = itrRow->second;
298 ScExternalRefCache::Cell aCell;
299 aCell.mxToken = pToken;
300 aCell.mnFmtIndex = nFmtIndex;
301 rRow.insert(RowDataType::value_type(nCol, aCell));
302 if (bSetCacheRange)
303 setCachedCell(nCol, nRow);
306 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const
308 RowsDataType::const_iterator itrTable = maRows.find(nRow);
309 if (itrTable == maRows.end())
311 // this table doesn't have the specified row.
312 return getEmptyOrNullToken(nCol, nRow);
315 const RowDataType& rRowData = itrTable->second;
316 RowDataType::const_iterator itrRow = rRowData.find(nCol);
317 if (itrRow == rRowData.end())
319 // this row doesn't have the specified column.
320 return getEmptyOrNullToken(nCol, nRow);
323 const Cell& rCell = itrRow->second;
324 if (pnFmtIndex)
325 *pnFmtIndex = rCell.mnFmtIndex;
327 return rCell.mxToken;
330 bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const
332 RowsDataType::const_iterator itrRow = maRows.find(nRow);
333 return itrRow != maRows.end();
336 void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const
338 vector<SCROW> aRows;
339 aRows.reserve(maRows.size());
340 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end();
341 for (; itr != itrEnd; ++itr)
342 if (nLow <= itr->first && itr->first <= nHigh)
343 aRows.push_back(itr->first);
345 // hash map is not ordered, so we need to explicitly sort it.
346 ::std::sort(aRows.begin(), aRows.end());
347 rRows.swap(aRows);
350 ::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const
352 ::std::pair< SCROW, SCROW > aRange( 0, 0 );
353 if( !maRows.empty() )
355 // iterate over entire container (hash map is not sorted by key)
356 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end();
357 aRange.first = itr->first;
358 aRange.second = itr->first + 1;
359 while( ++itr != itrEnd )
361 if( itr->first < aRange.first )
362 aRange.first = itr->first;
363 else if( itr->first >= aRange.second )
364 aRange.second = itr->first + 1;
367 return aRange;
370 void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const
372 RowsDataType::const_iterator itrRow = maRows.find(nRow);
373 if (itrRow == maRows.end())
374 // this table doesn't have the specified row.
375 return;
377 const RowDataType& rRowData = itrRow->second;
378 vector<SCCOL> aCols;
379 aCols.reserve(rRowData.size());
380 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
381 for (; itrCol != itrColEnd; ++itrCol)
382 if (nLow <= itrCol->first && itrCol->first <= nHigh)
383 aCols.push_back(itrCol->first);
385 // hash map is not ordered, so we need to explicitly sort it.
386 ::std::sort(aCols.begin(), aCols.end());
387 rCols.swap(aCols);
390 ::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const
392 ::std::pair< SCCOL, SCCOL > aRange( 0, 0 );
394 RowsDataType::const_iterator itrRow = maRows.find( nRow );
395 if (itrRow == maRows.end())
396 // this table doesn't have the specified row.
397 return aRange;
399 const RowDataType& rRowData = itrRow->second;
400 if( !rRowData.empty() )
402 // iterate over entire container (hash map is not sorted by key)
403 RowDataType::const_iterator itr = rRowData.begin(), itrEnd = rRowData.end();
404 aRange.first = itr->first;
405 aRange.second = itr->first + 1;
406 while( ++itr != itrEnd )
408 if( itr->first < aRange.first )
409 aRange.first = itr->first;
410 else if( itr->first >= aRange.second )
411 aRange.second = itr->first + 1;
414 return aRange;
417 void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
419 RowsDataType::const_iterator itrRow = maRows.begin(), itrRowEnd = maRows.end();
420 for (; itrRow != itrRowEnd; ++itrRow)
422 const RowDataType& rRowData = itrRow->second;
423 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
424 for (; itrCol != itrColEnd; ++itrCol)
426 const Cell& rCell = itrCol->second;
427 rNumFmts.push_back(rCell.mnFmtIndex);
432 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
434 return maCachedRanges.In(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
437 void ScExternalRefCache::Table::setCachedCell(SCCOL nCol, SCROW nRow)
439 setCachedCellRange(nCol, nRow, nCol, nRow);
442 void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
444 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
445 if ( maCachedRanges.empty() )
446 maCachedRanges.Append(aRange);
447 else
448 maCachedRanges.Join(aRange);
450 OUString aStr;
451 maCachedRanges.Format(aStr, SCA_VALID);
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 // ----------------------------------------------------------------------------
477 ScExternalRefCache::TableName::TableName(const OUString& rUpper, const OUString& rReal) :
478 maUpperName(rUpper), maRealName(rReal)
482 // ----------------------------------------------------------------------------
484 ScExternalRefCache::CellFormat::CellFormat() :
485 mbIsSet(false), mnType(NUMBERFORMAT_ALL), mnIndex(0)
489 // ----------------------------------------------------------------------------
491 ScExternalRefCache::ScExternalRefCache() {}
493 ScExternalRefCache::~ScExternalRefCache() {}
495 const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
497 osl::MutexGuard aGuard(&maMtxDocs);
499 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
500 if (itrDoc == maDocs.end())
502 // specified document is not cached.
503 return NULL;
506 const DocItem& rDoc = itrDoc->second;
507 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
508 ScGlobal::pCharClass->uppercase(rTabName));
509 if (itrTabId == rDoc.maTableNameIndex.end())
511 // the specified table is not in cache.
512 return NULL;
515 return &rDoc.maTableNames[itrTabId->second].maRealName;
518 const OUString* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
520 osl::MutexGuard aGuard(&maMtxDocs);
522 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
523 if (itrDoc == maDocs.end())
525 // specified document is not cached.
526 return NULL;
529 const DocItem& rDoc = itrDoc->second;
530 NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find(
531 ScGlobal::pCharClass->uppercase(rRangeName));
532 if (itr == rDoc.maRealRangeNameMap.end())
533 // range name not found.
534 return NULL;
536 return &itr->second;
539 ScExternalRefCache::TokenRef ScExternalRefCache::getCellData(
540 sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex)
542 osl::MutexGuard aGuard(&maMtxDocs);
544 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
545 if (itrDoc == maDocs.end())
547 // specified document is not cached.
548 return TokenRef();
551 const DocItem& rDoc = itrDoc->second;
552 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
553 ScGlobal::pCharClass->uppercase(rTabName));
554 if (itrTabId == rDoc.maTableNameIndex.end())
556 // the specified table is not in cache.
557 return TokenRef();
560 const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
561 if (!pTableData.get())
563 // the table data is not instantiated yet.
564 return TokenRef();
567 return pTableData->getCell(nCol, nRow, pnFmtIndex);
570 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData(
571 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange)
573 osl::MutexGuard aGuard(&maMtxDocs);
575 DocDataType::iterator itrDoc = maDocs.find(nFileId);
576 if (itrDoc == maDocs.end())
577 // specified document is not cached.
578 return TokenArrayRef();
580 DocItem& rDoc = itrDoc->second;
582 TableNameIndexMap::iterator itrTabId = rDoc.maTableNameIndex.find(
583 ScGlobal::pCharClass->uppercase(rTabName));
584 if (itrTabId == rDoc.maTableNameIndex.end())
585 // the specified table is not in cache.
586 return TokenArrayRef();
588 const ScAddress& s = rRange.aStart;
589 const ScAddress& e = rRange.aEnd;
591 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
592 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
593 SCROW nRow1 = s.Row(), nRow2 = e.Row();
595 // Make sure I have all the tables cached.
596 size_t nTabFirstId = itrTabId->second;
597 size_t nTabLastId = nTabFirstId + nTab2 - nTab1;
598 if (nTabLastId >= rDoc.maTables.size())
599 // not all tables are cached.
600 return TokenArrayRef();
602 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
604 RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
605 if (itrRange != rDoc.maRangeArrays.end())
606 // Cache hit!
607 return itrRange->second;
609 ::boost::scoped_ptr<ScRange> pNewRange;
610 TokenArrayRef pArray;
611 bool bFirstTab = true;
612 for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
614 TableTypeRef pTab = rDoc.maTables[nTab];
615 if (!pTab.get())
616 return TokenArrayRef();
618 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
619 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
621 if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2))
623 // specified range is not entirely within cached ranges.
624 return TokenArrayRef();
627 ScMatrixRef xMat = new ScMatrix(
628 static_cast<SCSIZE>(nDataCol2-nDataCol1+1), static_cast<SCSIZE>(nDataRow2-nDataRow1+1));
630 // Only fill non-empty cells, for better performance.
631 vector<SCROW> aRows;
632 pTab->getAllRows(aRows, nDataRow1, nDataRow2);
633 for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr)
635 SCROW nRow = *itr;
636 vector<SCCOL> aCols;
637 pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2);
638 for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol)
640 SCCOL nCol = *itrCol;
641 TokenRef pToken = pTab->getCell(nCol, nRow);
642 if (!pToken)
643 // This should never happen!
644 return TokenArrayRef();
646 SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1;
647 switch (pToken->GetType())
649 case svDouble:
650 xMat->PutDouble(pToken->GetDouble(), nC, nR);
651 break;
652 case svString:
653 xMat->PutString(pToken->GetString(), nC, nR);
654 break;
655 default:
661 if (!bFirstTab)
662 pArray->AddOpCode(ocSep);
664 ScMatrixToken aToken(xMat);
665 if (!pArray)
666 pArray.reset(new ScTokenArray);
667 pArray->AddToken(aToken);
669 bFirstTab = false;
671 if (!pNewRange)
672 pNewRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
673 else
674 pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
677 if (pNewRange)
678 rDoc.maRangeArrays.insert( RangeArrayMap::value_type(*pNewRange, pArray));
679 return pArray;
682 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const OUString& rName)
684 osl::MutexGuard aGuard(&maMtxDocs);
686 DocItem* pDoc = getDocItem(nFileId);
687 if (!pDoc)
688 return TokenArrayRef();
690 RangeNameMap& rMap = pDoc->maRangeNames;
691 RangeNameMap::const_iterator itr = rMap.find(
692 ScGlobal::pCharClass->uppercase(rName));
693 if (itr == rMap.end())
694 return TokenArrayRef();
696 return itr->second;
699 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, TokenArrayRef pArray)
701 osl::MutexGuard aGuard(&maMtxDocs);
703 DocItem* pDoc = getDocItem(nFileId);
704 if (!pDoc)
705 return;
707 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
708 RangeNameMap& rMap = pDoc->maRangeNames;
709 rMap.insert(RangeNameMap::value_type(aUpperName, pArray));
710 pDoc->maRealRangeNameMap.insert(NamePairMap::value_type(aUpperName, rName));
713 bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId, const OUString& rName) const
715 osl::MutexGuard aGuard(&maMtxDocs);
717 DocItem* pDoc = getDocItem(nFileId);
718 if (!pDoc)
719 return false;
721 const RangeNameMap& rMap = pDoc->maRangeNames;
722 return rMap.count(rName) > 0;
725 void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow,
726 TokenRef pToken, sal_uLong nFmtIndex)
728 if (!isDocInitialized(nFileId))
729 return;
731 using ::std::pair;
732 DocItem* pDocItem = getDocItem(nFileId);
733 if (!pDocItem)
734 return;
736 DocItem& rDoc = *pDocItem;
738 // See if the table by this name already exists.
739 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
740 ScGlobal::pCharClass->uppercase(rTabName));
741 if (itrTabName == rDoc.maTableNameIndex.end())
742 // Table not found. Maybe the table name or the file id is wrong ???
743 return;
745 TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
746 if (!pTableData.get())
747 pTableData.reset(new Table);
749 pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
750 pTableData->setCachedCell(nCol, nRow);
753 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
754 const TokenArrayRef& pArray)
756 using ::std::pair;
757 if (rData.empty() || !isDocInitialized(nFileId))
758 // nothing to cache
759 return;
761 // First, get the document item for the given file ID.
762 DocItem* pDocItem = getDocItem(nFileId);
763 if (!pDocItem)
764 return;
766 DocItem& rDoc = *pDocItem;
768 // Now, find the table position of the first table to cache.
769 const OUString& rFirstTabName = rData.front().maTableName;
770 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
771 ScGlobal::pCharClass->uppercase(rFirstTabName));
772 if (itrTabName == rDoc.maTableNameIndex.end())
774 // table index not found.
775 return;
778 size_t nTabFirstId = itrTabName->second;
779 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
780 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
781 vector<SingleRangeData>::const_iterator itrDataBeg = rData.begin(), itrDataEnd = rData.end();
782 for (vector<SingleRangeData>::const_iterator itrData = itrDataBeg; itrData != itrDataEnd; ++itrData)
784 size_t i = nTabFirstId + ::std::distance(itrDataBeg, itrData);
785 TableTypeRef& pTabData = rDoc.maTables[i];
786 if (!pTabData.get())
787 pTabData.reset(new Table);
789 const ScMatrixRef& pMat = itrData->mpRangeData;
790 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
792 const SCSIZE nR = nRow - nRow1;
793 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
795 const SCSIZE nC = nCol - nCol1;
797 ScMatrixValue value = pMat->Get(nC, nR);
799 TokenRef pToken;
801 switch (value.nType) {
802 case SC_MATVAL_VALUE:
803 case SC_MATVAL_BOOLEAN:
804 pToken.reset(new formula::FormulaDoubleToken(value.fVal));
805 break;
806 case SC_MATVAL_STRING:
807 pToken.reset(new formula::FormulaStringToken(value.aStr));
808 break;
809 default:
810 // Don't cache empty cells.
811 break;
814 if (pToken)
815 // Don't mark this cell 'cached' here, for better performance.
816 pTabData->setCell(nCol, nRow, pToken, 0, false);
819 // Mark the whole range 'cached'.
820 pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2);
823 size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab();
824 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
826 rDoc.maRangeArrays.insert( RangeArrayMap::value_type( aCacheRange, pArray));
829 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId)
831 DocItem* pDoc = getDocItem(nFileId);
832 if (!pDoc)
833 return false;
835 return pDoc->mbInitFromSource;
838 static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex)
840 ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
841 if (itr == rMap.end())
842 return false;
844 rIndex = itr->second;
845 return true;
848 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames)
850 DocItem* pDoc = getDocItem(nFileId);
851 if (!pDoc)
852 return;
854 size_t n = rTabNames.size();
856 // table name list - the list must include all table names in the source
857 // document and only to be populated when loading the source document, not
858 // when loading cached data from, say, Excel XCT/CRN records.
859 vector<TableName> aNewTabNames;
860 aNewTabNames.reserve(n);
861 for (vector<OUString>::const_iterator itr = rTabNames.begin(), itrEnd = rTabNames.end();
862 itr != itrEnd; ++itr)
864 TableName aNameItem(ScGlobal::pCharClass->uppercase(*itr), *itr);
865 aNewTabNames.push_back(aNameItem);
867 pDoc->maTableNames.swap(aNewTabNames);
869 // data tables - preserve any existing data that may have been set during
870 // file import.
871 vector<TableTypeRef> aNewTables(n);
872 for (size_t i = 0; i < n; ++i)
874 size_t nIndex;
875 if (lcl_getTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
877 aNewTables[i] = pDoc->maTables[nIndex];
880 pDoc->maTables.swap(aNewTables);
882 // name index map
883 TableNameIndexMap aNewNameIndex;
884 for (size_t i = 0; i < n; ++i)
885 aNewNameIndex.insert(TableNameIndexMap::value_type(pDoc->maTableNames[i].maUpperName, i));
886 pDoc->maTableNameIndex.swap(aNewNameIndex);
888 pDoc->mbInitFromSource = true;
891 OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
893 if( DocItem* pDoc = getDocItem( nFileId ) )
894 if( nCacheId < pDoc->maTableNames.size() )
895 return pDoc->maTableNames[ nCacheId ].maRealName;
896 return EMPTY_OUSTRING;
899 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
901 rTabNames.clear();
902 DocItem* pDoc = getDocItem(nFileId);
903 if (!pDoc)
904 return;
906 size_t n = pDoc->maTableNames.size();
907 rTabNames.reserve(n);
908 for (vector<TableName>::const_iterator itr = pDoc->maTableNames.begin(), itrEnd = pDoc->maTableNames.end();
909 itr != itrEnd; ++itr)
910 rTabNames.push_back(itr->maRealName);
913 SCsTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
915 DocItem* pDoc = getDocItem(nFileId);
916 if (!pDoc)
917 return -1;
919 vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
920 vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
922 vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
923 TabNameSearchPredicate( rStartTabName));
924 if (itrStartTab == itrEnd)
925 return -1;
927 vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
928 TabNameSearchPredicate( rEndTabName));
929 if (itrEndTab == itrEnd)
930 return 0;
932 size_t nStartDist = ::std::distance( itrBeg, itrStartTab);
933 size_t nEndDist = ::std::distance( itrBeg, itrEndTab);
934 return nStartDist <= nEndDist ? static_cast<SCsTAB>(nEndDist - nStartDist + 1) : -static_cast<SCsTAB>(nStartDist - nEndDist + 1);
937 void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
939 osl::MutexGuard aGuard(&maMtxDocs);
941 using ::std::sort;
942 using ::std::unique;
944 vector<sal_uInt32> aNumFmts;
945 for (DocDataType::const_iterator itrDoc = maDocs.begin(), itrDocEnd = maDocs.end();
946 itrDoc != itrDocEnd; ++itrDoc)
948 const vector<TableTypeRef>& rTables = itrDoc->second.maTables;
949 for (vector<TableTypeRef>::const_iterator itrTab = rTables.begin(), itrTabEnd = rTables.end();
950 itrTab != itrTabEnd; ++itrTab)
952 TableTypeRef pTab = *itrTab;
953 if (!pTab)
954 continue;
956 pTab->getAllNumberFormats(aNumFmts);
960 // remove duplicates.
961 sort(aNumFmts.begin(), aNumFmts.end());
962 aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
963 rNumFmts.swap(aNumFmts);
966 bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId )
968 DocItem* pDocItem = getDocItem(nFileId);
969 if (!pDocItem)
970 return areAllCacheTablesReferenced();
972 for (::std::vector<TableTypeRef>::iterator itrTab = pDocItem->maTables.begin();
973 itrTab != pDocItem->maTables.end(); ++itrTab)
975 if ((*itrTab).get())
976 (*itrTab)->setReferenced( true);
978 addCacheDocToReferenced( nFileId);
979 return areAllCacheTablesReferenced();
982 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets, bool bPermanent )
984 DocItem* pDoc = getDocItem(nFileId);
985 if (pDoc)
987 size_t nIndex = 0;
988 OUString aTabNameUpper = ScGlobal::pCharClass->uppercase( rTabName);
989 if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex))
991 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
992 for (size_t i = nIndex; i < nStop; ++i)
994 TableTypeRef pTab = pDoc->maTables[i];
995 if (pTab.get())
997 Table::ReferencedFlag eNewFlag = (bPermanent ?
998 Table::REFERENCED_PERMANENT :
999 Table::REFERENCED_MARKED);
1000 Table::ReferencedFlag eOldFlag = pTab->getReferencedFlag();
1001 if (eOldFlag != Table::REFERENCED_PERMANENT && eNewFlag != eOldFlag)
1003 pTab->setReferencedFlag( eNewFlag);
1004 addCacheTableToReferenced( nFileId, i);
1010 return areAllCacheTablesReferenced();
1013 void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced )
1015 osl::MutexGuard aGuard(&maMtxDocs);
1017 if (bReferenced)
1019 maReferenced.reset(0);
1020 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1022 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
1023 for (::std::vector<TableTypeRef>::iterator itrTab = rDocItem.maTables.begin();
1024 itrTab != rDocItem.maTables.end(); ++itrTab)
1026 if ((*itrTab).get())
1027 (*itrTab)->setReferenced( true);
1031 else
1033 size_t nDocs = 0;
1034 for (DocDataType::const_iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1036 if (nDocs <= (*itrDoc).first)
1037 nDocs = (*itrDoc).first + 1;
1039 maReferenced.reset( nDocs);
1041 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
1043 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
1044 sal_uInt16 nFileId = (*itrDoc).first;
1045 size_t nTables = rDocItem.maTables.size();
1046 ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId];
1047 // All referenced => non-existing tables evaluate as completed.
1048 rDocReferenced.maTables.resize( nTables, true);
1049 for (size_t i=0; i < nTables; ++i)
1051 TableTypeRef & xTab = rDocItem.maTables[i];
1052 if (xTab.get())
1054 if (xTab->getReferencedFlag() == Table::REFERENCED_PERMANENT)
1055 addCacheTableToReferenced( nFileId, i);
1056 else
1058 xTab->setReferencedFlag( Table::UNREFERENCED);
1059 rDocReferenced.maTables[i] = false;
1060 rDocReferenced.mbAllTablesReferenced = false;
1061 // An addCacheTableToReferenced() actually may have
1062 // resulted in mbAllReferenced been set. Clear it.
1063 maReferenced.mbAllReferenced = false;
1071 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
1073 if (nFileId >= maReferenced.maDocs.size())
1074 return;
1076 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1077 size_t nTables = rTables.size();
1078 if (nIndex >= nTables)
1079 return;
1081 if (!rTables[nIndex])
1083 rTables[nIndex] = true;
1084 size_t i = 0;
1085 while (i < nTables && rTables[i])
1086 ++i;
1087 if (i == nTables)
1089 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1090 maReferenced.checkAllDocs();
1095 void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId )
1097 if (nFileId >= maReferenced.maDocs.size())
1098 return;
1100 if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
1102 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1103 size_t nSize = rTables.size();
1104 for (size_t i=0; i < nSize; ++i)
1105 rTables[i] = true;
1106 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1107 maReferenced.checkAllDocs();
1111 bool ScExternalRefCache::areAllCacheTablesReferenced() const
1113 return maReferenced.mbAllReferenced;
1116 ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
1117 mbAllReferenced(false)
1119 reset(0);
1122 void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs )
1124 if (nDocs)
1126 mbAllReferenced = false;
1127 DocReferencedVec aRefs( nDocs);
1128 maDocs.swap( aRefs);
1130 else
1132 mbAllReferenced = true;
1133 DocReferencedVec aRefs;
1134 maDocs.swap( aRefs);
1138 void ScExternalRefCache::ReferencedStatus::checkAllDocs()
1140 for (DocReferencedVec::const_iterator itr = maDocs.begin(); itr != maDocs.end(); ++itr)
1142 if (!(*itr).mbAllTablesReferenced)
1143 return;
1145 mbAllReferenced = true;
1148 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1150 DocItem* pDoc = getDocItem(nFileId);
1151 if (!pDoc || nTabIndex >= pDoc->maTables.size())
1152 return TableTypeRef();
1154 return pDoc->maTables[nTabIndex];
1157 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex)
1159 // In API, the index is transported as cached sheet ID of type sal_Int32 in
1160 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
1161 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
1162 // being 0xffffffff
1163 const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
1165 DocItem* pDoc = getDocItem(nFileId);
1166 if (!pDoc)
1168 if (pnIndex) *pnIndex = nNotAvailable;
1169 return TableTypeRef();
1172 DocItem& rDoc = *pDoc;
1174 size_t nIndex;
1175 OUString aTabNameUpper = ScGlobal::pCharClass->uppercase(rTabName);
1176 if (lcl_getTableDataIndex(rDoc.maTableNameIndex, aTabNameUpper, nIndex))
1178 // specified table found.
1179 if( pnIndex ) *pnIndex = nIndex;
1180 if (bCreateNew && !rDoc.maTables[nIndex])
1181 rDoc.maTables[nIndex].reset(new Table);
1183 return rDoc.maTables[nIndex];
1186 if (!bCreateNew)
1188 if (pnIndex) *pnIndex = nNotAvailable;
1189 return TableTypeRef();
1192 // Specified table doesn't exist yet. Create one.
1193 nIndex = rDoc.maTables.size();
1194 if( pnIndex ) *pnIndex = nIndex;
1195 TableTypeRef pTab(new Table);
1196 rDoc.maTables.push_back(pTab);
1197 rDoc.maTableNames.push_back(TableName(aTabNameUpper, rTabName));
1198 rDoc.maTableNameIndex.insert(
1199 TableNameIndexMap::value_type(aTabNameUpper, nIndex));
1200 return pTab;
1203 void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
1205 osl::MutexGuard aGuard(&maMtxDocs);
1206 maDocs.erase(nFileId);
1209 ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const
1211 osl::MutexGuard aGuard(&maMtxDocs);
1213 using ::std::pair;
1214 DocDataType::iterator itrDoc = maDocs.find(nFileId);
1215 if (itrDoc == maDocs.end())
1217 // specified document is not cached.
1218 pair<DocDataType::iterator, bool> res = maDocs.insert(
1219 DocDataType::value_type(nFileId, DocItem()));
1221 if (!res.second)
1222 // insertion failed.
1223 return NULL;
1225 itrDoc = res.first;
1228 return &itrDoc->second;
1231 // ============================================================================
1233 ScExternalRefLink::ScExternalRefLink(ScDocument* pDoc, sal_uInt16 nFileId, const OUString& rFilter) :
1234 ::sfx2::SvBaseLink(::sfx2::LINKUPDATE_ONCALL, FORMAT_FILE),
1235 mnFileId(nFileId),
1236 maFilterName(rFilter),
1237 mpDoc(pDoc),
1238 mbDoRefresh(true)
1242 ScExternalRefLink::~ScExternalRefLink()
1246 void ScExternalRefLink::Closed()
1248 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
1249 pMgr->breakLink(mnFileId);
1252 ::sfx2::SvBaseLink::UpdateResult ScExternalRefLink::DataChanged(const OUString& /*rMimeType*/, const Any& /*rValue*/)
1254 if (!mbDoRefresh)
1255 return SUCCESS;
1257 OUString aFile, aFilter;
1258 mpDoc->GetLinkManager()->GetDisplayNames(this, NULL, &aFile, NULL, &aFilter);
1259 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
1261 if (!pMgr->isFileLoadable(aFile))
1262 return ERROR_GENERAL;
1264 const OUString* pCurFile = pMgr->getExternalFileName(mnFileId);
1265 if (!pCurFile)
1266 return ERROR_GENERAL;
1268 if (pCurFile->equals(aFile))
1270 // Refresh the current source document.
1271 pMgr->refreshNames(mnFileId);
1273 else
1275 // The source document has changed.
1276 ScDocShell* pDocShell = ScDocShell::GetViewData()->GetDocShell();
1277 ScDocShellModificator aMod(*pDocShell);
1278 pMgr->switchSrcFile(mnFileId, aFile, aFilter);
1279 maFilterName = aFilter;
1280 aMod.SetDocumentModified();
1283 return SUCCESS;
1286 void ScExternalRefLink::Edit(Window* pParent, const Link& /*rEndEditHdl*/)
1288 SvBaseLink::Edit(pParent, LINK(this, ScExternalRefLink, ExternalRefEndEditHdl));
1291 void ScExternalRefLink::SetDoReferesh(bool b)
1293 mbDoRefresh = b;
1296 IMPL_LINK_NOARG(ScExternalRefLink, ExternalRefEndEditHdl)
1298 return 0;
1301 // ============================================================================
1303 static FormulaToken* convertToToken( ScRefCellValue& rCell )
1305 if (rCell.hasEmptyValue())
1307 bool bInherited = (rCell.meType == CELLTYPE_FORMULA);
1308 return new ScEmptyCellToken(bInherited, false);
1311 switch (rCell.meType)
1313 case CELLTYPE_EDIT:
1314 case CELLTYPE_STRING:
1315 return new formula::FormulaStringToken(rCell.getString(NULL));
1316 case CELLTYPE_VALUE:
1317 return new formula::FormulaDoubleToken(rCell.mfValue);
1318 case CELLTYPE_FORMULA:
1320 ScFormulaCell* pFCell = rCell.mpFormula;
1321 sal_uInt16 nError = pFCell->GetErrCode();
1322 if (nError)
1323 return new FormulaErrorToken( nError);
1324 else if (pFCell->IsValue())
1326 double fVal = pFCell->GetValue();
1327 return new formula::FormulaDoubleToken(fVal);
1329 else
1331 svl::SharedString aStr = pFCell->GetString();
1332 return new formula::FormulaStringToken(aStr);
1335 default:
1336 OSL_FAIL("attempted to convert an unknown cell type.");
1339 return NULL;
1342 template<class T>
1343 struct ColumnBatch
1345 std::vector<T> maStorage;
1346 CellType meType1;
1347 CellType meType2;
1348 SCROW mnRowStart;
1350 ColumnBatch(CellType eType1, CellType eType2) :
1351 meType1(eType1), meType2(eType2), mnRowStart(-1) {}
1353 void update(ScRefCellValue& raCell, const SCCOL nCol, const SCROW nRow, ScMatrixRef& xMat)
1355 if (raCell.meType == meType1 || raCell.meType == meType2)
1357 if (mnRowStart < 0)
1358 mnRowStart = nRow;
1359 maStorage.push_back(getValue(raCell));
1361 else
1363 flush(nCol, xMat);
1367 void flush(const SCCOL nCol, ScMatrixRef& xMat)
1369 if (maStorage.empty())
1370 return;
1371 putValues(xMat, nCol);
1372 mnRowStart = -1;
1373 maStorage.clear();
1376 T getValue(ScRefCellValue& raCell) const;
1377 void putValues(ScMatrixRef& xMat, const SCCOL nCol) const;
1380 template<>
1381 inline svl::SharedString ColumnBatch<svl::SharedString>::getValue(ScRefCellValue& rCell) const
1383 return svl::SharedString(rCell.getString(NULL));
1386 template<class T>
1387 inline T ColumnBatch<T>::getValue(ScRefCellValue& raCell) const
1389 return raCell.mfValue;
1392 template<>
1393 inline void ColumnBatch<svl::SharedString>::putValues(ScMatrixRef& xMat, const SCCOL nCol) const
1395 xMat->PutString(maStorage.data(), maStorage.size(), nCol, mnRowStart);
1398 template<class T>
1399 inline void ColumnBatch<T>::putValues(ScMatrixRef& xMat, const SCCOL nCol) const
1401 xMat->PutDouble(maStorage.data(), maStorage.size(), nCol, mnRowStart);
1404 static ScTokenArray* convertToTokenArray(
1405 ScDocument* pSrcDoc, ScRange& rRange, vector<ScExternalRefCache::SingleRangeData>& rCacheData )
1407 ScAddress& s = rRange.aStart;
1408 ScAddress& e = rRange.aEnd;
1410 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1411 SCCOL nCol1 = s.Col(), nCol2 = e.Col();
1412 SCROW nRow1 = s.Row(), nRow2 = e.Row();
1414 if (nTab2 != nTab1)
1415 // For now, we don't support multi-sheet ranges intentionally because
1416 // we don't have a way to express them in a single token. In the
1417 // future we can introduce a new stack variable type svMatrixList with
1418 // a new token type that can store a 3D matrix value and convert a 3D
1419 // range to it.
1420 return NULL;
1422 ::boost::scoped_ptr<ScRange> pUsedRange;
1424 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1425 auto_ptr<ScTokenArray> pArray(new ScTokenArray);
1426 SAL_WNODEPRECATED_DECLARATIONS_POP
1427 bool bFirstTab = true;
1428 vector<ScExternalRefCache::SingleRangeData>::iterator
1429 itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
1431 for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
1433 // Only loop within the data area.
1434 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
1435 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
1436 bool bShrunk;
1437 if (!pSrcDoc->ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false))
1438 // no data within specified range.
1439 continue;
1441 if (pUsedRange.get())
1442 // Make sure the used area only grows, not shrinks.
1443 pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1444 else
1445 pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1447 ScMatrixRef xMat = new ScMatrix(
1448 static_cast<SCSIZE>(nCol2-nCol1+1), static_cast<SCSIZE>(nRow2-nRow1+1));
1450 ScRefCellValue aCell;
1451 ColumnBatch<svl::SharedString> aStringBatch(CELLTYPE_STRING, CELLTYPE_EDIT);
1452 ColumnBatch<double> aDoubleBatch(CELLTYPE_VALUE, CELLTYPE_VALUE);
1454 for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol)
1456 const SCSIZE nC = nCol - nCol1;
1457 for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow)
1459 const SCSIZE nR = nRow - nRow1;
1461 aCell.assign(*pSrcDoc, ScAddress(nCol, nRow, nTab));
1463 aStringBatch.update(aCell, nC, nR, xMat);
1464 aDoubleBatch.update(aCell, nC, nR, xMat);
1466 if (aCell.hasEmptyValue())
1467 // Skip empty cells. Matrix's default values are empty elements.
1468 continue;
1470 switch (aCell.meType)
1472 case CELLTYPE_FORMULA:
1474 ScFormulaCell* pFCell = aCell.mpFormula;
1475 sal_uInt16 nError = pFCell->GetErrCode();
1476 if (nError)
1477 xMat->PutDouble( CreateDoubleError( nError), nC, nR);
1478 else if (pFCell->IsValue())
1480 double fVal = pFCell->GetValue();
1481 xMat->PutDouble(fVal, nC, nR);
1483 else
1485 svl::SharedString aStr = pFCell->GetString();
1486 xMat->PutString(aStr, nC, nR);
1489 break;
1490 default:
1491 OSL_FAIL("attempted to convert an unknown cell type.");
1495 aStringBatch.flush(nC, xMat);
1496 aDoubleBatch.flush(nC, xMat);
1498 if (!bFirstTab)
1499 pArray->AddOpCode(ocSep);
1501 ScMatrixToken aToken(xMat);
1502 pArray->AddToken(aToken);
1504 itrCache->mpRangeData = xMat;
1506 bFirstTab = false;
1509 if (!pUsedRange.get())
1510 return NULL;
1512 s.SetCol(pUsedRange->aStart.Col());
1513 s.SetRow(pUsedRange->aStart.Row());
1514 e.SetCol(pUsedRange->aEnd.Col());
1515 e.SetRow(pUsedRange->aEnd.Row());
1517 return pArray.release();
1520 static ScTokenArray* lcl_fillEmptyMatrix(const ScRange& rRange)
1522 SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1);
1523 SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1);
1524 ScMatrixRef xMat = new ScMatrix(nC, nR);
1526 ScMatrixToken aToken(xMat);
1527 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1528 auto_ptr<ScTokenArray> pArray(new ScTokenArray);
1529 SAL_WNODEPRECATED_DECLARATIONS_POP
1530 pArray->AddToken(aToken);
1531 return pArray.release();
1534 ScExternalRefManager::ScExternalRefManager(ScDocument* pDoc) :
1535 mpDoc(pDoc),
1536 mbInReferenceMarking(false),
1537 mbUserInteractionEnabled(true)
1539 maSrcDocTimer.SetTimeoutHdl( LINK(this, ScExternalRefManager, TimeOutHdl) );
1540 maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL);
1543 ScExternalRefManager::~ScExternalRefManager()
1545 clear();
1548 OUString ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
1550 return maRefCache.getTableName(nFileId, nTabIndex);
1553 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1555 return maRefCache.getCacheTable(nFileId, nTabIndex);
1558 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(
1559 sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex)
1561 return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex);
1564 // ============================================================================
1566 ScExternalRefManager::LinkListener::LinkListener()
1570 ScExternalRefManager::LinkListener::~LinkListener()
1574 // ----------------------------------------------------------------------------
1576 ScExternalRefManager::ApiGuard::ApiGuard(ScDocument* pDoc) :
1577 mpMgr(pDoc->GetExternalRefManager()),
1578 mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled)
1580 // We don't want user interaction handled in the API.
1581 mpMgr->mbUserInteractionEnabled = false;
1584 ScExternalRefManager::ApiGuard::~ApiGuard()
1586 // Restore old value.
1587 mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled;
1590 // ----------------------------------------------------------------------------
1592 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1594 maRefCache.getAllTableNames(nFileId, rTabNames);
1597 SCsTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1599 return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
1602 void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
1604 maRefCache.getAllNumberFormats(rNumFmts);
1607 sal_uInt16 ScExternalRefManager::getExternalFileCount() const
1609 return static_cast< sal_uInt16 >( maSrcFiles.size() );
1612 bool ScExternalRefManager::markUsedByLinkListeners()
1614 bool bAllMarked = false;
1615 for (LinkListenerMap::const_iterator itr = maLinkListeners.begin();
1616 itr != maLinkListeners.end() && !bAllMarked; ++itr)
1618 if (!(*itr).second.empty())
1619 bAllMarked = maRefCache.setCacheDocReferenced( (*itr).first);
1620 /* TODO: LinkListeners should remember the table they're listening to.
1621 * As is, listening to one table will mark all tables of the document
1622 * being referenced. */
1624 return bAllMarked;
1627 bool ScExternalRefManager::markUsedExternalRefCells()
1629 RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end();
1630 for (; itr != itrEnd; ++itr)
1632 RefCellSet::iterator itrCell = itr->second.begin(), itrCellEnd = itr->second.end();
1633 for (; itrCell != itrCellEnd; ++itrCell)
1635 ScFormulaCell* pCell = *itrCell;
1636 bool bUsed = pCell->MarkUsedExternalReferences();
1637 if (bUsed)
1638 // Return true when at least one cell references external docs.
1639 return true;
1642 return false;
1645 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1647 return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, false);
1650 void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced )
1652 mbInReferenceMarking = !bReferenced;
1653 maRefCache.setAllCacheTableReferencedStati( bReferenced );
1656 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, const ScTokenArray& rArray)
1658 ScExternalRefCache::TokenArrayRef pArray(rArray.Clone());
1659 maRefCache.setRangeNameTokens(nFileId, rName, pArray);
1662 namespace {
1665 * Put a single cell data into internal cache table.
1667 * @param pFmt optional cell format index that may need to be stored with
1668 * the cell value.
1670 void putCellDataIntoCache(
1671 ScExternalRefCache& rRefCache, const ScExternalRefCache::TokenRef& pToken,
1672 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1673 const ScExternalRefCache::CellFormat* pFmt)
1675 // Now, insert the token into cache table but don't cache empty cells.
1676 if (pToken->GetType() != formula::svEmptyCell)
1678 sal_uLong nFmtIndex = (pFmt && pFmt->mbIsSet) ? pFmt->mnIndex : 0;
1679 rRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pToken, nFmtIndex);
1684 * Put the data into our internal cache table.
1686 * @param rRefCache cache table set.
1687 * @param pArray single range data to be returned.
1688 * @param nFileId external file ID
1689 * @param rTabName name of the table where the data should be cached.
1690 * @param rCacheData range data to be cached.
1691 * @param rCacheRange original cache range, including the empty region if
1692 * any.
1693 * @param rDataRange reduced cache range that includes only the non-empty
1694 * data area.
1696 void putRangeDataIntoCache(
1697 ScExternalRefCache& rRefCache, ScExternalRefCache::TokenArrayRef& pArray,
1698 sal_uInt16 nFileId, const OUString& rTabName,
1699 const vector<ScExternalRefCache::SingleRangeData>& rCacheData,
1700 const ScRange& rCacheRange, const ScRange& rDataRange)
1702 if (pArray)
1703 // Cache these values.
1704 rRefCache.setCellRangeData(nFileId, rDataRange, rCacheData, pArray);
1705 else
1707 // Array is empty. Fill it with an empty matrix of the required size.
1708 pArray.reset(lcl_fillEmptyMatrix(rCacheRange));
1710 // Make sure to set this range 'cached', to prevent unnecessarily
1711 // accessing the src document time and time again.
1712 ScExternalRefCache::TableTypeRef pCacheTab =
1713 rRefCache.getCacheTable(nFileId, rTabName, true, NULL);
1714 if (pCacheTab)
1715 pCacheTab->setCachedCellRange(
1716 rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row());
1721 * When accessing an external document for the first time, we need to
1722 * populate the cache with all its sheet names (whether they are referenced
1723 * or not) in the correct order. Many client codes that use external
1724 * references make this assumption.
1726 * @param rRefCache cache table set.
1727 * @param pSrcDoc source document instance.
1728 * @param nFileId external file ID associated with the source document.
1730 void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sal_uInt16 nFileId)
1732 if (!pSrcDoc)
1733 return;
1735 if (rRefCache.isDocInitialized(nFileId))
1736 // Already initialized. No need to do this twice.
1737 return;
1739 SCTAB nTabCount = pSrcDoc->GetTableCount();
1740 if (nTabCount)
1742 // Populate the cache with all table names in the source document.
1743 vector<OUString> aTabNames;
1744 aTabNames.reserve(nTabCount);
1745 for (SCTAB i = 0; i < nTabCount; ++i)
1747 OUString aName;
1748 pSrcDoc->GetName(i, aName);
1749 aTabNames.push_back(aName);
1751 rRefCache.initializeDoc(nFileId, aTabNames);
1757 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken(
1758 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1759 const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
1761 if (pCurPos)
1762 insertRefCell(nFileId, *pCurPos);
1764 maybeLinkExternalFile(nFileId);
1766 if (pTab)
1767 *pTab = -1;
1769 if (pFmt)
1770 pFmt->mbIsSet = false;
1772 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1773 if (pSrcDoc)
1775 // source document already loaded in memory. Re-use this instance.
1776 SCTAB nTab;
1777 if (!pSrcDoc->GetTable(rTabName, nTab))
1779 // specified table name doesn't exist in the source document.
1780 ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(errNoRef));
1781 return pToken;
1784 if (pTab)
1785 *pTab = nTab;
1787 ScExternalRefCache::TokenRef pToken =
1788 getSingleRefTokenFromSrcDoc(
1789 nFileId, pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1791 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1792 return pToken;
1795 // Check if the given table name and the cell position is cached.
1796 sal_uInt32 nFmtIndex = 0;
1797 ScExternalRefCache::TokenRef pToken = maRefCache.getCellData(
1798 nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex);
1799 if (pToken)
1801 // Cache hit !
1802 fillCellFormat(nFmtIndex, pFmt);
1803 return pToken;
1806 // reference not cached. read from the source document.
1807 pSrcDoc = getSrcDocument(nFileId);
1808 if (!pSrcDoc)
1810 // Source document not reachable. Throw a reference error.
1811 pToken.reset(new FormulaErrorToken(errNoRef));
1812 return pToken;
1815 SCTAB nTab;
1816 if (!pSrcDoc->GetTable(rTabName, nTab))
1818 // specified table name doesn't exist in the source document.
1819 pToken.reset(new FormulaErrorToken(errNoRef));
1820 return pToken;
1823 if (pTab)
1824 *pTab = nTab;
1826 SCCOL nDataCol1 = 0, nDataCol2 = MAXCOL;
1827 SCROW nDataRow1 = 0, nDataRow2 = MAXROW;
1828 bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2);
1829 if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row())
1831 // requested cell is outside the data area. Don't even bother caching
1832 // this data, but add it to the cached range to prevent accessing the
1833 // source document time and time again.
1834 ScExternalRefCache::TableTypeRef pCacheTab =
1835 maRefCache.getCacheTable(nFileId, rTabName, true, NULL);
1836 if (pCacheTab)
1837 pCacheTab->setCachedCell(rCell.Col(), rCell.Row());
1839 pToken.reset(new ScEmptyCellToken(false, false));
1840 return pToken;
1843 pToken = getSingleRefTokenFromSrcDoc(
1844 nFileId, pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1846 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1847 return pToken;
1850 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens(
1851 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
1853 if (pCurPos)
1854 insertRefCell(nFileId, *pCurPos);
1856 maybeLinkExternalFile(nFileId);
1858 ScRange aDataRange(rRange);
1859 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1860 if (pSrcDoc)
1862 // Document already loaded in memory.
1863 vector<ScExternalRefCache::SingleRangeData> aCacheData;
1864 ScExternalRefCache::TokenArrayRef pArray =
1865 getDoubleRefTokensFromSrcDoc(pSrcDoc, rTabName, aDataRange, aCacheData);
1867 // Put the data into cache.
1868 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1869 return pArray;
1872 // Check if the given table name and the cell position is cached.
1873 ScExternalRefCache::TokenArrayRef pArray =
1874 maRefCache.getCellRangeData(nFileId, rTabName, rRange);
1875 if (pArray)
1876 // Cache hit !
1877 return pArray;
1879 pSrcDoc = getSrcDocument(nFileId);
1880 if (!pSrcDoc)
1882 // Source document is not reachable. Throw a reference error.
1883 pArray.reset(new ScTokenArray);
1884 pArray->AddToken(FormulaErrorToken(errNoRef));
1885 return pArray;
1888 vector<ScExternalRefCache::SingleRangeData> aCacheData;
1889 pArray = getDoubleRefTokensFromSrcDoc(pSrcDoc, rTabName, aDataRange, aCacheData);
1891 // Put the data into cache.
1892 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1893 return pArray;
1896 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(
1897 sal_uInt16 nFileId, const OUString& rName, const ScAddress* pCurPos)
1899 if (pCurPos)
1900 insertRefCell(nFileId, *pCurPos);
1902 maybeLinkExternalFile(nFileId);
1904 OUString aName = rName; // make a copy to have the casing corrected.
1905 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1906 if (pSrcDoc)
1908 // Document already loaded in memory.
1909 ScExternalRefCache::TokenArrayRef pArray =
1910 getRangeNameTokensFromSrcDoc(nFileId, pSrcDoc, aName);
1912 if (pArray)
1913 // Cache this range name array.
1914 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
1916 return pArray;
1919 ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName);
1920 if (pArray.get())
1921 // This range name is cached.
1922 return pArray;
1924 pSrcDoc = getSrcDocument(nFileId);
1925 if (!pSrcDoc)
1926 // failed to load document from disk.
1927 return ScExternalRefCache::TokenArrayRef();
1929 pArray = getRangeNameTokensFromSrcDoc(nFileId, pSrcDoc, aName);
1931 if (pArray)
1932 // Cache this range name array.
1933 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
1935 return pArray;
1938 namespace {
1940 bool hasRangeName(ScDocument& rDoc, const OUString& rName)
1942 ScRangeName* pExtNames = rDoc.GetRangeName();
1943 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
1944 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
1945 return pRangeData != NULL;
1950 bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId, const OUString& rName)
1952 maybeLinkExternalFile(nFileId);
1953 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1954 if (pSrcDoc)
1956 // Only check the presence of the name.
1957 return hasRangeName(*pSrcDoc, rName);
1960 if (maRefCache.isValidRangeName(nFileId, rName))
1961 // Range name is cached.
1962 return true;
1964 pSrcDoc = getSrcDocument(nFileId);
1965 if (!pSrcDoc)
1966 // failed to load document from disk.
1967 return false;
1969 return hasRangeName(*pSrcDoc, rName);
1972 void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId)
1974 RefCellMap::iterator itrFile = maRefCells.find(nFileId);
1975 if (itrFile == maRefCells.end())
1976 return;
1978 RefCellSet& rRefCells = itrFile->second;
1979 for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell());
1981 ScViewData* pViewData = ScDocShell::GetViewData();
1982 if (!pViewData)
1983 return;
1985 ScTabViewShell* pVShell = pViewData->GetViewShell();
1986 if (!pVShell)
1987 return;
1989 // Repainting the grid also repaints the texts, but is there a better way
1990 // to refresh texts?
1991 pVShell->Invalidate(FID_REPAINT);
1992 pVShell->PaintGrid();
1995 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
1997 RefCellMap::iterator itr = maRefCells.find(nFileId);
1998 if (itr == maRefCells.end())
2000 RefCellSet aRefCells;
2001 pair<RefCellMap::iterator, bool> r = maRefCells.insert(
2002 RefCellMap::value_type(nFileId, aRefCells));
2003 if (!r.second)
2004 // insertion failed.
2005 return;
2007 itr = r.first;
2010 ScFormulaCell* pCell = mpDoc->GetFormulaCell(rCell);
2011 if (pCell)
2012 itr->second.insert(pCell);
2015 void ScExternalRefManager::fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat* pFmt) const
2017 if (!pFmt)
2018 return;
2020 short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex);
2021 if (nFmtType != NUMBERFORMAT_UNDEFINED)
2023 pFmt->mbIsSet = true;
2024 pFmt->mnIndex = nFmtIndex;
2025 pFmt->mnType = nFmtType;
2029 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefTokenFromSrcDoc(
2030 sal_uInt16 nFileId, ScDocument* pSrcDoc, const ScAddress& rPos,
2031 ScExternalRefCache::CellFormat* pFmt)
2033 // Get the cell from src doc, and convert it into a token.
2034 ScRefCellValue aCell;
2035 aCell.assign(*pSrcDoc, rPos);
2036 ScExternalRefCache::TokenRef pToken(convertToToken(aCell));
2038 if (!pToken.get())
2040 // Generate an error for unresolvable cells.
2041 pToken.reset( new FormulaErrorToken( errNoValue));
2044 // Get number format information.
2045 sal_uInt32 nFmtIndex = 0;
2046 pSrcDoc->GetNumberFormat(rPos.Col(), rPos.Row(), rPos.Tab(), nFmtIndex);
2047 nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, pSrcDoc);
2048 fillCellFormat(nFmtIndex, pFmt);
2049 return pToken;
2052 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokensFromSrcDoc(
2053 ScDocument* pSrcDoc, const OUString& rTabName, ScRange& rRange,
2054 vector<ScExternalRefCache::SingleRangeData>& rCacheData)
2056 ScExternalRefCache::TokenArrayRef pArray;
2057 SCTAB nTab1;
2059 if (!pSrcDoc->GetTable(rTabName, nTab1))
2061 // specified table name doesn't exist in the source document.
2062 pArray.reset(new ScTokenArray);
2063 pArray->AddToken(FormulaErrorToken(errNoRef));
2064 return pArray;
2067 ScRange aRange(rRange);
2068 SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
2070 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2071 aCacheData.reserve(nTabSpan+1);
2072 aCacheData.push_back(ScExternalRefCache::SingleRangeData());
2073 aCacheData.back().maTableName = ScGlobal::pCharClass->uppercase(rTabName);
2075 for (SCTAB i = 1; i < nTabSpan + 1; ++i)
2077 OUString aTabName;
2078 if (!pSrcDoc->GetName(nTab1 + 1, aTabName))
2079 // source document doesn't have any table by the specified name.
2080 break;
2082 aCacheData.push_back(ScExternalRefCache::SingleRangeData());
2083 aCacheData.back().maTableName = ScGlobal::pCharClass->uppercase(aTabName);
2086 aRange.aStart.SetTab(nTab1);
2087 aRange.aEnd.SetTab(nTab1 + nTabSpan);
2089 pArray.reset(convertToTokenArray(pSrcDoc, aRange, aCacheData));
2090 rRange = aRange;
2091 rCacheData.swap(aCacheData);
2092 return pArray;
2095 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokensFromSrcDoc(
2096 sal_uInt16 nFileId, ScDocument* pSrcDoc, OUString& rName)
2098 ScRangeName* pExtNames = pSrcDoc->GetRangeName();
2099 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
2100 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2101 if (!pRangeData)
2102 return ScExternalRefCache::TokenArrayRef();
2104 // Parse all tokens in this external range data, and replace each absolute
2105 // reference token with an external reference token, and cache them. Also
2106 // register the source document with the link manager if it's a new
2107 // source.
2109 ScExternalRefCache::TokenArrayRef pNew(new ScTokenArray);
2111 ScTokenArray aCode(*pRangeData->GetCode());
2112 for (const FormulaToken* pToken = aCode.First(); pToken; pToken = aCode.Next())
2114 bool bTokenAdded = false;
2115 switch (pToken->GetType())
2117 case svSingleRef:
2119 const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
2120 OUString aTabName;
2121 pSrcDoc->GetName(rRef.Tab(), aTabName);
2122 ScExternalSingleRefToken aNewToken(nFileId, aTabName, static_cast<const ScToken*>(pToken)->GetSingleRef());
2123 pNew->AddToken(aNewToken);
2124 bTokenAdded = true;
2126 break;
2127 case svDoubleRef:
2129 const ScSingleRefData& rRef = static_cast<const ScToken*>(pToken)->GetSingleRef();
2130 OUString aTabName;
2131 pSrcDoc->GetName(rRef.Tab(), aTabName);
2132 ScExternalDoubleRefToken aNewToken(nFileId, aTabName, static_cast<const ScToken*>(pToken)->GetDoubleRef());
2133 pNew->AddToken(aNewToken);
2134 bTokenAdded = true;
2136 break;
2137 default:
2138 ; // nothing
2141 if (!bTokenAdded)
2142 pNew->AddToken(*pToken);
2145 rName = pRangeData->GetName(); // Get the correctly-cased name.
2146 return pNew;
2149 ScDocument* ScExternalRefManager::getInMemorySrcDocument(sal_uInt16 nFileId)
2151 const OUString* pFileName = getExternalFileName(nFileId);
2152 if (!pFileName)
2153 return NULL;
2155 ScDocument* pSrcDoc = NULL;
2156 TypeId aType(TYPE(ScDocShell));
2157 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
2158 while (pShell)
2160 SfxMedium* pMedium = pShell->GetMedium();
2161 if (pMedium && !pMedium->GetName().isEmpty())
2163 // TODO: We should make the case sensitivity platform dependent.
2164 if (pFileName->equalsIgnoreAsciiCase(pMedium->GetName()))
2166 // Found !
2167 pSrcDoc = pShell->GetDocument();
2168 break;
2171 else
2173 // handle unsaved documents here
2174 OUString aName = pShell->GetName();
2175 if (pFileName->equalsIgnoreAsciiCase(aName))
2177 // Found !
2178 SrcShell aSrcDoc;
2179 aSrcDoc.maShell = pShell;
2180 maUnsavedDocShells.insert(DocShellMap::value_type(nFileId, aSrcDoc));
2181 StartListening(*pShell);
2182 pSrcDoc = pShell->GetDocument();
2183 break;
2186 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
2189 initDocInCache(maRefCache, pSrcDoc, nFileId);
2190 return pSrcDoc;
2193 ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId)
2195 if (!mpDoc->IsExecuteLinkEnabled())
2196 return NULL;
2198 DocShellMap::iterator itrEnd = maDocShells.end();
2199 DocShellMap::iterator itr = maDocShells.find(nFileId);
2201 if (itr != itrEnd)
2203 // document already loaded.
2205 SfxObjectShell* p = itr->second.maShell;
2206 itr->second.maLastAccess = Time( Time::SYSTEM );
2207 return static_cast<ScDocShell*>(p)->GetDocument();
2210 itrEnd = maUnsavedDocShells.end();
2211 itr = maUnsavedDocShells.find(nFileId);
2212 if (itr != itrEnd)
2214 //document is unsaved document
2216 SfxObjectShell* p = itr->second.maShell;
2217 itr->second.maLastAccess = Time( Time::SYSTEM );
2218 return static_cast<ScDocShell*>(p)->GetDocument();
2221 const OUString* pFile = getExternalFileName(nFileId);
2222 if (!pFile)
2223 // no file name associated with this ID.
2224 return NULL;
2226 OUString aFilter;
2227 SrcShell aSrcDoc;
2228 aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
2229 if (!aSrcDoc.maShell.Is())
2231 // source document could not be loaded.
2232 return NULL;
2235 if (maDocShells.empty())
2237 // If this is the first source document insertion, start up the timer.
2238 maSrcDocTimer.Start();
2241 maDocShells.insert(DocShellMap::value_type(nFileId, aSrcDoc));
2242 SfxObjectShell* p = aSrcDoc.maShell;
2243 ScDocument* pSrcDoc = static_cast<ScDocShell*>(p)->GetDocument();
2244 initDocInCache(maRefCache, pSrcDoc, nFileId);
2245 return pSrcDoc;
2248 SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUString& rFilter)
2250 const SrcFileData* pFileData = getExternalFileData(nFileId);
2251 if (!pFileData)
2252 return NULL;
2254 // Always load the document by using the path created from the relative
2255 // path. If the referenced document is not there, simply exit. The
2256 // original file name should be used only when the relative path is not
2257 // given.
2258 OUString aFile = pFileData->maFileName;
2259 maybeCreateRealFileName(nFileId);
2260 if (!pFileData->maRealFileName.isEmpty())
2261 aFile = pFileData->maRealFileName;
2263 if (!isFileLoadable(aFile))
2264 return NULL;
2266 OUString aOptions = pFileData->maFilterOptions;
2267 if ( !pFileData->maFilterName.isEmpty() )
2268 rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter
2269 else
2270 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);;
2271 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
2272 const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
2274 if (pFileData->maRelativeName.isEmpty())
2276 // Generate a relative file path.
2277 INetURLObject aBaseURL(getOwnDocumentName());
2278 aBaseURL.insertName(OUString("content.xml"));
2280 OUString aStr = URIHelper::simpleNormalizedMakeRelative(
2281 aBaseURL.GetMainURL(INetURLObject::NO_DECODE), aFile);
2283 setRelativeFileName(nFileId, aStr);
2286 SfxItemSet* pSet = new SfxAllItemSet(SFX_APP()->GetPool());
2287 if (!aOptions.isEmpty())
2288 pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
2290 // make medium hidden to prevent assertion from progress bar
2291 pSet->Put( SfxBoolItem(SID_HIDDEN, true) );
2293 SAL_WNODEPRECATED_DECLARATIONS_PUSH
2294 auto_ptr<SfxMedium> pMedium(new SfxMedium(aFile, STREAM_STD_READ, pFilter, pSet));
2295 SAL_WNODEPRECATED_DECLARATIONS_POP
2296 if (pMedium->GetError() != ERRCODE_NONE)
2297 return NULL;
2299 // To load encrypted documents with password, user interaction needs to be enabled.
2300 pMedium->UseInteractionHandler(mbUserInteractionEnabled);
2302 ScDocShell* pNewShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL);
2303 SfxObjectShellRef aRef = pNewShell;
2305 // increment the recursive link count of the source document.
2306 ScExtDocOptions* pExtOpt = mpDoc->GetExtDocOptions();
2307 sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0;
2308 ScDocument* pSrcDoc = pNewShell->GetDocument();
2309 pSrcDoc->EnableExecuteLink(false); // to prevent circular access of external references.
2310 pSrcDoc->EnableUndo(false);
2311 pSrcDoc->EnableAdjustHeight(false);
2312 pSrcDoc->EnableUserInteraction(false);
2314 ScExtDocOptions* pExtOptNew = pSrcDoc->GetExtDocOptions();
2315 if (!pExtOptNew)
2317 pExtOptNew = new ScExtDocOptions;
2318 pSrcDoc->SetExtDocOptions(pExtOptNew);
2320 pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
2322 pNewShell->DoLoad(pMedium.release());
2324 // with UseInteractionHandler, options may be set by dialog during DoLoad
2325 OUString aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium());
2326 if (!aNew.isEmpty() && aNew != aOptions)
2327 aOptions = aNew;
2328 setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options
2330 return aRef;
2333 bool ScExternalRefManager::isFileLoadable(const OUString& rFile) const
2335 if (rFile.isEmpty())
2336 return false;
2338 if (isOwnDocument(rFile))
2339 return false;
2340 OUString aPhysical;
2341 if (utl::LocalFileHelper::ConvertURLToPhysicalName(rFile, aPhysical) && !aPhysical.isEmpty())
2343 // #i114504# try IsFolder/Exists only for file URLs
2345 if (utl::UCBContentHelper::IsFolder(rFile))
2346 return false;
2348 return utl::UCBContentHelper::Exists(rFile);
2350 else
2351 return true; // for http and others, Exists doesn't work, but the URL can still be opened
2354 void ScExternalRefManager::maybeLinkExternalFile(sal_uInt16 nFileId)
2356 if (maLinkedDocs.count(nFileId))
2357 // file alerady linked, or the link has been broken.
2358 return;
2360 // Source document not linked yet. Link it now.
2361 const OUString* pFileName = getExternalFileName(nFileId);
2362 if (!pFileName)
2363 return;
2365 OUString aFilter, aOptions;
2366 const SrcFileData* pFileData = getExternalFileData(nFileId);
2367 if (pFileData)
2369 aFilter = pFileData->maFilterName;
2370 aOptions = pFileData->maFilterOptions;
2372 // If a filter was already set (for example, loading the cached table),
2373 // don't call GetFilterName which has to access the source file.
2374 if (aFilter.isEmpty())
2375 ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false);
2376 sfx2::LinkManager* pLinkMgr = mpDoc->GetLinkManager();
2377 ScExternalRefLink* pLink = new ScExternalRefLink(mpDoc, nFileId, aFilter);
2378 OSL_ENSURE(pFileName, "ScExternalRefManager::insertExternalFileLink: file name pointer is NULL");
2379 OUString aTmp = aFilter;
2380 pLinkMgr->InsertFileLink(*pLink, OBJECT_CLIENT_FILE, *pFileName, &aTmp);
2382 pLink->SetDoReferesh(false);
2383 pLink->Update();
2384 pLink->SetDoReferesh(true);
2386 maLinkedDocs.insert(LinkedDocMap::value_type(nFileId, true));
2389 void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const OUString& rOwnDocName)
2391 if (maRelativeName.isEmpty())
2392 // No relative path given. Nothing to do.
2393 return;
2395 if (!maRealFileName.isEmpty())
2396 // Real file name already created. Nothing to do.
2397 return;
2399 // Formulate the absolute file path from the relative path.
2400 const OUString& rRelPath = maRelativeName;
2401 INetURLObject aBaseURL(rOwnDocName);
2402 aBaseURL.insertName(OUString("content.xml"));
2403 bool bWasAbs = false;
2404 maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::NO_DECODE);
2407 void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId)
2409 if (nFileId >= maSrcFiles.size())
2410 return;
2412 maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
2415 OUString ScExternalRefManager::getOwnDocumentName() const
2417 SfxObjectShell* pShell = mpDoc->GetDocumentShell();
2418 if (!pShell)
2419 // This should not happen!
2420 return OUString();
2422 SfxMedium* pMed = pShell->GetMedium();
2423 if (!pMed)
2424 return OUString();
2426 return pMed->GetName();
2429 bool ScExternalRefManager::isOwnDocument(const OUString& rFile) const
2431 return getOwnDocumentName().equals(rFile);
2434 void ScExternalRefManager::convertToAbsName(OUString& rFile) const
2436 // unsaved documents have no AbsName
2437 TypeId aType(TYPE(ScDocShell));
2438 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
2439 while (pShell)
2441 if (rFile == pShell->GetName())
2442 return;
2444 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
2447 SfxObjectShell* pDocShell = mpDoc->GetDocumentShell();
2448 rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
2451 sal_uInt16 ScExternalRefManager::getExternalFileId(const OUString& rFile)
2453 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2454 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2455 if (itr != itrEnd)
2457 size_t nId = distance(itrBeg, itr);
2458 return static_cast<sal_uInt16>(nId);
2461 SrcFileData aData;
2462 aData.maFileName = rFile;
2463 maSrcFiles.push_back(aData);
2464 return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
2467 const OUString* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
2469 if (nFileId >= maSrcFiles.size())
2470 return NULL;
2472 if (bForceOriginal)
2473 return &maSrcFiles[nFileId].maFileName;
2475 maybeCreateRealFileName(nFileId);
2477 if (!maSrcFiles[nFileId].maRealFileName.isEmpty())
2478 return &maSrcFiles[nFileId].maRealFileName;
2480 return &maSrcFiles[nFileId].maFileName;
2483 std::vector<OUString> ScExternalRefManager::getAllCachedExternalFileNames() const
2485 std::vector<OUString> aNames;
2486 aNames.reserve(maSrcFiles.size());
2487 std::vector<SrcFileData>::const_iterator it = maSrcFiles.begin(), itEnd = maSrcFiles.end();
2488 for (; it != itEnd; ++it)
2490 const SrcFileData& rData = *it;
2491 aNames.push_back(rData.maFileName);
2494 return aNames;
2497 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
2499 return nFileId < maSrcFiles.size();
2502 bool ScExternalRefManager::hasExternalFile(const OUString& rFile) const
2504 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2505 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2506 return itr != itrEnd;
2509 const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const
2511 if (nFileId >= maSrcFiles.size())
2512 return NULL;
2514 return &maSrcFiles[nFileId];
2517 const OUString* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
2519 return maRefCache.getRealTableName(nFileId, rTabName);
2522 const OUString* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
2524 return maRefCache.getRealRangeName(nFileId, rRangeName);
2527 template<typename MapContainer>
2528 static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
2530 typename MapContainer::iterator itr = rMap.find(nFileId);
2531 if (itr != rMap.end())
2533 // Close this document shell.
2534 itr->second.maShell->DoClose();
2535 rMap.erase(itr);
2539 void ScExternalRefManager::clearCache(sal_uInt16 nFileId)
2541 maRefCache.clearCache(nFileId);
2544 void ScExternalRefManager::refreshNames(sal_uInt16 nFileId)
2546 clearCache(nFileId);
2547 lcl_removeByFileId(nFileId, maDocShells);
2549 if (maDocShells.empty())
2550 maSrcDocTimer.Stop();
2552 // Update all cells containing names from this source document.
2553 refreshAllRefCells(nFileId);
2555 notifyAllLinkListeners(nFileId, LINK_MODIFIED);
2558 void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
2560 // Turn all formula cells referencing this external document into static
2561 // cells.
2562 RefCellMap::iterator itrRefs = maRefCells.find(nFileId);
2563 if (itrRefs != maRefCells.end())
2565 // Make a copy because removing the formula cells below will modify
2566 // the original container.
2567 RefCellSet aSet = itrRefs->second;
2568 for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(mpDoc));
2569 maRefCells.erase(nFileId);
2572 // Remove all named ranges that reference this document.
2574 // Global named ranges.
2575 ScRangeName* pRanges = mpDoc->GetRangeName();
2576 if (pRanges)
2577 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2579 // Sheet-local named ranges.
2580 for (SCTAB i = 0, n = mpDoc->GetTableCount(); i < n; ++i)
2582 pRanges = mpDoc->GetRangeName(i);
2583 if (pRanges)
2584 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2587 clearCache(nFileId);
2588 lcl_removeByFileId(nFileId, maDocShells);
2590 if (maDocShells.empty())
2591 maSrcDocTimer.Stop();
2593 LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
2594 if (itr != maLinkedDocs.end())
2595 itr->second = false;
2597 notifyAllLinkListeners(nFileId, LINK_BROKEN);
2600 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const OUString& rNewFile, const OUString& rNewFilter)
2602 maSrcFiles[nFileId].maFileName = rNewFile;
2603 maSrcFiles[nFileId].maRelativeName = OUString();
2604 maSrcFiles[nFileId].maRealFileName = OUString();
2605 if (!maSrcFiles[nFileId].maFilterName.equals(rNewFilter))
2607 // Filter type has changed.
2608 maSrcFiles[nFileId].maFilterName = rNewFilter;
2609 maSrcFiles[nFileId].maFilterOptions = OUString();
2611 refreshNames(nFileId);
2614 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const OUString& rRelUrl)
2616 if (nFileId >= maSrcFiles.size())
2617 return;
2618 maSrcFiles[nFileId].maRelativeName = rRelUrl;
2621 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const OUString& rFilterName, const OUString& rOptions)
2623 if (nFileId >= maSrcFiles.size())
2624 return;
2625 maSrcFiles[nFileId].maFilterName = rFilterName;
2626 maSrcFiles[nFileId].maFilterOptions = rOptions;
2629 void ScExternalRefManager::clear()
2631 DocShellMap::iterator itrEnd = maDocShells.end();
2632 for (DocShellMap::iterator itr = maDocShells.begin(); itr != itrEnd; ++itr)
2633 itr->second.maShell->DoClose();
2635 maDocShells.clear();
2636 maSrcDocTimer.Stop();
2639 bool ScExternalRefManager::hasExternalData() const
2641 return !maSrcFiles.empty();
2644 void ScExternalRefManager::resetSrcFileData(const OUString& rBaseFileUrl)
2646 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2647 itr != itrEnd; ++itr)
2649 // Re-generate relative file name from the absolute file name.
2650 OUString aAbsName = itr->maRealFileName;
2651 if (aAbsName.isEmpty())
2652 aAbsName = itr->maFileName;
2654 itr->maRelativeName = URIHelper::simpleNormalizedMakeRelative(
2655 rBaseFileUrl, aAbsName);
2659 void ScExternalRefManager::updateAbsAfterLoad()
2661 OUString aOwn( getOwnDocumentName() );
2662 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2663 itr != itrEnd; ++itr)
2665 // update maFileName to the real file name,
2666 // to be called when the original name is no longer needed (after CompileXML)
2668 itr->maybeCreateRealFileName( aOwn );
2669 OUString aReal = itr->maRealFileName;
2670 if (!aReal.isEmpty())
2671 itr->maFileName = aReal;
2675 void ScExternalRefManager::removeRefCell(ScFormulaCell* pCell)
2677 for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell));
2680 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
2682 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2683 if (itr == maLinkListeners.end())
2685 pair<LinkListenerMap::iterator, bool> r = maLinkListeners.insert(
2686 LinkListenerMap::value_type(nFileId, LinkListeners()));
2687 if (!r.second)
2689 OSL_FAIL("insertion of new link listener list failed");
2690 return;
2693 itr = r.first;
2696 LinkListeners& rList = itr->second;
2697 rList.insert(pListener);
2700 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
2702 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2703 if (itr == maLinkListeners.end())
2704 // no listeners for a specified file.
2705 return;
2707 LinkListeners& rList = itr->second;
2708 rList.erase(pListener);
2710 if (rList.empty())
2711 // No more listeners for this file. Remove its entry.
2712 maLinkListeners.erase(itr);
2715 void ScExternalRefManager::removeLinkListener(LinkListener* pListener)
2717 LinkListenerMap::iterator itr = maLinkListeners.begin(), itrEnd = maLinkListeners.end();
2718 for (; itr != itrEnd; ++itr)
2719 itr->second.erase(pListener);
2722 void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
2724 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
2725 if (itr == maLinkListeners.end())
2726 // no listeners for a specified file.
2727 return;
2729 LinkListeners& rList = itr->second;
2730 for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
2733 void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut)
2735 DocShellMap aNewDocShells;
2736 DocShellMap::iterator itr = maDocShells.begin(), itrEnd = maDocShells.end();
2737 for (; itr != itrEnd; ++itr)
2739 // in 100th of a second.
2740 sal_Int32 nSinceLastAccess = (Time( Time::SYSTEM ) - itr->second.maLastAccess).GetTime();
2741 if (nSinceLastAccess < nTimeOut)
2742 aNewDocShells.insert(*itr);
2743 else
2744 // Timed out. Let's close this.
2745 itr->second.maShell->DoClose();
2747 maDocShells.swap(aNewDocShells);
2749 if (maDocShells.empty())
2750 maSrcDocTimer.Stop();
2753 sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument* pSrcDoc)
2755 NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
2756 if (itr == maNumFormatMap.end())
2758 // Number formatter map is not initialized for this external document.
2759 pair<NumFmtMap::iterator, bool> r = maNumFormatMap.insert(
2760 NumFmtMap::value_type(nFileId, SvNumberFormatterMergeMap()));
2762 if (!r.second)
2763 // insertion failed.
2764 return nNumFmt;
2766 itr = r.first;
2767 mpDoc->GetFormatTable()->MergeFormatter( *pSrcDoc->GetFormatTable());
2768 SvNumberFormatterMergeMap aMap = mpDoc->GetFormatTable()->ConvertMergeTableToMap();
2769 itr->second.swap(aMap);
2771 const SvNumberFormatterMergeMap& rMap = itr->second;
2772 SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
2773 if (itrNumFmt != rMap.end())
2774 // mapped value found.
2775 return itrNumFmt->second;
2777 return nNumFmt;
2780 void ScExternalRefManager::transformUnsavedRefToSavedRef( SfxObjectShell* pShell )
2782 DocShellMap::iterator itr = maUnsavedDocShells.begin();
2783 while( itr != maUnsavedDocShells.end() )
2785 if (&(itr->second.maShell) == pShell)
2787 // found that the shell is marked as unsaved
2788 OUString aFileURL = pShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DECODE_TO_IURI);
2789 switchSrcFile(itr->first, aFileURL, OUString());
2790 EndListening(*pShell);
2791 maUnsavedDocShells.erase(itr++);
2796 void ScExternalRefManager::Notify( SfxBroadcaster&, const SfxHint& rHint )
2798 if ( rHint.ISA( SfxEventHint ) )
2800 sal_uLong nEventId = ((SfxEventHint&)rHint).GetEventId();
2801 switch ( nEventId )
2803 case SFX_EVENT_PREPARECLOSEDOC:
2805 SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
2806 ScDocShell* pDocShell = static_cast< ScDocShell* >( pObjShell );
2807 WarningBox aBox( pDocShell->GetActiveDialogParent(), WinBits( WB_OK ),
2808 ScGlobal::GetRscString( STR_CLOSE_WITH_UNSAVED_REFS ) );
2809 aBox.Execute();
2811 break;
2812 case SFX_EVENT_SAVEDOCDONE:
2813 case SFX_EVENT_SAVEASDOCDONE:
2815 SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
2816 transformUnsavedRefToSavedRef(pObjShell);
2818 break;
2819 default:
2820 break;
2825 IMPL_LINK(ScExternalRefManager, TimeOutHdl, AutoTimer*, pTimer)
2827 if (pTimer == &maSrcDocTimer)
2828 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN);
2830 return 0;
2833 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */