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 <scitems.hxx>
21 #include <comphelper/fileformat.h>
22 #include <comphelper/processfactory.hxx>
23 #include <comphelper/servicehelper.hxx>
24 #include <officecfg/Office/Common.hxx>
25 #include <tools/urlobj.hxx>
26 #include <editeng/frmdiritem.hxx>
27 #include <editeng/langitem.hxx>
28 #include <sfx2/linkmgr.hxx>
29 #include <sfx2/bindings.hxx>
30 #include <sfx2/objsh.hxx>
31 #include <sfx2/printer.hxx>
32 #include <sfx2/viewfrm.hxx>
33 #include <sfx2/viewsh.hxx>
34 #include <svl/flagitem.hxx>
35 #include <svl/intitem.hxx>
36 #include <svl/zforlist.hxx>
37 #include <svl/zformat.hxx>
38 #include <unotools/transliterationwrapper.hxx>
39 #include <sal/log.hxx>
40 #include <osl/diagnose.h>
41
42 #include <vcl/svapp.hxx>
43 #include <vcl/virdev.hxx>
44 #include <vcl/weld.hxx>
45 #include <vcl/TaskStopwatch.hxx>
46
47 #include <inputopt.hxx>
48 #include <global.hxx>
49 #include <table.hxx>
50 #include <column.hxx>
51 #include <poolhelp.hxx>
52 #include <docpool.hxx>
53 #include <stlpool.hxx>
54 #include <stlsheet.hxx>
55 #include <docoptio.hxx>
56 #include <viewopti.hxx>
57 #include <scextopt.hxx>
58 #include <rechead.hxx>
59 #include <ddelink.hxx>
60 #include <scmatrix.hxx>
61 #include <arealink.hxx>
62 #include <patattr.hxx>
63 #include <editutil.hxx>
64 #include <progress.hxx>
65 #include <document.hxx>
66 #include <chartlis.hxx>
67 #include <chartlock.hxx>
68 #include <refupdat.hxx>
69 #include <markdata.hxx>
70 #include <scmod.hxx>
71 #include <externalrefmgr.hxx>
72 #include <globstr.hrc>
73 #include <strings.hrc>
74 #include <sc.hrc>
75 #include <charthelper.hxx>
76 #include <macromgr.hxx>
77 #include <docuno.hxx>
78 #include <scresid.hxx>
79 #include <columniterator.hxx>
80 #include <globalnames.hxx>
81 #include <stringutil.hxx>
82 #include <documentlinkmgr.hxx>
83 #include <tokenarray.hxx>
84 #include <recursionhelper.hxx>
85
86 #include <memory>
87 #include <utility>
88
89 using namespace com::sun::star;
90
91 namespace {
92
getScaleValue(SfxStyleSheetBase & rStyle,sal_uInt16 nWhich)93 sal_uInt16 getScaleValue(SfxStyleSheetBase& rStyle, sal_uInt16 nWhich)
94 {
95 return static_cast<const SfxUInt16Item&>(rStyle.GetItemSet().Get(nWhich)).GetValue();
96 }
97
98 }
99
ImplCreateOptions()100 void ScDocument::ImplCreateOptions()
101 {
102 pDocOptions.reset( new ScDocOptions() );
103 pViewOptions.reset( new ScViewOptions() );
104 }
105
ImplDeleteOptions()106 void ScDocument::ImplDeleteOptions()
107 {
108 pDocOptions.reset();
109 pViewOptions.reset();
110 pExtDocOptions.reset();
111 }
112
GetPrinter(bool bCreateIfNotExist)113 SfxPrinter* ScDocument::GetPrinter(bool bCreateIfNotExist)
114 {
115 if ( !mpPrinter && bCreateIfNotExist )
116 {
117 auto pSet =
118 std::make_unique<SfxItemSet>( *mxPoolHelper->GetDocPool(),
119 svl::Items<SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
120 SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
121 SID_PRINT_SELECTEDSHEET, SID_PRINT_SELECTEDSHEET,
122 SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS>{} );
123
124 SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
125 if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
126 nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
127 if (officecfg::Office::Common::Print::Warning::PaperSize::get())
128 nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
129 pSet->Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
130 pSet->Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );
131
132 mpPrinter = VclPtr<SfxPrinter>::Create( std::move(pSet) );
133 mpPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
134 UpdateDrawPrinter();
135 mpPrinter->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() );
136 }
137
138 return mpPrinter;
139 }
140
SetPrinter(VclPtr<SfxPrinter> const & pNewPrinter)141 void ScDocument::SetPrinter( VclPtr<SfxPrinter> const & pNewPrinter )
142 {
143 if ( pNewPrinter == mpPrinter.get() )
144 {
145 // #i6706# SetPrinter is called with the same printer again if
146 // the JobSetup has changed. In that case just call UpdateDrawPrinter
147 // (SetRefDevice for drawing layer) because of changed text sizes.
148 UpdateDrawPrinter();
149 }
150 else
151 {
152 ScopedVclPtr<SfxPrinter> xKeepAlive( mpPrinter );
153 mpPrinter = pNewPrinter;
154 UpdateDrawPrinter();
155 mpPrinter->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() );
156 }
157 InvalidateTextWidth(nullptr, nullptr, false); // in both cases
158 }
159
SetPrintOptions()160 void ScDocument::SetPrintOptions()
161 {
162 if ( !mpPrinter ) GetPrinter(); // this sets mpPrinter
163 OSL_ENSURE( mpPrinter, "Error in printer creation :-/" );
164
165 if ( !mpPrinter )
166 return;
167
168 SfxItemSet aOptSet( mpPrinter->GetOptions() );
169
170 SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
171 if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
172 nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
173 if (officecfg::Office::Common::Print::Warning::PaperSize::get())
174 nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
175 aOptSet.Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
176 aOptSet.Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );
177
178 mpPrinter->SetOptions( aOptSet );
179 }
180
GetVirtualDevice_100th_mm()181 VirtualDevice* ScDocument::GetVirtualDevice_100th_mm()
182 {
183 if (!mpVirtualDevice_100th_mm)
184 {
185 #ifdef IOS
186 mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::GRAYSCALE);
187 #else
188 mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
189 #endif
190 mpVirtualDevice_100th_mm->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1);
191 MapMode aMapMode( mpVirtualDevice_100th_mm->GetMapMode() );
192 aMapMode.SetMapUnit( MapUnit::Map100thMM );
193 mpVirtualDevice_100th_mm->SetMapMode( aMapMode );
194 }
195 return mpVirtualDevice_100th_mm;
196 }
197
GetRefDevice()198 OutputDevice* ScDocument::GetRefDevice()
199 {
200 // Create printer like ref device, see Writer...
201 OutputDevice* pRefDevice = nullptr;
202 if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
203 pRefDevice = GetPrinter();
204 else
205 pRefDevice = GetVirtualDevice_100th_mm();
206 return pRefDevice;
207 }
208
ModifyStyleSheet(SfxStyleSheetBase & rStyleSheet,const SfxItemSet & rChanges)209 void ScDocument::ModifyStyleSheet( SfxStyleSheetBase& rStyleSheet,
210 const SfxItemSet& rChanges )
211 {
212 SfxItemSet& rSet = rStyleSheet.GetItemSet();
213
214 switch ( rStyleSheet.GetFamily() )
215 {
216 case SfxStyleFamily::Page:
217 {
218 const sal_uInt16 nOldScale = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
219 const sal_uInt16 nOldScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);
220 rSet.Put( rChanges );
221 const sal_uInt16 nNewScale = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
222 const sal_uInt16 nNewScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);
223
224 if ( (nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages) )
225 InvalidateTextWidth( rStyleSheet.GetName() );
226
227 if( SvtLanguageOptions().IsCTLFontEnabled() )
228 {
229 const SfxPoolItem *pItem = nullptr;
230 if( rChanges.GetItemState(ATTR_WRITINGDIR, true, &pItem ) == SfxItemState::SET )
231 ScChartHelper::DoUpdateAllCharts( *this );
232 }
233 }
234 break;
235
236 case SfxStyleFamily::Para:
237 {
238 bool bNumFormatChanged;
239 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
240 rSet, rChanges ) )
241 InvalidateTextWidth( nullptr, nullptr, bNumFormatChanged );
242
243 for (SCTAB nTab=0; nTab<=MAXTAB; ++nTab)
244 if (maTabs[nTab])
245 maTabs[nTab]->SetStreamValid( false );
246
247 sal_uLong nOldFormat =
248 rSet.Get( ATTR_VALUE_FORMAT ).GetValue();
249 sal_uLong nNewFormat =
250 rChanges.Get( ATTR_VALUE_FORMAT ).GetValue();
251 LanguageType eNewLang, eOldLang;
252 eNewLang = eOldLang = LANGUAGE_DONTKNOW;
253 if ( nNewFormat != nOldFormat )
254 {
255 SvNumberFormatter* pFormatter = GetFormatTable();
256 eOldLang = pFormatter->GetEntry( nOldFormat )->GetLanguage();
257 eNewLang = pFormatter->GetEntry( nNewFormat )->GetLanguage();
258 }
259
260 // Explanation to Items in rChanges:
261 // Set Item - take over change
262 // Dontcare - Set Default
263 // Default - No change
264 // ("no change" is not possible with PutExtended, thus the loop)
265 for (sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; nWhich++)
266 {
267 const SfxPoolItem* pItem;
268 SfxItemState eState = rChanges.GetItemState( nWhich, false, &pItem );
269 if ( eState == SfxItemState::SET )
270 rSet.Put( *pItem );
271 else if ( eState == SfxItemState::DONTCARE )
272 rSet.ClearItem( nWhich );
273 // when Default nothing
274 }
275
276 if ( eNewLang != eOldLang )
277 rSet.Put(
278 SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
279 }
280 break;
281 default:
282 {
283 // added to avoid warnings
284 }
285 }
286 }
287
CopyStdStylesFrom(const ScDocument & rSrcDoc)288 void ScDocument::CopyStdStylesFrom( const ScDocument& rSrcDoc )
289 {
290 // number format exchange list has to be handled here, too
291 NumFmtMergeHandler aNumFmtMergeHdl(*this, rSrcDoc);
292 mxPoolHelper->GetStylePool()->CopyStdStylesFrom( rSrcDoc.mxPoolHelper->GetStylePool() );
293 }
294
InvalidateTextWidth(std::u16string_view rStyleName)295 void ScDocument::InvalidateTextWidth( std::u16string_view rStyleName )
296 {
297 const SCTAB nCount = GetTableCount();
298 for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
299 if ( maTabs[i]->GetPageStyle() == rStyleName )
300 InvalidateTextWidth( i );
301 }
302
InvalidateTextWidth(SCTAB nTab)303 void ScDocument::InvalidateTextWidth( SCTAB nTab )
304 {
305 ScAddress aAdrFrom( 0, 0, nTab );
306 ScAddress aAdrTo ( MaxCol(), MaxRow(), nTab );
307 InvalidateTextWidth( &aAdrFrom, &aAdrTo, false );
308 }
309
IsPageStyleInUse(std::u16string_view rStrPageStyle,SCTAB * pInTab)310 bool ScDocument::IsPageStyleInUse( std::u16string_view rStrPageStyle, SCTAB* pInTab )
311 {
312 bool bInUse = false;
313 const SCTAB nCount = GetTableCount();
314 SCTAB i;
315
316 for ( i = 0; !bInUse && i < nCount && maTabs[i]; i++ )
317 bInUse = ( maTabs[i]->GetPageStyle() == rStrPageStyle );
318
319 if ( pInTab )
320 *pInTab = i-1;
321
322 return bInUse;
323 }
324
RemovePageStyleInUse(std::u16string_view rStyle)325 bool ScDocument::RemovePageStyleInUse( std::u16string_view rStyle )
326 {
327 bool bWasInUse = false;
328 const SCTAB nCount = GetTableCount();
329
330 for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
331 if ( maTabs[i]->GetPageStyle() == rStyle )
332 {
333 bWasInUse = true;
334 maTabs[i]->SetPageStyle( ScResId(STR_STYLENAME_STANDARD) );
335 }
336
337 return bWasInUse;
338 }
339
RenamePageStyleInUse(std::u16string_view rOld,const OUString & rNew)340 bool ScDocument::RenamePageStyleInUse( std::u16string_view rOld, const OUString& rNew )
341 {
342 bool bWasInUse = false;
343 const SCTAB nCount = GetTableCount();
344
345 for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
346 if ( maTabs[i]->GetPageStyle() == rOld )
347 {
348 bWasInUse = true;
349 maTabs[i]->SetPageStyle( rNew );
350 }
351
352 return bWasInUse;
353 }
354
GetEditTextDirection(SCTAB nTab) const355 EEHorizontalTextDirection ScDocument::GetEditTextDirection(SCTAB nTab) const
356 {
357 EEHorizontalTextDirection eRet = EEHorizontalTextDirection::Default;
358
359 OUString aStyleName = GetPageStyle( nTab );
360 SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( aStyleName, SfxStyleFamily::Page );
361 if ( pStyle )
362 {
363 SfxItemSet& rStyleSet = pStyle->GetItemSet();
364 SvxFrameDirection eDirection =
365 rStyleSet.Get( ATTR_WRITINGDIR ).GetValue();
366
367 if ( eDirection == SvxFrameDirection::Horizontal_LR_TB )
368 eRet = EEHorizontalTextDirection::L2R;
369 else if ( eDirection == SvxFrameDirection::Horizontal_RL_TB )
370 eRet = EEHorizontalTextDirection::R2L;
371 // else (invalid for EditEngine): keep "default"
372 }
373
374 return eRet;
375 }
376
GetMacroManager()377 ScMacroManager* ScDocument::GetMacroManager()
378 {
379 if (!mpMacroMgr)
380 mpMacroMgr.reset(new ScMacroManager(*this));
381 return mpMacroMgr.get();
382 }
383
FillMatrix(ScMatrix & rMat,SCTAB nTab,SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,svl::SharedStringPool * pPool) const384 void ScDocument::FillMatrix(
385 ScMatrix& rMat, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool* pPool ) const
386 {
387 const ScTable* pTab = FetchTable(nTab);
388 if (!pTab)
389 return;
390
391 if (nCol1 > nCol2 || nRow1 > nRow2)
392 return;
393
394 SCSIZE nC, nR;
395 rMat.GetDimensions(nC, nR);
396 if (static_cast<SCROW>(nR) != nRow2 - nRow1 + 1 || static_cast<SCCOL>(nC) != nCol2 - nCol1 + 1)
397 return;
398
399 pTab->FillMatrix(rMat, nCol1, nRow1, nCol2, nRow2, pPool);
400 }
401
SetFormulaResults(const ScAddress & rTopPos,const double * pResults,size_t nLen)402 void ScDocument::SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen )
403 {
404 ScTable* pTab = FetchTable(rTopPos.Tab());
405 if (!pTab)
406 return;
407
408 pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen);
409 }
410
CalculateInColumnInThread(ScInterpreterContext & rContext,const ScRange & rCalcRange,unsigned nThisThread,unsigned nThreadsTotal)411 void ScDocument::CalculateInColumnInThread( ScInterpreterContext& rContext, const ScRange& rCalcRange, unsigned nThisThread, unsigned nThreadsTotal)
412 {
413 ScTable* pTab = FetchTable(rCalcRange.aStart.Tab());
414 if (!pTab)
415 return;
416
417 assert(IsThreadedGroupCalcInProgress());
418
419 maThreadSpecific.pContext = &rContext;
420 pTab->CalculateInColumnInThread(rContext, rCalcRange.aStart.Col(), rCalcRange.aEnd.Col(), rCalcRange.aStart.Row(), rCalcRange.aEnd.Row(), nThisThread, nThreadsTotal);
421
422 assert(IsThreadedGroupCalcInProgress());
423 maThreadSpecific.pContext = nullptr;
424 // If any of the thread_local data would cause problems if they stay around for too long
425 // (and e.g. outlive the ScDocument), clean them up here, they cannot be cleaned up
426 // later from the main thread.
427 if(maThreadSpecific.xRecursionHelper)
428 maThreadSpecific.xRecursionHelper->Clear();
429 }
430
HandleStuffAfterParallelCalculation(SCCOL nColStart,SCCOL nColEnd,SCROW nRow,size_t nLen,SCTAB nTab,ScInterpreter * pInterpreter)431 void ScDocument::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen, SCTAB nTab, ScInterpreter* pInterpreter )
432 {
433 assert(!IsThreadedGroupCalcInProgress());
434 for( const DelayedSetNumberFormat& data : GetNonThreadedContext().maDelayedSetNumberFormat)
435 SetNumberFormat( ScAddress( data.mCol, data.mRow, nTab ), data.mnNumberFormat );
436 GetNonThreadedContext().maDelayedSetNumberFormat.clear();
437
438 ScTable* pTab = FetchTable(nTab);
439 if (!pTab)
440 return;
441
442 pTab->HandleStuffAfterParallelCalculation(nColStart, nColEnd, nRow, nLen, pInterpreter);
443 }
444
InvalidateTextWidth(const ScAddress * pAdrFrom,const ScAddress * pAdrTo,bool bNumFormatChanged)445 void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo,
446 bool bNumFormatChanged )
447 {
448 bool bBroadcast = (bNumFormatChanged && GetDocOptions().IsCalcAsShown() && !IsImportingXML() && !IsClipboard());
449 if ( pAdrFrom && !pAdrTo )
450 {
451 const SCTAB nTab = pAdrFrom->Tab();
452
453 if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
454 maTabs[nTab]->InvalidateTextWidth( pAdrFrom, nullptr, bNumFormatChanged, bBroadcast );
455 }
456 else
457 {
458 const SCTAB nTabStart = pAdrFrom ? pAdrFrom->Tab() : 0;
459 const SCTAB nTabEnd = pAdrTo ? pAdrTo->Tab() : MAXTAB;
460
461 for ( SCTAB nTab=nTabStart; nTab<=nTabEnd && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
462 if ( maTabs[nTab] )
463 maTabs[nTab]->InvalidateTextWidth( pAdrFrom, pAdrTo, bNumFormatChanged, bBroadcast );
464 }
465 }
466
467 #define CALCMAX 1000 // Calculations
468
469 namespace {
470
471 class IdleCalcTextWidthScope : public TaskStopwatch
472 {
473 ScDocument& mrDoc;
474 ScAddress& mrCalcPos;
475 MapMode maOldMapMode;
476 ScStyleSheetPool* mpStylePool;
477 bool mbNeedMore;
478 bool mbProgress;
479
480 public:
IdleCalcTextWidthScope(ScDocument & rDoc,ScAddress & rCalcPos)481 IdleCalcTextWidthScope(ScDocument& rDoc, ScAddress& rCalcPos) :
482 mrDoc(rDoc),
483 mrCalcPos(rCalcPos),
484 mpStylePool(rDoc.GetStyleSheetPool()),
485 mbNeedMore(false),
486 mbProgress(false)
487 {
488 mrDoc.EnableIdle(false);
489 }
490
~IdleCalcTextWidthScope()491 ~IdleCalcTextWidthScope() COVERITY_NOEXCEPT_FALSE
492 {
493 SfxPrinter* pDev = mrDoc.GetPrinter();
494 if (pDev)
495 pDev->SetMapMode(maOldMapMode);
496
497 if (mbProgress)
498 ScProgress::DeleteInterpretProgress();
499
500 mrDoc.EnableIdle(true);
501 }
502
Tab() const503 SCTAB Tab() const { return mrCalcPos.Tab(); }
Col() const504 SCCOL Col() const { return mrCalcPos.Col(); }
Row() const505 SCROW Row() const { return mrCalcPos.Row(); }
506
setTab(SCTAB nTab)507 void setTab(SCTAB nTab) { mrCalcPos.SetTab(nTab); }
setCol(SCCOL nCol)508 void setCol(SCCOL nCol) { mrCalcPos.SetCol(nCol); }
setRow(SCROW nRow)509 void setRow(SCROW nRow) { mrCalcPos.SetRow(nRow); }
510
incTab()511 void incTab() { mrCalcPos.IncTab(); }
incCol(SCCOL nInc)512 void incCol(SCCOL nInc) { mrCalcPos.IncCol(nInc); }
513
setOldMapMode(const MapMode & rOldMapMode)514 void setOldMapMode(const MapMode& rOldMapMode) { maOldMapMode = rOldMapMode; }
515
setNeedMore(bool b)516 void setNeedMore(bool b) { mbNeedMore = b; }
getNeedMore() const517 bool getNeedMore() const { return mbNeedMore; }
518
createProgressBar()519 void createProgressBar()
520 {
521 ScProgress::CreateInterpretProgress(&mrDoc, false);
522 mbProgress = true;
523 }
524
hasProgressBar() const525 bool hasProgressBar() const { return mbProgress; }
526
getStylePool()527 ScStyleSheetPool* getStylePool() { return mpStylePool; }
528 };
529
530 }
531
IdleCalcTextWidth()532 bool ScDocument::IdleCalcTextWidth() // true = try next again
533 {
534 // #i75610# if a printer hasn't been set or created yet, don't create one for this
535 if (!mbIdleEnabled || IsInLinkUpdate() || GetPrinter(false) == nullptr)
536 return false;
537
538 IdleCalcTextWidthScope aScope(*this, aCurTextWidthCalcPos);
539
540 if (!ValidRow(aScope.Row()))
541 {
542 aScope.setRow(0);
543 aScope.incCol(-1);
544 }
545
546 if (aScope.Col() < 0)
547 {
548 aScope.setCol(MaxCol());
549 aScope.incTab();
550 }
551
552 if (!ValidTab(aScope.Tab()) || aScope.Tab() >= static_cast<SCTAB>(maTabs.size()) || !maTabs[aScope.Tab()])
553 aScope.setTab(0);
554
555 ScTable* pTab = maTabs[aScope.Tab()].get();
556 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(pTab->aPageStyle, SfxStyleFamily::Page));
557 OSL_ENSURE( pStyle, "Missing StyleSheet :-/" );
558
559 if (!pStyle || getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
560 {
561 // Move to the next sheet as the current one has scale-to-pages set,
562 // and bail out.
563 aScope.incTab();
564 return false;
565 }
566
567 sal_uInt16 nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
568 Fraction aZoomFract(nZoom, 100);
569
570 aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
571 // Start at specified cell position (nCol, nRow, nTab).
572 ScColumn* pCol = &pTab->aCol[aScope.Col()];
573 std::optional<ScColumnTextWidthIterator> pColIter(std::in_place, *this, *pCol, aScope.Row(), MaxRow());
574
575 OutputDevice* pDev = nullptr;
576 sal_uInt16 nRestart = 0;
577 sal_uInt16 nCount = 0;
578 while ( (nZoom > 0) && (nCount < CALCMAX) && (nRestart < 2) )
579 {
580 if (pColIter->hasCell())
581 {
582 // More cell in this column.
583 SCROW nRow = pColIter->getPos();
584 aScope.setRow(nRow);
585
586 if (pColIter->getValue() == TEXTWIDTH_DIRTY)
587 {
588 // Calculate text width for this cell.
589 double nPPTX = 0.0;
590 double nPPTY = 0.0;
591 if (!pDev)
592 {
593 pDev = GetPrinter();
594 aScope.setOldMapMode(pDev->GetMapMode());
595 pDev->SetMapMode(MapMode(MapUnit::MapPixel)); // Important for GetNeededSize
596
597 Point aPix1000 = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
598 nPPTX = aPix1000.X() / 1000.0;
599 nPPTY = aPix1000.Y() / 1000.0;
600 }
601
602 if (!aScope.hasProgressBar() && pCol->IsFormulaDirty(nRow))
603 aScope.createProgressBar();
604
605 sal_uInt16 nNewWidth = static_cast<sal_uInt16>(GetNeededSize(
606 aScope.Col(), aScope.Row(), aScope.Tab(),
607 pDev, nPPTX, nPPTY, aZoomFract,aZoomFract, true, true)); // bTotalSize
608
609 pColIter->setValue(nNewWidth);
610 aScope.setNeedMore(true);
611 }
612 pColIter->next();
613 }
614 else
615 {
616 // No more cell in this column. Move to the left column and start at row 0.
617
618 bool bNewTab = false;
619
620 aScope.setRow(0);
621 aScope.incCol(-1);
622
623 if (aScope.Col() < 0)
624 {
625 // No more column to the left. Move to the right-most column of the next sheet.
626 aScope.setCol(MaxCol());
627 aScope.incTab();
628 bNewTab = true;
629 }
630
631 if (!ValidTab(aScope.Tab()) || aScope.Tab() >= static_cast<SCTAB>(maTabs.size()) || !maTabs[aScope.Tab()] )
632 {
633 // Sheet doesn't exist at specified sheet position. Restart at sheet 0.
634 aScope.setTab(0);
635 nRestart++;
636 bNewTab = true;
637 }
638
639 if ( nRestart < 2 )
640 {
641 if ( bNewTab )
642 {
643 pTab = maTabs[aScope.Tab()].get();
644 aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
645 pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(
646 pTab->aPageStyle, SfxStyleFamily::Page));
647
648 if ( pStyle )
649 {
650 // Check if the scale-to-pages setting is set. If
651 // set, we exit the loop. If not, get the page
652 // scale factor of the new sheet.
653 if (getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
654 {
655 nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
656 aZoomFract = Fraction(nZoom, 100);
657 }
658 else
659 nZoom = 0;
660 }
661 else
662 {
663 OSL_FAIL( "Missing StyleSheet :-/" );
664 }
665 }
666
667 if ( nZoom > 0 )
668 {
669 pCol = &pTab->aCol[aScope.Col()];
670 pColIter.emplace(*this, *pCol, aScope.Row(), MaxRow());
671 }
672 else
673 {
674 aScope.incTab(); // Move to the next sheet as the current one has scale-to-pages set.
675 return false;
676 }
677 }
678 }
679
680 ++nCount;
681
682 if (!aScope.continueIter())
683 break;
684 }
685
686 return aScope.getNeedMore();
687 }
688
RepaintRange(const ScRange & rRange)689 void ScDocument::RepaintRange( const ScRange& rRange )
690 {
691 if ( bIsVisible && mpShell )
692 {
693 ScModelObj* pModel = comphelper::getUnoTunnelImplementation<ScModelObj>( mpShell->GetModel() );
694 if ( pModel )
695 pModel->RepaintRange( rRange ); // locked repaints are checked there
696 }
697 }
698
RepaintRange(const ScRangeList & rRange)699 void ScDocument::RepaintRange( const ScRangeList& rRange )
700 {
701 if ( bIsVisible && mpShell )
702 {
703 ScModelObj* pModel = comphelper::getUnoTunnelImplementation<ScModelObj>( mpShell->GetModel() );
704 if ( pModel )
705 pModel->RepaintRange( rRange ); // locked repaints are checked there
706 }
707 }
708
SaveDdeLinks(SvStream & rStream) const709 void ScDocument::SaveDdeLinks(SvStream& rStream) const
710 {
711 // when 4.0-Export, remove all with mode != DEFAULT
712 bool bExport40 = ( rStream.GetVersion() <= SOFFICE_FILEFORMAT_40 );
713
714 const ::sfx2::SvBaseLinks& rLinks = GetLinkManager()->GetLinks();
715 sal_uInt16 nCount = rLinks.size();
716
717 // Count them first
718
719 sal_uInt16 nDdeCount = 0;
720 sal_uInt16 i;
721 for (i=0; i<nCount; i++)
722 {
723 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
724 if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
725 if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
726 ++nDdeCount;
727 }
728
729 // Header
730
731 ScMultipleWriteHeader aHdr( rStream );
732 rStream.WriteUInt16( nDdeCount );
733
734 // Save links
735
736 for (i=0; i<nCount; i++)
737 {
738 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
739 if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
740 {
741 if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
742 pLink->Store( rStream, aHdr );
743 }
744 }
745 }
746
LoadDdeLinks(SvStream & rStream)747 void ScDocument::LoadDdeLinks(SvStream& rStream)
748 {
749 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
750 if (!pMgr)
751 return;
752
753 ScMultipleReadHeader aHdr( rStream );
754
755 sal_uInt16 nCount(0);
756 rStream.ReadUInt16( nCount );
757
758 const rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
759 const size_t nMinStringSize = eCharSet == RTL_TEXTENCODING_UNICODE ? sizeof(sal_uInt32) : sizeof(sal_uInt16);
760 const size_t nMinRecordSize = 1 + nMinStringSize*3;
761 const size_t nMaxRecords = rStream.remainingSize() / nMinRecordSize;
762 if (nCount > nMaxRecords)
763 {
764 SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
765 " max possible entries, but " << nCount << " claimed, truncating");
766 nCount = nMaxRecords;
767 }
768
769 for (sal_uInt16 i=0; i<nCount; ++i)
770 {
771 ScDdeLink* pLink = new ScDdeLink( *this, rStream, aHdr );
772 pMgr->InsertDDELink(pLink, pLink->GetAppl(), pLink->GetTopic(), pLink->GetItem());
773 }
774 }
775
SetInLinkUpdate(bool bSet)776 void ScDocument::SetInLinkUpdate(bool bSet)
777 {
778 // called from TableLink and AreaLink
779
780 OSL_ENSURE( bInLinkUpdate != bSet, "SetInLinkUpdate twice" );
781 bInLinkUpdate = bSet;
782 }
783
IsInLinkUpdate() const784 bool ScDocument::IsInLinkUpdate() const
785 {
786 return bInLinkUpdate || IsInDdeLinkUpdate();
787 }
788
UpdateExternalRefLinks(weld::Window * pWin)789 void ScDocument::UpdateExternalRefLinks(weld::Window* pWin)
790 {
791 if (!pExternalRefMgr)
792 return;
793
794 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
795 if (!pMgr)
796 return;
797
798 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
799 sal_uInt16 nCount = rLinks.size();
800
801 bool bAny = false;
802
803 // Collect all the external ref links first.
804 std::vector<ScExternalRefLink*> aRefLinks;
805 for (sal_uInt16 i = 0; i < nCount; ++i)
806 {
807 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
808 ScExternalRefLink* pRefLink = dynamic_cast<ScExternalRefLink*>(pBase);
809 if (pRefLink)
810 aRefLinks.push_back(pRefLink);
811 }
812
813 weld::WaitObject aWaitSwitch(pWin);
814
815 pExternalRefMgr->enableDocTimer(false);
816 ScProgress aProgress(GetDocumentShell(), ScResId(SCSTR_UPDATE_EXTDOCS), aRefLinks.size(), true);
817 for (size_t i = 0, n = aRefLinks.size(); i < n; ++i)
818 {
819 aProgress.SetState(i+1);
820
821 ScExternalRefLink* pRefLink = aRefLinks[i];
822 if (pRefLink->Update())
823 {
824 bAny = true;
825 continue;
826 }
827
828 // Update failed. Notify the user.
829
830 OUString aFile;
831 sfx2::LinkManager::GetDisplayNames(pRefLink, nullptr, &aFile);
832 // Decode encoded URL for display friendliness.
833 INetURLObject aUrl(aFile,INetURLObject::EncodeMechanism::WasEncoded);
834 aFile = aUrl.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
835
836 OUString sMessage = ScResId(SCSTR_EXTDOC_NOT_LOADED) +
837 "\n\n" +
838 aFile;
839 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
840 VclMessageType::Warning, VclButtonsType::Ok,
841 sMessage));
842 xBox->run();
843 }
844
845 pExternalRefMgr->enableDocTimer(true);
846
847 if (!bAny)
848 return;
849
850 TrackFormulas();
851 mpShell->Broadcast( SfxHint(SfxHintId::ScDataChanged) );
852
853 // #i101960# set document modified, as in TrackTimeHdl for DDE links
854 if (!mpShell->IsModified())
855 {
856 mpShell->SetModified();
857 SfxBindings* pBindings = GetViewBindings();
858 if (pBindings)
859 {
860 pBindings->Invalidate( SID_SAVEDOC );
861 pBindings->Invalidate( SID_DOC_MODIFIED );
862 }
863 }
864 }
865
CopyDdeLinks(ScDocument & rDestDoc) const866 void ScDocument::CopyDdeLinks( ScDocument& rDestDoc ) const
867 {
868 if (bIsClip) // Create from Stream
869 {
870 if (pClipData)
871 {
872 pClipData->Seek(0);
873 rDestDoc.LoadDdeLinks(*pClipData);
874 }
875
876 return;
877 }
878
879 const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
880 if (!pMgr)
881 return;
882
883 sfx2::LinkManager* pDestMgr = rDestDoc.GetDocLinkManager().getLinkManager(rDestDoc.bAutoCalc);
884 if (!pDestMgr)
885 return;
886
887 const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
888 for (const auto & rLink : rLinks)
889 {
890 const sfx2::SvBaseLink* pBase = rLink.get();
891 if (const ScDdeLink* p = dynamic_cast<const ScDdeLink*>(pBase))
892 {
893 ScDdeLink* pNew = new ScDdeLink(rDestDoc, *p);
894 pDestMgr->InsertDDELink(
895 pNew, pNew->GetAppl(), pNew->GetTopic(), pNew->GetItem());
896 }
897 }
898 }
899
900 namespace {
901
902 /** Tries to find the specified DDE link.
903 @param pnDdePos (out-param) if not 0, the index of the DDE link is returned here
904 (does not include other links from link manager).
905 @return The DDE link, if it exists, otherwise 0. */
lclGetDdeLink(const sfx2::LinkManager * pLinkManager,std::u16string_view rAppl,std::u16string_view rTopic,std::u16string_view rItem,sal_uInt8 nMode,size_t * pnDdePos=nullptr)906 ScDdeLink* lclGetDdeLink(
907 const sfx2::LinkManager* pLinkManager,
908 std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem, sal_uInt8 nMode,
909 size_t* pnDdePos = nullptr )
910 {
911 if( pLinkManager )
912 {
913 const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
914 size_t nCount = rLinks.size();
915 if( pnDdePos ) *pnDdePos = 0;
916 for( size_t nIndex = 0; nIndex < nCount; ++nIndex )
917 {
918 ::sfx2::SvBaseLink* pLink = rLinks[ nIndex ].get();
919 if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( pLink ) )
920 {
921 if( (pDdeLink->GetAppl() == rAppl) &&
922 (pDdeLink->GetTopic() == rTopic) &&
923 (pDdeLink->GetItem() == rItem) &&
924 ((nMode == SC_DDE_IGNOREMODE) || (nMode == pDdeLink->GetMode())) )
925 return pDdeLink;
926 if( pnDdePos ) ++*pnDdePos;
927 }
928 }
929 }
930 return nullptr;
931 }
932
933 /** Returns a pointer to the specified DDE link.
934 @param nDdePos Index of the DDE link (does not include other links from link manager).
935 @return The DDE link, if it exists, otherwise 0. */
lclGetDdeLink(const sfx2::LinkManager * pLinkManager,size_t nDdePos)936 ScDdeLink* lclGetDdeLink( const sfx2::LinkManager* pLinkManager, size_t nDdePos )
937 {
938 if( pLinkManager )
939 {
940 const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
941 size_t nCount = rLinks.size();
942 size_t nDdeIndex = 0; // counts only the DDE links
943 for( size_t nIndex = 0; nIndex < nCount; ++nIndex )
944 {
945 ::sfx2::SvBaseLink* pLink = rLinks[ nIndex ].get();
946 if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( pLink ) )
947 {
948 if( nDdeIndex == nDdePos )
949 return pDdeLink;
950 ++nDdeIndex;
951 }
952 }
953 }
954 return nullptr;
955 }
956
957 } // namespace
958
FindDdeLink(std::u16string_view rAppl,std::u16string_view rTopic,std::u16string_view rItem,sal_uInt8 nMode,size_t & rnDdePos)959 bool ScDocument::FindDdeLink( std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem,
960 sal_uInt8 nMode, size_t& rnDdePos )
961 {
962 return lclGetDdeLink( GetLinkManager(), rAppl, rTopic, rItem, nMode, &rnDdePos ) != nullptr;
963 }
964
GetDdeLinkData(size_t nDdePos,OUString & rAppl,OUString & rTopic,OUString & rItem) const965 bool ScDocument::GetDdeLinkData( size_t nDdePos, OUString& rAppl, OUString& rTopic, OUString& rItem ) const
966 {
967 if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
968 {
969 rAppl = pDdeLink->GetAppl();
970 rTopic = pDdeLink->GetTopic();
971 rItem = pDdeLink->GetItem();
972 return true;
973 }
974 return false;
975 }
976
GetDdeLinkMode(size_t nDdePos,sal_uInt8 & rnMode) const977 bool ScDocument::GetDdeLinkMode( size_t nDdePos, sal_uInt8& rnMode ) const
978 {
979 if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
980 {
981 rnMode = pDdeLink->GetMode();
982 return true;
983 }
984 return false;
985 }
986
GetDdeLinkResultMatrix(size_t nDdePos) const987 const ScMatrix* ScDocument::GetDdeLinkResultMatrix( size_t nDdePos ) const
988 {
989 const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos );
990 return pDdeLink ? pDdeLink->GetResult() : nullptr;
991 }
992
CreateDdeLink(const OUString & rAppl,const OUString & rTopic,const OUString & rItem,sal_uInt8 nMode,const ScMatrixRef & pResults)993 bool ScDocument::CreateDdeLink( const OUString& rAppl, const OUString& rTopic, const OUString& rItem, sal_uInt8 nMode, const ScMatrixRef& pResults )
994 {
995 /* Create a DDE link without updating it (i.e. for Excel import), to prevent
996 unwanted connections. First try to find existing link. Set result array
997 on existing and new links. */
998 //TODO: store DDE links additionally at document (for efficiency)?
999 OSL_ENSURE( nMode != SC_DDE_IGNOREMODE, "ScDocument::CreateDdeLink - SC_DDE_IGNOREMODE not allowed here" );
1000
1001 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
1002 if (!pMgr)
1003 return false;
1004
1005 if (nMode != SC_DDE_IGNOREMODE)
1006 {
1007 ScDdeLink* pDdeLink = lclGetDdeLink(pMgr, rAppl, rTopic, rItem, nMode);
1008 if( !pDdeLink )
1009 {
1010 // create a new DDE link, but without TryUpdate
1011 pDdeLink = new ScDdeLink( *this, rAppl, rTopic, rItem, nMode );
1012 pMgr->InsertDDELink(pDdeLink, rAppl, rTopic, rItem);
1013 }
1014
1015 // insert link results
1016 if( pResults )
1017 pDdeLink->SetResult( pResults );
1018
1019 return true;
1020 }
1021 return false;
1022 }
1023
SetDdeLinkResultMatrix(size_t nDdePos,const ScMatrixRef & pResults)1024 bool ScDocument::SetDdeLinkResultMatrix( size_t nDdePos, const ScMatrixRef& pResults )
1025 {
1026 if( ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
1027 {
1028 pDdeLink->SetResult( pResults );
1029 return true;
1030 }
1031 return false;
1032 }
1033
HasAreaLinks() const1034 bool ScDocument::HasAreaLinks() const
1035 {
1036 const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
1037 if (!pMgr)
1038 return false;
1039
1040 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1041 sal_uInt16 nCount = rLinks.size();
1042 for (sal_uInt16 i=0; i<nCount; i++)
1043 if (nullptr != dynamic_cast<const ScAreaLink* >(rLinks[i].get()))
1044 return true;
1045
1046 return false;
1047 }
1048
UpdateAreaLinks()1049 void ScDocument::UpdateAreaLinks()
1050 {
1051 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
1052 if (!pMgr)
1053 return;
1054
1055 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1056 for (const auto & rLink : rLinks)
1057 {
1058 ::sfx2::SvBaseLink* pBase = rLink.get();
1059 if (dynamic_cast<const ScAreaLink*>( pBase) != nullptr)
1060 pBase->Update();
1061 }
1062 }
1063
DeleteAreaLinksOnTab(SCTAB nTab)1064 void ScDocument::DeleteAreaLinksOnTab( SCTAB nTab )
1065 {
1066 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
1067 if (!pMgr)
1068 return;
1069
1070 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1071 sfx2::SvBaseLinks::size_type nPos = 0;
1072 while ( nPos < rLinks.size() )
1073 {
1074 const ::sfx2::SvBaseLink* pBase = rLinks[nPos].get();
1075 const ScAreaLink* pLink = dynamic_cast<const ScAreaLink*>(pBase);
1076 if (pLink && pLink->GetDestArea().aStart.Tab() == nTab)
1077 pMgr->Remove(nPos);
1078 else
1079 ++nPos;
1080 }
1081 }
1082
UpdateRefAreaLinks(UpdateRefMode eUpdateRefMode,const ScRange & rRange,SCCOL nDx,SCROW nDy,SCTAB nDz)1083 void ScDocument::UpdateRefAreaLinks( UpdateRefMode eUpdateRefMode,
1084 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
1085 {
1086 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
1087 if (!pMgr)
1088 return;
1089
1090 bool bAnyUpdate = false;
1091
1092 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1093 sal_uInt16 nCount = rLinks.size();
1094 for (sal_uInt16 i=0; i<nCount; i++)
1095 {
1096 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
1097 if (ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase))
1098 {
1099 ScRange aOutRange = pLink->GetDestArea();
1100
1101 SCCOL nCol1 = aOutRange.aStart.Col();
1102 SCROW nRow1 = aOutRange.aStart.Row();
1103 SCTAB nTab1 = aOutRange.aStart.Tab();
1104 SCCOL nCol2 = aOutRange.aEnd.Col();
1105 SCROW nRow2 = aOutRange.aEnd.Row();
1106 SCTAB nTab2 = aOutRange.aEnd.Tab();
1107
1108 ScRefUpdateRes eRes =
1109 ScRefUpdate::Update( this, eUpdateRefMode,
1110 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
1111 rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
1112 nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
1113 if ( eRes != UR_NOTHING )
1114 {
1115 pLink->SetDestArea( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );
1116 bAnyUpdate = true;
1117 }
1118 }
1119 }
1120
1121 if ( !bAnyUpdate )
1122 return;
1123
1124 // #i52120# Look for duplicates (after updating all positions).
1125 // If several links start at the same cell, the one with the lower index is removed
1126 // (file format specifies only one link definition for a cell).
1127
1128 sal_uInt16 nFirstIndex = 0;
1129 while ( nFirstIndex < nCount )
1130 {
1131 bool bFound = false;
1132 ::sfx2::SvBaseLink* pFirst = rLinks[nFirstIndex].get();
1133 if (ScAreaLink* pFirstLink = dynamic_cast<ScAreaLink*>(pFirst))
1134 {
1135 ScAddress aFirstPos = pFirstLink->GetDestArea().aStart;
1136 for ( sal_uInt16 nSecondIndex = nFirstIndex + 1; nSecondIndex < nCount && !bFound; ++nSecondIndex )
1137 {
1138 ::sfx2::SvBaseLink* pSecond = rLinks[nSecondIndex].get();
1139 ScAreaLink* pSecondLink = dynamic_cast<ScAreaLink*>(pSecond);
1140 if (pSecondLink && pSecondLink->GetDestArea().aStart == aFirstPos)
1141 {
1142 // remove the first link, exit the inner loop, don't increment nFirstIndex
1143 pMgr->Remove(pFirst);
1144 nCount = rLinks.size();
1145 bFound = true;
1146 }
1147 }
1148 }
1149 if (!bFound)
1150 ++nFirstIndex;
1151 }
1152 }
1153
CheckLinkFormulaNeedingCheck(const ScTokenArray & rCode)1154 void ScDocument::CheckLinkFormulaNeedingCheck( const ScTokenArray& rCode )
1155 {
1156 if (HasLinkFormulaNeedingCheck())
1157 return;
1158
1159 // Prefer RPN over tokenized formula if available.
1160 if (rCode.GetCodeLen())
1161 {
1162 if (rCode.HasOpCodeRPN(ocDde) || rCode.HasOpCodeRPN(ocWebservice))
1163 SetLinkFormulaNeedingCheck(true);
1164 }
1165 else if (rCode.GetLen())
1166 {
1167 if (rCode.HasOpCode(ocDde) || rCode.HasOpCode(ocWebservice))
1168 SetLinkFormulaNeedingCheck(true);
1169 }
1170 else
1171 {
1172 // Possible with named expression without expression like Excel
1173 // internal print ranges, obscure user define names, ... formula error
1174 // cells without formula ...
1175 SAL_WARN("sc.core","ScDocument::CheckLinkFormulaNeedingCheck - called with empty ScTokenArray");
1176 }
1177 }
1178
1179 // TimerDelays etc.
KeyInput()1180 void ScDocument::KeyInput()
1181 {
1182 if ( pChartListenerCollection->hasListeners() )
1183 pChartListenerCollection->StartTimer();
1184 if (apTemporaryChartLock)
1185 apTemporaryChartLock->StartOrContinueLocking();
1186 }
1187
GetViewBindings()1188 SfxBindings* ScDocument::GetViewBindings()
1189 {
1190 // used to invalidate slots after changes to this document
1191
1192 if ( !mpShell )
1193 return nullptr; // no ObjShell -> no view
1194
1195 // first check current view
1196 SfxViewFrame* pViewFrame = SfxViewFrame::Current();
1197 if ( pViewFrame && pViewFrame->GetObjectShell() != mpShell ) // wrong document?
1198 pViewFrame = nullptr;
1199
1200 // otherwise use first view for this doc
1201 if ( !pViewFrame )
1202 pViewFrame = SfxViewFrame::GetFirst( mpShell );
1203
1204 if (pViewFrame)
1205 return &pViewFrame->GetBindings();
1206 else
1207 return nullptr;
1208 }
1209
TransliterateText(const ScMarkData & rMultiMark,TransliterationFlags nType)1210 void ScDocument::TransliterateText( const ScMarkData& rMultiMark, TransliterationFlags nType )
1211 {
1212 OSL_ENSURE( rMultiMark.IsMultiMarked(), "TransliterateText: no selection" );
1213
1214 utl::TransliterationWrapper aTransliterationWrapper( comphelper::getProcessComponentContext(), nType );
1215 bool bConsiderLanguage = aTransliterationWrapper.needLanguageForTheMode();
1216 LanguageType nLanguage = LANGUAGE_SYSTEM;
1217
1218 std::unique_ptr<ScEditEngineDefaulter> pEngine; // not using mpEditEngine member because of defaults
1219
1220 SCTAB nCount = GetTableCount();
1221 for (const SCTAB& nTab : rMultiMark)
1222 {
1223 if (nTab >= nCount)
1224 break;
1225
1226 if ( maTabs[nTab] )
1227 {
1228 SCCOL nCol = 0;
1229 SCROW nRow = 0;
1230
1231 bool bFound = rMultiMark.IsCellMarked( nCol, nRow );
1232 if (!bFound)
1233 bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );
1234
1235 while (bFound)
1236 {
1237 ScRefCellValue aCell(*this, ScAddress(nCol, nRow, nTab));
1238
1239 // fdo#32786 TITLE_CASE/SENTENCE_CASE need the extra handling in EditEngine (loop over words/sentences).
1240 // Still use TransliterationWrapper directly for text cells with other transliteration types,
1241 // for performance reasons.
1242 if (aCell.meType == CELLTYPE_EDIT ||
1243 (aCell.meType == CELLTYPE_STRING &&
1244 ( nType == TransliterationFlags::SENTENCE_CASE || nType == TransliterationFlags::TITLE_CASE)))
1245 {
1246 if (!pEngine)
1247 pEngine.reset(new ScFieldEditEngine(this, GetEnginePool(), GetEditPool()));
1248
1249 // defaults from cell attributes must be set so right language is used
1250 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
1251 std::unique_ptr<SfxItemSet> pDefaults(new SfxItemSet( pEngine->GetEmptyItemSet() ));
1252 if ( ScStyleSheet* pPreviewStyle = GetPreviewCellStyle( nCol, nRow, nTab ) )
1253 {
1254 ScPatternAttr aPreviewPattern( *pPattern );
1255 aPreviewPattern.SetStyleSheet(pPreviewStyle);
1256 aPreviewPattern.FillEditItemSet( pDefaults.get() );
1257 }
1258 else
1259 {
1260 SfxItemSet* pFontSet = GetPreviewFont( nCol, nRow, nTab );
1261 pPattern->FillEditItemSet( pDefaults.get(), pFontSet );
1262 }
1263 pEngine->SetDefaults( std::move(pDefaults) );
1264 if (aCell.meType == CELLTYPE_STRING)
1265 pEngine->SetTextCurrentDefaults(aCell.mpString->getString());
1266 else if (aCell.mpEditText)
1267 pEngine->SetTextCurrentDefaults(*aCell.mpEditText);
1268
1269 pEngine->ClearModifyFlag();
1270
1271 sal_Int32 nLastPar = pEngine->GetParagraphCount();
1272 if (nLastPar)
1273 --nLastPar;
1274 sal_Int32 nTxtLen = pEngine->GetTextLen(nLastPar);
1275 ESelection aSelAll( 0, 0, nLastPar, nTxtLen );
1276
1277 pEngine->TransliterateText( aSelAll, nType );
1278
1279 if ( pEngine->IsModified() )
1280 {
1281 ScEditAttrTester aTester( pEngine.get() );
1282 if ( aTester.NeedsObject() )
1283 {
1284 // remove defaults (paragraph attributes) before creating text object
1285 pEngine->SetDefaults( std::make_unique<SfxItemSet>( pEngine->GetEmptyItemSet() ) );
1286
1287 // The cell will take ownership of the text object instance.
1288 SetEditText(ScAddress(nCol,nRow,nTab), pEngine->CreateTextObject());
1289 }
1290 else
1291 {
1292 ScSetStringParam aParam;
1293 aParam.setTextInput();
1294 SetString(ScAddress(nCol,nRow,nTab), pEngine->GetText(), &aParam);
1295 }
1296 }
1297 }
1298
1299 else if (aCell.meType == CELLTYPE_STRING)
1300 {
1301 OUString aOldStr = aCell.mpString->getString();
1302 sal_Int32 nOldLen = aOldStr.getLength();
1303
1304 if ( bConsiderLanguage )
1305 {
1306 SvtScriptType nScript = GetStringScriptType( aOldStr ); //TODO: cell script type?
1307 sal_uInt16 nWhich = ( nScript == SvtScriptType::ASIAN ) ? ATTR_CJK_FONT_LANGUAGE :
1308 ( ( nScript == SvtScriptType::COMPLEX ) ? ATTR_CTL_FONT_LANGUAGE :
1309 ATTR_FONT_LANGUAGE );
1310 nLanguage = static_cast<const SvxLanguageItem*>(GetAttr( nCol, nRow, nTab, nWhich ))->GetValue();
1311 }
1312
1313 uno::Sequence<sal_Int32> aOffsets;
1314 OUString aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );
1315
1316 if ( aNewStr != aOldStr )
1317 {
1318 ScSetStringParam aParam;
1319 aParam.setTextInput();
1320 SetString(ScAddress(nCol,nRow,nTab), aNewStr, &aParam);
1321 }
1322 }
1323 bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );
1324 }
1325 }
1326 }
1327 }
1328
1329 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1330