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 <svx/algitem.hxx>
22 #include <editeng/brushitem.hxx>
23 #include <editeng/editobj.hxx>
24 #include <svl/srchitem.hxx>
25 #include <editeng/langitem.hxx>
26 #include <o3tl/unit_conversion.hxx>
27 #include <sfx2/docfile.hxx>
28 #include <sfx2/dispatch.hxx>
29 #include <sfx2/objsh.hxx>
30 #include <sfx2/sfxsids.hrc>
31 #include <sfx2/viewfrm.hxx>
32 #include <sfx2/viewsh.hxx>
33 #include <svl/intitem.hxx>
34 #include <svl/stritem.hxx>
35 #include <svl/zforlist.hxx>
36 #include <svl/zformat.hxx>
37 #include <vcl/keycodes.hxx>
38 #include <vcl/virdev.hxx>
39 #include <vcl/settings.hxx>
40 #include <vcl/svapp.hxx>
41 #include <unotools/charclass.hxx>
42 #include <unotools/securityoptions.hxx>
43 #include <osl/diagnose.h>
44 
45 #include <i18nlangtag/mslangid.hxx>
46 #include <comphelper/doublecheckedinit.hxx>
47 #include <comphelper/processfactory.hxx>
48 #include <comphelper/string.hxx>
49 #include <unotools/calendarwrapper.hxx>
50 #include <unotools/collatorwrapper.hxx>
51 #include <unotools/syslocale.hxx>
52 #include <unotools/transliterationwrapper.hxx>
53 
54 #include <comphelper/lok.hxx>
55 
56 #include <global.hxx>
57 #include <scresid.hxx>
58 #include <autoform.hxx>
59 #include <patattr.hxx>
60 #include <addincol.hxx>
61 #include <adiasync.hxx>
62 #include <userlist.hxx>
63 #include <interpre.hxx>
64 #include <unitconv.hxx>
65 #include <compiler.hxx>
66 #include <parclass.hxx>
67 #include <funcdesc.hxx>
68 #include <globstr.hrc>
69 #include <strings.hrc>
70 #include <scmod.hxx>
71 #include <editutil.hxx>
72 #include <docsh.hxx>
73 
74 tools::SvRef<ScDocShell>  ScGlobal::xDrawClipDocShellRef;
75 std::unique_ptr<SvxSearchItem> ScGlobal::xSearchItem;
76 std::unique_ptr<ScAutoFormat> ScGlobal::xAutoFormat;
77 std::atomic<LegacyFuncCollection*> ScGlobal::pLegacyFuncCollection(nullptr);
78 std::atomic<ScUnoAddInCollection*> ScGlobal::pAddInCollection(nullptr);
79 std::unique_ptr<ScUserList> ScGlobal::xUserList;
80 LanguageType    ScGlobal::eLnge = LANGUAGE_SYSTEM;
81 std::atomic<css::lang::Locale*> ScGlobal::pLocale(nullptr);
82 std::unique_ptr<SvtSysLocale>   ScGlobal::xSysLocale;
83 std::unique_ptr<CalendarWrapper> ScGlobal::xCalendar;
84 std::atomic<CollatorWrapper*> ScGlobal::pCollator(nullptr);
85 std::atomic<CollatorWrapper*> ScGlobal::pCaseCollator(nullptr);
86 std::atomic<::utl::TransliterationWrapper*> ScGlobal::pTransliteration(nullptr);
87 std::atomic<::utl::TransliterationWrapper*> ScGlobal::pCaseTransliteration(nullptr);
88 css::uno::Reference< css::i18n::XOrdinalSuffix> ScGlobal::xOrdinalSuffix;
89 const OUString  ScGlobal::aEmptyOUString;
90 OUString        ScGlobal::aStrClipDocName;
91 OUString        ScGlobal::aStrErrorStringNoRef;
92 
93 std::unique_ptr<SvxBrushItem> ScGlobal::xEmptyBrushItem;
94 std::unique_ptr<SvxBrushItem> ScGlobal::xButtonBrushItem;
95 
96 std::unique_ptr<ScFunctionList> ScGlobal::xStarCalcFunctionList;
97 std::unique_ptr<ScFunctionMgr> ScGlobal::xStarCalcFunctionMgr;
98 
99 std::atomic<ScUnitConverter*> ScGlobal::pUnitConverter(nullptr);
100 std::unique_ptr<SvNumberFormatter> ScGlobal::xEnglishFormatter;
101 std::unique_ptr<ScFieldEditEngine> ScGlobal::xFieldEditEngine;
102 
103 double          ScGlobal::nScreenPPTX           = 96.0;
104 double          ScGlobal::nScreenPPTY           = 96.0;
105 
106 sal_uInt16          ScGlobal::nDefFontHeight        = 225;
107 sal_uInt16          ScGlobal::nStdRowHeight         = 256;
108 
109 tools::Long            ScGlobal::nLastRowHeightExtra   = 0;
110 tools::Long            ScGlobal::nLastColWidthExtra    = STD_EXTRA_WIDTH;
111 
112 SfxViewShell* pScActiveViewShell = nullptr; //FIXME: Make this a member
113 sal_uInt16 nScClickMouseModifier = 0;    //FIXME: This too
114 sal_uInt16 nScFillModeMouseModifier = 0; //FIXME: And this
115 
116 bool ScGlobal::bThreadedGroupCalcInProgress = false;
117 
118 // Static functions
119 
HasAttrChanged(const SfxItemSet & rNewAttrs,const SfxItemSet & rOldAttrs,const sal_uInt16 nWhich)120 bool ScGlobal::HasAttrChanged( const SfxItemSet&  rNewAttrs,
121                                const SfxItemSet&  rOldAttrs,
122                                const sal_uInt16       nWhich )
123 {
124     bool                bInvalidate = false;
125     const SfxPoolItem*  pNewItem    = nullptr;
126     const SfxItemState  eNewState   = rNewAttrs.GetItemState( nWhich, true, &pNewItem );
127     const SfxPoolItem*  pOldItem    = nullptr;
128     const SfxItemState  eOldState   = rOldAttrs.GetItemState( nWhich, true, &pOldItem );
129 
130     if ( eNewState == eOldState )
131     {
132         // Both Items set
133         // PoolItems, meaning comparing pointers is valid
134         if ( SfxItemState::SET == eOldState )
135             bInvalidate = (pNewItem != pOldItem);
136     }
137     else
138     {
139         // Contains a Default Item
140         // PoolItems, meaning Item comparison necessary
141         if (!pOldItem)
142             pOldItem = &rOldAttrs.GetPool()->GetDefaultItem( nWhich );
143 
144         if (!pNewItem)
145             pNewItem = &rNewAttrs.GetPool()->GetDefaultItem( nWhich );
146 
147         bInvalidate = (*pNewItem != *pOldItem);
148     }
149 
150     return bInvalidate;
151 }
152 
GetStandardFormat(SvNumberFormatter & rFormatter,sal_uInt32 nFormat,SvNumFormatType nType)153 sal_uInt32 ScGlobal::GetStandardFormat( SvNumberFormatter& rFormatter,
154         sal_uInt32 nFormat, SvNumFormatType nType )
155 {
156     const SvNumberformat* pFormat = rFormatter.GetEntry( nFormat );
157     if ( pFormat )
158         return rFormatter.GetStandardFormat( nFormat, nType, pFormat->GetLanguage() );
159     return rFormatter.GetStandardFormat( nType, eLnge );
160 }
161 
GetStandardRowHeight()162 sal_uInt16 ScGlobal::GetStandardRowHeight()
163 {
164     return nStdRowHeight;
165 }
166 
GetEnglishFormatter()167 SvNumberFormatter* ScGlobal::GetEnglishFormatter()
168 {
169     assert(!bThreadedGroupCalcInProgress);
170     if ( !xEnglishFormatter )
171     {
172         xEnglishFormatter.reset( new SvNumberFormatter(
173             ::comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ) );
174         xEnglishFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT );
175     }
176     return xEnglishFormatter.get();
177 }
178 
CheckWidthInvalidate(bool & bNumFormatChanged,const SfxItemSet & rNewAttrs,const SfxItemSet & rOldAttrs)179 bool ScGlobal::CheckWidthInvalidate( bool& bNumFormatChanged,
180                                      const SfxItemSet& rNewAttrs,
181                                      const SfxItemSet& rOldAttrs )
182 {
183     // Check whether attribute changes in rNewAttrs compared to rOldAttrs render
184     // the text width at a cell invalid
185     bNumFormatChanged =
186             HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_VALUE_FORMAT );
187     return ( bNumFormatChanged
188         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_LANGUAGE_FORMAT )
189         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT )
190         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT )
191         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT )
192         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_HEIGHT )
193         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_HEIGHT )
194         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_HEIGHT )
195         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_WEIGHT )
196         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_WEIGHT )
197         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_WEIGHT )
198         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_POSTURE )
199         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_POSTURE )
200         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_POSTURE )
201         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_UNDERLINE )
202         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_OVERLINE )
203         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_CROSSEDOUT )
204         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_CONTOUR )
205         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_SHADOWED )
206         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_STACKED )
207         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_ROTATE_VALUE )
208         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_ROTATE_MODE )
209         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_LINEBREAK )
210         || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_MARGIN )
211         );
212 }
213 
GetSearchItem()214 const SvxSearchItem& ScGlobal::GetSearchItem()
215 {
216     assert(!bThreadedGroupCalcInProgress);
217     if (!xSearchItem)
218     {
219         xSearchItem.reset(new SvxSearchItem( SID_SEARCH_ITEM ));
220         xSearchItem->SetAppFlag( SvxSearchApp::CALC );
221     }
222     return *xSearchItem;
223 }
224 
SetSearchItem(const SvxSearchItem & rNew)225 void ScGlobal::SetSearchItem( const SvxSearchItem& rNew )
226 {
227     assert(!bThreadedGroupCalcInProgress);
228     // FIXME: An assignment operator would be nice here
229     xSearchItem.reset(rNew.Clone());
230 
231     xSearchItem->SetWhich( SID_SEARCH_ITEM );
232     xSearchItem->SetAppFlag( SvxSearchApp::CALC );
233 }
234 
ClearAutoFormat()235 void ScGlobal::ClearAutoFormat()
236 {
237     assert(!bThreadedGroupCalcInProgress);
238     if (xAutoFormat)
239     {
240         //  When modified via StarOne then only the SaveLater flag is set and no saving is done.
241         //  If the flag is set then save now.
242         if (xAutoFormat->IsSaveLater())
243             xAutoFormat->Save();
244         xAutoFormat.reset();
245     }
246 }
247 
GetAutoFormat()248 ScAutoFormat* ScGlobal::GetAutoFormat()
249 {
250     return xAutoFormat.get();
251 }
252 
GetOrCreateAutoFormat()253 ScAutoFormat* ScGlobal::GetOrCreateAutoFormat()
254 {
255     assert(!bThreadedGroupCalcInProgress);
256     if ( !xAutoFormat )
257     {
258         xAutoFormat.reset(new ScAutoFormat);
259         xAutoFormat->Load();
260     }
261 
262     return xAutoFormat.get();
263 }
264 
GetLegacyFuncCollection()265 LegacyFuncCollection* ScGlobal::GetLegacyFuncCollection()
266 {
267     return comphelper::doubleCheckedInit( pLegacyFuncCollection, []() { return new LegacyFuncCollection(); });
268 }
269 
GetAddInCollection()270 ScUnoAddInCollection* ScGlobal::GetAddInCollection()
271 {
272     return comphelper::doubleCheckedInit( pAddInCollection, []() { return new ScUnoAddInCollection(); });
273 }
274 
GetUserList()275 ScUserList* ScGlobal::GetUserList()
276 {
277     assert(!bThreadedGroupCalcInProgress);
278     // Hack: Load Cfg item at the App
279     global_InitAppOptions();
280 
281     if (!xUserList)
282         xUserList.reset(new ScUserList());
283     return xUserList.get();
284 }
285 
SetUserList(const ScUserList * pNewList)286 void ScGlobal::SetUserList( const ScUserList* pNewList )
287 {
288     assert(!bThreadedGroupCalcInProgress);
289     if ( pNewList )
290     {
291         if ( !xUserList )
292             xUserList.reset( new ScUserList( *pNewList ) );
293         else
294             *xUserList = *pNewList;
295     }
296     else
297     {
298         xUserList.reset();
299     }
300 }
301 
GetErrorString(FormulaError nErr)302 OUString ScGlobal::GetErrorString(FormulaError nErr)
303 {
304     const char* pErrNumber;
305     switch (nErr)
306     {
307         case FormulaError::NoRef:
308             // tdf#144249 This may get called very extensively, so cached.
309             return aStrErrorStringNoRef;
310         case FormulaError::NoAddin:
311             pErrNumber = STR_NO_ADDIN;
312             break;
313         case FormulaError::NoMacro:
314             pErrNumber = STR_NO_MACRO;
315             break;
316         case FormulaError::NotAvailable:
317             return ScCompiler::GetNativeSymbol(ocErrNA);
318         case FormulaError::NoName:
319             return ScCompiler::GetNativeSymbol(ocErrName);
320         case FormulaError::NoValue:
321             return ScCompiler::GetNativeSymbol(ocErrValue);
322         case FormulaError::NoCode:
323             return ScCompiler::GetNativeSymbol(ocErrNull);
324         case FormulaError::DivisionByZero:
325             return ScCompiler::GetNativeSymbol(ocErrDivZero);
326         case FormulaError::IllegalFPOperation:
327             return ScCompiler::GetNativeSymbol(ocErrNum);
328         default:
329             return ScResId(STR_ERROR_STR) + OUString::number( static_cast<int>(nErr) );
330     }
331     return ScResId(pErrNumber);
332 }
333 
GetLongErrorString(FormulaError nErr)334 OUString ScGlobal::GetLongErrorString(FormulaError nErr)
335 {
336     const char* pErrNumber;
337     switch (nErr)
338     {
339         case FormulaError::NONE:
340             return OUString();
341         case FormulaError::IllegalArgument:
342             pErrNumber = STR_LONG_ERR_ILL_ARG;
343         break;
344         case FormulaError::IllegalFPOperation:
345             pErrNumber = STR_LONG_ERR_ILL_FPO;
346         break;
347         case FormulaError::IllegalChar:
348             pErrNumber = STR_LONG_ERR_ILL_CHAR;
349         break;
350         case FormulaError::IllegalParameter:
351             pErrNumber = STR_LONG_ERR_ILL_PAR;
352         break;
353         case FormulaError::Pair:
354         case FormulaError::PairExpected:
355             pErrNumber = STR_LONG_ERR_PAIR;
356         break;
357         case FormulaError::OperatorExpected:
358             pErrNumber = STR_LONG_ERR_OP_EXP;
359         break;
360         case FormulaError::VariableExpected:
361         case FormulaError::ParameterExpected:
362             pErrNumber = STR_LONG_ERR_VAR_EXP;
363         break;
364         case FormulaError::CodeOverflow:
365             pErrNumber = STR_LONG_ERR_CODE_OVF;
366         break;
367         case FormulaError::StringOverflow:
368             pErrNumber = STR_LONG_ERR_STR_OVF;
369         break;
370         case FormulaError::StackOverflow:
371             pErrNumber = STR_LONG_ERR_STACK_OVF;
372         break;
373         case FormulaError::MatrixSize:
374             pErrNumber = STR_LONG_ERR_MATRIX_SIZE;
375         break;
376         case FormulaError::UnknownState:
377         case FormulaError::UnknownVariable:
378         case FormulaError::UnknownOpCode:
379         case FormulaError::UnknownStackVariable:
380         case FormulaError::UnknownToken:
381         case FormulaError::NoCode:
382             pErrNumber = STR_LONG_ERR_SYNTAX;
383         break;
384         case FormulaError::CircularReference:
385             pErrNumber = STR_LONG_ERR_CIRC_REF;
386         break;
387         case FormulaError::NoConvergence:
388             pErrNumber = STR_LONG_ERR_NO_CONV;
389         break;
390         case FormulaError::NoRef:
391             pErrNumber = STR_LONG_ERR_NO_REF;
392         break;
393         case FormulaError::NoName:
394             pErrNumber = STR_LONG_ERR_NO_NAME;
395         break;
396         case FormulaError::NoAddin:
397             pErrNumber = STR_LONG_ERR_NO_ADDIN;
398         break;
399         case FormulaError::NoMacro:
400             pErrNumber = STR_LONG_ERR_NO_MACRO;
401         break;
402         case FormulaError::DivisionByZero:
403             pErrNumber = STR_LONG_ERR_DIV_ZERO;
404         break;
405         case FormulaError::NestedArray:
406             pErrNumber = STR_ERR_LONG_NESTED_ARRAY;
407         break;
408         case FormulaError::BadArrayContent:
409             pErrNumber = STR_ERR_LONG_BAD_ARRAY_CONTENT;
410         break;
411         case FormulaError::LinkFormulaNeedingCheck:
412             pErrNumber = STR_ERR_LONG_LINK_FORMULA_NEEDING_CHECK;
413         break;
414         case FormulaError::NoValue:
415             pErrNumber = STR_LONG_ERR_NO_VALUE;
416         break;
417         case FormulaError::NotAvailable:
418             pErrNumber = STR_LONG_ERR_NV;
419         break;
420         default:
421             return ScResId(STR_ERROR_STR) + OUString::number( static_cast<int>(nErr) );
422     }
423     return ScResId(pErrNumber);
424 }
425 
GetButtonBrushItem()426 SvxBrushItem* ScGlobal::GetButtonBrushItem()
427 {
428     assert(!bThreadedGroupCalcInProgress);
429     xButtonBrushItem->SetColor( Application::GetSettings().GetStyleSettings().GetFaceColor() );
430     return xButtonBrushItem.get();
431 }
432 
Init()433 void ScGlobal::Init()
434 {
435     // The default language for number formats (ScGlobal::eLnge) must
436     // always be LANGUAGE_SYSTEM
437     // FIXME: So remove this variable?
438     eLnge = LANGUAGE_SYSTEM;
439 
440     xSysLocale = std::make_unique<SvtSysLocale>();
441 
442     xEmptyBrushItem = std::make_unique<SvxBrushItem>( COL_TRANSPARENT, ATTR_BACKGROUND );
443     xButtonBrushItem = std::make_unique<SvxBrushItem>( Color(), ATTR_BACKGROUND );
444 
445     InitPPT();
446     //ScCompiler::InitSymbolsNative();
447     // ScParameterClassification _after_ Compiler, needs function resources if
448     // arguments are to be merged in, which in turn need strings of function
449     // names from the compiler.
450     ScParameterClassification::Init();
451 
452     InitAddIns();
453 
454     aStrClipDocName = ScResId( SCSTR_NONAME ) + "1";
455     aStrErrorStringNoRef = ScResId( STR_NO_REF_TABLE );
456 
457     //  ScDocumentPool::InitVersionMaps() has been called earlier already
458 }
459 
InitPPT()460 void ScGlobal::InitPPT()
461 {
462     OutputDevice* pDev = Application::GetDefaultDevice();
463 
464     if (comphelper::LibreOfficeKit::isActive())
465     {
466         // LOK: the below limited precision is not enough for RowColumnHeader.
467         nScreenPPTX = o3tl::convert<double>(pDev->GetDPIX(), o3tl::Length::twip, o3tl::Length::in);
468         nScreenPPTY = o3tl::convert<double>(pDev->GetDPIY(), o3tl::Length::twip, o3tl::Length::in);
469     }
470     else
471     {
472         // Avoid cumulative placement errors by intentionally limiting
473         // precision.
474         Point aPix1000 = pDev->LogicToPixel(Point(1000, 1000), MapMode(MapUnit::MapTwip));
475         nScreenPPTX = aPix1000.X() / 1000.0;
476         nScreenPPTY = aPix1000.Y() / 1000.0;
477     }
478 }
479 
GetClipDocName()480 const OUString& ScGlobal::GetClipDocName()
481 {
482     return aStrClipDocName;
483 }
484 
SetClipDocName(const OUString & rNew)485 void ScGlobal::SetClipDocName( const OUString& rNew )
486 {
487     assert(!bThreadedGroupCalcInProgress);
488     aStrClipDocName = rNew;
489 }
490 
InitTextHeight(const SfxItemPool * pPool)491 void ScGlobal::InitTextHeight(const SfxItemPool* pPool)
492 {
493     if (!pPool)
494     {
495         OSL_FAIL("ScGlobal::InitTextHeight: No Pool");
496         return;
497     }
498 
499     const ScPatternAttr& rPattern = pPool->GetDefaultItem(ATTR_PATTERN);
500 
501     OutputDevice* pDefaultDev = Application::GetDefaultDevice();
502     ScopedVclPtrInstance< VirtualDevice > pVirtWindow( *pDefaultDev );
503     pVirtWindow->SetMapMode(MapMode(MapUnit::MapPixel));
504     vcl::Font aDefFont;
505     rPattern.GetFont(aDefFont, SC_AUTOCOL_BLACK, pVirtWindow); // Font color doesn't matter here
506     pVirtWindow->SetFont(aDefFont);
507     sal_uInt16 nTest = static_cast<sal_uInt16>(
508         pVirtWindow->PixelToLogic(Size(0, pVirtWindow->GetTextHeight()), MapMode(MapUnit::MapTwip)).Height());
509 
510     if (nTest > nDefFontHeight)
511         nDefFontHeight = nTest;
512 
513     const SvxMarginItem& rMargin = rPattern.GetItem(ATTR_MARGIN);
514 
515     nTest = static_cast<sal_uInt16>(nDefFontHeight + rMargin.GetTopMargin()
516                                     + rMargin.GetBottomMargin() - STD_ROWHEIGHT_DIFF);
517 
518     if (nTest > nStdRowHeight)
519         nStdRowHeight = nTest;
520 }
521 
Clear()522 void ScGlobal::Clear()
523 {
524     // Destroy asyncs _before_ ExitExternalFunc!
525     theAddInAsyncTbl.clear();
526     ExitExternalFunc();
527     ClearAutoFormat();
528     xSearchItem.reset();
529     delete pLegacyFuncCollection.load(); pLegacyFuncCollection = nullptr;
530     delete pAddInCollection.load(); pAddInCollection = nullptr;
531     xUserList.reset();
532     xStarCalcFunctionList.reset(); // Destroy before ResMgr!
533     xStarCalcFunctionMgr.reset();
534     ScParameterClassification::Exit();
535     ScCompiler::DeInit();
536     ScInterpreter::GlobalExit(); // Delete static Stack
537 
538     xEmptyBrushItem.reset();
539     xButtonBrushItem.reset();
540     xEnglishFormatter.reset();
541     delete pCaseTransliteration.load(); pCaseTransliteration = nullptr;
542     delete pTransliteration.load(); pTransliteration = nullptr;
543     delete pCaseCollator.load(); pCaseCollator = nullptr;
544     delete pCollator.load(); pCollator = nullptr;
545     xCalendar.reset();
546     xSysLocale.reset();
547     delete pLocale.load(); pLocale = nullptr;
548 
549     delete pUnitConverter.load(); pUnitConverter = nullptr;
550     xFieldEditEngine.reset();
551 
552     xDrawClipDocShellRef.clear();
553 }
554 
GetCharsetValue(const OUString & rCharSet)555 rtl_TextEncoding ScGlobal::GetCharsetValue( const OUString& rCharSet )
556 {
557     // new TextEncoding values
558     if ( CharClass::isAsciiNumeric( rCharSet ) )
559     {
560         sal_Int32 nVal = rCharSet.toInt32();
561         if ( nVal == RTL_TEXTENCODING_DONTKNOW )
562             return osl_getThreadTextEncoding();
563         return static_cast<rtl_TextEncoding>(nVal);
564     }
565     // old CharSet values for compatibility
566     else if (rCharSet.equalsIgnoreAsciiCase("ANSI")     ) return RTL_TEXTENCODING_MS_1252;
567     else if (rCharSet.equalsIgnoreAsciiCase("MAC")      ) return RTL_TEXTENCODING_APPLE_ROMAN;
568     else if (rCharSet.equalsIgnoreAsciiCase("IBMPC")    ) return RTL_TEXTENCODING_IBM_850;
569     else if (rCharSet.equalsIgnoreAsciiCase("IBMPC_437")) return RTL_TEXTENCODING_IBM_437;
570     else if (rCharSet.equalsIgnoreAsciiCase("IBMPC_850")) return RTL_TEXTENCODING_IBM_850;
571     else if (rCharSet.equalsIgnoreAsciiCase("IBMPC_860")) return RTL_TEXTENCODING_IBM_860;
572     else if (rCharSet.equalsIgnoreAsciiCase("IBMPC_861")) return RTL_TEXTENCODING_IBM_861;
573     else if (rCharSet.equalsIgnoreAsciiCase("IBMPC_863")) return RTL_TEXTENCODING_IBM_863;
574     else if (rCharSet.equalsIgnoreAsciiCase("IBMPC_865")) return RTL_TEXTENCODING_IBM_865;
575     // Some wrong "help" on the net mentions UTF8 and even unoconv uses it,
576     // which worked accidentally if the system encoding is UTF-8 anyway, so
577     // support it ;) but only when reading.
578     else if (rCharSet.equalsIgnoreAsciiCase("UTF8"))      return RTL_TEXTENCODING_UTF8;
579     else if (rCharSet.equalsIgnoreAsciiCase("UTF-8"))     return RTL_TEXTENCODING_UTF8;
580     else return osl_getThreadTextEncoding();
581 }
582 
GetCharsetString(rtl_TextEncoding eVal)583 OUString ScGlobal::GetCharsetString( rtl_TextEncoding eVal )
584 {
585     const char* pChar;
586     switch ( eVal )
587     {
588         // old CharSet strings for compatibility
589         case RTL_TEXTENCODING_MS_1252:      pChar = "ANSI";         break;
590         case RTL_TEXTENCODING_APPLE_ROMAN:  pChar = "MAC";          break;
591         // IBMPC == IBMPC_850
592         case RTL_TEXTENCODING_IBM_437:      pChar = "IBMPC_437";    break;
593         case RTL_TEXTENCODING_IBM_850:      pChar = "IBMPC_850";    break;
594         case RTL_TEXTENCODING_IBM_860:      pChar = "IBMPC_860";    break;
595         case RTL_TEXTENCODING_IBM_861:      pChar = "IBMPC_861";    break;
596         case RTL_TEXTENCODING_IBM_863:      pChar = "IBMPC_863";    break;
597         case RTL_TEXTENCODING_IBM_865:      pChar = "IBMPC_865";    break;
598         case RTL_TEXTENCODING_DONTKNOW:     pChar = "SYSTEM";       break;
599         // new string of TextEncoding value
600         default:
601             return OUString::number( eVal );
602     }
603     return OUString::createFromAscii(pChar);
604 }
605 
HasStarCalcFunctionList()606 bool ScGlobal::HasStarCalcFunctionList()
607 {
608     return bool(xStarCalcFunctionList);
609 }
610 
GetStarCalcFunctionList()611 ScFunctionList* ScGlobal::GetStarCalcFunctionList()
612 {
613     assert(!bThreadedGroupCalcInProgress);
614     if ( !xStarCalcFunctionList )
615         xStarCalcFunctionList.reset(new ScFunctionList);
616 
617     return xStarCalcFunctionList.get();
618 }
619 
GetStarCalcFunctionMgr()620 ScFunctionMgr* ScGlobal::GetStarCalcFunctionMgr()
621 {
622     assert(!bThreadedGroupCalcInProgress);
623     if ( !xStarCalcFunctionMgr )
624         xStarCalcFunctionMgr.reset(new ScFunctionMgr);
625 
626     return xStarCalcFunctionMgr.get();
627 }
628 
ResetFunctionList()629 void ScGlobal::ResetFunctionList()
630 {
631     // FunctionMgr has pointers into FunctionList, must also be updated
632     xStarCalcFunctionMgr.reset();
633     xStarCalcFunctionList.reset();
634 }
635 
GetUnitConverter()636 ScUnitConverter* ScGlobal::GetUnitConverter()
637 {
638     return comphelper::doubleCheckedInit( pUnitConverter,
639         []() { return new ScUnitConverter; });
640 }
641 
UnicodeStrChr(const sal_Unicode * pStr,sal_Unicode c)642 const sal_Unicode* ScGlobal::UnicodeStrChr( const sal_Unicode* pStr,
643             sal_Unicode c )
644 {
645     if ( !pStr )
646         return nullptr;
647     while ( *pStr )
648     {
649         if ( *pStr == c )
650             return pStr;
651         pStr++;
652     }
653     return nullptr;
654 }
655 
addToken(const OUString & rTokenList,std::u16string_view rToken,sal_Unicode cSep,sal_Int32 nSepCount,bool bForceSep)656 OUString ScGlobal::addToken(const OUString& rTokenList, std::u16string_view rToken,
657     sal_Unicode cSep, sal_Int32 nSepCount, bool bForceSep)
658 {
659     OUStringBuffer aBuf(rTokenList);
660     if( bForceSep || (!rToken.empty() && !rTokenList.isEmpty()) )
661         comphelper::string::padToLength(aBuf, aBuf.getLength() + nSepCount, cSep);
662     aBuf.append(rToken);
663     return aBuf.makeStringAndClear();
664 }
665 
IsQuoted(const OUString & rString,sal_Unicode cQuote)666 bool ScGlobal::IsQuoted( const OUString& rString, sal_Unicode cQuote )
667 {
668     return (rString.getLength() >= 2) && (rString[0] == cQuote) && (rString[ rString.getLength() - 1 ] == cQuote);
669 }
670 
AddQuotes(OUString & rString,sal_Unicode cQuote,bool bEscapeEmbedded)671 void ScGlobal::AddQuotes( OUString& rString, sal_Unicode cQuote, bool bEscapeEmbedded )
672 {
673     if (bEscapeEmbedded)
674     {
675         sal_Unicode pQ[3];
676         pQ[0] = pQ[1] = cQuote;
677         pQ[2] = 0;
678         OUString aQuotes( pQ );
679         rString = rString.replaceAll( OUStringChar(cQuote), aQuotes);
680     }
681     rString = OUStringChar( cQuote ) + rString + OUStringChar( cQuote );
682 }
683 
EraseQuotes(OUString & rString,sal_Unicode cQuote,bool bUnescapeEmbedded)684 void ScGlobal::EraseQuotes( OUString& rString, sal_Unicode cQuote, bool bUnescapeEmbedded )
685 {
686     if ( IsQuoted( rString, cQuote ) )
687     {
688         rString = rString.copy( 1, rString.getLength() - 2 );
689         if (bUnescapeEmbedded)
690         {
691             sal_Unicode pQ[3];
692             pQ[0] = pQ[1] = cQuote;
693             pQ[2] = 0;
694             OUString aQuotes( pQ );
695             rString = rString.replaceAll( aQuotes, OUStringChar(cQuote));
696         }
697     }
698 }
699 
FindUnquoted(const OUString & rString,sal_Unicode cChar,sal_Int32 nStart)700 sal_Int32 ScGlobal::FindUnquoted( const OUString& rString, sal_Unicode cChar, sal_Int32 nStart )
701 {
702     assert(nStart >= 0);
703     const sal_Unicode cQuote = '\'';
704     const sal_Unicode* const pStart = rString.getStr();
705     const sal_Unicode* const pStop = pStart + rString.getLength();
706     const sal_Unicode* p = pStart + nStart;
707     bool bQuoted = false;
708     while (p < pStop)
709     {
710         if (*p == cChar && !bQuoted)
711             return sal::static_int_cast< sal_Int32 >( p - pStart );
712         else if (*p == cQuote)
713         {
714             if (!bQuoted)
715                 bQuoted = true;
716             else if (p < pStop-1 && *(p+1) == cQuote)
717                 ++p;
718             else
719                 bQuoted = false;
720         }
721         ++p;
722     }
723     return -1;
724 }
725 
FindUnquoted(const sal_Unicode * pString,sal_Unicode cChar)726 const sal_Unicode* ScGlobal::FindUnquoted( const sal_Unicode* pString, sal_Unicode cChar )
727 {
728     sal_Unicode cQuote = '\'';
729     const sal_Unicode* p = pString;
730     bool bQuoted = false;
731     while (*p)
732     {
733         if (*p == cChar && !bQuoted)
734             return p;
735         else if (*p == cQuote)
736         {
737             if (!bQuoted)
738                 bQuoted = true;
739             else if (*(p+1) == cQuote)
740                 ++p;
741             else
742                 bQuoted = false;
743         }
744         ++p;
745     }
746     return nullptr;
747 }
748 
EETextObjEqual(const EditTextObject * pObj1,const EditTextObject * pObj2)749 bool ScGlobal::EETextObjEqual( const EditTextObject* pObj1,
750                                const EditTextObject* pObj2 )
751 {
752     if ( pObj1 == pObj2 ) // Both empty or the same object
753         return true;
754 
755     if ( pObj1 && pObj2 )
756         return pObj1->Equals( *pObj2);
757 
758     return false;
759 }
760 
OpenURL(const OUString & rURL,const OUString & rTarget,bool bIgnoreSettings)761 void ScGlobal::OpenURL(const OUString& rURL, const OUString& rTarget, bool bIgnoreSettings)
762 {
763     // OpenURL is always called in the GridWindow by mouse clicks in some way or another.
764     // That's why pScActiveViewShell and nScClickMouseModifier are correct.
765 
766     if (!bIgnoreSettings && !ShouldOpenURL())
767         return;
768 
769     SfxViewFrame* pViewFrm = SfxViewFrame::Current();
770     if (!pViewFrm)
771         return;
772 
773     OUString aUrlName( rURL );
774     SfxViewFrame* pFrame = nullptr;
775     const SfxObjectShell* pObjShell = nullptr;
776     OUString aReferName;
777     if ( pScActiveViewShell )
778     {
779         pFrame = pScActiveViewShell->GetViewFrame();
780         pObjShell = pFrame->GetObjectShell();
781         const SfxMedium* pMed = pObjShell->GetMedium();
782         if (pMed)
783             aReferName = pMed->GetName();
784     }
785 
786     // Don't fiddle with fragments pointing into current document.
787     // Also don't mess around with a vnd.sun.star.script or service or other
788     // internal "URI".
789     if (!aUrlName.startsWith("#")
790             && !aUrlName.startsWithIgnoreAsciiCase("vnd.sun.star.script:")
791             && !aUrlName.startsWithIgnoreAsciiCase("macro:")
792             && !aUrlName.startsWithIgnoreAsciiCase("slot:")
793             && !aUrlName.startsWithIgnoreAsciiCase("service:")
794             && !aUrlName.startsWithIgnoreAsciiCase(".uno:"))
795     {
796         // Any relative reference would fail with "not an absolute URL"
797         // error, try to construct an absolute URI with the path relative
798         // to the current document's path or work path, as usual for all
799         // external references.
800         // This then also, as ScGlobal::GetAbsDocName() uses
801         // INetURLObject::smartRel2Abs(), supports "\\" UNC path names as
802         // smb:// Samba shares and DOS path separators converted to proper
803         // file:// URI.
804         const OUString aNewUrlName( ScGlobal::GetAbsDocName( aUrlName, pObjShell));
805         if (!aNewUrlName.isEmpty())
806             aUrlName = aNewUrlName;
807     }
808 
809     SfxStringItem aUrl( SID_FILE_NAME, aUrlName );
810     SfxStringItem aTarget( SID_TARGETNAME, rTarget );
811     if ( nScClickMouseModifier & KEY_SHIFT )     // control-click -> into new window
812         aTarget.SetValue("_blank");
813 
814     SfxFrameItem aFrm( SID_DOCFRAME, pFrame );
815     SfxStringItem aReferer( SID_REFERER, aReferName );
816 
817     SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false );
818     SfxBoolItem aBrowsing( SID_BROWSE, true );
819 
820     // No SID_SILENT anymore
821     pViewFrm->GetDispatcher()->ExecuteList(SID_OPENDOC,
822             SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
823             { &aUrl, &aTarget, &aFrm, &aReferer, &aNewView, &aBrowsing });
824 }
825 
ShouldOpenURL()826 bool ScGlobal::ShouldOpenURL()
827 {
828     SvtSecurityOptions aSecOpt;
829     bool bCtrlClickHappened = (nScClickMouseModifier & KEY_MOD1);
830     bool bCtrlClickSecOption = aSecOpt.IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink );
831     if( bCtrlClickHappened && ! bCtrlClickSecOption )
832     {
833         // return since ctrl+click happened when the
834         // ctrl+click security option was disabled, link should not open
835         return false;
836     }
837     else if( ! bCtrlClickHappened && bCtrlClickSecOption )
838     {
839         // ctrl+click did not happen; only click happened maybe with some
840         // other key combo. and security option is set, so return
841         return false;
842     }
843     return true;
844 }
845 
IsSystemRTL()846 bool ScGlobal::IsSystemRTL()
847 {
848     return MsLangId::isRightToLeft( Application::GetSettings().GetLanguageTag().getLanguageType() );
849 }
850 
GetDefaultScriptType()851 SvtScriptType ScGlobal::GetDefaultScriptType()
852 {
853     // Used when text contains only WEAK characters.
854     // Script type of office language is used then (same as GetEditDefaultLanguage,
855     // to get consistent behavior of text in simple cells and EditEngine,
856     // also same as GetAppLanguage() in Writer)
857     return SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
858 }
859 
GetEditDefaultLanguage()860 LanguageType ScGlobal::GetEditDefaultLanguage()
861 {
862     // Used for EditEngine::SetDefaultLanguage
863     return Application::GetSettings().GetLanguageTag().getLanguageType();
864 }
865 
GetScriptedWhichID(SvtScriptType nScriptType,sal_uInt16 nWhich)866 sal_uInt16 ScGlobal::GetScriptedWhichID( SvtScriptType nScriptType, sal_uInt16 nWhich )
867 {
868     switch ( nScriptType )
869     {
870         case SvtScriptType::LATIN:
871         case SvtScriptType::ASIAN:
872         case SvtScriptType::COMPLEX:
873         break;      // take exact matches
874         default:    // prefer one, first COMPLEX, then ASIAN
875             if ( nScriptType & SvtScriptType::COMPLEX )
876                 nScriptType = SvtScriptType::COMPLEX;
877             else if ( nScriptType & SvtScriptType::ASIAN )
878                 nScriptType = SvtScriptType::ASIAN;
879     }
880     switch ( nScriptType )
881     {
882         case SvtScriptType::COMPLEX:
883         {
884             switch ( nWhich )
885             {
886                 case ATTR_FONT:
887                 case ATTR_CJK_FONT:
888                     nWhich = ATTR_CTL_FONT;
889                 break;
890                 case ATTR_FONT_HEIGHT:
891                 case ATTR_CJK_FONT_HEIGHT:
892                     nWhich = ATTR_CTL_FONT_HEIGHT;
893                 break;
894                 case ATTR_FONT_WEIGHT:
895                 case ATTR_CJK_FONT_WEIGHT:
896                     nWhich = ATTR_CTL_FONT_WEIGHT;
897                 break;
898                 case ATTR_FONT_POSTURE:
899                 case ATTR_CJK_FONT_POSTURE:
900                     nWhich = ATTR_CTL_FONT_POSTURE;
901                 break;
902             }
903         }
904         break;
905         case SvtScriptType::ASIAN:
906         {
907             switch ( nWhich )
908             {
909                 case ATTR_FONT:
910                 case ATTR_CTL_FONT:
911                     nWhich = ATTR_CJK_FONT;
912                 break;
913                 case ATTR_FONT_HEIGHT:
914                 case ATTR_CTL_FONT_HEIGHT:
915                     nWhich = ATTR_CJK_FONT_HEIGHT;
916                 break;
917                 case ATTR_FONT_WEIGHT:
918                 case ATTR_CTL_FONT_WEIGHT:
919                     nWhich = ATTR_CJK_FONT_WEIGHT;
920                 break;
921                 case ATTR_FONT_POSTURE:
922                 case ATTR_CTL_FONT_POSTURE:
923                     nWhich = ATTR_CJK_FONT_POSTURE;
924                 break;
925             }
926         }
927         break;
928         default:
929         {
930             switch ( nWhich )
931             {
932                 case ATTR_CTL_FONT:
933                 case ATTR_CJK_FONT:
934                     nWhich = ATTR_FONT;
935                 break;
936                 case ATTR_CTL_FONT_HEIGHT:
937                 case ATTR_CJK_FONT_HEIGHT:
938                     nWhich = ATTR_FONT_HEIGHT;
939                 break;
940                 case ATTR_CTL_FONT_WEIGHT:
941                 case ATTR_CJK_FONT_WEIGHT:
942                     nWhich = ATTR_FONT_WEIGHT;
943                 break;
944                 case ATTR_CTL_FONT_POSTURE:
945                 case ATTR_CJK_FONT_POSTURE:
946                     nWhich = ATTR_FONT_POSTURE;
947                 break;
948             }
949         }
950     }
951     return nWhich;
952 }
953 
AddLanguage(SfxItemSet & rSet,const SvNumberFormatter & rFormatter)954 void ScGlobal::AddLanguage( SfxItemSet& rSet, const SvNumberFormatter& rFormatter )
955 {
956     OSL_ENSURE( rSet.GetItemState( ATTR_LANGUAGE_FORMAT, false ) == SfxItemState::DEFAULT,
957         "ScGlobal::AddLanguage - language already added");
958 
959     const SfxPoolItem* pHardItem;
960     if ( rSet.GetItemState( ATTR_VALUE_FORMAT, false, &pHardItem ) != SfxItemState::SET )
961         return;
962 
963     const SvNumberformat* pHardFormat = rFormatter.GetEntry(
964         static_cast<const SfxUInt32Item*>(pHardItem)->GetValue() );
965 
966     sal_uInt32 nParentFmt = 0; // Pool default
967     const SfxItemSet* pParent = rSet.GetParent();
968     if ( pParent )
969         nParentFmt = pParent->Get( ATTR_VALUE_FORMAT ).GetValue();
970     const SvNumberformat* pParFormat = rFormatter.GetEntry( nParentFmt );
971 
972     if ( pHardFormat && pParFormat &&
973             (pHardFormat->GetLanguage() != pParFormat->GetLanguage()) )
974         rSet.Put( SvxLanguageItem( pHardFormat->GetLanguage(), ATTR_LANGUAGE_FORMAT ) );
975 }
976 
GetpTransliteration()977 utl::TransliterationWrapper* ScGlobal::GetpTransliteration()
978 {
979     return comphelper::doubleCheckedInit( pTransliteration,
980         []()
981         {
982             const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
983             ::utl::TransliterationWrapper* p = new ::utl::TransliterationWrapper(
984                 ::comphelper::getProcessComponentContext(), TransliterationFlags::IGNORE_CASE );
985             p->loadModuleIfNeeded( eOfficeLanguage );
986             return p;
987         });
988 }
GetCaseTransliteration()989 ::utl::TransliterationWrapper* ScGlobal::GetCaseTransliteration()
990 {
991     return comphelper::doubleCheckedInit( pCaseTransliteration,
992         []()
993         {
994             const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
995             ::utl::TransliterationWrapper* p = new ::utl::TransliterationWrapper(
996                 ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
997             p->loadModuleIfNeeded( eOfficeLanguage );
998             return p;
999         });
1000 }
1001 
getLocaleDataPtr()1002 const LocaleDataWrapper* ScGlobal::getLocaleDataPtr()
1003 {
1004     OSL_ENSURE(
1005         xSysLocale,
1006         "ScGlobal::getLocaleDataPtr() called before ScGlobal::Init()");
1007 
1008     return &xSysLocale->GetLocaleData();
1009 }
1010 
getCharClassPtr()1011 const CharClass* ScGlobal::getCharClassPtr()
1012 {
1013     OSL_ENSURE(
1014         xSysLocale,
1015         "ScGlobal::getCharClassPtr() called before ScGlobal::Init()");
1016 
1017     return xSysLocale->GetCharClassPtr();
1018 }
1019 
GetCalendar()1020 CalendarWrapper*     ScGlobal::GetCalendar()
1021 {
1022     assert(!bThreadedGroupCalcInProgress);
1023     if ( !xCalendar )
1024     {
1025         xCalendar.reset( new CalendarWrapper( ::comphelper::getProcessComponentContext() ) );
1026         xCalendar->loadDefaultCalendar( *GetLocale() );
1027     }
1028     return xCalendar.get();
1029 }
GetCollator()1030 CollatorWrapper*        ScGlobal::GetCollator()
1031 {
1032     return comphelper::doubleCheckedInit( pCollator,
1033         []()
1034         {
1035             CollatorWrapper* p = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
1036             p->loadDefaultCollator( *GetLocale(), SC_COLLATOR_IGNORES );
1037             return p;
1038         });
1039 }
GetCaseCollator()1040 CollatorWrapper*        ScGlobal::GetCaseCollator()
1041 {
1042     return comphelper::doubleCheckedInit( pCaseCollator,
1043         []()
1044         {
1045             CollatorWrapper* p = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
1046             p->loadDefaultCollator( *GetLocale(), 0 );
1047             return p;
1048         });
1049 }
GetLocale()1050 css::lang::Locale*     ScGlobal::GetLocale()
1051 {
1052     return comphelper::doubleCheckedInit( pLocale,
1053         []() { return new css::lang::Locale( Application::GetSettings().GetLanguageTag().getLocale()); });
1054 }
1055 
GetStaticFieldEditEngine()1056 ScFieldEditEngine& ScGlobal::GetStaticFieldEditEngine()
1057 {
1058     assert(!bThreadedGroupCalcInProgress);
1059     if (!xFieldEditEngine)
1060     {
1061         // Creating a ScFieldEditEngine with pDocument=NULL leads to document
1062         // specific fields not being resolvable! See
1063         // ScFieldEditEngine::CalcFieldValue(). pEnginePool=NULL lets
1064         // EditEngine internally create and delete a default pool.
1065         xFieldEditEngine.reset(new ScFieldEditEngine( nullptr, nullptr));
1066     }
1067     return *xFieldEditEngine;
1068 }
1069 
ReplaceOrAppend(const OUString & rString,std::u16string_view rPlaceholder,const OUString & rReplacement)1070 OUString ScGlobal::ReplaceOrAppend( const OUString& rString,
1071         std::u16string_view rPlaceholder, const OUString& rReplacement )
1072 {
1073     if (rString.isEmpty())
1074         return rReplacement;
1075     sal_Int32 nFound = rString.indexOf( rPlaceholder);
1076     if (nFound < 0)
1077     {
1078         if (rString[rString.getLength()-1] == ' ')
1079             return rString + rReplacement;
1080         return rString + " " + rReplacement;
1081     }
1082     return rString.replaceFirst( rPlaceholder, rReplacement, &nFound);
1083 }
1084 
1085 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1086