Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / data / documen8.cxx
blobe2522605c9c52a2742d15809500e7abacc6e2dcc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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/numformat.hxx>
37 #include <svl/zformat.hxx>
38 #include <svl/ctloptions.hxx>
39 #include <unotools/transliterationwrapper.hxx>
40 #include <sal/log.hxx>
41 #include <osl/diagnose.h>
43 #include <vcl/svapp.hxx>
44 #include <vcl/virdev.hxx>
45 #include <vcl/weld.hxx>
46 #include <vcl/TaskStopwatch.hxx>
48 #include <inputopt.hxx>
49 #include <global.hxx>
50 #include <table.hxx>
51 #include <column.hxx>
52 #include <poolhelp.hxx>
53 #include <docpool.hxx>
54 #include <docsh.hxx>
55 #include <stlpool.hxx>
56 #include <stlsheet.hxx>
57 #include <docoptio.hxx>
58 #include <viewopti.hxx>
59 #include <scextopt.hxx>
60 #include <rechead.hxx>
61 #include <ddelink.hxx>
62 #include <scmatrix.hxx>
63 #include <arealink.hxx>
64 #include <patattr.hxx>
65 #include <editutil.hxx>
66 #include <progress.hxx>
67 #include <document.hxx>
68 #include <chartlis.hxx>
69 #include <chartlock.hxx>
70 #include <refupdat.hxx>
71 #include <markdata.hxx>
72 #include <scmod.hxx>
73 #include <externalrefmgr.hxx>
74 #include <globstr.hrc>
75 #include <strings.hrc>
76 #include <sc.hrc>
77 #include <charthelper.hxx>
78 #include <macromgr.hxx>
79 #include <docuno.hxx>
80 #include <scresid.hxx>
81 #include <columniterator.hxx>
82 #include <globalnames.hxx>
83 #include <stringutil.hxx>
84 #include <documentlinkmgr.hxx>
85 #include <tokenarray.hxx>
86 #include <recursionhelper.hxx>
88 #include <memory>
89 #include <utility>
91 using namespace com::sun::star;
93 namespace {
95 sal_uInt16 getScaleValue(SfxStyleSheetBase& rStyle, sal_uInt16 nWhich)
97 return static_cast<const SfxUInt16Item&>(rStyle.GetItemSet().Get(nWhich)).GetValue();
102 void ScDocument::ImplCreateOptions()
104 pDocOptions.reset( new ScDocOptions() );
105 pViewOptions.reset( new ScViewOptions() );
108 void ScDocument::ImplDeleteOptions()
110 pDocOptions.reset();
111 pViewOptions.reset();
112 pExtDocOptions.reset();
115 SfxPrinter* ScDocument::GetPrinter(bool bCreateIfNotExist)
117 if (!mpPrinter && bCreateIfNotExist && mxPoolHelper)
119 auto pSet =
120 std::make_unique<SfxItemSetFixed
121 <SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
122 SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
123 SID_PRINT_SELECTEDSHEET, SID_PRINT_SELECTEDSHEET,
124 SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS>>(*mxPoolHelper->GetDocPool());
126 SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
127 if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
128 nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
129 if (officecfg::Office::Common::Print::Warning::PaperSize::get())
130 nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
131 pSet->Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
132 pSet->Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );
134 mpPrinter = VclPtr<SfxPrinter>::Create( std::move(pSet) );
135 mpPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
136 UpdateDrawPrinter();
137 mpPrinter->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
140 return mpPrinter;
143 void ScDocument::SetPrinter( VclPtr<SfxPrinter> const & pNewPrinter )
145 if ( pNewPrinter == mpPrinter.get() )
147 // #i6706# SetPrinter is called with the same printer again if
148 // the JobSetup has changed. In that case just call UpdateDrawPrinter
149 // (SetRefDevice for drawing layer) because of changed text sizes.
150 UpdateDrawPrinter();
152 else
154 ScopedVclPtr<SfxPrinter> xKeepAlive( mpPrinter );
155 mpPrinter = pNewPrinter;
156 UpdateDrawPrinter();
157 mpPrinter->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
159 InvalidateTextWidth(nullptr, nullptr, false); // in both cases
162 void ScDocument::SetPrintOptions()
164 if ( !mpPrinter ) GetPrinter(); // this sets mpPrinter
165 OSL_ENSURE( mpPrinter, "Error in printer creation :-/" );
167 if ( !mpPrinter )
168 return;
170 SfxItemSet aOptSet( mpPrinter->GetOptions() );
172 SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
173 if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
174 nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
175 if (officecfg::Office::Common::Print::Warning::PaperSize::get())
176 nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
177 aOptSet.Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
178 aOptSet.Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );
180 mpPrinter->SetOptions( aOptSet );
183 VirtualDevice* ScDocument::GetVirtualDevice_100th_mm()
185 if (!mpVirtualDevice_100th_mm)
187 #ifdef IOS
188 mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::GRAYSCALE);
189 #else
190 mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
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 );
197 return mpVirtualDevice_100th_mm;
200 OutputDevice* ScDocument::GetRefDevice(bool bForceVirtDev)
202 // Create printer like ref device, see Writer...
203 OutputDevice* pRefDevice = nullptr;
204 if ( !bForceVirtDev && SC_MOD()->GetInputOptions().GetTextWysiwyg() )
206 pRefDevice = GetPrinter();
207 SAL_WARN_IF(!pRefDevice, "sc", "unable to get a printer, fallback to virdev");
209 if (!pRefDevice)
210 pRefDevice = GetVirtualDevice_100th_mm();
211 return pRefDevice;
214 void ScDocument::ModifyStyleSheet( SfxStyleSheetBase& rStyleSheet,
215 const SfxItemSet& rChanges )
217 SfxItemSet& rSet = rStyleSheet.GetItemSet();
219 switch ( rStyleSheet.GetFamily() )
221 case SfxStyleFamily::Page:
223 const sal_uInt16 nOldScale = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
224 const sal_uInt16 nOldScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);
225 rSet.Put( rChanges );
226 const sal_uInt16 nNewScale = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
227 const sal_uInt16 nNewScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);
229 if ( (nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages) )
230 InvalidateTextWidth( rStyleSheet.GetName() );
232 if( SvtCTLOptions::IsCTLFontEnabled() )
234 if( rChanges.GetItemState(ATTR_WRITINGDIR ) == SfxItemState::SET )
235 ScChartHelper::DoUpdateAllCharts( *this );
238 break;
240 case SfxStyleFamily::Para:
242 bool bNumFormatChanged;
243 if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
244 rSet, rChanges ) )
245 InvalidateTextWidth( nullptr, nullptr, bNumFormatChanged );
247 for (SCTAB nTab=0; nTab<=MAXTAB; ++nTab)
248 if (maTabs[nTab])
249 maTabs[nTab]->SetStreamValid( false );
251 sal_uInt32 nOldFormat =
252 rSet.Get( ATTR_VALUE_FORMAT ).GetValue();
253 sal_uInt32 nNewFormat =
254 rChanges.Get( ATTR_VALUE_FORMAT ).GetValue();
255 LanguageType eNewLang, eOldLang;
256 eNewLang = eOldLang = LANGUAGE_DONTKNOW;
257 if ( nNewFormat != nOldFormat )
259 SvNumberFormatter* pFormatter = GetFormatTable();
260 eOldLang = pFormatter->GetEntry( nOldFormat )->GetLanguage();
261 eNewLang = pFormatter->GetEntry( nNewFormat )->GetLanguage();
264 // Explanation to Items in rChanges:
265 // Set Item - take over change
266 // Dontcare - Set Default
267 // Default - No change
268 // ("no change" is not possible with PutExtended, thus the loop)
269 for (sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; nWhich++)
271 const SfxPoolItem* pItem;
272 SfxItemState eState = rChanges.GetItemState( nWhich, false, &pItem );
273 if ( eState == SfxItemState::SET )
274 rSet.Put( *pItem );
275 else if ( eState == SfxItemState::DONTCARE )
276 rSet.ClearItem( nWhich );
277 // when Default nothing
280 if ( eNewLang != eOldLang )
281 rSet.Put(
282 SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
284 break;
285 default:
287 // added to avoid warnings
292 void ScDocument::CopyStdStylesFrom( const ScDocument& rSrcDoc )
294 // number format exchange list has to be handled here, too
295 NumFmtMergeHandler aNumFmtMergeHdl(*this, rSrcDoc);
296 mxPoolHelper->GetStylePool()->CopyStdStylesFrom( rSrcDoc.mxPoolHelper->GetStylePool() );
299 void ScDocument::InvalidateTextWidth( std::u16string_view rStyleName )
301 const SCTAB nCount = GetTableCount();
302 for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
303 if ( maTabs[i]->GetPageStyle() == rStyleName )
304 InvalidateTextWidth( i );
307 void ScDocument::InvalidateTextWidth( SCTAB nTab )
309 ScAddress aAdrFrom( 0, 0, nTab );
310 ScAddress aAdrTo ( MaxCol(), MaxRow(), nTab );
311 InvalidateTextWidth( &aAdrFrom, &aAdrTo, false );
314 bool ScDocument::IsPageStyleInUse( std::u16string_view rStrPageStyle, SCTAB* pInTab )
316 bool bInUse = false;
317 const SCTAB nCount = GetTableCount();
318 SCTAB i;
320 for ( i = 0; !bInUse && i < nCount && maTabs[i]; i++ )
321 bInUse = ( maTabs[i]->GetPageStyle() == rStrPageStyle );
323 if ( pInTab )
324 *pInTab = i-1;
326 return bInUse;
329 bool ScDocument::RemovePageStyleInUse( std::u16string_view rStyle )
331 bool bWasInUse = false;
332 const SCTAB nCount = GetTableCount();
334 for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
335 if ( maTabs[i]->GetPageStyle() == rStyle )
337 bWasInUse = true;
338 maTabs[i]->SetPageStyle( ScResId(STR_STYLENAME_STANDARD) );
341 return bWasInUse;
344 bool ScDocument::RenamePageStyleInUse( std::u16string_view rOld, const OUString& rNew )
346 bool bWasInUse = false;
347 const SCTAB nCount = GetTableCount();
349 for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
350 if ( maTabs[i]->GetPageStyle() == rOld )
352 bWasInUse = true;
353 maTabs[i]->SetPageStyle( rNew );
356 return bWasInUse;
359 EEHorizontalTextDirection ScDocument::GetEditTextDirection(SCTAB nTab) const
361 EEHorizontalTextDirection eRet = EEHorizontalTextDirection::Default;
363 OUString aStyleName = GetPageStyle( nTab );
364 SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( aStyleName, SfxStyleFamily::Page );
365 if ( pStyle )
367 SfxItemSet& rStyleSet = pStyle->GetItemSet();
368 SvxFrameDirection eDirection =
369 rStyleSet.Get( ATTR_WRITINGDIR ).GetValue();
371 if ( eDirection == SvxFrameDirection::Horizontal_LR_TB )
372 eRet = EEHorizontalTextDirection::L2R;
373 else if ( eDirection == SvxFrameDirection::Horizontal_RL_TB )
374 eRet = EEHorizontalTextDirection::R2L;
375 // else (invalid for EditEngine): keep "default"
378 return eRet;
381 ScMacroManager* ScDocument::GetMacroManager()
383 if (!mpMacroMgr)
384 mpMacroMgr.reset(new ScMacroManager(*this));
385 return mpMacroMgr.get();
388 void ScDocument::FillMatrix(
389 ScMatrix& rMat, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool* pPool ) const
391 const ScTable* pTab = FetchTable(nTab);
392 if (!pTab)
393 return;
395 if (nCol1 > nCol2 || nRow1 > nRow2)
396 return;
398 SCSIZE nC, nR;
399 rMat.GetDimensions(nC, nR);
400 if (static_cast<SCROW>(nR) != nRow2 - nRow1 + 1 || static_cast<SCCOL>(nC) != nCol2 - nCol1 + 1)
401 return;
403 pTab->FillMatrix(rMat, nCol1, nRow1, nCol2, nRow2, pPool);
406 void ScDocument::SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen )
408 ScTable* pTab = FetchTable(rTopPos.Tab());
409 if (!pTab)
410 return;
412 pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen);
415 void ScDocument::CalculateInColumnInThread( ScInterpreterContext& rContext, const ScRange& rCalcRange, unsigned nThisThread, unsigned nThreadsTotal)
417 ScTable* pTab = FetchTable(rCalcRange.aStart.Tab());
418 if (!pTab)
419 return;
421 assert(IsThreadedGroupCalcInProgress());
423 maThreadSpecific.pContext = &rContext;
424 pTab->CalculateInColumnInThread(rContext, rCalcRange.aStart.Col(), rCalcRange.aEnd.Col(), rCalcRange.aStart.Row(), rCalcRange.aEnd.Row(), nThisThread, nThreadsTotal);
426 assert(IsThreadedGroupCalcInProgress());
427 maThreadSpecific.pContext = nullptr;
428 // If any of the thread_local data would cause problems if they stay around for too long
429 // (and e.g. outlive the ScDocument), clean them up here, they cannot be cleaned up
430 // later from the main thread.
431 if(maThreadSpecific.xRecursionHelper)
432 maThreadSpecific.xRecursionHelper->Clear();
435 void ScDocument::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen, SCTAB nTab, ScInterpreter* pInterpreter )
437 assert(!IsThreadedGroupCalcInProgress());
438 for( const DelayedSetNumberFormat& data : GetNonThreadedContext().maDelayedSetNumberFormat)
439 SetNumberFormat( ScAddress( data.mCol, data.mRow, nTab ), data.mnNumberFormat );
440 GetNonThreadedContext().maDelayedSetNumberFormat.clear();
442 ScTable* pTab = FetchTable(nTab);
443 if (!pTab)
444 return;
446 pTab->HandleStuffAfterParallelCalculation(nColStart, nColEnd, nRow, nLen, pInterpreter);
449 void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo,
450 bool bNumFormatChanged )
452 bool bBroadcast = (bNumFormatChanged && GetDocOptions().IsCalcAsShown() && !IsImportingXML() && !IsClipboard());
453 if ( pAdrFrom && !pAdrTo )
455 const SCTAB nTab = pAdrFrom->Tab();
457 if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
458 maTabs[nTab]->InvalidateTextWidth( pAdrFrom, nullptr, bNumFormatChanged, bBroadcast );
460 else
462 const SCTAB nTabStart = pAdrFrom ? pAdrFrom->Tab() : 0;
463 const SCTAB nTabEnd = pAdrTo ? pAdrTo->Tab() : MAXTAB;
465 for ( SCTAB nTab=nTabStart; nTab<=nTabEnd && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
466 if ( maTabs[nTab] )
467 maTabs[nTab]->InvalidateTextWidth( pAdrFrom, pAdrTo, bNumFormatChanged, bBroadcast );
471 #define CALCMAX 1000 // Calculations
473 namespace {
475 class IdleCalcTextWidthScope : public TaskStopwatch
477 ScDocument& mrDoc;
478 ScAddress& mrCalcPos;
479 MapMode maOldMapMode;
480 ScStyleSheetPool* mpStylePool;
481 bool mbNeedMore;
482 bool mbProgress;
484 public:
485 IdleCalcTextWidthScope(ScDocument& rDoc, ScAddress& rCalcPos) :
486 mrDoc(rDoc),
487 mrCalcPos(rCalcPos),
488 mpStylePool(rDoc.GetStyleSheetPool()),
489 mbNeedMore(false),
490 mbProgress(false)
492 mrDoc.EnableIdle(false);
495 ~IdleCalcTextWidthScope() COVERITY_NOEXCEPT_FALSE
497 SfxPrinter* pDev = mrDoc.GetPrinter();
498 if (pDev)
499 pDev->SetMapMode(maOldMapMode);
501 if (mbProgress)
502 ScProgress::DeleteInterpretProgress();
504 mrDoc.EnableIdle(true);
507 SCTAB Tab() const { return mrCalcPos.Tab(); }
508 SCCOL Col() const { return mrCalcPos.Col(); }
509 SCROW Row() const { return mrCalcPos.Row(); }
511 void setTab(SCTAB nTab) { mrCalcPos.SetTab(nTab); }
512 void setCol(SCCOL nCol) { mrCalcPos.SetCol(nCol); }
513 void setRow(SCROW nRow) { mrCalcPos.SetRow(nRow); }
515 void incTab() { mrCalcPos.IncTab(); }
516 void incCol(SCCOL nInc) { mrCalcPos.IncCol(nInc); }
518 void setOldMapMode(const MapMode& rOldMapMode) { maOldMapMode = rOldMapMode; }
520 void setNeedMore(bool b) { mbNeedMore = b; }
521 bool getNeedMore() const { return mbNeedMore; }
523 void createProgressBar()
525 ScProgress::CreateInterpretProgress(&mrDoc, false);
526 mbProgress = true;
529 bool hasProgressBar() const { return mbProgress; }
531 ScStyleSheetPool* getStylePool() { return mpStylePool; }
536 bool ScDocument::IdleCalcTextWidth() // true = try next again
538 // #i75610# if a printer hasn't been set or created yet, don't create one for this
539 if (!mbIdleEnabled || IsInLinkUpdate() || GetPrinter(false) == nullptr)
540 return false;
542 IdleCalcTextWidthScope aScope(*this, aCurTextWidthCalcPos);
544 if (!ValidRow(aScope.Row()))
546 aScope.setRow(0);
547 aScope.incCol(-1);
550 if (aScope.Col() < 0)
552 aScope.setCol(MaxCol());
553 aScope.incTab();
556 if (!HasTable(aScope.Tab()))
557 aScope.setTab(0);
559 ScTable* pTab = maTabs[aScope.Tab()].get();
560 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(pTab->aPageStyle, SfxStyleFamily::Page));
561 OSL_ENSURE( pStyle, "Missing StyleSheet :-/" );
563 if (!pStyle || getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
565 // Move to the next sheet as the current one has scale-to-pages set,
566 // and bail out.
567 aScope.incTab();
568 return false;
571 sal_uInt16 nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
572 Fraction aZoomFract(nZoom, 100);
574 aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
575 // Start at specified cell position (nCol, nRow, nTab).
576 ScColumn* pCol = &pTab->aCol[aScope.Col()];
577 std::optional<ScColumnTextWidthIterator> pColIter(std::in_place, *this, *pCol, aScope.Row(), MaxRow());
579 OutputDevice* pDev = nullptr;
580 sal_uInt16 nRestart = 0;
581 sal_uInt16 nCount = 0;
582 while ( (nZoom > 0) && (nCount < CALCMAX) && (nRestart < 2) )
584 if (pColIter->hasCell())
586 // More cell in this column.
587 SCROW nRow = pColIter->getPos();
588 aScope.setRow(nRow);
590 if (pColIter->getValue() == TEXTWIDTH_DIRTY)
592 // Calculate text width for this cell.
593 double nPPTX = 0.0;
594 double nPPTY = 0.0;
595 if (!pDev)
597 pDev = GetPrinter();
598 aScope.setOldMapMode(pDev->GetMapMode());
599 pDev->SetMapMode(MapMode(MapUnit::MapPixel)); // Important for GetNeededSize
601 Point aPix1000 = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
602 nPPTX = aPix1000.X() / 1000.0;
603 nPPTY = aPix1000.Y() / 1000.0;
606 if (!aScope.hasProgressBar() && pCol->IsFormulaDirty(nRow))
607 aScope.createProgressBar();
609 sal_uInt16 nNewWidth = static_cast<sal_uInt16>(GetNeededSize(
610 aScope.Col(), aScope.Row(), aScope.Tab(),
611 pDev, nPPTX, nPPTY, aZoomFract,aZoomFract, true, true)); // bTotalSize
613 pColIter->setValue(nNewWidth);
614 aScope.setNeedMore(true);
616 pColIter->next();
618 else
620 // No more cell in this column. Move to the left column and start at row 0.
622 bool bNewTab = false;
624 aScope.setRow(0);
625 aScope.incCol(-1);
627 if (aScope.Col() < 0)
629 // No more column to the left. Move to the right-most column of the next sheet.
630 aScope.setCol(MaxCol());
631 aScope.incTab();
632 bNewTab = true;
635 if (!HasTable(aScope.Tab()))
637 // Sheet doesn't exist at specified sheet position. Restart at sheet 0.
638 aScope.setTab(0);
639 nRestart++;
640 bNewTab = true;
643 if ( nRestart < 2 )
645 if ( bNewTab )
647 pTab = maTabs[aScope.Tab()].get();
648 aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
649 pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(
650 pTab->aPageStyle, SfxStyleFamily::Page));
652 if ( pStyle )
654 // Check if the scale-to-pages setting is set. If
655 // set, we exit the loop. If not, get the page
656 // scale factor of the new sheet.
657 if (getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
659 nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
660 aZoomFract = Fraction(nZoom, 100);
662 else
663 nZoom = 0;
665 else
667 OSL_FAIL( "Missing StyleSheet :-/" );
671 if ( nZoom > 0 )
673 pCol = &pTab->aCol[aScope.Col()];
674 pColIter.emplace(*this, *pCol, aScope.Row(), MaxRow());
676 else
678 aScope.incTab(); // Move to the next sheet as the current one has scale-to-pages set.
679 return false;
684 ++nCount;
686 if (!aScope.continueIter())
687 break;
690 return aScope.getNeedMore();
693 void ScDocument::RepaintRange( const ScRange& rRange )
695 if ( bIsVisible && mpShell )
697 ScModelObj* pModel = mpShell->GetModel();
698 if ( pModel )
699 pModel->RepaintRange( rRange ); // locked repaints are checked there
703 void ScDocument::RepaintRange( const ScRangeList& rRange )
705 if ( bIsVisible && mpShell )
707 ScModelObj* pModel = mpShell->GetModel();
708 if ( pModel )
709 pModel->RepaintRange( rRange ); // locked repaints are checked there
713 void ScDocument::SaveDdeLinks(SvStream& rStream) const
715 // when 4.0-Export, remove all with mode != DEFAULT
716 bool bExport40 = ( rStream.GetVersion() <= SOFFICE_FILEFORMAT_40 );
718 const ::sfx2::SvBaseLinks& rLinks = GetLinkManager()->GetLinks();
719 sal_uInt16 nCount = rLinks.size();
721 // Count them first
723 sal_uInt16 nDdeCount = 0;
724 sal_uInt16 i;
725 for (i=0; i<nCount; i++)
727 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
728 if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
729 if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
730 ++nDdeCount;
733 // Header
735 ScMultipleWriteHeader aHdr( rStream );
736 rStream.WriteUInt16( nDdeCount );
738 // Save links
740 for (i=0; i<nCount; i++)
742 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
743 if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
745 if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
746 pLink->Store( rStream, aHdr );
751 void ScDocument::LoadDdeLinks(SvStream& rStream)
753 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
754 if (!pMgr)
755 return;
757 ScMultipleReadHeader aHdr( rStream );
759 sal_uInt16 nCount(0);
760 rStream.ReadUInt16( nCount );
762 const rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
763 const size_t nMinStringSize = eCharSet == RTL_TEXTENCODING_UNICODE ? sizeof(sal_uInt32) : sizeof(sal_uInt16);
764 const size_t nMinRecordSize = 1 + nMinStringSize*3;
765 const size_t nMaxRecords = rStream.remainingSize() / nMinRecordSize;
766 if (nCount > nMaxRecords)
768 SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
769 " max possible entries, but " << nCount << " claimed, truncating");
770 nCount = nMaxRecords;
773 for (sal_uInt16 i=0; i<nCount; ++i)
775 ScDdeLink* pLink = new ScDdeLink( *this, rStream, aHdr );
776 pMgr->InsertDDELink(pLink, pLink->GetAppl(), pLink->GetTopic(), pLink->GetItem());
780 void ScDocument::SetInLinkUpdate(bool bSet)
782 // called from TableLink and AreaLink
784 OSL_ENSURE( bInLinkUpdate != bSet, "SetInLinkUpdate twice" );
785 bInLinkUpdate = bSet;
788 bool ScDocument::IsInLinkUpdate() const
790 return bInLinkUpdate || IsInDdeLinkUpdate();
793 void ScDocument::UpdateExternalRefLinks(weld::Window* pWin)
795 if (!pExternalRefMgr)
796 return;
798 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
799 if (!pMgr)
800 return;
802 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
803 sal_uInt16 nCount = rLinks.size();
805 bool bAny = false;
807 // Collect all the external ref links first.
808 std::vector<ScExternalRefLink*> aRefLinks;
809 for (sal_uInt16 i = 0; i < nCount; ++i)
811 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
812 ScExternalRefLink* pRefLink = dynamic_cast<ScExternalRefLink*>(pBase);
813 if (pRefLink)
814 aRefLinks.push_back(pRefLink);
817 weld::WaitObject aWaitSwitch(pWin);
819 pExternalRefMgr->enableDocTimer(false);
820 ScProgress aProgress(GetDocumentShell(), ScResId(SCSTR_UPDATE_EXTDOCS), aRefLinks.size(), true);
821 for (size_t i = 0, n = aRefLinks.size(); i < n; ++i)
823 aProgress.SetState(i+1);
825 ScExternalRefLink* pRefLink = aRefLinks[i];
826 if (pRefLink->Update())
828 bAny = true;
829 continue;
832 // Update failed. Notify the user.
834 OUString aFile;
835 sfx2::LinkManager::GetDisplayNames(pRefLink, nullptr, &aFile);
836 // Decode encoded URL for display friendliness.
837 INetURLObject aUrl(aFile,INetURLObject::EncodeMechanism::WasEncoded);
838 aFile = aUrl.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
840 OUString sMessage = ScResId(SCSTR_EXTDOC_NOT_LOADED) +
841 "\n\n" +
842 aFile;
843 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
844 VclMessageType::Warning, VclButtonsType::Ok,
845 sMessage));
846 xBox->run();
849 pExternalRefMgr->enableDocTimer(true);
851 if (!bAny)
852 return;
854 TrackFormulas();
855 mpShell->Broadcast( SfxHint(SfxHintId::ScDataChanged) );
857 // #i101960# set document modified, as in TrackTimeHdl for DDE links
858 if (!mpShell->IsModified())
860 mpShell->SetModified();
861 SfxBindings* pBindings = GetViewBindings();
862 if (pBindings)
864 pBindings->Invalidate( SID_SAVEDOC );
865 pBindings->Invalidate( SID_DOC_MODIFIED );
870 void ScDocument::CopyDdeLinks( ScDocument& rDestDoc ) const
872 if (bIsClip) // Create from Stream
874 if (pClipData)
876 pClipData->Seek(0);
877 rDestDoc.LoadDdeLinks(*pClipData);
880 return;
883 const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
884 if (!pMgr)
885 return;
887 sfx2::LinkManager* pDestMgr = rDestDoc.GetDocLinkManager().getLinkManager(rDestDoc.bAutoCalc);
888 if (!pDestMgr)
889 return;
891 const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
892 for (const auto & rLink : rLinks)
894 const sfx2::SvBaseLink* pBase = rLink.get();
895 if (const ScDdeLink* p = dynamic_cast<const ScDdeLink*>(pBase))
897 ScDdeLink* pNew = new ScDdeLink(rDestDoc, *p);
898 pDestMgr->InsertDDELink(
899 pNew, pNew->GetAppl(), pNew->GetTopic(), pNew->GetItem());
904 namespace {
906 /** Tries to find the specified DDE link.
907 @param pnDdePos (out-param) if not 0, the index of the DDE link is returned here
908 (does not include other links from link manager).
909 @return The DDE link, if it exists, otherwise 0. */
910 ScDdeLink* lclGetDdeLink(
911 const sfx2::LinkManager* pLinkManager,
912 std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem, sal_uInt8 nMode,
913 size_t* pnDdePos = nullptr )
915 if( pLinkManager )
917 const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
918 if( pnDdePos ) *pnDdePos = 0;
919 for( const auto& nLinks : rLinks )
921 if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( nLinks.get() ) )
923 if( (pDdeLink->GetAppl() == rAppl) &&
924 (pDdeLink->GetTopic() == rTopic) &&
925 (pDdeLink->GetItem() == rItem) &&
926 ((nMode == SC_DDE_IGNOREMODE) || (nMode == pDdeLink->GetMode())) )
927 return pDdeLink;
928 if( pnDdePos ) ++*pnDdePos;
932 return nullptr;
935 /** Returns a pointer to the specified DDE link.
936 @param nDdePos Index of the DDE link (does not include other links from link manager).
937 @return The DDE link, if it exists, otherwise 0. */
938 ScDdeLink* lclGetDdeLink( const sfx2::LinkManager* pLinkManager, size_t nDdePos )
940 if( pLinkManager )
942 size_t nDdeIndex = 0; // counts only the DDE links
943 for( const auto& pLink : pLinkManager->GetLinks() )
945 if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( pLink.get() ) )
947 if( nDdeIndex == nDdePos )
948 return pDdeLink;
949 ++nDdeIndex;
953 return nullptr;
956 } // namespace
958 bool ScDocument::FindDdeLink( std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem,
959 sal_uInt8 nMode, size_t& rnDdePos )
961 return lclGetDdeLink( GetLinkManager(), rAppl, rTopic, rItem, nMode, &rnDdePos ) != nullptr;
964 bool ScDocument::GetDdeLinkData( size_t nDdePos, OUString& rAppl, OUString& rTopic, OUString& rItem ) const
966 if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
968 rAppl = pDdeLink->GetAppl();
969 rTopic = pDdeLink->GetTopic();
970 rItem = pDdeLink->GetItem();
971 return true;
973 return false;
976 bool ScDocument::GetDdeLinkMode( size_t nDdePos, sal_uInt8& rnMode ) const
978 if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
980 rnMode = pDdeLink->GetMode();
981 return true;
983 return false;
986 const ScMatrix* ScDocument::GetDdeLinkResultMatrix( size_t nDdePos ) const
988 const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos );
989 return pDdeLink ? pDdeLink->GetResult() : nullptr;
992 bool ScDocument::CreateDdeLink( const OUString& rAppl, const OUString& rTopic, const OUString& rItem, sal_uInt8 nMode, const ScMatrixRef& pResults )
994 /* Create a DDE link without updating it (i.e. for Excel import), to prevent
995 unwanted connections. First try to find existing link. Set result array
996 on existing and new links. */
997 //TODO: store DDE links additionally at document (for efficiency)?
998 OSL_ENSURE( nMode != SC_DDE_IGNOREMODE, "ScDocument::CreateDdeLink - SC_DDE_IGNOREMODE not allowed here" );
1000 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
1001 if (!pMgr)
1002 return false;
1004 if (nMode != SC_DDE_IGNOREMODE)
1006 ScDdeLink* pDdeLink = lclGetDdeLink(pMgr, rAppl, rTopic, rItem, nMode);
1007 if( !pDdeLink )
1009 // create a new DDE link, but without TryUpdate
1010 pDdeLink = new ScDdeLink( *this, rAppl, rTopic, rItem, nMode );
1011 pMgr->InsertDDELink(pDdeLink, rAppl, rTopic, rItem);
1014 // insert link results
1015 if( pResults )
1016 pDdeLink->SetResult( pResults );
1018 return true;
1020 return false;
1023 bool ScDocument::SetDdeLinkResultMatrix( size_t nDdePos, const ScMatrixRef& pResults )
1025 if( ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
1027 pDdeLink->SetResult( pResults );
1028 return true;
1030 return false;
1033 bool ScDocument::HasAreaLinks() const
1035 const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
1036 if (!pMgr)
1037 return false;
1039 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1040 sal_uInt16 nCount = rLinks.size();
1041 for (sal_uInt16 i=0; i<nCount; i++)
1042 if (nullptr != dynamic_cast<const ScAreaLink* >(rLinks[i].get()))
1043 return true;
1045 return false;
1048 void ScDocument::UpdateAreaLinks()
1050 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
1051 if (!pMgr)
1052 return;
1054 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1055 for (const auto & rLink : rLinks)
1057 ::sfx2::SvBaseLink* pBase = rLink.get();
1058 if (dynamic_cast<const ScAreaLink*>( pBase) != nullptr)
1059 pBase->Update();
1063 void ScDocument::DeleteAreaLinksOnTab( SCTAB nTab )
1065 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
1066 if (!pMgr)
1067 return;
1069 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1070 sfx2::SvBaseLinks::size_type nPos = 0;
1071 while ( nPos < rLinks.size() )
1073 const ::sfx2::SvBaseLink* pBase = rLinks[nPos].get();
1074 const ScAreaLink* pLink = dynamic_cast<const ScAreaLink*>(pBase);
1075 if (pLink && pLink->GetDestArea().aStart.Tab() == nTab)
1076 pMgr->Remove(nPos);
1077 else
1078 ++nPos;
1082 void ScDocument::UpdateRefAreaLinks( UpdateRefMode eUpdateRefMode,
1083 const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
1085 sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
1086 if (!pMgr)
1087 return;
1089 bool bAnyUpdate = false;
1091 const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
1092 sal_uInt16 nCount = rLinks.size();
1093 for (sal_uInt16 i=0; i<nCount; i++)
1095 ::sfx2::SvBaseLink* pBase = rLinks[i].get();
1096 if (ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase))
1098 ScRange aOutRange = pLink->GetDestArea();
1100 SCCOL nCol1 = aOutRange.aStart.Col();
1101 SCROW nRow1 = aOutRange.aStart.Row();
1102 SCTAB nTab1 = aOutRange.aStart.Tab();
1103 SCCOL nCol2 = aOutRange.aEnd.Col();
1104 SCROW nRow2 = aOutRange.aEnd.Row();
1105 SCTAB nTab2 = aOutRange.aEnd.Tab();
1107 ScRefUpdateRes eRes =
1108 ScRefUpdate::Update( this, eUpdateRefMode,
1109 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
1110 rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
1111 nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
1112 if ( eRes != UR_NOTHING )
1114 pLink->SetDestArea( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );
1115 bAnyUpdate = true;
1120 if ( !bAnyUpdate )
1121 return;
1123 // #i52120# Look for duplicates (after updating all positions).
1124 // If several links start at the same cell, the one with the lower index is removed
1125 // (file format specifies only one link definition for a cell).
1127 sal_uInt16 nFirstIndex = 0;
1128 while ( nFirstIndex < nCount )
1130 bool bFound = false;
1131 ::sfx2::SvBaseLink* pFirst = rLinks[nFirstIndex].get();
1132 if (ScAreaLink* pFirstLink = dynamic_cast<ScAreaLink*>(pFirst))
1134 ScAddress aFirstPos = pFirstLink->GetDestArea().aStart;
1135 for ( sal_uInt16 nSecondIndex = nFirstIndex + 1; nSecondIndex < nCount && !bFound; ++nSecondIndex )
1137 ::sfx2::SvBaseLink* pSecond = rLinks[nSecondIndex].get();
1138 ScAreaLink* pSecondLink = dynamic_cast<ScAreaLink*>(pSecond);
1139 if (pSecondLink && pSecondLink->GetDestArea().aStart == aFirstPos)
1141 // remove the first link, exit the inner loop, don't increment nFirstIndex
1142 pMgr->Remove(pFirst);
1143 nCount = rLinks.size();
1144 bFound = true;
1148 if (!bFound)
1149 ++nFirstIndex;
1153 void ScDocument::CheckLinkFormulaNeedingCheck( const ScTokenArray& rCode )
1155 if (HasLinkFormulaNeedingCheck())
1156 return;
1158 // Prefer RPN over tokenized formula if available.
1159 if (rCode.GetCodeLen())
1161 if (rCode.HasOpCodeRPN(ocDde) || rCode.HasOpCodeRPN(ocWebservice))
1162 SetLinkFormulaNeedingCheck(true);
1164 else if (rCode.GetLen())
1166 if (rCode.HasOpCode(ocDde) || rCode.HasOpCode(ocWebservice))
1167 SetLinkFormulaNeedingCheck(true);
1169 else
1171 // Possible with named expression without expression like Excel
1172 // internal print ranges, obscure user define names, ... formula error
1173 // cells without formula ...
1174 SAL_WARN("sc.core","ScDocument::CheckLinkFormulaNeedingCheck - called with empty ScTokenArray");
1178 // TimerDelays etc.
1179 void ScDocument::KeyInput()
1181 if ( pChartListenerCollection->hasListeners() )
1182 pChartListenerCollection->StartTimer();
1183 if (apTemporaryChartLock)
1184 apTemporaryChartLock->StartOrContinueLocking();
1187 SfxBindings* ScDocument::GetViewBindings()
1189 // used to invalidate slots after changes to this document
1191 if ( !mpShell )
1192 return nullptr; // no ObjShell -> no view
1194 // first check current view
1195 SfxViewFrame* pViewFrame = SfxViewFrame::Current();
1196 if ( pViewFrame && pViewFrame->GetObjectShell() != mpShell ) // wrong document?
1197 pViewFrame = nullptr;
1199 // otherwise use first view for this doc
1200 if ( !pViewFrame )
1201 pViewFrame = SfxViewFrame::GetFirst( mpShell );
1203 if (pViewFrame)
1204 return &pViewFrame->GetBindings();
1205 else
1206 return nullptr;
1209 void ScDocument::TransliterateText( const ScMarkData& rMultiMark, TransliterationFlags nType )
1211 OSL_ENSURE( rMultiMark.IsMultiMarked(), "TransliterateText: no selection" );
1213 utl::TransliterationWrapper aTransliterationWrapper( comphelper::getProcessComponentContext(), nType );
1214 bool bConsiderLanguage = aTransliterationWrapper.needLanguageForTheMode();
1215 LanguageType nLanguage = LANGUAGE_SYSTEM;
1217 std::unique_ptr<ScEditEngineDefaulter> pEngine; // not using mpEditEngine member because of defaults
1219 SCTAB nCount = GetTableCount();
1220 for (const SCTAB& nTab : rMultiMark)
1222 if (nTab >= nCount)
1223 break;
1225 if ( maTabs[nTab] )
1227 SCCOL nCol = 0;
1228 SCROW nRow = 0;
1230 bool bFound = rMultiMark.IsCellMarked( nCol, nRow );
1231 if (!bFound)
1232 bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );
1234 while (bFound)
1236 ScRefCellValue aCell(*this, ScAddress(nCol, nRow, nTab));
1238 // fdo#32786 TITLE_CASE/SENTENCE_CASE need the extra handling in EditEngine (loop over words/sentences).
1239 // Still use TransliterationWrapper directly for text cells with other transliteration types,
1240 // for performance reasons.
1241 if (aCell.getType() == CELLTYPE_EDIT ||
1242 (aCell.getType() == CELLTYPE_STRING &&
1243 ( nType == TransliterationFlags::SENTENCE_CASE || nType == TransliterationFlags::TITLE_CASE)))
1245 if (!pEngine)
1246 pEngine.reset(new ScFieldEditEngine(this, GetEnginePool(), GetEditPool()));
1248 // defaults from cell attributes must be set so right language is used
1249 const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
1250 SfxItemSet aDefaults( pEngine->GetEmptyItemSet() );
1251 if ( ScStyleSheet* pPreviewStyle = GetPreviewCellStyle( nCol, nRow, nTab ) )
1253 ScPatternAttr aPreviewPattern( *pPattern );
1254 aPreviewPattern.SetStyleSheet(pPreviewStyle);
1255 aPreviewPattern.FillEditItemSet( &aDefaults );
1257 else
1259 SfxItemSet* pFontSet = GetPreviewFont( nCol, nRow, nTab );
1260 pPattern->FillEditItemSet( &aDefaults, pFontSet );
1262 pEngine->SetDefaults( std::move(aDefaults) );
1263 if (aCell.getType() == CELLTYPE_STRING)
1264 pEngine->SetTextCurrentDefaults(aCell.getSharedString()->getString());
1265 else if (aCell.getEditText())
1266 pEngine->SetTextCurrentDefaults(*aCell.getEditText());
1268 pEngine->ClearModifyFlag();
1270 sal_Int32 nLastPar = pEngine->GetParagraphCount();
1271 if (nLastPar)
1272 --nLastPar;
1273 sal_Int32 nTxtLen = pEngine->GetTextLen(nLastPar);
1274 ESelection aSelAll( 0, 0, nLastPar, nTxtLen );
1276 pEngine->TransliterateText( aSelAll, nType );
1278 if ( pEngine->IsModified() )
1280 ScEditAttrTester aTester( pEngine.get() );
1281 if ( aTester.NeedsObject() )
1283 // remove defaults (paragraph attributes) before creating text object
1284 pEngine->SetDefaults( std::make_unique<SfxItemSet>( pEngine->GetEmptyItemSet() ) );
1286 // The cell will take ownership of the text object instance.
1287 SetEditText(ScAddress(nCol,nRow,nTab), pEngine->CreateTextObject());
1289 else
1291 ScSetStringParam aParam;
1292 aParam.setTextInput();
1293 SetString(ScAddress(nCol,nRow,nTab), pEngine->GetText(), &aParam);
1298 else if (aCell.getType() == CELLTYPE_STRING)
1300 OUString aOldStr = aCell.getSharedString()->getString();
1301 sal_Int32 nOldLen = aOldStr.getLength();
1303 if ( bConsiderLanguage )
1305 SvtScriptType nScript = GetStringScriptType( aOldStr ); //TODO: cell script type?
1306 sal_uInt16 nWhich = ( nScript == SvtScriptType::ASIAN ) ? ATTR_CJK_FONT_LANGUAGE :
1307 ( ( nScript == SvtScriptType::COMPLEX ) ? ATTR_CTL_FONT_LANGUAGE :
1308 ATTR_FONT_LANGUAGE );
1309 nLanguage = static_cast<const SvxLanguageItem*>(GetAttr( nCol, nRow, nTab, nWhich ))->GetValue();
1312 uno::Sequence<sal_Int32> aOffsets;
1313 OUString aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );
1315 if ( aNewStr != aOldStr )
1317 ScSetStringParam aParam;
1318 aParam.setTextInput();
1319 SetString(ScAddress(nCol,nRow,nTab), aNewStr, &aParam);
1322 bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );
1328 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */