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