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