xref: /core/sc/source/ui/app/inputhdl.cxx (revision fee44aa4)
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 <iterator>
21 #include <memory>
22 #include <string_view>
23 
24 #include <inputhdl.hxx>
25 #include <scitems.hxx>
26 #include <editeng/eeitem.hxx>
27 
28 #include <sfx2/app.hxx>
29 #include <editeng/acorrcfg.hxx>
30 #include <formula/errorcodes.hxx>
31 #include <editeng/adjustitem.hxx>
32 #include <editeng/brushitem.hxx>
33 #include <svtools/colorcfg.hxx>
34 #include <editeng/colritem.hxx>
35 #include <editeng/editobj.hxx>
36 #include <editeng/editstat.hxx>
37 #include <editeng/editview.hxx>
38 #include <editeng/langitem.hxx>
39 #include <editeng/svxacorr.hxx>
40 #include <editeng/unolingu.hxx>
41 #include <editeng/wghtitem.hxx>
42 #include <editeng/justifyitem.hxx>
43 #include <editeng/misspellrange.hxx>
44 #include <sfx2/bindings.hxx>
45 #include <sfx2/viewfrm.hxx>
46 #include <sfx2/docfile.hxx>
47 #include <sfx2/printer.hxx>
48 #include <svl/numformat.hxx>
49 #include <svl/zforlist.hxx>
50 #include <unotools/localedatawrapper.hxx>
51 #include <unotools/charclass.hxx>
52 #include <utility>
53 #include <vcl/help.hxx>
54 #include <vcl/jsdialog/executor.hxx>
55 #include <vcl/commandevent.hxx>
56 #include <vcl/cursor.hxx>
57 #include <vcl/settings.hxx>
58 #include <vcl/svapp.hxx>
59 #include <tools/urlobj.hxx>
60 #include <formula/formulahelper.hxx>
61 #include <formula/funcvarargs.h>
62 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
63 #include <comphelper/lok.hxx>
64 #include <osl/diagnose.h>
65 
66 #include <attrib.hxx>
67 #include <inputwin.hxx>
68 #include <tabvwsh.hxx>
69 #include <docsh.hxx>
70 #include <scmod.hxx>
71 #include <formulaopt.hxx>
72 #include <uiitems.hxx>
73 #include <global.hxx>
74 #include <sc.hrc>
75 #include <globstr.hrc>
76 #include <scresid.hxx>
77 #include <patattr.hxx>
78 #include <viewdata.hxx>
79 #include <document.hxx>
80 #include <docpool.hxx>
81 #include <editutil.hxx>
82 #include <appoptio.hxx>
83 #include <docoptio.hxx>
84 #include <validat.hxx>
85 #include <rfindlst.hxx>
86 #include <inputopt.hxx>
87 #include <simpleformulacalc.hxx>
88 #include <compiler.hxx>
89 #include <editable.hxx>
90 #include <funcdesc.hxx>
91 #include <markdata.hxx>
92 #include <tokenarray.hxx>
93 #include <gridwin.hxx>
94 #include <output.hxx>
95 #include <fillinfo.hxx>
96 
97 // Maximum Ranges in RangeFinder
98 #define RANGEFIND_MAX   128
99 
100 using namespace formula;
101 
102 namespace {
103 
104 ScTypedCaseStrSet::const_iterator findText(
105     const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
106     const OUString& rStart, OUString& rResult, bool bBack)
107 {
108     auto lIsMatch = [&rStart](const ScTypedStrData& rData) {
109         return (rData.GetStringType() != ScTypedStrData::Value) && ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()); };
110 
111     if (bBack) // Backwards
112     {
113         ScTypedCaseStrSet::const_reverse_iterator it = rDataSet.rbegin(), itEnd = rDataSet.rend();
114         if (itPos != rDataSet.end())
115         {
116             size_t nPos = std::distance(rDataSet.begin(), itPos);
117             size_t nRPos = rDataSet.size() - 1 - nPos;
118             std::advance(it, nRPos);
119             ++it;
120         }
121 
122         it = std::find_if(it, itEnd, lIsMatch);
123         if (it != itEnd)
124         {
125             rResult = it->GetString();
126             return (++it).base(); // convert the reverse iterator back to iterator.
127         }
128     }
129     else // Forwards
130     {
131         ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end();
132         if (itPos != itEnd)
133         {
134             it = std::next(itPos);
135         }
136 
137         it = std::find_if(it, itEnd, lIsMatch);
138         if (it != itEnd)
139         {
140             rResult = it->GetString();
141             return it;
142         }
143     }
144 
145     return rDataSet.end(); // no matching text found
146 }
147 
148 OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rString)
149 {
150     auto it = std::find_if(rDataSet.begin(), rDataSet.end(),
151         [&rString](const ScTypedStrData& rData) {
152             return (rData.GetStringType() != ScTypedStrData::Value)
153                 && ScGlobal::GetTransliteration().isEqual(rData.GetString(), rString);
154         });
155     if (it != rDataSet.end())
156         return it->GetString();
157     return rString;
158 }
159 
160 // This assumes that rResults is a sorted ring w.r.t ScTypedStrData::LessCaseInsensitive() or
161 // in the reverse direction, whose origin is specified by nRingOrigin.
162 sal_Int32 getLongestCommonPrefixLength(const std::vector<OUString>& rResults, std::u16string_view aUserEntry, sal_Int32 nRingOrigin)
163 {
164     sal_Int32 nResults = rResults.size();
165     if (!nResults)
166         return 0;
167 
168     if (nResults == 1)
169         return rResults[0].getLength();
170 
171     sal_Int32 nMinLen = aUserEntry.size();
172     sal_Int32 nLastIdx = nRingOrigin ? nRingOrigin - 1 : nResults - 1;
173     const OUString& rFirst = rResults[nRingOrigin];
174     const OUString& rLast = rResults[nLastIdx];
175     const sal_Int32 nMaxLen = std::min(rFirst.getLength(), rLast.getLength());
176 
177     for (sal_Int32 nLen = nMaxLen; nLen > nMinLen; --nLen)
178     {
179         if (ScGlobal::GetTransliteration().isMatch(rFirst.copy(0, nLen), rLast))
180             return nLen;
181     }
182 
183     return nMinLen;
184 }
185 
186 ScTypedCaseStrSet::const_iterator findTextAll(
187     const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
188     const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack, sal_Int32* pLongestPrefixLen = nullptr)
189 {
190     rResultVec.clear(); // clear contents
191 
192     if (!rDataSet.size())
193         return rDataSet.end();
194 
195     sal_Int32 nRingOrigin = 0;
196     size_t nCount = 0;
197     ScTypedCaseStrSet::const_iterator retit;
198     if ( bBack ) // Backwards
199     {
200         ScTypedCaseStrSet::const_reverse_iterator it, itEnd;
201         if ( itPos == rDataSet.end() )
202         {
203             it = rDataSet.rend();
204             --it;
205             itEnd = it;
206         }
207         else
208         {
209             it = rDataSet.rbegin();
210             size_t nPos = std::distance(rDataSet.begin(), itPos);
211             size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1
212             std::advance(it, nRPos);
213             if ( it == rDataSet.rend() )
214                 it = rDataSet.rbegin();
215             itEnd = it;
216         }
217         bool bFirstTime = true;
218 
219         while ( it != itEnd || bFirstTime )
220         {
221             ++it;
222             if ( it == rDataSet.rend() ) // go to the first if reach the end
223             {
224                 it = rDataSet.rbegin();
225                 nRingOrigin = nCount;
226             }
227 
228             if ( bFirstTime )
229                 bFirstTime = false;
230             const ScTypedStrData& rData = *it;
231             if ( rData.GetStringType() == ScTypedStrData::Value )
232                 // skip values
233                 continue;
234 
235             if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) )
236                 // not a match
237                 continue;
238 
239             rResultVec.push_back(rData.GetString()); // set the match data
240             if ( nCount == 0 ) // convert the reverse iterator back to iterator.
241             {
242                 // actually we want to do "retit = it;".
243                 retit = rDataSet.begin();
244                 size_t nRPos = std::distance(rDataSet.rbegin(), it);
245                 size_t nPos = rDataSet.size() - 1 - nRPos;
246                 std::advance(retit, nPos);
247             }
248             ++nCount;
249         }
250     }
251     else // Forwards
252     {
253         ScTypedCaseStrSet::const_iterator it, itEnd;
254         it = itPos;
255         if ( it == rDataSet.end() )
256             it = --rDataSet.end();
257         itEnd = it;
258         bool bFirstTime = true;
259 
260         while ( it != itEnd || bFirstTime )
261         {
262             ++it;
263             if ( it == rDataSet.end() ) // go to the first if reach the end
264             {
265                 it = rDataSet.begin();
266                 nRingOrigin = nCount;
267             }
268 
269             if ( bFirstTime )
270                 bFirstTime = false;
271             const ScTypedStrData& rData = *it;
272             if ( rData.GetStringType() == ScTypedStrData::Value )
273                 // skip values
274                 continue;
275 
276             if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) )
277                 // not a match
278                 continue;
279 
280             rResultVec.push_back(rData.GetString()); // set the match data
281             if ( nCount == 0 )
282                 retit = it; // remember first match iterator
283             ++nCount;
284         }
285     }
286 
287     if (pLongestPrefixLen)
288     {
289         if (nRingOrigin >= static_cast<sal_Int32>(nCount))
290         {
291             // All matches were picked when rDataSet was read in one direction.
292             nRingOrigin = 0;
293         }
294         // rResultsVec is a sorted ring with nRingOrigin "origin".
295         // The direction of sorting is not important for getLongestCommonPrefixLength.
296         *pLongestPrefixLen = getLongestCommonPrefixLength(rResultVec, rStart, nRingOrigin);
297     }
298 
299     if ( nCount > 0 ) // at least one function has matched
300         return retit;
301     return rDataSet.end(); // no matching text found
302 }
303 
304 }
305 
306 void ScInputHandler::SendReferenceMarks( const SfxViewShell* pViewShell,
307                             const std::vector<ReferenceMark>& rReferenceMarks )
308 {
309     if ( !pViewShell )
310         return;
311 
312     bool bSend = false;
313 
314     std::stringstream ss;
315 
316     ss << "{ \"marks\": [ ";
317 
318     for ( size_t i = 0; i < rReferenceMarks.size(); i++ )
319     {
320         if ( rReferenceMarks[i].Is() )
321         {
322             if ( bSend )
323                 ss << ", ";
324 
325             ss << "{ \"rectangle\": \""
326                << rReferenceMarks[i].nX << ", "
327                << rReferenceMarks[i].nY << ", "
328                << rReferenceMarks[i].nWidth << ", "
329                << rReferenceMarks[i].nHeight << "\", "
330                   "\"color\": \"" << rReferenceMarks[i].aColor.AsRGBHexString() << "\", "
331                   "\"part\": \"" << rReferenceMarks[i].nTab << "\" } ";
332 
333             bSend = true;
334         }
335     }
336 
337     ss <<  " ] }";
338 
339     OString aPayload( ss.str() );
340     pViewShell->libreOfficeKitViewCallback(
341                 LOK_CALLBACK_REFERENCE_MARKS, aPayload );
342 }
343 
344 static inline void incPos( const sal_Unicode c, sal_Int32& rPos, ESelection& rSel )
345 {
346     ++rPos;
347     if (c == '\n')
348     {
349         ++rSel.nEndPara;
350         rSel.nEndPos = 0;
351     }
352     else
353     {
354         ++rSel.nEndPos;
355     }
356 }
357 
358 void ScInputHandler::InitRangeFinder( const OUString& rFormula )
359 {
360     DeleteRangeFinder();
361     if ( !pActiveViewSh || !SC_MOD()->GetInputOptions().GetRangeFinder() )
362         return;
363     ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
364     ScDocument& rDoc = pDocSh->GetDocument();
365     const sal_Unicode cSheetSep = rDoc.GetSheetSeparator();
366 
367     OUString aDelimiters = ScEditUtil::ModifyDelimiters(" !~%\"\t\n");
368         // delimiters (in addition to ScEditUtil): only characters that are
369         // allowed in formulas next to references and the quotation mark (so
370         // string constants can be skipped)
371 
372     sal_Int32 nColon = aDelimiters.indexOf( ':' );
373     if ( nColon != -1 )
374         aDelimiters = aDelimiters.replaceAt( nColon, 1, u""); // Delimiter without colon
375     sal_Int32 nDot = aDelimiters.indexOf(cSheetSep);
376     if ( nDot != -1 )
377         aDelimiters = aDelimiters.replaceAt( nDot, 1 , u""); // Delimiter without dot
378 
379     const sal_Unicode* pChar = rFormula.getStr();
380     sal_Int32 nLen = rFormula.getLength();
381     sal_Int32 nPos = 0;
382     sal_Int32 nStart = 0;
383     ESelection aSel;
384     sal_uInt16 nCount = 0;
385     ScRange aRange;
386     while ( nPos < nLen && nCount < RANGEFIND_MAX )
387     {
388         // Skip separator
389         while ( nPos<nLen && ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) )
390         {
391             if ( pChar[nPos] == '"' )                       // String
392             {
393                 incPos( pChar[nPos], nPos, aSel);
394                 while (nPos<nLen && pChar[nPos] != '"')     // Skip until end
395                     incPos( pChar[nPos], nPos, aSel);
396             }
397             incPos( pChar[nPos], nPos, aSel);  // Separator or closing quote
398         }
399 
400         // Text between separators. We only consider within one line/paragraph.
401         aSel.nStartPara = aSel.nEndPara;
402         aSel.nStartPos = aSel.nEndPos;
403         nStart = nPos;
404 handle_r1c1:
405         {
406             bool bSingleQuoted = false;
407             while (nPos < nLen)
408             {
409                 // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1"
410                 // Literal single quotes in sheet names are masked by another single quote
411                 if (pChar[nPos] == '\'')
412                 {
413                     bSingleQuoted = !bSingleQuoted;
414                 }
415                 else if (!bSingleQuoted) // Get everything in single quotes, including separators
416                 {
417                     if (ScGlobal::UnicodeStrChr(aDelimiters.getStr(), pChar[nPos]))
418                         break;
419                 }
420                 incPos( pChar[nPos], nPos, aSel);
421             }
422         }
423 
424         // for R1C1 '-' in R[-]... or C[-]... are not delimiters
425         // Nothing heroic here to ensure that there are '[]' around a negative
426         // integer.  we need to clean up this code.
427         if( nPos < nLen && nPos > 0 &&
428             '-' == pChar[nPos] && '[' == pChar[nPos-1] &&
429             formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() )
430         {
431             incPos( pChar[nPos], nPos, aSel);
432             goto handle_r1c1;
433         }
434 
435         if ( nPos > nStart )
436         {
437             OUString aTest = rFormula.copy( nStart, nPos-nStart );
438             const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
439             ScRefFlags nFlags = aRange.ParseAny( aTest, rDoc, aAddrDetails );
440             if ( nFlags & ScRefFlags::VALID )
441             {
442                 //  Set tables if not specified
443                 if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
444                     aRange.aStart.SetTab( pActiveViewSh->GetViewData().GetTabNo() );
445                 if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
446                     aRange.aEnd.SetTab( aRange.aStart.Tab() );
447 
448                 if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) ==
449                                  ScRefFlags::ZERO )
450                 {
451                     // #i73766# if a single ref was parsed, set the same "abs" flags for ref2,
452                     // so Format doesn't output a double ref because of different flags.
453                     ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS);
454                     applyStartToEndFlags(nFlags, nAbsFlags);
455                 }
456 
457                 if (!nCount)
458                 {
459                     mpEditEngine->SetUpdateLayout( false );
460                     pRangeFindList.reset(new ScRangeFindList( pDocSh->GetTitle() ));
461                 }
462 
463                 Color nColor = pRangeFindList->Insert( ScRangeFindData( aRange, nFlags, aSel));
464 
465                 SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
466                 aSet.Put( SvxColorItem( nColor, EE_CHAR_COLOR ) );
467                 mpEditEngine->QuickSetAttribs( aSet, aSel );
468                 ++nCount;
469             }
470         }
471 
472         // Do not skip last separator; could be a quote (?)
473     }
474 
475     UpdateLokReferenceMarks();
476 
477     if (nCount)
478     {
479         mpEditEngine->SetUpdateLayout( true );
480 
481         pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) );
482     }
483 }
484 
485 ReferenceMark ScInputHandler::GetReferenceMark( const ScViewData& rViewData, ScDocShell* pDocSh,
486                                     tools::Long nX1, tools::Long nX2, tools::Long nY1, tools::Long nY2,
487                                     tools::Long nTab, const Color& rColor )
488 {
489     ScSplitPos eWhich = rViewData.GetActivePart();
490 
491     // This method is LOK specific.
492     if (comphelper::LibreOfficeKit::isCompatFlagSet(
493             comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
494     {
495         SCCOL nCol1 = nX1, nCol2 = nX2;
496         SCROW nRow1 = nY1, nRow2 = nY2;
497         ScDocument& rDoc = pDocSh->GetDocument();
498 
499         PutInOrder(nCol1, nCol2);
500         PutInOrder(nRow1, nRow2);
501 
502         if (nCol1 == nCol2 && nRow1 == nRow2)
503             rDoc.ExtendMerge(nCol1, nRow1, nCol2, nRow2, nTab);
504         else if (rDoc.HasAttrib(nCol2, nRow2, nTab, HasAttrFlags::Merged))
505             rDoc.ExtendMerge(nCol2, nRow2, nCol2, nRow2, nTab);
506 
507         Point aTopLeft = rViewData.GetPrintTwipsPos(nCol1, nRow1);
508         Point aBottomRight = rViewData.GetPrintTwipsPos(nCol2 + 1, nRow2 + 1);
509         tools::Long nSizeX = aBottomRight.X() - aTopLeft.X() - 1;
510         tools::Long nSizeY = aBottomRight.Y() - aTopLeft.Y() - 1;
511 
512         return ReferenceMark(aTopLeft.X(), aTopLeft.Y(), nSizeX, nSizeY, nTab, rColor);
513     }
514 
515     Point aScrPos = rViewData.GetScrPos( nX1, nY1, eWhich );
516     tools::Long nScrX = aScrPos.X();
517     tools::Long nScrY = aScrPos.Y();
518 
519     double nPPTX = rViewData.GetPPTX();
520     double nPPTY = rViewData.GetPPTY();
521 
522     Fraction aZoomX = rViewData.GetZoomX();
523     Fraction aZoomY = rViewData.GetZoomY();
524 
525     ScTableInfo aTabInfo;
526     pDocSh->GetDocument().FillInfo( aTabInfo, nX1, nY1, nX2, nY2,
527                                     nTab, nPPTX, nPPTY, false, false );
528 
529     ScOutputData aOutputData( nullptr, OUTTYPE_WINDOW, aTabInfo,
530                               &( pDocSh->GetDocument() ), nTab,
531                               nScrX, nScrY,
532                               nX1, nY1, nX2, nY2,
533                               nPPTX, nPPTY,
534                               &aZoomX, &aZoomY );
535 
536     return aOutputData.FillReferenceMark( nX1, nY1, nX2, nY2,
537                                           rColor );
538 }
539 
540 void ScInputHandler::UpdateLokReferenceMarks()
541 {
542     if ( !comphelper::LibreOfficeKit::isActive())
543         return;
544 
545     ScTabViewShell* pShell = pActiveViewSh ? pActiveViewSh
546                                 : dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
547 
548     if (!pShell)
549         return;
550 
551     ScViewData& rViewData = pShell->GetViewData();
552     ScDocShell* pDocSh = rViewData.GetDocShell();
553     ScRangeFindList* pRangeFinder = GetRangeFindList();
554 
555     if ( !pRangeFinder && !rViewData.IsRefMode() )
556         return;
557 
558     sal_uInt16 nAdditionalMarks = 0;
559     std::vector<ReferenceMark> aReferenceMarks( 1 );
560 
561     if ( rViewData.IsRefMode() )
562     {
563         nAdditionalMarks = 1;
564 
565         const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
566         Color aRefColor( rColorCfg.GetColorValue( svtools::CALCREFERENCE ).nColor );
567         tools::Long nX1 = rViewData.GetRefStartX();
568         tools::Long nX2 = rViewData.GetRefEndX();
569         tools::Long nY1 = rViewData.GetRefStartY();
570         tools::Long nY2 = rViewData.GetRefEndY();
571         tools::Long nTab = rViewData.GetRefStartZ();
572 
573         if (rViewData.GetRefEndZ() == rViewData.GetTabNo())
574             nTab = rViewData.GetRefEndZ();
575 
576         PutInOrder(nX1, nX2);
577         PutInOrder(nY1, nY2);
578 
579         aReferenceMarks[0] = ScInputHandler::GetReferenceMark( rViewData, pDocSh,
580                                                    nX1, nX2, nY1, nY2,
581                                                    nTab, aRefColor );
582     }
583 
584     sal_uInt16 nCount = pRangeFinder ?
585         ( static_cast<sal_uInt16>( pRangeFinder->Count() ) + nAdditionalMarks ) : nAdditionalMarks;
586     aReferenceMarks.resize( nCount );
587 
588     if ( nCount && pRangeFinder && !pRangeFinder->IsHidden() &&
589          pRangeFinder->GetDocName() == pDocSh->GetTitle() )
590     {
591         for (sal_uInt16 i = 0; i < nCount - nAdditionalMarks; i++)
592         {
593             ScRangeFindData& rData = pRangeFinder->GetObject( i );
594             ScRange aRef = rData.aRef;
595             aRef.PutInOrder();
596 
597             tools::Long nX1 = aRef.aStart.Col();
598             tools::Long nX2 = aRef.aEnd.Col();
599             tools::Long nY1 = aRef.aStart.Row();
600             tools::Long nY2 = aRef.aEnd.Row();
601             tools::Long nTab = aRef.aStart.Tab();
602 
603             aReferenceMarks[i + nAdditionalMarks] = ScInputHandler::GetReferenceMark( rViewData, pDocSh,
604                                                                           nX1, nX2, nY1, nY2,
605                                                                           nTab, rData.nColor );
606 
607             ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
608         }
609     }
610     else if ( nCount )
611     {
612         ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
613     }
614     else
615     {
616         // Clear
617         aReferenceMarks.clear();
618         ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
619     }
620 }
621 
622 void ScInputHandler::SetDocumentDisposing( bool b )
623 {
624     mbDocumentDisposing = b;
625 }
626 
627 static void lcl_Replace( EditView* pView, const OUString& rNewStr, const ESelection& rOldSel )
628 {
629     if ( !pView )
630         return;
631 
632     ESelection aOldSel = pView->GetSelection();
633     if (aOldSel.HasRange())
634         pView->SetSelection( ESelection( aOldSel.nEndPara, aOldSel.nEndPos,
635                                          aOldSel.nEndPara, aOldSel.nEndPos ) );
636 
637     EditEngine* pEngine = pView->GetEditEngine();
638     pEngine->QuickInsertText( rNewStr, rOldSel );
639 
640     // Dummy InsertText for Update and Paint
641     // To do that we need to cancel the selection from above (before QuickInsertText)
642     pView->InsertText( OUString() );
643 
644     const sal_Int32 nPara = pEngine->GetParagraphCount() - 1;
645     const sal_Int32 nLen = pEngine->GetTextLen(nPara);
646     ESelection aSel( nPara, nLen, nPara, nLen );
647     pView->SetSelection( aSel ); // Set cursor to the end
648 }
649 
650 void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew )
651 {
652     ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh;
653     if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() )
654     {
655         ScRangeFindData& rData = pRangeFindList->GetObject( nIndex );
656         Color nNewColor = pRangeFindList->FindColor( rNew, nIndex );
657 
658         ScRange aJustified = rNew;
659         aJustified.PutInOrder(); // Always display Ref in the Formula the right way
660         ScDocument& rDoc = pDocView->GetViewData().GetDocument();
661         const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
662         OUString aNewStr(aJustified.Format(rDoc, rData.nFlags, aAddrDetails));
663         SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
664 
665         DataChanging();
666 
667         lcl_Replace( pTopView, aNewStr, rData.maSel );
668         lcl_Replace( pTableView, aNewStr, rData.maSel );
669 
670         // We are within one paragraph.
671         const sal_Int32 nDiff = aNewStr.getLength() - (rData.maSel.nEndPos - rData.maSel.nStartPos);
672         rData.maSel.nEndPos += nDiff;
673 
674         aSet.Put( SvxColorItem( nNewColor, EE_CHAR_COLOR ) );
675         mpEditEngine->QuickSetAttribs( aSet, rData.maSel );
676 
677         bInRangeUpdate = true;
678         DataChanged();
679         bInRangeUpdate = false;
680 
681         rData.aRef = rNew;
682         rData.nColor = nNewColor;
683 
684         if (nDiff)
685         {
686             const size_t nCount = pRangeFindList->Count();
687             for (size_t i = nIndex + 1; i < nCount; ++i)
688             {
689                 ScRangeFindData& rNext = pRangeFindList->GetObject( i );
690                 if (rNext.maSel.nStartPara != rData.maSel.nStartPara)
691                     break;
692 
693                 rNext.maSel.nStartPos += nDiff;
694                 rNext.maSel.nEndPos   += nDiff;
695             }
696         }
697 
698         EditView* pActiveView = pTopView ? pTopView : pTableView;
699         pActiveView->ShowCursor( false );
700     }
701     else
702     {
703         OSL_FAIL("UpdateRange: we're missing something");
704     }
705 }
706 
707 void ScInputHandler::DeleteRangeFinder()
708 {
709     ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh;
710     if ( pRangeFindList && pPaintView )
711     {
712         ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
713         pRangeFindList->SetHidden(true);
714         pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) );  // Steal
715         pRangeFindList.reset();
716     }
717 }
718 
719 static OUString GetEditText(const EditEngine* pEng)
720 {
721     return ScEditUtil::GetMultilineString(*pEng);
722 }
723 
724 static void lcl_RemoveTabs(OUString& rStr)
725 {
726     rStr = rStr.replace('\t', ' ');
727 }
728 
729 static void lcl_RemoveLineEnd(OUString& rStr)
730 {
731     rStr = convertLineEnd(rStr, LINEEND_LF);
732     rStr = rStr.replace('\n', ' ');
733 }
734 
735 static sal_Int32 lcl_MatchParenthesis( const OUString& rStr, sal_Int32 nPos )
736 {
737     int nDir;
738     sal_Unicode c1, c2 = 0;
739     c1 = rStr[nPos];
740     switch ( c1 )
741     {
742     case '(' :
743         c2 = ')';
744         nDir = 1;
745         break;
746     case ')' :
747         c2 = '(';
748         nDir = -1;
749         break;
750     case '<' :
751         c2 = '>';
752         nDir = 1;
753         break;
754     case '>' :
755         c2 = '<';
756         nDir = -1;
757         break;
758     case '{' :
759         c2 = '}';
760         nDir = 1;
761         break;
762     case '}' :
763         c2 = '{';
764         nDir = -1;
765         break;
766     case '[' :
767         c2 = ']';
768         nDir = 1;
769         break;
770     case ']' :
771         c2 = '[';
772         nDir = -1;
773         break;
774     default:
775         nDir = 0;
776     }
777     if ( !nDir )
778         return -1;
779     sal_Int32 nLen = rStr.getLength();
780     const sal_Unicode* p0 = rStr.getStr();
781     const sal_Unicode* p;
782     const sal_Unicode* p1;
783     sal_uInt16 nQuotes = 0;
784     if ( nPos < nLen / 2 )
785     {
786         p = p0;
787         p1 = p0 + nPos;
788     }
789     else
790     {
791         p = p0 + nPos;
792         p1 = p0 + nLen;
793     }
794     while ( p < p1 )
795     {
796         if ( *p++ == '\"' )
797             nQuotes++;
798     }
799     // Odd number of quotes that we find ourselves in a string
800     bool bLookInString = ((nQuotes % 2) != 0);
801     bool bInString = bLookInString;
802     p = p0 + nPos;
803     p1 = (nDir < 0 ? p0 : p0 + nLen) ;
804     sal_uInt16 nLevel = 1;
805     while ( p != p1 && nLevel )
806     {
807         p += nDir;
808         if ( *p == '\"' )
809         {
810             bInString = !bInString;
811             if ( bLookInString && !bInString )
812                 p = p1; // That's it then
813         }
814         else if ( bInString == bLookInString )
815         {
816             if ( *p == c1 )
817                 nLevel++;
818             else if ( *p == c2 )
819                 nLevel--;
820         }
821     }
822     if ( nLevel )
823         return -1;
824     return static_cast<sal_Int32>(p - p0);
825 }
826 
827 ScInputHandler::ScInputHandler()
828     :   pInputWin( nullptr ),
829         pTableView( nullptr ),
830         pTopView( nullptr ),
831         pTipVisibleParent( nullptr ),
832         nTipVisible( nullptr ),
833         pTipVisibleSecParent( nullptr ),
834         nTipVisibleSec( nullptr ),
835         nFormSelStart( 0 ),
836         nFormSelEnd( 0 ),
837         nCellPercentFormatDecSep( 0 ),
838         nAutoPar( 0 ),
839         eMode( SC_INPUT_NONE ),
840         bUseTab( false ),
841         bTextValid( true ),
842         bModified( false ),
843         bSelIsRef( false ),
844         bFormulaMode( false ),
845         bInRangeUpdate( false ),
846         bParenthesisShown( false ),
847         bCreatingFuncView( false ),
848         bInEnterHandler( false ),
849         bCommandErrorShown( false ),
850         bInOwnChange( false ),
851         bProtected( false ),
852         bLastIsSymbol( false ),
853         mbDocumentDisposing(false),
854         mbPartialPrefix(false),
855         mbEditingExistingContent(false),
856         nValidation( 0 ),
857         eAttrAdjust( SvxCellHorJustify::Standard ),
858         aScaleX( 1,1 ),
859         aScaleY( 1,1 ),
860         pRefViewSh( nullptr ),
861         pLastPattern( nullptr )
862 {
863     //  The InputHandler is constructed with the view, so SfxViewShell::Current
864     //  doesn't have the right view yet. pActiveViewSh is updated in NotifyChange.
865     pActiveViewSh = nullptr;
866 
867     //  Bindings (only still used for Invalidate) are retrieved if needed on demand
868 
869     pDelayTimer.reset( new Timer( "ScInputHandlerDelay timer" ) );
870     pDelayTimer->SetTimeout( 500 ); // 500 ms delay
871     pDelayTimer->SetInvokeHandler( LINK( this, ScInputHandler, DelayTimer ) );
872 }
873 
874 ScInputHandler::~ScInputHandler()
875 {
876     //  If this is the application InputHandler, the dtor is called after SfxApplication::Main,
877     //  thus we can't rely on any Sfx functions
878     if (!mbDocumentDisposing) // inplace
879         EnterHandler(); // Finish input
880 
881     if (SC_MOD()->GetRefInputHdl() == this)
882         SC_MOD()->SetRefInputHdl(nullptr);
883 
884     if ( pInputWin && pInputWin->GetInputHandler() == this )
885         pInputWin->SetInputHandler( nullptr );
886 }
887 
888 void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY )
889 {
890     if ( rX != aScaleX || rY != aScaleY )
891     {
892         aScaleX = rX;
893         aScaleY = rY;
894         if (mpEditEngine)
895         {
896             MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
897             mpEditEngine->SetRefMapMode( aMode );
898         }
899     }
900 }
901 
902 void ScInputHandler::UpdateRefDevice()
903 {
904     if (!mpEditEngine)
905         return;
906 
907     bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
908     bool bInPlace = pActiveViewSh && pActiveViewSh->GetViewFrame().GetFrame().IsInPlace();
909     EEControlBits nCtrl = mpEditEngine->GetControlWord();
910     if ( bTextWysiwyg || bInPlace )
911         nCtrl |= EEControlBits::FORMAT100;    // EditEngine default: always format for 100%
912     else
913         nCtrl &= ~EEControlBits::FORMAT100;   // when formatting for screen, use the actual MapMode
914     mpEditEngine->SetControlWord( nCtrl );
915     if ( bTextWysiwyg && pActiveViewSh )
916         mpEditEngine->SetRefDevice( pActiveViewSh->GetViewData().GetDocument().GetPrinter() );
917     else
918         mpEditEngine->SetRefDevice( nullptr );
919 
920     MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
921     mpEditEngine->SetRefMapMode( aMode );
922 
923     //  SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev,
924     //  so the DigitLanguage can be safely modified (might use an own VDev instead of NULL).
925     if ( !( bTextWysiwyg && pActiveViewSh ) )
926     {
927         mpEditEngine->GetRefDevice()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
928     }
929 }
930 
931 void ScInputHandler::ImplCreateEditEngine()
932 {
933     if ( mpEditEngine )
934         return;
935 
936     // we cannot create a properly initialised EditEngine until we have a document
937     assert( pActiveViewSh );
938     ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
939     mpEditEngine = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool());
940     mpEditEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine->GetWordDelimiters() ) );
941     UpdateRefDevice();      // also sets MapMode
942     mpEditEngine->SetPaperSize( Size( 1000000, 1000000 ) );
943     pEditDefaults.reset( new SfxItemSet( mpEditEngine->GetEmptyItemSet() ) );
944 
945     mpEditEngine->SetControlWord( mpEditEngine->GetControlWord() | EEControlBits::AUTOCORRECT );
946     mpEditEngine->SetReplaceLeadingSingleQuotationMark( false );
947     mpEditEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) );
948 }
949 
950 void ScInputHandler::UpdateAutoCorrFlag()
951 {
952     EEControlBits nCntrl = mpEditEngine->GetControlWord();
953     EEControlBits nOld = nCntrl;
954 
955     // Don't use pLastPattern here (may be invalid because of AutoStyle)
956     bool bDisable = bLastIsSymbol || bFormulaMode;
957     if ( bDisable )
958         nCntrl &= ~EEControlBits::AUTOCORRECT;
959     else
960         nCntrl |= EEControlBits::AUTOCORRECT;
961 
962     if ( nCntrl != nOld )
963         mpEditEngine->SetControlWord(nCntrl);
964 }
965 
966 void ScInputHandler::UpdateSpellSettings( bool bFromStartTab )
967 {
968     if ( !pActiveViewSh )
969         return;
970 
971     ScViewData& rViewData = pActiveViewSh->GetViewData();
972     bool bOnlineSpell = rViewData.GetDocument().GetDocOptions().IsAutoSpell();
973 
974     //  SetDefaultLanguage is independent of the language attributes,
975     //  ScGlobal::GetEditDefaultLanguage is always used.
976     //  It must be set every time in case the office language was changed.
977 
978     mpEditEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
979 
980     //  if called for changed options, update flags only if already editing
981     //  if called from StartTable, always update flags
982 
983     if ( bFromStartTab || eMode != SC_INPUT_NONE )
984     {
985         EEControlBits nCntrl = mpEditEngine->GetControlWord();
986         EEControlBits nOld = nCntrl;
987         if( bOnlineSpell )
988             nCntrl |= EEControlBits::ONLINESPELLING;
989         else
990             nCntrl &= ~EEControlBits::ONLINESPELLING;
991         // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
992         if ( pLastPattern && pLastPattern->IsSymbolFont() )
993             nCntrl &= ~EEControlBits::AUTOCORRECT;
994         else
995             nCntrl |= EEControlBits::AUTOCORRECT;
996         if ( nCntrl != nOld )
997             mpEditEngine->SetControlWord(nCntrl);
998 
999         ScDocument& rDoc = rViewData.GetDocument();
1000         rDoc.ApplyAsianEditSettings( *mpEditEngine );
1001         mpEditEngine->SetDefaultHorizontalTextDirection(
1002             rDoc.GetEditTextDirection( rViewData.GetTabNo() ) );
1003         mpEditEngine->SetFirstWordCapitalization( false );
1004     }
1005 
1006     //  Language is set separately, so the speller is needed only if online spelling is active
1007     if ( bOnlineSpell ) {
1008         css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
1009         mpEditEngine->SetSpeller( xXSpellChecker1 );
1010     }
1011 
1012     bool bHyphen = pLastPattern && pLastPattern->GetItem(ATTR_HYPHENATE).GetValue();
1013     if ( bHyphen ) {
1014         css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
1015         mpEditEngine->SetHyphenator( xXHyphenator );
1016     }
1017 }
1018 
1019 // Function/Range names etc. as Tip help
1020 
1021 //  The other types are defined in ScDocument::GetFormulaEntries
1022 void ScInputHandler::GetFormulaData()
1023 {
1024     if ( !pActiveViewSh )
1025         return;
1026 
1027     ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1028 
1029     if ( pFormulaData )
1030         pFormulaData->clear();
1031     else
1032     {
1033         pFormulaData.reset( new ScTypedCaseStrSet );
1034     }
1035 
1036     if( pFormulaDataPara )
1037         pFormulaDataPara->clear();
1038     else
1039         pFormulaDataPara.reset( new ScTypedCaseStrSet );
1040 
1041     const OUString aParenthesesReplacement( cParenthesesReplacement);
1042     const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
1043     const sal_uInt32 nListCount = pFuncList->GetCount();
1044     const InputHandlerFunctionNames& rFunctionNames = ScGlobal::GetInputHandlerFunctionNames();
1045     *pFormulaData     = rFunctionNames.maFunctionData;
1046     *pFormulaDataPara = rFunctionNames.maFunctionDataPara;
1047     maFormulaChar     = rFunctionNames.maFunctionChar;
1048 
1049     // Increase suggestion priority of MRU formulas
1050     const ScAppOptions& rOpt = SC_MOD()->GetAppOptions();
1051     const sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount();
1052     const sal_uInt16* pMRUList = rOpt.GetLRUFuncList();
1053     for (sal_uInt16 i = 0; i < nMRUCount; i++)
1054     {
1055         const sal_uInt16 nId = pMRUList[i];
1056         for (sal_uInt32 j = 0; j < nListCount; j++)
1057         {
1058             const ScFuncDesc* pDesc = pFuncList->GetFunction(j);
1059             if (pDesc->nFIndex == nId && pDesc->mxFuncName)
1060             {
1061                 const OUString aEntry = *pDesc->mxFuncName + aParenthesesReplacement;;
1062                 const ScTypedStrData aData(aEntry, 0.0, 0.0, ScTypedStrData::Standard);
1063                 auto it = pFormulaData->find(aData);
1064                 if (it != pFormulaData->end())
1065                     pFormulaData->erase(it);
1066                 pFormulaData->insert(ScTypedStrData(aEntry, 0.0, 0.0, ScTypedStrData::MRU));
1067                 break; // Stop searching
1068             }
1069         }
1070     }
1071     miAutoPosFormula = pFormulaData->end();
1072 
1073     // tdf#142031 - collect all the characters for the formula suggestion auto input
1074     ScTypedCaseStrSet aStrSet;
1075     rDoc.GetFormulaEntries( aStrSet );
1076     for (auto iter = aStrSet.begin(); iter != aStrSet.end(); ++iter)
1077     {
1078         const OUString aFuncName = ScGlobal::getCharClass().uppercase((*iter).GetString());
1079         // fdo#75264 fill maFormulaChar with all characters used in formula names
1080         for (sal_Int32 j = 0; j < aFuncName.getLength(); j++)
1081             maFormulaChar.insert(aFuncName[j]);
1082     }
1083     pFormulaData->insert(aStrSet.begin(), aStrSet.end());
1084     pFormulaDataPara->insert(aStrSet.begin(), aStrSet.end());
1085 }
1086 
1087 IMPL_LINK( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent&, rEvent, void )
1088 {
1089     if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
1090         || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus)
1091         HideTip();
1092 }
1093 
1094 IMPL_LINK( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent&, rEvent, void )
1095 {
1096     if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
1097         || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus)
1098         HideTipBelow();
1099 }
1100 
1101 void ScInputHandler::HideTip()
1102 {
1103     if ( nTipVisible )
1104     {
1105         pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
1106         Help::HidePopover(pTipVisibleParent, nTipVisible );
1107         nTipVisible = nullptr;
1108         pTipVisibleParent = nullptr;
1109     }
1110     aManualTip.clear();
1111 }
1112 void ScInputHandler::HideTipBelow()
1113 {
1114     if ( nTipVisibleSec )
1115     {
1116         pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
1117         Help::HidePopover(pTipVisibleSecParent, nTipVisibleSec);
1118         nTipVisibleSec = nullptr;
1119         pTipVisibleSecParent = nullptr;
1120     }
1121     aManualTip.clear();
1122 }
1123 
1124 namespace
1125 {
1126 
1127 bool lcl_hasSingleToken(std::u16string_view s, sal_Unicode c)
1128 {
1129     return !s.empty() && s.find(c) == std::u16string_view::npos;
1130 }
1131 
1132 }
1133 
1134 void ScInputHandler::ShowArgumentsTip( OUString& rSelText )
1135 {
1136     if (comphelper::LibreOfficeKit::isActive())
1137     {
1138         return;
1139     }
1140 
1141     if ( !pActiveViewSh )
1142         return;
1143 
1144     ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
1145     const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1146     const sal_Unicode cSheetSep = pDocSh->GetDocument().GetSheetSeparator();
1147     FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1148     bool bFound = false;
1149     while( !bFound )
1150     {
1151         rSelText += ")";
1152         sal_Int32 nLeftParentPos = lcl_MatchParenthesis( rSelText, rSelText.getLength()-1 );
1153         if( nLeftParentPos != -1 )
1154         {
1155             sal_Int32 nNextFStart = aHelper.GetFunctionStart( rSelText, nLeftParentPos, true);
1156             const IFunctionDescription* ppFDesc;
1157             ::std::vector< OUString> aArgs;
1158             if( aHelper.GetNextFunc( rSelText, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1159             {
1160                 if( !ppFDesc->getFunctionName().isEmpty() )
1161                 {
1162                     sal_Int32 nArgPos = aHelper.GetArgStart( rSelText, nNextFStart, 0 );
1163                     sal_uInt16 nArgs = static_cast<sal_uInt16>(ppFDesc->getParameterCount());
1164                     OUString aFuncName( ppFDesc->getFunctionName() + "(");
1165                     OUString aNew;
1166                     ScTypedCaseStrSet::const_iterator it =
1167                         findText(*pFormulaDataPara, pFormulaDataPara->end(), aFuncName, aNew, false);
1168                     if (it != pFormulaDataPara->end())
1169                     {
1170                         bool bFlag = false;
1171                         sal_uInt16 nActive = 0;
1172                         for( sal_uInt16 i=0; i < nArgs; i++ )
1173                         {
1174                             sal_Int32 nLength = aArgs[i].getLength();
1175                             if( nArgPos <= rSelText.getLength()-1 )
1176                             {
1177                                 nActive = i+1;
1178                                 bFlag = true;
1179                             }
1180                             nArgPos+=nLength+1;
1181                         }
1182                         if( bFlag )
1183                         {
1184                             sal_Int32 nStartPosition = 0;
1185                             sal_Int32 nEndPosition = 0;
1186 
1187                             if( lcl_hasSingleToken(aNew, cSep) )
1188                             {
1189                                 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
1190                                 {
1191                                     sal_Unicode cNext = aNew[i];
1192                                     if( cNext == '(' )
1193                                     {
1194                                         nStartPosition = i+1;
1195                                     }
1196                                 }
1197                             }
1198                             else if( lcl_hasSingleToken(aNew, cSheetSep) )
1199                             {
1200                                 sal_uInt16 nCount = 0;
1201                                 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
1202                                 {
1203                                     sal_Unicode cNext = aNew[i];
1204                                     if( cNext == '(' )
1205                                     {
1206                                         nStartPosition = i+1;
1207                                     }
1208                                     else if( cNext == cSep )
1209                                     {
1210                                         nCount ++;
1211                                         nEndPosition = i;
1212                                         if( nCount == nActive )
1213                                         {
1214                                             break;
1215                                         }
1216                                         nStartPosition = nEndPosition+1;
1217                                     }
1218                                 }
1219                             }
1220                             else
1221                             {
1222                                 sal_uInt16 nCount = 0;
1223                                 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
1224                                 {
1225                                     sal_Unicode cNext = aNew[i];
1226                                     if( cNext == '(' )
1227                                     {
1228                                         nStartPosition = i+1;
1229                                     }
1230                                     else if( cNext == cSep )
1231                                     {
1232                                         nCount ++;
1233                                         nEndPosition = i;
1234                                         if( nCount == nActive )
1235                                         {
1236                                             break;
1237                                         }
1238                                         nStartPosition = nEndPosition+1;
1239                                     }
1240                                     else if( cNext == cSheetSep )
1241                                     {
1242                                         continue;
1243                                     }
1244                                 }
1245                             }
1246 
1247                             if (nStartPosition > 0)
1248                             {
1249                                 nArgs = ppFDesc->getParameterCount();
1250                                 sal_Int16 nVarArgsSet = 0;
1251                                 if ( nArgs >= PAIRED_VAR_ARGS )
1252                                 {
1253                                     nVarArgsSet = 2;
1254                                     nArgs -= PAIRED_VAR_ARGS - nVarArgsSet;
1255                                 }
1256                                 else if ( nArgs >= VAR_ARGS )
1257                                 {
1258                                     nVarArgsSet = 1;
1259                                     nArgs -= VAR_ARGS - nVarArgsSet;
1260                                 }
1261                                 if ( nVarArgsSet > 0 && nActive > nArgs )
1262                                     nActive = nArgs - (nActive - nArgs) % nVarArgsSet;
1263                                 aNew = OUString::Concat(aNew.subView(0, nStartPosition)) +
1264                                         u"\x25BA" +
1265                                         aNew.subView(nStartPosition) +
1266                                         " : " +
1267                                         ppFDesc->getParameterDescription(nActive-1);
1268                                 if (eMode != SC_INPUT_TOP)
1269                                 {
1270                                     ShowTipBelow( aNew );
1271                                 }
1272                                 else
1273                                 {
1274                                     ShowTip(aNew);
1275                                 }
1276                                 bFound = true;
1277                             }
1278                         }
1279                         else
1280                         {
1281                             ShowTipBelow( aNew );
1282                             bFound = true;
1283                         }
1284                     }
1285                 }
1286             }
1287         }
1288         else
1289         {
1290             break;
1291         }
1292     }
1293 }
1294 
1295 void ScInputHandler::ShowTipCursor()
1296 {
1297     HideTip();
1298     HideTipBelow();
1299     EditView* pActiveView = pTopView ? pTopView : pTableView;
1300 
1301     /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1302     if ( !(bFormulaMode && pActiveView && pFormulaDataPara && mpEditEngine->GetParagraphCount() == 1) )
1303         return;
1304 
1305     OUString aParagraph = mpEditEngine->GetText( 0 );
1306     ESelection aSel = pActiveView->GetSelection();
1307     aSel.Adjust();
1308 
1309     if ( aParagraph.getLength() < aSel.nEndPos )
1310         return;
1311 
1312     if ( aSel.nEndPos > 0 )
1313     {
1314         OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
1315 
1316         ShowArgumentsTip( aSelText );
1317     }
1318 }
1319 
1320 void ScInputHandler::ShowTip( const OUString& rText )
1321 {
1322     // aManualTip needs to be set afterwards from outside
1323 
1324     HideTip();
1325     HideTipBelow();
1326 
1327     EditView* pActiveView = pTopView ? pTopView : pTableView;
1328     if (!pActiveView)
1329         return;
1330 
1331     Point aPos;
1332     if (pInputWin && pInputWin->GetEditView() == pActiveView)
1333     {
1334         pTipVisibleParent = pInputWin->GetEditWindow();
1335         aPos = pInputWin->GetCursorScreenPixelPos();
1336     }
1337     else
1338     {
1339         pTipVisibleParent = pActiveView->GetWindow();
1340         if (vcl::Cursor* pCur = pActiveView->GetCursor())
1341             aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() );
1342         aPos = pTipVisibleParent->OutputToScreenPixel( aPos );
1343     }
1344 
1345     tools::Rectangle aRect( aPos, aPos );
1346     QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
1347     nTipVisible = Help::ShowPopover(pTipVisibleParent, aRect, rText, nAlign);
1348     pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
1349 }
1350 
1351 void ScInputHandler::ShowTipBelow( const OUString& rText )
1352 {
1353     HideTipBelow();
1354 
1355     EditView* pActiveView = pTopView ? pTopView : pTableView;
1356     if ( !pActiveView )
1357         return;
1358 
1359     Point aPos;
1360     if (pInputWin && pInputWin->GetEditView() == pActiveView)
1361     {
1362         pTipVisibleSecParent = pInputWin->GetEditWindow();
1363         aPos = pInputWin->GetCursorScreenPixelPos(true);
1364     }
1365     else
1366     {
1367         pTipVisibleSecParent = pActiveView->GetWindow();
1368         if (vcl::Cursor* pCur = pActiveView->GetCursor())
1369         {
1370             Point aLogicPos = pCur->GetPos();
1371             aLogicPos.AdjustY(pCur->GetHeight() );
1372             aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos );
1373         }
1374         aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos );
1375     }
1376 
1377     tools::Rectangle aRect( aPos, aPos );
1378     QuickHelpFlags const nAlign = QuickHelpFlags::Left | QuickHelpFlags::Top | QuickHelpFlags::NoEvadePointer;
1379     nTipVisibleSec = Help::ShowPopover(pTipVisibleSecParent, aRect, rText, nAlign);
1380     pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
1381 }
1382 
1383 bool ScInputHandler::GetFuncName( OUString& aStart, OUString& aResult )
1384 {
1385     if ( aStart.isEmpty() )
1386         return false;
1387 
1388     aStart = ScGlobal::getCharClass().uppercase( aStart );
1389     sal_Int32 nPos = aStart.getLength() - 1;
1390     sal_Unicode c = aStart[ nPos ];
1391     // fdo#75264 use maFormulaChar to check if characters are used in function names
1392     ::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c );
1393     if ( p == maFormulaChar.end() )
1394         return false; // last character is not part of any function name, quit
1395 
1396     ::std::vector<sal_Unicode> aTemp { c };
1397     for(sal_Int32 i = nPos - 1; i >= 0; --i)
1398     {
1399         c = aStart[ i ];
1400         p = maFormulaChar.find( c );
1401 
1402         if (p == maFormulaChar.end())
1403             break;
1404 
1405         aTemp.push_back( c );
1406     }
1407 
1408     ::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin();
1409     aResult = OUString( *rIt++ );
1410     while ( rIt != aTemp.rend() )
1411         aResult += OUStringChar( *rIt++ );
1412 
1413     return true;
1414 }
1415 
1416 namespace {
1417     /// Rid ourselves of unwanted " quoted json characters.
1418     OString escapeJSON(const OUString &aStr)
1419     {
1420         OUString aEscaped = aStr;
1421         aEscaped = aEscaped.replaceAll("\n", " ");
1422         aEscaped = aEscaped.replaceAll("\"", "'");
1423         return OUStringToOString(aEscaped, RTL_TEXTENCODING_UTF8);
1424     }
1425 }
1426 
1427 void ScInputHandler::ShowFuncList( const ::std::vector< OUString > & rFuncStrVec )
1428 {
1429     const SfxViewShell* pViewShell = SfxViewShell::Current();
1430     if (comphelper::LibreOfficeKit::isActive())
1431     {
1432         if (rFuncStrVec.size() && pViewShell && pViewShell->isLOKMobilePhone())
1433         {
1434             auto aPos = pFormulaData->begin();
1435             sal_uInt32 nCurIndex = std::distance(aPos, miAutoPosFormula);
1436             const sal_uInt32 nSize = pFormulaData->size();
1437 
1438             OUString aFuncNameStr;
1439             OUString aDescFuncNameStr;
1440             OStringBuffer aPayload("[ ");
1441             for (const OUString& rFunc : rFuncStrVec)
1442             {
1443                 if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
1444                 {
1445                     aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
1446                 }
1447                 else
1448                 {
1449                     aFuncNameStr = rFunc;
1450                 }
1451 
1452                 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1453                 aDescFuncNameStr = aFuncNameStr + "()";
1454                 sal_Int32 nNextFStart = 0;
1455                 const IFunctionDescription* ppFDesc;
1456                 ::std::vector< OUString > aArgs;
1457                 OUString eqPlusFuncName = "=" + aDescFuncNameStr;
1458                 if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1459                 {
1460                     if ( !ppFDesc->getFunctionName().isEmpty() )
1461                     {
1462                         aPayload.append("{"
1463                             "\"index\": "
1464                             + OString::number(static_cast<sal_Int64>(nCurIndex))
1465                             + ", "
1466                             "\"signature\": \""
1467                             + escapeJSON(ppFDesc->getSignature())
1468                             + "\", "
1469                             "\"description\": \""
1470                             + escapeJSON(ppFDesc->getDescription())
1471                             + "\"}, ");
1472                     }
1473                 }
1474                 ++nCurIndex;
1475                 if (nCurIndex == nSize)
1476                     nCurIndex = 0;
1477             }
1478             sal_Int32 nLen = aPayload.getLength();
1479             aPayload[nLen - 2] = ' ';
1480             aPayload[nLen - 1] = ']';
1481 
1482             OString s = aPayload.makeStringAndClear();
1483             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, s);
1484         }
1485         // not tunnel tooltips in the lok case
1486         return;
1487     }
1488 
1489     OUStringBuffer aTipStr;
1490     OUString aFuncNameStr;
1491     OUString aDescFuncNameStr;
1492     ::std::vector<OUString>::const_iterator itStr = rFuncStrVec.begin();
1493     sal_Int32 nMaxFindNumber = 3;
1494     sal_Int32 nRemainFindNumber = nMaxFindNumber;
1495     for ( ; itStr != rFuncStrVec.end(); ++itStr )
1496     {
1497         const OUString& rFunc = *itStr;
1498         if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
1499         {
1500             aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
1501         }
1502         else
1503         {
1504             aFuncNameStr = rFunc;
1505         }
1506         if ( itStr == rFuncStrVec.begin() )
1507         {
1508             aTipStr = "[";
1509             aDescFuncNameStr = aFuncNameStr + "()";
1510         }
1511         else
1512         {
1513             aTipStr.append(", ");
1514         }
1515         aTipStr.append(aFuncNameStr);
1516         if ( itStr == rFuncStrVec.begin() )
1517             aTipStr.append("]");
1518         if ( --nRemainFindNumber <= 0 )
1519             break;
1520     }
1521     sal_Int32 nRemainNumber = rFuncStrVec.size() - nMaxFindNumber;
1522     if ( nRemainFindNumber == 0 && nRemainNumber > 0 )
1523     {
1524         OUString aMessage( ScResId( STR_FUNCTIONS_FOUND ) );
1525         aMessage = aMessage.replaceFirst("%2", OUString::number(nRemainNumber));
1526         aMessage = aMessage.replaceFirst("%1", aTipStr);
1527         aTipStr = aMessage;
1528     }
1529     FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1530     sal_Int32 nNextFStart = 0;
1531     const IFunctionDescription* ppFDesc;
1532     ::std::vector< OUString > aArgs;
1533     OUString eqPlusFuncName = "=" + aDescFuncNameStr;
1534     if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1535     {
1536         if ( !ppFDesc->getFunctionName().isEmpty() )
1537         {
1538             aTipStr.append(" : " + ppFDesc->getDescription());
1539         }
1540     }
1541     ShowTip( aTipStr.makeStringAndClear() );
1542 }
1543 
1544 void ScInputHandler::UseFormulaData()
1545 {
1546     EditView* pActiveView = pTopView ? pTopView : pTableView;
1547 
1548     /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1549     if ( !(pActiveView && pFormulaData && mpEditEngine->GetParagraphCount() == 1) )
1550         return;
1551 
1552     OUString aParagraph = mpEditEngine->GetText( 0 );
1553     ESelection aSel = pActiveView->GetSelection();
1554     aSel.Adjust();
1555 
1556     // Due to differences between table and input cell (e.g clipboard with line breaks),
1557     // the selection may not be in line with the EditEngine anymore.
1558     // Just return without any indication as to why.
1559     if ( aSel.nEndPos > aParagraph.getLength() )
1560         return;
1561 
1562     if ( aParagraph.getLength() > aSel.nEndPos &&
1563          ( ScGlobal::getCharClass().isLetterNumeric( aParagraph, aSel.nEndPos ) ||
1564            aParagraph[ aSel.nEndPos ] == '_' ||
1565            aParagraph[ aSel.nEndPos ] == '.' ||
1566            aParagraph[ aSel.nEndPos ] == '$'   ) )
1567         return;
1568 
1569     //  Is the cursor at the end of a word?
1570     if ( aSel.nEndPos <= 0 )
1571         return;
1572 
1573     OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
1574 
1575     OUString aText;
1576     if ( GetFuncName( aSelText, aText ) )
1577     {
1578         // function name is incomplete:
1579         // show matching functions name as tip above cell
1580         ::std::vector<OUString> aNewVec;
1581         miAutoPosFormula = pFormulaData->end();
1582         miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false);
1583         if (miAutoPosFormula != pFormulaData->end())
1584         {
1585             // check if partial function name is not between quotes
1586             sal_Unicode cBetweenQuotes = 0;
1587             for ( int n = 0; n < aSelText.getLength(); n++ )
1588             {
1589                 if (cBetweenQuotes)
1590                 {
1591                     if (aSelText[n] == cBetweenQuotes)
1592                         cBetweenQuotes = 0;
1593                 }
1594                 else if ( aSelText[ n ] == '"' )
1595                     cBetweenQuotes = '"';
1596                 else if ( aSelText[ n ] == '\'' )
1597                     cBetweenQuotes = '\'';
1598             }
1599             if ( cBetweenQuotes )
1600                 return;  // we're between quotes
1601 
1602             ShowFuncList(aNewVec);
1603             aAutoSearch = aText;
1604         }
1605         return;
1606     }
1607 
1608     // function name is complete:
1609     // show tip below the cell with function name and arguments of function
1610     ShowArgumentsTip( aSelText );
1611 }
1612 
1613 void ScInputHandler::NextFormulaEntry( bool bBack )
1614 {
1615     EditView* pActiveView = pTopView ? pTopView : pTableView;
1616     if ( pActiveView && pFormulaData )
1617     {
1618         ::std::vector<OUString> aNewVec;
1619         ScTypedCaseStrSet::const_iterator itNew = findTextAll(*pFormulaData, miAutoPosFormula, aAutoSearch, aNewVec, bBack);
1620         if (itNew != pFormulaData->end())
1621         {
1622             miAutoPosFormula = itNew;
1623             ShowFuncList( aNewVec );
1624         }
1625     }
1626 
1627     // For Tab we always call HideCursor first
1628     if (pActiveView)
1629         pActiveView->ShowCursor();
1630 }
1631 
1632 namespace {
1633 
1634 void completeFunction( EditView* pView, const OUString& rInsert, bool& rParInserted )
1635 {
1636     if (!pView)
1637         return;
1638 
1639     ESelection aSel = pView->GetSelection();
1640 
1641     bool bNoInitialLetter = false;
1642     OUString aOld = pView->GetEditEngine()->GetText(0);
1643     // in case we want just insert a function and not completing
1644     if ( comphelper::LibreOfficeKit::isActive() )
1645     {
1646         ESelection aSelRange = aSel;
1647         --aSelRange.nStartPos;
1648         --aSelRange.nEndPos;
1649         pView->SetSelection(aSelRange);
1650         pView->SelectCurrentWord();
1651 
1652         if ( aOld == "=" )
1653         {
1654             bNoInitialLetter = true;
1655             aSelRange.nStartPos = 1;
1656             aSelRange.nEndPos = 1;
1657             pView->SetSelection(aSelRange);
1658         }
1659         else if ( pView->GetSelected().startsWith("()") )
1660         {
1661             bNoInitialLetter = true;
1662             ++aSelRange.nStartPos;
1663             ++aSelRange.nEndPos;
1664             pView->SetSelection(aSelRange);
1665         }
1666     }
1667 
1668     if(!bNoInitialLetter)
1669     {
1670         const sal_Int32 nMinLen = std::max(aSel.nEndPos - aSel.nStartPos, sal_Int32(1));
1671         // Since transliteration service is used to test for match, the replaced string could be
1672         // longer than rInsert, so in order to find longest match before the cursor, test whole
1673         // string from start to current cursor position (don't limit to length of rInsert)
1674         // Disclaimer: I really don't know if a match longer than rInsert is actually possible,
1675         // so the above is based on assumptions how "transliteration" might possibly work. If
1676         // it's in fact impossible, an optimization would be useful to limit aSel.nStartPos to
1677         // std::max(sal_Int32(0), aSel.nEndPos - rInsert.getLength()).
1678         aSel.nStartPos = 0;
1679         pView->SetSelection(aSel);
1680         const OUString aAll = pView->GetSelected();
1681         OUString aMatch;
1682         for (sal_Int32 n = aAll.getLength(); n >= nMinLen && aMatch.isEmpty(); --n)
1683         {
1684             const OUString aTest = aAll.copy(aAll.getLength() - n); // n trailing chars
1685             if (ScGlobal::GetTransliteration().isMatch(aTest, rInsert))
1686                 aMatch = aTest; // Found => break the loop
1687         }
1688 
1689         aSel.nStartPos = aSel.nEndPos - aMatch.getLength();
1690         pView->SetSelection(aSel);
1691     }
1692 
1693     OUString aInsStr = rInsert;
1694     sal_Int32 nInsLen = aInsStr.getLength();
1695     bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '('
1696                                   && aInsStr[nInsLen-1] == ')' );
1697     if ( bDoParen )
1698     {
1699         // Do not insert parentheses after function names if there already are some
1700         // (e.g. if the function name was edited).
1701         ESelection aWordSel = pView->GetSelection();
1702 
1703         // aWordSel.EndPos points one behind string if word at end
1704         if (aWordSel.nEndPos < aOld.getLength())
1705         {
1706             sal_Unicode cNext = aOld[aWordSel.nEndPos];
1707             if ( cNext == '(' )
1708             {
1709                 bDoParen = false;
1710                 aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses
1711             }
1712         }
1713     }
1714 
1715     pView->InsertText( aInsStr );
1716 
1717     if ( bDoParen ) // Put cursor between parentheses
1718     {
1719         aSel = pView->GetSelection();
1720         --aSel.nStartPos;
1721         --aSel.nEndPos;
1722         pView->SetSelection(aSel);
1723 
1724         rParInserted = true;
1725     }
1726 }
1727 
1728 }
1729 
1730 void ScInputHandler::PasteFunctionData()
1731 {
1732     if (pFormulaData && miAutoPosFormula != pFormulaData->end())
1733     {
1734         const ScTypedStrData& rData = *miAutoPosFormula;
1735         OUString aInsert = rData.GetString();
1736         if (aInsert[aInsert.getLength()-1] == cParenthesesReplacement)
1737             aInsert = OUString::Concat(aInsert.subView( 0, aInsert.getLength()-1)) + "()";
1738         bool bParInserted = false;
1739 
1740         DataChanging(); // Cannot be new
1741         completeFunction( pTopView, aInsert, bParInserted );
1742         completeFunction( pTableView, aInsert, bParInserted );
1743         DataChanged();
1744         ShowTipCursor();
1745 
1746         if (bParInserted)
1747             AutoParAdded();
1748     }
1749 
1750     HideTip();
1751 
1752     EditView* pActiveView = pTopView ? pTopView : pTableView;
1753     if (comphelper::LibreOfficeKit::isActive() && pTopView && pInputWin)
1754         pInputWin->TextGrabFocus();
1755     if (pActiveView)
1756         pActiveView->ShowCursor();
1757 }
1758 
1759 void ScInputHandler::LOKPasteFunctionData(const OUString& rFunctionName)
1760 {
1761     // in case we have no top view try to create it
1762     if (!pTopView && pInputWin)
1763     {
1764         ScInputMode eCurMode = eMode;
1765         SetMode(SC_INPUT_TOP);
1766         if (!pTopView)
1767             SetMode(eCurMode);
1768     }
1769 
1770     EditView* pEditView = pTopView ? pTopView : pTableView;
1771 
1772     if (!pActiveViewSh || !pEditView)
1773         return;
1774 
1775     bool bEdit = false;
1776     OUString aFormula;
1777     const EditEngine* pEditEngine = pEditView->GetEditEngine();
1778     if (pEditEngine)
1779     {
1780         aFormula = pEditEngine->GetText(0);
1781         /* TODO: LOK: are you sure you want '+' and '-' let start formulas with
1782          * function names? That was meant for "data typist" numeric keyboard
1783          * input. */
1784         bEdit = aFormula.getLength() > 1 && (aFormula[0] == '=' || aFormula[0] == '+' || aFormula[0] == '-');
1785     }
1786 
1787     if ( !bEdit )
1788     {
1789         OUString aNewFormula('=');
1790         if ( aFormula.startsWith("=") )
1791             aNewFormula = aFormula;
1792 
1793         InputReplaceSelection( aNewFormula );
1794     }
1795 
1796     if (pFormulaData)
1797     {
1798         OUString aNew;
1799         ScTypedCaseStrSet::const_iterator aPos = findText(*pFormulaData, pFormulaData->begin(), rFunctionName, aNew, /* backward = */false);
1800 
1801         if (aPos != pFormulaData->end())
1802         {
1803             miAutoPosFormula = aPos;
1804             PasteFunctionData();
1805         }
1806     }
1807 }
1808 
1809 void ScInputHandler::LOKSendFormulabarUpdate(EditView* pActiveView,
1810                                              const SfxViewShell* pActiveViewSh,
1811                                              const OUString& rText,
1812                                              const ESelection& rSelection)
1813 {
1814     OUString aSelection;
1815     if (pActiveView)
1816     {
1817         aSelection = OUString::number(pActiveView->GetPosWithField(0, rSelection.nStartPos)) + ";" +
1818             OUString::number(pActiveView->GetPosWithField(0, rSelection.nEndPos)) + ";" +
1819             OUString::number(rSelection.nStartPara) + ";" + OUString::number(rSelection.nEndPara);
1820     }
1821     else
1822     {
1823         aSelection = OUString::number(rSelection.nStartPos) + ";" + OUString::number(rSelection.nEndPos) + ";" +
1824             OUString::number(rSelection.nStartPara) + ";" + OUString::number(rSelection.nEndPara);
1825     }
1826 
1827     std::unique_ptr<jsdialog::ActionDataMap> pData = std::make_unique<jsdialog::ActionDataMap>();
1828     (*pData)["action_type"] = "setText";
1829     (*pData)["text"] = rText;
1830     (*pData)["selection"] = aSelection;
1831 
1832     sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(pActiveViewSh);
1833     OUString sWindowId = OUString::number(nCurrentShellId) + "formulabar";
1834     jsdialog::SendAction(sWindowId, "sc_input_window", std::move(pData));
1835 }
1836 
1837 // Calculate selection and display as tip help
1838 static OUString lcl_Calculate( const OUString& rFormula, ScDocument& rDoc, const ScAddress &rPos )
1839 {
1840     //TODO: Merge with ScFormulaDlg::CalcValue and move into Document!
1841     // Quotation marks for Strings are only inserted here.
1842 
1843     if(rFormula.isEmpty())
1844         return OUString();
1845 
1846     std::optional<ScSimpleFormulaCalculator> pCalc( std::in_place, rDoc, rPos, rFormula, false );
1847 
1848     // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range
1849     // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own
1850     bool bColRowName = pCalc->HasColRowName();
1851     if ( bColRowName )
1852     {
1853         // ColRowName in RPN code?
1854         if ( pCalc->GetCode()->GetCodeLen() <= 1 )
1855         {   // ==1: Single one is as a Parameter always a Range
1856             // ==0: It might be one, if ...
1857             OUString aBraced = "(" + rFormula + ")";
1858             pCalc.emplace( rDoc, rPos, aBraced, false );
1859         }
1860         else
1861             bColRowName = false;
1862     }
1863 
1864     FormulaError nErrCode = pCalc->GetErrCode();
1865     if ( nErrCode != FormulaError::NONE )
1866         return ScGlobal::GetErrorString(nErrCode);
1867 
1868     SvNumberFormatter& aFormatter = *rDoc.GetFormatTable();
1869     OUString aValue;
1870     if ( pCalc->IsValue() )
1871     {
1872         double n = pCalc->GetValue();
1873         sal_uInt32 nFormat = aFormatter.GetStandardFormat( n, 0,
1874                 pCalc->GetFormatType(), ScGlobal::eLnge );
1875         aFormatter.GetInputLineString( n, nFormat, aValue );
1876         //! display OutputString but insert InputLineString
1877     }
1878     else
1879     {
1880         OUString aStr = pCalc->GetString().getString();
1881         sal_uInt32 nFormat = aFormatter.GetStandardFormat(
1882                 pCalc->GetFormatType(), ScGlobal::eLnge);
1883         {
1884             const Color* pColor;
1885             aFormatter.GetOutputString( aStr, nFormat,
1886                     aValue, &pColor );
1887         }
1888 
1889         aValue = "\"" + aValue + "\"";
1890         //! Escape quotation marks in String??
1891     }
1892 
1893     ScRange aTestRange;
1894     if ( bColRowName || (aTestRange.Parse(rFormula, rDoc) & ScRefFlags::VALID) )
1895         aValue += " ...";
1896 
1897     return aValue;
1898 }
1899 
1900 void ScInputHandler::FormulaPreview()
1901 {
1902     OUString aValue;
1903     EditView* pActiveView = pTopView ? pTopView : pTableView;
1904     if ( pActiveView && pActiveViewSh )
1905     {
1906         OUString aPart = pActiveView->GetSelected();
1907         if (aPart.isEmpty())
1908             aPart = mpEditEngine->GetText(0);
1909         ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1910         aValue = lcl_Calculate( aPart, rDoc, aCursorPos );
1911     }
1912 
1913     if (!aValue.isEmpty())
1914     {
1915         ShowTip( aValue );          //  Display as QuickHelp
1916         aManualTip = aValue;        //  Set after ShowTip
1917         if (pFormulaData)
1918             miAutoPosFormula = pFormulaData->end();
1919         if (pColumnData)
1920             miAutoPosColumn = pColumnData->end();
1921     }
1922 }
1923 
1924 void ScInputHandler::PasteManualTip()
1925 {
1926     //  Three dots at the end -> Range reference -> do not insert
1927     //  FIXME: Once we have matrix constants, we can change this
1928     sal_Int32 nTipLen = aManualTip.getLength();
1929     sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen));
1930     if ( nTipLen && ( nTipLen < 3 || aManualTip.subView( nTipLen2-3 ) != u"..." ) )
1931     {
1932         DataChanging(); // Cannot be new
1933 
1934         OUString aInsert = aManualTip;
1935         EditView* pActiveView = pTopView ? pTopView : pTableView;
1936         if (!pActiveView->HasSelection())
1937         {
1938             // Nothing selected -> select everything
1939             sal_Int32 nOldLen = mpEditEngine->GetTextLen(0);
1940             ESelection aAllSel( 0, 0, 0, nOldLen );
1941             if ( pTopView )
1942                 pTopView->SetSelection( aAllSel );
1943             if ( pTableView )
1944                 pTableView->SetSelection( aAllSel );
1945         }
1946 
1947         ESelection aSel = pActiveView->GetSelection();
1948         aSel.Adjust();
1949         OSL_ENSURE( !aSel.nStartPara && !aSel.nEndPara, "Too many paragraphs in Formula" );
1950         if ( !aSel.nStartPos )  // Selection from the start?
1951         {
1952             if ( aSel.nEndPos == mpEditEngine->GetTextLen(0) )
1953             {
1954                 //  Everything selected -> skip quotation marks
1955                 if ( aInsert[0] == '"' )
1956                     aInsert = aInsert.copy(1);
1957                 sal_Int32 nInsLen = aInsert.getLength();
1958                 if ( aInsert.endsWith("\"") )
1959                     aInsert = aInsert.copy( 0, nInsLen-1 );
1960             }
1961             else if ( aSel.nEndPos )
1962             {
1963                 //  Not everything selected -> do not overwrite equality sign
1964                 //FIXME: Even double equality signs??
1965                 aSel.nStartPos = 1;
1966                 if ( pTopView )
1967                     pTopView->SetSelection( aSel );
1968                 if ( pTableView )
1969                     pTableView->SetSelection( aSel );
1970             }
1971         }
1972         if ( pTopView )
1973             pTopView->InsertText( aInsert, true );
1974         if ( pTableView )
1975             pTableView->InsertText( aInsert, true );
1976 
1977         DataChanged();
1978     }
1979 
1980     HideTip();
1981 }
1982 
1983 void ScInputHandler::ResetAutoPar()
1984 {
1985     nAutoPar = 0;
1986 }
1987 
1988 void ScInputHandler::AutoParAdded()
1989 {
1990     ++nAutoPar; // Closing parenthesis can be overwritten
1991 }
1992 
1993 bool ScInputHandler::CursorAtClosingPar()
1994 {
1995     // Test if the cursor is before a closing parenthesis
1996     // Selection from SetReference has been removed before
1997     EditView* pActiveView = pTopView ? pTopView : pTableView;
1998     if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode )
1999     {
2000         ESelection aSel = pActiveView->GetSelection();
2001         sal_Int32 nPos = aSel.nStartPos;
2002         OUString aFormula = mpEditEngine->GetText(0);
2003         if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' )
2004             return true;
2005     }
2006     return false;
2007 }
2008 
2009 void ScInputHandler::SkipClosingPar()
2010 {
2011     //  this is called when a ')' is typed and the cursor is before a ')'
2012     //  that can be overwritten -> just set the cursor behind the ')'
2013 
2014     EditView* pActiveView = pTopView ? pTopView : pTableView;
2015     if (pActiveView)
2016     {
2017         ESelection aSel = pActiveView->GetSelection();
2018         ++aSel.nStartPos;
2019         ++aSel.nEndPos;
2020 
2021         //  this is in a formula (only one paragraph), so the selection
2022         //  can be used directly for the TopView
2023 
2024         if ( pTopView )
2025             pTopView->SetSelection( aSel );
2026         if ( pTableView )
2027             pTableView->SetSelection( aSel );
2028     }
2029 
2030     OSL_ENSURE(nAutoPar, "SkipClosingPar: count is wrong");
2031     --nAutoPar;
2032 }
2033 
2034 // Auto input
2035 
2036 void ScInputHandler::GetColData()
2037 {
2038     if ( !pActiveViewSh )
2039         return;
2040 
2041     ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2042 
2043     if ( pColumnData )
2044         pColumnData->clear();
2045     else
2046         pColumnData.reset( new ScTypedCaseStrSet );
2047 
2048     std::vector<ScTypedStrData> aEntries;
2049     rDoc.GetDataEntries(
2050         aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aEntries);
2051     if (!aEntries.empty())
2052         pColumnData->insert(aEntries.begin(), aEntries.end());
2053 
2054     miAutoPosColumn = pColumnData->end();
2055 }
2056 
2057 void ScInputHandler::UseColData() // When typing
2058 {
2059     EditView* pActiveView = pTopView ? pTopView : pTableView;
2060     if ( !(pActiveView && pColumnData) )
2061         return;
2062 
2063     //  Only change when cursor is at the end
2064     ESelection aSel = pActiveView->GetSelection();
2065     aSel.Adjust();
2066 
2067     sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
2068     if ( aSel.nEndPara+1 != nParCnt )
2069         return;
2070 
2071     sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara );
2072     if ( aSel.nEndPos != nParLen )
2073         return;
2074 
2075     OUString aText = GetEditText(mpEditEngine.get());
2076     if (aText.isEmpty())
2077         return;
2078 
2079     std::vector< OUString > aResultVec;
2080     OUString aNew;
2081     sal_Int32 nLongestPrefixLen = 0;
2082     miAutoPosColumn = pColumnData->end();
2083     mbPartialPrefix = false;
2084     miAutoPosColumn = findTextAll(*pColumnData, miAutoPosColumn, aText, aResultVec, false, &nLongestPrefixLen);
2085 
2086     if (nLongestPrefixLen <= 0 || aResultVec.empty())
2087         return;
2088 
2089     if (aResultVec.size() > 1)
2090     {
2091         mbPartialPrefix = true;
2092         bUseTab = true; // Allow Ctrl (+ Shift + ) + TAB cycling.
2093         miAutoPosColumn = pColumnData->end();
2094 
2095         // Display the rest of longest common prefix as suggestion.
2096         aNew = aResultVec[0].copy(0, nLongestPrefixLen);
2097     }
2098     else
2099     {
2100         aNew = aResultVec[0];
2101     }
2102 
2103     // Strings can contain line endings (e.g. due to dBase import),
2104     // which would result in multiple paragraphs here, which is not desirable.
2105     //! Then GetExactMatch doesn't work either
2106     lcl_RemoveLineEnd( aNew );
2107 
2108     // Keep paragraph, just append the rest
2109     //! Exact replacement in EnterHandler !!!
2110     // One Space between paragraphs:
2111     sal_Int32 nEdLen = mpEditEngine->GetTextLen() + nParCnt - 1;
2112     OUString aIns = aNew.copy(nEdLen);
2113 
2114     // Selection must be "backwards", so the cursor stays behind the last
2115     // typed character
2116     ESelection aSelection( aSel.nEndPara, aSel.nEndPos + aIns.getLength(),
2117                            aSel.nEndPara, aSel.nEndPos );
2118 
2119     // When editing in input line, apply to both edit views
2120     if ( pTableView )
2121     {
2122         pTableView->InsertText( aIns );
2123         pTableView->SetSelection( aSelection );
2124     }
2125     if ( pTopView )
2126     {
2127         pTopView->InsertText( aIns );
2128         pTopView->SetSelection( aSelection );
2129     }
2130 
2131     aAutoSearch = aText; // To keep searching - nAutoPos is set
2132 }
2133 
2134 void ScInputHandler::NextAutoEntry( bool bBack )
2135 {
2136     EditView* pActiveView = pTopView ? pTopView : pTableView;
2137     if ( pActiveView && pColumnData )
2138     {
2139         if (!aAutoSearch.isEmpty())
2140         {
2141             // Is the selection still valid (could be changed via the mouse)?
2142             ESelection aSel = pActiveView->GetSelection();
2143             aSel.Adjust();
2144             sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
2145             if ( aSel.nEndPara+1 == nParCnt && aSel.nStartPara == aSel.nEndPara )
2146             {
2147                 OUString aText = GetEditText(mpEditEngine.get());
2148                 sal_Int32 nSelLen = aSel.nEndPos - aSel.nStartPos;
2149                 sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara );
2150                 if ( aSel.nEndPos == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen )
2151                 {
2152                     OUString aNew;
2153                     ScTypedCaseStrSet::const_iterator itNew =
2154                         findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack);
2155 
2156                     if (itNew != pColumnData->end())
2157                     {
2158                         // match found!
2159                         miAutoPosColumn = itNew;
2160                         bInOwnChange = true;        // disable ModifyHdl (reset below)
2161                         mbPartialPrefix = false;
2162 
2163                         lcl_RemoveLineEnd( aNew );
2164                         OUString aIns = aNew.copy(aAutoSearch.getLength());
2165 
2166                         //  when editing in input line, apply to both edit views
2167                         if ( pTableView )
2168                         {
2169                             pTableView->DeleteSelected();
2170                             pTableView->InsertText( aIns );
2171                             pTableView->SetSelection( ESelection(
2172                                                         aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
2173                                                         aSel.nEndPara, aSel.nStartPos ) );
2174                         }
2175                         if ( pTopView )
2176                         {
2177                             pTopView->DeleteSelected();
2178                             pTopView->InsertText( aIns );
2179                             pTopView->SetSelection( ESelection(
2180                                                         aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
2181                                                         aSel.nEndPara, aSel.nStartPos ) );
2182                         }
2183 
2184                         bInOwnChange = false;
2185                     }
2186                 }
2187             }
2188         }
2189     }
2190 
2191     // For Tab, HideCursor was always called first
2192     if (pActiveView)
2193         pActiveView->ShowCursor();
2194 }
2195 
2196 // Highlight parentheses
2197 void ScInputHandler::UpdateParenthesis()
2198 {
2199     // Find parentheses
2200     //TODO: Can we disable parentheses highlighting per parentheses?
2201     bool bFound = false;
2202     if ( bFormulaMode && eMode != SC_INPUT_TOP )
2203     {
2204         if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom
2205         {
2206             ESelection aSel = pTableView->GetSelection();
2207             if (aSel.nStartPos)
2208             {
2209                 // Examine character left to the cursor
2210                 sal_Int32 nPos = aSel.nStartPos - 1;
2211                 OUString aFormula = mpEditEngine->GetText(aSel.nStartPara);
2212                 sal_Unicode c = aFormula[nPos];
2213                 if ( c == '(' || c == ')' )
2214                 {
2215                     // Note this matches only within one paragraph.
2216                     sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos );
2217                     if ( nOther != -1 )
2218                     {
2219                         SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
2220                         aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
2221 
2222                         //! Distinguish if cell is already highlighted!!!!
2223                         if (bParenthesisShown)
2224                         {
2225                             // Remove old highlighting
2226                             sal_Int32 nCount = mpEditEngine->GetParagraphCount();
2227                             for (sal_Int32 i=0; i<nCount; i++)
2228                                 mpEditEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
2229                         }
2230 
2231                         ESelection aSelThis( aSel.nStartPara, nPos, aSel.nStartPara, nPos+1);
2232                         mpEditEngine->QuickSetAttribs( aSet, aSelThis );
2233                         ESelection aSelOther( aSel.nStartPara, nOther, aSel.nStartPara, nOther+1);
2234                         mpEditEngine->QuickSetAttribs( aSet, aSelOther );
2235 
2236                         // Dummy InsertText for Update and Paint (selection is empty)
2237                         pTableView->InsertText( OUString() );
2238 
2239                         bFound = true;
2240                     }
2241                 }
2242             }
2243 
2244             //  mark parenthesis right of cursor if it will be overwritten (nAutoPar)
2245             //  with different color (COL_LIGHTBLUE) ??
2246         }
2247     }
2248 
2249     // Remove old highlighting, if no new one is set
2250     if ( bParenthesisShown && !bFound && pTableView )
2251     {
2252         sal_Int32 nCount = mpEditEngine->GetParagraphCount();
2253         for (sal_Int32 i=0; i<nCount; i++)
2254             pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
2255     }
2256 
2257     bParenthesisShown = bFound;
2258 }
2259 
2260 void ScInputHandler::ViewShellGone(const ScTabViewShell* pViewSh) // Executed synchronously!
2261 {
2262     if ( pViewSh == pActiveViewSh )
2263     {
2264         pLastState.reset();
2265         pLastPattern = nullptr;
2266     }
2267 
2268     if ( pViewSh == pRefViewSh )
2269     {
2270         //! The input from the EnterHandler does not arrive anymore
2271         // We end the EditMode anyways
2272         EnterHandler();
2273         bFormulaMode = false;
2274         pRefViewSh = nullptr;
2275         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2276         SC_MOD()->SetRefInputHdl(nullptr);
2277         if (pInputWin)
2278             pInputWin->SetFormulaMode(false);
2279         UpdateAutoCorrFlag();
2280     }
2281 
2282     pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current()  );
2283 
2284     if ( pActiveViewSh && pActiveViewSh == pViewSh )
2285     {
2286         OSL_FAIL("pActiveViewSh is gone");
2287         pActiveViewSh = nullptr;
2288     }
2289 
2290     if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
2291         UpdateRefDevice(); // Don't keep old document's printer as RefDevice
2292 }
2293 
2294 void ScInputHandler::UpdateActiveView()
2295 {
2296     ImplCreateEditEngine();
2297 
2298     // #i20588# Don't rely on focus to find the active edit view. Instead, the
2299     // active pane at the start of editing is now stored (GetEditActivePart).
2300     // GetActiveWin (the currently active pane) fails for ref input across the
2301     // panes of a split view.
2302 
2303     vcl::Window* pShellWin = pActiveViewSh ?
2304                 pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData().GetEditActivePart() ) :
2305                 nullptr;
2306 
2307     sal_uInt16 nCount = mpEditEngine->GetViewCount();
2308     if (nCount > 0)
2309     {
2310         pTableView = mpEditEngine->GetView();
2311         for (sal_uInt16 i=1; i<nCount; i++)
2312         {
2313             EditView* pThis = mpEditEngine->GetView(i);
2314             vcl::Window* pWin = pThis->GetWindow();
2315             if ( pWin==pShellWin )
2316                 pTableView = pThis;
2317         }
2318     }
2319     else
2320         pTableView = nullptr;
2321 
2322     // setup the pTableView editeng for tiled rendering to get cursor and selections
2323     if (pTableView && pActiveViewSh)
2324     {
2325         if (comphelper::LibreOfficeKit::isActive())
2326         {
2327             pTableView->RegisterViewShell(pActiveViewSh);
2328         }
2329     }
2330 
2331     if (pInputWin && (eMode == SC_INPUT_TOP || eMode == SC_INPUT_TABLE))
2332     {
2333         // tdf#71409: Always create the edit engine instance for the input
2334         // window, in order to properly manage accessibility events.
2335         pTopView = pInputWin->GetEditView();
2336         if (eMode != SC_INPUT_TOP)
2337             pTopView = nullptr;
2338     }
2339     else
2340         pTopView = nullptr;
2341 }
2342 
2343 void ScInputHandler::SetInputWindow(  ScInputWindow* pNew )
2344 {
2345     pInputWin = pNew;
2346 }
2347 
2348 void ScInputHandler::StopInputWinEngine( bool bAll )
2349 {
2350     if (pInputWin && !pInputWin->isDisposed())
2351         pInputWin->StopEditEngine( bAll );
2352 
2353     pTopView = nullptr; // invalid now
2354 }
2355 
2356 EditView* ScInputHandler::GetActiveView()
2357 {
2358     UpdateActiveView();
2359     return pTopView ? pTopView : pTableView;
2360 }
2361 
2362 void ScInputHandler::ForgetLastPattern()
2363 {
2364     pLastPattern = nullptr;
2365     if ( !pLastState && pActiveViewSh )
2366         pActiveViewSh->UpdateInputHandler( true ); // Get status again
2367     else
2368         NotifyChange( pLastState.get(), true );
2369 }
2370 
2371 void ScInputHandler::UpdateAdjust( sal_Unicode cTyped )
2372 {
2373     SvxAdjust eSvxAdjust;
2374     switch (eAttrAdjust)
2375     {
2376         case SvxCellHorJustify::Standard:
2377             {
2378                 bool bNumber = false;
2379                 if (cTyped)                                     // Restarted
2380                     bNumber = (cTyped>='0' && cTyped<='9');     // Only ciphers are numbers
2381                 else if ( pActiveViewSh )
2382                 {
2383                     ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2384                     bNumber = ( rDoc.GetCellType( aCursorPos ) == CELLTYPE_VALUE );
2385                 }
2386                 eSvxAdjust = bNumber ? SvxAdjust::Right : SvxAdjust::Left;
2387             }
2388             break;
2389         case SvxCellHorJustify::Block:
2390             eSvxAdjust = SvxAdjust::Block;
2391             break;
2392         case SvxCellHorJustify::Center:
2393             eSvxAdjust = SvxAdjust::Center;
2394             break;
2395         case SvxCellHorJustify::Right:
2396             eSvxAdjust = SvxAdjust::Right;
2397             break;
2398         default:    // SvxCellHorJustify::Left
2399             eSvxAdjust = SvxAdjust::Left;
2400             break;
2401     }
2402 
2403     bool bAsianVertical = pLastPattern &&
2404         pLastPattern->GetItem( ATTR_STACKED ).GetValue() &&
2405         pLastPattern->GetItem( ATTR_VERTICAL_ASIAN ).GetValue();
2406     if ( bAsianVertical )
2407     {
2408         // Always edit at top of cell -> LEFT when editing vertically
2409         eSvxAdjust = SvxAdjust::Left;
2410     }
2411 
2412     pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
2413     mpEditEngine->SetDefaults( *pEditDefaults );
2414 
2415     if ( pActiveViewSh )
2416     {
2417         pActiveViewSh->GetViewData().SetEditAdjust( eSvxAdjust );
2418     }
2419     mpEditEngine->SetVertical( bAsianVertical );
2420 }
2421 
2422 void ScInputHandler::RemoveAdjust()
2423 {
2424     // Delete hard alignment attributes
2425     bool bUndo = mpEditEngine->IsUndoEnabled();
2426     if ( bUndo )
2427         mpEditEngine->EnableUndo( false );
2428 
2429     // Non-default paragraph attributes (e.g. from clipboard)
2430     // must be turned into character attributes
2431     mpEditEngine->RemoveParaAttribs();
2432 
2433     if ( bUndo )
2434         mpEditEngine->EnableUndo( true );
2435 
2436 }
2437 
2438 void ScInputHandler::RemoveRangeFinder()
2439 {
2440     // Delete pRangeFindList and colors
2441     mpEditEngine->SetUpdateLayout(false);
2442     sal_Int32 nCount = mpEditEngine->GetParagraphCount(); // Could just have been inserted
2443     for (sal_Int32 i=0; i<nCount; i++)
2444         mpEditEngine->RemoveCharAttribs( i, EE_CHAR_COLOR );
2445     mpEditEngine->SetUpdateLayout(true);
2446 
2447     EditView* pActiveView = pTopView ? pTopView : pTableView;
2448     pActiveView->ShowCursor( false );
2449 
2450     DeleteRangeFinder(); // Deletes the list and the labels on the table
2451 }
2452 
2453 bool ScInputHandler::StartTable( sal_Unicode cTyped, bool bFromCommand, bool bInputActivated,
2454         ScEditEngineDefaulter* pTopEngine )
2455 {
2456     bool bNewTable = false;
2457 
2458     if (bModified)
2459         return false;
2460 
2461     if (pActiveViewSh)
2462     {
2463         ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2464 
2465         if (!rDoc.ValidCol(aCursorPos.Col()))
2466             return false;
2467 
2468         ImplCreateEditEngine();
2469         UpdateActiveView();
2470         SyncViews();
2471 
2472 
2473         const ScMarkData& rMark = pActiveViewSh->GetViewData().GetMarkData();
2474         ScEditableTester aTester;
2475         if ( rMark.IsMarked() || rMark.IsMultiMarked() )
2476             aTester.TestSelection( rDoc, rMark );
2477         else
2478             aTester.TestSelectedBlock(
2479                 rDoc, aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Col(), aCursorPos.Row(), rMark );
2480 
2481         bool bStartInputMode = true;
2482 
2483         if (!aTester.IsEditable())
2484         {
2485             bProtected = true;
2486             // We allow read-only input mode activation regardless
2487             // whether it's part of an array or not or whether explicit cell
2488             // activation is requested (double-click or F2) or a click in input
2489             // line.
2490             bool bShowError = (!bInputActivated || !aTester.GetMessageId() || aTester.GetMessageId() != STR_PROTECTIONERR) &&
2491                 !pActiveViewSh->GetViewData().GetDocShell()->IsReadOnly();
2492             if (bShowError)
2493             {
2494                 eMode = SC_INPUT_NONE;
2495                 StopInputWinEngine( true );
2496                 UpdateFormulaMode();
2497                 if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) )
2498                 {
2499                     //  Prevent repeated error messages for the same cell from command events
2500                     //  (for keyboard events, multiple messages are wanted).
2501                     //  Set the flag before showing the error message because the command handler
2502                     //  for the next IME command may be called when showing the dialog.
2503                     if ( bFromCommand )
2504                         bCommandErrorShown = true;
2505 
2506                     pActiveViewSh->GetActiveWin()->GrabFocus();
2507                     pActiveViewSh->ErrorMessage(aTester.GetMessageId());
2508                 }
2509                 bStartInputMode = false;
2510             }
2511         }
2512 
2513         if (bStartInputMode)
2514         {
2515             // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise)
2516             mpEditEngine->SetUpdateLayout( false );
2517 
2518             // Take over attributes in EditEngine
2519             const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(),
2520                                                               aCursorPos.Row(),
2521                                                               aCursorPos.Tab() );
2522             if (pPattern != pLastPattern)
2523             {
2524                 // Percent format?
2525                 const SfxItemSet& rAttrSet = pPattern->GetItemSet();
2526 
2527                 if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
2528                 {
2529                     sal_uInt32 nFormat = pItem->GetValue();
2530                     if (SvNumFormatType::PERCENT == rDoc.GetFormatTable()->GetType( nFormat ))
2531                         nCellPercentFormatDecSep = rDoc.GetFormatTable()->GetFormatDecimalSep( nFormat).toChar();
2532                     else
2533                         nCellPercentFormatDecSep = 0;
2534                 }
2535                 else
2536                     nCellPercentFormatDecSep = 0; // Default: no percent
2537 
2538                 // Validity specified?
2539                 if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALIDDATA ) )
2540                     nValidation = pItem->GetValue();
2541                 else
2542                     nValidation = 0;
2543 
2544                 //  EditEngine Defaults
2545                 //  In no case SetParaAttribs, because the EditEngine might already
2546                 //  be filled (for Edit cells).
2547                 //  SetParaAttribs would change the content.
2548 
2549                 //! The SetDefaults is now (since MUST/src602
2550                 //! EditEngine changes) implemented as a SetParaAttribs.
2551                 //! Any problems?
2552 
2553                 pPattern->FillEditItemSet( pEditDefaults.get() );
2554                 mpEditEngine->SetDefaults( *pEditDefaults );
2555                 pLastPattern = pPattern;
2556                 bLastIsSymbol = pPattern->IsSymbolFont();
2557 
2558                 //  Background color must be known for automatic font color.
2559                 //  For transparent cell background, the document background color must be used.
2560 
2561                 Color aBackCol = pPattern->GetItem( ATTR_BACKGROUND ).GetColor();
2562                 ScModule* pScMod = SC_MOD();
2563                 if ( aBackCol.IsTransparent() ||
2564                         Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2565                     aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
2566                 mpEditEngine->SetBackgroundColor( aBackCol );
2567 
2568                 // Adjustment
2569                 eAttrAdjust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
2570                 if ( eAttrAdjust == SvxCellHorJustify::Repeat &&
2571                      pPattern->GetItem(ATTR_LINEBREAK).GetValue() )
2572                 {
2573                     // #i31843# "repeat" with "line breaks" is treated as default alignment
2574                     eAttrAdjust = SvxCellHorJustify::Standard;
2575                 }
2576             }
2577 
2578             if (pTopEngine)
2579             {
2580                 // Necessary to sync SvxAutoCorrect behavior. This has to be
2581                 // done before InitRangeFinder() below.
2582                 MergeLanguageAttributes( *pTopEngine);
2583             }
2584 
2585             //  UpdateSpellSettings enables online spelling if needed
2586             //  -> also call if attributes are unchanged
2587             UpdateSpellSettings( true ); // uses pLastPattern
2588 
2589             // Fill EditEngine
2590             OUString aStr;
2591             if (bTextValid)
2592             {
2593                 mpEditEngine->SetTextCurrentDefaults(aCurrentText);
2594                 aStr = aCurrentText;
2595                 bTextValid = false;
2596                 aCurrentText.clear();
2597             }
2598             else
2599                 aStr = GetEditText(mpEditEngine.get());
2600 
2601             // cTyped!=0 is overtyping, not editing.
2602             mbEditingExistingContent = !cTyped && !aStr.isEmpty();
2603 
2604             if (aStr.startsWith("{=") && aStr.endsWith("}") )  // Matrix formula?
2605             {
2606                 aStr = aStr.copy(1, aStr.getLength() -2);
2607                 mpEditEngine->SetTextCurrentDefaults(aStr);
2608                 if ( pInputWin )
2609                     pInputWin->SetTextString(aStr);
2610             }
2611 
2612             UpdateAdjust( cTyped );
2613 
2614             if ( SC_MOD()->GetAppOptions().GetAutoComplete() )
2615                 GetColData();
2616 
2617             if (!cTyped && !bCreatingFuncView && StartsLikeFormula(aStr))
2618                 InitRangeFinder(aStr); // Formula is being edited -> RangeFinder
2619 
2620             bNewTable = true; // -> PostEditView Call
2621         }
2622     }
2623 
2624     if (!bProtected && pInputWin)
2625         pInputWin->SetOkCancelMode();
2626 
2627     return bNewTable;
2628 }
2629 
2630 void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter& rDestEngine ) const
2631 {
2632     const SfxItemSet& rSrcSet = mpEditEngine->GetDefaults();
2633     rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE ));
2634     rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CJK ));
2635     rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CTL ));
2636 }
2637 
2638 static void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel )
2639 {
2640     OSL_ENSURE( rSel.nStartPara==0 && rSel.nEndPara==0, "SetTopSelection: Para != 0" );
2641 
2642     EditEngine* pEngine = pEditView->GetEditEngine();
2643     sal_Int32 nCount = pEngine->GetParagraphCount();
2644     if (nCount > 1)
2645     {
2646         sal_Int32 nParLen = pEngine->GetTextLen(rSel.nStartPara);
2647         while (rSel.nStartPos > nParLen && rSel.nStartPara+1 < nCount)
2648         {
2649             rSel.nStartPos -= nParLen + 1; // Including space from line break
2650             nParLen = pEngine->GetTextLen(++rSel.nStartPara);
2651         }
2652 
2653         nParLen = pEngine->GetTextLen(rSel.nEndPara);
2654         while (rSel.nEndPos > nParLen && rSel.nEndPara+1 < nCount)
2655         {
2656             rSel.nEndPos -= nParLen + 1; // Including space from line break
2657             nParLen = pEngine->GetTextLen(++rSel.nEndPara);
2658         }
2659     }
2660 
2661     ESelection aSel = pEditView->GetSelection();
2662 
2663     if (   rSel.nStartPara != aSel.nStartPara || rSel.nEndPara != aSel.nEndPara
2664         || rSel.nStartPos  != aSel.nStartPos  || rSel.nEndPos  != aSel.nEndPos )
2665         pEditView->SetSelection( rSel );
2666 }
2667 
2668 void ScInputHandler::SyncViews( const EditView* pSourceView )
2669 {
2670     if (pSourceView)
2671     {
2672         bool bSelectionForTopView = false;
2673         if (pTopView && pTopView != pSourceView)
2674             bSelectionForTopView = true;
2675         bool bSelectionForTableView = false;
2676         if (pTableView && pTableView != pSourceView)
2677             bSelectionForTableView = true;
2678         if (bSelectionForTopView || bSelectionForTableView)
2679         {
2680             ESelection aSel(pSourceView->GetSelection());
2681             if (bSelectionForTopView)
2682                 pTopView->SetSelection(aSel);
2683             if (bSelectionForTableView)
2684                 lcl_SetTopSelection(pTableView, aSel);
2685         }
2686     }
2687     // Only sync selection from topView if we are actually editing there
2688     else if (pTopView && pTableView)
2689     {
2690         ESelection aSel(pTopView->GetSelection());
2691         lcl_SetTopSelection( pTableView, aSel );
2692     }
2693 }
2694 
2695 IMPL_LINK_NOARG(ScInputHandler, ModifyHdl, LinkParamNone*, void)
2696 {
2697     if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) &&
2698          mpEditEngine && mpEditEngine->IsUpdateLayout() && pInputWin )
2699     {
2700         // Update input line from ModifyHdl for changes that are not
2701         // wrapped by DataChanging/DataChanged calls (like Drag&Drop)
2702         OUString aText(ScEditUtil::GetMultilineString(*mpEditEngine));
2703         lcl_RemoveTabs(aText);
2704         pInputWin->SetTextString(aText);
2705     }
2706 }
2707 
2708 /**
2709  * @return true means new view created
2710  */
2711 bool ScInputHandler::DataChanging( sal_Unicode cTyped, bool bFromCommand )
2712 {
2713     if (pActiveViewSh)
2714         pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
2715     bInOwnChange = true; // disable ModifyHdl (reset in DataChanged)
2716 
2717     if ( eMode == SC_INPUT_NONE )
2718         return StartTable( cTyped, bFromCommand, false, nullptr );
2719     else
2720         return false;
2721 }
2722 
2723 void ScInputHandler::DataChanged( bool bFromTopNotify, bool bSetModified )
2724 {
2725     ImplCreateEditEngine();
2726 
2727     if (eMode==SC_INPUT_NONE)
2728         eMode = SC_INPUT_TYPE;
2729 
2730     if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify )
2731     {
2732         //  table EditEngine is formatted below, input line needs formatting after paste
2733         //  #i20282# not when called from the input line's modify handler
2734         pTopView->GetEditEngine()->QuickFormatDoc( true );
2735 
2736         //  #i23720# QuickFormatDoc hides the cursor, but can't show it again because it
2737         //  can't safely access the EditEngine's current view, so the cursor has to be
2738         //  shown again here.
2739         pTopView->ShowCursor();
2740     }
2741 
2742     if (bSetModified)
2743         bModified = true;
2744     bSelIsRef = false;
2745 
2746     if ( pRangeFindList && !bInRangeUpdate )
2747         RemoveRangeFinder(); // Delete attributes and labels
2748 
2749     UpdateParenthesis(); // Highlight parentheses anew
2750 
2751     if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE)
2752     {
2753         OUString aText;
2754         if (pInputWin)
2755             aText = ScEditUtil::GetMultilineString(*mpEditEngine);
2756         else
2757             aText = GetEditText(mpEditEngine.get());
2758         lcl_RemoveTabs(aText);
2759 
2760         if ( pInputWin )
2761             pInputWin->SetTextString( aText );
2762 
2763         if (comphelper::LibreOfficeKit::isActive())
2764         {
2765             if (pActiveViewSh)
2766             {
2767                 // TODO: deprecated?
2768                 pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8());
2769             }
2770         }
2771     }
2772 
2773     // If the cursor is before the end of a paragraph, parts are being pushed to
2774     // the right (independently from the eMode) -> Adapt View!
2775     // If the cursor is at the end, the StatusHandler of the ViewData is sufficient.
2776     //
2777     // First make sure the status handler is called now if the cursor
2778     // is outside the visible area
2779     mpEditEngine->QuickFormatDoc();
2780 
2781     EditView* pActiveView = pTopView ? pTopView : pTableView;
2782     ESelection aSel;
2783     if (pActiveView && pActiveViewSh)
2784     {
2785         ScViewData& rViewData = pActiveViewSh->GetViewData();
2786 
2787         bool bNeedGrow = ( rViewData.GetEditAdjust() != SvxAdjust::Left ); // Always right-aligned
2788         if (!bNeedGrow)
2789         {
2790             // Cursor before the end?
2791             aSel = pActiveView->GetSelection();
2792             aSel.Adjust();
2793             bNeedGrow = ( aSel.nEndPos != mpEditEngine->GetTextLen(aSel.nEndPara) );
2794         }
2795         if (!bNeedGrow)
2796         {
2797             bNeedGrow = rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() );
2798         }
2799         if (bNeedGrow)
2800         {
2801             // Adjust inplace view
2802             rViewData.EditGrowY();
2803             rViewData.EditGrowX();
2804         }
2805     }
2806 
2807     if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh && pInputWin)
2808     {
2809         UpdateActiveView();
2810         if (pActiveView)
2811             aSel = pActiveView->GetSelection();
2812 
2813         ScInputHandler::LOKSendFormulabarUpdate(pActiveView, pActiveViewSh,
2814                                                 ScEditUtil::GetMultilineString(*mpEditEngine),
2815                                                 aSel);
2816     }
2817 
2818     UpdateFormulaMode();
2819     bTextValid = false; // Changes only in the EditEngine
2820     bInOwnChange = false;
2821 }
2822 
2823 bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr ) const
2824 {
2825     // For new input '+' and '-' may start the dreaded "lazy data typist"
2826     // formula input, editing existing formula content can only start with '='.
2827     return !rStr.empty() && (rStr[0] == '=' || (!mbEditingExistingContent && (rStr[0] == '+' || rStr[0] == '-')));
2828 }
2829 
2830 void ScInputHandler::UpdateFormulaMode()
2831 {
2832     SfxApplication* pSfxApp = SfxGetpApp();
2833 
2834     bool bIsFormula = !bProtected;
2835     if (bIsFormula)
2836     {
2837         const OUString& rText = mpEditEngine->GetText(0);
2838         bIsFormula = StartsLikeFormula(rText);
2839     }
2840 
2841     if ( bIsFormula )
2842     {
2843         if (!bFormulaMode)
2844         {
2845             bFormulaMode = true;
2846             pRefViewSh = pActiveViewSh;
2847             pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2848             ScModule* pMod = SC_MOD();
2849             pMod->SetRefInputHdl(this);
2850             if (pInputWin)
2851                 pInputWin->SetFormulaMode(true);
2852 
2853             // in LOK, we always need to perform the GetFormulaData() call so
2854             // that the formula insertion works
2855             if (comphelper::LibreOfficeKit::isActive() || pMod->GetAppOptions().GetAutoComplete())
2856                 GetFormulaData();
2857 
2858             UpdateParenthesis();
2859             UpdateAutoCorrFlag();
2860         }
2861     }
2862     else // Deactivate
2863     {
2864         if (bFormulaMode)
2865         {
2866             ShowRefFrame();
2867             bFormulaMode = false;
2868             pRefViewSh = nullptr;
2869             pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2870             SC_MOD()->SetRefInputHdl(nullptr);
2871             if (pInputWin)
2872                 pInputWin->SetFormulaMode(false);
2873             UpdateAutoCorrFlag();
2874         }
2875     }
2876 }
2877 
2878 void ScInputHandler::ShowRefFrame()
2879 {
2880     // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat
2881     // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh.
2882     // A local variable is used instead.
2883     ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current()  );
2884     if ( !(pRefViewSh && pRefViewSh != pVisibleSh) )
2885         return;
2886 
2887     bool bFound = false;
2888     SfxViewFrame& rRefFrame = pRefViewSh->GetViewFrame();
2889     SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst();
2890     while ( pOneFrame && !bFound )
2891     {
2892         if ( pOneFrame == &rRefFrame )
2893             bFound = true;
2894         pOneFrame = SfxViewFrame::GetNext( *pOneFrame );
2895     }
2896 
2897     if (bFound)
2898     {
2899         // We count on Activate working synchronously here
2900         // (pActiveViewSh is set while doing so)
2901         pRefViewSh->SetActive(); // Appear and SetViewFrame
2902 
2903         //  pLastState is set correctly in the NotifyChange from the Activate
2904     }
2905     else
2906     {
2907         OSL_FAIL("ViewFrame for reference input is not here anymore");
2908     }
2909 }
2910 
2911 void ScInputHandler::RemoveSelection()
2912 {
2913     EditView* pActiveView = pTopView ? pTopView : pTableView;
2914     if (!pActiveView)
2915         return;
2916 
2917     ESelection aSel = pActiveView->GetSelection();
2918     aSel.nStartPara = aSel.nEndPara;
2919     aSel.nStartPos  = aSel.nEndPos;
2920     if (pTableView)
2921         pTableView->SetSelection( aSel );
2922     if (pTopView)
2923         pTopView->SetSelection( aSel );
2924 }
2925 
2926 void ScInputHandler::InvalidateAttribs()
2927 {
2928     SfxViewFrame* pViewFrm = SfxViewFrame::Current();
2929     if (!pViewFrm)
2930         return;
2931 
2932     SfxBindings& rBindings = pViewFrm->GetBindings();
2933 
2934     rBindings.Invalidate( SID_ATTR_CHAR_FONT );
2935     rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
2936     rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
2937 
2938     rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
2939     rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
2940     rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
2941     rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
2942     rBindings.Invalidate( SID_ULINE_VAL_NONE );
2943     rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
2944     rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
2945     rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
2946 
2947     rBindings.Invalidate( SID_HYPERLINK_GETLINK );
2948 
2949     rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
2950     rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
2951     rBindings.Invalidate( SID_SET_SUB_SCRIPT );
2952     rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
2953     rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
2954 
2955     rBindings.Invalidate( SID_SAVEDOC );
2956     rBindings.Invalidate( SID_DOC_MODIFIED );
2957 }
2958 
2959 // --------------- public methods --------------------------------------------
2960 
2961 void ScInputHandler::SetMode( ScInputMode eNewMode, const OUString* pInitText, ScEditEngineDefaulter* pTopEngine )
2962 {
2963     if ( eMode == eNewMode )
2964         return;
2965 
2966     ImplCreateEditEngine();
2967 
2968     if (bProtected)
2969     {
2970         eMode = SC_INPUT_NONE;
2971         StopInputWinEngine( true );
2972         if (pActiveViewSh)
2973             pActiveViewSh->GetActiveWin()->GrabFocus();
2974         return;
2975     }
2976 
2977     if (eNewMode != SC_INPUT_NONE && pActiveViewSh)
2978         // Disable paste mode when edit mode starts.
2979         pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
2980 
2981     bInOwnChange = true; // disable ModifyHdl (reset below)
2982 
2983     ScInputMode eOldMode = eMode;
2984     eMode = eNewMode;
2985     if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode)
2986         StopInputWinEngine( false );
2987 
2988     if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE)
2989     {
2990         if (eOldMode == SC_INPUT_NONE) // not if switching between modes
2991         {
2992             if (StartTable(0, false, eMode == SC_INPUT_TABLE, pTopEngine))
2993             {
2994                 if (pActiveViewSh)
2995                     pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
2996             }
2997         }
2998 
2999         if (pInitText)
3000         {
3001             mpEditEngine->SetTextCurrentDefaults(*pInitText);
3002             bModified = true;
3003         }
3004 
3005         sal_Int32 nPara = mpEditEngine->GetParagraphCount()-1;
3006         sal_Int32 nLen = mpEditEngine->GetText(nPara).getLength();
3007         sal_uInt16 nCount = mpEditEngine->GetViewCount();
3008 
3009         for (sal_uInt16 i=0; i<nCount; i++)
3010         {
3011             if ( eMode == SC_INPUT_TABLE && eOldMode == SC_INPUT_TOP )
3012             {
3013                 // Keep Selection
3014             }
3015             else
3016             {
3017                 mpEditEngine->GetView(i)->
3018                     SetSelection( ESelection( nPara, nLen, nPara, nLen ) );
3019             }
3020             mpEditEngine->GetView(i)->ShowCursor(false);
3021         }
3022     }
3023 
3024     UpdateActiveView();
3025     if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE)
3026     {
3027         if (pTableView)
3028             pTableView->SetEditEngineUpdateLayout(true);
3029     }
3030     else
3031     {
3032         if (pTopView)
3033             pTopView->SetEditEngineUpdateLayout(true);
3034     }
3035 
3036     if (eNewMode != eOldMode)
3037         UpdateFormulaMode();
3038 
3039     bInOwnChange = false;
3040 }
3041 
3042 /**
3043  * @return true if rString only contains digits (no autocorrect then)
3044  */
3045 static bool lcl_IsNumber(std::u16string_view aString)
3046 {
3047     size_t nLen = aString.size();
3048     for (size_t i=0; i<nLen; i++)
3049     {
3050         sal_Unicode c = aString[i];
3051         if ( c < '0' || c > '9' )
3052             return false;
3053     }
3054     return true;
3055 }
3056 
3057 static void lcl_SelectionToEnd( EditView* pView )
3058 {
3059     if ( pView )
3060     {
3061         EditEngine* pEngine = pView->GetEditEngine();
3062         sal_Int32 nParCnt = pEngine->GetParagraphCount();
3063         if ( nParCnt == 0 )
3064             nParCnt = 1;
3065         ESelection aSel( nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); // empty selection, cursor at the end
3066         pView->SetSelection( aSel );
3067     }
3068 }
3069 
3070 void ScInputHandler::EnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK )
3071 {
3072     if (!mbDocumentDisposing && comphelper::LibreOfficeKit::isActive()
3073         && pActiveViewSh != SfxViewShell::Current())
3074         return;
3075 
3076     if (!pActiveViewSh)
3077         return;
3078 
3079     // Macro calls for validity can cause a lot of problems, so inhibit
3080     // nested calls of EnterHandler().
3081     if (bInEnterHandler) return;
3082     bInEnterHandler = true;
3083     bInOwnChange = true; // disable ModifyHdl (reset below)
3084     mbPartialPrefix = false;
3085 
3086     ImplCreateEditEngine();
3087 
3088     bool bMatrix = ( nBlockMode == ScEnterMode::MATRIX );
3089 
3090     SfxApplication* pSfxApp     = SfxGetpApp();
3091     std::unique_ptr<EditTextObject> pObject;
3092     std::unique_ptr<ScPatternAttr> pCellAttrs;
3093     bool            bForget     = false; // Remove due to validity?
3094 
3095     OUString aString = GetEditText(mpEditEngine.get());
3096     OUString aPreAutoCorrectString(aString);
3097     EditView* pActiveView = pTopView ? pTopView : pTableView;
3098     if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString))
3099     {
3100         if (pColumnData && miAutoPosColumn != pColumnData->end())
3101         {
3102             // #i47125# If AutoInput appended something, do the final AutoCorrect
3103             // with the cursor at the end of the input.
3104             lcl_SelectionToEnd(pTopView);
3105             lcl_SelectionToEnd(pTableView);
3106         }
3107 
3108         vcl::Window* pFrameWin = pActiveViewSh->GetFrameWin();
3109 
3110         if (pTopView)
3111             pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views
3112         if (pTableView)
3113             pTableView->CompleteAutoCorrect(pFrameWin);
3114         aString = GetEditText(mpEditEngine.get());
3115     }
3116     lcl_RemoveTabs(aString);
3117     lcl_RemoveTabs(aPreAutoCorrectString);
3118 
3119     // Test if valid (always with simple string)
3120     if (bModified && nValidation)
3121     {
3122         ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3123         const ScValidationData* pData = rDoc.GetValidationEntry( nValidation );
3124         if (pData && pData->HasErrMsg())
3125         {
3126             // #i67990# don't use pLastPattern in EnterHandler
3127             const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
3128 
3129             bool bOk;
3130 
3131             if (pData->GetDataMode() == SC_VALID_CUSTOM)
3132             {
3133                 bOk = pData->IsDataValidCustom( aString, *pPattern, aCursorPos,  ScValidationData::CustomValidationPrivateAccess() );
3134             }
3135             else
3136             {
3137                 bOk = pData->IsDataValid( aString, *pPattern, aCursorPos );
3138             }
3139 
3140             if (!bOk)
3141             {
3142                 pActiveViewSh->StopMarking();   // (the InfoBox consumes the MouseButtonUp)
3143 
3144                 // tdf#125917 Release the grab that a current mouse-down event being handled
3145                 // by ScTabView has put on the mouse via its SelectionEngine.
3146                 // Otherwise the warning box cannot interact with the mouse
3147                 if (ScTabView* pView = pActiveViewSh->GetViewData().GetView())
3148                 {
3149                     if (ScViewSelectionEngine* pSelEngine = pView->GetSelEngine())
3150                         pSelEngine->ReleaseMouse();
3151                 }
3152 
3153                 if (bBeforeSavingInLOK)
3154                 {
3155                     // Invalid entry but not applied to the document model.
3156                     // Exit to complete the "save", leaving the edit view as it is
3157                     // for the user to continue after save.
3158                     bInOwnChange = false;
3159                     bInEnterHandler = false;
3160                     return;
3161                 }
3162 
3163                 if (pData->DoError(pActiveViewSh->GetFrameWeld(), aString, aCursorPos))
3164                     bForget = true;                 // Do not take over input
3165             }
3166         }
3167     }
3168 
3169     // Check for input into DataPilot table
3170     if ( bModified && !bForget )
3171     {
3172         ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3173         ScDPObject* pDPObj = rDoc.GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
3174         if ( pDPObj )
3175         {
3176             // Any input within the DataPilot table is either a valid renaming
3177             // or an invalid action - normal cell input is always aborted
3178             pActiveViewSh->DataPilotInput( aCursorPos, aString );
3179             bForget = true;
3180         }
3181     }
3182 
3183     std::vector<editeng::MisspellRanges> aMisspellRanges;
3184     // UpdateLayout must be true during CompleteOnlineSpelling
3185     const bool bUpdateLayout = mpEditEngine->SetUpdateLayout( true );
3186     mpEditEngine->CompleteOnlineSpelling();
3187     bool bSpellErrors = !bFormulaMode && mpEditEngine->HasOnlineSpellErrors();
3188     if ( bSpellErrors )
3189     {
3190         //  #i3820# If the spell checker flags numerical input as error,
3191         //  it still has to be treated as number, not EditEngine object.
3192         ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3193         // #i67990# don't use pLastPattern in EnterHandler
3194         const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
3195         if (pPattern)
3196         {
3197             SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
3198             // without conditional format, as in ScColumn::SetString
3199             sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
3200             double nVal;
3201             if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) )
3202             {
3203                 bSpellErrors = false;       // ignore the spelling errors
3204             }
3205         }
3206     }
3207 
3208     //  After RemoveAdjust, the EditView must not be repainted (has wrong font size etc).
3209     //  SetUpdateLayout must come after CompleteOnlineSpelling.
3210     //  The view is hidden in any case below (Broadcast).
3211     mpEditEngine->SetUpdateLayout( false );
3212 
3213     if ( bModified && !bForget ) // What is being entered (text/object)?
3214     {
3215         sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
3216         if ( nParCnt == 0 )
3217             nParCnt = 1;
3218 
3219         bool bUniformAttribs = true;
3220         SfxItemSet aPara1Attribs = mpEditEngine->GetAttribs(0, 0, mpEditEngine->GetTextLen(0));
3221         for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara)
3222         {
3223             SfxItemSet aPara2Attribs = mpEditEngine->GetAttribs(nPara, 0, mpEditEngine->GetTextLen(nPara));
3224             if (!(aPara1Attribs == aPara2Attribs))
3225             {
3226                 // Paragraph format different from that of the 1st paragraph.
3227                 bUniformAttribs = false;
3228                 break;
3229             }
3230         }
3231 
3232         ESelection aSel( 0, 0, nParCnt-1, mpEditEngine->GetTextLen(nParCnt-1) );
3233         SfxItemSet aOldAttribs = mpEditEngine->GetAttribs( aSel );
3234         const SfxPoolItem* pItem = nullptr;
3235 
3236         // Find common (cell) attributes before RemoveAdjust
3237         if ( bUniformAttribs )
3238         {
3239             std::optional<SfxItemSet> pCommonAttrs;
3240             for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++)
3241             {
3242                 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
3243                 if ( eState == SfxItemState::SET &&
3244                         nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING &&
3245                         nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS &&
3246                             *pItem != pEditDefaults->Get(nId) )
3247                 {
3248                     if ( !pCommonAttrs )
3249                         pCommonAttrs.emplace( mpEditEngine->GetEmptyItemSet() );
3250                     pCommonAttrs->Put( *pItem );
3251                 }
3252             }
3253 
3254             if ( pCommonAttrs )
3255             {
3256                 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3257                 pCellAttrs = std::make_unique<ScPatternAttr>(rDoc.GetPool());
3258                 pCellAttrs->GetFromEditItemSet( &*pCommonAttrs );
3259             }
3260         }
3261 
3262         // Clear ParaAttribs (including adjustment)
3263         RemoveAdjust();
3264 
3265         bool bAttrib = false; // Formatting present?
3266 
3267         //  check if EditObject is needed
3268         if (nParCnt > 1)
3269             bAttrib = true;
3270         else
3271         {
3272             for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++)
3273             {
3274                 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
3275                 if (eState == SfxItemState::DONTCARE)
3276                     bAttrib = true;
3277                 else if (eState == SfxItemState::SET)
3278                 {
3279                     // Keep same items in EditEngine as in ScEditAttrTester
3280                     if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING ||
3281                          nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS )
3282                     {
3283                         if ( *pItem != pEditDefaults->Get(nId) )
3284                             bAttrib = true;
3285                     }
3286                 }
3287             }
3288 
3289             // Contains fields?
3290             SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, false );
3291             if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
3292                 bAttrib = true;
3293 
3294             // Not converted characters?
3295             SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, false );
3296             if ( eConvState == SfxItemState::DONTCARE || eConvState == SfxItemState::SET )
3297                 bAttrib = true;
3298 
3299             // Always recognize formulas as formulas
3300             // We still need the preceding test due to cell attributes
3301         }
3302 
3303         if (bSpellErrors)
3304             mpEditEngine->GetAllMisspellRanges(aMisspellRanges);
3305 
3306         if (bMatrix)
3307             bAttrib = false;
3308 
3309         if (bAttrib)
3310         {
3311             mpEditEngine->ClearSpellErrors();
3312             pObject = mpEditEngine->CreateTextObject();
3313         }
3314         else if (SC_MOD()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case
3315         {
3316             // Perform case-matching only when the typed text is partial.
3317             if (pColumnData && aAutoSearch.getLength() < aString.getLength())
3318                 aString = getExactMatch(*pColumnData, aString);
3319         }
3320     }
3321 
3322     // Don't rely on ShowRefFrame switching the active view synchronously
3323     // execute the function directly on the correct view's bindings instead
3324     // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3325     ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
3326 
3327     if (bFormulaMode)
3328     {
3329         ShowRefFrame();
3330 
3331         if (pExecuteSh)
3332         {
3333             pExecuteSh->SetTabNo(aCursorPos.Tab());
3334             pExecuteSh->ActiveGrabFocus();
3335         }
3336 
3337         bFormulaMode = false;
3338         pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
3339         SC_MOD()->SetRefInputHdl(nullptr);
3340         if (pInputWin)
3341             pInputWin->SetFormulaMode(false);
3342         UpdateAutoCorrFlag();
3343     }
3344     pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3345     DeleteRangeFinder();
3346     ResetAutoPar();
3347 
3348     bool bOldMod = bModified;
3349 
3350     bModified = false;
3351     bSelIsRef = false;
3352     eMode     = SC_INPUT_NONE;
3353     StopInputWinEngine(true);
3354 
3355     // Text input (through number formats) or ApplySelectionPattern modify
3356     // the cell's attributes, so pLastPattern is no longer valid
3357     pLastPattern = nullptr;
3358 
3359     if (bOldMod && !bProtected && !bForget)
3360     {
3361         bool bInsertPreCorrectedString = true;
3362         // No typographic quotes in formulas
3363         if (aString.startsWith("="))
3364         {
3365             SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get().GetAutoCorrect();
3366             if ( pAuto )
3367             {
3368                 bInsertPreCorrectedString = false;
3369                 OUString aReplace(pAuto->GetStartDoubleQuote());
3370                 if( aReplace.isEmpty() )
3371                     aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkStart();
3372                 if( aReplace != "\"" )
3373                     aString = aString.replaceAll( aReplace, "\"" );
3374 
3375                 aReplace = OUString(pAuto->GetEndDoubleQuote());
3376                 if( aReplace.isEmpty() )
3377                     aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkEnd();
3378                 if( aReplace != "\"" )
3379                     aString = aString.replaceAll( aReplace, "\"" );
3380 
3381                 aReplace = OUString(pAuto->GetStartSingleQuote());
3382                 if( aReplace.isEmpty() )
3383                     aReplace = ScGlobal::getLocaleData().getQuotationMarkStart();
3384                 if( aReplace != "'" )
3385                     aString = aString.replaceAll( aReplace, "'" );
3386 
3387                 aReplace = OUString(pAuto->GetEndSingleQuote());
3388                 if( aReplace.isEmpty() )
3389                     aReplace = ScGlobal::getLocaleData().getQuotationMarkEnd();
3390                 if( aReplace != "'" )
3391                     aString = aString.replaceAll( aReplace, "'");
3392             }
3393         }
3394 
3395         pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint ) );
3396 
3397         if ( pExecuteSh )
3398         {
3399             SfxBindings& rBindings = pExecuteSh->GetViewFrame().GetBindings();
3400 
3401             sal_uInt16 nId = FID_INPUTLINE_ENTER;
3402             if ( nBlockMode == ScEnterMode::BLOCK )
3403                 nId = FID_INPUTLINE_BLOCK;
3404             else if ( nBlockMode == ScEnterMode::MATRIX )
3405                 nId = FID_INPUTLINE_MATRIX;
3406 
3407             const SfxPoolItem* aArgs[2];
3408             aArgs[1] = nullptr;
3409 
3410             if ( bInsertPreCorrectedString && aString != aPreAutoCorrectString )
3411             {
3412                 ScInputStatusItem aItem(FID_INPUTLINE_STATUS,
3413                                        aCursorPos, aCursorPos, aCursorPos,
3414                                        aPreAutoCorrectString, pObject.get());
3415                 aArgs[0] = &aItem;
3416                 rBindings.Execute(nId, aArgs);
3417             }
3418 
3419             ScInputStatusItem aItemCorrected(FID_INPUTLINE_STATUS,
3420                                              aCursorPos, aCursorPos, aCursorPos,
3421                                              aString, pObject.get());
3422             if ( !aMisspellRanges.empty() )
3423                 aItemCorrected.SetMisspellRanges(&aMisspellRanges);
3424 
3425             aArgs[0] = &aItemCorrected;
3426             rBindings.Execute(nId, aArgs);
3427         }
3428 
3429         pLastState.reset(); // pLastState still contains the old text
3430     }
3431     else
3432         pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
3433 
3434     if ( bOldMod && pExecuteSh && pCellAttrs && !bForget )
3435     {
3436         // Combine with input?
3437         pExecuteSh->ApplySelectionPattern( *pCellAttrs, true );
3438         pExecuteSh->AdjustBlockHeight();
3439     }
3440 
3441     HideTip();
3442     HideTipBelow();
3443 
3444     nFormSelStart = nFormSelEnd = 0;
3445     aFormText.clear();
3446 
3447     mbEditingExistingContent = false;
3448     bInOwnChange = false;
3449     bInEnterHandler = false;
3450     if (bUpdateLayout)
3451         mpEditEngine->SetUpdateLayout( true );
3452 }
3453 
3454 void ScInputHandler::CancelHandler()
3455 {
3456     bInOwnChange = true; // Also without FormulaMode due to FunctionsAutoPilot
3457 
3458     ImplCreateEditEngine();
3459 
3460     bModified = false;
3461     mbPartialPrefix = false;
3462     mbEditingExistingContent = false;
3463 
3464     // Don't rely on ShowRefFrame switching the active view synchronously
3465     // execute the function directly on the correct view's bindings instead
3466     // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3467     ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
3468 
3469     if (bFormulaMode)
3470     {
3471         ShowRefFrame();
3472         if (pExecuteSh)
3473         {
3474             pExecuteSh->SetTabNo(aCursorPos.Tab());
3475             pExecuteSh->ActiveGrabFocus();
3476         }
3477         bFormulaMode = false;
3478         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
3479         SC_MOD()->SetRefInputHdl(nullptr);
3480         if (pInputWin)
3481             pInputWin->SetFormulaMode(false);
3482         UpdateAutoCorrFlag();
3483     }
3484     pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3485     DeleteRangeFinder();
3486     ResetAutoPar();
3487 
3488     eMode = SC_INPUT_NONE;
3489     StopInputWinEngine( true );
3490     SCCOL nMaxCol(MAXCOL);
3491     if (pExecuteSh)
3492     {
3493         pExecuteSh->StopEditShell();
3494         nMaxCol = pExecuteSh->GetViewData().GetDocument().MaxCol();
3495     }
3496 
3497     aCursorPos.Set(nMaxCol+1,0,0); // Invalid flag
3498     mpEditEngine->SetTextCurrentDefaults(OUString());
3499 
3500     if ( !pLastState && pExecuteSh )
3501         pExecuteSh->UpdateInputHandler( true );  // Update status again
3502     else
3503         NotifyChange( pLastState.get(), true );
3504 
3505     nFormSelStart = nFormSelEnd = 0;
3506     aFormText.clear();
3507 
3508     bInOwnChange = false;
3509 
3510     if ( comphelper::LibreOfficeKit::isActive() && pExecuteSh )
3511     {
3512         // Clear
3513         std::vector<ReferenceMark> aReferenceMarks;
3514         ScInputHandler::SendReferenceMarks( pActiveViewSh, aReferenceMarks );
3515     }
3516 }
3517 
3518 bool ScInputHandler::IsModalMode( const SfxObjectShell* pDocSh )
3519 {
3520     // References to unnamed document; that doesn't work
3521     return bFormulaMode && pRefViewSh
3522             && pRefViewSh->GetViewData().GetDocument().GetDocumentShell() != pDocSh
3523             && !pDocSh->HasName();
3524 }
3525 
3526 void ScInputHandler::AddRefEntry()
3527 {
3528     const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
3529     UpdateActiveView();
3530     if (!pTableView && !pTopView)
3531         return;                             // E.g. FillMode
3532 
3533     DataChanging();                         // Cannot be new
3534 
3535     RemoveSelection();
3536     OUString aText = GetEditText(mpEditEngine.get());
3537     sal_Unicode cLastChar = 0;
3538     sal_Int32 nPos = aText.getLength() - 1;
3539     while (nPos >= 0) //checking space
3540     {
3541         cLastChar = aText[nPos];
3542         if (cLastChar != ' ')
3543             break;
3544         --nPos;
3545     }
3546 
3547     bool bAppendSeparator = (cLastChar != '(' && cLastChar != cSep && cLastChar != '=');
3548     if (bAppendSeparator)
3549     {
3550         if (pTableView)
3551             pTableView->InsertText( OUString(cSep) );
3552         if (pTopView)
3553             pTopView->InsertText( OUString(cSep) );
3554     }
3555 
3556     DataChanged();
3557 }
3558 
3559 void ScInputHandler::SetReference( const ScRange& rRef, const ScDocument& rDoc )
3560 {
3561     HideTip();
3562 
3563     const ScDocument* pThisDoc = nullptr;
3564     if (pRefViewSh)
3565         pThisDoc = &pRefViewSh->GetViewData().GetDocument();
3566     bool bOtherDoc = (pThisDoc != &rDoc);
3567     if (bOtherDoc && !rDoc.GetDocumentShell()->HasName())
3568     {
3569         // References to unnamed document; that doesn't work
3570         // SetReference should not be called, then
3571         return;
3572     }
3573     if (!pThisDoc)
3574         pThisDoc = &rDoc;
3575 
3576     UpdateActiveView();
3577     if (!pTableView && !pTopView)
3578         return;                             // E.g. FillMode
3579 
3580     // Never overwrite the "="!
3581     EditView* pActiveView = pTopView ? pTopView : pTableView;
3582     ESelection aSel = pActiveView->GetSelection();
3583     aSel.Adjust();
3584     if ( aSel.nStartPara == 0 && aSel.nStartPos == 0 )
3585         return;
3586 
3587     DataChanging();                         // Cannot be new
3588 
3589     // Turn around selection if backwards.
3590     if (pTableView)
3591     {
3592         ESelection aTabSel = pTableView->GetSelection();
3593         if (aTabSel.nStartPos > aTabSel.nEndPos && aTabSel.nStartPara == aTabSel.nEndPara)
3594         {
3595             aTabSel.Adjust();
3596             pTableView->SetSelection(aTabSel);
3597         }
3598     }
3599     if (pTopView)
3600     {
3601         ESelection aTopSel = pTopView->GetSelection();
3602         if (aTopSel.nStartPos > aTopSel.nEndPos && aTopSel.nStartPara == aTopSel.nEndPara)
3603         {
3604             aTopSel.Adjust();
3605             pTopView->SetSelection(aTopSel);
3606         }
3607     }
3608 
3609     // Create string from reference, in the syntax of the document being edited.
3610     OUString aRefStr;
3611     const ScAddress::Details aAddrDetails( *pThisDoc, aCursorPos );
3612     if (bOtherDoc)
3613     {
3614         // Reference to other document
3615         OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab");
3616 
3617         // Always 3D and absolute.
3618         OUString aTmp(rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails));
3619 
3620         ScDocShell* pObjSh = rDoc.GetDocumentShell();
3621         // #i75893# convert escaped URL of the document to something user friendly
3622         OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
3623 
3624         switch(aAddrDetails.eConv)
3625         {
3626              case formula::FormulaGrammar::CONV_XL_A1 :
3627              case formula::FormulaGrammar::CONV_XL_OOX :
3628              case formula::FormulaGrammar::CONV_XL_R1C1 :
3629                          aRefStr = "[\'" + aFileName + "']";
3630                          break;
3631              case formula::FormulaGrammar::CONV_OOO :
3632              default:
3633                          aRefStr = "\'" + aFileName + "'#";
3634                          break;
3635         }
3636         aRefStr += aTmp;
3637     }
3638     else
3639     {
3640         if ( rRef.aStart.Tab() != aCursorPos.Tab() ||
3641              rRef.aStart.Tab() != rRef.aEnd.Tab() )
3642             // pointer-selected => absolute sheet reference
3643             aRefStr = rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails);
3644         else
3645             aRefStr = rRef.Format(rDoc, ScRefFlags::VALID, aAddrDetails);
3646     }
3647     bool bLOKShowSelect = true;
3648     if(comphelper::LibreOfficeKit::isActive() && pRefViewSh->GetViewData().GetRefTabNo() != pRefViewSh->GetViewData().GetTabNo())
3649         bLOKShowSelect = false;
3650 
3651     if (pTableView || pTopView)
3652     {
3653         if (pTableView)
3654             pTableView->InsertText( aRefStr, true, bLOKShowSelect );
3655         if (pTopView)
3656             pTopView->InsertText( aRefStr, true, bLOKShowSelect );
3657 
3658         DataChanged();
3659     }
3660 
3661     bSelIsRef = true;
3662 }
3663 
3664 void ScInputHandler::InsertFunction( const OUString& rFuncName, bool bAddPar )
3665 {
3666     if ( eMode == SC_INPUT_NONE )
3667     {
3668         OSL_FAIL("InsertFunction, not during input mode");
3669         return;
3670     }
3671 
3672     UpdateActiveView();
3673     if (!pTableView && !pTopView)
3674         return;                             // E.g. FillMode
3675 
3676     DataChanging();                         // Cannot be new
3677 
3678     OUString aText = rFuncName;
3679     if (bAddPar)
3680         aText += "()";
3681 
3682     if (pTableView)
3683     {
3684         pTableView->InsertText( aText );
3685         if (bAddPar)
3686         {
3687             ESelection aSel = pTableView->GetSelection();
3688             --aSel.nStartPos;
3689             --aSel.nEndPos;
3690             pTableView->SetSelection(aSel);
3691         }
3692     }
3693     if (pTopView)
3694     {
3695         pTopView->InsertText( aText );
3696         if (bAddPar)
3697         {
3698             ESelection aSel = pTopView->GetSelection();
3699             --aSel.nStartPos;
3700             --aSel.nEndPos;
3701             pTopView->SetSelection(aSel);
3702         }
3703     }
3704 
3705     DataChanged();
3706 
3707     if (bAddPar)
3708         AutoParAdded();
3709 }
3710 
3711 void ScInputHandler::ClearText()
3712 {
3713     if ( eMode == SC_INPUT_NONE )
3714     {
3715         OSL_FAIL("ClearText, not during input mode");
3716         return;
3717     }
3718 
3719     UpdateActiveView();
3720     if (!pTableView && !pTopView)
3721         return;                             // E.g. FillMode
3722 
3723     DataChanging();                         // Cannot be new
3724 
3725     if (pTableView)
3726     {
3727         pTableView->GetEditEngine()->SetText( "" );
3728         pTableView->SetSelection( ESelection(0,0, 0,0) );
3729     }
3730     if (pTopView)
3731     {
3732         pTopView->GetEditEngine()->SetText( "" );
3733         pTopView->SetSelection( ESelection(0,0, 0,0) );
3734     }
3735 
3736     DataChanged();
3737 }
3738 
3739 bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false */ )
3740 {
3741     vcl::KeyCode aCode = rKEvt.GetKeyCode();
3742     sal_uInt16 nModi  = aCode.GetModifier();
3743     bool bShift   = aCode.IsShift();
3744     bool bControl = aCode.IsMod1();
3745     bool bAlt     = aCode.IsMod2();
3746     sal_uInt16 nCode  = aCode.GetCode();
3747     sal_Unicode nChar = rKEvt.GetCharCode();
3748 
3749     if (bAlt && !bControl && nCode != KEY_RETURN)
3750         // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not.
3751         return false;
3752 
3753     // There is a partial autocomplete suggestion.
3754     // Allow its completion with right arrow key (without modifiers).
3755     if (mbPartialPrefix && nCode == KEY_RIGHT && !bControl && !bShift && !bAlt &&
3756         (pTopView || pTableView))
3757     {
3758         if (pTopView)
3759             pTopView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH));
3760         if (pTableView)
3761             pTableView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH));
3762 
3763         mbPartialPrefix = false;
3764 
3765         // Indicate that this event has been consumed and ScTabViewShell should not act on this.
3766         return true;
3767     }
3768 
3769     if (!bControl && nCode == KEY_TAB)
3770     {
3771         // Normal TAB moves the cursor right.
3772         EnterHandler();
3773 
3774         if (pActiveViewSh)
3775             pActiveViewSh->FindNextUnprot( bShift, true );
3776         return true;
3777     }
3778 
3779     bool bInputLine = ( eMode==SC_INPUT_TOP );
3780 
3781     bool bUsed = false;
3782     bool bSkip = false;
3783     bool bDoEnter = false;
3784 
3785     switch ( nCode )
3786     {
3787         case KEY_RETURN:
3788             // New line when in the input line and Shift/Ctrl-Enter is pressed,
3789             // or when in a cell and Ctrl-Enter is pressed.
3790             if ((pInputWin && bInputLine && bControl != bShift) || (!bInputLine && bControl && !bShift))
3791             {
3792                 bDoEnter = true;
3793             }
3794             else if (nModi == 0 && nTipVisible && pFormulaData && miAutoPosFormula != pFormulaData->end())
3795             {
3796                 PasteFunctionData();
3797                 bUsed = true;
3798             }
3799             else if ( nModi == 0 && nTipVisible && !aManualTip.isEmpty() )
3800             {
3801                 PasteManualTip();
3802                 bUsed = true;
3803             }
3804             else
3805             {
3806                 ScEnterMode nMode = ScEnterMode::NORMAL;
3807                 if ( bShift && bControl )
3808                     nMode = ScEnterMode::MATRIX;
3809                 else if ( bAlt )
3810                     nMode = ScEnterMode::BLOCK;
3811                 EnterHandler( nMode );
3812 
3813                 if (pActiveViewSh)
3814                     pActiveViewSh->MoveCursorEnter( bShift && !bControl );
3815 
3816                 bUsed = true;
3817             }
3818             break;
3819         case KEY_TAB:
3820             if (bControl && !bAlt)
3821             {
3822                 if (pFormulaData && nTipVisible && miAutoPosFormula != pFormulaData->end())
3823                 {
3824                     // Iterate
3825                     NextFormulaEntry( bShift );
3826                     bUsed = true;
3827                 }
3828                 else if (pColumnData && bUseTab)
3829                 {
3830                     // Iterate through AutoInput entries
3831                     NextAutoEntry( bShift );
3832                     bUsed = true;
3833                 }
3834             }
3835             break;
3836         case KEY_ESCAPE:
3837             if ( nTipVisible )
3838             {
3839                 HideTip();
3840                 bUsed = true;
3841             }
3842             else if( nTipVisibleSec )
3843             {
3844                 HideTipBelow();
3845                 bUsed = true;
3846             }
3847             else if (eMode != SC_INPUT_NONE)
3848             {
3849                 CancelHandler();
3850                 bUsed = true;
3851             }
3852             else
3853                 bSkip = true;
3854             break;
3855         case KEY_F2:
3856             if ( !bShift && !bControl && !bAlt && eMode == SC_INPUT_TABLE )
3857             {
3858                 eMode = SC_INPUT_TYPE;
3859                 bUsed = true;
3860             }
3861             break;
3862     }
3863 
3864     // Only execute cursor keys if already in EditMode
3865     // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator)
3866     bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt);
3867     bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Treat Insert like Cursorkeys
3868     if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) ||
3869                     ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) )
3870     {
3871         HideTip();
3872         HideTipBelow();
3873 
3874         if (bSelIsRef)
3875         {
3876             RemoveSelection();
3877             bSelIsRef = false;
3878         }
3879 
3880         UpdateActiveView();
3881         bool bNewView = DataChanging( nChar );
3882 
3883         if (bProtected)                             // Protected cell?
3884             bUsed = true;                           // Don't forward KeyEvent
3885         else                                        // Changes allowed
3886         {
3887             if (bNewView )                          // Create anew
3888             {
3889                 if (pActiveViewSh)
3890                     pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
3891                 UpdateActiveView();
3892                 if (eMode==SC_INPUT_NONE)
3893                     if (pTableView || pTopView)
3894                     {
3895                         OUString aStrLoP;
3896 
3897                         if (bStartEdit && nCellPercentFormatDecSep != 0 &&
3898                                 ((nChar >= '0' && nChar <= '9') || nChar == '-' || nChar == nCellPercentFormatDecSep))
3899                         {
3900                             aStrLoP = "%";
3901                         }
3902 
3903                         if (pTableView)
3904                         {
3905                             pTableView->GetEditEngine()->SetText( aStrLoP );
3906                             if ( !aStrLoP.isEmpty() )
3907                                 pTableView->SetSelection( ESelection(0,0, 0,0) );   // before the '%'
3908 
3909                             // Don't call SetSelection if the string is empty anyway,
3910                             // to avoid breaking the bInitial handling in ScViewData::EditGrowY
3911                         }
3912                         if (pTopView)
3913                         {
3914                             pTopView->GetEditEngine()->SetText( aStrLoP );
3915                             if ( !aStrLoP.isEmpty() )
3916                                 pTopView->SetSelection( ESelection(0,0, 0,0) );     // before the '%'
3917                         }
3918                     }
3919                 SyncViews();
3920             }
3921 
3922             if (pTableView || pTopView)
3923             {
3924                 if (bDoEnter)
3925                 {
3926                     if (pTableView)
3927                         if( pTableView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
3928                             bUsed = true;
3929                     if (pTopView)
3930                         if( pTopView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
3931                             bUsed = true;
3932                 }
3933                 else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() )
3934                 {
3935                     SkipClosingPar();
3936                     bUsed = true;
3937                 }
3938                 else
3939                 {
3940                     if (pTableView)
3941                     {
3942                         if (pTopView)
3943                             pTableView->SetControlWord(pTableView->GetControlWord() | EVControlBits::SINGLELINEPASTE);
3944 
3945                         vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr;
3946                         if ( pTableView->PostKeyEvent( rKEvt, pFrameWin ) )
3947                             bUsed = true;
3948 
3949                         pTableView->SetControlWord(pTableView->GetControlWord() & ~EVControlBits::SINGLELINEPASTE);
3950                     }
3951                     if (pTopView)
3952                     {
3953                         if ( bUsed && rKEvt.GetKeyCode().GetFunction() == KeyFuncType::CUT )
3954                             pTopView->DeleteSelected();
3955                         else if ( pTopView->PostKeyEvent( rKEvt ) )
3956                             bUsed = true;
3957                     }
3958                 }
3959 
3960                 // AutoInput:
3961                 if ( bUsed && SC_MOD()->GetAppOptions().GetAutoComplete() )
3962                 {
3963                     bUseTab = false;
3964                     if (pFormulaData)
3965                         miAutoPosFormula = pFormulaData->end();     // do not search further
3966                     if (pColumnData)
3967                         miAutoPosColumn = pColumnData->end();
3968 
3969                     KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
3970                     if ( nChar && nChar != 8 && nChar != 127 &&     // no 'backspace', no 'delete'
3971                          KeyFuncType::CUT != eFunc)                      // and no 'CTRL-X'
3972                     {
3973                         if (bFormulaMode)
3974                             UseFormulaData();
3975                         else
3976                             UseColData();
3977                     }
3978                 }
3979 
3980                 // When the selection is changed manually or an opening parenthesis
3981                 // is typed, stop overwriting parentheses
3982                 if ( bUsed && nChar == '(' )
3983                     ResetAutoPar();
3984 
3985                 if ( KEY_INSERT == nCode )
3986                 {
3987                     SfxViewFrame* pViewFrm = SfxViewFrame::Current();
3988                     if (pViewFrm)
3989                         pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
3990                 }
3991                 if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) )
3992                 {
3993                     ShowTipCursor();
3994                 }
3995                 if( bUsed && bFormulaMode && nCode == KEY_BACKSPACE )
3996                 {
3997                     UseFormulaData();
3998                 }
3999 
4000             }
4001 
4002             // #i114511# don't count cursor keys as modification
4003             bool bSetModified = !bCursorKey;
4004             DataChanged(false, bSetModified); // also calls UpdateParenthesis()
4005 
4006             // In the LOK case, we want to set the document modified state
4007             // right away at the start of the edit, so that the content is
4008             // saved even when the user leaves the document before hitting
4009             // Enter
4010             if (comphelper::LibreOfficeKit::isActive() && bSetModified && pActiveViewSh && !pActiveViewSh->GetViewData().GetDocShell()->IsModified())
4011                 pActiveViewSh->GetViewData().GetDocShell()->SetModified();
4012 
4013             InvalidateAttribs();        //! in DataChanged?
4014         }
4015     }
4016 
4017     if (pTopView && eMode != SC_INPUT_NONE)
4018         SyncViews();
4019 
4020     return bUsed;
4021 }
4022 
4023 OUString ScInputHandler::GetSurroundingText()
4024 {
4025     if (eMode != SC_INPUT_NONE)
4026     {
4027         UpdateActiveView();
4028         if (pTableView || pTopView)
4029         {
4030             if (pTableView)
4031                 return pTableView->GetSurroundingText();
4032             else if (pTopView)                      // call only once
4033                 return pTopView->GetSurroundingText();
4034         }
4035     }
4036     return OUString();
4037 }
4038 
4039 Selection ScInputHandler::GetSurroundingTextSelection()
4040 {
4041     if (eMode != SC_INPUT_NONE)
4042     {
4043         UpdateActiveView();
4044         if (pTableView || pTopView)
4045         {
4046             if (pTableView)
4047                 return pTableView->GetSurroundingTextSelection();
4048             else if (pTopView)                      // call only once
4049                 return pTopView->GetSurroundingTextSelection();
4050         }
4051     }
4052     return Selection(0, 0);
4053 }
4054 
4055 bool ScInputHandler::DeleteSurroundingText(const Selection& rSelection)
4056 {
4057     if (eMode != SC_INPUT_NONE)
4058     {
4059         UpdateActiveView();
4060         if (pTableView || pTopView)
4061         {
4062             if (pTableView)
4063                 return pTableView->DeleteSurroundingText(rSelection);
4064             else if (pTopView)                      // call only once
4065                 return pTopView->DeleteSurroundingText(rSelection);
4066         }
4067     }
4068     return false;
4069 }
4070 
4071 void ScInputHandler::InputCommand( const CommandEvent& rCEvt )
4072 {
4073     if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
4074     {
4075         // For CommandEventId::CursorPos, do as little as possible, because
4076         // with remote VCL, even a ShowCursor will generate another event.
4077         if ( eMode != SC_INPUT_NONE )
4078         {
4079             UpdateActiveView();
4080             if (pTableView || pTopView)
4081             {
4082                 if (pTableView)
4083                     pTableView->Command( rCEvt );
4084                 else if (pTopView)                      // call only once
4085                     pTopView->Command( rCEvt );
4086             }
4087         }
4088     }
4089     else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
4090     {
4091         if ( eMode != SC_INPUT_NONE )
4092         {
4093             UpdateActiveView();
4094             if (pTableView || pTopView)
4095             {
4096                 if (pTableView)
4097                     pTableView->Command( rCEvt );
4098                 else if (pTopView)                      // call only once
4099                     pTopView->Command( rCEvt );
4100             }
4101         }
4102     }
4103     else
4104     {
4105         HideTip();
4106         HideTipBelow();
4107 
4108         if ( bSelIsRef )
4109         {
4110             RemoveSelection();
4111             bSelIsRef = false;
4112         }
4113 
4114         UpdateActiveView();
4115         bool bNewView = DataChanging( 0, true );
4116 
4117         if (!bProtected)                            // changes allowed
4118         {
4119             if (bNewView)                           // create new edit view
4120             {
4121                 if (pActiveViewSh)
4122                     pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
4123                 UpdateActiveView();
4124                 if (eMode==SC_INPUT_NONE)
4125                     if (pTableView || pTopView)
4126                     {
4127                         if (pTableView)
4128                         {
4129                             pTableView->GetEditEngine()->SetText( "" );
4130                             pTableView->SetSelection( ESelection(0,0, 0,0) );
4131                         }
4132                         if (pTopView)
4133                         {
4134                             pTopView->GetEditEngine()->SetText( "" );
4135                             pTopView->SetSelection( ESelection(0,0, 0,0) );
4136                         }
4137                     }
4138                 SyncViews();
4139             }
4140 
4141             if (pTableView || pTopView)
4142             {
4143                 if (pTableView)
4144                     pTableView->Command( rCEvt );
4145                 if (pTopView)
4146                     pTopView->Command( rCEvt );
4147 
4148                 if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
4149                 {
4150                     //  AutoInput after ext text input
4151 
4152                     if (pFormulaData)
4153                         miAutoPosFormula = pFormulaData->end();
4154                     if (pColumnData)
4155                         miAutoPosColumn = pColumnData->end();
4156 
4157                     if (bFormulaMode)
4158                         UseFormulaData();
4159                     else
4160                         UseColData();
4161                 }
4162             }
4163 
4164             DataChanged();              //  calls UpdateParenthesis()
4165             InvalidateAttribs();        //! in DataChanged ?
4166         }
4167 
4168         if (pTopView && eMode != SC_INPUT_NONE)
4169             SyncViews();
4170     }
4171 }
4172 
4173 void ScInputHandler::NotifyChange( const ScInputHdlState* pState,
4174                                    bool bForce, ScTabViewShell* pSourceSh,
4175                                    bool bStopEditing)
4176 {
4177     // If the call originates from a macro call in the EnterHandler,
4178     // return immediately and don't mess up the status
4179     if (bInEnterHandler)
4180         return;
4181 
4182     bool bRepeat = (pState == pLastState.get());
4183     if (!bRepeat && pState && pLastState)
4184         bRepeat = (*pState == *pLastState);
4185     if (bRepeat && !bForce)
4186         return;
4187 
4188     bInOwnChange = true;                // disable ModifyHdl (reset below)
4189 
4190     if ( pState && !pLastState )        // Enable again
4191         bForce = true;
4192 
4193     bool bHadObject = pLastState && pLastState->GetEditData();
4194 
4195     //! Before EditEngine gets eventually created (so it gets the right pools)
4196     if ( pSourceSh )
4197         pActiveViewSh = pSourceSh;
4198     else
4199         pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
4200 
4201     if (pActiveViewSh)
4202         ImplCreateEditEngine();
4203 
4204     if ( pState != pLastState.get() )
4205     {
4206         pLastState.reset( pState ? new ScInputHdlState( *pState ) : nullptr);
4207     }
4208 
4209     if ( pState && pActiveViewSh )
4210     {
4211         ScModule* pScMod = SC_MOD();
4212 
4213         ScTabViewShell* pScTabViewShell = dynamic_cast<ScTabViewShell*>(pScMod->GetViewShell());
4214 
4215         // Also take foreign reference input into account here (e.g. FunctionsAutoPilot),
4216         // FormEditData, if we're switching from Help to Calc:
4217         if ( !bFormulaMode && !pScMod->IsFormulaMode() &&
4218              ( !pScTabViewShell || !pScTabViewShell->GetFormEditData() ) )
4219         {
4220             bool bIgnore = false;
4221             if ( bModified )
4222             {
4223                 if (pState->GetPos() != aCursorPos)
4224                 {
4225                     if (!bProtected)
4226                         EnterHandler();
4227                 }
4228                 else
4229                     bIgnore = true;
4230             }
4231 
4232             if ( !bIgnore )
4233             {
4234                 const ScAddress&        rSPos   = pState->GetStartPos();
4235                 const ScAddress&        rEPos   = pState->GetEndPos();
4236                 const EditTextObject*   pData   = pState->GetEditData();
4237                 OUString aString = pState->GetString();
4238                 bool bTxtMod = false;
4239                 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
4240                 ScDocument& rDoc = pDocSh->GetDocument();
4241 
4242                 aCursorPos  = pState->GetPos();
4243 
4244                 if ( pData )
4245                     bTxtMod = true;
4246                 else if ( bHadObject )
4247                     bTxtMod = true;
4248                 else if ( bTextValid )
4249                     bTxtMod = ( aString != aCurrentText );
4250                 else
4251                     bTxtMod = ( aString != GetEditText(mpEditEngine.get()) );
4252 
4253                 if ( bTxtMod || bForce )
4254                 {
4255                     if (pData)
4256                     {
4257                         mpEditEngine->SetTextCurrentDefaults( *pData );
4258                         if (pInputWin)
4259                             aString = ScEditUtil::GetMultilineString(*mpEditEngine);
4260                         else
4261                             aString = GetEditText(mpEditEngine.get());
4262                         lcl_RemoveTabs(aString);
4263                         bTextValid = false;
4264                         aCurrentText.clear();
4265                     }
4266                     else
4267                     {
4268                         aCurrentText = aString;
4269                         bTextValid = true;              //! To begin with remember as a string
4270                     }
4271 
4272                     if ( pInputWin )
4273                         pInputWin->SetTextString(aString);
4274 
4275                     if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
4276                     {
4277                         UpdateActiveView();
4278                         EditView* pActiveView = pTopView ? pTopView : pTableView;
4279                         ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection();
4280 
4281                         // if we switched content completely - don't send huge numbers
4282                         if (aSel.nStartPara == EE_PARA_NOT_FOUND)
4283                             aSel.nStartPara = 0;
4284 
4285                         if (aSel.nEndPara == EE_PARA_NOT_FOUND)
4286                             aSel.nEndPara = 0;
4287 
4288                         ScInputHandler::LOKSendFormulabarUpdate(pActiveView, pActiveViewSh, aString, aSel);
4289                         // TODO: deprecated?
4290                         pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8());
4291                     }
4292                 }
4293 
4294                 if ( pInputWin || comphelper::LibreOfficeKit::isActive())                        // Named range input
4295                 {
4296                     OUString aPosStr;
4297                     bool bSheetLocal = false;
4298                     const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
4299 
4300                     // Is the range a name?
4301                     //! Find by Timer?
4302                     if ( pActiveViewSh )
4303                         pActiveViewSh->GetViewData().GetDocument().
4304                             GetRangeAtBlock( ScRange( rSPos, rEPos ), aPosStr, &bSheetLocal);
4305 
4306                     if ( aPosStr.isEmpty() )           // Not a name -> format
4307                     {
4308                         ScRefFlags nFlags = ScRefFlags::ZERO;
4309                         if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 )
4310                             nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS;
4311                         if ( rSPos != rEPos )
4312                         {
4313                             ScRange r(rSPos, rEPos);
4314                             applyStartToEndFlags(nFlags);
4315                             aPosStr = r.Format(rDoc, ScRefFlags::VALID | nFlags, aAddrDetails);
4316                         }
4317                         else
4318                             aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
4319                     }
4320                     else if (bSheetLocal)
4321                     {
4322                         OUString aName;
4323                         if (rDoc.GetName( rSPos.Tab(), aName))
4324                             aPosStr = ScPosWnd::createLocalRangeName( aPosStr, aName);
4325                     }
4326 
4327                     if (pInputWin)
4328                     {
4329                         // Disable the accessible VALUE_CHANGE event
4330                         bool bIsSuppressed = pInputWin->IsAccessibilityEventsSuppressed(false);
4331                         pInputWin->SetAccessibilityEventsSuppressed(true);
4332                         pInputWin->SetPosString(aPosStr);
4333                         pInputWin->SetAccessibilityEventsSuppressed(bIsSuppressed);
4334                         pInputWin->SetSumAssignMode();
4335                     }
4336 
4337                     if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
4338                         pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS, aPosStr.toUtf8());
4339                 }
4340 
4341                 if (bStopEditing) {
4342                     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
4343 
4344                     //  As long as the content is not edited, turn off online spelling.
4345                     //  Online spelling is turned back on in StartTable, after setting
4346                     //  the right language from cell attributes.
4347 
4348                     EEControlBits nCntrl = mpEditEngine->GetControlWord();
4349                     if ( nCntrl & EEControlBits::ONLINESPELLING )
4350                         mpEditEngine->SetControlWord( nCntrl & ~EEControlBits::ONLINESPELLING );
4351                 }
4352 
4353                 bModified = false;
4354                 bSelIsRef = false;
4355                 bProtected = false;
4356                 bCommandErrorShown = false;
4357             }
4358         }
4359 
4360         if ( pInputWin)
4361         {
4362             // Do not enable if RefDialog is open
4363             if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen())
4364             {
4365                 if ( !pInputWin->IsEnabled())
4366                 {
4367                     pDelayTimer->Stop();
4368                     pInputWin->Enable();
4369                 }
4370             }
4371             else if(pScMod->IsRefDialogOpen())
4372             {   // Because every document has its own InputWin,
4373                 // we should start Timer again, because the input line may
4374                 // still be active
4375                 if ( !pDelayTimer->IsActive() )
4376                     pDelayTimer->Start();
4377             }
4378         }
4379     }
4380     else // !pState || !pActiveViewSh
4381     {
4382         if ( !pDelayTimer->IsActive() )
4383             pDelayTimer->Start();
4384     }
4385 
4386     HideTip();
4387     HideTipBelow();
4388     bInOwnChange = false;
4389 }
4390 
4391 void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust )
4392 {
4393     eAttrAdjust = eJust;
4394     UpdateAdjust( 0 );
4395 }
4396 
4397 void ScInputHandler::ResetDelayTimer()
4398 {
4399     if( pDelayTimer->IsActive() )
4400     {
4401         pDelayTimer->Stop();
4402         if ( pInputWin )
4403             pInputWin->Enable();
4404     }
4405 }
4406 
4407 IMPL_LINK_NOARG( ScInputHandler, DelayTimer, Timer*, void )
4408 {
4409     if ( !(nullptr == pLastState || SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen()))
4410         return;
4411 
4412     //! New method at ScModule to query if function autopilot is open
4413     SfxViewFrame* pViewFrm = SfxViewFrame::Current();
4414     if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
4415     {
4416         if ( pInputWin)
4417         {
4418             pInputWin->EnableButtons( false );
4419             pInputWin->Disable();
4420         }
4421     }
4422     else if ( !bFormulaMode ) // Keep formula e.g. for help
4423     {
4424         bInOwnChange = true; // disable ModifyHdl (reset below)
4425 
4426         pActiveViewSh = nullptr;
4427         mpEditEngine->SetTextCurrentDefaults( OUString() );
4428         if ( pInputWin )
4429         {
4430             pInputWin->SetPosString( OUString() );
4431             pInputWin->SetTextString( OUString() );
4432             pInputWin->Disable();
4433         }
4434 
4435         bInOwnChange = false;
4436     }
4437 }
4438 
4439 void ScInputHandler::InputSelection( const EditView* pView )
4440 {
4441     SyncViews( pView );
4442     ShowTipCursor();
4443     UpdateParenthesis(); // Selection changed -> update parentheses highlighting
4444 
4445     // When the selection is changed manually, stop overwriting parentheses
4446     ResetAutoPar();
4447 
4448     if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
4449     {
4450         EditView* pActiveView = pTopView ? pTopView : pTableView;
4451         ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection();
4452         ScInputHandler::LOKSendFormulabarUpdate(pActiveView, pActiveViewSh, GetEditString(), aSel);
4453     }
4454 }
4455 
4456 void ScInputHandler::InputChanged( const EditView* pView, bool bFromNotify )
4457 {
4458     if ( !pView )
4459         return;
4460 
4461     UpdateActiveView();
4462 
4463     // #i20282# DataChanged needs to know if this is from the input line's modify handler
4464     bool bFromTopNotify = ( bFromNotify && pView == pTopView );
4465 
4466     bool bNewView = DataChanging();                     //FIXME: Is this at all possible?
4467     aCurrentText = pView->GetEditEngine()->GetText();   // Also remember the string
4468     mpEditEngine->SetTextCurrentDefaults( aCurrentText );
4469     DataChanged( bFromTopNotify );
4470     bTextValid = true; // Is set to false in DataChanged
4471 
4472     if ( pActiveViewSh )
4473     {
4474         ScViewData& rViewData = pActiveViewSh->GetViewData();
4475         if ( bNewView )
4476             rViewData.GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
4477 
4478         rViewData.EditGrowY();
4479         rViewData.EditGrowX();
4480     }
4481 
4482     SyncViews( pView );
4483 }
4484 
4485 const OUString& ScInputHandler::GetEditString()
4486 {
4487     if (mpEditEngine)
4488     {
4489         aCurrentText = mpEditEngine->GetText(); // Always new from Engine
4490         bTextValid = true;
4491     }
4492 
4493     return aCurrentText;
4494 }
4495 
4496 Size ScInputHandler::GetTextSize()
4497 {
4498     Size aSize;
4499     if ( mpEditEngine )
4500         aSize = Size( mpEditEngine->CalcTextWidth(), mpEditEngine->GetTextHeight() );
4501 
4502     return aSize;
4503 }
4504 
4505 bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine )
4506 {
4507     bool bRet = false;
4508     if (mpEditEngine)
4509     {
4510         // Contains field?
4511         sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
4512         SfxItemSet aSet = mpEditEngine->GetAttribs( ESelection(0,0,nParCnt,0) );
4513         SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
4514         if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
4515         {
4516             // Copy content
4517             std::unique_ptr<EditTextObject> pObj = mpEditEngine->CreateTextObject();
4518             rDestEngine.SetTextCurrentDefaults(*pObj);
4519             pObj.reset();
4520 
4521             // Delete attributes
4522             for (sal_Int32 i=0; i<nParCnt; i++)
4523                 rDestEngine.RemoveCharAttribs( i );
4524 
4525             // Combine paragraphs
4526             while ( nParCnt > 1 )
4527             {
4528                 sal_Int32 nLen = rDestEngine.GetTextLen( 0 );
4529                 ESelection aSel( 0,nLen, 1,0 );
4530                 rDestEngine.QuickInsertText( OUString(' '), aSel ); // Replace line break with space
4531                 --nParCnt;
4532             }
4533 
4534             bRet = true;
4535         }
4536     }
4537     return bRet;
4538 }
4539 
4540 /**
4541  * Methods for FunctionAutoPilot:
4542  * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
4543  */
4544 void ScInputHandler::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd )
4545 {
4546     rStart = nFormSelStart;
4547     rEnd = nFormSelEnd;
4548 }
4549 
4550 EditView* ScInputHandler::GetFuncEditView()
4551 {
4552     UpdateActiveView(); // Due to pTableView
4553 
4554     EditView* pView = nullptr;
4555     if ( pInputWin )
4556     {
4557         pInputWin->MakeDialogEditView();
4558         pView = pInputWin->GetEditView();
4559     }
4560     else
4561     {
4562         if ( eMode != SC_INPUT_TABLE )
4563         {
4564             bCreatingFuncView = true; // Don't display RangeFinder
4565             SetMode( SC_INPUT_TABLE );
4566             bCreatingFuncView = false;
4567             if ( pTableView )
4568                 pTableView->GetEditEngine()->SetText( OUString() );
4569         }
4570         pView = pTableView;
4571     }
4572 
4573     return pView;
4574 }
4575 
4576 void ScInputHandler::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd )
4577 {
4578     if ( nStart <= nEnd )
4579     {
4580         nFormSelStart = nStart;
4581         nFormSelEnd = nEnd;
4582     }
4583     else
4584     {
4585         nFormSelEnd = nStart;
4586         nFormSelStart = nEnd;
4587     }
4588 
4589     EditView* pView = GetFuncEditView();
4590     if (pView)
4591         pView->SetSelection( ESelection(0,nStart, 0,nEnd) );
4592 
4593     bModified = true;
4594 }
4595 
4596 void ScInputHandler::InputReplaceSelection( std::u16string_view aStr )
4597 {
4598     if (!pRefViewSh)
4599         pRefViewSh = pActiveViewSh;
4600 
4601     OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken...");
4602 
4603     sal_Int32 nOldLen = nFormSelEnd - nFormSelStart;
4604     sal_Int32 nNewLen = aStr.size();
4605 
4606     OUStringBuffer aBuf(aFormText);
4607     if (nOldLen)
4608         aBuf.remove(nFormSelStart, nOldLen);
4609     if (nNewLen)
4610         aBuf.insert(nFormSelStart, aStr);
4611 
4612     aFormText = aBuf.makeStringAndClear();
4613 
4614     nFormSelEnd = nFormSelStart + nNewLen;
4615 
4616     EditView* pView = GetFuncEditView();
4617     if (pView)
4618     {
4619         pView->SetEditEngineUpdateLayout( false );
4620         pView->GetEditEngine()->SetText( aFormText );
4621         pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) );
4622         pView->SetEditEngineUpdateLayout( true );
4623     }
4624     bModified = true;
4625 }
4626 
4627 void ScInputHandler::InputTurnOffWinEngine()
4628 {
4629     bInOwnChange = true;                // disable ModifyHdl (reset below)
4630 
4631     eMode = SC_INPUT_NONE;
4632     /* TODO: it would be better if there was some way to reset the input bar
4633      * engine instead of deleting and having it recreate through
4634      * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
4635      * fdo#72278 without reintroducing fdo#69971. */
4636     StopInputWinEngine(true);
4637 
4638     bInOwnChange = false;
4639 }
4640 
4641 /**
4642  * ScInputHdlState
4643  */
4644 ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos,
4645                                   const ScAddress& rStartPos,
4646                                   const ScAddress& rEndPos,
4647                                   OUString _aString,
4648                                   const EditTextObject* pData )
4649     :   aCursorPos  ( rCurPos ),
4650         aStartPos   ( rStartPos ),
4651         aEndPos     ( rEndPos ),
4652         aString     (std::move( _aString )),
4653         pEditData   ( pData ? pData->Clone() : nullptr )
4654 {
4655 }
4656 
4657 ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy )
4658 {
4659     *this = rCpy;
4660 }
4661 
4662 ScInputHdlState::~ScInputHdlState()
4663 {
4664 }
4665 
4666 bool ScInputHdlState::operator==( const ScInputHdlState& r ) const
4667 {
4668     return (    (aStartPos  == r.aStartPos)
4669              && (aEndPos    == r.aEndPos)
4670              && (aCursorPos == r.aCursorPos)
4671              && (aString    == r.aString)
4672              && ScGlobal::EETextObjEqual( pEditData.get(), r.pEditData.get() ) );
4673 }
4674 
4675 ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r )
4676 {
4677     if (this != &r)
4678     {
4679         aCursorPos  = r.aCursorPos;
4680         aStartPos   = r.aStartPos;
4681         aEndPos     = r.aEndPos;
4682         aString     = r.aString;
4683         pEditData.reset();
4684         if (r.pEditData)
4685             pEditData = r.pEditData->Clone();
4686     }
4687     return *this;
4688 }
4689 
4690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4691