summaryrefslogtreecommitdiff
path: root/sc/source/ui/docshell/externalrefmgr.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/ui/docshell/externalrefmgr.cxx')
-rw-r--r--sc/source/ui/docshell/externalrefmgr.cxx2378
1 files changed, 2378 insertions, 0 deletions
diff --git a/sc/source/ui/docshell/externalrefmgr.cxx b/sc/source/ui/docshell/externalrefmgr.cxx
new file mode 100644
index 000000000000..e18ef20d96b9
--- /dev/null
+++ b/sc/source/ui/docshell/externalrefmgr.cxx
@@ -0,0 +1,2378 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: externalrefmgr.cxx,v $
+ * $Revision: 1.1.2.33 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sc.hxx"
+
+
+
+// INCLUDE ---------------------------------------------------------------
+
+#include "externalrefmgr.hxx"
+#include "document.hxx"
+#include "token.hxx"
+#include "tokenarray.hxx"
+#include "address.hxx"
+#include "tablink.hxx"
+#include "docsh.hxx"
+#include "scextopt.hxx"
+#include "rangenam.hxx"
+#include "cell.hxx"
+#include "viewdata.hxx"
+#include "tabvwsh.hxx"
+#include "sc.hrc"
+
+#include "sfx2/app.hxx"
+#include "sfx2/docfilt.hxx"
+#include "sfx2/docfile.hxx"
+#include "sfx2/fcontnr.hxx"
+#include "sfx2/sfxsids.hrc"
+#include "sfx2/objsh.hxx"
+#include "svl/broadcast.hxx"
+#include "svl/smplhint.hxx"
+#include "svl/itemset.hxx"
+#include "svl/stritem.hxx"
+#include "svl/urihelper.hxx"
+#include "svl/zformat.hxx"
+#include "svx/linkmgr.hxx"
+#include "tools/urlobj.hxx"
+#include "unotools/ucbhelper.hxx"
+
+#include <memory>
+#include <algorithm>
+
+using ::std::auto_ptr;
+using ::com::sun::star::uno::Any;
+using ::rtl::OUString;
+using ::std::vector;
+using ::std::find;
+using ::std::find_if;
+using ::std::distance;
+using ::std::pair;
+using ::std::list;
+using ::std::unary_function;
+using namespace formula;
+
+#define SRCDOC_LIFE_SPAN 6000 // 1 minute (in 100th of a sec)
+#define SRCDOC_SCAN_INTERVAL 1000*5 // every 5 seconds (in msec)
+
+namespace {
+
+class TabNameSearchPredicate : public unary_function<bool, ScExternalRefCache::TableName>
+{
+public:
+ explicit TabNameSearchPredicate(const String& rSearchName) :
+ maSearchName(ScGlobal::pCharClass->upper(rSearchName))
+ {
+ }
+
+ bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const
+ {
+ // Ok, I'm doing case insensitive search here.
+ return rTabNameSet.maUpperName.Equals(maSearchName);
+ }
+
+private:
+ String maSearchName;
+};
+
+class FindSrcFileByName : public unary_function<ScExternalRefManager::SrcFileData, bool>
+{
+public:
+ FindSrcFileByName(const String& rMatchName) :
+ mrMatchName(rMatchName)
+ {
+ }
+
+ bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const
+ {
+ return rSrcData.maFileName.Equals(mrMatchName);
+ }
+
+private:
+ const String& mrMatchName;
+};
+
+class NotifyLinkListener : public unary_function<ScExternalRefManager::LinkListener*, void>
+{
+public:
+ NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) :
+ mnFileId(nFileId), meType(eType) {}
+
+ NotifyLinkListener(const NotifyLinkListener& r) :
+ mnFileId(r.mnFileId), meType(r.meType) {}
+
+ void operator() (ScExternalRefManager::LinkListener* p) const
+ {
+ p->notify(mnFileId, meType);
+ }
+private:
+ sal_uInt16 mnFileId;
+ ScExternalRefManager::LinkUpdateType meType;
+};
+
+}
+
+// ============================================================================
+
+ScExternalRefCache::Table::Table()
+ : meReferenced( REFERENCED_MARKED )
+ // Prevent accidental data loss due to lack of knowledge.
+{
+}
+
+ScExternalRefCache::Table::~Table()
+{
+}
+
+void ScExternalRefCache::Table::setReferencedFlag( ScExternalRefCache::Table::ReferencedFlag eFlag )
+{
+ meReferenced = eFlag;
+}
+
+void ScExternalRefCache::Table::setReferenced( bool bReferenced )
+{
+ if (meReferenced != REFERENCED_PERMANENT)
+ meReferenced = (bReferenced ? REFERENCED_MARKED : UNREFERENCED);
+}
+
+ScExternalRefCache::Table::ReferencedFlag ScExternalRefCache::Table::getReferencedFlag() const
+{
+ return meReferenced;
+}
+
+bool ScExternalRefCache::Table::isReferenced() const
+{
+ return meReferenced != UNREFERENCED;
+}
+
+void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef pToken, sal_uInt32 nFmtIndex)
+{
+ using ::std::pair;
+ RowsDataType::iterator itrRow = maRows.find(nRow);
+ if (itrRow == maRows.end())
+ {
+ // This row does not exist yet.
+ pair<RowsDataType::iterator, bool> res = maRows.insert(
+ RowsDataType::value_type(nRow, RowDataType()));
+
+ if (!res.second)
+ return;
+
+ itrRow = res.first;
+ }
+
+ // Insert this token into the specified column location. I don't need to
+ // check for existing data. Just overwrite it.
+ RowDataType& rRow = itrRow->second;
+ ScExternalRefCache::Cell aCell;
+ aCell.mxToken = pToken;
+ aCell.mnFmtIndex = nFmtIndex;
+ rRow.insert(RowDataType::value_type(nCol, aCell));
+}
+
+ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const
+{
+ RowsDataType::const_iterator itrTable = maRows.find(nRow);
+ if (itrTable == maRows.end())
+ {
+ // this table doesn't have the specified row.
+ return TokenRef();
+ }
+
+ const RowDataType& rRowData = itrTable->second;
+ RowDataType::const_iterator itrRow = rRowData.find(nCol);
+ if (itrRow == rRowData.end())
+ {
+ // this row doesn't have the specified column.
+ return TokenRef();
+ }
+
+ const Cell& rCell = itrRow->second;
+ if (pnFmtIndex)
+ *pnFmtIndex = rCell.mnFmtIndex;
+
+ return rCell.mxToken;
+}
+
+bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const
+{
+ RowsDataType::const_iterator itrRow = maRows.find(nRow);
+ return itrRow != maRows.end();
+}
+
+void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows) const
+{
+ vector<SCROW> aRows;
+ aRows.reserve(maRows.size());
+ RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end();
+ for (; itr != itrEnd; ++itr)
+ aRows.push_back(itr->first);
+
+ // hash map is not ordered, so we need to explicitly sort it.
+ ::std::sort(aRows.begin(), aRows.end());
+ rRows.swap(aRows);
+}
+
+void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols) const
+{
+ RowsDataType::const_iterator itrRow = maRows.find(nRow);
+ if (itrRow == maRows.end())
+ // this table doesn't have the specified row.
+ return;
+
+ const RowDataType& rRowData = itrRow->second;
+ vector<SCCOL> aCols;
+ aCols.reserve(rRowData.size());
+ RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
+ for (; itrCol != itrColEnd; ++itrCol)
+ aCols.push_back(itrCol->first);
+
+ // hash map is not ordered, so we need to explicitly sort it.
+ ::std::sort(aCols.begin(), aCols.end());
+ rCols.swap(aCols);
+}
+
+void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
+{
+ RowsDataType::const_iterator itrRow = maRows.begin(), itrRowEnd = maRows.end();
+ for (; itrRow != itrRowEnd; ++itrRow)
+ {
+ const RowDataType& rRowData = itrRow->second;
+ RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end();
+ for (; itrCol != itrColEnd; ++itrCol)
+ {
+ const Cell& rCell = itrCol->second;
+ rNumFmts.push_back(rCell.mnFmtIndex);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+ScExternalRefCache::TableName::TableName(const String& rUpper, const String& rReal) :
+ maUpperName(rUpper), maRealName(rReal)
+{
+}
+
+// ----------------------------------------------------------------------------
+
+ScExternalRefCache::CellFormat::CellFormat() :
+ mbIsSet(false), mnType(NUMBERFORMAT_ALL), mnIndex(0)
+{
+}
+
+// ----------------------------------------------------------------------------
+
+ScExternalRefCache::ScExternalRefCache()
+{
+}
+ScExternalRefCache::~ScExternalRefCache()
+{
+}
+
+const String* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const String& rTabName) const
+{
+ DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ return NULL;
+ }
+
+ const DocItem& rDoc = itrDoc->second;
+ TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
+ ScGlobal::pCharClass->upper(rTabName));
+ if (itrTabId == rDoc.maTableNameIndex.end())
+ {
+ // the specified table is not in cache.
+ return NULL;
+ }
+
+ return &rDoc.maTableNames[itrTabId->second].maRealName;
+}
+
+const String* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const String& rRangeName) const
+{
+ DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ return NULL;
+ }
+
+ const DocItem& rDoc = itrDoc->second;
+ NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find(
+ ScGlobal::pCharClass->upper(rRangeName));
+ if (itr == rDoc.maRealRangeNameMap.end())
+ // range name not found.
+ return NULL;
+
+ return &itr->second;
+}
+
+ScExternalRefCache::TokenRef ScExternalRefCache::getCellData(
+ sal_uInt16 nFileId, const String& rTabName, SCCOL nCol, SCROW nRow,
+ bool bEmptyCellOnNull, bool bWriteEmpty, sal_uInt32* pnFmtIndex)
+{
+ DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ return TokenRef();
+ }
+
+ const DocItem& rDoc = itrDoc->second;
+ TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find(
+ ScGlobal::pCharClass->upper(rTabName));
+ if (itrTabId == rDoc.maTableNameIndex.end())
+ {
+ // the specified table is not in cache.
+ return TokenRef();
+ }
+
+ const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
+ if (!pTableData.get())
+ {
+ // the table data is not instantiated yet.
+ return TokenRef();
+ }
+
+ TokenRef pToken = pTableData->getCell(nCol, nRow, pnFmtIndex);
+ if (!pToken && bEmptyCellOnNull)
+ {
+ pToken.reset(new ScEmptyCellToken(false, false));
+ if (bWriteEmpty)
+ pTableData->setCell(nCol, nRow, pToken);
+ }
+ return pToken;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData(
+ sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange, bool bEmptyCellOnNull, bool bWriteEmpty)
+{
+ DocDataType::iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ // specified document is not cached.
+ return TokenArrayRef();
+
+ DocItem& rDoc = itrDoc->second;
+
+ TableNameIndexMap::iterator itrTabId = rDoc.maTableNameIndex.find(
+ ScGlobal::pCharClass->upper(rTabName));
+ if (itrTabId == rDoc.maTableNameIndex.end())
+ // the specified table is not in cache.
+ return TokenArrayRef();
+
+ const ScAddress& s = rRange.aStart;
+ const ScAddress& e = rRange.aEnd;
+
+ SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
+ SCCOL nCol1 = s.Col(), nCol2 = e.Col();
+ SCROW nRow1 = s.Row(), nRow2 = e.Row();
+
+ // Make sure I have all the tables cached.
+ size_t nTabFirstId = itrTabId->second;
+ size_t nTabLastId = nTabFirstId + nTab2 - nTab1;
+ if (nTabLastId >= rDoc.maTables.size())
+ // not all tables are cached.
+ return TokenArrayRef();
+
+ ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
+ RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
+ if (itrRange != rDoc.maRangeArrays.end())
+ {
+ return itrRange->second;
+ }
+
+ TokenArrayRef pArray(new ScTokenArray);
+ bool bFirstTab = true;
+ for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
+ {
+ TableTypeRef pTab = rDoc.maTables[nTab];
+ if (!pTab.get())
+ return TokenArrayRef();
+
+ ScMatrixRef xMat = new ScMatrix(
+ static_cast<SCSIZE>(nCol2-nCol1+1), static_cast<SCSIZE>(nRow2-nRow1+1));
+
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ TokenRef pToken = pTab->getCell(nCol, nRow);
+ if (!pToken)
+ {
+ if (bEmptyCellOnNull)
+ {
+ pToken.reset(new ScEmptyCellToken(false, false));
+ if (bWriteEmpty)
+ pTab->setCell(nCol, nRow, pToken);
+ }
+ else
+ return TokenArrayRef();
+ }
+
+ SCSIZE nC = nCol - nCol1, nR = nRow - nRow1;
+ switch (pToken->GetType())
+ {
+ case svDouble:
+ xMat->PutDouble(pToken->GetDouble(), nC, nR);
+ break;
+ case svString:
+ xMat->PutString(pToken->GetString(), nC, nR);
+ break;
+ default:
+ xMat->PutEmpty(nC, nR);
+ }
+ }
+ }
+
+ if (!bFirstTab)
+ pArray->AddOpCode(ocSep);
+
+ ScMatrix* pMat2 = xMat;
+ ScMatrixToken aToken(pMat2);
+ pArray->AddToken(aToken);
+
+ bFirstTab = false;
+ }
+ rDoc.maRangeArrays.insert( RangeArrayMap::value_type( aCacheRange, pArray));
+ return pArray;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const String& rName)
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return TokenArrayRef();
+
+ RangeNameMap& rMap = pDoc->maRangeNames;
+ RangeNameMap::const_iterator itr = rMap.find(
+ ScGlobal::pCharClass->upper(rName));
+ if (itr == rMap.end())
+ return TokenArrayRef();
+
+ return itr->second;
+}
+
+void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const String& rName, TokenArrayRef pArray)
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return;
+
+ String aUpperName = ScGlobal::pCharClass->upper(rName);
+ RangeNameMap& rMap = pDoc->maRangeNames;
+ rMap.insert(RangeNameMap::value_type(aUpperName, pArray));
+ pDoc->maRealRangeNameMap.insert(NamePairMap::value_type(aUpperName, rName));
+}
+
+void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const String& rTabName, SCROW nRow, SCCOL nCol,
+ TokenRef pToken, sal_uInt32 nFmtIndex)
+{
+ if (!isDocInitialized(nFileId))
+ return;
+
+ using ::std::pair;
+ DocItem* pDocItem = getDocItem(nFileId);
+ if (!pDocItem)
+ return;
+
+ DocItem& rDoc = *pDocItem;
+
+ // See if the table by this name already exists.
+ TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
+ ScGlobal::pCharClass->upper(rTabName));
+ if (itrTabName == rDoc.maTableNameIndex.end())
+ // Table not found. Maybe the table name or the file id is wrong ???
+ return;
+
+ TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
+ if (!pTableData.get())
+ pTableData.reset(new Table);
+
+ pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
+}
+
+void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
+ TokenArrayRef pArray)
+{
+ using ::std::pair;
+ if (rData.empty() || !isDocInitialized(nFileId))
+ // nothing to cache
+ return;
+
+ // First, get the document item for the given file ID.
+ DocItem* pDocItem = getDocItem(nFileId);
+ if (!pDocItem)
+ return;
+
+ DocItem& rDoc = *pDocItem;
+
+ // Now, find the table position of the first table to cache.
+ const String& rFirstTabName = rData.front().maTableName;
+ TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find(
+ ScGlobal::pCharClass->upper(rFirstTabName));
+ if (itrTabName == rDoc.maTableNameIndex.end())
+ {
+ // table index not found.
+ return;
+ }
+
+ size_t nTabFirstId = itrTabName->second;
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+ SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
+ vector<SingleRangeData>::const_iterator itrDataBeg = rData.begin(), itrDataEnd = rData.end();
+ for (vector<SingleRangeData>::const_iterator itrData = itrDataBeg; itrData != itrDataEnd; ++itrData)
+ {
+ size_t i = nTabFirstId + ::std::distance(itrDataBeg, itrData);
+ TableTypeRef& pTabData = rDoc.maTables[i];
+ if (!pTabData.get())
+ pTabData.reset(new Table);
+
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ SCSIZE nC = nCol - nCol1, nR = nRow - nRow1;
+ TokenRef pToken;
+ const ScMatrixRef& pMat = itrData->mpRangeData;
+ if (pMat->IsValue(nC, nR))
+ pToken.reset(new formula::FormulaDoubleToken(pMat->GetDouble(nC, nR)));
+ else if (pMat->IsString(nC, nR))
+ pToken.reset(new formula::FormulaStringToken(pMat->GetString(nC, nR)));
+ else
+ pToken.reset(new ScEmptyCellToken(false, false));
+
+ pTabData->setCell(nCol, nRow, pToken);
+ }
+ }
+ }
+
+ size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab();
+ ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
+ rDoc.maRangeArrays.insert( RangeArrayMap::value_type( aCacheRange, pArray));
+}
+
+bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId)
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return false;
+
+ return pDoc->mbInitFromSource;
+}
+
+static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const String& rName, size_t& rIndex)
+{
+ ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
+ if (itr == rMap.end())
+ return false;
+
+ rIndex = itr->second;
+ return true;
+}
+
+void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<String>& rTabNames)
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return;
+
+ size_t n = rTabNames.size();
+
+ // table name list - the list must include all table names in the source
+ // document and only to be populated when loading the source document, not
+ // when loading cached data from, say, Excel XCT/CRN records.
+ vector<TableName> aNewTabNames;
+ aNewTabNames.reserve(n);
+ for (vector<String>::const_iterator itr = rTabNames.begin(), itrEnd = rTabNames.end();
+ itr != itrEnd; ++itr)
+ {
+ TableName aNameItem(ScGlobal::pCharClass->upper(*itr), *itr);
+ aNewTabNames.push_back(aNameItem);
+ }
+ pDoc->maTableNames.swap(aNewTabNames);
+
+ // data tables - preserve any existing data that may have been set during
+ // file import.
+ vector<TableTypeRef> aNewTables(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ size_t nIndex;
+ if (lcl_getTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
+ {
+ aNewTables[i] = pDoc->maTables[nIndex];
+ }
+ }
+ pDoc->maTables.swap(aNewTables);
+
+ // name index map
+ TableNameIndexMap aNewNameIndex;
+ for (size_t i = 0; i < n; ++i)
+ aNewNameIndex.insert(TableNameIndexMap::value_type(pDoc->maTableNames[i].maUpperName, i));
+ pDoc->maTableNameIndex.swap(aNewNameIndex);
+
+ pDoc->mbInitFromSource = true;
+}
+
+String ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
+{
+ if( DocItem* pDoc = getDocItem( nFileId ) )
+ if( nCacheId < pDoc->maTableNames.size() )
+ return pDoc->maTableNames[ nCacheId ].maRealName;
+ return EMPTY_STRING;
+}
+
+void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<String>& rTabNames) const
+{
+ rTabNames.clear();
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return;
+
+ size_t n = pDoc->maTableNames.size();
+ rTabNames.reserve(n);
+ for (vector<TableName>::const_iterator itr = pDoc->maTableNames.begin(), itrEnd = pDoc->maTableNames.end();
+ itr != itrEnd; ++itr)
+ rTabNames.push_back(itr->maRealName);
+}
+
+SCsTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const String& rStartTabName, const String& rEndTabName ) const
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return -1;
+
+ vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
+ vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
+
+ vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
+ TabNameSearchPredicate( rStartTabName));
+ if (itrStartTab == itrEnd)
+ return -1;
+
+ vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
+ TabNameSearchPredicate( rEndTabName));
+ if (itrEndTab == itrEnd)
+ return 0;
+
+ size_t nStartDist = ::std::distance( itrBeg, itrStartTab);
+ size_t nEndDist = ::std::distance( itrBeg, itrEndTab);
+ return nStartDist <= nEndDist ? static_cast<SCsTAB>(nEndDist - nStartDist + 1) : -static_cast<SCsTAB>(nStartDist - nEndDist + 1);
+}
+
+void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
+{
+ using ::std::sort;
+ using ::std::unique;
+
+ vector<sal_uInt32> aNumFmts;
+ for (DocDataType::const_iterator itrDoc = maDocs.begin(), itrDocEnd = maDocs.end();
+ itrDoc != itrDocEnd; ++itrDoc)
+ {
+ const vector<TableTypeRef>& rTables = itrDoc->second.maTables;
+ for (vector<TableTypeRef>::const_iterator itrTab = rTables.begin(), itrTabEnd = rTables.end();
+ itrTab != itrTabEnd; ++itrTab)
+ {
+ TableTypeRef pTab = *itrTab;
+ if (!pTab)
+ continue;
+
+ pTab->getAllNumberFormats(aNumFmts);
+ }
+ }
+
+ // remove duplicates.
+ sort(aNumFmts.begin(), aNumFmts.end());
+ aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
+ rNumFmts.swap(aNumFmts);
+}
+
+bool ScExternalRefCache::hasCacheTable(sal_uInt16 nFileId, const String& rTabName) const
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return false;
+
+ String aUpperName = ScGlobal::pCharClass->upper(rTabName);
+ vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin(), itrEnd = pDoc->maTableNames.end();
+ vector<TableName>::const_iterator itr = ::std::find_if(
+ itrBeg, itrEnd, TabNameSearchPredicate(aUpperName));
+
+ return itr != itrEnd;
+}
+
+size_t ScExternalRefCache::getCacheTableCount(sal_uInt16 nFileId) const
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ return pDoc ? pDoc->maTables.size() : 0;
+}
+
+bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId )
+{
+ DocItem* pDocItem = getDocItem(nFileId);
+ if (!pDocItem)
+ return areAllCacheTablesReferenced();
+
+ for (::std::vector<TableTypeRef>::iterator itrTab = pDocItem->maTables.begin();
+ itrTab != pDocItem->maTables.end(); ++itrTab)
+ {
+ if ((*itrTab).get())
+ (*itrTab)->setReferenced( true);
+ }
+ addCacheDocToReferenced( nFileId);
+ return areAllCacheTablesReferenced();
+}
+
+bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const String& rTabName, size_t nSheets, bool bPermanent )
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (pDoc)
+ {
+ size_t nIndex = 0;
+ String aTabNameUpper = ScGlobal::pCharClass->upper( rTabName);
+ if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex))
+ {
+ size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
+ for (size_t i = nIndex; i < nStop; ++i)
+ {
+ TableTypeRef pTab = pDoc->maTables[i];
+ if (pTab.get())
+ {
+ Table::ReferencedFlag eNewFlag = (bPermanent ?
+ Table::REFERENCED_PERMANENT :
+ Table::REFERENCED_MARKED);
+ Table::ReferencedFlag eOldFlag = pTab->getReferencedFlag();
+ if (eOldFlag != Table::REFERENCED_PERMANENT && eNewFlag != eOldFlag)
+ {
+ pTab->setReferencedFlag( eNewFlag);
+ addCacheTableToReferenced( nFileId, i);
+ }
+ }
+ }
+ }
+ }
+ return areAllCacheTablesReferenced();
+}
+
+void ScExternalRefCache::setCacheTableReferencedPermanently( sal_uInt16 nFileId, const String& rTabName, size_t nSheets )
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (pDoc)
+ {
+ size_t nIndex = 0;
+ String aTabNameUpper = ScGlobal::pCharClass->upper( rTabName);
+ if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex))
+ {
+ size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
+ for (size_t i = nIndex; i < nStop; ++i)
+ {
+ TableTypeRef pTab = pDoc->maTables[i];
+ if (pTab.get())
+ pTab->setReferencedFlag( Table::REFERENCED_PERMANENT);
+ }
+ }
+ }
+}
+
+void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced )
+{
+ if (bReferenced)
+ {
+ maReferenced.reset(0);
+ for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
+ {
+ ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
+ for (::std::vector<TableTypeRef>::iterator itrTab = rDocItem.maTables.begin();
+ itrTab != rDocItem.maTables.end(); ++itrTab)
+ {
+ if ((*itrTab).get())
+ (*itrTab)->setReferenced( true);
+ }
+ }
+ }
+ else
+ {
+ size_t nDocs = 0;
+ for (DocDataType::const_iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
+ {
+ if (nDocs <= (*itrDoc).first)
+ nDocs = (*itrDoc).first + 1;
+ }
+ maReferenced.reset( nDocs);
+
+ for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc)
+ {
+ ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second;
+ sal_uInt16 nFileId = (*itrDoc).first;
+ size_t nTables = rDocItem.maTables.size();
+ ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId];
+ // All referenced => non-existing tables evaluate as completed.
+ rDocReferenced.maTables.resize( nTables, true);
+ for (size_t i=0; i < nTables; ++i)
+ {
+ TableTypeRef & xTab = rDocItem.maTables[i];
+ if (xTab.get())
+ {
+ if (xTab->getReferencedFlag() == Table::REFERENCED_PERMANENT)
+ addCacheTableToReferenced( nFileId, i);
+ else
+ {
+ xTab->setReferencedFlag( Table::UNREFERENCED);
+ rDocReferenced.maTables[i] = false;
+ rDocReferenced.mbAllTablesReferenced = false;
+ // An addCacheTableToReferenced() actually may have
+ // resulted in mbAllReferenced been set. Clear it.
+ maReferenced.mbAllReferenced = false;
+ }
+ }
+ }
+ }
+ }
+}
+
+void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
+{
+ if (nFileId >= maReferenced.maDocs.size())
+ return;
+
+ ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
+ size_t nTables = rTables.size();
+ if (nIndex >= nTables)
+ return;
+
+ if (!rTables[nIndex])
+ {
+ rTables[nIndex] = true;
+ size_t i = 0;
+ while (i < nTables && rTables[i])
+ ++i;
+ if (i == nTables)
+ {
+ maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
+ maReferenced.checkAllDocs();
+ }
+ }
+}
+
+void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId )
+{
+ if (nFileId >= maReferenced.maDocs.size())
+ return;
+
+ if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
+ {
+ ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
+ size_t nSize = rTables.size();
+ for (size_t i=0; i < nSize; ++i)
+ rTables[i] = true;
+ maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
+ maReferenced.checkAllDocs();
+ }
+}
+
+bool ScExternalRefCache::areAllCacheTablesReferenced() const
+{
+ return maReferenced.mbAllReferenced;
+}
+
+ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
+ mbAllReferenced(false)
+{
+ reset(0);
+}
+
+ScExternalRefCache::ReferencedStatus::ReferencedStatus( size_t nDocs ) :
+ mbAllReferenced(false)
+{
+ reset( nDocs);
+}
+
+void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs )
+{
+ if (nDocs)
+ {
+ mbAllReferenced = false;
+ DocReferencedVec aRefs( nDocs);
+ maDocs.swap( aRefs);
+ }
+ else
+ {
+ mbAllReferenced = true;
+ DocReferencedVec aRefs;
+ maDocs.swap( aRefs);
+ }
+}
+
+void ScExternalRefCache::ReferencedStatus::checkAllDocs()
+{
+ for (DocReferencedVec::const_iterator itr = maDocs.begin(); itr != maDocs.end(); ++itr)
+ {
+ if (!(*itr).mbAllTablesReferenced)
+ return;
+ }
+ mbAllReferenced = true;
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc || nTabIndex >= pDoc->maTables.size())
+ return TableTypeRef();
+
+ return pDoc->maTables[nTabIndex];
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const String& rTabName, bool bCreateNew, size_t* pnIndex)
+{
+ // In API, the index is transported as cached sheet ID of type sal_Int32 in
+ // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
+ // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
+ // being 0xffffffff
+ const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
+
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ {
+ if (pnIndex) *pnIndex = nNotAvailable;
+ return TableTypeRef();
+ }
+
+ DocItem& rDoc = *pDoc;
+
+ size_t nIndex;
+ String aTabNameUpper = ScGlobal::pCharClass->upper(rTabName);
+ if (lcl_getTableDataIndex(rDoc.maTableNameIndex, aTabNameUpper, nIndex))
+ {
+ // specified table found.
+ if( pnIndex ) *pnIndex = nIndex;
+ return rDoc.maTables[nIndex];
+ }
+
+ if (!bCreateNew)
+ {
+ if (pnIndex) *pnIndex = nNotAvailable;
+ return TableTypeRef();
+ }
+
+ // Specified table doesn't exist yet. Create one.
+ nIndex = rDoc.maTables.size();
+ if( pnIndex ) *pnIndex = nIndex;
+ TableTypeRef pTab(new Table);
+ rDoc.maTables.push_back(pTab);
+ rDoc.maTableNames.push_back(TableName(aTabNameUpper, rTabName));
+ rDoc.maTableNameIndex.insert(
+ TableNameIndexMap::value_type(aTabNameUpper, nIndex));
+ return pTab;
+}
+
+void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
+{
+ maDocs.erase(nFileId);
+}
+
+ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const
+{
+ using ::std::pair;
+ DocDataType::iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ pair<DocDataType::iterator, bool> res = maDocs.insert(
+ DocDataType::value_type(nFileId, DocItem()));
+
+ if (!res.second)
+ // insertion failed.
+ return NULL;
+
+ itrDoc = res.first;
+ }
+
+ return &itrDoc->second;
+}
+
+// ============================================================================
+
+ScExternalRefLink::ScExternalRefLink(ScDocument* pDoc, sal_uInt16 nFileId, const String& rFilter) :
+ ::sfx2::SvBaseLink(::sfx2::LINKUPDATE_ONCALL, FORMAT_FILE),
+ mnFileId(nFileId),
+ maFilterName(rFilter),
+ mpDoc(pDoc),
+ mbDoRefresh(true)
+{
+}
+
+ScExternalRefLink::~ScExternalRefLink()
+{
+}
+
+void ScExternalRefLink::Closed()
+{
+ ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
+ pMgr->breakLink(mnFileId);
+}
+
+void ScExternalRefLink::DataChanged(const String& /*rMimeType*/, const Any& /*rValue*/)
+{
+ if (!mbDoRefresh)
+ return;
+
+ String aFile, aFilter;
+ mpDoc->GetLinkManager()->GetDisplayNames(this, NULL, &aFile, NULL, &aFilter);
+ ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager();
+ const String* pCurFile = pMgr->getExternalFileName(mnFileId);
+ if (!pCurFile)
+ return;
+
+ if (pCurFile->Equals(aFile))
+ {
+ // Refresh the current source document.
+ pMgr->refreshNames(mnFileId);
+ }
+ else
+ {
+ // The source document has changed.
+ pMgr->switchSrcFile(mnFileId, aFile, aFilter);
+ maFilterName = aFilter;
+ }
+}
+
+void ScExternalRefLink::Edit(Window* pParent, const Link& /*rEndEditHdl*/)
+{
+ SvBaseLink::Edit(pParent, LINK(this, ScExternalRefLink, ExternalRefEndEditHdl));
+}
+
+void ScExternalRefLink::SetDoReferesh(bool b)
+{
+ mbDoRefresh = b;
+}
+
+IMPL_LINK( ScExternalRefLink, ExternalRefEndEditHdl, ::sfx2::SvBaseLink*, EMPTYARG )
+{
+ return 0;
+}
+
+// ============================================================================
+
+static FormulaToken* lcl_convertToToken(ScBaseCell* pCell)
+{
+ if (!pCell || pCell->HasEmptyData())
+ {
+ bool bInherited = (pCell && pCell->GetCellType() == CELLTYPE_FORMULA);
+ return new ScEmptyCellToken( bInherited, false);
+ }
+
+ switch (pCell->GetCellType())
+ {
+ case CELLTYPE_EDIT:
+ {
+ String aStr;
+ static_cast<ScEditCell*>(pCell)->GetString(aStr);
+ return new formula::FormulaStringToken(aStr);
+ }
+ //break;
+ case CELLTYPE_STRING:
+ {
+ String aStr;
+ static_cast<ScStringCell*>(pCell)->GetString(aStr);
+ return new formula::FormulaStringToken(aStr);
+ }
+ //break;
+ case CELLTYPE_VALUE:
+ {
+ double fVal = static_cast<ScValueCell*>(pCell)->GetValue();
+ return new formula::FormulaDoubleToken(fVal);
+ }
+ //break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell);
+ USHORT nError = pFCell->GetErrCode();
+ if (nError)
+ return new FormulaErrorToken( nError);
+ else if (pFCell->IsValue())
+ {
+ double fVal = pFCell->GetValue();
+ return new formula::FormulaDoubleToken(fVal);
+ }
+ else
+ {
+ String aStr;
+ pFCell->GetString(aStr);
+ return new formula::FormulaStringToken(aStr);
+ }
+ }
+ //break;
+ default:
+ DBG_ERROR("attempted to convert an unknown cell type.");
+ }
+
+ return NULL;
+}
+
+static ScTokenArray* lcl_convertToTokenArray(ScDocument* pSrcDoc, const ScRange& rRange,
+ vector<ScExternalRefCache::SingleRangeData>& rCacheData)
+{
+ const ScAddress& s = rRange.aStart;
+ const ScAddress& e = rRange.aEnd;
+
+ SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
+ SCCOL nCol1 = s.Col(), nCol2 = e.Col();
+ SCROW nRow1 = s.Row(), nRow2 = e.Row();
+
+ if (nTab2 != nTab1)
+ // For now, we don't support multi-sheet ranges intentionally because
+ // we don't have a way to express them in a single token. In the
+ // future we can introduce a new stack variable type svMatrixList with
+ // a new token type that can store a 3D matrix value and convert a 3D
+ // range to it.
+ return NULL;
+
+ auto_ptr<ScTokenArray> pArray(new ScTokenArray);
+ bool bFirstTab = true;
+ vector<ScExternalRefCache::SingleRangeData>::iterator
+ itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
+ for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
+ {
+ ScMatrixRef xMat = new ScMatrix(
+ static_cast<SCSIZE>(nCol2-nCol1+1),
+ static_cast<SCSIZE>(nRow2-nRow1+1));
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ SCSIZE nC = nCol - nCol1, nR = nRow - nRow1;
+ ScBaseCell* pCell;
+ pSrcDoc->GetCell(nCol, nRow, nTab, pCell);
+ if (!pCell || pCell->HasEmptyData())
+ xMat->PutEmpty(nC, nR);
+ else
+ {
+ switch (pCell->GetCellType())
+ {
+ case CELLTYPE_EDIT:
+ {
+ String aStr;
+ static_cast<ScEditCell*>(pCell)->GetString(aStr);
+ xMat->PutString(aStr, nC, nR);
+ }
+ break;
+ case CELLTYPE_STRING:
+ {
+ String aStr;
+ static_cast<ScStringCell*>(pCell)->GetString(aStr);
+ xMat->PutString(aStr, nC, nR);
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ double fVal = static_cast<ScValueCell*>(pCell)->GetValue();
+ xMat->PutDouble(fVal, nC, nR);
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell);
+ USHORT nError = pFCell->GetErrCode();
+ if (nError)
+ xMat->PutDouble( CreateDoubleError( nError), nC, nR);
+ else if (pFCell->IsValue())
+ {
+ double fVal = pFCell->GetValue();
+ xMat->PutDouble(fVal, nC, nR);
+ }
+ else
+ {
+ String aStr;
+ pFCell->GetString(aStr);
+ xMat->PutString(aStr, nC, nR);
+ }
+ }
+ break;
+ default:
+ DBG_ERROR("attempted to convert an unknown cell type.");
+ }
+ }
+ }
+ }
+ if (!bFirstTab)
+ pArray->AddOpCode(ocSep);
+
+ ScMatrix* pMat2 = xMat;
+ ScMatrixToken aToken(pMat2);
+ pArray->AddToken(aToken);
+
+ itrCache->mpRangeData = xMat;
+
+ bFirstTab = false;
+ }
+ return pArray.release();
+}
+
+ScExternalRefManager::ScExternalRefManager(ScDocument* pDoc) :
+ mpDoc(pDoc),
+ bInReferenceMarking(false)
+{
+ maSrcDocTimer.SetTimeoutHdl( LINK(this, ScExternalRefManager, TimeOutHdl) );
+ maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL);
+}
+
+ScExternalRefManager::~ScExternalRefManager()
+{
+ clear();
+}
+
+String ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
+{
+ return maRefCache.getTableName(nFileId, nTabIndex);
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
+{
+ return maRefCache.getCacheTable(nFileId, nTabIndex);
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, const String& rTabName, bool bCreateNew, size_t* pnIndex)
+{
+ return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex);
+}
+
+// ============================================================================
+
+ScExternalRefManager::RefCells::TabItem::TabItem(SCTAB nIndex) :
+ mnIndex(nIndex)
+{
+}
+
+ScExternalRefManager::RefCells::TabItem::TabItem(const TabItem& r) :
+ mnIndex(r.mnIndex),
+ maCols(r.maCols)
+{
+}
+
+ScExternalRefManager::RefCells::RefCells()
+{
+}
+
+ScExternalRefManager::RefCells::~RefCells()
+{
+}
+
+list<ScExternalRefManager::RefCells::TabItemRef>::iterator ScExternalRefManager::RefCells::getTabPos(SCTAB nTab)
+{
+ list<TabItemRef>::iterator itr = maTables.begin(), itrEnd = maTables.end();
+ for (; itr != itrEnd; ++itr)
+ if ((*itr)->mnIndex >= nTab)
+ return itr;
+ // Not found. return the end position.
+ return itrEnd;
+}
+
+void ScExternalRefManager::RefCells::insertCell(const ScAddress& rAddr)
+{
+ SCTAB nTab = rAddr.Tab();
+ SCCOL nCol = rAddr.Col();
+ SCROW nRow = rAddr.Row();
+
+ // Search by table index.
+ list<TabItemRef>::iterator itrTab = getTabPos(nTab);
+ TabItemRef xTabRef;
+ if (itrTab == maTables.end())
+ {
+ // All previous tables come before the specificed table.
+ xTabRef.reset(new TabItem(nTab));
+ maTables.push_back(xTabRef);
+ }
+ else if ((*itrTab)->mnIndex > nTab)
+ {
+ // Insert at the current iterator position.
+ xTabRef.reset(new TabItem(nTab));
+ maTables.insert(itrTab, xTabRef);
+ }
+ else if ((*itrTab)->mnIndex == nTab)
+ {
+ // The table found.
+ xTabRef = *itrTab;
+ }
+ ColSet& rCols = xTabRef->maCols;
+
+ // Then by column index.
+ ColSet::iterator itrCol = rCols.find(nCol);
+ if (itrCol == rCols.end())
+ {
+ RowSet aRows;
+ pair<ColSet::iterator, bool> r = rCols.insert(ColSet::value_type(nCol, aRows));
+ if (!r.second)
+ // column insertion failed.
+ return;
+ itrCol = r.first;
+ }
+ RowSet& rRows = itrCol->second;
+
+ // Finally, insert the row index.
+ rRows.insert(nRow);
+}
+
+void ScExternalRefManager::RefCells::removeCell(const ScAddress& rAddr)
+{
+ SCTAB nTab = rAddr.Tab();
+ SCCOL nCol = rAddr.Col();
+ SCROW nRow = rAddr.Row();
+
+ // Search by table index.
+ list<TabItemRef>::iterator itrTab = getTabPos(nTab);
+ if (itrTab == maTables.end() || (*itrTab)->mnIndex != nTab)
+ // No such table.
+ return;
+
+ ColSet& rCols = (*itrTab)->maCols;
+
+ // Then by column index.
+ ColSet::iterator itrCol = rCols.find(nCol);
+ if (itrCol == rCols.end())
+ // No such column
+ return;
+
+ RowSet& rRows = itrCol->second;
+ rRows.erase(nRow);
+}
+
+void ScExternalRefManager::RefCells::moveTable(SCTAB nOldTab, SCTAB nNewTab, bool bCopy)
+{
+ if (nOldTab == nNewTab)
+ // Nothing to do here.
+ return;
+
+ list<TabItemRef>::iterator itrOld = getTabPos(nOldTab);
+ if (itrOld == maTables.end() || (*itrOld)->mnIndex != nOldTab)
+ // No table to move or copy.
+ return;
+
+ list<TabItemRef>::iterator itrNew = getTabPos(nNewTab);
+ if (bCopy)
+ {
+ // Simply make a duplicate of the original table, insert it at the
+ // new tab position, and increment the table index for all tables
+ // that come after that inserted table.
+
+ TabItemRef xNewTab(new TabItem(*(*itrOld)));
+ xNewTab->mnIndex = nNewTab;
+ maTables.insert(itrNew, xNewTab);
+ list<TabItemRef>::iterator itr = itrNew, itrEnd = maTables.end();
+ if (itr != itrEnd) // #i99807# check that itr is not at end already
+ for (++itr; itr != itrEnd; ++itr)
+ (*itr)->mnIndex += 1;
+ }
+ else
+ {
+ if (itrOld == itrNew)
+ {
+ // No need to move the table. Just update the table index.
+ (*itrOld)->mnIndex = nNewTab;
+ return;
+ }
+
+ if (nOldTab < nNewTab)
+ {
+ // Iterate from the old tab position to the new tab position (not
+ // inclusive of the old tab itself), and decrement their tab
+ // index by one.
+ list<TabItemRef>::iterator itr = itrOld;
+ for (++itr; itr != itrNew; ++itr)
+ (*itr)->mnIndex -= 1;
+
+ // Insert a duplicate of the original table. This does not
+ // invalidate the iterators.
+ (*itrOld)->mnIndex = nNewTab - 1;
+ if (itrNew == maTables.end())
+ maTables.push_back(*itrOld);
+ else
+ maTables.insert(itrNew, *itrOld);
+
+ // Remove the original table.
+ maTables.erase(itrOld);
+ }
+ else
+ {
+ // nNewTab < nOldTab
+
+ // Iterate from the new tab position to the one before the old tab
+ // position, and increment their tab index by one.
+ list<TabItemRef>::iterator itr = itrNew;
+ for (++itr; itr != itrOld; ++itr)
+ (*itr)->mnIndex += 1;
+
+ (*itrOld)->mnIndex = nNewTab;
+ maTables.insert(itrNew, *itrOld);
+
+ // Remove the original table.
+ maTables.erase(itrOld);
+ }
+ }
+}
+
+void ScExternalRefManager::RefCells::insertTable(SCTAB nPos)
+{
+ TabItemRef xNewTab(new TabItem(nPos));
+ list<TabItemRef>::iterator itr = getTabPos(nPos);
+ if (itr == maTables.end())
+ maTables.push_back(xNewTab);
+ else
+ maTables.insert(itr, xNewTab);
+}
+
+void ScExternalRefManager::RefCells::removeTable(SCTAB nPos)
+{
+ list<TabItemRef>::iterator itr = getTabPos(nPos);
+ if (itr == maTables.end())
+ // nothing to remove.
+ return;
+
+ maTables.erase(itr);
+}
+
+void ScExternalRefManager::RefCells::refreshAllCells(ScExternalRefManager& rRefMgr)
+{
+ // Get ALL the cell positions for re-compilation.
+ for (list<TabItemRef>::iterator itrTab = maTables.begin(), itrTabEnd = maTables.end();
+ itrTab != itrTabEnd; ++itrTab)
+ {
+ SCTAB nTab = (*itrTab)->mnIndex;
+ ColSet& rCols = (*itrTab)->maCols;
+ for (ColSet::iterator itrCol = rCols.begin(), itrColEnd = rCols.end();
+ itrCol != itrColEnd; ++itrCol)
+ {
+ SCCOL nCol = itrCol->first;
+ RowSet& rRows = itrCol->second;
+ RowSet aNewRows;
+ for (RowSet::iterator itrRow = rRows.begin(), itrRowEnd = rRows.end();
+ itrRow != itrRowEnd; ++itrRow)
+ {
+ SCROW nRow = *itrRow;
+ ScAddress aCell(nCol, nRow, nTab);
+ if (rRefMgr.compileTokensByCell(aCell))
+ // This cell still contains an external refernce.
+ aNewRows.insert(nRow);
+ }
+ // Update the rows so that cells with no external references are
+ // no longer tracked.
+ rRows.swap(aNewRows);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+ScExternalRefManager::LinkListener::LinkListener()
+{
+}
+
+ScExternalRefManager::LinkListener::~LinkListener()
+{
+}
+
+// ----------------------------------------------------------------------------
+
+void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<String>& rTabNames) const
+{
+ maRefCache.getAllTableNames(nFileId, rTabNames);
+}
+
+SCsTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const String& rStartTabName, const String& rEndTabName ) const
+{
+ return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
+}
+
+void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
+{
+ maRefCache.getAllNumberFormats(rNumFmts);
+}
+
+bool ScExternalRefManager::hasCacheTable(sal_uInt16 nFileId, const String& rTabName) const
+{
+ return maRefCache.hasCacheTable(nFileId, rTabName);
+}
+
+size_t ScExternalRefManager::getCacheTableCount(sal_uInt16 nFileId) const
+{
+ return maRefCache.getCacheTableCount(nFileId);
+}
+
+sal_uInt16 ScExternalRefManager::getExternalFileCount() const
+{
+ return static_cast< sal_uInt16 >( maSrcFiles.size() );
+}
+
+bool ScExternalRefManager::markUsedByLinkListeners()
+{
+ bool bAllMarked = false;
+ for (LinkListenerMap::const_iterator itr = maLinkListeners.begin();
+ itr != maLinkListeners.end() && !bAllMarked; ++itr)
+ {
+ if (!(*itr).second.empty())
+ bAllMarked = maRefCache.setCacheDocReferenced( (*itr).first);
+ /* TODO: LinkListeners should remember the table they're listening to.
+ * As is, listening to one table will mark all tables of the document
+ * being referenced. */
+ }
+ return bAllMarked;
+}
+
+bool ScExternalRefManager::setCacheDocReferenced( sal_uInt16 nFileId )
+{
+ return maRefCache.setCacheDocReferenced( nFileId);
+}
+
+bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const String& rTabName, size_t nSheets )
+{
+ return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, false);
+}
+
+void ScExternalRefManager::setCacheTableReferencedPermanently( sal_uInt16 nFileId, const String& rTabName, size_t nSheets )
+{
+ if (isInReferenceMarking())
+ // Do all maintenance work.
+ maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, true);
+ else
+ // Set only the permanent flag.
+ maRefCache.setCacheTableReferencedPermanently( nFileId, rTabName, nSheets);
+}
+
+void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced )
+{
+ bInReferenceMarking = !bReferenced;
+ maRefCache.setAllCacheTableReferencedStati( bReferenced );
+}
+
+void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const String& rName, const ScTokenArray& rArray)
+{
+ ScExternalRefCache::TokenArrayRef pArray(rArray.Clone());
+ maRefCache.setRangeNameTokens(nFileId, rName, pArray);
+}
+
+ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken(
+ sal_uInt16 nFileId, const String& rTabName, const ScAddress& rCell,
+ const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
+{
+ if (pCurPos)
+ insertRefCell(nFileId, *pCurPos);
+
+ maybeLinkExternalFile(nFileId);
+
+ if (pTab)
+ *pTab = -1;
+
+ if (pFmt)
+ pFmt->mbIsSet = false;
+
+ bool bLoading = mpDoc->IsImportingXML();
+
+ // Check if the given table name and the cell position is cached.
+ // #i101304# When loading a file, the saved cache (hidden sheet)
+ // is assumed to contain all data for the loaded formulas.
+ // No cache entries are created from empty cells in the saved sheet,
+ // so they have to be created here (bWriteEmpty parameter).
+ // Otherwise, later interpretation of the loaded formulas would
+ // load the source document even if the user didn't want to update.
+ sal_uInt32 nFmtIndex = 0;
+ ScExternalRefCache::TokenRef pToken = maRefCache.getCellData(
+ nFileId, rTabName, rCell.Col(), rCell.Row(), bLoading, bLoading, &nFmtIndex);
+ if (pToken)
+ {
+ if (pFmt)
+ {
+ short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex);
+ if (nFmtType != NUMBERFORMAT_UNDEFINED)
+ {
+ pFmt->mbIsSet = true;
+ pFmt->mnIndex = nFmtIndex;
+ pFmt->mnType = nFmtType;
+ }
+ }
+ return pToken;
+ }
+
+ // reference not cached. read from the source document.
+ ScDocument* pSrcDoc = getSrcDocument(nFileId);
+ if (!pSrcDoc)
+ {
+ // Source document is not reachable. Try to get data from the cache
+ // once again, but this time treat a non-cached cell as an empty cell
+ // as long as the table itself is cached.
+ pToken = maRefCache.getCellData(
+ nFileId, rTabName, rCell.Col(), rCell.Row(), true, false, &nFmtIndex);
+ return pToken;
+ }
+
+ ScBaseCell* pCell = NULL;
+ SCTAB nTab;
+ if (!pSrcDoc->GetTable(rTabName, nTab))
+ {
+ // specified table name doesn't exist in the source document.
+ return ScExternalRefCache::TokenRef();
+ }
+
+ if (pTab)
+ *pTab = nTab;
+
+ pSrcDoc->GetCell(rCell.Col(), rCell.Row(), nTab, pCell);
+ ScExternalRefCache::TokenRef pTok(lcl_convertToToken(pCell));
+
+ pSrcDoc->GetNumberFormat(rCell.Col(), rCell.Row(), nTab, nFmtIndex);
+ nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, pSrcDoc);
+ if (pFmt)
+ {
+ short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex);
+ if (nFmtType != NUMBERFORMAT_UNDEFINED)
+ {
+ pFmt->mbIsSet = true;
+ pFmt->mnIndex = nFmtIndex;
+ pFmt->mnType = nFmtType;
+ }
+ }
+
+ if (!pTok.get())
+ {
+ // Generate an error for unresolvable cells.
+ pTok.reset( new FormulaErrorToken( errNoValue));
+ }
+
+ // Now, insert the token into cache table.
+ maRefCache.setCellData(nFileId, rTabName, rCell.Row(), rCell.Col(), pTok, nFmtIndex);
+ return pTok;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens(sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
+{
+ if (pCurPos)
+ insertRefCell(nFileId, *pCurPos);
+
+ maybeLinkExternalFile(nFileId);
+
+ bool bLoading = mpDoc->IsImportingXML();
+
+ // Check if the given table name and the cell position is cached.
+ // #i101304# When loading, put empty cells into cache, see getSingleRefToken.
+ ScExternalRefCache::TokenArrayRef p = maRefCache.getCellRangeData(nFileId, rTabName, rRange, bLoading, bLoading);
+ if (p.get())
+ return p;
+
+ ScDocument* pSrcDoc = getSrcDocument(nFileId);
+ if (!pSrcDoc)
+ {
+ // Source document is not reachable. Try to get data from the cache
+ // once again, but this time treat non-cached cells as empty cells as
+ // long as the table itself is cached.
+ return maRefCache.getCellRangeData(nFileId, rTabName, rRange, true, false);
+ }
+
+ SCTAB nTab1;
+ if (!pSrcDoc->GetTable(rTabName, nTab1))
+ // specified table name doesn't exist in the source document.
+ return ScExternalRefCache::TokenArrayRef();
+
+ ScRange aRange(rRange);
+ SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
+
+ vector<ScExternalRefCache::SingleRangeData> aCacheData;
+ aCacheData.reserve(nTabSpan+1);
+ aCacheData.push_back(ScExternalRefCache::SingleRangeData());
+ aCacheData.back().maTableName = ScGlobal::pCharClass->upper(rTabName);
+
+ for (SCTAB i = 1; i < nTabSpan + 1; ++i)
+ {
+ String aTabName;
+ if (!pSrcDoc->GetName(nTab1 + 1, aTabName))
+ // source document doesn't have any table by the specified name.
+ break;
+
+ aCacheData.push_back(ScExternalRefCache::SingleRangeData());
+ aCacheData.back().maTableName = ScGlobal::pCharClass->upper(aTabName);
+ }
+
+ aRange.aStart.SetTab(nTab1);
+ aRange.aEnd.SetTab(nTab1 + nTabSpan);
+
+ ScExternalRefCache::TokenArrayRef pArray;
+ pArray.reset(lcl_convertToTokenArray(pSrcDoc, aRange, aCacheData));
+
+ if (pArray)
+ // Cache these values.
+ maRefCache.setCellRangeData(nFileId, rRange, aCacheData, pArray);
+
+ return pArray;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(sal_uInt16 nFileId, const String& rName, const ScAddress* pCurPos)
+{
+ if (pCurPos)
+ insertRefCell(nFileId, *pCurPos);
+
+ maybeLinkExternalFile(nFileId);
+
+ ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName);
+ if (pArray.get())
+ return pArray;
+
+ ScDocument* pSrcDoc = getSrcDocument(nFileId);
+ if (!pSrcDoc)
+ return ScExternalRefCache::TokenArrayRef();
+
+ ScRangeName* pExtNames = pSrcDoc->GetRangeName();
+ String aUpperName = ScGlobal::pCharClass->upper(rName);
+ USHORT n;
+ bool bRes = pExtNames->SearchNameUpper(aUpperName, n);
+ if (!bRes)
+ return ScExternalRefCache::TokenArrayRef();
+
+ ScRangeData* pRangeData = (*pExtNames)[n];
+ if (!pRangeData)
+ return ScExternalRefCache::TokenArrayRef();
+
+ // Parse all tokens in this external range data, and replace each absolute
+ // reference token with an external reference token, and cache them. Also
+ // register the source document with the link manager if it's a new
+ // source.
+
+ ScExternalRefCache::TokenArrayRef pNew(new ScTokenArray);
+
+ ScTokenArray* pCode = pRangeData->GetCode();
+ for (FormulaToken* pToken = pCode->First(); pToken; pToken = pCode->Next())
+ {
+ bool bTokenAdded = false;
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRef = static_cast<ScToken*>(pToken)->GetSingleRef();
+ String aTabName;
+ pSrcDoc->GetName(rRef.nTab, aTabName);
+ ScExternalSingleRefToken aNewToken(nFileId, aTabName, static_cast<ScToken*>(pToken)->GetSingleRef());
+ pNew->AddToken(aNewToken);
+ bTokenAdded = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScSingleRefData& rRef = static_cast<ScToken*>(pToken)->GetSingleRef();
+ String aTabName;
+ pSrcDoc->GetName(rRef.nTab, aTabName);
+ ScExternalDoubleRefToken aNewToken(nFileId, aTabName, static_cast<ScToken*>(pToken)->GetDoubleRef());
+ pNew->AddToken(aNewToken);
+ bTokenAdded = true;
+ }
+ break;
+ default:
+ ; // nothing
+ }
+
+ if (!bTokenAdded)
+ pNew->AddToken(*pToken);
+ }
+
+ // Make sure to pass the correctly-cased range name here.
+ maRefCache.setRangeNameTokens(nFileId, pRangeData->GetName(), pNew);
+ return pNew;
+}
+
+void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId)
+{
+ RefCellMap::iterator itrFile = maRefCells.find(nFileId);
+ if (itrFile == maRefCells.end())
+ return;
+
+ RefCells& rRefCells = itrFile->second;
+ rRefCells.refreshAllCells(*this);
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScTabViewShell* pVShell = pViewData->GetViewShell();
+ if (!pVShell)
+ return;
+
+ // Repainting the grid also repaints the texts, but is there a better way
+ // to refresh texts?
+ pVShell->Invalidate(FID_REPAINT);
+ pVShell->PaintGrid();
+}
+
+void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
+{
+ RefCellMap::iterator itr = maRefCells.find(nFileId);
+ if (itr == maRefCells.end())
+ {
+ RefCells aRefCells;
+ pair<RefCellMap::iterator, bool> r = maRefCells.insert(
+ RefCellMap::value_type(nFileId, aRefCells));
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.insertCell(rCell);
+}
+
+ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId)
+{
+ if (!mpDoc->IsExecuteLinkEnabled())
+ return NULL;
+
+ DocShellMap::iterator itrEnd = maDocShells.end();
+ DocShellMap::iterator itr = maDocShells.find(nFileId);
+
+ if (itr != itrEnd)
+ {
+ SfxObjectShell* p = itr->second.maShell;
+ itr->second.maLastAccess = Time();
+ return static_cast<ScDocShell*>(p)->GetDocument();
+ }
+
+ const String* pFile = getExternalFileName(nFileId);
+ if (!pFile)
+ // no file name associated with this ID.
+ return NULL;
+
+ String aFilter;
+ SrcShell aSrcDoc;
+ aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
+ if (!aSrcDoc.maShell.Is())
+ {
+ // source document could not be loaded.
+ return NULL;
+ }
+
+ if (maDocShells.empty())
+ {
+ // If this is the first source document insertion, start up the timer.
+ maSrcDocTimer.Start();
+ }
+
+ maDocShells.insert(DocShellMap::value_type(nFileId, aSrcDoc));
+ SfxObjectShell* p = aSrcDoc.maShell;
+ ScDocument* pSrcDoc = static_cast<ScDocShell*>(p)->GetDocument();
+
+ SCTAB nTabCount = pSrcDoc->GetTableCount();
+ if (!maRefCache.isDocInitialized(nFileId) && nTabCount)
+ {
+ // Populate the cache with all table names in the source document.
+ vector<String> aTabNames;
+ aTabNames.reserve(nTabCount);
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ String aName;
+ pSrcDoc->GetName(i, aName);
+ aTabNames.push_back(aName);
+ }
+ maRefCache.initializeDoc(nFileId, aTabNames);
+ }
+ return pSrcDoc;
+}
+
+SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, String& rFilter)
+{
+ const SrcFileData* pFileData = getExternalFileData(nFileId);
+ if (!pFileData)
+ return NULL;
+
+ // Always load the document by using the path created from the relative
+ // path. If the referenced document is not there, simply exit. The
+ // original file name should be used only when the relative path is not
+ // given.
+ String aFile = pFileData->maFileName;
+ maybeCreateRealFileName(nFileId);
+ if (pFileData->maRealFileName.Len())
+ aFile = pFileData->maRealFileName;
+
+ if (!isFileLoadable(aFile))
+ return NULL;
+
+ String aOptions;
+ ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
+ const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
+
+ if (!pFileData->maRelativeName.Len())
+ {
+ // Generate a relative file path.
+ INetURLObject aBaseURL(getOwnDocumentName());
+ aBaseURL.insertName(OUString::createFromAscii("content.xml"));
+
+ String aStr = URIHelper::simpleNormalizedMakeRelative(
+ aBaseURL.GetMainURL(INetURLObject::NO_DECODE), aFile);
+
+ setRelativeFileName(nFileId, aStr);
+ }
+
+ // Update the filter data now that we are loading it again.
+ setFilterData(nFileId, rFilter, aOptions);
+
+ SfxItemSet* pSet = new SfxAllItemSet(SFX_APP()->GetPool());
+ if (aOptions.Len())
+ pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
+
+ auto_ptr<SfxMedium> pMedium(new SfxMedium(aFile, STREAM_STD_READ, false, pFilter, pSet));
+ if (pMedium->GetError() != ERRCODE_NONE)
+ return NULL;
+
+ pMedium->UseInteractionHandler(false);
+
+ ScDocShell* pNewShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL);
+ SfxObjectShellRef aRef = pNewShell;
+
+ // increment the recursive link count of the source document.
+ ScExtDocOptions* pExtOpt = mpDoc->GetExtDocOptions();
+ sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0;
+ ScDocument* pSrcDoc = pNewShell->GetDocument();
+ ScExtDocOptions* pExtOptNew = pSrcDoc->GetExtDocOptions();
+ if (!pExtOptNew)
+ {
+ pExtOptNew = new ScExtDocOptions;
+ pSrcDoc->SetExtDocOptions(pExtOptNew);
+ }
+ pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
+
+ pNewShell->DoLoad(pMedium.release());
+ return aRef;
+}
+
+bool ScExternalRefManager::isFileLoadable(const String& rFile) const
+{
+ if (!rFile.Len())
+ return false;
+
+ if (isOwnDocument(rFile))
+ return false;
+
+ if (utl::UCBContentHelper::IsFolder(rFile))
+ return false;
+
+ return utl::UCBContentHelper::Exists(rFile);
+}
+
+void ScExternalRefManager::maybeLinkExternalFile(sal_uInt16 nFileId)
+{
+ if (maLinkedDocs.count(nFileId))
+ // file alerady linked, or the link has been broken.
+ return;
+
+ // Source document not linked yet. Link it now.
+ const String* pFileName = getExternalFileName(nFileId);
+ if (!pFileName)
+ return;
+
+ String aFilter, aOptions;
+ ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false);
+ SvxLinkManager* pLinkMgr = mpDoc->GetLinkManager();
+ ScExternalRefLink* pLink = new ScExternalRefLink(mpDoc, nFileId, aFilter);
+ DBG_ASSERT(pFileName, "ScExternalRefManager::insertExternalFileLink: file name pointer is NULL");
+ pLinkMgr->InsertFileLink(*pLink, OBJECT_CLIENT_FILE, *pFileName, &aFilter);
+
+ pLink->SetDoReferesh(false);
+ pLink->Update();
+ pLink->SetDoReferesh(true);
+
+ maLinkedDocs.insert(LinkedDocMap::value_type(nFileId, true));
+}
+
+void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const String& rOwnDocName)
+{
+ if (!maRelativeName.Len())
+ // No relative path given. Nothing to do.
+ return;
+
+ if (maRealFileName.Len())
+ // Real file name already created. Nothing to do.
+ return;
+
+ // Formulate the absolute file path from the relative path.
+ const String& rRelPath = maRelativeName;
+ INetURLObject aBaseURL(rOwnDocName);
+ aBaseURL.insertName(OUString::createFromAscii("content.xml"));
+ bool bWasAbs = false;
+ maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::NO_DECODE);
+}
+
+void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId)
+{
+ if (nFileId >= maSrcFiles.size())
+ return;
+
+ maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
+}
+
+bool ScExternalRefManager::compileTokensByCell(const ScAddress& rCell)
+{
+ ScBaseCell* pCell;
+ mpDoc->GetCell(rCell.Col(), rCell.Row(), rCell.Tab(), pCell);
+
+ if (!pCell || pCell->GetCellType() != CELLTYPE_FORMULA)
+ return false;
+
+ ScFormulaCell* pFC = static_cast<ScFormulaCell*>(pCell);
+
+ // Check to make sure the cell really contains ocExternalRef.
+ // External names, external cell and range references all have a
+ // ocExternalRef token.
+ const ScTokenArray* pCode = pFC->GetCode();
+ if (!pCode->HasOpCode( ocExternalRef))
+ return false;
+
+ ScTokenArray* pArray = pFC->GetCode();
+ if (pArray)
+ // Clear the error code, or a cell with error won't get re-compiled.
+ pArray->SetCodeError(0);
+
+ pFC->SetCompile(true);
+ pFC->CompileTokenArray();
+ pFC->SetDirty();
+
+ return true;
+}
+
+const String& ScExternalRefManager::getOwnDocumentName() const
+{
+ SfxObjectShell* pShell = mpDoc->GetDocumentShell();
+ if (!pShell)
+ // This should not happen!
+ return EMPTY_STRING;
+
+ SfxMedium* pMed = pShell->GetMedium();
+ if (!pMed)
+ return EMPTY_STRING;
+
+ return pMed->GetName();
+}
+
+bool ScExternalRefManager::isOwnDocument(const String& rFile) const
+{
+ return getOwnDocumentName().Equals(rFile);
+}
+
+void ScExternalRefManager::convertToAbsName(String& rFile) const
+{
+ SfxObjectShell* pDocShell = mpDoc->GetDocumentShell();
+ rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
+}
+
+sal_uInt16 ScExternalRefManager::getExternalFileId(const String& rFile)
+{
+ vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
+ vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
+ if (itr != itrEnd)
+ {
+ size_t nId = distance(itrBeg, itr);
+ return static_cast<sal_uInt16>(nId);
+ }
+
+ SrcFileData aData;
+ aData.maFileName = rFile;
+ maSrcFiles.push_back(aData);
+ return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
+}
+
+const String* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
+{
+ if (nFileId >= maSrcFiles.size())
+ return NULL;
+
+ if (bForceOriginal)
+ return &maSrcFiles[nFileId].maFileName;
+
+ maybeCreateRealFileName(nFileId);
+
+ if (maSrcFiles[nFileId].maRealFileName.Len())
+ return &maSrcFiles[nFileId].maRealFileName;
+ else
+ return &maSrcFiles[nFileId].maFileName;
+}
+
+bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
+{
+ return nFileId < maSrcFiles.size();
+}
+
+bool ScExternalRefManager::hasExternalFile(const String& rFile) const
+{
+ vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
+ vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
+ return itr != itrEnd;
+}
+
+const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const
+{
+ if (nFileId >= maSrcFiles.size())
+ return NULL;
+
+ return &maSrcFiles[nFileId];
+}
+
+const String* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const String& rTabName) const
+{
+ return maRefCache.getRealTableName(nFileId, rTabName);
+}
+
+const String* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const String& rRangeName) const
+{
+ return maRefCache.getRealRangeName(nFileId, rRangeName);
+}
+
+template<typename MapContainer>
+void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
+{
+ typename MapContainer::iterator itr = rMap.find(nFileId);
+ if (itr != rMap.end())
+ rMap.erase(itr);
+}
+
+void ScExternalRefManager::refreshNames(sal_uInt16 nFileId)
+{
+ maRefCache.clearCache(nFileId);
+ lcl_removeByFileId(nFileId, maDocShells);
+
+ if (maDocShells.empty())
+ maSrcDocTimer.Stop();
+
+ // Update all cells containing names from this source document.
+ refreshAllRefCells(nFileId);
+
+ notifyAllLinkListeners(nFileId, LINK_MODIFIED);
+}
+
+void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
+{
+ lcl_removeByFileId(nFileId, maDocShells);
+
+ if (maDocShells.empty())
+ maSrcDocTimer.Stop();
+
+ LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
+ if (itr != maLinkedDocs.end())
+ itr->second = false;
+
+ notifyAllLinkListeners(nFileId, LINK_BROKEN);
+}
+
+void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const String& rNewFile, const String& rNewFilter)
+{
+ maSrcFiles[nFileId].maFileName = rNewFile;
+ maSrcFiles[nFileId].maRelativeName.Erase();
+ maSrcFiles[nFileId].maRealFileName.Erase();
+ if (!maSrcFiles[nFileId].maFilterName.Equals(rNewFilter))
+ {
+ // Filter type has changed.
+ maSrcFiles[nFileId].maFilterName = rNewFilter;
+ maSrcFiles[nFileId].maFilterOptions.Erase();
+ }
+ refreshNames(nFileId);
+}
+
+void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const String& rRelUrl)
+{
+ if (nFileId >= maSrcFiles.size())
+ return;
+ maSrcFiles[nFileId].maRelativeName = rRelUrl;
+}
+
+void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const String& rFilterName, const String& rOptions)
+{
+ if (nFileId >= maSrcFiles.size())
+ return;
+ maSrcFiles[nFileId].maFilterName = rFilterName;
+ maSrcFiles[nFileId].maFilterOptions = rOptions;
+}
+
+void ScExternalRefManager::clear()
+{
+ DocShellMap::iterator itrEnd = maDocShells.end();
+ for (DocShellMap::iterator itr = maDocShells.begin(); itr != itrEnd; ++itr)
+ itr->second.maShell->DoClose();
+
+ maDocShells.clear();
+ maSrcDocTimer.Stop();
+}
+
+bool ScExternalRefManager::hasExternalData() const
+{
+ return !maSrcFiles.empty();
+}
+
+void ScExternalRefManager::resetSrcFileData(const String& rBaseFileUrl)
+{
+ for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
+ itr != itrEnd; ++itr)
+ {
+ // Re-generate relative file name from the absolute file name.
+ String aAbsName = itr->maRealFileName;
+ if (!aAbsName.Len())
+ aAbsName = itr->maFileName;
+
+ itr->maRelativeName = URIHelper::simpleNormalizedMakeRelative(
+ rBaseFileUrl, aAbsName);
+ }
+}
+
+void ScExternalRefManager::updateRefCell(const ScAddress& rOldPos, const ScAddress& rNewPos, bool bCopy)
+{
+ for (RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end(); itr != itrEnd; ++itr)
+ {
+ if (!bCopy)
+ itr->second.removeCell(rOldPos);
+ itr->second.insertCell(rNewPos);
+ }
+}
+
+void ScExternalRefManager::updateRefMoveTable(SCTAB nOldTab, SCTAB nNewTab, bool bCopy)
+{
+ for (RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end(); itr != itrEnd; ++itr)
+ itr->second.moveTable(nOldTab, nNewTab, bCopy);
+}
+
+void ScExternalRefManager::updateRefInsertTable(SCTAB nPos)
+{
+ for (RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end(); itr != itrEnd; ++itr)
+ itr->second.insertTable(nPos);
+}
+
+void ScExternalRefManager::updateRefDeleteTable(SCTAB nPos)
+{
+ for (RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end(); itr != itrEnd; ++itr)
+ itr->second.removeTable(nPos);
+}
+
+void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
+{
+ LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
+ if (itr == maLinkListeners.end())
+ {
+ pair<LinkListenerMap::iterator, bool> r = maLinkListeners.insert(
+ LinkListenerMap::value_type(nFileId, LinkListeners()));
+ if (!r.second)
+ {
+ DBG_ERROR("insertion of new link listener list failed");
+ return;
+ }
+
+ itr = r.first;
+ }
+
+ LinkListeners& rList = itr->second;
+ rList.insert(pListener);
+}
+
+void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
+{
+ LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
+ if (itr == maLinkListeners.end())
+ // no listeners for a specified file.
+ return;
+
+ LinkListeners& rList = itr->second;
+ rList.erase(pListener);
+
+ if (rList.empty())
+ // No more listeners for this file. Remove its entry.
+ maLinkListeners.erase(itr);
+}
+
+void ScExternalRefManager::removeLinkListener(LinkListener* pListener)
+{
+ LinkListenerMap::iterator itr = maLinkListeners.begin(), itrEnd = maLinkListeners.end();
+ for (; itr != itrEnd; ++itr)
+ itr->second.erase(pListener);
+}
+
+void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
+{
+ LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
+ if (itr == maLinkListeners.end())
+ // no listeners for a specified file.
+ return;
+
+ LinkListeners& rList = itr->second;
+ for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
+}
+
+void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut)
+{
+ DocShellMap aNewDocShells;
+ DocShellMap::iterator itr = maDocShells.begin(), itrEnd = maDocShells.end();
+ for (; itr != itrEnd; ++itr)
+ {
+ // in 100th of a second.
+ sal_Int32 nSinceLastAccess = (Time() - itr->second.maLastAccess).GetTime();
+ if (nSinceLastAccess < nTimeOut)
+ aNewDocShells.insert(*itr);
+ }
+ maDocShells.swap(aNewDocShells);
+
+ if (maDocShells.empty())
+ maSrcDocTimer.Stop();
+}
+
+sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, ScDocument* pSrcDoc)
+{
+ NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
+ if (itr == maNumFormatMap.end())
+ {
+ // Number formatter map is not initialized for this external document.
+ pair<NumFmtMap::iterator, bool> r = maNumFormatMap.insert(
+ NumFmtMap::value_type(nFileId, SvNumberFormatterMergeMap()));
+
+ if (!r.second)
+ // insertion failed.
+ return nNumFmt;
+
+ itr = r.first;
+ mpDoc->GetFormatTable()->MergeFormatter( *pSrcDoc->GetFormatTable());
+ SvNumberFormatterMergeMap aMap = mpDoc->GetFormatTable()->ConvertMergeTableToMap();
+ itr->second.swap(aMap);
+ }
+ const SvNumberFormatterMergeMap& rMap = itr->second;
+ SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
+ if (itrNumFmt != rMap.end())
+ // mapped value found.
+ return itrNumFmt->second;
+
+ return nNumFmt;
+}
+
+IMPL_LINK(ScExternalRefManager, TimeOutHdl, AutoTimer*, pTimer)
+{
+ if (pTimer == &maSrcDocTimer)
+ purgeStaleSrcDocument(SRCDOC_LIFE_SPAN);
+
+ return 0;
+}
+