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