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>
62 #include <boost/scoped_ptr.hpp>
64 using ::std::auto_ptr
;
65 using ::com::sun::star::uno::Any
;
69 using ::std::remove_if
;
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 ScToken
* p
= static_cast<ScToken
*>(pArray
->GetNextReference());
209 for (; p
; p
= static_cast<ScToken
*>(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 ScExternalRefCache::Table::ReferencedFlag
ScExternalRefCache::Table::getReferencedFlag() const
282 bool ScExternalRefCache::Table::isReferenced() const
284 return meReferenced
!= UNREFERENCED
;
287 void ScExternalRefCache::Table::setCell(SCCOL nCol
, SCROW nRow
, TokenRef pToken
, sal_uLong nFmtIndex
, bool bSetCacheRange
)
290 RowsDataType::iterator itrRow
= maRows
.find(nRow
);
291 if (itrRow
== maRows
.end())
293 // This row does not exist yet.
294 pair
<RowsDataType::iterator
, bool> res
= maRows
.insert(
295 RowsDataType::value_type(nRow
, RowDataType()));
303 // Insert this token into the specified column location. I don't need to
304 // check for existing data. Just overwrite it.
305 RowDataType
& rRow
= itrRow
->second
;
306 ScExternalRefCache::Cell aCell
;
307 aCell
.mxToken
= pToken
;
308 aCell
.mnFmtIndex
= nFmtIndex
;
309 rRow
.insert(RowDataType::value_type(nCol
, aCell
));
311 setCachedCell(nCol
, nRow
);
314 ScExternalRefCache::TokenRef
ScExternalRefCache::Table::getCell(SCCOL nCol
, SCROW nRow
, sal_uInt32
* pnFmtIndex
) const
316 RowsDataType::const_iterator itrTable
= maRows
.find(nRow
);
317 if (itrTable
== maRows
.end())
319 // this table doesn't have the specified row.
320 return getEmptyOrNullToken(nCol
, nRow
);
323 const RowDataType
& rRowData
= itrTable
->second
;
324 RowDataType::const_iterator itrRow
= rRowData
.find(nCol
);
325 if (itrRow
== rRowData
.end())
327 // this row doesn't have the specified column.
328 return getEmptyOrNullToken(nCol
, nRow
);
331 const Cell
& rCell
= itrRow
->second
;
333 *pnFmtIndex
= rCell
.mnFmtIndex
;
335 return rCell
.mxToken
;
338 bool ScExternalRefCache::Table::hasRow( SCROW nRow
) const
340 RowsDataType::const_iterator itrRow
= maRows
.find(nRow
);
341 return itrRow
!= maRows
.end();
344 void ScExternalRefCache::Table::getAllRows(vector
<SCROW
>& rRows
, SCROW nLow
, SCROW nHigh
) const
347 aRows
.reserve(maRows
.size());
348 RowsDataType::const_iterator itr
= maRows
.begin(), itrEnd
= maRows
.end();
349 for (; itr
!= itrEnd
; ++itr
)
350 if (nLow
<= itr
->first
&& itr
->first
<= nHigh
)
351 aRows
.push_back(itr
->first
);
353 // hash map is not ordered, so we need to explicitly sort it.
354 ::std::sort(aRows
.begin(), aRows
.end());
358 ::std::pair
< SCROW
, SCROW
> ScExternalRefCache::Table::getRowRange() const
360 ::std::pair
< SCROW
, SCROW
> aRange( 0, 0 );
361 if( !maRows
.empty() )
363 // iterate over entire container (hash map is not sorted by key)
364 RowsDataType::const_iterator itr
= maRows
.begin(), itrEnd
= maRows
.end();
365 aRange
.first
= itr
->first
;
366 aRange
.second
= itr
->first
+ 1;
367 while( ++itr
!= itrEnd
)
369 if( itr
->first
< aRange
.first
)
370 aRange
.first
= itr
->first
;
371 else if( itr
->first
>= aRange
.second
)
372 aRange
.second
= itr
->first
+ 1;
378 void ScExternalRefCache::Table::getAllCols(SCROW nRow
, vector
<SCCOL
>& rCols
, SCCOL nLow
, SCCOL nHigh
) const
380 RowsDataType::const_iterator itrRow
= maRows
.find(nRow
);
381 if (itrRow
== maRows
.end())
382 // this table doesn't have the specified row.
385 const RowDataType
& rRowData
= itrRow
->second
;
387 aCols
.reserve(rRowData
.size());
388 RowDataType::const_iterator itrCol
= rRowData
.begin(), itrColEnd
= rRowData
.end();
389 for (; itrCol
!= itrColEnd
; ++itrCol
)
390 if (nLow
<= itrCol
->first
&& itrCol
->first
<= nHigh
)
391 aCols
.push_back(itrCol
->first
);
393 // hash map is not ordered, so we need to explicitly sort it.
394 ::std::sort(aCols
.begin(), aCols
.end());
398 ::std::pair
< SCCOL
, SCCOL
> ScExternalRefCache::Table::getColRange( SCROW nRow
) const
400 ::std::pair
< SCCOL
, SCCOL
> aRange( 0, 0 );
402 RowsDataType::const_iterator itrRow
= maRows
.find( nRow
);
403 if (itrRow
== maRows
.end())
404 // this table doesn't have the specified row.
407 const RowDataType
& rRowData
= itrRow
->second
;
408 if( !rRowData
.empty() )
410 // iterate over entire container (hash map is not sorted by key)
411 RowDataType::const_iterator itr
= rRowData
.begin(), itrEnd
= rRowData
.end();
412 aRange
.first
= itr
->first
;
413 aRange
.second
= itr
->first
+ 1;
414 while( ++itr
!= itrEnd
)
416 if( itr
->first
< aRange
.first
)
417 aRange
.first
= itr
->first
;
418 else if( itr
->first
>= aRange
.second
)
419 aRange
.second
= itr
->first
+ 1;
425 void ScExternalRefCache::Table::getAllNumberFormats(vector
<sal_uInt32
>& rNumFmts
) const
427 RowsDataType::const_iterator itrRow
= maRows
.begin(), itrRowEnd
= maRows
.end();
428 for (; itrRow
!= itrRowEnd
; ++itrRow
)
430 const RowDataType
& rRowData
= itrRow
->second
;
431 RowDataType::const_iterator itrCol
= rRowData
.begin(), itrColEnd
= rRowData
.end();
432 for (; itrCol
!= itrColEnd
; ++itrCol
)
434 const Cell
& rCell
= itrCol
->second
;
435 rNumFmts
.push_back(rCell
.mnFmtIndex
);
440 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) const
442 return maCachedRanges
.In(ScRange(nCol1
, nRow1
, 0, nCol2
, nRow2
, 0));
445 void ScExternalRefCache::Table::setCachedCell(SCCOL nCol
, SCROW nRow
)
447 setCachedCellRange(nCol
, nRow
, nCol
, nRow
);
450 void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
452 ScRange
aRange(nCol1
, nRow1
, 0, nCol2
, nRow2
, 0);
453 if ( maCachedRanges
.empty() )
454 maCachedRanges
.Append(aRange
);
456 maCachedRanges
.Join(aRange
);
459 void ScExternalRefCache::Table::setWholeTableCached()
461 setCachedCellRange(0, 0, MAXCOL
, MAXROW
);
464 bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol
, SCROW nRow
) const
466 return maCachedRanges
.In(ScRange(nCol
, nRow
, 0, nCol
, nRow
, 0));
469 ScExternalRefCache::TokenRef
ScExternalRefCache::Table::getEmptyOrNullToken(
470 SCCOL nCol
, SCROW nRow
) const
472 if (isInCachedRanges(nCol
, nRow
))
474 TokenRef
p(new ScEmptyCellToken(false, false));
480 ScExternalRefCache::TableName::TableName(const OUString
& rUpper
, const OUString
& rReal
) :
481 maUpperName(rUpper
), maRealName(rReal
)
485 ScExternalRefCache::CellFormat::CellFormat() :
486 mbIsSet(false), mnType(NUMBERFORMAT_ALL
), mnIndex(0)
490 ScExternalRefCache::ScExternalRefCache() {}
492 ScExternalRefCache::~ScExternalRefCache() {}
494 const OUString
* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId
, const OUString
& rTabName
) const
496 osl::MutexGuard
aGuard(&maMtxDocs
);
498 DocDataType::const_iterator itrDoc
= maDocs
.find(nFileId
);
499 if (itrDoc
== maDocs
.end())
501 // specified document is not cached.
505 const DocItem
& rDoc
= itrDoc
->second
;
506 TableNameIndexMap::const_iterator itrTabId
= rDoc
.maTableNameIndex
.find(
507 ScGlobal::pCharClass
->uppercase(rTabName
));
508 if (itrTabId
== rDoc
.maTableNameIndex
.end())
510 // the specified table is not in cache.
514 return &rDoc
.maTableNames
[itrTabId
->second
].maRealName
;
517 const OUString
* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId
, const OUString
& rRangeName
) const
519 osl::MutexGuard
aGuard(&maMtxDocs
);
521 DocDataType::const_iterator itrDoc
= maDocs
.find(nFileId
);
522 if (itrDoc
== maDocs
.end())
524 // specified document is not cached.
528 const DocItem
& rDoc
= itrDoc
->second
;
529 NamePairMap::const_iterator itr
= rDoc
.maRealRangeNameMap
.find(
530 ScGlobal::pCharClass
->uppercase(rRangeName
));
531 if (itr
== rDoc
.maRealRangeNameMap
.end())
532 // range name not found.
538 ScExternalRefCache::TokenRef
ScExternalRefCache::getCellData(
539 sal_uInt16 nFileId
, const OUString
& rTabName
, SCCOL nCol
, SCROW nRow
, sal_uInt32
* pnFmtIndex
)
541 osl::MutexGuard
aGuard(&maMtxDocs
);
543 DocDataType::const_iterator itrDoc
= maDocs
.find(nFileId
);
544 if (itrDoc
== maDocs
.end())
546 // specified document is not cached.
550 const DocItem
& rDoc
= itrDoc
->second
;
551 TableNameIndexMap::const_iterator itrTabId
= rDoc
.maTableNameIndex
.find(
552 ScGlobal::pCharClass
->uppercase(rTabName
));
553 if (itrTabId
== rDoc
.maTableNameIndex
.end())
555 // the specified table is not in cache.
559 const TableTypeRef
& pTableData
= rDoc
.maTables
[itrTabId
->second
];
560 if (!pTableData
.get())
562 // the table data is not instantiated yet.
566 return pTableData
->getCell(nCol
, nRow
, pnFmtIndex
);
569 ScExternalRefCache::TokenArrayRef
ScExternalRefCache::getCellRangeData(
570 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScRange
& rRange
)
572 osl::MutexGuard
aGuard(&maMtxDocs
);
574 DocDataType::iterator itrDoc
= maDocs
.find(nFileId
);
575 if (itrDoc
== maDocs
.end())
576 // specified document is not cached.
577 return TokenArrayRef();
579 DocItem
& rDoc
= itrDoc
->second
;
581 TableNameIndexMap::iterator itrTabId
= rDoc
.maTableNameIndex
.find(
582 ScGlobal::pCharClass
->uppercase(rTabName
));
583 if (itrTabId
== rDoc
.maTableNameIndex
.end())
584 // the specified table is not in cache.
585 return TokenArrayRef();
587 const ScAddress
& s
= rRange
.aStart
;
588 const ScAddress
& e
= rRange
.aEnd
;
590 SCTAB nTab1
= s
.Tab(), nTab2
= e
.Tab();
591 SCCOL nCol1
= s
.Col(), nCol2
= e
.Col();
592 SCROW nRow1
= s
.Row(), nRow2
= e
.Row();
594 // Make sure I have all the tables cached.
595 size_t nTabFirstId
= itrTabId
->second
;
596 size_t nTabLastId
= nTabFirstId
+ nTab2
- nTab1
;
597 if (nTabLastId
>= rDoc
.maTables
.size())
598 // not all tables are cached.
599 return TokenArrayRef();
601 ScRange
aCacheRange( nCol1
, nRow1
, static_cast<SCTAB
>(nTabFirstId
), nCol2
, nRow2
, static_cast<SCTAB
>(nTabLastId
));
603 RangeArrayMap::const_iterator itrRange
= rDoc
.maRangeArrays
.find( aCacheRange
);
604 if (itrRange
!= rDoc
.maRangeArrays
.end())
606 return itrRange
->second
;
608 ::boost::scoped_ptr
<ScRange
> pNewRange
;
609 TokenArrayRef pArray
;
610 bool bFirstTab
= true;
611 for (size_t nTab
= nTabFirstId
; nTab
<= nTabLastId
; ++nTab
)
613 TableTypeRef pTab
= rDoc
.maTables
[nTab
];
615 return TokenArrayRef();
617 SCCOL nDataCol1
= nCol1
, nDataCol2
= nCol2
;
618 SCROW nDataRow1
= nRow1
, nDataRow2
= nRow2
;
620 if (!pTab
->isRangeCached(nDataCol1
, nDataRow1
, nDataCol2
, nDataRow2
))
622 // specified range is not entirely within cached ranges.
623 return TokenArrayRef();
626 ScMatrixRef xMat
= new ScMatrix(
627 static_cast<SCSIZE
>(nDataCol2
-nDataCol1
+1), static_cast<SCSIZE
>(nDataRow2
-nDataRow1
+1));
629 // Only fill non-empty cells, for better performance.
631 pTab
->getAllRows(aRows
, nDataRow1
, nDataRow2
);
632 for (vector
<SCROW
>::const_iterator itr
= aRows
.begin(), itrEnd
= aRows
.end(); itr
!= itrEnd
; ++itr
)
636 pTab
->getAllCols(nRow
, aCols
, nDataCol1
, nDataCol2
);
637 for (vector
<SCCOL
>::const_iterator itrCol
= aCols
.begin(), itrColEnd
= aCols
.end(); itrCol
!= itrColEnd
; ++itrCol
)
639 SCCOL nCol
= *itrCol
;
640 TokenRef pToken
= pTab
->getCell(nCol
, nRow
);
642 // This should never happen!
643 return TokenArrayRef();
645 SCSIZE nC
= nCol
- nDataCol1
, nR
= nRow
- nDataRow1
;
646 switch (pToken
->GetType())
649 xMat
->PutDouble(pToken
->GetDouble(), nC
, nR
);
652 xMat
->PutString(pToken
->GetString(), nC
, nR
);
661 pArray
->AddOpCode(ocSep
);
663 ScMatrixToken
aToken(xMat
);
665 pArray
.reset(new ScTokenArray
);
666 pArray
->AddToken(aToken
);
671 pNewRange
.reset(new ScRange(nDataCol1
, nDataRow1
, 0, nDataCol2
, nDataRow2
, 0));
673 pNewRange
->ExtendTo(ScRange(nDataCol1
, nDataRow1
, 0, nDataCol2
, nDataRow2
, 0));
677 rDoc
.maRangeArrays
.insert( RangeArrayMap::value_type(*pNewRange
, pArray
));
681 ScExternalRefCache::TokenArrayRef
ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId
, const OUString
& rName
)
683 osl::MutexGuard
aGuard(&maMtxDocs
);
685 DocItem
* pDoc
= getDocItem(nFileId
);
687 return TokenArrayRef();
689 RangeNameMap
& rMap
= pDoc
->maRangeNames
;
690 RangeNameMap::const_iterator itr
= rMap
.find(
691 ScGlobal::pCharClass
->uppercase(rName
));
692 if (itr
== rMap
.end())
693 return TokenArrayRef();
698 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId
, const OUString
& rName
, TokenArrayRef pArray
)
700 osl::MutexGuard
aGuard(&maMtxDocs
);
702 DocItem
* pDoc
= getDocItem(nFileId
);
706 OUString aUpperName
= ScGlobal::pCharClass
->uppercase(rName
);
707 RangeNameMap
& rMap
= pDoc
->maRangeNames
;
708 rMap
.insert(RangeNameMap::value_type(aUpperName
, pArray
));
709 pDoc
->maRealRangeNameMap
.insert(NamePairMap::value_type(aUpperName
, rName
));
712 bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId
, const OUString
& rName
) const
714 osl::MutexGuard
aGuard(&maMtxDocs
);
716 DocItem
* pDoc
= getDocItem(nFileId
);
720 const RangeNameMap
& rMap
= pDoc
->maRangeNames
;
721 return rMap
.count(rName
) > 0;
724 void ScExternalRefCache::setCellData(sal_uInt16 nFileId
, const OUString
& rTabName
, SCCOL nCol
, SCROW nRow
,
725 TokenRef pToken
, sal_uLong nFmtIndex
)
727 if (!isDocInitialized(nFileId
))
731 DocItem
* pDocItem
= getDocItem(nFileId
);
735 DocItem
& rDoc
= *pDocItem
;
737 // See if the table by this name already exists.
738 TableNameIndexMap::iterator itrTabName
= rDoc
.maTableNameIndex
.find(
739 ScGlobal::pCharClass
->uppercase(rTabName
));
740 if (itrTabName
== rDoc
.maTableNameIndex
.end())
741 // Table not found. Maybe the table name or the file id is wrong ???
744 TableTypeRef
& pTableData
= rDoc
.maTables
[itrTabName
->second
];
745 if (!pTableData
.get())
746 pTableData
.reset(new Table
);
748 pTableData
->setCell(nCol
, nRow
, pToken
, nFmtIndex
);
749 pTableData
->setCachedCell(nCol
, nRow
);
752 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId
, const ScRange
& rRange
, const vector
<SingleRangeData
>& rData
,
753 const TokenArrayRef
& pArray
)
756 if (rData
.empty() || !isDocInitialized(nFileId
))
760 // First, get the document item for the given file ID.
761 DocItem
* pDocItem
= getDocItem(nFileId
);
765 DocItem
& rDoc
= *pDocItem
;
767 // Now, find the table position of the first table to cache.
768 const OUString
& rFirstTabName
= rData
.front().maTableName
;
769 TableNameIndexMap::iterator itrTabName
= rDoc
.maTableNameIndex
.find(
770 ScGlobal::pCharClass
->uppercase(rFirstTabName
));
771 if (itrTabName
== rDoc
.maTableNameIndex
.end())
773 // table index not found.
777 size_t nTabFirstId
= itrTabName
->second
;
778 SCROW nRow1
= rRange
.aStart
.Row(), nRow2
= rRange
.aEnd
.Row();
779 SCCOL nCol1
= rRange
.aStart
.Col(), nCol2
= rRange
.aEnd
.Col();
780 vector
<SingleRangeData
>::const_iterator itrDataBeg
= rData
.begin(), itrDataEnd
= rData
.end();
781 for (vector
<SingleRangeData
>::const_iterator itrData
= itrDataBeg
; itrData
!= itrDataEnd
; ++itrData
)
783 size_t i
= nTabFirstId
+ ::std::distance(itrDataBeg
, itrData
);
784 TableTypeRef
& pTabData
= rDoc
.maTables
[i
];
786 pTabData
.reset(new Table
);
788 const ScMatrixRef
& pMat
= itrData
->mpRangeData
;
789 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
791 const SCSIZE nR
= nRow
- nRow1
;
792 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
794 const SCSIZE nC
= nCol
- nCol1
;
796 ScMatrixValue value
= pMat
->Get(nC
, nR
);
800 switch (value
.nType
) {
801 case SC_MATVAL_VALUE
:
802 case SC_MATVAL_BOOLEAN
:
803 pToken
.reset(new formula::FormulaDoubleToken(value
.fVal
));
805 case SC_MATVAL_STRING
:
806 pToken
.reset(new formula::FormulaStringToken(value
.aStr
));
809 // Don't cache empty cells.
814 // Don't mark this cell 'cached' here, for better performance.
815 pTabData
->setCell(nCol
, nRow
, pToken
, 0, false);
818 // Mark the whole range 'cached'.
819 pTabData
->setCachedCellRange(nCol1
, nRow1
, nCol2
, nRow2
);
822 size_t nTabLastId
= nTabFirstId
+ rRange
.aEnd
.Tab() - rRange
.aStart
.Tab();
823 ScRange
aCacheRange( nCol1
, nRow1
, static_cast<SCTAB
>(nTabFirstId
), nCol2
, nRow2
, static_cast<SCTAB
>(nTabLastId
));
825 rDoc
.maRangeArrays
.insert( RangeArrayMap::value_type( aCacheRange
, pArray
));
828 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId
)
830 DocItem
* pDoc
= getDocItem(nFileId
);
834 return pDoc
->mbInitFromSource
;
837 static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap
& rMap
, const OUString
& rName
, size_t& rIndex
)
839 ScExternalRefCache::TableNameIndexMap::const_iterator itr
= rMap
.find(rName
);
840 if (itr
== rMap
.end())
843 rIndex
= itr
->second
;
847 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId
, const vector
<OUString
>& rTabNames
)
849 DocItem
* pDoc
= getDocItem(nFileId
);
853 size_t n
= rTabNames
.size();
855 // table name list - the list must include all table names in the source
856 // document and only to be populated when loading the source document, not
857 // when loading cached data from, say, Excel XCT/CRN records.
858 vector
<TableName
> aNewTabNames
;
859 aNewTabNames
.reserve(n
);
860 for (vector
<OUString
>::const_iterator itr
= rTabNames
.begin(), itrEnd
= rTabNames
.end();
861 itr
!= itrEnd
; ++itr
)
863 TableName
aNameItem(ScGlobal::pCharClass
->uppercase(*itr
), *itr
);
864 aNewTabNames
.push_back(aNameItem
);
866 pDoc
->maTableNames
.swap(aNewTabNames
);
868 // data tables - preserve any existing data that may have been set during
870 vector
<TableTypeRef
> aNewTables(n
);
871 for (size_t i
= 0; i
< n
; ++i
)
874 if (lcl_getTableDataIndex(pDoc
->maTableNameIndex
, pDoc
->maTableNames
[i
].maUpperName
, nIndex
))
876 aNewTables
[i
] = pDoc
->maTables
[nIndex
];
879 pDoc
->maTables
.swap(aNewTables
);
882 TableNameIndexMap aNewNameIndex
;
883 for (size_t i
= 0; i
< n
; ++i
)
884 aNewNameIndex
.insert(TableNameIndexMap::value_type(pDoc
->maTableNames
[i
].maUpperName
, i
));
885 pDoc
->maTableNameIndex
.swap(aNewNameIndex
);
887 pDoc
->mbInitFromSource
= true;
890 OUString
ScExternalRefCache::getTableName(sal_uInt16 nFileId
, size_t nCacheId
) const
892 if( DocItem
* pDoc
= getDocItem( nFileId
) )
893 if( nCacheId
< pDoc
->maTableNames
.size() )
894 return pDoc
->maTableNames
[ nCacheId
].maRealName
;
895 return EMPTY_OUSTRING
;
898 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId
, vector
<OUString
>& rTabNames
) const
901 DocItem
* pDoc
= getDocItem(nFileId
);
905 size_t n
= pDoc
->maTableNames
.size();
906 rTabNames
.reserve(n
);
907 for (vector
<TableName
>::const_iterator itr
= pDoc
->maTableNames
.begin(), itrEnd
= pDoc
->maTableNames
.end();
908 itr
!= itrEnd
; ++itr
)
909 rTabNames
.push_back(itr
->maRealName
);
912 SCsTAB
ScExternalRefCache::getTabSpan( sal_uInt16 nFileId
, const OUString
& rStartTabName
, const OUString
& rEndTabName
) const
914 DocItem
* pDoc
= getDocItem(nFileId
);
918 vector
<TableName
>::const_iterator itrBeg
= pDoc
->maTableNames
.begin();
919 vector
<TableName
>::const_iterator itrEnd
= pDoc
->maTableNames
.end();
921 vector
<TableName
>::const_iterator itrStartTab
= ::std::find_if( itrBeg
, itrEnd
,
922 TabNameSearchPredicate( rStartTabName
));
923 if (itrStartTab
== itrEnd
)
926 vector
<TableName
>::const_iterator itrEndTab
= ::std::find_if( itrBeg
, itrEnd
,
927 TabNameSearchPredicate( rEndTabName
));
928 if (itrEndTab
== itrEnd
)
931 size_t nStartDist
= ::std::distance( itrBeg
, itrStartTab
);
932 size_t nEndDist
= ::std::distance( itrBeg
, itrEndTab
);
933 return nStartDist
<= nEndDist
? static_cast<SCsTAB
>(nEndDist
- nStartDist
+ 1) : -static_cast<SCsTAB
>(nStartDist
- nEndDist
+ 1);
936 void ScExternalRefCache::getAllNumberFormats(vector
<sal_uInt32
>& rNumFmts
) const
938 osl::MutexGuard
aGuard(&maMtxDocs
);
943 vector
<sal_uInt32
> aNumFmts
;
944 for (DocDataType::const_iterator itrDoc
= maDocs
.begin(), itrDocEnd
= maDocs
.end();
945 itrDoc
!= itrDocEnd
; ++itrDoc
)
947 const vector
<TableTypeRef
>& rTables
= itrDoc
->second
.maTables
;
948 for (vector
<TableTypeRef
>::const_iterator itrTab
= rTables
.begin(), itrTabEnd
= rTables
.end();
949 itrTab
!= itrTabEnd
; ++itrTab
)
951 TableTypeRef pTab
= *itrTab
;
955 pTab
->getAllNumberFormats(aNumFmts
);
959 // remove duplicates.
960 sort(aNumFmts
.begin(), aNumFmts
.end());
961 aNumFmts
.erase(unique(aNumFmts
.begin(), aNumFmts
.end()), aNumFmts
.end());
962 rNumFmts
.swap(aNumFmts
);
965 bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId
)
967 DocItem
* pDocItem
= getDocItem(nFileId
);
969 return areAllCacheTablesReferenced();
971 for (::std::vector
<TableTypeRef
>::iterator itrTab
= pDocItem
->maTables
.begin();
972 itrTab
!= pDocItem
->maTables
.end(); ++itrTab
)
975 (*itrTab
)->setReferenced( true);
977 addCacheDocToReferenced( nFileId
);
978 return areAllCacheTablesReferenced();
981 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId
, const OUString
& rTabName
, size_t nSheets
, bool bPermanent
)
983 DocItem
* pDoc
= getDocItem(nFileId
);
987 OUString aTabNameUpper
= ScGlobal::pCharClass
->uppercase( rTabName
);
988 if (lcl_getTableDataIndex( pDoc
->maTableNameIndex
, aTabNameUpper
, nIndex
))
990 size_t nStop
= ::std::min( nIndex
+ nSheets
, pDoc
->maTables
.size());
991 for (size_t i
= nIndex
; i
< nStop
; ++i
)
993 TableTypeRef pTab
= pDoc
->maTables
[i
];
996 Table::ReferencedFlag eNewFlag
= (bPermanent
?
997 Table::REFERENCED_PERMANENT
:
998 Table::REFERENCED_MARKED
);
999 Table::ReferencedFlag eOldFlag
= pTab
->getReferencedFlag();
1000 if (eOldFlag
!= Table::REFERENCED_PERMANENT
&& eNewFlag
!= eOldFlag
)
1002 pTab
->setReferencedFlag( eNewFlag
);
1003 addCacheTableToReferenced( nFileId
, i
);
1009 return areAllCacheTablesReferenced();
1012 void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced
)
1014 osl::MutexGuard
aGuard(&maMtxDocs
);
1018 maReferenced
.reset(0);
1019 for (DocDataType::iterator itrDoc
= maDocs
.begin(); itrDoc
!= maDocs
.end(); ++itrDoc
)
1021 ScExternalRefCache::DocItem
& rDocItem
= (*itrDoc
).second
;
1022 for (::std::vector
<TableTypeRef
>::iterator itrTab
= rDocItem
.maTables
.begin();
1023 itrTab
!= rDocItem
.maTables
.end(); ++itrTab
)
1025 if ((*itrTab
).get())
1026 (*itrTab
)->setReferenced( true);
1033 for (DocDataType::const_iterator itrDoc
= maDocs
.begin(); itrDoc
!= maDocs
.end(); ++itrDoc
)
1035 if (nDocs
<= (*itrDoc
).first
)
1036 nDocs
= (*itrDoc
).first
+ 1;
1038 maReferenced
.reset( nDocs
);
1040 for (DocDataType::iterator itrDoc
= maDocs
.begin(); itrDoc
!= maDocs
.end(); ++itrDoc
)
1042 ScExternalRefCache::DocItem
& rDocItem
= (*itrDoc
).second
;
1043 sal_uInt16 nFileId
= (*itrDoc
).first
;
1044 size_t nTables
= rDocItem
.maTables
.size();
1045 ReferencedStatus::DocReferenced
& rDocReferenced
= maReferenced
.maDocs
[nFileId
];
1046 // All referenced => non-existing tables evaluate as completed.
1047 rDocReferenced
.maTables
.resize( nTables
, true);
1048 for (size_t i
=0; i
< nTables
; ++i
)
1050 TableTypeRef
& xTab
= rDocItem
.maTables
[i
];
1053 if (xTab
->getReferencedFlag() == Table::REFERENCED_PERMANENT
)
1054 addCacheTableToReferenced( nFileId
, i
);
1057 xTab
->setReferencedFlag( Table::UNREFERENCED
);
1058 rDocReferenced
.maTables
[i
] = false;
1059 rDocReferenced
.mbAllTablesReferenced
= false;
1060 // An addCacheTableToReferenced() actually may have
1061 // resulted in mbAllReferenced been set. Clear it.
1062 maReferenced
.mbAllReferenced
= false;
1070 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId
, size_t nIndex
)
1072 if (nFileId
>= maReferenced
.maDocs
.size())
1075 ::std::vector
<bool> & rTables
= maReferenced
.maDocs
[nFileId
].maTables
;
1076 size_t nTables
= rTables
.size();
1077 if (nIndex
>= nTables
)
1080 if (!rTables
[nIndex
])
1082 rTables
[nIndex
] = true;
1084 while (i
< nTables
&& rTables
[i
])
1088 maReferenced
.maDocs
[nFileId
].mbAllTablesReferenced
= true;
1089 maReferenced
.checkAllDocs();
1094 void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId
)
1096 if (nFileId
>= maReferenced
.maDocs
.size())
1099 if (!maReferenced
.maDocs
[nFileId
].mbAllTablesReferenced
)
1101 ::std::vector
<bool> & rTables
= maReferenced
.maDocs
[nFileId
].maTables
;
1102 size_t nSize
= rTables
.size();
1103 for (size_t i
=0; i
< nSize
; ++i
)
1105 maReferenced
.maDocs
[nFileId
].mbAllTablesReferenced
= true;
1106 maReferenced
.checkAllDocs();
1110 bool ScExternalRefCache::areAllCacheTablesReferenced() const
1112 return maReferenced
.mbAllReferenced
;
1115 void ScExternalRefCache::getAllCachedDataSpans( sal_uInt16 nFileId
, sc::ColumnSpanSet
& rSet
) const
1117 const DocItem
* pDocItem
= getDocItem(nFileId
);
1119 // This document is not cached.
1122 const std::vector
<TableTypeRef
>& rTables
= pDocItem
->maTables
;
1123 for (size_t nTab
= 0, nTabCount
= rTables
.size(); nTab
< nTabCount
; ++nTab
)
1125 TableTypeRef pTab
= rTables
[nTab
];
1129 std::vector
<SCROW
> aRows
;
1130 pTab
->getAllRows(aRows
);
1131 std::vector
<SCROW
>::const_iterator itRow
= aRows
.begin(), itRowEnd
= aRows
.end();
1132 for (; itRow
!= itRowEnd
; ++itRow
)
1134 SCROW nRow
= *itRow
;
1135 std::vector
<SCCOL
> aCols
;
1136 pTab
->getAllCols(nRow
, aCols
);
1137 std::vector
<SCCOL
>::const_iterator itCol
= aCols
.begin(), itColEnd
= aCols
.end();
1138 for (; itCol
!= itColEnd
; ++itCol
)
1140 SCCOL nCol
= *itCol
;
1141 rSet
.set(nTab
, nCol
, nRow
, true);
1147 ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
1148 mbAllReferenced(false)
1153 void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs
)
1157 mbAllReferenced
= false;
1158 DocReferencedVec
aRefs( nDocs
);
1159 maDocs
.swap( aRefs
);
1163 mbAllReferenced
= true;
1164 DocReferencedVec aRefs
;
1165 maDocs
.swap( aRefs
);
1169 void ScExternalRefCache::ReferencedStatus::checkAllDocs()
1171 for (DocReferencedVec::const_iterator itr
= maDocs
.begin(); itr
!= maDocs
.end(); ++itr
)
1173 if (!(*itr
).mbAllTablesReferenced
)
1176 mbAllReferenced
= true;
1179 ScExternalRefCache::TableTypeRef
ScExternalRefCache::getCacheTable(sal_uInt16 nFileId
, size_t nTabIndex
) const
1181 DocItem
* pDoc
= getDocItem(nFileId
);
1182 if (!pDoc
|| nTabIndex
>= pDoc
->maTables
.size())
1183 return TableTypeRef();
1185 return pDoc
->maTables
[nTabIndex
];
1188 ScExternalRefCache::TableTypeRef
ScExternalRefCache::getCacheTable(sal_uInt16 nFileId
, const OUString
& rTabName
, bool bCreateNew
, size_t* pnIndex
)
1190 // In API, the index is transported as cached sheet ID of type sal_Int32 in
1191 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
1192 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
1194 const size_t nNotAvailable
= static_cast<size_t>( static_cast<sal_Int32
>( -1));
1196 DocItem
* pDoc
= getDocItem(nFileId
);
1199 if (pnIndex
) *pnIndex
= nNotAvailable
;
1200 return TableTypeRef();
1203 DocItem
& rDoc
= *pDoc
;
1206 OUString aTabNameUpper
= ScGlobal::pCharClass
->uppercase(rTabName
);
1207 if (lcl_getTableDataIndex(rDoc
.maTableNameIndex
, aTabNameUpper
, nIndex
))
1209 // specified table found.
1210 if( pnIndex
) *pnIndex
= nIndex
;
1211 if (bCreateNew
&& !rDoc
.maTables
[nIndex
])
1212 rDoc
.maTables
[nIndex
].reset(new Table
);
1214 return rDoc
.maTables
[nIndex
];
1219 if (pnIndex
) *pnIndex
= nNotAvailable
;
1220 return TableTypeRef();
1223 // Specified table doesn't exist yet. Create one.
1224 nIndex
= rDoc
.maTables
.size();
1225 if( pnIndex
) *pnIndex
= nIndex
;
1226 TableTypeRef
pTab(new Table
);
1227 rDoc
.maTables
.push_back(pTab
);
1228 rDoc
.maTableNames
.push_back(TableName(aTabNameUpper
, rTabName
));
1229 rDoc
.maTableNameIndex
.insert(
1230 TableNameIndexMap::value_type(aTabNameUpper
, nIndex
));
1234 void ScExternalRefCache::clearCache(sal_uInt16 nFileId
)
1236 osl::MutexGuard
aGuard(&maMtxDocs
);
1237 maDocs
.erase(nFileId
);
1240 void ScExternalRefCache::clearCacheTables(sal_uInt16 nFileId
)
1242 osl::MutexGuard
aGuard(&maMtxDocs
);
1243 DocItem
* pDocItem
= getDocItem(nFileId
);
1245 // This document is not cached at all.
1248 // Clear all cache table content, but keep the tables.
1249 std::vector
<TableTypeRef
>& rTabs
= pDocItem
->maTables
;
1250 for (size_t i
= 0, n
= rTabs
.size(); i
< n
; ++i
)
1252 TableTypeRef pTab
= rTabs
[i
];
1259 // Clear the external range name caches.
1260 pDocItem
->maRangeNames
.clear();
1261 pDocItem
->maRangeArrays
.clear();
1262 pDocItem
->maRealRangeNameMap
.clear();
1265 ScExternalRefCache::DocItem
* ScExternalRefCache::getDocItem(sal_uInt16 nFileId
) const
1267 osl::MutexGuard
aGuard(&maMtxDocs
);
1270 DocDataType::iterator itrDoc
= maDocs
.find(nFileId
);
1271 if (itrDoc
== maDocs
.end())
1273 // specified document is not cached.
1274 pair
<DocDataType::iterator
, bool> res
= maDocs
.insert(
1275 DocDataType::value_type(nFileId
, DocItem()));
1278 // insertion failed.
1284 return &itrDoc
->second
;
1287 ScExternalRefLink::ScExternalRefLink(ScDocument
* pDoc
, sal_uInt16 nFileId
, const OUString
& rFilter
) :
1288 ::sfx2::SvBaseLink(::sfx2::LINKUPDATE_ONCALL
, FORMAT_FILE
),
1290 maFilterName(rFilter
),
1296 ScExternalRefLink::~ScExternalRefLink()
1300 void ScExternalRefLink::Closed()
1302 ScExternalRefManager
* pMgr
= mpDoc
->GetExternalRefManager();
1303 pMgr
->breakLink(mnFileId
);
1306 ::sfx2::SvBaseLink::UpdateResult
ScExternalRefLink::DataChanged(const OUString
& /*rMimeType*/, const Any
& /*rValue*/)
1311 OUString aFile
, aFilter
;
1312 mpDoc
->GetLinkManager()->GetDisplayNames(this, NULL
, &aFile
, NULL
, &aFilter
);
1313 ScExternalRefManager
* pMgr
= mpDoc
->GetExternalRefManager();
1315 if (!pMgr
->isFileLoadable(aFile
))
1316 return ERROR_GENERAL
;
1318 const OUString
* pCurFile
= pMgr
->getExternalFileName(mnFileId
);
1320 return ERROR_GENERAL
;
1322 if (pCurFile
->equals(aFile
))
1324 // Refresh the current source document.
1325 if (!pMgr
->refreshSrcDocument(mnFileId
))
1326 return ERROR_GENERAL
;
1330 // The source document has changed.
1331 ScDocShell
* pDocShell
= ScDocShell::GetViewData()->GetDocShell();
1332 ScDocShellModificator
aMod(*pDocShell
);
1333 pMgr
->switchSrcFile(mnFileId
, aFile
, aFilter
);
1334 maFilterName
= aFilter
;
1335 aMod
.SetDocumentModified();
1341 void ScExternalRefLink::Edit(Window
* pParent
, const Link
& /*rEndEditHdl*/)
1343 SvBaseLink::Edit(pParent
, LINK(this, ScExternalRefLink
, ExternalRefEndEditHdl
));
1346 void ScExternalRefLink::SetDoReferesh(bool b
)
1351 IMPL_LINK_NOARG(ScExternalRefLink
, ExternalRefEndEditHdl
)
1356 static FormulaToken
* convertToToken( ScDocument
* pHostDoc
, ScDocument
* pSrcDoc
, ScRefCellValue
& rCell
)
1358 if (rCell
.hasEmptyValue())
1360 bool bInherited
= (rCell
.meType
== CELLTYPE_FORMULA
);
1361 return new ScEmptyCellToken(bInherited
, false);
1364 switch (rCell
.meType
)
1367 case CELLTYPE_STRING
:
1369 OUString aStr
= rCell
.getString(pSrcDoc
);
1370 svl::SharedString aSS
= pHostDoc
->GetSharedStringPool().intern(aStr
);
1371 return new formula::FormulaStringToken(aSS
);
1373 case CELLTYPE_VALUE
:
1374 return new formula::FormulaDoubleToken(rCell
.mfValue
);
1375 case CELLTYPE_FORMULA
:
1377 ScFormulaCell
* pFCell
= rCell
.mpFormula
;
1378 sal_uInt16 nError
= pFCell
->GetErrCode();
1380 return new FormulaErrorToken( nError
);
1381 else if (pFCell
->IsValue())
1383 double fVal
= pFCell
->GetValue();
1384 return new formula::FormulaDoubleToken(fVal
);
1388 svl::SharedString aStr
= pFCell
->GetString();
1389 return new formula::FormulaStringToken(aStr
);
1393 OSL_FAIL("attempted to convert an unknown cell type.");
1402 ScDocument
* mpHostDoc
;
1403 ScDocument
* mpSrcDoc
;
1405 std::vector
<T
> maStorage
;
1410 ColumnBatch( ScDocument
* pHostDoc
, ScDocument
* pSrcDoc
, CellType eType1
, CellType eType2
) :
1411 mpHostDoc(pHostDoc
),
1417 void update(ScRefCellValue
& raCell
, const SCCOL nCol
, const SCROW nRow
, ScMatrixRef
& xMat
)
1419 if (raCell
.meType
== meType1
|| raCell
.meType
== meType2
)
1423 maStorage
.push_back(getValue(raCell
));
1431 void flush(const SCCOL nCol
, ScMatrixRef
& xMat
)
1433 if (maStorage
.empty())
1435 putValues(xMat
, nCol
);
1440 T
getValue(ScRefCellValue
& raCell
) const;
1441 void putValues(ScMatrixRef
& xMat
, const SCCOL nCol
) const;
1445 inline svl::SharedString ColumnBatch
<svl::SharedString
>::getValue(ScRefCellValue
& rCell
) const
1447 OUString aStr
= rCell
.getString(mpSrcDoc
);
1448 return mpHostDoc
->GetSharedStringPool().intern(aStr
);
1452 inline T ColumnBatch
<T
>::getValue(ScRefCellValue
& raCell
) const
1454 return raCell
.mfValue
;
1458 inline void ColumnBatch
<svl::SharedString
>::putValues(ScMatrixRef
& xMat
, const SCCOL nCol
) const
1460 xMat
->PutString(&maStorage
.front(), maStorage
.size(), nCol
, mnRowStart
);
1464 inline void ColumnBatch
<T
>::putValues(ScMatrixRef
& xMat
, const SCCOL nCol
) const
1466 xMat
->PutDouble(&maStorage
.front(), maStorage
.size(), nCol
, mnRowStart
);
1469 static ScTokenArray
* convertToTokenArray(
1470 ScDocument
* pHostDoc
, ScDocument
* pSrcDoc
, ScRange
& rRange
, vector
<ScExternalRefCache::SingleRangeData
>& rCacheData
)
1472 ScAddress
& s
= rRange
.aStart
;
1473 ScAddress
& e
= rRange
.aEnd
;
1475 SCTAB nTab1
= s
.Tab(), nTab2
= e
.Tab();
1476 SCCOL nCol1
= s
.Col(), nCol2
= e
.Col();
1477 SCROW nRow1
= s
.Row(), nRow2
= e
.Row();
1480 // For now, we don't support multi-sheet ranges intentionally because
1481 // we don't have a way to express them in a single token. In the
1482 // future we can introduce a new stack variable type svMatrixList with
1483 // a new token type that can store a 3D matrix value and convert a 3D
1487 ::boost::scoped_ptr
<ScRange
> pUsedRange
;
1489 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1490 auto_ptr
<ScTokenArray
> pArray(new ScTokenArray
);
1491 SAL_WNODEPRECATED_DECLARATIONS_POP
1492 bool bFirstTab
= true;
1493 vector
<ScExternalRefCache::SingleRangeData
>::iterator
1494 itrCache
= rCacheData
.begin(), itrCacheEnd
= rCacheData
.end();
1496 for (SCTAB nTab
= nTab1
; nTab
<= nTab2
&& itrCache
!= itrCacheEnd
; ++nTab
, ++itrCache
)
1498 // Only loop within the data area.
1499 SCCOL nDataCol1
= nCol1
, nDataCol2
= nCol2
;
1500 SCROW nDataRow1
= nRow1
, nDataRow2
= nRow2
;
1502 if (!pSrcDoc
->ShrinkToUsedDataArea( bShrunk
, nTab
, nDataCol1
, nDataRow1
, nDataCol2
, nDataRow2
, false))
1503 // no data within specified range.
1506 if (pUsedRange
.get())
1507 // Make sure the used area only grows, not shrinks.
1508 pUsedRange
->ExtendTo(ScRange(nDataCol1
, nDataRow1
, 0, nDataCol2
, nDataRow2
, 0));
1510 pUsedRange
.reset(new ScRange(nDataCol1
, nDataRow1
, 0, nDataCol2
, nDataRow2
, 0));
1512 ScMatrixRef xMat
= new ScMatrix(
1513 static_cast<SCSIZE
>(nCol2
-nCol1
+1), static_cast<SCSIZE
>(nRow2
-nRow1
+1));
1515 ScRefCellValue aCell
;
1516 ColumnBatch
<svl::SharedString
> aStringBatch(pHostDoc
, pSrcDoc
, CELLTYPE_STRING
, CELLTYPE_EDIT
);
1517 ColumnBatch
<double> aDoubleBatch(pHostDoc
, pSrcDoc
, CELLTYPE_VALUE
, CELLTYPE_VALUE
);
1519 for (SCCOL nCol
= nDataCol1
; nCol
<= nDataCol2
; ++nCol
)
1521 const SCSIZE nC
= nCol
- nCol1
;
1522 for (SCROW nRow
= nDataRow1
; nRow
<= nDataRow2
; ++nRow
)
1524 const SCSIZE nR
= nRow
- nRow1
;
1526 aCell
.assign(*pSrcDoc
, ScAddress(nCol
, nRow
, nTab
));
1528 aStringBatch
.update(aCell
, nC
, nR
, xMat
);
1529 aDoubleBatch
.update(aCell
, nC
, nR
, xMat
);
1531 if (aCell
.hasEmptyValue())
1532 // Skip empty cells. Matrix's default values are empty elements.
1535 switch (aCell
.meType
)
1537 case CELLTYPE_FORMULA
:
1539 ScFormulaCell
* pFCell
= aCell
.mpFormula
;
1540 sal_uInt16 nError
= pFCell
->GetErrCode();
1542 xMat
->PutDouble( CreateDoubleError( nError
), nC
, nR
);
1543 else if (pFCell
->IsValue())
1545 double fVal
= pFCell
->GetValue();
1546 xMat
->PutDouble(fVal
, nC
, nR
);
1550 svl::SharedString aStr
= pFCell
->GetString();
1551 aStr
= pHostDoc
->GetSharedStringPool().intern(aStr
.getString());
1552 xMat
->PutString(aStr
, nC
, nR
);
1557 OSL_FAIL("attempted to convert an unknown cell type.");
1561 aStringBatch
.flush(nC
, xMat
);
1562 aDoubleBatch
.flush(nC
, xMat
);
1565 pArray
->AddOpCode(ocSep
);
1567 ScMatrixToken
aToken(xMat
);
1568 pArray
->AddToken(aToken
);
1570 itrCache
->mpRangeData
= xMat
;
1575 if (!pUsedRange
.get())
1578 s
.SetCol(pUsedRange
->aStart
.Col());
1579 s
.SetRow(pUsedRange
->aStart
.Row());
1580 e
.SetCol(pUsedRange
->aEnd
.Col());
1581 e
.SetRow(pUsedRange
->aEnd
.Row());
1583 return pArray
.release();
1586 static ScTokenArray
* lcl_fillEmptyMatrix(const ScRange
& rRange
)
1588 SCSIZE nC
= static_cast<SCSIZE
>(rRange
.aEnd
.Col()-rRange
.aStart
.Col()+1);
1589 SCSIZE nR
= static_cast<SCSIZE
>(rRange
.aEnd
.Row()-rRange
.aStart
.Row()+1);
1590 ScMatrixRef xMat
= new ScMatrix(nC
, nR
);
1592 ScMatrixToken
aToken(xMat
);
1593 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1594 auto_ptr
<ScTokenArray
> pArray(new ScTokenArray
);
1595 SAL_WNODEPRECATED_DECLARATIONS_POP
1596 pArray
->AddToken(aToken
);
1597 return pArray
.release();
1600 ScExternalRefManager::ScExternalRefManager(ScDocument
* pDoc
) :
1602 mbInReferenceMarking(false),
1603 mbUserInteractionEnabled(true),
1604 mbDocTimerEnabled(true)
1606 maSrcDocTimer
.SetTimeoutHdl( LINK(this, ScExternalRefManager
, TimeOutHdl
) );
1607 maSrcDocTimer
.SetTimeout(SRCDOC_SCAN_INTERVAL
);
1610 ScExternalRefManager::~ScExternalRefManager()
1615 OUString
ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId
, size_t nTabIndex
) const
1617 return maRefCache
.getTableName(nFileId
, nTabIndex
);
1620 ScExternalRefCache::TableTypeRef
ScExternalRefManager::getCacheTable(sal_uInt16 nFileId
, size_t nTabIndex
) const
1622 return maRefCache
.getCacheTable(nFileId
, nTabIndex
);
1625 ScExternalRefCache::TableTypeRef
ScExternalRefManager::getCacheTable(
1626 sal_uInt16 nFileId
, const OUString
& rTabName
, bool bCreateNew
, size_t* pnIndex
)
1628 return maRefCache
.getCacheTable(nFileId
, rTabName
, bCreateNew
, pnIndex
);
1631 ScExternalRefManager::LinkListener::LinkListener()
1635 ScExternalRefManager::LinkListener::~LinkListener()
1639 ScExternalRefManager::ApiGuard::ApiGuard(ScDocument
* pDoc
) :
1640 mpMgr(pDoc
->GetExternalRefManager()),
1641 mbOldInteractionEnabled(mpMgr
->mbUserInteractionEnabled
)
1643 // We don't want user interaction handled in the API.
1644 mpMgr
->mbUserInteractionEnabled
= false;
1647 ScExternalRefManager::ApiGuard::~ApiGuard()
1649 // Restore old value.
1650 mpMgr
->mbUserInteractionEnabled
= mbOldInteractionEnabled
;
1653 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId
, vector
<OUString
>& rTabNames
) const
1655 maRefCache
.getAllTableNames(nFileId
, rTabNames
);
1658 SCsTAB
ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId
, const OUString
& rStartTabName
, const OUString
& rEndTabName
) const
1660 return maRefCache
.getTabSpan( nFileId
, rStartTabName
, rEndTabName
);
1663 void ScExternalRefManager::getAllCachedNumberFormats(vector
<sal_uInt32
>& rNumFmts
) const
1665 maRefCache
.getAllNumberFormats(rNumFmts
);
1668 sal_uInt16
ScExternalRefManager::getExternalFileCount() const
1670 return static_cast< sal_uInt16
>( maSrcFiles
.size() );
1673 bool ScExternalRefManager::markUsedByLinkListeners()
1675 bool bAllMarked
= false;
1676 for (LinkListenerMap::const_iterator itr
= maLinkListeners
.begin();
1677 itr
!= maLinkListeners
.end() && !bAllMarked
; ++itr
)
1679 if (!(*itr
).second
.empty())
1680 bAllMarked
= maRefCache
.setCacheDocReferenced( (*itr
).first
);
1681 /* TODO: LinkListeners should remember the table they're listening to.
1682 * As is, listening to one table will mark all tables of the document
1683 * being referenced. */
1688 bool ScExternalRefManager::markUsedExternalRefCells()
1690 RefCellMap::iterator itr
= maRefCells
.begin(), itrEnd
= maRefCells
.end();
1691 for (; itr
!= itrEnd
; ++itr
)
1693 RefCellSet::iterator itrCell
= itr
->second
.begin(), itrCellEnd
= itr
->second
.end();
1694 for (; itrCell
!= itrCellEnd
; ++itrCell
)
1696 ScFormulaCell
* pCell
= *itrCell
;
1697 bool bUsed
= pCell
->MarkUsedExternalReferences();
1699 // Return true when at least one cell references external docs.
1706 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId
, const OUString
& rTabName
, size_t nSheets
)
1708 return maRefCache
.setCacheTableReferenced( nFileId
, rTabName
, nSheets
, false);
1711 void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced
)
1713 mbInReferenceMarking
= !bReferenced
;
1714 maRefCache
.setAllCacheTableReferencedStati( bReferenced
);
1717 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId
, const OUString
& rName
, const ScTokenArray
& rArray
)
1719 ScExternalRefCache::TokenArrayRef
pArray(rArray
.Clone());
1720 maRefCache
.setRangeNameTokens(nFileId
, rName
, pArray
);
1726 * Put a single cell data into internal cache table.
1728 * @param pFmt optional cell format index that may need to be stored with
1731 void putCellDataIntoCache(
1732 ScExternalRefCache
& rRefCache
, const ScExternalRefCache::TokenRef
& pToken
,
1733 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScAddress
& rCell
,
1734 const ScExternalRefCache::CellFormat
* pFmt
)
1736 // Now, insert the token into cache table but don't cache empty cells.
1737 if (pToken
->GetType() != formula::svEmptyCell
)
1739 sal_uLong nFmtIndex
= (pFmt
&& pFmt
->mbIsSet
) ? pFmt
->mnIndex
: 0;
1740 rRefCache
.setCellData(nFileId
, rTabName
, rCell
.Col(), rCell
.Row(), pToken
, nFmtIndex
);
1745 * Put the data into our internal cache table.
1747 * @param rRefCache cache table set.
1748 * @param pArray single range data to be returned.
1749 * @param nFileId external file ID
1750 * @param rTabName name of the table where the data should be cached.
1751 * @param rCacheData range data to be cached.
1752 * @param rCacheRange original cache range, including the empty region if
1754 * @param rDataRange reduced cache range that includes only the non-empty
1757 void putRangeDataIntoCache(
1758 ScExternalRefCache
& rRefCache
, ScExternalRefCache::TokenArrayRef
& pArray
,
1759 sal_uInt16 nFileId
, const OUString
& rTabName
,
1760 const vector
<ScExternalRefCache::SingleRangeData
>& rCacheData
,
1761 const ScRange
& rCacheRange
, const ScRange
& rDataRange
)
1764 // Cache these values.
1765 rRefCache
.setCellRangeData(nFileId
, rDataRange
, rCacheData
, pArray
);
1768 // Array is empty. Fill it with an empty matrix of the required size.
1769 pArray
.reset(lcl_fillEmptyMatrix(rCacheRange
));
1771 // Make sure to set this range 'cached', to prevent unnecessarily
1772 // accessing the src document time and time again.
1773 ScExternalRefCache::TableTypeRef pCacheTab
=
1774 rRefCache
.getCacheTable(nFileId
, rTabName
, true, NULL
);
1776 pCacheTab
->setCachedCellRange(
1777 rCacheRange
.aStart
.Col(), rCacheRange
.aStart
.Row(), rCacheRange
.aEnd
.Col(), rCacheRange
.aEnd
.Row());
1782 * When accessing an external document for the first time, we need to
1783 * populate the cache with all its sheet names (whether they are referenced
1784 * or not) in the correct order. Many client codes that use external
1785 * references make this assumption.
1787 * @param rRefCache cache table set.
1788 * @param pSrcDoc source document instance.
1789 * @param nFileId external file ID associated with the source document.
1791 void initDocInCache(ScExternalRefCache
& rRefCache
, const ScDocument
* pSrcDoc
, sal_uInt16 nFileId
)
1796 if (rRefCache
.isDocInitialized(nFileId
))
1797 // Already initialized. No need to do this twice.
1800 SCTAB nTabCount
= pSrcDoc
->GetTableCount();
1803 // Populate the cache with all table names in the source document.
1804 vector
<OUString
> aTabNames
;
1805 aTabNames
.reserve(nTabCount
);
1806 for (SCTAB i
= 0; i
< nTabCount
; ++i
)
1809 pSrcDoc
->GetName(i
, aName
);
1810 aTabNames
.push_back(aName
);
1812 rRefCache
.initializeDoc(nFileId
, aTabNames
);
1818 ScExternalRefCache::TokenRef
ScExternalRefManager::getSingleRefToken(
1819 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScAddress
& rCell
,
1820 const ScAddress
* pCurPos
, SCTAB
* pTab
, ScExternalRefCache::CellFormat
* pFmt
)
1823 insertRefCell(nFileId
, *pCurPos
);
1825 maybeLinkExternalFile(nFileId
);
1831 pFmt
->mbIsSet
= false;
1833 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
1836 // source document already loaded in memory. Re-use this instance.
1838 if (!pSrcDoc
->GetTable(rTabName
, nTab
))
1840 // specified table name doesn't exist in the source document.
1841 ScExternalRefCache::TokenRef
pToken(new FormulaErrorToken(errNoRef
));
1848 ScExternalRefCache::TokenRef pToken
=
1849 getSingleRefTokenFromSrcDoc(
1850 nFileId
, pSrcDoc
, ScAddress(rCell
.Col(),rCell
.Row(),nTab
), pFmt
);
1852 putCellDataIntoCache(maRefCache
, pToken
, nFileId
, rTabName
, rCell
, pFmt
);
1856 // Check if the given table name and the cell position is cached.
1857 sal_uInt32 nFmtIndex
= 0;
1858 ScExternalRefCache::TokenRef pToken
= maRefCache
.getCellData(
1859 nFileId
, rTabName
, rCell
.Col(), rCell
.Row(), &nFmtIndex
);
1863 fillCellFormat(nFmtIndex
, pFmt
);
1867 // reference not cached. read from the source document.
1868 pSrcDoc
= getSrcDocument(nFileId
);
1871 // Source document not reachable. Throw a reference error.
1872 pToken
.reset(new FormulaErrorToken(errNoRef
));
1877 if (!pSrcDoc
->GetTable(rTabName
, nTab
))
1879 // specified table name doesn't exist in the source document.
1880 pToken
.reset(new FormulaErrorToken(errNoRef
));
1887 SCCOL nDataCol1
= 0, nDataCol2
= MAXCOL
;
1888 SCROW nDataRow1
= 0, nDataRow2
= MAXROW
;
1889 bool bData
= pSrcDoc
->ShrinkToDataArea(nTab
, nDataCol1
, nDataRow1
, nDataCol2
, nDataRow2
);
1890 if (!bData
|| rCell
.Col() < nDataCol1
|| nDataCol2
< rCell
.Col() || rCell
.Row() < nDataRow1
|| nDataRow2
< rCell
.Row())
1892 // requested cell is outside the data area. Don't even bother caching
1893 // this data, but add it to the cached range to prevent accessing the
1894 // source document time and time again.
1895 ScExternalRefCache::TableTypeRef pCacheTab
=
1896 maRefCache
.getCacheTable(nFileId
, rTabName
, true, NULL
);
1898 pCacheTab
->setCachedCell(rCell
.Col(), rCell
.Row());
1900 pToken
.reset(new ScEmptyCellToken(false, false));
1904 pToken
= getSingleRefTokenFromSrcDoc(
1905 nFileId
, pSrcDoc
, ScAddress(rCell
.Col(),rCell
.Row(),nTab
), pFmt
);
1907 putCellDataIntoCache(maRefCache
, pToken
, nFileId
, rTabName
, rCell
, pFmt
);
1911 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getDoubleRefTokens(
1912 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScRange
& rRange
, const ScAddress
* pCurPos
)
1915 insertRefCell(nFileId
, *pCurPos
);
1917 maybeLinkExternalFile(nFileId
);
1919 ScRange
aDataRange(rRange
);
1920 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
1923 // Document already loaded in memory.
1924 vector
<ScExternalRefCache::SingleRangeData
> aCacheData
;
1925 ScExternalRefCache::TokenArrayRef pArray
=
1926 getDoubleRefTokensFromSrcDoc(pSrcDoc
, rTabName
, aDataRange
, aCacheData
);
1928 // Put the data into cache.
1929 putRangeDataIntoCache(maRefCache
, pArray
, nFileId
, rTabName
, aCacheData
, rRange
, aDataRange
);
1933 // Check if the given table name and the cell position is cached.
1934 ScExternalRefCache::TokenArrayRef pArray
=
1935 maRefCache
.getCellRangeData(nFileId
, rTabName
, rRange
);
1940 pSrcDoc
= getSrcDocument(nFileId
);
1943 // Source document is not reachable. Throw a reference error.
1944 pArray
.reset(new ScTokenArray
);
1945 pArray
->AddToken(FormulaErrorToken(errNoRef
));
1949 vector
<ScExternalRefCache::SingleRangeData
> aCacheData
;
1950 pArray
= getDoubleRefTokensFromSrcDoc(pSrcDoc
, rTabName
, aDataRange
, aCacheData
);
1952 // Put the data into cache.
1953 putRangeDataIntoCache(maRefCache
, pArray
, nFileId
, rTabName
, aCacheData
, rRange
, aDataRange
);
1957 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getRangeNameTokens(
1958 sal_uInt16 nFileId
, const OUString
& rName
, const ScAddress
* pCurPos
)
1961 insertRefCell(nFileId
, *pCurPos
);
1963 maybeLinkExternalFile(nFileId
);
1965 OUString aName
= rName
; // make a copy to have the casing corrected.
1966 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
1969 // Document already loaded in memory.
1970 ScExternalRefCache::TokenArrayRef pArray
=
1971 getRangeNameTokensFromSrcDoc(nFileId
, pSrcDoc
, aName
);
1974 // Cache this range name array.
1975 maRefCache
.setRangeNameTokens(nFileId
, aName
, pArray
);
1980 ScExternalRefCache::TokenArrayRef pArray
= maRefCache
.getRangeNameTokens(nFileId
, rName
);
1982 // This range name is cached.
1985 pSrcDoc
= getSrcDocument(nFileId
);
1987 // failed to load document from disk.
1988 return ScExternalRefCache::TokenArrayRef();
1990 pArray
= getRangeNameTokensFromSrcDoc(nFileId
, pSrcDoc
, aName
);
1993 // Cache this range name array.
1994 maRefCache
.setRangeNameTokens(nFileId
, aName
, pArray
);
2001 bool hasRangeName(ScDocument
& rDoc
, const OUString
& rName
)
2003 ScRangeName
* pExtNames
= rDoc
.GetRangeName();
2004 OUString aUpperName
= ScGlobal::pCharClass
->uppercase(rName
);
2005 const ScRangeData
* pRangeData
= pExtNames
->findByUpperName(aUpperName
);
2006 return pRangeData
!= NULL
;
2011 bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId
, const OUString
& rName
)
2013 maybeLinkExternalFile(nFileId
);
2014 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
2017 // Only check the presence of the name.
2018 return hasRangeName(*pSrcDoc
, rName
);
2021 if (maRefCache
.isValidRangeName(nFileId
, rName
))
2022 // Range name is cached.
2025 pSrcDoc
= getSrcDocument(nFileId
);
2027 // failed to load document from disk.
2030 return hasRangeName(*pSrcDoc
, rName
);
2033 void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId
)
2035 RefCellMap::iterator itrFile
= maRefCells
.find(nFileId
);
2036 if (itrFile
== maRefCells
.end())
2039 RefCellSet
& rRefCells
= itrFile
->second
;
2040 for_each(rRefCells
.begin(), rRefCells
.end(), UpdateFormulaCell());
2042 ScViewData
* pViewData
= ScDocShell::GetViewData();
2046 ScTabViewShell
* pVShell
= pViewData
->GetViewShell();
2050 // Repainting the grid also repaints the texts, but is there a better way
2051 // to refresh texts?
2052 pVShell
->Invalidate(FID_REPAINT
);
2053 pVShell
->PaintGrid();
2056 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId
, const ScAddress
& rCell
)
2058 RefCellMap::iterator itr
= maRefCells
.find(nFileId
);
2059 if (itr
== maRefCells
.end())
2061 RefCellSet aRefCells
;
2062 pair
<RefCellMap::iterator
, bool> r
= maRefCells
.insert(
2063 RefCellMap::value_type(nFileId
, aRefCells
));
2065 // insertion failed.
2071 ScFormulaCell
* pCell
= mpDoc
->GetFormulaCell(rCell
);
2073 itr
->second
.insert(pCell
);
2076 void ScExternalRefManager::enableDocTimer( bool bEnable
)
2078 if (mbDocTimerEnabled
== bEnable
)
2081 mbDocTimerEnabled
= bEnable
;
2082 if (mbDocTimerEnabled
)
2084 if (!maDocShells
.empty())
2086 DocShellMap::iterator it
= maDocShells
.begin(), itEnd
= maDocShells
.end();
2087 for (; it
!= itEnd
; ++it
)
2088 it
->second
.maLastAccess
= Time(Time::SYSTEM
);
2090 maSrcDocTimer
.Start();
2094 maSrcDocTimer
.Stop();
2097 void ScExternalRefManager::fillCellFormat(sal_uLong nFmtIndex
, ScExternalRefCache::CellFormat
* pFmt
) const
2102 short nFmtType
= mpDoc
->GetFormatTable()->GetType(nFmtIndex
);
2103 if (nFmtType
!= NUMBERFORMAT_UNDEFINED
)
2105 pFmt
->mbIsSet
= true;
2106 pFmt
->mnIndex
= nFmtIndex
;
2107 pFmt
->mnType
= nFmtType
;
2111 ScExternalRefCache::TokenRef
ScExternalRefManager::getSingleRefTokenFromSrcDoc(
2112 sal_uInt16 nFileId
, ScDocument
* pSrcDoc
, const ScAddress
& rPos
,
2113 ScExternalRefCache::CellFormat
* pFmt
)
2115 // Get the cell from src doc, and convert it into a token.
2116 ScRefCellValue aCell
;
2117 aCell
.assign(*pSrcDoc
, rPos
);
2118 ScExternalRefCache::TokenRef
pToken(convertToToken(mpDoc
, pSrcDoc
, aCell
));
2122 // Generate an error for unresolvable cells.
2123 pToken
.reset( new FormulaErrorToken( errNoValue
));
2126 // Get number format information.
2127 sal_uInt32 nFmtIndex
= 0;
2128 pSrcDoc
->GetNumberFormat(rPos
.Col(), rPos
.Row(), rPos
.Tab(), nFmtIndex
);
2129 nFmtIndex
= getMappedNumberFormat(nFileId
, nFmtIndex
, pSrcDoc
);
2130 fillCellFormat(nFmtIndex
, pFmt
);
2134 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getDoubleRefTokensFromSrcDoc(
2135 ScDocument
* pSrcDoc
, const OUString
& rTabName
, ScRange
& rRange
,
2136 vector
<ScExternalRefCache::SingleRangeData
>& rCacheData
)
2138 ScExternalRefCache::TokenArrayRef pArray
;
2141 if (!pSrcDoc
->GetTable(rTabName
, nTab1
))
2143 // specified table name doesn't exist in the source document.
2144 pArray
.reset(new ScTokenArray
);
2145 pArray
->AddToken(FormulaErrorToken(errNoRef
));
2149 ScRange
aRange(rRange
);
2151 SCTAB nTabSpan
= aRange
.aEnd
.Tab() - aRange
.aStart
.Tab();
2153 vector
<ScExternalRefCache::SingleRangeData
> aCacheData
;
2154 aCacheData
.reserve(nTabSpan
+1);
2155 aCacheData
.push_back(ScExternalRefCache::SingleRangeData());
2156 aCacheData
.back().maTableName
= ScGlobal::pCharClass
->uppercase(rTabName
);
2158 for (SCTAB i
= 1; i
< nTabSpan
+ 1; ++i
)
2161 if (!pSrcDoc
->GetName(nTab1
+ 1, aTabName
))
2162 // source document doesn't have any table by the specified name.
2165 aCacheData
.push_back(ScExternalRefCache::SingleRangeData());
2166 aCacheData
.back().maTableName
= ScGlobal::pCharClass
->uppercase(aTabName
);
2169 aRange
.aStart
.SetTab(nTab1
);
2170 aRange
.aEnd
.SetTab(nTab1
+ nTabSpan
);
2172 pArray
.reset(convertToTokenArray(mpDoc
, pSrcDoc
, aRange
, aCacheData
));
2174 rCacheData
.swap(aCacheData
);
2178 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getRangeNameTokensFromSrcDoc(
2179 sal_uInt16 nFileId
, ScDocument
* pSrcDoc
, OUString
& rName
)
2181 ScRangeName
* pExtNames
= pSrcDoc
->GetRangeName();
2182 OUString aUpperName
= ScGlobal::pCharClass
->uppercase(rName
);
2183 const ScRangeData
* pRangeData
= pExtNames
->findByUpperName(aUpperName
);
2185 return ScExternalRefCache::TokenArrayRef();
2187 // Parse all tokens in this external range data, and replace each absolute
2188 // reference token with an external reference token, and cache them. Also
2189 // register the source document with the link manager if it's a new
2192 ScExternalRefCache::TokenArrayRef
pNew(new ScTokenArray
);
2194 ScTokenArray
aCode(*pRangeData
->GetCode());
2195 for (const FormulaToken
* pToken
= aCode
.First(); pToken
; pToken
= aCode
.Next())
2197 bool bTokenAdded
= false;
2198 switch (pToken
->GetType())
2202 const ScSingleRefData
& rRef
= static_cast<const ScToken
*>(pToken
)->GetSingleRef();
2204 pSrcDoc
->GetName(rRef
.Tab(), aTabName
);
2205 ScExternalSingleRefToken
aNewToken(nFileId
, aTabName
, static_cast<const ScToken
*>(pToken
)->GetSingleRef());
2206 pNew
->AddToken(aNewToken
);
2212 const ScSingleRefData
& rRef
= static_cast<const ScToken
*>(pToken
)->GetSingleRef();
2214 pSrcDoc
->GetName(rRef
.Tab(), aTabName
);
2215 ScExternalDoubleRefToken
aNewToken(nFileId
, aTabName
, static_cast<const ScToken
*>(pToken
)->GetDoubleRef());
2216 pNew
->AddToken(aNewToken
);
2225 pNew
->AddToken(*pToken
);
2228 rName
= pRangeData
->GetName(); // Get the correctly-cased name.
2232 ScDocument
* ScExternalRefManager::getInMemorySrcDocument(sal_uInt16 nFileId
)
2234 const OUString
* pFileName
= getExternalFileName(nFileId
);
2238 ScDocument
* pSrcDoc
= NULL
;
2239 TypeId
aType(TYPE(ScDocShell
));
2240 ScDocShell
* pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(&aType
, false));
2243 SfxMedium
* pMedium
= pShell
->GetMedium();
2244 if (pMedium
&& !pMedium
->GetName().isEmpty())
2246 // TODO: We should make the case sensitivity platform dependent.
2247 if (pFileName
->equalsIgnoreAsciiCase(pMedium
->GetName()))
2250 pSrcDoc
= pShell
->GetDocument();
2256 // handle unsaved documents here
2257 OUString aName
= pShell
->GetName();
2258 if (pFileName
->equalsIgnoreAsciiCase(aName
))
2262 aSrcDoc
.maShell
= pShell
;
2263 maUnsavedDocShells
.insert(DocShellMap::value_type(nFileId
, aSrcDoc
));
2264 StartListening(*pShell
);
2265 pSrcDoc
= pShell
->GetDocument();
2269 pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pShell
, &aType
, false));
2272 initDocInCache(maRefCache
, pSrcDoc
, nFileId
);
2276 ScDocument
* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId
)
2278 if (!mpDoc
->IsExecuteLinkEnabled())
2281 DocShellMap::iterator itrEnd
= maDocShells
.end();
2282 DocShellMap::iterator itr
= maDocShells
.find(nFileId
);
2286 // document already loaded.
2288 SfxObjectShell
* p
= itr
->second
.maShell
;
2289 itr
->second
.maLastAccess
= Time( Time::SYSTEM
);
2290 return static_cast<ScDocShell
*>(p
)->GetDocument();
2293 itrEnd
= maUnsavedDocShells
.end();
2294 itr
= maUnsavedDocShells
.find(nFileId
);
2297 //document is unsaved document
2299 SfxObjectShell
* p
= itr
->second
.maShell
;
2300 itr
->second
.maLastAccess
= Time( Time::SYSTEM
);
2301 return static_cast<ScDocShell
*>(p
)->GetDocument();
2304 const OUString
* pFile
= getExternalFileName(nFileId
);
2306 // no file name associated with this ID.
2313 aSrcDoc
.maShell
= loadSrcDocument(nFileId
, aFilter
);
2315 catch (const css::uno::Exception
&)
2318 if (!aSrcDoc
.maShell
.Is())
2320 // source document could not be loaded.
2324 return cacheNewDocShell(nFileId
, aSrcDoc
);
2327 SfxObjectShellRef
ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId
, OUString
& rFilter
)
2329 const SrcFileData
* pFileData
= getExternalFileData(nFileId
);
2333 // Always load the document by using the path created from the relative
2334 // path. If the referenced document is not there, simply exit. The
2335 // original file name should be used only when the relative path is not
2337 OUString aFile
= pFileData
->maFileName
;
2338 maybeCreateRealFileName(nFileId
);
2339 if (!pFileData
->maRealFileName
.isEmpty())
2340 aFile
= pFileData
->maRealFileName
;
2342 if (!isFileLoadable(aFile
))
2345 OUString aOptions
= pFileData
->maFilterOptions
;
2346 if ( !pFileData
->maFilterName
.isEmpty() )
2347 rFilter
= pFileData
->maFilterName
; // don't overwrite stored filter with guessed filter
2349 ScDocumentLoader::GetFilterName(aFile
, rFilter
, aOptions
, true, false);;
2350 ScDocumentLoader::GetFilterName(aFile
, rFilter
, aOptions
, true, false);
2351 const SfxFilter
* pFilter
= ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter
);
2353 if (pFileData
->maRelativeName
.isEmpty())
2355 // Generate a relative file path.
2356 INetURLObject
aBaseURL(getOwnDocumentName());
2357 aBaseURL
.insertName(OUString("content.xml"));
2359 OUString aStr
= URIHelper::simpleNormalizedMakeRelative(
2360 aBaseURL
.GetMainURL(INetURLObject::NO_DECODE
), aFile
);
2362 setRelativeFileName(nFileId
, aStr
);
2365 SfxItemSet
* pSet
= new SfxAllItemSet(SFX_APP()->GetPool());
2366 if (!aOptions
.isEmpty())
2367 pSet
->Put(SfxStringItem(SID_FILE_FILTEROPTIONS
, aOptions
));
2369 // make medium hidden to prevent assertion from progress bar
2370 pSet
->Put( SfxBoolItem(SID_HIDDEN
, true) );
2372 SAL_WNODEPRECATED_DECLARATIONS_PUSH
2373 auto_ptr
<SfxMedium
> pMedium(new SfxMedium(aFile
, STREAM_STD_READ
, pFilter
, pSet
));
2374 SAL_WNODEPRECATED_DECLARATIONS_POP
2375 if (pMedium
->GetError() != ERRCODE_NONE
)
2378 // To load encrypted documents with password, user interaction needs to be enabled.
2379 pMedium
->UseInteractionHandler(mbUserInteractionEnabled
);
2381 ScDocShell
* pNewShell
= new ScDocShell(SFXMODEL_EXTERNAL_LINK
);
2382 SfxObjectShellRef aRef
= pNewShell
;
2384 // increment the recursive link count of the source document.
2385 ScExtDocOptions
* pExtOpt
= mpDoc
->GetExtDocOptions();
2386 sal_uInt32 nLinkCount
= pExtOpt
? pExtOpt
->GetDocSettings().mnLinkCnt
: 0;
2387 ScDocument
* pSrcDoc
= pNewShell
->GetDocument();
2388 pSrcDoc
->EnableExecuteLink(false); // to prevent circular access of external references.
2389 pSrcDoc
->EnableUndo(false);
2390 pSrcDoc
->EnableAdjustHeight(false);
2391 pSrcDoc
->EnableUserInteraction(false);
2393 ScExtDocOptions
* pExtOptNew
= pSrcDoc
->GetExtDocOptions();
2396 pExtOptNew
= new ScExtDocOptions
;
2397 pSrcDoc
->SetExtDocOptions(pExtOptNew
);
2399 pExtOptNew
->GetDocSettings().mnLinkCnt
= nLinkCount
+ 1;
2401 if (!pNewShell
->DoLoad(pMedium
.release()))
2408 // with UseInteractionHandler, options may be set by dialog during DoLoad
2409 OUString aNew
= ScDocumentLoader::GetOptions(*pNewShell
->GetMedium());
2410 if (!aNew
.isEmpty() && aNew
!= aOptions
)
2412 setFilterData(nFileId
, rFilter
, aOptions
); // update the filter data, including the new options
2417 ScDocument
* ScExternalRefManager::cacheNewDocShell( sal_uInt16 nFileId
, SrcShell
& rSrcShell
)
2419 if (mbDocTimerEnabled
&& maDocShells
.empty())
2420 // If this is the first source document insertion, start up the timer.
2421 maSrcDocTimer
.Start();
2423 maDocShells
.insert(DocShellMap::value_type(nFileId
, rSrcShell
));
2424 SfxObjectShell
& rShell
= *rSrcShell
.maShell
;
2425 ScDocument
* pSrcDoc
= static_cast<ScDocShell
&>(rShell
).GetDocument();
2426 initDocInCache(maRefCache
, pSrcDoc
, nFileId
);
2430 bool ScExternalRefManager::isFileLoadable(const OUString
& rFile
) const
2432 if (rFile
.isEmpty())
2435 if (isOwnDocument(rFile
))
2438 if (utl::LocalFileHelper::ConvertURLToPhysicalName(rFile
, aPhysical
) && !aPhysical
.isEmpty())
2440 // #i114504# try IsFolder/Exists only for file URLs
2442 if (utl::UCBContentHelper::IsFolder(rFile
))
2445 return utl::UCBContentHelper::Exists(rFile
);
2448 return true; // for http and others, Exists doesn't work, but the URL can still be opened
2451 void ScExternalRefManager::maybeLinkExternalFile(sal_uInt16 nFileId
)
2453 if (maLinkedDocs
.count(nFileId
))
2454 // file already linked, or the link has been broken.
2457 // Source document not linked yet. Link it now.
2458 const OUString
* pFileName
= getExternalFileName(nFileId
);
2462 OUString aFilter
, aOptions
;
2463 const SrcFileData
* pFileData
= getExternalFileData(nFileId
);
2466 aFilter
= pFileData
->maFilterName
;
2467 aOptions
= pFileData
->maFilterOptions
;
2469 // If a filter was already set (for example, loading the cached table),
2470 // don't call GetFilterName which has to access the source file.
2471 if (aFilter
.isEmpty())
2472 ScDocumentLoader::GetFilterName(*pFileName
, aFilter
, aOptions
, true, false);
2473 sfx2::LinkManager
* pLinkMgr
= mpDoc
->GetLinkManager();
2476 SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL");
2479 ScExternalRefLink
* pLink
= new ScExternalRefLink(mpDoc
, nFileId
, aFilter
);
2480 OSL_ENSURE(pFileName
, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL");
2481 OUString aTmp
= aFilter
;
2482 pLinkMgr
->InsertFileLink(*pLink
, OBJECT_CLIENT_FILE
, *pFileName
, &aTmp
);
2484 pLink
->SetDoReferesh(false);
2486 pLink
->SetDoReferesh(true);
2488 maLinkedDocs
.insert(LinkedDocMap::value_type(nFileId
, true));
2491 void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const OUString
& rOwnDocName
)
2493 if (maRelativeName
.isEmpty())
2494 // No relative path given. Nothing to do.
2497 if (!maRealFileName
.isEmpty())
2498 // Real file name already created. Nothing to do.
2501 // Formulate the absolute file path from the relative path.
2502 const OUString
& rRelPath
= maRelativeName
;
2503 INetURLObject
aBaseURL(rOwnDocName
);
2504 aBaseURL
.insertName(OUString("content.xml"));
2505 bool bWasAbs
= false;
2506 maRealFileName
= aBaseURL
.smartRel2Abs(rRelPath
, bWasAbs
).GetMainURL(INetURLObject::NO_DECODE
);
2509 void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId
)
2511 if (nFileId
>= maSrcFiles
.size())
2514 maSrcFiles
[nFileId
].maybeCreateRealFileName(getOwnDocumentName());
2517 OUString
ScExternalRefManager::getOwnDocumentName() const
2519 SfxObjectShell
* pShell
= mpDoc
->GetDocumentShell();
2521 // This should not happen!
2524 SfxMedium
* pMed
= pShell
->GetMedium();
2528 return pMed
->GetName();
2531 bool ScExternalRefManager::isOwnDocument(const OUString
& rFile
) const
2533 return getOwnDocumentName().equals(rFile
);
2536 void ScExternalRefManager::convertToAbsName(OUString
& rFile
) const
2538 // unsaved documents have no AbsName
2539 TypeId
aType(TYPE(ScDocShell
));
2540 ScDocShell
* pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(&aType
, false));
2543 if (rFile
== pShell
->GetName())
2546 pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pShell
, &aType
, false));
2549 SfxObjectShell
* pDocShell
= mpDoc
->GetDocumentShell();
2550 rFile
= ScGlobal::GetAbsDocName(rFile
, pDocShell
);
2553 sal_uInt16
ScExternalRefManager::getExternalFileId(const OUString
& rFile
)
2555 vector
<SrcFileData
>::const_iterator itrBeg
= maSrcFiles
.begin(), itrEnd
= maSrcFiles
.end();
2556 vector
<SrcFileData
>::const_iterator itr
= find_if(itrBeg
, itrEnd
, FindSrcFileByName(rFile
));
2559 size_t nId
= distance(itrBeg
, itr
);
2560 return static_cast<sal_uInt16
>(nId
);
2564 aData
.maFileName
= rFile
;
2565 maSrcFiles
.push_back(aData
);
2566 return static_cast<sal_uInt16
>(maSrcFiles
.size() - 1);
2569 const OUString
* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId
, bool bForceOriginal
)
2571 if (nFileId
>= maSrcFiles
.size())
2575 return &maSrcFiles
[nFileId
].maFileName
;
2577 maybeCreateRealFileName(nFileId
);
2579 if (!maSrcFiles
[nFileId
].maRealFileName
.isEmpty())
2580 return &maSrcFiles
[nFileId
].maRealFileName
;
2582 return &maSrcFiles
[nFileId
].maFileName
;
2585 std::vector
<OUString
> ScExternalRefManager::getAllCachedExternalFileNames() const
2587 std::vector
<OUString
> aNames
;
2588 aNames
.reserve(maSrcFiles
.size());
2589 std::vector
<SrcFileData
>::const_iterator it
= maSrcFiles
.begin(), itEnd
= maSrcFiles
.end();
2590 for (; it
!= itEnd
; ++it
)
2592 const SrcFileData
& rData
= *it
;
2593 aNames
.push_back(rData
.maFileName
);
2599 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId
) const
2601 return nFileId
< maSrcFiles
.size();
2604 bool ScExternalRefManager::hasExternalFile(const OUString
& rFile
) const
2606 vector
<SrcFileData
>::const_iterator itrBeg
= maSrcFiles
.begin(), itrEnd
= maSrcFiles
.end();
2607 vector
<SrcFileData
>::const_iterator itr
= find_if(itrBeg
, itrEnd
, FindSrcFileByName(rFile
));
2608 return itr
!= itrEnd
;
2611 const ScExternalRefManager::SrcFileData
* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId
) const
2613 if (nFileId
>= maSrcFiles
.size())
2616 return &maSrcFiles
[nFileId
];
2619 const OUString
* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId
, const OUString
& rTabName
) const
2621 return maRefCache
.getRealTableName(nFileId
, rTabName
);
2624 const OUString
* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId
, const OUString
& rRangeName
) const
2626 return maRefCache
.getRealRangeName(nFileId
, rRangeName
);
2629 template<typename MapContainer
>
2630 static void lcl_removeByFileId(sal_uInt16 nFileId
, MapContainer
& rMap
)
2632 typename
MapContainer::iterator itr
= rMap
.find(nFileId
);
2633 if (itr
!= rMap
.end())
2635 // Close this document shell.
2636 itr
->second
.maShell
->DoClose();
2641 void ScExternalRefManager::clearCache(sal_uInt16 nFileId
)
2643 maRefCache
.clearCache(nFileId
);
2648 class RefCacheFiller
: public sc::ColumnSpanSet::ColumnAction
2650 svl::SharedStringPool
& mrStrPool
;
2652 ScExternalRefCache
& mrRefCache
;
2653 ScExternalRefCache::TableTypeRef mpRefTab
;
2654 sal_uInt16 mnFileId
;
2656 sc::ColumnBlockConstPosition maBlockPos
;
2659 RefCacheFiller( svl::SharedStringPool
& rStrPool
, ScExternalRefCache
& rRefCache
, sal_uInt16 nFileId
) :
2660 mrStrPool(rStrPool
), mrRefCache(rRefCache
), mnFileId(nFileId
), mpCurCol(NULL
) {}
2662 virtual void startColumn( ScColumn
* pCol
) SAL_OVERRIDE
2668 mpCurCol
->InitBlockPosition(maBlockPos
);
2669 mpRefTab
= mrRefCache
.getCacheTable(mnFileId
, mpCurCol
->GetTab());
2672 virtual void execute( SCROW nRow1
, SCROW nRow2
, bool bVal
) SAL_OVERRIDE
2674 if (!mpCurCol
|| !bVal
)
2680 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
2682 ScExternalRefCache::TokenRef pTok
;
2683 ScRefCellValue aCell
= mpCurCol
->GetCellValue(maBlockPos
, nRow
);
2684 switch (aCell
.meType
)
2686 case CELLTYPE_STRING
:
2689 OUString aStr
= aCell
.getString(&mpCurCol
->GetDoc());
2690 svl::SharedString aSS
= mrStrPool
.intern(aStr
);
2691 pTok
.reset(new formula::FormulaStringToken(aSS
));
2694 case CELLTYPE_VALUE
:
2695 pTok
.reset(new formula::FormulaDoubleToken(aCell
.mfValue
));
2697 case CELLTYPE_FORMULA
:
2699 sc::FormulaResultValue aRes
= aCell
.mpFormula
->GetResult();
2700 switch (aRes
.meType
)
2702 case sc::FormulaResultValue::Value
:
2703 pTok
.reset(new formula::FormulaDoubleToken(aRes
.mfValue
));
2705 case sc::FormulaResultValue::String
:
2707 // Re-intern the string to the host document pool.
2708 svl::SharedString aInterned
= mrStrPool
.intern(aRes
.maString
.getString());
2709 pTok
.reset(new formula::FormulaStringToken(aInterned
));
2712 case sc::FormulaResultValue::Error
:
2713 case sc::FormulaResultValue::Invalid
:
2715 pTok
.reset(new FormulaErrorToken(errNoValue
));
2720 pTok
.reset(new FormulaErrorToken(errNoValue
));
2726 mpRefTab
->setCell(mpCurCol
->GetCol(), nRow
, pTok
, mpCurCol
->GetNumberFormat(nRow
));
2727 mpRefTab
->setCachedCell(mpCurCol
->GetCol(), nRow
);
2735 bool ScExternalRefManager::refreshSrcDocument(sal_uInt16 nFileId
)
2737 sc::ColumnSpanSet
aCachedArea(false);
2738 maRefCache
.getAllCachedDataSpans(nFileId
, aCachedArea
);
2741 SfxObjectShellRef xDocShell
;
2744 xDocShell
= loadSrcDocument(nFileId
, aFilter
);
2746 catch ( const css::uno::Exception
& ) {}
2748 if (!xDocShell
.Is())
2749 // Failed to load the document. Bail out.
2752 ScDocShell
& rDocSh
= static_cast<ScDocShell
&>(*xDocShell
);
2753 ScDocument
* pSrcDoc
= rDocSh
.GetDocument();
2755 // Clear the existing cache, and refill it. Make sure we keep the
2756 // existing cache table instances here.
2757 maRefCache
.clearCacheTables(nFileId
);
2758 RefCacheFiller
aAction(mpDoc
->GetSharedStringPool(), maRefCache
, nFileId
);
2759 aCachedArea
.executeColumnAction(*pSrcDoc
, aAction
);
2761 DocShellMap::iterator it
= maDocShells
.find(nFileId
);
2762 if (it
!= maDocShells
.end())
2764 it
->second
.maShell
->DoClose();
2765 it
->second
.maShell
= xDocShell
;
2766 it
->second
.maLastAccess
= Time(Time::SYSTEM
);
2771 aSrcDoc
.maShell
= xDocShell
;
2772 aSrcDoc
.maLastAccess
= Time(Time::SYSTEM
);
2773 cacheNewDocShell(nFileId
, aSrcDoc
);
2776 // Update all cells containing names from this source document.
2777 refreshAllRefCells(nFileId
);
2779 notifyAllLinkListeners(nFileId
, LINK_MODIFIED
);
2784 void ScExternalRefManager::breakLink(sal_uInt16 nFileId
)
2786 // Turn all formula cells referencing this external document into static
2788 RefCellMap::iterator itrRefs
= maRefCells
.find(nFileId
);
2789 if (itrRefs
!= maRefCells
.end())
2791 // Make a copy because removing the formula cells below will modify
2792 // the original container.
2793 RefCellSet aSet
= itrRefs
->second
;
2794 for_each(aSet
.begin(), aSet
.end(), ConvertFormulaToStatic(mpDoc
));
2795 maRefCells
.erase(nFileId
);
2798 // Remove all named ranges that reference this document.
2800 // Global named ranges.
2801 ScRangeName
* pRanges
= mpDoc
->GetRangeName();
2803 removeRangeNamesBySrcDoc(*pRanges
, nFileId
);
2805 // Sheet-local named ranges.
2806 for (SCTAB i
= 0, n
= mpDoc
->GetTableCount(); i
< n
; ++i
)
2808 pRanges
= mpDoc
->GetRangeName(i
);
2810 removeRangeNamesBySrcDoc(*pRanges
, nFileId
);
2813 clearCache(nFileId
);
2814 lcl_removeByFileId(nFileId
, maDocShells
);
2816 if (maDocShells
.empty())
2817 maSrcDocTimer
.Stop();
2819 LinkedDocMap::iterator itr
= maLinkedDocs
.find(nFileId
);
2820 if (itr
!= maLinkedDocs
.end())
2821 itr
->second
= false;
2823 notifyAllLinkListeners(nFileId
, LINK_BROKEN
);
2826 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId
, const OUString
& rNewFile
, const OUString
& rNewFilter
)
2828 maSrcFiles
[nFileId
].maFileName
= rNewFile
;
2829 maSrcFiles
[nFileId
].maRelativeName
= OUString();
2830 maSrcFiles
[nFileId
].maRealFileName
= OUString();
2831 if (!maSrcFiles
[nFileId
].maFilterName
.equals(rNewFilter
))
2833 // Filter type has changed.
2834 maSrcFiles
[nFileId
].maFilterName
= rNewFilter
;
2835 maSrcFiles
[nFileId
].maFilterOptions
= OUString();
2837 refreshSrcDocument(nFileId
);
2840 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId
, const OUString
& rRelUrl
)
2842 if (nFileId
>= maSrcFiles
.size())
2844 maSrcFiles
[nFileId
].maRelativeName
= rRelUrl
;
2847 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId
, const OUString
& rFilterName
, const OUString
& rOptions
)
2849 if (nFileId
>= maSrcFiles
.size())
2851 maSrcFiles
[nFileId
].maFilterName
= rFilterName
;
2852 maSrcFiles
[nFileId
].maFilterOptions
= rOptions
;
2855 void ScExternalRefManager::clear()
2857 DocShellMap::iterator itrEnd
= maDocShells
.end();
2858 for (DocShellMap::iterator itr
= maDocShells
.begin(); itr
!= itrEnd
; ++itr
)
2859 itr
->second
.maShell
->DoClose();
2861 maDocShells
.clear();
2862 maSrcDocTimer
.Stop();
2865 bool ScExternalRefManager::hasExternalData() const
2867 return !maSrcFiles
.empty();
2870 void ScExternalRefManager::resetSrcFileData(const OUString
& rBaseFileUrl
)
2872 for (vector
<SrcFileData
>::iterator itr
= maSrcFiles
.begin(), itrEnd
= maSrcFiles
.end();
2873 itr
!= itrEnd
; ++itr
)
2875 // Re-generate relative file name from the absolute file name.
2876 OUString aAbsName
= itr
->maRealFileName
;
2877 if (aAbsName
.isEmpty())
2878 aAbsName
= itr
->maFileName
;
2880 itr
->maRelativeName
= URIHelper::simpleNormalizedMakeRelative(
2881 rBaseFileUrl
, aAbsName
);
2885 void ScExternalRefManager::updateAbsAfterLoad()
2887 OUString
aOwn( getOwnDocumentName() );
2888 for (vector
<SrcFileData
>::iterator itr
= maSrcFiles
.begin(), itrEnd
= maSrcFiles
.end();
2889 itr
!= itrEnd
; ++itr
)
2891 // update maFileName to the real file name,
2892 // to be called when the original name is no longer needed (after CompileXML)
2894 itr
->maybeCreateRealFileName( aOwn
);
2895 OUString aReal
= itr
->maRealFileName
;
2896 if (!aReal
.isEmpty())
2897 itr
->maFileName
= aReal
;
2901 void ScExternalRefManager::removeRefCell(ScFormulaCell
* pCell
)
2903 for_each(maRefCells
.begin(), maRefCells
.end(), RemoveFormulaCell(pCell
));
2906 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId
, LinkListener
* pListener
)
2908 LinkListenerMap::iterator itr
= maLinkListeners
.find(nFileId
);
2909 if (itr
== maLinkListeners
.end())
2911 pair
<LinkListenerMap::iterator
, bool> r
= maLinkListeners
.insert(
2912 LinkListenerMap::value_type(nFileId
, LinkListeners()));
2915 OSL_FAIL("insertion of new link listener list failed");
2922 LinkListeners
& rList
= itr
->second
;
2923 rList
.insert(pListener
);
2926 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId
, LinkListener
* pListener
)
2928 LinkListenerMap::iterator itr
= maLinkListeners
.find(nFileId
);
2929 if (itr
== maLinkListeners
.end())
2930 // no listeners for a specified file.
2933 LinkListeners
& rList
= itr
->second
;
2934 rList
.erase(pListener
);
2937 // No more listeners for this file. Remove its entry.
2938 maLinkListeners
.erase(itr
);
2941 void ScExternalRefManager::removeLinkListener(LinkListener
* pListener
)
2943 LinkListenerMap::iterator itr
= maLinkListeners
.begin(), itrEnd
= maLinkListeners
.end();
2944 for (; itr
!= itrEnd
; ++itr
)
2945 itr
->second
.erase(pListener
);
2948 void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId
, LinkUpdateType eType
)
2950 LinkListenerMap::iterator itr
= maLinkListeners
.find(nFileId
);
2951 if (itr
== maLinkListeners
.end())
2952 // no listeners for a specified file.
2955 LinkListeners
& rList
= itr
->second
;
2956 for_each(rList
.begin(), rList
.end(), NotifyLinkListener(nFileId
, eType
));
2959 void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut
)
2961 // To avoid potentially freezing Calc, we close one stale document at a time.
2962 DocShellMap::iterator itr
= maDocShells
.begin(), itrEnd
= maDocShells
.end();
2963 for (; itr
!= itrEnd
; ++itr
)
2965 // in 100th of a second.
2966 sal_Int32 nSinceLastAccess
= (Time( Time::SYSTEM
) - itr
->second
.maLastAccess
).GetTime();
2967 if (nSinceLastAccess
>= nTimeOut
)
2969 // Timed out. Let's close this, and exit the loop.
2970 itr
->second
.maShell
->DoClose();
2971 maDocShells
.erase(itr
);
2976 if (maDocShells
.empty())
2977 maSrcDocTimer
.Stop();
2980 sal_uInt32
ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId
, sal_uInt32 nNumFmt
, const ScDocument
* pSrcDoc
)
2982 NumFmtMap::iterator itr
= maNumFormatMap
.find(nFileId
);
2983 if (itr
== maNumFormatMap
.end())
2985 // Number formatter map is not initialized for this external document.
2986 pair
<NumFmtMap::iterator
, bool> r
= maNumFormatMap
.insert(
2987 NumFmtMap::value_type(nFileId
, SvNumberFormatterMergeMap()));
2990 // insertion failed.
2994 mpDoc
->GetFormatTable()->MergeFormatter( *pSrcDoc
->GetFormatTable());
2995 SvNumberFormatterMergeMap aMap
= mpDoc
->GetFormatTable()->ConvertMergeTableToMap();
2996 itr
->second
.swap(aMap
);
2998 const SvNumberFormatterMergeMap
& rMap
= itr
->second
;
2999 SvNumberFormatterMergeMap::const_iterator itrNumFmt
= rMap
.find(nNumFmt
);
3000 if (itrNumFmt
!= rMap
.end())
3001 // mapped value found.
3002 return itrNumFmt
->second
;
3007 void ScExternalRefManager::transformUnsavedRefToSavedRef( SfxObjectShell
* pShell
)
3009 DocShellMap::iterator itr
= maUnsavedDocShells
.begin();
3010 while( itr
!= maUnsavedDocShells
.end() )
3012 if (&(itr
->second
.maShell
) == pShell
)
3014 // found that the shell is marked as unsaved
3015 OUString aFileURL
= pShell
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DECODE_TO_IURI
);
3016 switchSrcFile(itr
->first
, aFileURL
, OUString());
3017 EndListening(*pShell
);
3018 maUnsavedDocShells
.erase(itr
++);
3023 void ScExternalRefManager::Notify( SfxBroadcaster
&, const SfxHint
& rHint
)
3025 if ( rHint
.ISA( SfxEventHint
) )
3027 sal_uLong nEventId
= ((SfxEventHint
&)rHint
).GetEventId();
3030 case SFX_EVENT_PREPARECLOSEDOC
:
3032 SfxObjectShell
* pObjShell
= static_cast<const SfxEventHint
&>( rHint
).GetObjShell();
3033 ScDocShell
* pDocShell
= static_cast< ScDocShell
* >( pObjShell
);
3034 WarningBox
aBox( pDocShell
->GetActiveDialogParent(), WinBits( WB_OK
),
3035 ScGlobal::GetRscString( STR_CLOSE_WITH_UNSAVED_REFS
) );
3039 case SFX_EVENT_SAVEDOCDONE
:
3040 case SFX_EVENT_SAVEASDOCDONE
:
3042 SfxObjectShell
* pObjShell
= static_cast<const SfxEventHint
&>( rHint
).GetObjShell();
3043 transformUnsavedRefToSavedRef(pObjShell
);
3052 IMPL_LINK(ScExternalRefManager
, TimeOutHdl
, AutoTimer
*, pTimer
)
3054 if (pTimer
== &maSrcDocTimer
)
3055 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN
);
3060 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */