LibreOffice Module sc (master) 1
externalrefmgr.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <externalrefmgr.hxx>
21#include <document.hxx>
22#include <token.hxx>
23#include <tokenarray.hxx>
24#include <address.hxx>
25#include <tablink.hxx>
26#include <docsh.hxx>
27#include <scextopt.hxx>
28#include <rangenam.hxx>
29#include <formulacell.hxx>
30#include <utility>
31#include <viewdata.hxx>
32#include <tabvwsh.hxx>
33#include <sc.hrc>
34#include <globstr.hrc>
35#include <scresid.hxx>
36#include <cellvalue.hxx>
37#include <defaultsoptions.hxx>
38#include <scmod.hxx>
39
40#include <o3tl/safeint.hxx>
41#include <osl/file.hxx>
42#include <sfx2/app.hxx>
43#include <sfx2/docfile.hxx>
44#include <sfx2/event.hxx>
45#include <sfx2/fcontnr.hxx>
46#include <sfx2/objsh.hxx>
47#include <svl/itemset.hxx>
48#include <svl/numformat.hxx>
49#include <svl/stritem.hxx>
50#include <svl/urihelper.hxx>
52#include <sfx2/linkmgr.hxx>
53#include <tools/urlobj.hxx>
57#include <vcl/svapp.hxx>
58#include <vcl/weld.hxx>
59#include <stringutil.hxx>
60#include <scmatrix.hxx>
61#include <columnspanset.hxx>
62#include <column.hxx>
63#include <com/sun/star/document/MacroExecMode.hpp>
64#include <com/sun/star/document/UpdateDocMode.hpp>
65#include <sal/log.hxx>
66
67#include <memory>
68#include <algorithm>
69
70using ::std::unique_ptr;
71using ::com::sun::star::uno::Any;
72using ::std::vector;
73using ::std::find_if;
74using ::std::for_each;
75using ::std::distance;
76using ::std::pair;
77using namespace formula;
78
79#define SRCDOC_LIFE_SPAN 30000 // 5 minutes (in 100th of a sec)
80#define SRCDOC_SCAN_INTERVAL 1000*30 // every 30 seconds (in msec)
81
82namespace {
83
84class TabNameSearchPredicate
85{
86public:
87 explicit TabNameSearchPredicate(const OUString& rSearchName) :
88 maSearchName(ScGlobal::getCharClass().uppercase(rSearchName))
89 {
90 }
91
92 bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const
93 {
94 // Ok, I'm doing case insensitive search here.
95 return rTabNameSet.maUpperName == maSearchName;
96 }
97
98private:
99 OUString maSearchName;
100};
101
102class FindSrcFileByName
103{
104public:
105 explicit FindSrcFileByName(const OUString& rMatchName) :
106 mrMatchName(rMatchName)
107 {
108 }
109
110 bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const
111 {
112 return rSrcData.maFileName == mrMatchName;
113 }
114
115private:
116 const OUString& mrMatchName;
117};
118
119class NotifyLinkListener
120{
121public:
122 NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) :
123 mnFileId(nFileId), meType(eType) {}
124
125 void operator() (ScExternalRefManager::LinkListener* p) const
126 {
127 p->notify(mnFileId, meType);
128 }
129private:
130 sal_uInt16 mnFileId;
132};
133
134struct UpdateFormulaCell
135{
136 void operator() (ScFormulaCell* pCell) const
137 {
138 // Check to make sure the cell really contains svExternal*.
139 // External names, external cell and range references all have a
140 // token of svExternal*. Additionally check for INDIRECT() that can be
141 // called with any constructed URI string.
142 ScTokenArray* pCode = pCell->GetCode();
143 if (!pCode->HasExternalRef() && !pCode->HasOpCode(ocIndirect))
144 return;
145
146 if (pCode->GetCodeError() != FormulaError::NONE)
147 {
148 // Clear the error code, or a cell with error won't get re-compiled.
149 pCode->SetCodeError(FormulaError::NONE);
150 pCell->SetCompile(true);
151 pCell->CompileTokenArray();
152 }
153
154 pCell->SetDirty();
155 }
156};
157
158class RemoveFormulaCell
159{
160public:
161 explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {}
162 void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const
163 {
164 r.second.erase(mpCell);
165 }
166private:
168};
169
170class ConvertFormulaToStatic
171{
172public:
173 explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {}
174 void operator() (ScFormulaCell* pCell) const
175 {
176 ScAddress aPos = pCell->aPos;
177
178 // We don't check for empty cells because empty external cells are
179 // treated as having a value of 0.
180
181 if (pCell->IsValue())
182 {
183 // Turn this into value cell.
184 mpDoc->SetValue(aPos, pCell->GetValue());
185 }
186 else
187 {
188 // string cell otherwise.
189 ScSetStringParam aParam;
190 aParam.setTextInput();
191 mpDoc->SetString(aPos, pCell->GetString().getString(), &aParam);
192 }
193 }
194private:
195 ScDocument* mpDoc;
196};
197
202bool hasRefsToSrcDoc(ScRangeData& rData, sal_uInt16 nFileId)
203{
204 ScTokenArray* pArray = rData.GetCode();
205 if (!pArray)
206 return false;
207
210 for (; p; p = aIter.GetNextReference())
211 {
212 if (!p->IsExternalRef())
213 continue;
214
215 if (p->GetIndex() == nFileId)
216 return true;
217 }
218 return false;
219}
220
221class EraseRangeByIterator
222{
223 ScRangeName& mrRanges;
224public:
225 explicit EraseRangeByIterator(ScRangeName& rRanges) : mrRanges(rRanges) {}
226 void operator() (const ScRangeName::const_iterator& itr)
227 {
228 mrRanges.erase(itr);
229 }
230};
231
236void removeRangeNamesBySrcDoc(ScRangeName& rRanges, sal_uInt16 nFileId)
237{
238 ScRangeName::const_iterator itr = rRanges.begin(), itrEnd = rRanges.end();
239 vector<ScRangeName::const_iterator> v;
240 for (; itr != itrEnd; ++itr)
241 {
242 if (hasRefsToSrcDoc(*itr->second, nFileId))
243 v.push_back(itr);
244 }
245 for_each(v.begin(), v.end(), EraseRangeByIterator(rRanges));
246}
247
248}
249
251 : mbReferenced( true )
252 // Prevent accidental data loss due to lack of knowledge.
253{
254}
255
257{
258}
259
261{
262 maRows.clear();
263 maCachedRanges.RemoveAll();
264 mbReferenced = true;
265}
266
268{
269 mbReferenced = bReferenced;
270}
271
273{
274 return mbReferenced;
275}
276
277void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef const & pToken, sal_uLong nFmtIndex, bool bSetCacheRange)
278{
279 using ::std::pair;
280 RowsDataType::iterator itrRow = maRows.find(nRow);
281 if (itrRow == maRows.end())
282 {
283 // This row does not exist yet.
284 pair<RowsDataType::iterator, bool> res = maRows.emplace(
285 nRow, RowDataType());
286
287 if (!res.second)
288 return;
289
290 itrRow = res.first;
291 }
292
293 // Insert this token into the specified column location. I don't need to
294 // check for existing data. Just overwrite it.
295 RowDataType& rRow = itrRow->second;
297 aCell.mxToken = pToken;
298 aCell.mnFmtIndex = nFmtIndex;
299 rRow.emplace(nCol, aCell);
300 if (bSetCacheRange)
301 setCachedCell(nCol, nRow);
302}
303
305{
306 RowsDataType::const_iterator itrTable = maRows.find(nRow);
307 if (itrTable == maRows.end())
308 {
309 // this table doesn't have the specified row.
310 return getEmptyOrNullToken(nCol, nRow);
311 }
312
313 const RowDataType& rRowData = itrTable->second;
314 RowDataType::const_iterator itrRow = rRowData.find(nCol);
315 if (itrRow == rRowData.end())
316 {
317 // this row doesn't have the specified column.
318 return getEmptyOrNullToken(nCol, nRow);
319 }
320
321 const Cell& rCell = itrRow->second;
322 if (pnFmtIndex)
323 *pnFmtIndex = rCell.mnFmtIndex;
324
325 return rCell.mxToken;
326}
327
329{
330 RowsDataType::const_iterator itrRow = maRows.find(nRow);
331 return itrRow != maRows.end();
332}
333
334template< typename P >
335void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, P predicate) const
336{
337 vector<SCROW> aRows;
338 aRows.reserve(maRows.size());
339 for (const auto& rEntry : maRows)
340 if (predicate(rEntry))
341 aRows.push_back(rEntry.first);
342
343 // hash map is not ordered, so we need to explicitly sort it.
344 ::std::sort(aRows.begin(), aRows.end());
345 rRows.swap(aRows);
346}
347
348void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const
349{
350 getAllRows(rRows,
351 [nLow, nHigh](std::pair<SCROW, RowDataType> rEntry) { return (nLow <= rEntry.first && rEntry.first <= nHigh); });
352}
353
354void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows) const
355{
356 getAllRows(rRows, [](std::pair<SCROW, RowDataType>) { return true; } );
357}
358
359::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const
360{
361 ::std::pair< SCROW, SCROW > aRange( 0, 0 );
362 if( !maRows.empty() )
363 {
364 // iterate over entire container (hash map is not sorted by key)
365 auto itMinMax = std::minmax_element(maRows.begin(), maRows.end(),
366 [](const RowsDataType::value_type& a, const RowsDataType::value_type& b) { return a.first < b.first; });
367 aRange.first = itMinMax.first->first;
368 aRange.second = itMinMax.second->first + 1;
369 }
370 return aRange;
371}
372
373template< typename P >
374void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, P predicate) const
375{
376 RowsDataType::const_iterator itrRow = maRows.find(nRow);
377 if (itrRow == maRows.end())
378 // this table doesn't have the specified row.
379 return;
380
381 const RowDataType& rRowData = itrRow->second;
382 vector<SCCOL> aCols;
383 aCols.reserve(rRowData.size());
384 for (const auto& rCol : rRowData)
385 if (predicate(rCol))
386 aCols.push_back(rCol.first);
387
388 // hash map is not ordered, so we need to explicitly sort it.
389 ::std::sort(aCols.begin(), aCols.end());
390 rCols.swap(aCols);
391}
392
393void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const
394{
395 getAllCols(nRow, rCols,
396 [nLow, nHigh](std::pair<SCCOL, Cell> rCol) { return nLow <= rCol.first && rCol.first <= nHigh; } );
397}
398
399void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols) const
400{
401 getAllCols(nRow, rCols, [](std::pair<SCCOL, Cell>) { return true; } );
402}
403
404::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const
405{
406 ::std::pair< SCCOL, SCCOL > aRange( 0, 0 );
407
408 RowsDataType::const_iterator itrRow = maRows.find( nRow );
409 if (itrRow == maRows.end())
410 // this table doesn't have the specified row.
411 return aRange;
412
413 const RowDataType& rRowData = itrRow->second;
414 if( !rRowData.empty() )
415 {
416 // iterate over entire container (hash map is not sorted by key)
417 auto itMinMax = std::minmax_element(rRowData.begin(), rRowData.end(),
418 [](const RowDataType::value_type& a, const RowDataType::value_type& b) { return a.first < b.first; });
419 aRange.first = itMinMax.first->first;
420 aRange.second = itMinMax.second->first + 1;
421 }
422 return aRange;
423}
424
425void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
426{
427 for (const auto& rRow : maRows)
428 {
429 const RowDataType& rRowData = rRow.second;
430 for (const auto& rCol : rRowData)
431 {
432 const Cell& rCell = rCol.second;
433 rNumFmts.push_back(rCell.mnFmtIndex);
434 }
435 }
436}
437
438bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
439{
440 return maCachedRanges.Contains(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
441}
442
444{
445 setCachedCellRange(nCol, nRow, nCol, nRow);
446}
447
449{
450 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
451 maCachedRanges.Join(aRange);
452}
453
455{
456 setCachedCellRange(0, 0, MAXCOL, MAXROW);
457}
458
460{
461 return maCachedRanges.Contains(ScRange(nCol, nRow, 0, nCol, nRow, 0));
462}
463
465 SCCOL nCol, SCROW nRow) const
466{
467 if (isInCachedRanges(nCol, nRow))
468 {
469 TokenRef p(new ScEmptyCellToken(false, false));
470 return p;
471 }
472 return TokenRef();
473}
474
475ScExternalRefCache::TableName::TableName(OUString aUpper, OUString aReal) :
476 maUpperName(std::move(aUpper)), maRealName(std::move(aReal))
477{
478}
479
481 mbIsSet(false), mnType(SvNumFormatType::ALL), mnIndex(0)
482{
483}
484
486 : mrDoc(rDoc)
487{
488}
489
491
492const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
493{
494 std::unique_lock aGuard(maMtxDocs);
495
496 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
497 if (itrDoc == maDocs.end())
498 {
499 // specified document is not cached.
500 return nullptr;
501 }
502
503 const DocItem& rDoc = itrDoc->second;
504 TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
505 if (itrTabId == rDoc.maTableNameIndex.end())
506 {
507 // the specified table is not in cache.
508 return nullptr;
509 }
510
511 return &rDoc.maTableNames[itrTabId->second].maRealName;
512}
513
514const OUString* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
515{
516 std::unique_lock aGuard(maMtxDocs);
517
518 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
519 if (itrDoc == maDocs.end())
520 {
521 // specified document is not cached.
522 return nullptr;
523 }
524
525 const DocItem& rDoc = itrDoc->second;
526 NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find(
527 ScGlobal::getCharClass().uppercase(rRangeName));
528 if (itr == rDoc.maRealRangeNameMap.end())
529 // range name not found.
530 return nullptr;
531
532 return &itr->second;
533}
534
536 sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex)
537{
538 std::unique_lock aGuard(maMtxDocs);
539
540 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
541 if (itrDoc == maDocs.end())
542 {
543 // specified document is not cached.
544 return TokenRef();
545 }
546
547 const DocItem& rDoc = itrDoc->second;
548 TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
549 if (itrTabId == rDoc.maTableNameIndex.end())
550 {
551 // the specified table is not in cache.
552 return TokenRef();
553 }
554
555 const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
556 if (!pTableData)
557 {
558 // the table data is not instantiated yet.
559 return TokenRef();
560 }
561
562 return pTableData->getCell(nCol, nRow, pnFmtIndex);
563}
564
566 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange)
567{
568 std::unique_lock aGuard(maMtxDocs);
569
570 DocDataType::iterator itrDoc = maDocs.find(nFileId);
571 if (itrDoc == maDocs.end())
572 // specified document is not cached.
573 return TokenArrayRef();
574
575 DocItem& rDoc = itrDoc->second;
576
577 TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
578 if (itrTabId == rDoc.maTableNameIndex.end())
579 // the specified table is not in cache.
580 return TokenArrayRef();
581
582 const ScAddress& s = rRange.aStart;
583 const ScAddress& e = rRange.aEnd;
584
585 const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
586 const SCCOL nCol1 = s.Col(), nCol2 = e.Col();
587 const SCROW nRow1 = s.Row(), nRow2 = e.Row();
588
589 // Make sure I have all the tables cached.
590 size_t nTabFirstId = itrTabId->second;
591 size_t nTabLastId = nTabFirstId + nTab2 - nTab1;
592 if (nTabLastId >= rDoc.maTables.size())
593 // not all tables are cached.
594 return TokenArrayRef();
595
596 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
597
598 RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
599 if (itrRange != rDoc.maRangeArrays.end())
600 // Cache hit!
601 return itrRange->second;
602
603 std::unique_ptr<ScRange> pNewRange;
604 TokenArrayRef pArray;
605 bool bFirstTab = true;
606 for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
607 {
608 TableTypeRef pTab = rDoc.maTables[nTab];
609 if (!pTab)
610 return TokenArrayRef();
611
612 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
613 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
614
615 if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2))
616 {
617 // specified range is not entirely within cached ranges.
618 return TokenArrayRef();
619 }
620
621 SCSIZE nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1);
622 SCSIZE nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
623 ScMatrixRef xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
624
625 // Needed in shrink and fill.
626 vector<SCROW> aRows;
627 pTab->getAllRows(aRows, nDataRow1, nDataRow2);
628 bool bFill = true;
629
630 // Check if size could be allocated and if not skip the fill, there's
631 // one error element instead. But retry first with the actual data area
632 // if that is smaller than the original range, which works for most
633 // functions just not some that operate/compare with the original size
634 // and expect empty values in non-data areas.
635 // Restrict this though to ranges of entire columns or rows, other
636 // ranges might be on purpose. (Other special cases to handle?)
637 /* TODO: sparse matrix could help */
638 SCSIZE nMatCols, nMatRows;
639 xMat->GetDimensions( nMatCols, nMatRows);
640 if (nMatCols != nMatrixColumns || nMatRows != nMatrixRows)
641 {
642 bFill = false;
643 if (aRows.empty())
644 {
645 // There's no data at all. Set the one matrix element to empty
646 // for column-repeated and row-repeated access.
647 xMat->PutEmpty(0,0);
648 }
649 else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW))
650 {
651 nDataRow1 = aRows.front();
652 nDataRow2 = aRows.back();
653 SCCOL nMinCol = std::numeric_limits<SCCOL>::max();
654 SCCOL nMaxCol = std::numeric_limits<SCCOL>::min();
655 for (const auto& rRow : aRows)
656 {
657 vector<SCCOL> aCols;
658 pTab->getAllCols(rRow, aCols, nDataCol1, nDataCol2);
659 if (!aCols.empty())
660 {
661 nMinCol = std::min( nMinCol, aCols.front());
662 nMaxCol = std::max( nMaxCol, aCols.back());
663 }
664 }
665
666 if (nMinCol <= nMaxCol && ((o3tl::make_unsigned(nMaxCol-nMinCol+1) < nMatrixColumns) ||
667 (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows)))
668 {
669 nMatrixColumns = static_cast<SCSIZE>(nMaxCol-nMinCol+1);
670 nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
671 xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
672 xMat->GetDimensions( nMatCols, nMatRows);
673 if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
674 {
675 nDataCol1 = nMinCol;
676 nDataCol2 = nMaxCol;
677 bFill = true;
678 }
679 }
680 }
681 }
682
683 if (bFill)
684 {
685 // Only fill non-empty cells, for better performance.
686 for (SCROW nRow : aRows)
687 {
688 vector<SCCOL> aCols;
689 pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2);
690 for (SCCOL nCol : aCols)
691 {
692 TokenRef pToken = pTab->getCell(nCol, nRow);
693 if (!pToken)
694 // This should never happen!
695 return TokenArrayRef();
696
697 SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1;
698 switch (pToken->GetType())
699 {
700 case svDouble:
701 xMat->PutDouble(pToken->GetDouble(), nC, nR);
702 break;
703 case svString:
704 xMat->PutString(pToken->GetString(), nC, nR);
705 break;
706 default:
707 ;
708 }
709 }
710 }
711
712 if (!bFirstTab)
713 pArray->AddOpCode(ocSep);
714
715 ScMatrixToken aToken(std::move(xMat));
716 if (!pArray)
717 pArray = std::make_shared<ScTokenArray>(mrDoc);
718 pArray->AddToken(aToken);
719
720 bFirstTab = false;
721
722 if (!pNewRange)
723 pNewRange.reset(new ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
724 else
725 pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
726 }
727 }
728
729 rDoc.maRangeArrays.emplace(aCacheRange, pArray);
730 if (pNewRange && *pNewRange != aCacheRange)
731 rDoc.maRangeArrays.emplace(*pNewRange, pArray);
732
733 return pArray;
734}
735
737{
738 std::unique_lock aGuard(maMtxDocs);
739
740 DocItem* pDoc = getDocItem(aGuard, nFileId);
741 if (!pDoc)
742 return TokenArrayRef();
743
744 RangeNameMap& rMap = pDoc->maRangeNames;
745 RangeNameMap::const_iterator itr = rMap.find(
746 ScGlobal::getCharClass().uppercase(rName));
747 if (itr == rMap.end())
748 return TokenArrayRef();
749
750 return itr->second;
751}
752
753void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, TokenArrayRef pArray)
754{
755 std::unique_lock aGuard(maMtxDocs);
756
757 DocItem* pDoc = getDocItem(aGuard, nFileId);
758 if (!pDoc)
759 return;
760
761 OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
762 RangeNameMap& rMap = pDoc->maRangeNames;
763 rMap.emplace(aUpperName, pArray);
764 pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
765}
766
767bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId, const OUString& rName) const
768{
769 std::unique_lock aGuard(maMtxDocs);
770
771 DocItem* pDoc = getDocItem(aGuard, nFileId);
772 if (!pDoc)
773 return false;
774
775 OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
776 const RangeNameMap& rMap = pDoc->maRangeNames;
777 return rMap.count(aUpperName) > 0;
778}
779
780void ScExternalRefCache::setRangeName(sal_uInt16 nFileId, const OUString& rName)
781{
782 std::unique_lock aGuard(maMtxDocs);
783
784 DocItem* pDoc = getDocItem(aGuard, nFileId);
785 if (!pDoc)
786 return;
787
788 OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
789 pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
790}
791
792void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow,
793 TokenRef const & pToken, sal_uLong nFmtIndex)
794{
795 if (!isDocInitialized(nFileId))
796 return;
797
798 using ::std::pair;
799 DocItem* pDocItem = getDocItem(nFileId);
800 if (!pDocItem)
801 return;
802
803 DocItem& rDoc = *pDocItem;
804
805 // See if the table by this name already exists.
806 TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rTabName);
807 if (itrTabName == rDoc.maTableNameIndex.end())
808 // Table not found. Maybe the table name or the file id is wrong ???
809 return;
810
811 TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
812 if (!pTableData)
813 pTableData = std::make_shared<Table>();
814
815 pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
816 pTableData->setCachedCell(nCol, nRow);
817}
818
819void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
820 const TokenArrayRef& pArray)
821{
822 using ::std::pair;
823 if (rData.empty() || !isDocInitialized(nFileId))
824 // nothing to cache
825 return;
826
827 // First, get the document item for the given file ID.
828 DocItem* pDocItem = getDocItem(nFileId);
829 if (!pDocItem)
830 return;
831
832 DocItem& rDoc = *pDocItem;
833
834 // Now, find the table position of the first table to cache.
835 const OUString& rFirstTabName = rData.front().maTableName;
836 TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rFirstTabName);
837 if (itrTabName == rDoc.maTableNameIndex.end())
838 {
839 // table index not found.
840 return;
841 }
842
843 size_t nTabFirstId = itrTabName->second;
844 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
845 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
846 size_t i = nTabFirstId;
847 for (const auto& rItem : rData)
848 {
849 TableTypeRef& pTabData = rDoc.maTables[i];
850 if (!pTabData)
851 pTabData = std::make_shared<Table>();
852
853 const ScMatrixRef& pMat = rItem.mpRangeData;
854 SCSIZE nMatCols, nMatRows;
855 pMat->GetDimensions( nMatCols, nMatRows);
856 if (nMatCols > o3tl::make_unsigned(nCol2 - nCol1) && nMatRows > o3tl::make_unsigned(nRow2 - nRow1))
857 {
858 ScMatrix::DoubleOpFunction aDoubleFunc = [=](size_t row, size_t col, double val) -> void
859 {
860 pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val), 0, false);
861 };
862 ScMatrix::BoolOpFunction aBoolFunc = [=](size_t row, size_t col, bool val) -> void
863 {
864 pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val ? 1.0 : 0.0), 0, false);
865 };
866 ScMatrix::StringOpFunction aStringFunc = [=](size_t row, size_t col, svl::SharedString val) -> void
867 {
868 pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaStringToken(std::move(val)), 0, false);
869 };
870 ScMatrix::EmptyOpFunction aEmptyFunc = [](size_t /*row*/, size_t /*col*/) -> void
871 {
872 // Nothing. Empty cell.
873 };
874 pMat->ExecuteOperation(std::pair<size_t, size_t>(0, 0),
875 std::pair<size_t, size_t>(nRow2-nRow1, nCol2-nCol1),
876 std::move(aDoubleFunc), std::move(aBoolFunc), std::move(aStringFunc), std::move(aEmptyFunc));
877 // Mark the whole range 'cached'.
878 pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2);
879 }
880 else
881 {
882 // This may happen due to a matrix not been allocated earlier, in
883 // which case it should have exactly one error element.
884 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix size mismatch");
885 if (nMatCols != 1 || nMatRows != 1)
886 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - not a one element matrix");
887 else
888 {
889 FormulaError nErr = GetDoubleErrorValue( pMat->GetDouble(0,0));
890 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix error value is " << static_cast<int>(nErr) <<
891 (nErr == FormulaError::MatrixSize ? ", ok" : ", not ok"));
892 }
893 }
894 ++i;
895 }
896
897 size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab();
898 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
899
900 rDoc.maRangeArrays.emplace(aCacheRange, pArray);
901}
902
904{
905 DocItem* pDoc = getDocItem(nFileId);
906 if (!pDoc)
907 return false;
908
909 return pDoc->mbInitFromSource;
910}
911
912static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex)
913{
914 ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
915 if (itr == rMap.end())
916 return false;
917
918 rIndex = itr->second;
919 return true;
920}
921
922bool ScExternalRefCache::DocItem::getTableDataIndex( const OUString& rTabName, size_t& rIndex ) const
923{
924 ScExternalRefCache::TableNameIndexMap::const_iterator itr = findTableNameIndex(rTabName);
925 if (itr == maTableNameIndex.end())
926 return false;
927
928 rIndex = itr->second;
929 return true;
930}
931
932namespace {
933OUString getFirstSheetName()
934{
935 // Get Custom prefix.
936 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
937 // Form sheet name identical to the first generated sheet name when
938 // creating an internal document, e.g. 'Sheet1'.
939 return rOpt.GetInitTabPrefix() + "1";
940}
941}
942
943void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames,
944 const OUString& rBaseName)
945{
946 DocItem* pDoc = getDocItem(nFileId);
947 if (!pDoc)
948 return;
949
950 size_t n = rTabNames.size();
951
952 // table name list - the list must include all table names in the source
953 // document and only to be populated when loading the source document, not
954 // when loading cached data from, say, Excel XCT/CRN records.
955 vector<TableName> aNewTabNames;
956 aNewTabNames.reserve(n);
957 for (const auto& rTabName : rTabNames)
958 {
959 TableName aNameItem(ScGlobal::getCharClass().uppercase(rTabName), rTabName);
960 aNewTabNames.push_back(aNameItem);
961 }
962 pDoc->maTableNames.swap(aNewTabNames);
963
964 // data tables - preserve any existing data that may have been set during
965 // file import.
966 vector<TableTypeRef> aNewTables(n);
967 for (size_t i = 0; i < n; ++i)
968 {
969 size_t nIndex;
970 if (lcl_getStrictTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
971 {
972 aNewTables[i] = pDoc->maTables[nIndex];
973 }
974 }
975 pDoc->maTables.swap(aNewTables);
976
977 // name index map
978 TableNameIndexMap aNewNameIndex;
979 for (size_t i = 0; i < n; ++i)
980 aNewNameIndex.emplace(pDoc->maTableNames[i].maUpperName, i);
981 pDoc->maTableNameIndex.swap(aNewNameIndex);
982
983 // Setup name for Sheet1 vs base name to be able to load documents
984 // that store the base name as table name, or vice versa.
985 pDoc->maSingleTableNameAlias.clear();
986 if (!rBaseName.isEmpty() && pDoc->maTableNames.size() == 1)
987 {
988 OUString aSheetName = getFirstSheetName();
989 // If the one and only table name matches exactly, carry on the base
990 // file name for further alias use. If instead the table name matches
991 // the base name, carry on the sheet name as alias.
992 if (ScGlobal::GetTransliteration().isEqual( pDoc->maTableNames[0].maRealName, aSheetName))
993 pDoc->maSingleTableNameAlias = rBaseName;
994 else if (ScGlobal::GetTransliteration().isEqual( pDoc->maTableNames[0].maRealName, rBaseName))
995 pDoc->maSingleTableNameAlias = aSheetName;
996 }
997
998 pDoc->mbInitFromSource = true;
999}
1000
1001ScExternalRefCache::TableNameIndexMap::const_iterator ScExternalRefCache::DocItem::findTableNameIndex(
1002 const OUString& rTabName ) const
1003{
1004 const OUString aTabNameUpper = ScGlobal::getCharClass().uppercase( rTabName);
1005 TableNameIndexMap::const_iterator itrTabName = maTableNameIndex.find( aTabNameUpper);
1006 if (itrTabName != maTableNameIndex.end())
1007 return itrTabName;
1008
1009 // Since some time for external references to CSV files the base name is
1010 // used as sheet name instead of Sheet1, check if we can resolve that.
1011 // Also helps users that got accustomed to one or the other way.
1012 if (maSingleTableNameAlias.isEmpty() || maTableNameIndex.size() != 1)
1013 return itrTabName;
1014
1015 // maSingleTableNameAlias has been set up only if the original file loaded
1016 // had exactly one sheet and internal sheet name was Sheet1 or localized or
1017 // customized equivalent, or base name.
1018 if (aTabNameUpper == ScGlobal::getCharClass().uppercase( maSingleTableNameAlias))
1019 return maTableNameIndex.begin();
1020
1021 return itrTabName;
1022}
1023
1025{
1026 if (maSingleTableNameAlias.isEmpty() || maTableNames.size() != 1)
1027 return false;
1028 if (ScGlobal::GetTransliteration().isEqual( rTabName, maTableNames[0].maRealName))
1029 {
1030 rTabName = maSingleTableNameAlias;
1031 return true;
1032 }
1033 if (ScGlobal::GetTransliteration().isEqual( rTabName, maSingleTableNameAlias))
1034 {
1035 rTabName = maTableNames[0].maRealName;
1036 return true;
1037 }
1038 return false;
1039}
1040
1041bool ScExternalRefCache::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
1042 sal_uInt16 nFileId ) const
1043{
1044 bool bFound = rSrcDoc.GetTable( rTabName, rTab);
1045 if (!bFound)
1046 {
1047 // Check the one table alias alternative.
1048 const DocItem* pDoc = getDocItem( nFileId );
1049 if (pDoc)
1050 {
1051 OUString aTabName( rTabName);
1052 if (pDoc->getSingleTableNameAlternative( aTabName))
1053 bFound = rSrcDoc.GetTable( aTabName, rTab);
1054 }
1055 }
1056 return bFound;
1057}
1058
1059OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
1060{
1061 if( DocItem* pDoc = getDocItem( nFileId ) )
1062 if( nCacheId < pDoc->maTableNames.size() )
1063 return pDoc->maTableNames[ nCacheId ].maRealName;
1064 return OUString();
1065}
1066
1067void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1068{
1069 rTabNames.clear();
1070 DocItem* pDoc = getDocItem(nFileId);
1071 if (!pDoc)
1072 return;
1073
1074 size_t n = pDoc->maTableNames.size();
1075 rTabNames.reserve(n);
1076 for (const auto& rTableName : pDoc->maTableNames)
1077 rTabNames.push_back(rTableName.maRealName);
1078}
1079
1080SCTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1081{
1082 DocItem* pDoc = getDocItem(nFileId);
1083 if (!pDoc)
1084 return -1;
1085
1086 vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
1087 vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
1088
1089 vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
1090 TabNameSearchPredicate( rStartTabName));
1091 if (itrStartTab == itrEnd)
1092 return -1;
1093
1094 vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
1095 TabNameSearchPredicate( rEndTabName));
1096 if (itrEndTab == itrEnd)
1097 return 0;
1098
1099 size_t nStartDist = ::std::distance( itrBeg, itrStartTab);
1100 size_t nEndDist = ::std::distance( itrBeg, itrEndTab);
1101 return nStartDist <= nEndDist ? static_cast<SCTAB>(nEndDist - nStartDist + 1) : -static_cast<SCTAB>(nStartDist - nEndDist + 1);
1102}
1103
1104void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
1105{
1106 std::unique_lock aGuard(maMtxDocs);
1107
1108 using ::std::sort;
1109 using ::std::unique;
1110
1111 vector<sal_uInt32> aNumFmts;
1112 for (const auto& rEntry : maDocs)
1113 {
1114 const vector<TableTypeRef>& rTables = rEntry.second.maTables;
1115 for (const TableTypeRef& pTab : rTables)
1116 {
1117 if (!pTab)
1118 continue;
1119
1120 pTab->getAllNumberFormats(aNumFmts);
1121 }
1122 }
1123
1124 // remove duplicates.
1125 sort(aNumFmts.begin(), aNumFmts.end());
1126 aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
1127 rNumFmts.swap(aNumFmts);
1128}
1129
1131{
1132 DocItem* pDocItem = getDocItem(nFileId);
1133 if (!pDocItem)
1135
1136 for (auto& rxTab : pDocItem->maTables)
1137 {
1138 if (rxTab)
1139 rxTab->setReferenced(true);
1140 }
1141 addCacheDocToReferenced( nFileId);
1143}
1144
1145bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1146{
1147 DocItem* pDoc = getDocItem(nFileId);
1148 if (pDoc)
1149 {
1150 size_t nIndex = 0;
1151 if (pDoc->getTableDataIndex( rTabName, nIndex))
1152 {
1153 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
1154 for (size_t i = nIndex; i < nStop; ++i)
1155 {
1156 TableTypeRef pTab = pDoc->maTables[i];
1157 if (pTab)
1158 {
1159 if (!pTab->isReferenced())
1160 {
1161 pTab->setReferenced(true);
1162 addCacheTableToReferenced( nFileId, i);
1163 }
1164 }
1165 }
1166 }
1167 }
1169}
1170
1172{
1173 std::unique_lock aGuard(maMtxDocs);
1174
1175 if (bReferenced)
1176 {
1178 for (auto& rEntry : maDocs)
1179 {
1180 ScExternalRefCache::DocItem& rDocItem = rEntry.second;
1181 for (auto& rxTab : rDocItem.maTables)
1182 {
1183 if (rxTab)
1184 rxTab->setReferenced(true);
1185 }
1186 }
1187 }
1188 else
1189 {
1190 size_t nDocs = 0;
1191 auto itrMax = std::max_element(maDocs.begin(), maDocs.end(),
1192 [](const DocDataType::value_type& a, const DocDataType::value_type& b) { return a.first < b.first; });
1193 if (itrMax != maDocs.end())
1194 nDocs = itrMax->first + 1;
1195 maReferenced.reset( nDocs);
1196
1197 for (auto& [nFileId, rDocItem] : maDocs)
1198 {
1199 size_t nTables = rDocItem.maTables.size();
1200 ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId];
1201 // All referenced => non-existing tables evaluate as completed.
1202 rDocReferenced.maTables.resize( nTables, true);
1203 for (size_t i=0; i < nTables; ++i)
1204 {
1205 TableTypeRef & xTab = rDocItem.maTables[i];
1206 if (xTab)
1207 {
1208 xTab->setReferenced(false);
1209 rDocReferenced.maTables[i] = false;
1210 rDocReferenced.mbAllTablesReferenced = false;
1211 // An addCacheTableToReferenced() actually may have
1212 // resulted in mbAllReferenced been set. Clear it.
1214 }
1215 }
1216 }
1217 }
1218}
1219
1220void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
1221{
1222 if (nFileId >= maReferenced.maDocs.size())
1223 return;
1224
1225 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1226 size_t nTables = rTables.size();
1227 if (nIndex >= nTables)
1228 return;
1229
1230 if (!rTables[nIndex])
1231 {
1232 rTables[nIndex] = true;
1233 size_t i = 0;
1234 while (i < nTables && rTables[i])
1235 ++i;
1236 if (i == nTables)
1237 {
1238 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1240 }
1241 }
1242}
1243
1245{
1246 if (nFileId >= maReferenced.maDocs.size())
1247 return;
1248
1249 if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
1250 {
1251 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1252 size_t nSize = rTables.size();
1253 for (size_t i=0; i < nSize; ++i)
1254 rTables[i] = true;
1255 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1257 }
1258}
1259
1260void ScExternalRefCache::getAllCachedDataSpans( const ScDocument& rSrcDoc, sal_uInt16 nFileId, sc::ColumnSpanSet& rSet ) const
1261{
1262 const DocItem* pDocItem = getDocItem(nFileId);
1263 if (!pDocItem)
1264 // This document is not cached.
1265 return;
1266
1267 const std::vector<TableTypeRef>& rTables = pDocItem->maTables;
1268 for (size_t nTab = 0, nTabCount = rTables.size(); nTab < nTabCount; ++nTab)
1269 {
1270 TableTypeRef pTab = rTables[nTab];
1271 if (!pTab)
1272 continue;
1273
1274 std::vector<SCROW> aRows;
1275 pTab->getAllRows(aRows);
1276 for (SCROW nRow : aRows)
1277 {
1278 std::vector<SCCOL> aCols;
1279 pTab->getAllCols(nRow, aCols);
1280 for (SCCOL nCol : aCols)
1281 {
1282 rSet.set(rSrcDoc, nTab, nCol, nRow, true);
1283 }
1284 }
1285 }
1286}
1287
1289 mbAllReferenced(false)
1290{
1291 reset(0);
1292}
1293
1295{
1296 if (nDocs)
1297 {
1298 mbAllReferenced = false;
1299 DocReferencedVec aRefs( nDocs);
1300 maDocs.swap( aRefs);
1301 }
1302 else
1303 {
1304 mbAllReferenced = true;
1305 DocReferencedVec aRefs;
1306 maDocs.swap( aRefs);
1307 }
1308}
1309
1311{
1312 if (std::all_of(maDocs.begin(), maDocs.end(), [](const DocReferenced& rDoc) { return rDoc.mbAllTablesReferenced; }))
1313 mbAllReferenced = true;
1314}
1315
1317{
1318 DocItem* pDoc = getDocItem(nFileId);
1319 if (!pDoc || nTabIndex >= pDoc->maTables.size())
1320 return TableTypeRef();
1321
1322 return pDoc->maTables[nTabIndex];
1323}
1324
1325ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName,
1326 bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
1327{
1328 // In API, the index is transported as cached sheet ID of type sal_Int32 in
1329 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
1330 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
1331 // being 0xffffffff
1332 const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
1333
1334 DocItem* pDoc = getDocItem(nFileId);
1335 if (!pDoc)
1336 {
1337 if (pnIndex) *pnIndex = nNotAvailable;
1338 return TableTypeRef();
1339 }
1340
1341 DocItem& rDoc = *pDoc;
1342
1343 size_t nIndex;
1344 if (rDoc.getTableDataIndex(rTabName, nIndex))
1345 {
1346 // specified table found.
1347 if( pnIndex ) *pnIndex = nIndex;
1348 if (bCreateNew && !rDoc.maTables[nIndex])
1349 rDoc.maTables[nIndex] = std::make_shared<Table>();
1350
1351 return rDoc.maTables[nIndex];
1352 }
1353
1354 if (!bCreateNew)
1355 {
1356 if (pnIndex) *pnIndex = nNotAvailable;
1357 return TableTypeRef();
1358 }
1359
1360 // If this is the first table to be created propagate the base name or
1361 // Sheet1 as an alias. For subsequent tables remove it again.
1362 if (rDoc.maTableNames.empty())
1363 {
1364 if (pExtUrl)
1365 {
1366 const OUString aBaseName( INetURLObject( *pExtUrl).GetBase());
1367 const OUString aSheetName( getFirstSheetName());
1368 if (ScGlobal::GetTransliteration().isEqual( rTabName, aSheetName))
1369 pDoc->maSingleTableNameAlias = aBaseName;
1370 else if (ScGlobal::GetTransliteration().isEqual( rTabName, aBaseName))
1371 pDoc->maSingleTableNameAlias = aSheetName;
1372 }
1373 }
1374 else
1375 {
1376 rDoc.maSingleTableNameAlias.clear();
1377 }
1378
1379 // Specified table doesn't exist yet. Create one.
1380 OUString aTabNameUpper = ScGlobal::getCharClass().uppercase(rTabName);
1381 nIndex = rDoc.maTables.size();
1382 if( pnIndex ) *pnIndex = nIndex;
1383 TableTypeRef pTab = std::make_shared<Table>();
1384 rDoc.maTables.push_back(pTab);
1385 rDoc.maTableNames.emplace_back(aTabNameUpper, rTabName);
1386 rDoc.maTableNameIndex.emplace(aTabNameUpper, nIndex);
1387 return pTab;
1388}
1389
1390void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
1391{
1392 std::unique_lock aGuard(maMtxDocs);
1393 maDocs.erase(nFileId);
1394}
1395
1397{
1398 std::unique_lock aGuard(maMtxDocs);
1399 DocItem* pDocItem = getDocItem(aGuard, nFileId);
1400 if (!pDocItem)
1401 // This document is not cached at all.
1402 return;
1403
1404 // Clear all cache table content, but keep the tables.
1405 std::vector<TableTypeRef>& rTabs = pDocItem->maTables;
1406 for (TableTypeRef & pTab : rTabs)
1407 {
1408 if (!pTab)
1409 continue;
1410
1411 pTab->clear();
1412 }
1413
1414 // Clear the external range name caches.
1415 pDocItem->maRangeNames.clear();
1416 pDocItem->maRangeArrays.clear();
1417 pDocItem->maRealRangeNameMap.clear();
1418}
1419
1421{
1422 std::unique_lock aGuard(maMtxDocs);
1423 return getDocItem(aGuard, nFileId);
1424}
1425
1426ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(std::unique_lock<std::mutex>& /*rGuard*/, sal_uInt16 nFileId) const
1427{
1428
1429 using ::std::pair;
1430 DocDataType::iterator itrDoc = maDocs.find(nFileId);
1431 if (itrDoc == maDocs.end())
1432 {
1433 // specified document is not cached.
1434 pair<DocDataType::iterator, bool> res = maDocs.emplace(
1435 nFileId, DocItem());
1436
1437 if (!res.second)
1438 // insertion failed.
1439 return nullptr;
1440
1441 itrDoc = res.first;
1442 }
1443
1444 return &itrDoc->second;
1445}
1446
1449 mnFileId(nFileId),
1450 mrDoc(rDoc),
1451 mbDoRefresh(true)
1452{
1453}
1454
1456{
1457}
1458
1460{
1462 pMgr->breakLink(mnFileId);
1463}
1464
1465::sfx2::SvBaseLink::UpdateResult ScExternalRefLink::DataChanged(const OUString& /*rMimeType*/, const Any& /*rValue*/)
1466{
1467 if (!mbDoRefresh)
1468 return SUCCESS;
1469
1470 OUString aFile, aFilter;
1471 sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile, nullptr, &aFilter);
1473
1474 if (!pMgr->isFileLoadable(aFile))
1475 return ERROR_GENERAL;
1476
1477 const OUString* pCurFile = pMgr->getExternalFileName(mnFileId);
1478 if (!pCurFile)
1479 return ERROR_GENERAL;
1480
1481 if (*pCurFile == aFile)
1482 {
1483 // Refresh the current source document.
1484 if (!pMgr->refreshSrcDocument(mnFileId))
1485 return ERROR_GENERAL;
1486 }
1487 else
1488 {
1489 // The source document has changed.
1490 ScViewData* pViewData = ScDocShell::GetViewData();
1491 if (!pViewData)
1492 return ERROR_GENERAL;
1493
1494 ScDocShell* pDocShell = pViewData->GetDocShell();
1495 ScDocShellModificator aMod(*pDocShell);
1496 pMgr->switchSrcFile(mnFileId, aFile, aFilter);
1497 aMod.SetDocumentModified();
1498 }
1499
1500 return SUCCESS;
1501}
1502
1504{
1505 SvBaseLink::Edit(pParent, Link<SvBaseLink&,void>());
1506}
1507
1509{
1510 mbDoRefresh = b;
1511}
1512
1513static FormulaToken* convertToToken( ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRefCellValue& rCell )
1514{
1515 if (rCell.hasEmptyValue())
1516 {
1517 bool bInherited = (rCell.getType() == CELLTYPE_FORMULA);
1518 return new ScEmptyCellToken(bInherited, false);
1519 }
1520
1521 switch (rCell.getType())
1522 {
1523 case CELLTYPE_EDIT:
1524 case CELLTYPE_STRING:
1525 {
1526 OUString aStr = rCell.getString(&rSrcDoc);
1528 return new formula::FormulaStringToken(std::move(aSS));
1529 }
1530 case CELLTYPE_VALUE:
1531 return new formula::FormulaDoubleToken(rCell.getDouble());
1532 case CELLTYPE_FORMULA:
1533 {
1534 ScFormulaCell* pFCell = rCell.getFormula();
1535 FormulaError nError = pFCell->GetErrCode();
1536 if (nError != FormulaError::NONE)
1537 return new FormulaErrorToken( nError);
1538 else if (pFCell->IsValue())
1539 {
1540 double fVal = pFCell->GetValue();
1541 return new formula::FormulaDoubleToken(fVal);
1542 }
1543 else
1544 {
1545 svl::SharedString aSS = rHostDoc.GetSharedStringPool().intern( pFCell->GetString().getString());
1546 return new formula::FormulaStringToken(std::move(aSS));
1547 }
1548 }
1549 default:
1550 OSL_FAIL("attempted to convert an unknown cell type.");
1551 }
1552
1553 return nullptr;
1554}
1555
1556static std::unique_ptr<ScTokenArray> convertToTokenArray(
1557 ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRange& rRange, vector<ScExternalRefCache::SingleRangeData>& rCacheData )
1558{
1559 ScAddress& s = rRange.aStart;
1560 ScAddress& e = rRange.aEnd;
1561
1562 const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1563 const SCCOL nCol1 = s.Col(), nCol2 = e.Col();
1564 const SCROW nRow1 = s.Row(), nRow2 = e.Row();
1565
1566 if (nTab2 != nTab1)
1567 // For now, we don't support multi-sheet ranges intentionally because
1568 // we don't have a way to express them in a single token. In the
1569 // future we can introduce a new stack variable type svMatrixList with
1570 // a new token type that can store a 3D matrix value and convert a 3D
1571 // range to it.
1572 return nullptr;
1573
1574 std::unique_ptr<ScRange> pUsedRange;
1575
1576 unique_ptr<ScTokenArray> pArray(new ScTokenArray(rSrcDoc));
1577 bool bFirstTab = true;
1578 vector<ScExternalRefCache::SingleRangeData>::iterator
1579 itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
1580
1581 for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
1582 {
1583 // Only loop within the data area.
1584 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
1585 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
1586 bool bShrunk;
1587 if (!rSrcDoc.ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false))
1588 // no data within specified range.
1589 continue;
1590
1591 if (pUsedRange)
1592 // Make sure the used area only grows, not shrinks.
1593 pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1594 else
1595 pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1596
1597 SCSIZE nMatrixColumns = static_cast<SCSIZE>(nCol2-nCol1+1);
1598 SCSIZE nMatrixRows = static_cast<SCSIZE>(nRow2-nRow1+1);
1599 ScMatrixRef xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
1600
1601 // Check if size could be allocated and if not skip the fill, there's
1602 // one error element instead. But retry first with the actual data area
1603 // if that is smaller than the original range, which works for most
1604 // functions just not some that operate/compare with the original size
1605 // and expect empty values in non-data areas.
1606 // Restrict this though to ranges of entire columns or rows, other
1607 // ranges might be on purpose. (Other special cases to handle?)
1608 /* TODO: sparse matrix could help */
1609 SCSIZE nMatCols, nMatRows;
1610 xMat->GetDimensions( nMatCols, nMatRows);
1611 if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
1612 {
1613 rSrcDoc.FillMatrix(*xMat, nTab, nCol1, nRow1, nCol2, nRow2, &rHostDoc.GetSharedStringPool());
1614 }
1615 else if ((nCol1 == 0 && nCol2 == rSrcDoc.MaxCol()) || (nRow1 == 0 && nRow2 == rSrcDoc.MaxRow()))
1616 {
1617 if ((o3tl::make_unsigned(nDataCol2-nDataCol1+1) < nMatrixColumns) ||
1618 (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows))
1619 {
1620 nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1);
1621 nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
1622 xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
1623 xMat->GetDimensions( nMatCols, nMatRows);
1624 if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
1625 rSrcDoc.FillMatrix(*xMat, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, &rHostDoc.GetSharedStringPool());
1626 }
1627 }
1628
1629 if (!bFirstTab)
1630 pArray->AddOpCode(ocSep);
1631
1632 ScMatrixToken aToken(xMat);
1633 pArray->AddToken(aToken);
1634
1635 itrCache->mpRangeData = xMat;
1636
1637 bFirstTab = false;
1638 }
1639
1640 if (!pUsedRange)
1641 return nullptr;
1642
1643 s.SetCol(pUsedRange->aStart.Col());
1644 s.SetRow(pUsedRange->aStart.Row());
1645 e.SetCol(pUsedRange->aEnd.Col());
1646 e.SetRow(pUsedRange->aEnd.Row());
1647
1648 return pArray;
1649}
1650
1651static std::unique_ptr<ScTokenArray> lcl_fillEmptyMatrix(const ScDocument& rDoc, const ScRange& rRange)
1652{
1653 SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1);
1654 SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1);
1655 ScMatrixRef xMat = new ScMatrix(nC, nR);
1656
1657 ScMatrixToken aToken(std::move(xMat));
1658 unique_ptr<ScTokenArray> pArray(new ScTokenArray(rDoc));
1659 pArray->AddToken(aToken);
1660 return pArray;
1661}
1662
1663namespace {
1664bool isLinkUpdateAllowedInDoc(const ScDocument& rDoc)
1665{
1666 SfxObjectShell* pDocShell = rDoc.GetDocumentShell();
1667 if (!pDocShell)
1668 return rDoc.IsFunctionAccess();
1669
1671}
1672}
1673
1675 mrDoc(rDoc),
1676 maRefCache(rDoc),
1677 mbInReferenceMarking(false),
1678 mbUserInteractionEnabled(true),
1679 mbDocTimerEnabled(true),
1680 maSrcDocTimer( "sc::ScExternalRefManager maSrcDocTimer" )
1681{
1684}
1685
1687{
1688 clear();
1689}
1690
1691OUString ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
1692{
1693 return maRefCache.getTableName(nFileId, nTabIndex);
1694}
1695
1697{
1698 return maRefCache.getCacheTable(nFileId, nTabIndex);
1699}
1700
1702 sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
1703{
1704 return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex, pExtUrl);
1705}
1706
1708{
1709}
1710
1712{
1713}
1714
1716 mpMgr(rDoc.GetExternalRefManager()),
1717 mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled)
1718{
1719 // We don't want user interaction handled in the API.
1721}
1722
1724{
1725 // Restore old value.
1726 mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled;
1727}
1728
1729void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1730{
1731 maRefCache.getAllTableNames(nFileId, rTabNames);
1732}
1733
1734SCTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1735{
1736 return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
1737}
1738
1739void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
1740{
1742}
1743
1745{
1746 return static_cast< sal_uInt16 >( maSrcFiles.size() );
1747}
1748
1750{
1751 bool bAllMarked = false;
1752 for (const auto& [rFileId, rLinkListeners] : maLinkListeners)
1753 {
1754 if (!rLinkListeners.empty())
1755 bAllMarked = maRefCache.setCacheDocReferenced(rFileId);
1756
1757 if (bAllMarked)
1758 break;
1759 /* TODO: LinkListeners should remember the table they're listening to.
1760 * As is, listening to one table will mark all tables of the document
1761 * being referenced. */
1762 }
1763}
1764
1766{
1767 for (const auto& rEntry : maRefCells)
1768 {
1769 for (ScFormulaCell* pCell : rEntry.second)
1770 {
1771 bool bUsed = pCell->MarkUsedExternalReferences();
1772 if (bUsed)
1773 // Return true when at least one cell references external docs.
1774 return;
1775 }
1776 }
1777}
1778
1779bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1780{
1781 return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets);
1782}
1783
1785{
1786 mbInReferenceMarking = !bReferenced;
1788}
1789
1790void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, const ScTokenArray& rArray)
1791{
1793 if (!rArray.HasExternalRef())
1794 {
1795 // Parse all tokens in this external range data, and replace each absolute
1796 // reference token with an external reference token, and cache them.
1797 pNewArray = std::make_shared<ScTokenArray>(mrDoc);
1798 FormulaTokenArrayPlainIterator aIter(rArray);
1799 for (const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next())
1800 {
1801 bool bTokenAdded = false;
1802 switch (pToken->GetType())
1803 {
1804 case svSingleRef:
1805 {
1806 const ScSingleRefData& rRef = *pToken->GetSingleRef();
1807 OUString aTabName;
1808 if (SCTAB nCacheId = rRef.Tab(); nCacheId >= 0)
1809 aTabName = maRefCache.getTableName(nFileId, nCacheId);
1810 ScExternalSingleRefToken aNewToken(nFileId, svl::SharedString(aTabName), // string not interned
1811 *pToken->GetSingleRef());
1812 pNewArray->AddToken(aNewToken);
1813 bTokenAdded = true;
1814 }
1815 break;
1816 case svDoubleRef:
1817 {
1818 const ScSingleRefData& rRef = *pToken->GetSingleRef();
1819 OUString aTabName;
1820 if (SCTAB nCacheId = rRef.Tab(); nCacheId >= 0)
1821 aTabName = maRefCache.getTableName(nFileId, nCacheId);
1822 ScExternalDoubleRefToken aNewToken(nFileId, svl::SharedString(aTabName), // string not interned
1823 *pToken->GetDoubleRef());
1824 pNewArray->AddToken(aNewToken);
1825 bTokenAdded = true;
1826 }
1827 break;
1828 default:
1829 ; // nothing
1830 }
1831
1832 if (!bTokenAdded)
1833 pNewArray->AddToken(*pToken);
1834 }
1835 }
1836 else
1837 pNewArray = rArray.Clone();
1838
1839 maRefCache.setRangeNameTokens(nFileId, rName, pNewArray);
1840}
1841
1842namespace {
1843
1850void putCellDataIntoCache(
1851 ScExternalRefCache& rRefCache, const ScExternalRefCache::TokenRef& pToken,
1852 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1854{
1855 // Now, insert the token into cache table but don't cache empty cells.
1856 if (pToken->GetType() != formula::svEmptyCell)
1857 {
1858 sal_uLong nFmtIndex = (pFmt && pFmt->mbIsSet) ? pFmt->mnIndex : 0;
1859 rRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pToken, nFmtIndex);
1860 }
1861}
1862
1876void putRangeDataIntoCache(
1878 sal_uInt16 nFileId, const OUString& rTabName,
1879 const vector<ScExternalRefCache::SingleRangeData>& rCacheData,
1880 const ScRange& rCacheRange, const ScRange& rDataRange)
1881{
1882 if (pArray)
1883 // Cache these values.
1884 rRefCache.setCellRangeData(nFileId, rDataRange, rCacheData, pArray);
1885 else
1886 {
1887 // Array is empty. Fill it with an empty matrix of the required size.
1888 pArray = lcl_fillEmptyMatrix(rRefCache.getDoc(), rCacheRange);
1889
1890 // Make sure to set this range 'cached', to prevent unnecessarily
1891 // accessing the src document time and time again.
1893 rRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr);
1894 if (pCacheTab)
1895 pCacheTab->setCachedCellRange(
1896 rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row());
1897 }
1898}
1899
1910void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sal_uInt16 nFileId)
1911{
1912 if (!pSrcDoc)
1913 return;
1914
1915 if (rRefCache.isDocInitialized(nFileId))
1916 // Already initialized. No need to do this twice.
1917 return;
1918
1919 SCTAB nTabCount = pSrcDoc->GetTableCount();
1920 if (!nTabCount)
1921 return;
1922
1923 // Populate the cache with all table names in the source document.
1924 vector<OUString> aTabNames;
1925 aTabNames.reserve(nTabCount);
1926 for (SCTAB i = 0; i < nTabCount; ++i)
1927 {
1928 OUString aName;
1929 pSrcDoc->GetName(i, aName);
1930 aTabNames.push_back(aName);
1931 }
1932
1933 // Obtain the base name, don't bother if there are more than one sheets.
1934 OUString aBaseName;
1935 if (nTabCount == 1)
1936 {
1937 const SfxObjectShell* pShell = pSrcDoc->GetDocumentShell();
1938 if (pShell && pShell->GetMedium())
1939 {
1940 OUString aName = pShell->GetMedium()->GetName();
1941 aBaseName = INetURLObject( aName).GetBase();
1942 }
1943 }
1944
1945 rRefCache.initializeDoc(nFileId, aTabNames, aBaseName);
1946}
1947
1948}
1949
1950bool ScExternalRefManager::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
1951 sal_uInt16 nFileId ) const
1952{
1953 return maRefCache.getSrcDocTable( rSrcDoc, rTabName, rTab, nFileId);
1954}
1955
1957 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1958 const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
1959{
1960 if (pCurPos)
1961 insertRefCell(nFileId, *pCurPos);
1962
1963 maybeLinkExternalFile(nFileId);
1964
1965 if (pTab)
1966 *pTab = -1;
1967
1968 if (pFmt)
1969 pFmt->mbIsSet = false;
1970
1971 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1972 if (pSrcDoc)
1973 {
1974 // source document already loaded in memory. Re-use this instance.
1975 SCTAB nTab;
1976 if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
1977 {
1978 // specified table name doesn't exist in the source document.
1979 ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(FormulaError::NoRef));
1980 return pToken;
1981 }
1982
1983 if (pTab)
1984 *pTab = nTab;
1985
1988 nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1989
1990 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1991 return pToken;
1992 }
1993
1994 // Check if the given table name and the cell position is cached.
1995 sal_uInt32 nFmtIndex = 0;
1997 nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex);
1998 if (pToken)
1999 {
2000 // Cache hit !
2001 fillCellFormat(nFmtIndex, pFmt);
2002 return pToken;
2003 }
2004
2005 // reference not cached. read from the source document.
2006 pSrcDoc = getSrcDocument(nFileId);
2007 if (!pSrcDoc)
2008 {
2009 // Source document not reachable.
2010 if (!isLinkUpdateAllowedInDoc(mrDoc))
2011 {
2012 // Indicate with specific error.
2013 pToken.reset(new FormulaErrorToken(FormulaError::LinkFormulaNeedingCheck));
2014 }
2015 else
2016 {
2017 // Throw a reference error.
2018 pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
2019 }
2020 return pToken;
2021 }
2022
2023 SCTAB nTab;
2024 if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
2025 {
2026 // specified table name doesn't exist in the source document.
2027 pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
2028 return pToken;
2029 }
2030
2031 if (pTab)
2032 *pTab = nTab;
2033
2034 SCCOL nDataCol1 = 0, nDataCol2 = pSrcDoc->MaxCol();
2035 SCROW nDataRow1 = 0, nDataRow2 = pSrcDoc->MaxRow();
2036 bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2);
2037 if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row())
2038 {
2039 // requested cell is outside the data area. Don't even bother caching
2040 // this data, but add it to the cached range to prevent accessing the
2041 // source document time and time again.
2043 maRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr);
2044 if (pCacheTab)
2045 pCacheTab->setCachedCell(rCell.Col(), rCell.Row());
2046
2047 pToken.reset(new ScEmptyCellToken(false, false));
2048 return pToken;
2049 }
2050
2052 nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
2053
2054 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
2055 return pToken;
2056}
2057
2059 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
2060{
2061 if (pCurPos)
2062 insertRefCell(nFileId, *pCurPos);
2063
2064 maybeLinkExternalFile(nFileId);
2065
2066 ScRange aDataRange(rRange);
2067 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2068 if (pSrcDoc)
2069 {
2070 // Document already loaded in memory.
2071 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2073 getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
2074
2075 // Put the data into cache.
2076 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
2077 return pArray;
2078 }
2079
2080 // Check if the given table name and the cell position is cached.
2082 maRefCache.getCellRangeData(nFileId, rTabName, rRange);
2083 if (pArray)
2084 // Cache hit !
2085 return pArray;
2086
2087 pSrcDoc = getSrcDocument(nFileId);
2088 if (!pSrcDoc)
2089 {
2090 // Source document is not reachable. Throw a reference error.
2091 pArray = std::make_shared<ScTokenArray>(maRefCache.getDoc());
2092 pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
2093 return pArray;
2094 }
2095
2096 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2097 pArray = getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
2098
2099 // Put the data into cache.
2100 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
2101 return pArray;
2102}
2103
2105 sal_uInt16 nFileId, const OUString& rName, const ScAddress* pCurPos)
2106{
2107 if (pCurPos)
2108 insertRefCell(nFileId, *pCurPos);
2109
2110 maybeLinkExternalFile(nFileId);
2111
2112 OUString aName = rName; // make a copy to have the casing corrected.
2113 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2114 if (pSrcDoc)
2115 {
2116 // Document already loaded in memory.
2118 getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
2119
2120 if (pArray)
2121 // Cache this range name array.
2122 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
2123
2124 return pArray;
2125 }
2126
2128 if (pArray)
2129 // This range name is cached.
2130 return pArray;
2131
2132 pSrcDoc = getSrcDocument(nFileId);
2133 if (!pSrcDoc)
2134 // failed to load document from disk.
2136
2137 pArray = getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
2138
2139 if (pArray)
2140 // Cache this range name array.
2141 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
2142
2143 return pArray;
2144}
2145
2146namespace {
2147
2148bool hasRangeName(const ScDocument& rDoc, const OUString& rName)
2149{
2150 ScRangeName* pExtNames = rDoc.GetRangeName();
2151 OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
2152 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2153 return pRangeData != nullptr;
2154}
2155
2156}
2157
2158bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId, const OUString& rName)
2159{
2160 maybeLinkExternalFile(nFileId);
2161 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2162 if (pSrcDoc)
2163 {
2164 // Only check the presence of the name.
2165 if (hasRangeName(*pSrcDoc, rName))
2166 {
2167 maRefCache.setRangeName(nFileId, rName);
2168 return true;
2169 }
2170 return false;
2171 }
2172
2173 if (maRefCache.isValidRangeName(nFileId, rName))
2174 // Range name is cached.
2175 return true;
2176
2177 pSrcDoc = getSrcDocument(nFileId);
2178 if (!pSrcDoc)
2179 // failed to load document from disk.
2180 return false;
2181
2182 if (hasRangeName(*pSrcDoc, rName))
2183 {
2184 maRefCache.setRangeName(nFileId, rName);
2185 return true;
2186 }
2187
2188 return false;
2189}
2190
2192{
2193 RefCellMap::iterator itrFile = maRefCells.find(nFileId);
2194 if (itrFile == maRefCells.end())
2195 return;
2196
2197 RefCellSet& rRefCells = itrFile->second;
2198 for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell());
2199
2200 ScViewData* pViewData = ScDocShell::GetViewData();
2201 if (!pViewData)
2202 return;
2203
2204 ScTabViewShell* pVShell = pViewData->GetViewShell();
2205 if (!pVShell)
2206 return;
2207
2208 // Repainting the grid also repaints the texts, but is there a better way
2209 // to refresh texts?
2210 pVShell->Invalidate(FID_REPAINT);
2211 pVShell->PaintGrid();
2212}
2213
2214namespace {
2215
2216void insertRefCellByIterator(
2217 const ScExternalRefManager::RefCellMap::iterator& itr, ScFormulaCell* pCell)
2218{
2219 if (pCell)
2220 {
2221 itr->second.insert(pCell);
2222 pCell->SetIsExtRef();
2223 }
2224}
2225
2226}
2227
2228void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
2229{
2230 RefCellMap::iterator itr = maRefCells.find(nFileId);
2231 if (itr == maRefCells.end())
2232 {
2233 RefCellSet aRefCells;
2234 pair<RefCellMap::iterator, bool> r = maRefCells.emplace(
2235 nFileId, aRefCells);
2236 if (!r.second)
2237 // insertion failed.
2238 return;
2239
2240 itr = r.first;
2241 }
2242
2243 insertRefCellByIterator(itr, mrDoc.GetFormulaCell(rCell));
2244}
2245
2247{
2248 if (!pTemplateCell || !pCell)
2249 return;
2250
2251 for (RefCellMap::iterator itr = maRefCells.begin(); itr != maRefCells.end(); ++itr)
2252 {
2253 if (itr->second.find(pTemplateCell) != itr->second.end())
2254 insertRefCellByIterator(itr, pCell);
2255 }
2256}
2257
2259{
2260 ScFormulaCell* pCell = mrDoc.GetFormulaCell(rCell);
2261
2262 if (pCell)
2263 return std::any_of(maRefCells.begin(), maRefCells.end(),
2264 [&pCell](const RefCellMap::value_type& rEntry) { return rEntry.second.find(pCell) != rEntry.second.end(); });
2265
2266 return false;
2267}
2268
2270{
2271 if (mbDocTimerEnabled == bEnable)
2272 return;
2273
2274 mbDocTimerEnabled = bEnable;
2276 {
2277 if (!maDocShells.empty())
2278 {
2279 for (auto& rEntry : maDocShells)
2280 rEntry.second.maLastAccess = tools::Time(tools::Time::SYSTEM);
2281
2283 }
2284 }
2285 else
2287}
2288
2290{
2291 if (!pFmt)
2292 return;
2293
2294 SvNumFormatType nFmtType = mrDoc.GetFormatTable()->GetType(nFmtIndex);
2295 if (nFmtType != SvNumFormatType::UNDEFINED)
2296 {
2297 pFmt->mbIsSet = true;
2298 pFmt->mnIndex = nFmtIndex;
2299 pFmt->mnType = nFmtType;
2300 }
2301}
2302
2304 sal_uInt16 nFileId, ScDocument& rSrcDoc, const ScAddress& rPos,
2306{
2307 // Get the cell from src doc, and convert it into a token.
2308 ScRefCellValue aCell(rSrcDoc, rPos);
2309 ScExternalRefCache::TokenRef pToken(convertToToken(mrDoc, rSrcDoc, aCell));
2310
2311 if (!pToken)
2312 {
2313 // Generate an error for unresolvable cells.
2314 pToken.reset( new FormulaErrorToken( FormulaError::NoValue));
2315 }
2316
2317 // Get number format information.
2318 sal_uInt32 nFmtIndex = rSrcDoc.GetNumberFormat(rPos.Col(), rPos.Row(), rPos.Tab());
2319 nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, rSrcDoc);
2320 fillCellFormat(nFmtIndex, pFmt);
2321 return pToken;
2322}
2323
2325 const ScDocument& rSrcDoc, const OUString& rTabName, ScRange& rRange,
2326 vector<ScExternalRefCache::SingleRangeData>& rCacheData)
2327{
2329 SCTAB nTab1;
2330
2331 if (!rSrcDoc.GetTable(rTabName, nTab1))
2332 {
2333 // specified table name doesn't exist in the source document.
2334 pArray = std::make_shared<ScTokenArray>(rSrcDoc);
2335 pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
2336 return pArray;
2337 }
2338
2339 ScRange aRange(rRange);
2340 aRange.PutInOrder();
2341 SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
2342
2343 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2344 aCacheData.reserve(nTabSpan+1);
2345 aCacheData.emplace_back();
2346 aCacheData.back().maTableName = ScGlobal::getCharClass().uppercase(rTabName);
2347
2348 for (SCTAB i = 1; i < nTabSpan + 1; ++i)
2349 {
2350 OUString aTabName;
2351 if (!rSrcDoc.GetName(nTab1 + 1, aTabName))
2352 // source document doesn't have any table by the specified name.
2353 break;
2354
2355 aCacheData.emplace_back();
2356 aCacheData.back().maTableName = ScGlobal::getCharClass().uppercase(aTabName);
2357 }
2358
2359 aRange.aStart.SetTab(nTab1);
2360 aRange.aEnd.SetTab(nTab1 + nTabSpan);
2361
2362 pArray = convertToTokenArray(mrDoc, rSrcDoc, aRange, aCacheData);
2363 rRange = aRange;
2364 rCacheData.swap(aCacheData);
2365 return pArray;
2366}
2367
2369 sal_uInt16 nFileId, const ScDocument& rSrcDoc, OUString& rName)
2370{
2371 ScRangeName* pExtNames = rSrcDoc.GetRangeName();
2372 OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
2373 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2374 if (!pRangeData)
2376
2377 // Parse all tokens in this external range data, and replace each absolute
2378 // reference token with an external reference token, and cache them. Also
2379 // register the source document with the link manager if it's a new
2380 // source.
2381
2382 ScExternalRefCache::TokenArrayRef pNew = std::make_shared<ScTokenArray>(rSrcDoc);
2383
2384 ScTokenArray aCode(*pRangeData->GetCode());
2385 FormulaTokenArrayPlainIterator aIter(aCode);
2386 for (const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next())
2387 {
2388 bool bTokenAdded = false;
2389 switch (pToken->GetType())
2390 {
2391 case svSingleRef:
2392 {
2393 const ScSingleRefData& rRef = *pToken->GetSingleRef();
2394 OUString aTabName;
2395 rSrcDoc.GetName(rRef.Tab(), aTabName);
2396 ScExternalSingleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
2397 *pToken->GetSingleRef());
2398 pNew->AddToken(aNewToken);
2399 bTokenAdded = true;
2400 }
2401 break;
2402 case svDoubleRef:
2403 {
2404 const ScSingleRefData& rRef = *pToken->GetSingleRef();
2405 OUString aTabName;
2406 rSrcDoc.GetName(rRef.Tab(), aTabName);
2407 ScExternalDoubleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
2408 *pToken->GetDoubleRef());
2409 pNew->AddToken(aNewToken);
2410 bTokenAdded = true;
2411 }
2412 break;
2413 default:
2414 ; // nothing
2415 }
2416
2417 if (!bTokenAdded)
2418 pNew->AddToken(*pToken);
2419 }
2420
2421 rName = pRangeData->GetName(); // Get the correctly-cased name.
2422 return pNew;
2423}
2424
2426{
2427 const OUString* pFileName = getExternalFileName(nFileId);
2428 if (!pFileName)
2429 return nullptr;
2430
2431 // Do not load document until it was allowed.
2432 if (!isLinkUpdateAllowedInDoc(mrDoc))
2433 return nullptr;
2434
2435 ScDocument* pSrcDoc = nullptr;
2436 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
2437 while (pShell)
2438 {
2439 SfxMedium* pMedium = pShell->GetMedium();
2440 if (pMedium && !pMedium->GetName().isEmpty())
2441 {
2442 // TODO: We should make the case sensitivity platform dependent.
2443 if (pFileName->equalsIgnoreAsciiCase(pMedium->GetName()))
2444 {
2445 // Found !
2446 pSrcDoc = &pShell->GetDocument();
2447 break;
2448 }
2449 }
2450 else
2451 {
2452 // handle unsaved documents here
2453 OUString aName = pShell->GetName();
2454 if (pFileName->equalsIgnoreAsciiCase(aName))
2455 {
2456 // Found !
2457 SrcShell aSrcDoc;
2458 aSrcDoc.maShell = pShell;
2459 maUnsavedDocShells.emplace(nFileId, aSrcDoc);
2460 StartListening(*pShell);
2461 pSrcDoc = &pShell->GetDocument();
2462 break;
2463 }
2464 }
2465 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
2466 }
2467
2468 initDocInCache(maRefCache, pSrcDoc, nFileId);
2469 return pSrcDoc;
2470}
2471
2473{
2475 return nullptr;
2476
2477 DocShellMap::iterator itrEnd = maDocShells.end();
2478 DocShellMap::iterator itr = maDocShells.find(nFileId);
2479
2480 if (itr != itrEnd)
2481 {
2482 // document already loaded.
2483
2484 SfxObjectShell* p = itr->second.maShell.get();
2485 itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2486 return &static_cast<ScDocShell*>(p)->GetDocument();
2487 }
2488
2489 itrEnd = maUnsavedDocShells.end();
2490 itr = maUnsavedDocShells.find(nFileId);
2491 if (itr != itrEnd)
2492 {
2493 //document is unsaved document
2494
2495 SfxObjectShell* p = itr->second.maShell.get();
2496 itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2497 return &static_cast<ScDocShell*>(p)->GetDocument();
2498 }
2499
2500 const OUString* pFile = getExternalFileName(nFileId);
2501 if (!pFile)
2502 // no file name associated with this ID.
2503 return nullptr;
2504
2505 SrcShell aSrcDoc;
2506 try
2507 {
2508 OUString aFilter;
2509 aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
2510 }
2511 catch (const css::uno::Exception&)
2512 {
2513 }
2514 if (!aSrcDoc.maShell.is())
2515 {
2516 // source document could not be loaded.
2517 return nullptr;
2518 }
2519
2520 return &cacheNewDocShell(nFileId, aSrcDoc);
2521}
2522
2523SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUString& rFilter)
2524{
2525 // Do not load document until it was allowed.
2526 if (!isLinkUpdateAllowedInDoc(mrDoc))
2527 return nullptr;
2528
2529 const SrcFileData* pFileData = getExternalFileData(nFileId);
2530 if (!pFileData)
2531 return nullptr;
2532
2533 // Always load the document by using the path created from the relative
2534 // path. If the referenced document is not there, simply exit. The
2535 // original file name should be used only when the relative path is not
2536 // given.
2537 OUString aFile = pFileData->maFileName;
2538 maybeCreateRealFileName(nFileId);
2539 if (!pFileData->maRealFileName.isEmpty())
2540 aFile = pFileData->maRealFileName;
2541
2542 if (!isFileLoadable(aFile))
2543 return nullptr;
2544
2545 OUString aOptions = pFileData->maFilterOptions;
2546 if ( !pFileData->maFilterName.isEmpty() )
2547 rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter
2548 else
2549 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
2550 std::shared_ptr<const SfxFilter> pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
2551
2552 if (pFileData->maRelativeName.isEmpty())
2553 {
2554 // Generate a relative file path.
2556 aBaseURL.insertName(u"content.xml");
2557
2560
2561 setRelativeFileName(nFileId, aStr);
2562 }
2563
2564 std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet(SfxGetpApp()->GetPool()));
2565 if (!aOptions.isEmpty())
2566 pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
2567
2568 // make medium hidden to prevent assertion from progress bar
2569 pSet->Put( SfxBoolItem(SID_HIDDEN, true) );
2570
2571 // If the current document is allowed to execute macros then the referenced
2572 // document may execute macros according to the security configuration.
2573 // Similar for UpdateDocMode to update links, just that if we reach here
2574 // the user already allowed updates and intermediate documents are expected
2575 // to update as well. When loading the document ScDocShell::Load() will
2576 // check through ScDocShell::GetLinkUpdateModeState() if its location is
2577 // trusted.
2579 if (pShell)
2580 {
2581 SfxMedium* pMedium = pShell->GetMedium();
2582 if (pMedium)
2583 {
2584 const SfxUInt16Item* pItem = pMedium->GetItemSet().GetItemIfSet( SID_MACROEXECMODE, false );
2585 if (pItem &&
2586 pItem->GetValue() != css::document::MacroExecMode::NEVER_EXECUTE)
2587 pSet->Put( SfxUInt16Item( SID_MACROEXECMODE, css::document::MacroExecMode::USE_CONFIG));
2588 }
2589
2590 pSet->Put( SfxUInt16Item( SID_UPDATEDOCMODE, css::document::UpdateDocMode::FULL_UPDATE));
2591 }
2592
2593 unique_ptr<SfxMedium> pMedium(new SfxMedium(aFile, StreamMode::STD_READ, pFilter, std::move(pSet)));
2594 if (pMedium->GetError() != ERRCODE_NONE)
2595 return nullptr;
2596
2597 // To load encrypted documents with password, user interaction needs to be enabled.
2598 pMedium->UseInteractionHandler(mbUserInteractionEnabled);
2599
2600 ScDocShell* pNewShell = new ScDocShell(SfxModelFlags::EXTERNAL_LINK);
2601 SfxObjectShellRef aRef = pNewShell;
2602
2603 // increment the recursive link count of the source document.
2605 sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0;
2606 ScDocument& rSrcDoc = pNewShell->GetDocument();
2607 rSrcDoc.EnableExecuteLink(false); // to prevent circular access of external references.
2608 rSrcDoc.EnableUndo(false);
2609 rSrcDoc.LockAdjustHeight();
2610 rSrcDoc.EnableUserInteraction(false);
2611
2612 ScExtDocOptions* pExtOptNew = rSrcDoc.GetExtDocOptions();
2613 if (!pExtOptNew)
2614 {
2615 rSrcDoc.SetExtDocOptions(std::make_unique<ScExtDocOptions>());
2616 pExtOptNew = rSrcDoc.GetExtDocOptions();
2617 }
2618 pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
2619
2620 if (!pNewShell->DoLoad(pMedium.release()))
2621 {
2622 aRef->DoClose();
2623 aRef.clear();
2624 return aRef;
2625 }
2626
2627 // with UseInteractionHandler, options may be set by dialog during DoLoad
2628 OUString aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium());
2629 if (!aNew.isEmpty() && aNew != aOptions)
2630 aOptions = aNew;
2631 setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options
2632
2633 return aRef;
2634}
2635
2637{
2638 if (mbDocTimerEnabled && maDocShells.empty())
2639 // If this is the first source document insertion, start up the timer.
2641
2642 maDocShells.emplace(nFileId, rSrcShell);
2643 SfxObjectShell& rShell = *rSrcShell.maShell;
2644 ScDocument& rSrcDoc = static_cast<ScDocShell&>(rShell).GetDocument();
2645 initDocInCache(maRefCache, &rSrcDoc, nFileId);
2646 return rSrcDoc;
2647}
2648
2649bool ScExternalRefManager::isFileLoadable(const OUString& rFile) const
2650{
2651 if (rFile.isEmpty())
2652 return false;
2653
2654 if (isOwnDocument(rFile))
2655 return false;
2656 OUString aPhysical;
2657 if (osl::FileBase::getSystemPathFromFileURL(rFile, aPhysical)
2658 == osl::FileBase::E_None)
2659 {
2660 // #i114504# try IsFolder/Exists only for file URLs
2661
2663 return false;
2664
2665 return utl::UCBContentHelper::Exists(rFile);
2666 }
2667 else
2668 return true; // for http and others, Exists doesn't work, but the URL can still be opened
2669}
2670
2671void ScExternalRefManager::maybeLinkExternalFile( sal_uInt16 nFileId, bool bDeferFilterDetection )
2672{
2673 if (maLinkedDocs.count(nFileId))
2674 // file already linked, or the link has been broken.
2675 return;
2676
2677 // Source document not linked yet. Link it now.
2678 const OUString* pFileName = getExternalFileName(nFileId);
2679 if (!pFileName)
2680 return;
2681
2682 OUString aFilter, aOptions;
2683 const SrcFileData* pFileData = getExternalFileData(nFileId);
2684 if (pFileData)
2685 {
2686 aFilter = pFileData->maFilterName;
2687 aOptions = pFileData->maFilterOptions;
2688 }
2689
2690 // Filter detection may access external links; defer it until we are allowed.
2691 if (!bDeferFilterDetection)
2692 bDeferFilterDetection = !isLinkUpdateAllowedInDoc(mrDoc);
2693
2694 // If a filter was already set (for example, loading the cached table),
2695 // don't call GetFilterName which has to access the source file.
2696 // If filter detection is deferred, the next successful loadSrcDocument()
2697 // will update SrcFileData filter name.
2698 if (aFilter.isEmpty() && !bDeferFilterDetection)
2699 ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false);
2701 if (!pLinkMgr)
2702 {
2703 SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL");
2704 return;
2705 }
2707 OSL_ENSURE(pFileName, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL");
2709 (aFilter.isEmpty() && bDeferFilterDetection ? nullptr : &aFilter));
2710
2711 pLink->SetDoRefresh(false);
2712 pLink->Update();
2713 pLink->SetDoRefresh(true);
2714
2715 maLinkedDocs.emplace(nFileId, true);
2716}
2717
2719{
2720 if (maSrcFiles.empty())
2721 return;
2722
2724 "sc.ui", "ScExternalRefManager::addFilesToLinkManager: files overflow");
2725 const sal_uInt16 nSize = static_cast<sal_uInt16>( std::min<size_t>( maSrcFiles.size(), SAL_MAX_UINT16));
2726 for (sal_uInt16 nFileId = 0; nFileId < nSize; ++nFileId)
2727 maybeLinkExternalFile( nFileId, true);
2728}
2729
2731{
2732 if (maRelativeName.isEmpty())
2733 // No relative path given. Nothing to do.
2734 return;
2735
2736 if (!maRealFileName.isEmpty())
2737 // Real file name already created. Nothing to do.
2738 return;
2739
2740 // Formulate the absolute file path from the relative path.
2741 const OUString& rRelPath = maRelativeName;
2742 INetURLObject aBaseURL(rOwnDocName);
2743 aBaseURL.insertName(u"content.xml");
2744 bool bWasAbs = false;
2745 maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::DecodeMechanism::NONE);
2746}
2747
2749{
2750 if (nFileId >= maSrcFiles.size())
2751 return;
2752
2753 maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
2754}
2755
2757{
2759 return "file:///tmp/document";
2760
2762 if (!pShell)
2763 // This should not happen!
2764 return OUString();
2765
2766 SfxMedium* pMed = pShell->GetMedium();
2767 if (!pMed)
2768 return OUString();
2769
2770 return pMed->GetName();
2771}
2772
2773bool ScExternalRefManager::isOwnDocument(std::u16string_view rFile) const
2774{
2775 return getOwnDocumentName() == rFile;
2776}
2777
2779{
2780 // unsaved documents have no AbsName
2781 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
2782 while (pShell)
2783 {
2784 if (rFile == pShell->GetName())
2785 return;
2786
2787 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
2788 }
2789
2790 SfxObjectShell* pDocShell = mrDoc.GetDocumentShell();
2791 rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
2792}
2793
2794sal_uInt16 ScExternalRefManager::getExternalFileId(const OUString& rFile)
2795{
2796 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2797 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2798 if (itr != itrEnd)
2799 {
2800 size_t nId = distance(itrBeg, itr);
2801 return static_cast<sal_uInt16>(nId);
2802 }
2803
2805 aData.maFileName = rFile;
2806 maSrcFiles.push_back(aData);
2807 return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
2808}
2809
2810const OUString* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
2811{
2812 if (nFileId >= maSrcFiles.size())
2813 return nullptr;
2814
2815 if (bForceOriginal)
2816 return &maSrcFiles[nFileId].maFileName;
2817
2818 maybeCreateRealFileName(nFileId);
2819
2820 if (!maSrcFiles[nFileId].maRealFileName.isEmpty())
2821 return &maSrcFiles[nFileId].maRealFileName;
2822
2823 return &maSrcFiles[nFileId].maFileName;
2824}
2825
2827{
2829 return nFileId;
2830 else
2831 return maConvertFileIdToUsedFileId[nFileId];
2832}
2833
2834void ScExternalRefManager::setSkipUnusedFileIds(std::vector<sal_uInt16>& rExternFileIds)
2835{
2836 mbSkipUnusedFileIds = true;
2838 std::fill(maConvertFileIdToUsedFileId.begin(), maConvertFileIdToUsedFileId.end(), 0);
2839 int nUsedCount = 0;
2840 for (auto nEntry : rExternFileIds)
2841 {
2842 maConvertFileIdToUsedFileId[nEntry] = nUsedCount++;
2843 }
2844}
2845
2847{
2848 mbSkipUnusedFileIds = false;
2849}
2850
2852{
2853 std::vector<OUString> aNames;
2854 aNames.reserve(maSrcFiles.size());
2855 for (const SrcFileData& rData : maSrcFiles)
2856 {
2857 aNames.push_back(rData.maFileName);
2858 }
2859
2860 return aNames;
2861}
2862
2863bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
2864{
2865 return nFileId < maSrcFiles.size();
2866}
2867
2868bool ScExternalRefManager::hasExternalFile(const OUString& rFile) const
2869{
2870 return ::std::any_of(maSrcFiles.begin(), maSrcFiles.end(), FindSrcFileByName(rFile));
2871}
2872
2874{
2875 if (nFileId >= maSrcFiles.size())
2876 return nullptr;
2877
2878 return &maSrcFiles[nFileId];
2879}
2880
2881const OUString* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
2882{
2883 return maRefCache.getRealTableName(nFileId, rTabName);
2884}
2885
2886const OUString* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
2887{
2888 return maRefCache.getRealRangeName(nFileId, rRangeName);
2889}
2890
2891template<typename MapContainer>
2892static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
2893{
2894 typename MapContainer::iterator itr = rMap.find(nFileId);
2895 if (itr != rMap.end())
2896 {
2897 // Close this document shell.
2898 itr->second.maShell->DoClose();
2899 rMap.erase(itr);
2900 }
2901}
2902
2903void ScExternalRefManager::clearCache(sal_uInt16 nFileId)
2904{
2905 maRefCache.clearCache(nFileId);
2906}
2907
2908namespace {
2909
2910class RefCacheFiller : public sc::ColumnSpanSet::ColumnAction
2911{
2912 svl::SharedStringPool& mrStrPool;
2913
2914 ScExternalRefCache& mrRefCache;
2916 sal_uInt16 mnFileId;
2917 ScColumn* mpCurCol;
2919
2920public:
2921 RefCacheFiller( svl::SharedStringPool& rStrPool, ScExternalRefCache& rRefCache, sal_uInt16 nFileId ) :
2922 mrStrPool(rStrPool), mrRefCache(rRefCache), mnFileId(nFileId), mpCurCol(nullptr) {}
2923
2924 virtual void startColumn( ScColumn* pCol ) override
2925 {
2926 mpCurCol = pCol;
2927 if (!mpCurCol)
2928 return;
2929
2930 mpCurCol->InitBlockPosition(maBlockPos);
2931 mpRefTab = mrRefCache.getCacheTable(mnFileId, mpCurCol->GetTab());
2932 }
2933
2934 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
2935 {
2936 if (!mpCurCol || !bVal)
2937 return;
2938
2939 if (!mpRefTab)
2940 return;
2941
2942 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2943 {
2945 ScRefCellValue aCell = mpCurCol->GetCellValue(maBlockPos, nRow);
2946 switch (aCell.getType())
2947 {
2948 case CELLTYPE_STRING:
2949 case CELLTYPE_EDIT:
2950 {
2951 OUString aStr = aCell.getString(&mpCurCol->GetDoc());
2952 svl::SharedString aSS = mrStrPool.intern(aStr);
2953 pTok.reset(new formula::FormulaStringToken(std::move(aSS)));
2954 }
2955 break;
2956 case CELLTYPE_VALUE:
2957 pTok.reset(new formula::FormulaDoubleToken(aCell.getDouble()));
2958 break;
2959 case CELLTYPE_FORMULA:
2960 {
2962 switch (aRes.meType)
2963 {
2965 pTok.reset(new formula::FormulaDoubleToken(aRes.mfValue));
2966 break;
2968 {
2969 // Re-intern the string to the host document pool.
2970 svl::SharedString aInterned = mrStrPool.intern(aRes.maString.getString());
2971 pTok.reset(new formula::FormulaStringToken(std::move(aInterned)));
2972 }
2973 break;
2976 default:
2977 pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
2978 }
2979 }
2980 break;
2981 default:
2982 pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
2983 }
2984
2985 if (pTok)
2986 {
2987 // Cache this cell.
2988 mpRefTab->setCell(mpCurCol->GetCol(), nRow, pTok, mpCurCol->GetNumberFormat(mpCurCol->GetDoc().GetNonThreadedContext(), nRow));
2989 mpRefTab->setCachedCell(mpCurCol->GetCol(), nRow);
2990 }
2991 }
2992 };
2993};
2994
2995}
2996
2998{
2999 SfxObjectShellRef xDocShell;
3000 try
3001 {
3002 OUString aFilter;
3003 xDocShell = loadSrcDocument(nFileId, aFilter);
3004 }
3005 catch ( const css::uno::Exception& ) {}
3006
3007 if (!xDocShell.is())
3008 // Failed to load the document. Bail out.
3009 return false;
3010
3011 ScDocShell& rDocSh = static_cast<ScDocShell&>(*xDocShell);
3012 ScDocument& rSrcDoc = rDocSh.GetDocument();
3013
3014 sc::ColumnSpanSet aCachedArea;
3015 maRefCache.getAllCachedDataSpans(rSrcDoc, nFileId, aCachedArea);
3016
3017 // Clear the existing cache, and refill it. Make sure we keep the
3018 // existing cache table instances here.
3020 RefCacheFiller aAction(mrDoc.GetSharedStringPool(), maRefCache, nFileId);
3021 aCachedArea.executeColumnAction(rSrcDoc, aAction);
3022
3023 DocShellMap::iterator it = maDocShells.find(nFileId);
3024 if (it != maDocShells.end())
3025 {
3026 it->second.maShell->DoClose();
3027 it->second.maShell = xDocShell;
3028 it->second.maLastAccess = tools::Time(tools::Time::SYSTEM);
3029 }
3030 else
3031 {
3032 SrcShell aSrcDoc;
3033 aSrcDoc.maShell = xDocShell;
3035 cacheNewDocShell(nFileId, aSrcDoc);
3036 }
3037
3038 // Update all cells containing names from this source document.
3039 refreshAllRefCells(nFileId);
3040
3042
3043 return true;
3044}
3045
3046void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
3047{
3048 // Turn all formula cells referencing this external document into static
3049 // cells.
3050 RefCellMap::iterator itrRefs = maRefCells.find(nFileId);
3051 if (itrRefs != maRefCells.end())
3052 {
3053 // Make a copy because removing the formula cells below will modify
3054 // the original container.
3055 RefCellSet aSet = itrRefs->second;
3056 for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(&mrDoc));
3057 maRefCells.erase(nFileId);
3058 }
3059
3060 // Remove all named ranges that reference this document.
3061
3062 // Global named ranges.
3063 ScRangeName* pRanges = mrDoc.GetRangeName();
3064 if (pRanges)
3065 removeRangeNamesBySrcDoc(*pRanges, nFileId);
3066
3067 // Sheet-local named ranges.
3068 for (SCTAB i = 0, n = mrDoc.GetTableCount(); i < n; ++i)
3069 {
3070 pRanges = mrDoc.GetRangeName(i);
3071 if (pRanges)
3072 removeRangeNamesBySrcDoc(*pRanges, nFileId);
3073 }
3074
3075 clearCache(nFileId);
3077
3078 if (maDocShells.empty())
3080
3081 LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
3082 if (itr != maLinkedDocs.end())
3083 itr->second = false;
3084
3086}
3087
3088void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const OUString& rNewFile, const OUString& rNewFilter)
3089{
3090 maSrcFiles[nFileId].maFileName = rNewFile;
3091 maSrcFiles[nFileId].maRelativeName.clear();
3092 maSrcFiles[nFileId].maRealFileName.clear();
3093 if (maSrcFiles[nFileId].maFilterName != rNewFilter)
3094 {
3095 // Filter type has changed.
3096 maSrcFiles[nFileId].maFilterName = rNewFilter;
3097 maSrcFiles[nFileId].maFilterOptions.clear();
3098 }
3099 refreshSrcDocument(nFileId);
3100}
3101
3102void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const OUString& rRelUrl)
3103{
3104 if (nFileId >= maSrcFiles.size())
3105 return;
3106 maSrcFiles[nFileId].maRelativeName = rRelUrl;
3107}
3108
3109void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const OUString& rFilterName, const OUString& rOptions)
3110{
3111 if (nFileId >= maSrcFiles.size())
3112 return;
3113 maSrcFiles[nFileId].maFilterName = rFilterName;
3114 maSrcFiles[nFileId].maFilterOptions = rOptions;
3115}
3116
3118{
3119 for (auto& rEntry : maLinkListeners)
3120 {
3121 for (auto& it : rEntry.second)
3122 {
3123 it->notify(0, OH_NO_WE_ARE_GOING_TO_DIE);
3124 }
3125 }
3126
3127 for (auto& rEntry : maDocShells)
3128 rEntry.second.maShell->DoClose();
3129
3130 maDocShells.clear();
3132}
3133
3135{
3136 return !maSrcFiles.empty();
3137}
3138
3139void ScExternalRefManager::resetSrcFileData(const OUString& rBaseFileUrl)
3140{
3141 for (auto& rSrcFile : maSrcFiles)
3142 {
3143 // Re-generate relative file name from the absolute file name.
3144 OUString aAbsName = rSrcFile.maRealFileName;
3145 if (aAbsName.isEmpty())
3146 aAbsName = rSrcFile.maFileName;
3147
3148 rSrcFile.maRelativeName = URIHelper::simpleNormalizedMakeRelative(
3149 rBaseFileUrl, aAbsName);
3150 }
3151}
3152
3154{
3155 OUString aOwn( getOwnDocumentName() );
3156 for (auto& rSrcFile : maSrcFiles)
3157 {
3158 // update maFileName to the real file name,
3159 // to be called when the original name is no longer needed (after CompileXML)
3160
3161 rSrcFile.maybeCreateRealFileName( aOwn );
3162 OUString aReal = rSrcFile.maRealFileName;
3163 if (!aReal.isEmpty())
3164 rSrcFile.maFileName = aReal;
3165 }
3166}
3167
3169{
3170 for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell));
3171}
3172
3173void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
3174{
3175 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3176 if (itr == maLinkListeners.end())
3177 {
3178 pair<LinkListenerMap::iterator, bool> r = maLinkListeners.emplace(
3179 nFileId, LinkListeners());
3180 if (!r.second)
3181 {
3182 OSL_FAIL("insertion of new link listener list failed");
3183 return;
3184 }
3185
3186 itr = r.first;
3187 }
3188
3189 LinkListeners& rList = itr->second;
3190 rList.insert(pListener);
3191}
3192
3193void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
3194{
3195 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3196 if (itr == maLinkListeners.end())
3197 // no listeners for a specified file.
3198 return;
3199
3200 LinkListeners& rList = itr->second;
3201 rList.erase(pListener);
3202
3203 if (rList.empty())
3204 // No more listeners for this file. Remove its entry.
3205 maLinkListeners.erase(itr);
3206}
3207
3209{
3210 for (auto& rEntry : maLinkListeners)
3211 rEntry.second.erase(pListener);
3212}
3213
3215{
3216 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3217 if (itr == maLinkListeners.end())
3218 // no listeners for a specified file.
3219 return;
3220
3221 LinkListeners& rList = itr->second;
3222 for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
3223}
3224
3226{
3227 // To avoid potentially freezing Calc, we close one stale document at a time.
3228 DocShellMap::iterator itr = std::find_if(maDocShells.begin(), maDocShells.end(),
3229 [nTimeOut](const DocShellMap::value_type& rEntry) {
3230 // in 100th of a second.
3231 sal_Int32 nSinceLastAccess = (tools::Time( tools::Time::SYSTEM ) - rEntry.second.maLastAccess).GetTime();
3232 return nSinceLastAccess >= nTimeOut;
3233 });
3234 if (itr != maDocShells.end())
3235 {
3236 // Timed out. Let's close this.
3237 itr->second.maShell->DoClose();
3238 maDocShells.erase(itr);
3239 }
3240
3241 if (maDocShells.empty())
3243}
3244
3245sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument& rSrcDoc)
3246{
3247 NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
3248 if (itr == maNumFormatMap.end())
3249 {
3250 // Number formatter map is not initialized for this external document.
3251 pair<NumFmtMap::iterator, bool> r = maNumFormatMap.emplace(
3252 nFileId, SvNumberFormatterMergeMap());
3253
3254 if (!r.second)
3255 // insertion failed.
3256 return nNumFmt;
3257
3258 itr = r.first;
3261 itr->second.swap(aMap);
3262 }
3263 const SvNumberFormatterMergeMap& rMap = itr->second;
3264 SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
3265 if (itrNumFmt != rMap.end())
3266 // mapped value found.
3267 return itrNumFmt->second;
3268
3269 return nNumFmt;
3270}
3271
3273{
3274 DocShellMap::iterator itr = maUnsavedDocShells.begin();
3275 while( itr != maUnsavedDocShells.end() )
3276 {
3277 if ( itr->second.maShell.get() == pShell )
3278 {
3279 // found that the shell is marked as unsaved
3281 switchSrcFile(itr->first, aFileURL, OUString());
3282 EndListening(*pShell);
3283 itr = maUnsavedDocShells.erase(itr);
3284 }
3285 else
3286 ++itr;
3287 }
3288}
3289
3291{
3292 const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
3293 if ( !pEventHint )
3294 return;
3295
3296 SfxEventHintId nEventId = pEventHint->GetEventId();
3297 switch ( nEventId )
3298 {
3299 case SfxEventHintId::PrepareCloseDoc:
3300 {
3301 std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
3302 VclMessageType::Warning, VclButtonsType::Ok,
3303 ScResId(STR_CLOSE_WITH_UNSAVED_REFS)));
3304 xWarn->run();
3305 }
3306 break;
3307 case SfxEventHintId::SaveDocDone:
3308 case SfxEventHintId::SaveAsDocDone:
3309 {
3310 SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
3312 }
3313 break;
3314 default:
3315 break;
3316 }
3317}
3318
3319IMPL_LINK(ScExternalRefManager, TimeOutHdl, Timer*, pTimer, void)
3320{
3321 if (pTimer == &maSrcDocTimer)
3323}
3324
3325/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const SCROW MAXROW
Definition: address.hxx:68
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
const SCCOL MAXCOL
Definition: address.hxx:69
SfxApplication * SfxGetpApp()
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, const ILibreOfficeKitNotifier *pNotifier=nullptr)
OUString uppercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
sal_uInt16 GetValue() const
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
INetURLObject smartRel2Abs(OUString const &rTheRelURIRef, bool &rWasAbsolute, bool bIgnoreFragment=false, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, bool bRelativeNonURIs=false, FSysStyle eStyle=FSysStyle::Detect) const
OUString GetBase() const
bool insertName(std::u16string_view rTheName, bool bAppendFinalSlash=false, sal_Int32 nIndex=LAST_SEGMENT, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
SCTAB Tab() const
Definition: address.hxx:283
void SetCol(SCCOL nColP)
Definition: address.hxx:291
SCROW Row() const
Definition: address.hxx:274
void SetRow(SCROW nRowP)
Definition: address.hxx:287
void SetTab(SCTAB nTabP)
Definition: address.hxx:295
SCCOL Col() const
Definition: address.hxx:279
SCTAB GetTab() const
Definition: column.hxx:255
void InitBlockPosition(sc::ColumnBlockPosition &rBlockPos)
Definition: column3.cxx:1159
SCCOL GetCol() const
Definition: column.hxx:256
ScRefCellValue GetCellValue(SCROW nRow) const
Definition: column.cxx:638
ScDocument & GetDoc() const
Definition: column.hxx:127
sal_uInt32 GetNumberFormat(const ScInterpreterContext &rContext, SCROW nRow) const
Definition: column.hxx:980
const OUString & GetInitTabPrefix() const
Create before modifications of the document and destroy thereafter.
Definition: docsh.hxx:455
void SetDocumentModified()
Definition: docsh.cxx:3315
static weld::Window * GetActiveDialogParent()
Definition: docsh.cxx:3112
const ScDocument & GetDocument() const
Definition: docsh.hxx:219
static ScViewData * GetViewData()
Definition: docsh4.cxx:2607
static OUString GetOptions(const SfxMedium &rMedium)
Definition: tablink.cxx:423
static bool GetFilterName(const OUString &rFileName, OUString &rFilter, OUString &rOptions, bool bWithContent, bool bWithInteraction)
Returns the filter name and options from a file name.
Definition: tablink.cxx:431
SC_DLLPUBLIC sal_uInt32 GetNumberFormat(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:3640
SC_DLLPUBLIC void EnableUserInteraction(bool bVal)
Definition: document.cxx:6472
SC_DLLPUBLIC bool GetTable(const OUString &rName, SCTAB &rTab) const
Definition: document.cxx:244
bool ShrinkToDataArea(SCTAB nTab, SCCOL &rStartCol, SCROW &rStartRow, SCCOL &rEndCol, SCROW &rEndRow) const
Shrink a range to only include data area.
Definition: document.cxx:1018
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:892
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:893
SC_DLLPUBLIC void EnableUndo(bool bVal)
Definition: document.cxx:6456
ScInterpreterContext & GetNonThreadedContext() const
Definition: document.hxx:617
bool ShrinkToUsedDataArea(bool &o_bShrunk, SCTAB nTab, SCCOL &rStartCol, SCROW &rStartRow, SCCOL &rEndCol, SCROW &rEndRow, bool bColumnsOnly, bool bStickyTopRow=false, bool bStickyLeftCol=false, ScDataAreaExtras *pDataAreaExtras=nullptr) const
Shrink a range to only include used data area.
Definition: document.cxx:1049
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:625
void EnableExecuteLink(bool bVal)
Definition: document.hxx:1603
ScExtDocOptions * GetExtDocOptions()
Definition: document.hxx:644
bool IsFunctionAccess() const
Definition: document.hxx:1597
bool IsExecuteLinkEnabled() const
Definition: document.hxx:1602
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1083
SC_DLLPUBLIC svl::SharedStringPool & GetSharedStringPool()
Definition: documen2.cxx:601
SC_DLLPUBLIC void SetExtDocOptions(std::unique_ptr< ScExtDocOptions > pNewOptions)
Definition: documen3.cxx:2016
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:467
SC_DLLPUBLIC ScRangeName * GetRangeName(SCTAB nTab) const
Definition: documen3.cxx:171
SC_DLLPUBLIC const ScFormulaCell * GetFormulaCell(const ScAddress &rPos) const
Definition: document.cxx:3714
SC_DLLPUBLIC sfx2::LinkManager * GetLinkManager()
Definition: documen2.cxx:231
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:204
void LockAdjustHeight()
Definition: document.hxx:1600
void FillMatrix(ScMatrix &rMat, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool *pPool=nullptr) const
Definition: documen8.cxx:384
SC_DLLPUBLIC SCTAB GetTableCount() const
Definition: document.cxx:297
Extended options held by an ScDocument containing additional settings for filters.
Definition: scextopt.hxx:77
const ScExtDocSettings & GetDocSettings() const
Definition: scextopt.cxx:170
void getAllRows(::std::vector< SCROW > &rRows, SCROW nLow, SCROW nHigh) const
Obtain a sorted vector of rows.
bool hasRow(SCROW nRow) const
SC_DLLPUBLIC TokenRef getCell(SCCOL nCol, SCROW nRow, sal_uInt32 *pnFmtIndex=nullptr) const
void getAllNumberFormats(::std::vector< sal_uInt32 > &rNumFmts) const
bool isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
void setReferenced(bool bReferenced)
Set/clear referenced status flag only if current status is not REFERENCED_PERMANENT.
SC_DLLPUBLIC void setWholeTableCached()
Call this to mark the entire table "cached".
SC_DLLPUBLIC void setCell(SCCOL nCol, SCROW nRow, TokenRef const &pToken, sal_uLong nFmtIndex=0, bool bSetCacheRange=true)
Add cell value to the cache.
bool isInCachedRanges(SCCOL nCol, SCROW nRow) const
TokenRef getEmptyOrNullToken(SCCOL nCol, SCROW nRow) const
void setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
void setCachedCell(SCCOL nCol, SCROW nRow)
SC_DLLPUBLIC ::std::pair< SCROW, SCROW > getRowRange() const
Returns the half-open range of used rows in this table. Returns [0,0) if table is empty.
SC_DLLPUBLIC ::std::pair< SCCOL, SCCOL > getColRange(SCROW nRow) const
Returns the half-open range of used columns in the specified row. Returns [0,0) if row is empty.
void getAllCols(SCROW nRow, ::std::vector< SCCOL > &rCols, SCCOL nLow, SCCOL nHigh) const
Obtain a sorted vector of columns.
Cache table for external reference data.
bool setCacheDocReferenced(sal_uInt16 nFileId)
Set all tables of a document as referenced, used only during store-to-file.
bool isValidRangeName(sal_uInt16 nFileId, const OUString &rName) const
bool isDocInitialized(sal_uInt16 nFileId)
void clearCache(sal_uInt16 nFileId)
Clear all caches including the cache tables.
const ScDocument & mrDoc
std::unordered_map< OUString, TokenArrayRef > RangeNameMap
std::shared_ptr< Table > TableTypeRef
void getAllTableNames(sal_uInt16 nFileId, ::std::vector< OUString > &rTabNames) const
ScExternalRefCache::TokenArrayRef getCellRangeData(sal_uInt16 nFileId, const OUString &rTabName, const ScRange &rRange)
Get a cached cell range data.
std::unordered_map< OUString, size_t > TableNameIndexMap
DocItem * getDocItem(sal_uInt16 nFileId) const
ScExternalRefCache::TokenArrayRef getRangeNameTokens(sal_uInt16 nFileId, const OUString &rName)
void setRangeNameTokens(sal_uInt16 nFileId, const OUString &rName, TokenArrayRef pArray)
void getAllNumberFormats(::std::vector< sal_uInt32 > &rNumFmts) const
struct ScExternalRefCache::ReferencedStatus maReferenced
void setAllCacheTableReferencedStati(bool bReferenced)
void setRangeName(sal_uInt16 nFileId, const OUString &rName)
void getAllCachedDataSpans(const ScDocument &rSrcDoc, sal_uInt16 nFileId, sc::ColumnSpanSet &rSet) const
Collect all cached non-empty cell positions, inferred directly from the cached data,...
bool setCacheTableReferenced(sal_uInt16 nFileId, const OUString &rTabName, size_t nSheets)
Set a table as referenced, used only during store-to-file.
const ScDocument & getDoc() const
void setCellRangeData(sal_uInt16 nFileId, const ScRange &rRange, const ::std::vector< SingleRangeData > &rData, const TokenArrayRef &pArray)
SCTAB getTabSpan(sal_uInt16 nFileId, const OUString &rStartTabName, const OUString &rEndTabName) const
void clearCacheTables(sal_uInt16 nFileId)
Clear all caches but keep the tables.
std::unordered_map< SCCOL, Cell > RowDataType
void addCacheDocToReferenced(sal_uInt16 nFileId)
ScExternalRefCache(const ScDocument &rDoc)
void initializeDoc(sal_uInt16 nFileId, const ::std::vector< OUString > &rTabNames, const OUString &rBaseName)
void addCacheTableToReferenced(sal_uInt16 nFileId, size_t nIndex)
::formula::FormulaTokenRef TokenRef
OUString getTableName(sal_uInt16 nFileId, size_t nCacheId) const
const OUString * getRealRangeName(sal_uInt16 nFileId, const OUString &rRangeName) const
bool getSrcDocTable(const ScDocument &rSrcDoc, const OUString &rTabName, SCTAB &rTab, sal_uInt16 nFileId) const
ScExternalRefCache::TokenRef getCellData(sal_uInt16 nFileId, const OUString &rTabName, SCCOL nCol, SCROW nRow, sal_uInt32 *pnFmtIndex)
Get a cached cell data at specified cell location.
bool areAllCacheTablesReferenced() const
const OUString * getRealTableName(sal_uInt16 nFileId, const OUString &rTabName) const
std::shared_ptr< ScTokenArray > TokenArrayRef
void setCellData(sal_uInt16 nFileId, const OUString &rTabName, SCCOL nCol, SCROW nRow, TokenRef const &pToken, sal_uLong nFmtIndex)
ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
ScExternalRefManager * mpMgr
ApiGuard(const ScDocument &rDoc)
Base class for objects that need to listen to link updates.
virtual ~LinkListener() COVERITY_NOEXCEPT_FALSE=0
sal_uInt16 getExternalFileCount() const
sal_uInt32 getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument &rSrcDoc)
SCTAB getCachedTabSpan(sal_uInt16 nFileId, const OUString &rStartTabName, const OUString &rEndTabName) const
Get the span (distance+sign(distance)) of two sheets of a specified file.
void setFilterData(sal_uInt16 nFileId, const OUString &rFilterName, const OUString &rOptions)
Set the filter name and options if any for a given source document.
bool isFileLoadable(const OUString &rFile) const
Check if the file specified by the path is a legitimate file that exists & can be loaded.
DocShellMap maUnsavedDocShells
DocShells to unsaved but referenced documents.
DocShellMap maDocShells
Source document cache.
ScExternalRefCache maRefCache
cache of referenced ranges and names from source documents.
void insertRefCellFromTemplate(ScFormulaCell *pTemplateCell, ScFormulaCell *pCell)
Add a cell to reference the same files as the template cell.
void enableDocTimer(bool bEnable)
SfxObjectShellRef loadSrcDocument(sal_uInt16 nFileId, OUString &rFilter)
bool getSrcDocTable(const ScDocument &rSrcDoc, const OUString &rTabName, SCTAB &rTab, sal_uInt16 nFileId) const
ScExternalRefCache::TokenArrayRef getDoubleRefTokensFromSrcDoc(const ScDocument &rSrcDoc, const OUString &rTabName, ScRange &rRange, ::std::vector< ScExternalRefCache::SingleRangeData > &rCacheData)
Retrieve a range token array from a source document instance.
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
LinkListenerMap maLinkListeners
sal_uInt16 getExternalFileId(const OUString &rFile)
const OUString * getRealTableName(sal_uInt16 nFileId, const OUString &rTabName) const
LinkedDocMap maLinkedDocs
list of source documents that are managed by the link manager.
void maybeLinkExternalFile(sal_uInt16 nFileId, bool bDeferFilterDetection=false)
void resetSrcFileData(const OUString &rBaseFileUrl)
Re-generates relative names for all stored source files.
std::vector< sal_uInt16 > maConvertFileIdToUsedFileId
void getAllCachedNumberFormats(::std::vector< sal_uInt32 > &rNumFmts) const
Get all unique number format indices that are used in the cache tables.
ScExternalRefManager(ScDocument &rDoc)
void removeRefCell(ScFormulaCell *pCell)
Stop tracking a specific formula cell.
ScDocument & cacheNewDocShell(sal_uInt16 nFileId, SrcShell &rSrcShell)
Caller must ensure that the passed shell is not already stored.
void setAllCacheTableReferencedStati(bool bReferenced)
std::set< ScFormulaCell * > RefCellSet
virtual ~ScExternalRefManager() override
void addLinkListener(sal_uInt16 nFileId, LinkListener *pListener)
Register a new link listener to a specified external document.
virtual OUString getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const override
void maybeCreateRealFileName(sal_uInt16 nFileId)
Try to create a "real" file name from the relative path.
bool setCacheTableReferenced(sal_uInt16 nFileId, const OUString &rTabName, size_t nSheets)
Set a table as referenced, used only during store-to-file.
bool hasExternalFile(sal_uInt16 nFileId) const
ScDocument * getSrcDocument(sal_uInt16 nFileId)
o3tl::sorted_vector< LinkListener * > LinkListeners
bool isValidRangeName(sal_uInt16 nFileId, const OUString &rName)
void setSkipUnusedFileIds(std::vector< sal_uInt16 > &pExternFileIds)
sal_uInt16 convertFileIdToUsedFileId(sal_uInt16 nFileId)
Reindex external file references to skip unused files, if skipping is enabled.
void switchSrcFile(sal_uInt16 nFileId, const OUString &rNewFile, const OUString &rNewFilter)
ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
Get a cache table instance for specified table and table index.
void refreshAllRefCells(sal_uInt16 nFileId)
const SrcFileData * getExternalFileData(sal_uInt16 nFileId) const
void removeLinkListener(sal_uInt16 nFileId, LinkListener *pListener)
Remove an existing link listener.
void updateAbsAfterLoad()
Replace the original URL with the real URL that was generated from the relative URL.
bool mbInReferenceMarking
Status whether in reference marking state.
RefCellMap maRefCells
List of referencing cells that may contain external names.
void markUsedByLinkListeners()
Mark all tables as referenced that are used by any LinkListener, used only during store-to-file.
const OUString * getRealRangeName(sal_uInt16 nFileId, const OUString &rRangeName) const
void addFilesToLinkManager()
Add all known external files to the LinkManager.
OUString getOwnDocumentName() const
bool hasCellExternalReference(const ScAddress &rCell)
const OUString * getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal=false)
It returns a pointer to the name of the URI associated with a given external file ID.
ScExternalRefCache::TokenRef getSingleRefTokenFromSrcDoc(sal_uInt16 nFileId, ScDocument &rSrcDoc, const ScAddress &rPos, ScExternalRefCache::CellFormat *pFmt)
ScExternalRefCache::TokenArrayRef getDoubleRefTokens(sal_uInt16 nFileId, const OUString &rTabName, const ScRange &rRange, const ScAddress *pCurPos)
Get an array of tokens that consist of the specified external cell range.
void transformUnsavedRefToSavedRef(SfxObjectShell *pShell)
If in maUnsavedDocShells move it to maDocShells and create a correct external reference entry.
void convertToAbsName(OUString &rFile) const
Takes a flat file name, and convert it to an absolute URL path.
void insertRefCell(sal_uInt16 nFileId, const ScAddress &rCell)
void getAllCachedTableNames(sal_uInt16 nFileId, ::std::vector< OUString > &rTabNames) const
Returns a vector containing all (real) table names and cache tables of the specified file.
bool hasExternalData() const
void notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
Notify all listeners that are listening to a specified external document.
bool mbUserInteractionEnabled
Controls whether or not to allow user interaction.
std::vector< SrcFileData > maSrcFiles
List of external source document meta-data, used to keep track of external document identifiers.
void clearCache(sal_uInt16 nFileId)
void setRelativeFileName(sal_uInt16 nFileId, const OUString &rRelUrl)
Set a relative file path for the specified file ID.
std::vector< OUString > getAllCachedExternalFileNames() const
Get all cached external file names as an array.
bool isOwnDocument(std::u16string_view rFile) const
void fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat *pFmt) const
ScDocument * getInMemorySrcDocument(sal_uInt16 nFileId)
void storeRangeNameTokens(sal_uInt16 nFileId, const OUString &rName, const ScTokenArray &rArray)
ScExternalRefCache::TokenRef getSingleRefToken(sal_uInt16 nFileId, const OUString &rTabName, const ScAddress &rCell, const ScAddress *pCurPos, SCTAB *pTab, ScExternalRefCache::CellFormat *pFmt=nullptr)
void purgeStaleSrcDocument(sal_Int32 nTimeOut)
Purge those source document instances that have not been accessed for the specified duration.
ScExternalRefCache::TokenArrayRef getRangeNameTokens(sal_uInt16 nFileId, const OUString &rName, const ScAddress *pCurPos=nullptr)
Get an array of tokens corresponding with a specified name in a specified file.
void breakLink(sal_uInt16 nFileId)
static ScExternalRefCache::TokenArrayRef getRangeNameTokensFromSrcDoc(sal_uInt16 nFileId, const ScDocument &rSrcDoc, OUString &rName)
Retrieve range name token array from a source document instance.
bool refreshSrcDocument(sal_uInt16 nFileId)
sc::FormulaResultValue GetResult()
void SetCompile(bool bVal)
double GetValue()
const svl::SharedString & GetString()
FormulaError GetErrCode()
void SetIsExtRef()
bool MarkUsedExternalReferences()
void SetDirty(bool bDirtyFlag=true)
ScTokenArray * GetCode()
ScAddress aPos
void CompileTokenArray(bool bNoListening=false)
static SC_DLLPUBLIC ::utl::TransliterationWrapper & GetTransliteration()
Definition: global.cxx:1026
static SC_DLLPUBLIC OUString GetAbsDocName(const OUString &rFileName, const SfxObjectShell *pShell)
Definition: global2.cxx:287
static SC_DLLPUBLIC const CharClass & getCharClass()
Definition: global.cxx:1064
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:101
std::function< void(size_t, size_t)> EmptyOpFunction
Definition: scmatrix.hxx:120
std::function< void(size_t, size_t, svl::SharedString)> StringOpFunction
Definition: scmatrix.hxx:119
std::function< void(size_t, size_t, double)> DoubleOpFunction
Definition: scmatrix.hxx:117
std::function< void(size_t, size_t, bool)> BoolOpFunction
Definition: scmatrix.hxx:118
ScTokenArray * GetCode()
Definition: rangenam.hxx:119
void GetName(OUString &rName) const
Definition: rangenam.hxx:110
SC_DLLPUBLIC ScRangeData * findByUpperName(const OUString &rName)
Definition: rangenam.cxx:704
void erase(const ScRangeData &r)
Definition: rangenam.cxx:846
SC_DLLPUBLIC const_iterator end() const
Definition: rangenam.hxx:246
DataType::const_iterator const_iterator
Definition: rangenam.hxx:203
SC_DLLPUBLIC const_iterator begin() const
Definition: rangenam.hxx:245
void PutInOrder()
Definition: address.hxx:622
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
void PaintGrid()
Definition: tabview3.cxx:2656
std::unique_ptr< ScTokenArray > Clone() const
Definition: token.cxx:1931
ScDocShell * GetDocShell() const
Definition: viewdata.hxx:354
ScTabViewShell * GetViewShell() const
Definition: viewdata.hxx:357
SfxEventHintId GetEventId() const
const T * GetItemIfSet(TypedWhichId< T > nWhich, bool bSrchInParent=true) const
void StartListening(SfxBroadcaster &rBroadcaster, DuplicateHandling eDuplicateHanding=DuplicateHandling::Unexpected)
void EndListening(SfxBroadcaster &rBroadcaster, bool bRemoveAllDuplicates=false)
const INetURLObject & GetURLObject() const
SfxItemSet & GetItemSet() const
const OUString & GetName() const
comphelper::EmbeddedObjectContainer & GetEmbeddedObjectContainer() const
static SAL_WARN_UNUSED_RESULT SfxObjectShell * GetNext(const SfxObjectShell &rPrev, const std::function< bool(const SfxObjectShell *)> &isObjectShell=nullptr, bool bOnlyVisible=true)
SfxMedium * GetMedium() const
bool DoLoad(SfxMedium *pMedium)
static SAL_WARN_UNUSED_RESULT SfxObjectShell * GetFirst(const std::function< bool(const SfxObjectShell *)> &isObjectShell=nullptr, bool bOnlyVisible=true)
const OUString & GetName() const
virtual void Invalidate(sal_uInt16 nId=0)
SvNumFormatType GetType(sal_uInt32 nFIndex) const
SvNumberFormatterMergeMap ConvertMergeTableToMap()
SvNumberFormatterIndexTable * MergeFormatter(SvNumberFormatter &rNewTable)
void Stop()
void SetTimeout(sal_uInt64 nTimeoutMs)
void SetInvokeHandler(const Link< Timer *, void > &rLink)
virtual void Start(bool bStartTimer=true) override
void SetCodeError(FormulaError n)
FormulaError GetCodeError() const
bool HasOpCode(OpCode) const
const_iterator begin() const
size_type erase(const Value &x)
bool empty() const
const_iterator end() const
std::pair< const_iterator, bool > insert(Value &&x)
Structure that stores segments of boolean flags per column, and perform custom action on those segmen...
void executeColumnAction(ScDocument &rDoc, ColumnAction &ac) const
void InsertFileLink(sfx2::SvBaseLink &, SvBaseLinkObjectType nFileType, std::u16string_view rFileNm, const OUString *pFilterNm=nullptr, const OUString *pRange=nullptr)
static bool GetDisplayNames(const SvBaseLink *, OUString *pType, OUString *pFile=nullptr, OUString *pLink=nullptr, OUString *pFilter=nullptr)
SharedString intern(const OUString &rStr)
const OUString & getString() const
bool is() const
static bool IsFuzzing()
float v
float u
#define ERRCODE_NONE
FormulaError
FormulaError GetDoubleErrorValue(double fVal)
SfxEventHintId
static std::unique_ptr< ScTokenArray > convertToTokenArray(ScDocument &rHostDoc, const ScDocument &rSrcDoc, ScRange &rRange, vector< ScExternalRefCache::SingleRangeData > &rCacheData)
static FormulaToken * convertToToken(ScDocument &rHostDoc, const ScDocument &rSrcDoc, ScRefCellValue &rCell)
#define SRCDOC_SCAN_INTERVAL
static std::unique_ptr< ScTokenArray > lcl_fillEmptyMatrix(const ScDocument &rDoc, const ScRange &rRange)
#define SRCDOC_LIFE_SPAN
static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer &rMap)
static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap &rMap, const OUString &rName, size_t &rIndex)
IMPL_LINK(ScExternalRefManager, TimeOutHdl, Timer *, pTimer, void)
DocumentType eType
SotClipboardFormatId
ScFormulaCell * mpCell
@ CELLTYPE_EDIT
Definition: global.hxx:277
@ CELLTYPE_STRING
Definition: global.hxx:275
@ CELLTYPE_FORMULA
Definition: global.hxx:276
@ CELLTYPE_VALUE
Definition: global.hxx:274
double distance
sal_Int32 nIndex
OUString aName
void * p
sal_Int64 n
uno_Any a
SvBaseLink * pLink
SfxLinkUpdateMode
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
aStr
RttiCompleteObjectLocator col
constexpr OUStringLiteral aData
SVL_DLLPUBLIC OUString simpleNormalizedMakeRelative(OUString const &baseUriReference, OUString const &uriReference)
svDouble
svDoubleRef
svString
svSingleRef
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
UNOTOOLS_DLLPUBLIC bool Exists(OUString const &url)
UNOTOOLS_DLLPUBLIC bool IsFolder(OUString const &url)
HashMap_OWString_Interface aMap
sal_Int16 nId
ocIndirect
ocSep
sal_Int32 mnType
OUString ScResId(TranslateId aId)
Definition: scdll.cxx:90
#define SC_MOD()
Definition: scmod.hxx:247
static SfxItemSet & rSet
sal_uIntPtr sal_uLong
sal_uInt32 mnLinkCnt
Recursive counter for loading external documents.
Definition: scextopt.hxx:31
individual cell within cached external ref table.
Represents data cached for a single external document.
TableNameIndexMap::const_iterator findTableNameIndex(const OUString &rTabName) const
bool getTableDataIndex(const OUString &rTabName, size_t &rIndex) const
::std::vector< TableTypeRef > maTables
The raw cache tables.
TableNameIndexMap maTableNameIndex
Table name to index map.
RangeNameMap maRangeNames
Range name cache.
::std::vector< TableName > maTableNames
Table name list in correct order, in both upper- and real-case.
NamePairMap maRealRangeNameMap
Upper- to real-case mapping for range names.
OUString maSingleTableNameAlias
Either the base name that was stored as sheet name for CSV files if sheet name is Sheet1,...
bool getSingleTableNameAlternative(OUString &rTabName) const
RangeArrayMap maRangeArrays
Token array cache for cell ranges.
::std::vector< DocReferenced > DocReferencedVec
TableName(OUString aUpper, OUString aReal)
Source document meta-data container.
void maybeCreateRealFileName(std::u16string_view rOwnDocName)
OUString maRealFileName
original file name as loaded from the file.
OUString maRelativeName
file name created from the relative name.
Shell instance for a source document.
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:108
ScFormulaCell * getFormula() const
Definition: cellvalue.hxx:137
bool hasEmptyValue()
Definition: cellvalue.cxx:672
double getDouble() const
Definition: cellvalue.hxx:134
OUString getString(const ScDocument *pDoc) const
Retrieve string value.
Definition: cellvalue.cxx:657
CellType getType() const
Definition: cellvalue.hxx:133
Store parameters used in the ScDocument::SetString() method.
Definition: stringutil.hxx:35
void setTextInput()
Call this whenever you need to unconditionally set input as text, no matter what the input is.
Definition: stringutil.cxx:39
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
SCTAB Tab() const
Definition: refdata.cxx:254
svl::SharedString maString
sal_uInt32 mnIndex
#define SAL_MAX_UINT16
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
::boost::intrusive_ptr< ScMatrix > ScMatrixRef
Definition: types.hxx:25
sal_Int32 SCROW
Definition: types.hxx:17
RedlineType meType
SvNumFormatType
std::unordered_map< sal_uInt32, sal_uInt32 > SvNumberFormatterMergeMap