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