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