Allow to use multiple Formula dialog instances
[LibreOffice.git] / sc / source / ui / app / inputhdl.cxx
blob1f601a901c2ad93a4dd84b8bf3ad64facfc15e85
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <string_view>
23 #include <inputhdl.hxx>
24 #include <scitems.hxx>
25 #include <editeng/eeitem.hxx>
27 #include <sfx2/app.hxx>
28 #include <editeng/acorrcfg.hxx>
29 #include <formula/errorcodes.hxx>
30 #include <editeng/adjustitem.hxx>
31 #include <editeng/brushitem.hxx>
32 #include <svtools/colorcfg.hxx>
33 #include <editeng/colritem.hxx>
34 #include <editeng/editobj.hxx>
35 #include <editeng/editstat.hxx>
36 #include <editeng/editview.hxx>
37 #include <editeng/langitem.hxx>
38 #include <editeng/svxacorr.hxx>
39 #include <editeng/unolingu.hxx>
40 #include <editeng/wghtitem.hxx>
41 #include <editeng/justifyitem.hxx>
42 #include <editeng/misspellrange.hxx>
43 #include <sfx2/bindings.hxx>
44 #include <sfx2/viewfrm.hxx>
45 #include <sfx2/docfile.hxx>
46 #include <sfx2/printer.hxx>
47 #include <svl/zforlist.hxx>
48 #include <unotools/localedatawrapper.hxx>
49 #include <unotools/charclass.hxx>
50 #include <vcl/help.hxx>
51 #include <vcl/commandevent.hxx>
52 #include <vcl/cursor.hxx>
53 #include <vcl/settings.hxx>
54 #include <vcl/svapp.hxx>
55 #include <tools/urlobj.hxx>
56 #include <formula/formulahelper.hxx>
57 #include <formula/funcvarargs.h>
58 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
59 #include <comphelper/lok.hxx>
61 #include <inputwin.hxx>
62 #include <tabvwsh.hxx>
63 #include <docsh.hxx>
64 #include <scmod.hxx>
65 #include <uiitems.hxx>
66 #include <global.hxx>
67 #include <sc.hrc>
68 #include <globstr.hrc>
69 #include <scresid.hxx>
70 #include <patattr.hxx>
71 #include <viewdata.hxx>
72 #include <document.hxx>
73 #include <docpool.hxx>
74 #include <editutil.hxx>
75 #include <appoptio.hxx>
76 #include <docoptio.hxx>
77 #include <validat.hxx>
78 #include <rfindlst.hxx>
79 #include <inputopt.hxx>
80 #include <simpleformulacalc.hxx>
81 #include <compiler.hxx>
82 #include <editable.hxx>
83 #include <funcdesc.hxx>
84 #include <markdata.hxx>
85 #include <tokenarray.hxx>
86 #include <gridwin.hxx>
88 // Maximum Ranges in RangeFinder
89 #define RANGEFIND_MAX 128
91 using namespace formula;
93 bool ScInputHandler::bOptLoaded = false; // Evaluate App options
94 bool ScInputHandler::bAutoComplete = false; // Is set in KeyInput
96 namespace {
98 // Formula data replacement character for a pair of parentheses at end of
99 // function name, to force sorting parentheses before all other characters.
100 // Collation may treat parentheses differently.
101 const sal_Unicode cParenthesesReplacement = 0x0001;
103 sal_Unicode lcl_getSheetSeparator(ScDocument* pDoc)
105 ScCompiler aComp(pDoc, ScAddress(), pDoc->GetGrammar());
106 return aComp.GetNativeAddressSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
109 ScTypedCaseStrSet::const_iterator findText(
110 const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
111 const OUString& rStart, OUString& rResult, bool bBack)
113 auto lIsMatch = [&rStart](const ScTypedStrData& rData) {
114 return (rData.GetStringType() != ScTypedStrData::Value) && ScGlobal::GetpTransliteration()->isMatch(rStart, rData.GetString()); };
116 if (bBack) // Backwards
118 ScTypedCaseStrSet::const_reverse_iterator it = rDataSet.rbegin(), itEnd = rDataSet.rend();
119 if (itPos != rDataSet.end())
121 size_t nPos = std::distance(rDataSet.begin(), itPos);
122 size_t nRPos = rDataSet.size() - 1 - nPos;
123 std::advance(it, nRPos);
124 ++it;
127 it = std::find_if(it, itEnd, lIsMatch);
128 if (it != itEnd)
130 rResult = it->GetString();
131 return (++it).base(); // convert the reverse iterator back to iterator.
134 else // Forwards
136 ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end();
137 if (itPos != itEnd)
139 it = std::next(itPos);
142 it = std::find_if(it, itEnd, lIsMatch);
143 if (it != itEnd)
145 rResult = it->GetString();
146 return it;
150 return rDataSet.end(); // no matching text found
153 OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rString)
155 auto it = std::find_if(rDataSet.begin(), rDataSet.end(),
156 [&rString](const ScTypedStrData& rData) {
157 return (rData.GetStringType() != ScTypedStrData::Value)
158 && ScGlobal::GetpTransliteration()->isEqual(rData.GetString(), rString);
160 if (it != rDataSet.end())
161 return it->GetString();
162 return rString;
165 ScTypedCaseStrSet::const_iterator findTextAll(
166 const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
167 const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack)
169 rResultVec.clear(); // clear contents
171 size_t nCount = 0;
172 ScTypedCaseStrSet::const_iterator retit;
173 if ( bBack ) // Backwards
175 ScTypedCaseStrSet::const_reverse_iterator it, itEnd;
176 if ( itPos == rDataSet.end() )
178 it = rDataSet.rend();
179 --it;
180 itEnd = it;
182 else
184 it = rDataSet.rbegin();
185 size_t nPos = std::distance(rDataSet.begin(), itPos);
186 size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1
187 std::advance(it, nRPos);
188 if ( it == rDataSet.rend() )
189 it = rDataSet.rbegin();
190 itEnd = it;
192 bool bFirstTime = true;
194 while ( it != itEnd || bFirstTime )
196 ++it;
197 if ( it == rDataSet.rend() ) // go to the first if reach the end
198 it = rDataSet.rbegin();
200 if ( bFirstTime )
201 bFirstTime = false;
202 const ScTypedStrData& rData = *it;
203 if ( rData.GetStringType() == ScTypedStrData::Value )
204 // skip values
205 continue;
207 if ( !ScGlobal::GetpTransliteration()->isMatch(rStart, rData.GetString()) )
208 // not a match
209 continue;
211 rResultVec.push_back(rData.GetString()); // set the match data
212 if ( nCount == 0 ) // convert the reverse iterator back to iterator.
214 // actually we want to do "retit = it;".
215 retit = rDataSet.begin();
216 size_t nRPos = std::distance(rDataSet.rbegin(), it);
217 size_t nPos = rDataSet.size() - 1 - nRPos;
218 std::advance(retit, nPos);
220 ++nCount;
223 else // Forwards
225 ScTypedCaseStrSet::const_iterator it, itEnd;
226 it = itPos;
227 if ( it == rDataSet.end() )
228 it = rDataSet.begin();
229 itEnd = it;
230 bool bFirstTime = true;
232 while ( it != itEnd || bFirstTime )
234 ++it;
235 if ( it == rDataSet.end() ) // go to the first if reach the end
236 it = rDataSet.begin();
238 if ( bFirstTime )
239 bFirstTime = false;
240 const ScTypedStrData& rData = *it;
241 if ( rData.GetStringType() == ScTypedStrData::Value )
242 // skip values
243 continue;
245 if ( !ScGlobal::GetpTransliteration()->isMatch(rStart, rData.GetString()) )
246 // not a match
247 continue;
249 rResultVec.push_back(rData.GetString()); // set the match data
250 if ( nCount == 0 )
251 retit = it; // remember first match iterator
252 ++nCount;
256 if ( nCount > 0 ) // at least one function has matched
257 return retit;
258 return rDataSet.end(); // no matching text found
263 void ScInputHandler::InitRangeFinder( const OUString& rFormula )
265 DeleteRangeFinder();
266 if ( !pActiveViewSh || !SC_MOD()->GetInputOptions().GetRangeFinder() )
267 return;
268 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
269 ScDocument& rDoc = pDocSh->GetDocument();
270 const sal_Unicode cSheetSep = lcl_getSheetSeparator(&rDoc);
272 OUString aDelimiters = ScEditUtil::ModifyDelimiters(" !~\"");
273 // delimiters (in addition to ScEditUtil): only characters that are
274 // allowed in formulas next to references and the quotation mark (so
275 // string constants can be skipped)
277 sal_Int32 nColon = aDelimiters.indexOf( ':' );
278 if ( nColon != -1 )
279 aDelimiters = aDelimiters.replaceAt( nColon, 1, ""); // Delimiter without colon
280 sal_Int32 nDot = aDelimiters.indexOf(cSheetSep);
281 if ( nDot != -1 )
282 aDelimiters = aDelimiters.replaceAt( nDot, 1 , ""); // Delimiter without dot
284 const sal_Unicode* pChar = rFormula.getStr();
285 sal_Int32 nLen = rFormula.getLength();
286 sal_Int32 nPos = 0;
287 sal_Int32 nStart = 0;
288 sal_uInt16 nCount = 0;
289 ScRange aRange;
290 while ( nPos < nLen && nCount < RANGEFIND_MAX )
292 // Skip separator
293 while ( nPos<nLen && ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) )
295 if ( pChar[nPos] == '"' ) // String
297 ++nPos;
298 while (nPos<nLen && pChar[nPos] != '"') // Skip until end
299 ++nPos;
301 ++nPos; // Separator or closing quote
304 // text between separators
305 nStart = nPos;
306 handle_r1c1:
308 bool bSingleQuoted = false;
309 while (nPos < nLen)
311 // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1"
312 // Literal single quotes in sheet names are masked by another single quote
313 if (pChar[nPos] == '\'')
315 bSingleQuoted = !bSingleQuoted;
317 else if (!bSingleQuoted) // Get everything in single quotes, including separators
319 if (ScGlobal::UnicodeStrChr(aDelimiters.getStr(), pChar[nPos]))
320 break;
322 ++nPos;
326 // for R1C1 '-' in R[-]... or C[-]... are not delimiters
327 // Nothing heroic here to ensure that there are '[]' around a negative
328 // integer. we need to clean up this code.
329 if( nPos < nLen && nPos > 0 &&
330 '-' == pChar[nPos] && '[' == pChar[nPos-1] &&
331 formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() )
333 nPos++;
334 goto handle_r1c1;
337 if ( nPos > nStart )
339 OUString aTest = rFormula.copy( nStart, nPos-nStart );
340 const ScAddress::Details aAddrDetails( &rDoc, aCursorPos );
341 ScRefFlags nFlags = aRange.ParseAny( aTest, &rDoc, aAddrDetails );
342 if ( nFlags & ScRefFlags::VALID )
344 // Set tables if not specified
345 if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
346 aRange.aStart.SetTab( pActiveViewSh->GetViewData().GetTabNo() );
347 if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
348 aRange.aEnd.SetTab( aRange.aStart.Tab() );
350 if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) ==
351 ScRefFlags::ZERO )
353 // #i73766# if a single ref was parsed, set the same "abs" flags for ref2,
354 // so Format doesn't output a double ref because of different flags.
355 ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS);
356 applyStartToEndFlags(nFlags, nAbsFlags);
359 if (!nCount)
361 mpEditEngine->SetUpdateMode( false );
362 pRangeFindList.reset(new ScRangeFindList( pDocSh->GetTitle() ));
365 Color nColor = pRangeFindList->Insert( ScRangeFindData( aRange, nFlags, nStart, nPos ) );
367 ESelection aSel( 0, nStart, 0, nPos );
368 SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
369 aSet.Put( SvxColorItem( nColor, EE_CHAR_COLOR ) );
370 mpEditEngine->QuickSetAttribs( aSet, aSel );
371 ++nCount;
375 // Do not skip last separator; could be a quote (?)
378 if (nCount)
380 mpEditEngine->SetUpdateMode( true );
382 pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) );
386 void ScInputHandler::SetDocumentDisposing( bool b )
388 mbDocumentDisposing = b;
391 static void lcl_Replace( EditView* pView, const OUString& rNewStr, const ESelection& rOldSel )
393 if ( pView )
395 ESelection aOldSel = pView->GetSelection();
396 if (aOldSel.HasRange())
397 pView->SetSelection( ESelection( aOldSel.nEndPara, aOldSel.nEndPos,
398 aOldSel.nEndPara, aOldSel.nEndPos ) );
400 EditEngine* pEngine = pView->GetEditEngine();
401 pEngine->QuickInsertText( rNewStr, rOldSel );
403 // Dummy InsertText for Update and Paint
404 // To do that we need to cancel the selection from above (before QuickInsertText)
405 pView->InsertText( EMPTY_OUSTRING );
407 sal_Int32 nLen = pEngine->GetTextLen(0);
408 ESelection aSel( 0, nLen, 0, nLen );
409 pView->SetSelection( aSel ); // Set cursor to the end
413 void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew )
415 ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh;
416 if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() )
418 ScRangeFindData& rData = pRangeFindList->GetObject( nIndex );
419 sal_Int32 nOldStart = rData.nSelStart;
420 sal_Int32 nOldEnd = rData.nSelEnd;
421 Color nNewColor = pRangeFindList->FindColor( rNew, nIndex );
423 ScRange aJustified = rNew;
424 aJustified.PutInOrder(); // Always display Ref in the Formula the right way
425 ScDocument* pDoc = pDocView->GetViewData().GetDocument();
426 const ScAddress::Details aAddrDetails( pDoc, aCursorPos );
427 OUString aNewStr(aJustified.Format(rData.nFlags, pDoc, aAddrDetails));
428 ESelection aOldSel( 0, nOldStart, 0, nOldEnd );
429 SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
431 DataChanging();
433 lcl_Replace( pTopView, aNewStr, aOldSel );
434 lcl_Replace( pTableView, aNewStr, aOldSel );
435 aSet.Put( SvxColorItem( nNewColor, EE_CHAR_COLOR ) );
436 mpEditEngine->QuickSetAttribs( aSet, aOldSel );
438 bInRangeUpdate = true;
439 DataChanged();
440 bInRangeUpdate = false;
442 long nDiff = aNewStr.getLength() - static_cast<long>(nOldEnd-nOldStart);
444 rData.aRef = rNew;
445 rData.nSelEnd = rData.nSelEnd + nDiff;
446 rData.nColor = nNewColor;
448 sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFindList->Count());
449 for (sal_uInt16 i=nIndex+1; i<nCount; i++)
451 ScRangeFindData& rNext = pRangeFindList->GetObject( i );
452 rNext.nSelStart = rNext.nSelStart + nDiff;
453 rNext.nSelEnd = rNext.nSelEnd + nDiff;
456 EditView* pActiveView = pTopView ? pTopView : pTableView;
457 pActiveView->ShowCursor( false );
459 else
461 OSL_FAIL("UpdateRange: we're missing something");
465 void ScInputHandler::DeleteRangeFinder()
467 ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh;
468 if ( pRangeFindList && pPaintView )
470 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
471 pRangeFindList->SetHidden(true);
472 pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) ); // Steal
473 pRangeFindList.reset();
477 static OUString GetEditText(const EditEngine* pEng)
479 return ScEditUtil::GetSpaceDelimitedString(*pEng);
482 static void lcl_RemoveTabs(OUString& rStr)
484 rStr = rStr.replace('\t', ' ');
487 static void lcl_RemoveLineEnd(OUString& rStr)
489 rStr = convertLineEnd(rStr, LINEEND_LF);
490 rStr = rStr.replace('\n', ' ');
493 static sal_Int32 lcl_MatchParenthesis( const OUString& rStr, sal_Int32 nPos )
495 int nDir;
496 sal_Unicode c1, c2 = 0;
497 c1 = rStr[nPos];
498 switch ( c1 )
500 case '(' :
501 c2 = ')';
502 nDir = 1;
503 break;
504 case ')' :
505 c2 = '(';
506 nDir = -1;
507 break;
508 case '<' :
509 c2 = '>';
510 nDir = 1;
511 break;
512 case '>' :
513 c2 = '<';
514 nDir = -1;
515 break;
516 case '{' :
517 c2 = '}';
518 nDir = 1;
519 break;
520 case '}' :
521 c2 = '{';
522 nDir = -1;
523 break;
524 case '[' :
525 c2 = ']';
526 nDir = 1;
527 break;
528 case ']' :
529 c2 = '[';
530 nDir = -1;
531 break;
532 default:
533 nDir = 0;
535 if ( !nDir )
536 return -1;
537 sal_Int32 nLen = rStr.getLength();
538 const sal_Unicode* p0 = rStr.getStr();
539 const sal_Unicode* p;
540 const sal_Unicode* p1;
541 sal_uInt16 nQuotes = 0;
542 if ( nPos < nLen / 2 )
544 p = p0;
545 p1 = p0 + nPos;
547 else
549 p = p0 + nPos;
550 p1 = p0 + nLen;
552 while ( p < p1 )
554 if ( *p++ == '\"' )
555 nQuotes++;
557 // Odd number of quotes that we find ourselves in a string
558 bool bLookInString = ((nQuotes % 2) != 0);
559 bool bInString = bLookInString;
560 p = p0 + nPos;
561 p1 = (nDir < 0 ? p0 : p0 + nLen) ;
562 sal_uInt16 nLevel = 1;
563 while ( p != p1 && nLevel )
565 p += nDir;
566 if ( *p == '\"' )
568 bInString = !bInString;
569 if ( bLookInString && !bInString )
570 p = p1; // That's it then
572 else if ( bInString == bLookInString )
574 if ( *p == c1 )
575 nLevel++;
576 else if ( *p == c2 )
577 nLevel--;
580 if ( nLevel )
581 return -1;
582 return static_cast<sal_Int32>(p - p0);
585 ScInputHandler::ScInputHandler()
586 : pInputWin( nullptr ),
587 pTableView( nullptr ),
588 pTopView( nullptr ),
589 pTipVisibleParent( nullptr ),
590 nTipVisible( nullptr ),
591 pTipVisibleSecParent( nullptr ),
592 nTipVisibleSec( nullptr ),
593 nFormSelStart( 0 ),
594 nFormSelEnd( 0 ),
595 nAutoPar( 0 ),
596 eMode( SC_INPUT_NONE ),
597 bUseTab( false ),
598 bTextValid( true ),
599 bModified( false ),
600 bSelIsRef( false ),
601 bFormulaMode( false ),
602 bInRangeUpdate( false ),
603 bParenthesisShown( false ),
604 bCreatingFuncView( false ),
605 bInEnterHandler( false ),
606 bCommandErrorShown( false ),
607 bInOwnChange( false ),
608 bProtected( false ),
609 bCellHasPercentFormat( false ),
610 bLastIsSymbol( false ),
611 mbDocumentDisposing(false),
612 nValidation( 0 ),
613 eAttrAdjust( SvxCellHorJustify::Standard ),
614 aScaleX( 1,1 ),
615 aScaleY( 1,1 ),
616 pRefViewSh( nullptr ),
617 pLastPattern( nullptr ),
618 maFormulaChar()
620 // The InputHandler is constructed with the view, so SfxViewShell::Current
621 // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange.
622 pActiveViewSh = nullptr;
624 // Bindings (only still used for Invalidate) are retrieved if needed on demand
626 pDelayTimer.reset( new Timer( "ScInputHandlerDelay timer" ) );
627 pDelayTimer->SetTimeout( 500 ); // 500 ms delay
628 pDelayTimer->SetInvokeHandler( LINK( this, ScInputHandler, DelayTimer ) );
631 ScInputHandler::~ScInputHandler()
633 // If this is the application InputHandler, the dtor is called after SfxApplication::Main,
634 // thus we can't rely on any Sfx functions
635 if (!mbDocumentDisposing) // inplace
636 EnterHandler(); // Finish input
638 if (SC_MOD()->GetRefInputHdl() == this)
639 SC_MOD()->SetRefInputHdl(nullptr);
641 if ( pInputWin && pInputWin->GetInputHandler() == this )
642 pInputWin->SetInputHandler( nullptr );
645 void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY )
647 if ( rX != aScaleX || rY != aScaleY )
649 aScaleX = rX;
650 aScaleY = rY;
651 if (mpEditEngine)
653 MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
654 mpEditEngine->SetRefMapMode( aMode );
659 void ScInputHandler::UpdateRefDevice()
661 if (!mpEditEngine)
662 return;
664 bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
665 bool bInPlace = pActiveViewSh && pActiveViewSh->GetViewFrame()->GetFrame().IsInPlace();
666 EEControlBits nCtrl = mpEditEngine->GetControlWord();
667 if ( bTextWysiwyg || bInPlace )
668 nCtrl |= EEControlBits::FORMAT100; // EditEngine default: always format for 100%
669 else
670 nCtrl &= ~EEControlBits::FORMAT100; // when formatting for screen, use the actual MapMode
671 mpEditEngine->SetControlWord( nCtrl );
672 if ( bTextWysiwyg && pActiveViewSh )
673 mpEditEngine->SetRefDevice( pActiveViewSh->GetViewData().GetDocument()->GetPrinter() );
674 else
675 mpEditEngine->SetRefDevice( nullptr );
677 MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
678 mpEditEngine->SetRefMapMode( aMode );
680 // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev,
681 // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL).
682 if ( !( bTextWysiwyg && pActiveViewSh ) )
684 mpEditEngine->GetRefDevice()->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() );
688 void ScInputHandler::ImplCreateEditEngine()
690 if ( !mpEditEngine )
692 if ( pActiveViewSh )
694 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
695 mpEditEngine = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool());
697 else
698 mpEditEngine = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool(), nullptr, true);
700 mpEditEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine->GetWordDelimiters() ) );
701 UpdateRefDevice(); // also sets MapMode
702 mpEditEngine->SetPaperSize( Size( 1000000, 1000000 ) );
703 pEditDefaults.reset( new SfxItemSet( mpEditEngine->GetEmptyItemSet() ) );
705 mpEditEngine->SetControlWord( mpEditEngine->GetControlWord() | EEControlBits::AUTOCORRECT );
706 mpEditEngine->SetReplaceLeadingSingleQuotationMark( false );
707 mpEditEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) );
711 void ScInputHandler::UpdateAutoCorrFlag()
713 EEControlBits nCntrl = mpEditEngine->GetControlWord();
714 EEControlBits nOld = nCntrl;
716 // Don't use pLastPattern here (may be invalid because of AutoStyle)
717 bool bDisable = bLastIsSymbol || bFormulaMode;
718 if ( bDisable )
719 nCntrl &= ~EEControlBits::AUTOCORRECT;
720 else
721 nCntrl |= EEControlBits::AUTOCORRECT;
723 if ( nCntrl != nOld )
724 mpEditEngine->SetControlWord(nCntrl);
727 void ScInputHandler::UpdateSpellSettings( bool bFromStartTab )
729 if ( pActiveViewSh )
731 ScViewData& rViewData = pActiveViewSh->GetViewData();
732 bool bOnlineSpell = rViewData.GetDocument()->GetDocOptions().IsAutoSpell();
734 // SetDefaultLanguage is independent of the language attributes,
735 // ScGlobal::GetEditDefaultLanguage is always used.
736 // It must be set every time in case the office language was changed.
738 mpEditEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
740 // if called for changed options, update flags only if already editing
741 // if called from StartTable, always update flags
743 if ( bFromStartTab || eMode != SC_INPUT_NONE )
745 EEControlBits nCntrl = mpEditEngine->GetControlWord();
746 EEControlBits nOld = nCntrl;
747 if( bOnlineSpell )
748 nCntrl |= EEControlBits::ONLINESPELLING;
749 else
750 nCntrl &= ~EEControlBits::ONLINESPELLING;
751 // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
752 if ( pLastPattern && pLastPattern->IsSymbolFont() )
753 nCntrl &= ~EEControlBits::AUTOCORRECT;
754 else
755 nCntrl |= EEControlBits::AUTOCORRECT;
756 if ( nCntrl != nOld )
757 mpEditEngine->SetControlWord(nCntrl);
759 ScDocument* pDoc = rViewData.GetDocument();
760 pDoc->ApplyAsianEditSettings( *mpEditEngine );
761 mpEditEngine->SetDefaultHorizontalTextDirection(
762 pDoc->GetEditTextDirection( rViewData.GetTabNo() ) );
763 mpEditEngine->SetFirstWordCapitalization( false );
766 // Language is set separately, so the speller is needed only if online spelling is active
767 if ( bOnlineSpell ) {
768 css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
769 mpEditEngine->SetSpeller( xXSpellChecker1 );
772 bool bHyphen = pLastPattern && pLastPattern->GetItem(ATTR_HYPHENATE).GetValue();
773 if ( bHyphen ) {
774 css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
775 mpEditEngine->SetHyphenator( xXHyphenator );
780 // Function/Range names etc. as Tip help
782 // The other types are defined in ScDocument::GetFormulaEntries
783 void ScInputHandler::GetFormulaData()
785 if ( pActiveViewSh )
787 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
789 if ( pFormulaData )
790 pFormulaData->clear();
791 else
793 pFormulaData.reset( new ScTypedCaseStrSet );
796 if( pFormulaDataPara )
797 pFormulaDataPara->clear();
798 else
799 pFormulaDataPara.reset( new ScTypedCaseStrSet );
801 const OUString aParenthesesReplacement( cParenthesesReplacement);
802 const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
803 sal_uInt32 nListCount = pFuncList->GetCount();
804 for(sal_uInt32 i=0;i<nListCount;i++)
806 const ScFuncDesc* pDesc = pFuncList->GetFunction( i );
807 if ( pDesc->mxFuncName )
809 const sal_Unicode* pName = pDesc->mxFuncName->getStr();
810 const sal_Int32 nLen = pDesc->mxFuncName->getLength();
811 // fdo#75264 fill maFormulaChar with all characters used in formula names
812 for ( sal_Int32 j = 0; j < nLen; j++ )
814 sal_Unicode c = pName[ j ];
815 maFormulaChar.insert( c );
817 OUString aFuncName = *pDesc->mxFuncName + aParenthesesReplacement;
818 pFormulaData->insert(ScTypedStrData(aFuncName, 0.0, ScTypedStrData::Standard));
819 pDesc->initArgumentInfo();
820 OUString aEntry = pDesc->getSignature();
821 pFormulaDataPara->insert(ScTypedStrData(aEntry, 0.0, ScTypedStrData::Standard));
824 miAutoPosFormula = pFormulaData->end();
825 rDoc.GetFormulaEntries( *pFormulaData );
826 rDoc.GetFormulaEntries( *pFormulaDataPara );
830 IMPL_LINK( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent&, rEvent, void )
832 if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
833 || rEvent.GetId() == VclEventId::WindowLoseFocus)
834 HideTip();
837 IMPL_LINK( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent&, rEvent, void )
839 if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
840 || rEvent.GetId() == VclEventId::WindowLoseFocus)
841 HideTipBelow();
844 void ScInputHandler::HideTip()
846 if ( nTipVisible )
848 pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
849 Help::HidePopover(pTipVisibleParent, nTipVisible );
850 nTipVisible = nullptr;
851 pTipVisibleParent = nullptr;
853 aManualTip.clear();
855 void ScInputHandler::HideTipBelow()
857 if ( nTipVisibleSec )
859 pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
860 Help::HidePopover(pTipVisibleSecParent, nTipVisibleSec);
861 nTipVisibleSec = nullptr;
862 pTipVisibleSecParent = nullptr;
864 aManualTip.clear();
867 namespace
870 bool lcl_hasSingleToken(const OUString& s, sal_Unicode c)
872 return !s.isEmpty() && s.indexOf(c)<0;
877 void ScInputHandler::ShowArgumentsTip( OUString& rSelText )
879 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
880 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
881 const sal_Unicode cSheetSep = lcl_getSheetSeparator(&pDocSh->GetDocument());
882 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
883 bool bFound = false;
884 while( !bFound )
886 rSelText += ")";
887 sal_Int32 nLeftParentPos = lcl_MatchParenthesis( rSelText, rSelText.getLength()-1 );
888 if( nLeftParentPos != -1 )
890 sal_Int32 nNextFStart = aHelper.GetFunctionStart( rSelText, nLeftParentPos, true);
891 const IFunctionDescription* ppFDesc;
892 ::std::vector< OUString> aArgs;
893 if( aHelper.GetNextFunc( rSelText, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
895 if( !ppFDesc->getFunctionName().isEmpty() )
897 sal_Int32 nArgPos = aHelper.GetArgStart( rSelText, nNextFStart, 0 );
898 sal_uInt16 nArgs = static_cast<sal_uInt16>(ppFDesc->getParameterCount());
899 OUString aFuncName( ppFDesc->getFunctionName() + "(");
900 OUString aNew;
901 ScTypedCaseStrSet::const_iterator it =
902 findText(*pFormulaDataPara, pFormulaDataPara->end(), aFuncName, aNew, false);
903 if (it != pFormulaDataPara->end())
905 bool bFlag = false;
906 sal_uInt16 nActive = 0;
907 for( sal_uInt16 i=0; i < nArgs; i++ )
909 sal_Int32 nLength = aArgs[i].getLength();
910 if( nArgPos <= rSelText.getLength()-1 )
912 nActive = i+1;
913 bFlag = true;
915 nArgPos+=nLength+1;
917 if( bFlag )
919 sal_Int32 nStartPosition = 0;
920 sal_Int32 nEndPosition = 0;
922 if( lcl_hasSingleToken(aNew, cSep) )
924 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
926 sal_Unicode cNext = aNew[i];
927 if( cNext == '(' )
929 nStartPosition = i+1;
933 else if( lcl_hasSingleToken(aNew, cSheetSep) )
935 sal_uInt16 nCount = 0;
936 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
938 sal_Unicode cNext = aNew[i];
939 if( cNext == '(' )
941 nStartPosition = i+1;
943 else if( cNext == cSep )
945 nCount ++;
946 nEndPosition = i;
947 if( nCount == nActive )
949 break;
951 nStartPosition = nEndPosition+1;
955 else
957 sal_uInt16 nCount = 0;
958 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
960 sal_Unicode cNext = aNew[i];
961 if( cNext == '(' )
963 nStartPosition = i+1;
965 else if( cNext == cSep )
967 nCount ++;
968 nEndPosition = i;
969 if( nCount == nActive )
971 break;
973 nStartPosition = nEndPosition+1;
975 else if( cNext == cSheetSep )
977 continue;
982 if (nStartPosition > 0)
984 OUStringBuffer aBuf;
985 aBuf.append(std::u16string_view(aNew).substr(0, nStartPosition));
986 aBuf.append(u'\x25BA');
987 aBuf.append(std::u16string_view(aNew).substr(nStartPosition));
988 nArgs = ppFDesc->getParameterCount();
989 sal_Int16 nVarArgsSet = 0;
990 if ( nArgs >= PAIRED_VAR_ARGS )
992 nVarArgsSet = 2;
993 nArgs -= PAIRED_VAR_ARGS - nVarArgsSet;
995 else if ( nArgs >= VAR_ARGS )
997 nVarArgsSet = 1;
998 nArgs -= VAR_ARGS - nVarArgsSet;
1000 if ( nVarArgsSet > 0 && nActive > nArgs )
1001 nActive = nArgs - (nActive - nArgs) % nVarArgsSet;
1002 aBuf.append( " : " );
1003 aBuf.append( ppFDesc->getParameterDescription(nActive-1) );
1004 aNew = aBuf.makeStringAndClear();
1005 ShowTipBelow( aNew );
1006 bFound = true;
1009 else
1011 ShowTipBelow( aNew );
1012 bFound = true;
1018 else
1020 break;
1025 void ScInputHandler::ShowTipCursor()
1027 HideTip();
1028 HideTipBelow();
1029 EditView* pActiveView = pTopView ? pTopView : pTableView;
1031 if ( bFormulaMode && pActiveView && pFormulaDataPara && mpEditEngine->GetParagraphCount() == 1 )
1033 OUString aParagraph = mpEditEngine->GetText( 0 );
1034 ESelection aSel = pActiveView->GetSelection();
1035 aSel.Adjust();
1037 if ( aParagraph.getLength() < aSel.nEndPos )
1038 return;
1040 if ( aSel.nEndPos > 0 )
1042 OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
1044 ShowArgumentsTip( aSelText );
1049 void ScInputHandler::ShowTip( const OUString& rText )
1051 // aManualTip needs to be set afterwards from outside
1053 HideTip();
1054 HideTipBelow();
1056 EditView* pActiveView = pTopView ? pTopView : pTableView;
1057 if (pActiveView)
1059 Point aPos;
1060 pTipVisibleParent = pActiveView->GetWindow();
1061 vcl::Cursor* pCur = pActiveView->GetCursor();
1062 if (pCur)
1063 aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() );
1064 aPos = pTipVisibleParent->OutputToScreenPixel( aPos );
1065 tools::Rectangle aRect( aPos, aPos );
1067 QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
1068 nTipVisible = Help::ShowPopover(pTipVisibleParent, aRect, rText, nAlign);
1069 pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
1073 void ScInputHandler::ShowTipBelow( const OUString& rText )
1075 HideTipBelow();
1077 EditView* pActiveView = pTopView ? pTopView : pTableView;
1078 if ( pActiveView )
1080 Point aPos;
1081 pTipVisibleSecParent = pActiveView->GetWindow();
1082 vcl::Cursor* pCur = pActiveView->GetCursor();
1083 if ( pCur )
1085 Point aLogicPos = pCur->GetPos();
1086 aLogicPos.AdjustY(pCur->GetHeight() );
1087 aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos );
1089 aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos );
1090 tools::Rectangle aRect( aPos, aPos );
1091 QuickHelpFlags const nAlign = QuickHelpFlags::Left | QuickHelpFlags::Top | QuickHelpFlags::NoEvadePointer;
1092 nTipVisibleSec = Help::ShowPopover(pTipVisibleSecParent, aRect, rText, nAlign);
1093 pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
1097 bool ScInputHandler::GetFuncName( OUString& aStart, OUString& aResult )
1099 if ( aStart.isEmpty() )
1100 return false;
1102 aStart = ScGlobal::pCharClass->uppercase( aStart );
1103 sal_Int32 nPos = aStart.getLength() - 1;
1104 sal_Unicode c = aStart[ nPos ];
1105 // fdo#75264 use maFormulaChar to check if characters are used in function names
1106 ::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c );
1107 if ( p == maFormulaChar.end() )
1108 return false; // last character is not part of any function name, quit
1110 ::std::vector<sal_Unicode> aTemp;
1111 aTemp.push_back( c );
1112 for(sal_Int32 i = nPos - 1; i >= 0; --i)
1114 c = aStart[ i ];
1115 p = maFormulaChar.find( c );
1117 if (p == maFormulaChar.end())
1118 break;
1120 aTemp.push_back( c );
1123 ::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin();
1124 aResult = OUString( *rIt++ );
1125 while ( rIt != aTemp.rend() )
1126 aResult += OUStringLiteral1( *rIt++ );
1128 return true;
1131 void ScInputHandler::ShowFuncList( const ::std::vector< OUString > & rFuncStrVec )
1133 OUStringBuffer aTipStr;
1134 OUString aFuncNameStr;
1135 OUString aDescFuncNameStr;
1136 ::std::vector<OUString>::const_iterator itStr = rFuncStrVec.begin();
1137 sal_Int32 nMaxFindNumber = 3;
1138 sal_Int32 nRemainFindNumber = nMaxFindNumber;
1139 for ( ; itStr != rFuncStrVec.end(); ++itStr )
1141 const OUString& rFunc = *itStr;
1142 if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
1144 aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
1146 else
1148 aFuncNameStr = rFunc;
1150 if ( itStr == rFuncStrVec.begin() )
1152 aTipStr = "[";
1153 aDescFuncNameStr = aFuncNameStr + "()";
1155 else
1157 aTipStr.append(", ");
1159 aTipStr.append(aFuncNameStr);
1160 if ( itStr == rFuncStrVec.begin() )
1161 aTipStr.append("]");
1162 if ( --nRemainFindNumber <= 0 )
1163 break;
1165 sal_Int32 nRemainNumber = rFuncStrVec.size() - nMaxFindNumber;
1166 if ( nRemainFindNumber == 0 && nRemainNumber > 0 )
1168 OUString aMessage( ScResId( STR_FUNCTIONS_FOUND ) );
1169 aMessage = aMessage.replaceFirst("%2", OUString::number(nRemainNumber));
1170 aMessage = aMessage.replaceFirst("%1", aTipStr.makeStringAndClear());
1171 aTipStr = aMessage;
1173 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1174 sal_Int32 nNextFStart = 0;
1175 const IFunctionDescription* ppFDesc;
1176 ::std::vector< OUString > aArgs;
1177 OUString eqPlusFuncName = "=" + aDescFuncNameStr;
1178 if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1180 if ( !ppFDesc->getFunctionName().isEmpty() )
1182 aTipStr.append(" : ").append(ppFDesc->getDescription());
1185 ShowTip( aTipStr.makeStringAndClear() );
1188 void ScInputHandler::UseFormulaData()
1190 EditView* pActiveView = pTopView ? pTopView : pTableView;
1192 // Formulas may only have 1 paragraph
1193 if ( pActiveView && pFormulaData && mpEditEngine->GetParagraphCount() == 1 )
1195 OUString aParagraph = mpEditEngine->GetText( 0 );
1196 ESelection aSel = pActiveView->GetSelection();
1197 aSel.Adjust();
1199 // Due to differences between table and input cell (e.g clipboard with line breaks),
1200 // the selection may not be in line with the EditEngine anymore.
1201 // Just return without any indication as to why.
1202 if ( aSel.nEndPos > aParagraph.getLength() )
1203 return;
1205 if ( aParagraph.getLength() > aSel.nEndPos &&
1206 ( ScGlobal::pCharClass->isLetterNumeric( aParagraph, aSel.nEndPos ) ||
1207 aParagraph[ aSel.nEndPos ] == '_' ||
1208 aParagraph[ aSel.nEndPos ] == '.' ||
1209 aParagraph[ aSel.nEndPos ] == '$' ) )
1210 return;
1212 // Is the cursor at the end of a word?
1213 if ( aSel.nEndPos > 0 )
1215 OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
1217 OUString aText;
1218 if ( GetFuncName( aSelText, aText ) )
1220 // function name is incomplete:
1221 // show matching functions name as tip above cell
1222 ::std::vector<OUString> aNewVec;
1223 miAutoPosFormula = pFormulaData->end();
1224 miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false);
1225 if (miAutoPosFormula != pFormulaData->end())
1227 // check if partial function name is not between quotes
1228 sal_Unicode cBetweenQuotes = 0;
1229 for ( int n = 0; n < aSelText.getLength(); n++ )
1231 if (cBetweenQuotes)
1233 if (aSelText[n] == cBetweenQuotes)
1234 cBetweenQuotes = 0;
1236 else if ( aSelText[ n ] == '"' )
1237 cBetweenQuotes = '"';
1238 else if ( aSelText[ n ] == '\'' )
1239 cBetweenQuotes = '\'';
1241 if ( cBetweenQuotes )
1242 return; // we're between quotes
1244 ShowFuncList(aNewVec);
1245 aAutoSearch = aText;
1247 return;
1250 // function name is complete:
1251 // show tip below the cell with function name and arguments of function
1252 ShowArgumentsTip( aSelText );
1257 void ScInputHandler::NextFormulaEntry( bool bBack )
1259 EditView* pActiveView = pTopView ? pTopView : pTableView;
1260 if ( pActiveView && pFormulaData )
1262 ::std::vector<OUString> aNewVec;
1263 ScTypedCaseStrSet::const_iterator itNew = findTextAll(*pFormulaData, miAutoPosFormula, aAutoSearch, aNewVec, bBack);
1264 if (itNew != pFormulaData->end())
1266 miAutoPosFormula = itNew;
1267 ShowFuncList( aNewVec );
1271 // For Tab we always call HideCursor first
1272 if (pActiveView)
1273 pActiveView->ShowCursor();
1276 namespace {
1278 bool needToExtendSelection(const OUString& rSelectedText, const OUString& rInsertText)
1280 return !ScGlobal::GetpTransliteration()->isMatch( rSelectedText, rInsertText);
1283 void completeFunction( EditView* pView, const OUString& rInsert, bool& rParInserted )
1285 if (pView)
1287 ESelection aSel = pView->GetSelection();
1288 --aSel.nStartPos;
1289 --aSel.nEndPos;
1290 pView->SetSelection(aSel);
1291 pView->SelectCurrentWord();
1293 // a dot and underscore are word separators so we need special
1294 // treatment for any formula containing a dot or underscore
1295 if(rInsert.indexOf(".") != -1 || rInsert.indexOf("_") != -1)
1297 // need to make sure that we replace also the part before the dot
1298 // go through the word to find the match with the insert string
1299 aSel = pView->GetSelection();
1300 ESelection aOldSelection = aSel;
1301 OUString aSelectedText = pView->GetSelected();
1302 if ( needToExtendSelection( aSelectedText, rInsert ) )
1304 while(needToExtendSelection(aSelectedText, rInsert))
1306 assert(aSel.nStartPos > 0);
1307 --aSel.nStartPos;
1308 aSel.nEndPos = aSel.nStartPos;
1309 pView->SetSelection(aSel);
1310 pView->SelectCurrentWord();
1311 aSelectedText = pView->GetSelected();
1313 aSel.nStartPos = aSel.nEndPos - ( aSelectedText.getLength() - 1 );
1315 else
1317 aSel.nStartPos = aSel.nEndPos - aSelectedText.getLength();
1319 aSel.nEndPos = aOldSelection.nEndPos;
1320 pView->SetSelection(aSel);
1323 OUString aInsStr = rInsert;
1324 sal_Int32 nInsLen = aInsStr.getLength();
1325 bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '('
1326 && aInsStr[nInsLen-1] == ')' );
1327 if ( bDoParen )
1329 // Do not insert parentheses after function names if there already are some
1330 // (e.g. if the function name was edited).
1331 ESelection aWordSel = pView->GetSelection();
1332 OUString aOld = pView->GetEditEngine()->GetText(0);
1334 // aWordSel.EndPos points one behind string if word at end
1335 if (aWordSel.nEndPos < aOld.getLength())
1337 sal_Unicode cNext = aOld[aWordSel.nEndPos];
1338 if ( cNext == '(' )
1340 bDoParen = false;
1341 aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses
1346 pView->InsertText( aInsStr );
1348 if ( bDoParen ) // Put cursor between parentheses
1350 aSel = pView->GetSelection();
1351 --aSel.nStartPos;
1352 --aSel.nEndPos;
1353 pView->SetSelection(aSel);
1355 rParInserted = true;
1362 void ScInputHandler::PasteFunctionData()
1364 if (pFormulaData && miAutoPosFormula != pFormulaData->end())
1366 const ScTypedStrData& rData = *miAutoPosFormula;
1367 OUString aInsert = rData.GetString();
1368 if (aInsert[aInsert.getLength()-1] == cParenthesesReplacement)
1369 aInsert = aInsert.copy( 0, aInsert.getLength()-1) + "()";
1370 bool bParInserted = false;
1372 DataChanging(); // Cannot be new
1373 completeFunction( pTopView, aInsert, bParInserted );
1374 completeFunction( pTableView, aInsert, bParInserted );
1375 DataChanged();
1376 ShowTipCursor();
1378 if (bParInserted)
1379 AutoParAdded();
1382 HideTip();
1384 EditView* pActiveView = pTopView ? pTopView : pTableView;
1385 if (pActiveView)
1386 pActiveView->ShowCursor();
1389 // Calculate selection and display as tip help
1390 static OUString lcl_Calculate( const OUString& rFormula, ScDocument* pDoc, const ScAddress &rPos )
1392 //TODO: Merge with ScFormulaDlg::CalcValue and move into Document!
1393 // Quotation marks for Strings are only inserted here.
1395 if(rFormula.isEmpty())
1396 return OUString();
1398 std::unique_ptr<ScSimpleFormulaCalculator> pCalc( new ScSimpleFormulaCalculator( pDoc, rPos, rFormula, false ) );
1400 // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range
1401 // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own
1402 bool bColRowName = pCalc->HasColRowName();
1403 if ( bColRowName )
1405 // ColRowName in RPN code?
1406 if ( pCalc->GetCode()->GetCodeLen() <= 1 )
1407 { // ==1: Single one is as a Parameter always a Range
1408 // ==0: It might be one, if ...
1409 OUStringBuffer aBraced;
1410 aBraced.append('(');
1411 aBraced.append(rFormula);
1412 aBraced.append(')');
1413 pCalc.reset( new ScSimpleFormulaCalculator( pDoc, rPos, aBraced.makeStringAndClear(), false ) );
1415 else
1416 bColRowName = false;
1419 FormulaError nErrCode = pCalc->GetErrCode();
1420 if ( nErrCode != FormulaError::NONE )
1421 return ScGlobal::GetErrorString(nErrCode);
1423 SvNumberFormatter& aFormatter = *(pDoc->GetFormatTable());
1424 OUString aValue;
1425 if ( pCalc->IsValue() )
1427 double n = pCalc->GetValue();
1428 sal_uInt32 nFormat = aFormatter.GetStandardFormat( n, 0,
1429 pCalc->GetFormatType(), ScGlobal::eLnge );
1430 aFormatter.GetInputLineString( n, nFormat, aValue );
1431 //! display OutputString but insert InputLineString
1433 else
1435 OUString aStr = pCalc->GetString().getString();
1436 sal_uInt32 nFormat = aFormatter.GetStandardFormat(
1437 pCalc->GetFormatType(), ScGlobal::eLnge);
1439 Color* pColor;
1440 aFormatter.GetOutputString( aStr, nFormat,
1441 aValue, &pColor );
1444 aValue = "\"" + aValue + "\"";
1445 //! Escape quotation marks in String??
1448 ScRange aTestRange;
1449 if ( bColRowName || (aTestRange.Parse(rFormula) & ScRefFlags::VALID) )
1450 aValue = aValue + " ...";
1452 return aValue;
1455 void ScInputHandler::FormulaPreview()
1457 OUString aValue;
1458 EditView* pActiveView = pTopView ? pTopView : pTableView;
1459 if ( pActiveView && pActiveViewSh )
1461 OUString aPart = pActiveView->GetSelected();
1462 if (aPart.isEmpty())
1463 aPart = mpEditEngine->GetText(0);
1464 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1465 aValue = lcl_Calculate( aPart, &rDoc, aCursorPos );
1468 if (!aValue.isEmpty())
1470 ShowTip( aValue ); // Display as QuickHelp
1471 aManualTip = aValue; // Set after ShowTip
1472 if (pFormulaData)
1473 miAutoPosFormula = pFormulaData->end();
1474 if (pColumnData)
1475 miAutoPosColumn = pColumnData->end();
1479 void ScInputHandler::PasteManualTip()
1481 // Three dots at the end -> Range reference -> do not insert
1482 // FIXME: Once we have matrix constants, we can change this
1483 sal_Int32 nTipLen = aManualTip.getLength();
1484 sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen));
1485 if ( nTipLen && ( nTipLen < 3 || aManualTip.copy( nTipLen2-3 ) != "..." ) )
1487 DataChanging(); // Cannot be new
1489 OUString aInsert = aManualTip;
1490 EditView* pActiveView = pTopView ? pTopView : pTableView;
1491 if (!pActiveView->HasSelection())
1493 // Nothing selected -> select everything
1494 sal_Int32 nOldLen = mpEditEngine->GetTextLen(0);
1495 ESelection aAllSel( 0, 0, 0, nOldLen );
1496 if ( pTopView )
1497 pTopView->SetSelection( aAllSel );
1498 if ( pTableView )
1499 pTableView->SetSelection( aAllSel );
1502 ESelection aSel = pActiveView->GetSelection();
1503 aSel.Adjust();
1504 OSL_ENSURE( !aSel.nStartPara && !aSel.nEndPara, "Too many paragraphs in Formula" );
1505 if ( !aSel.nStartPos ) // Selection from the start?
1507 if ( aSel.nEndPos == mpEditEngine->GetTextLen(0) )
1509 // Everything selected -> skip quotation marks
1510 if ( aInsert[0] == '"' )
1511 aInsert = aInsert.copy(1);
1512 sal_Int32 nInsLen = aInsert.getLength();
1513 if ( aInsert.endsWith("\"") )
1514 aInsert = aInsert.copy( 0, nInsLen-1 );
1516 else if ( aSel.nEndPos )
1518 // Not everything selected -> do not overwrite equality sign
1519 //FIXME: Even double equality signs??
1520 aSel.nStartPos = 1;
1521 if ( pTopView )
1522 pTopView->SetSelection( aSel );
1523 if ( pTableView )
1524 pTableView->SetSelection( aSel );
1527 if ( pTopView )
1528 pTopView->InsertText( aInsert, true );
1529 if ( pTableView )
1530 pTableView->InsertText( aInsert, true );
1532 DataChanged();
1535 HideTip();
1538 void ScInputHandler::ResetAutoPar()
1540 nAutoPar = 0;
1543 void ScInputHandler::AutoParAdded()
1545 ++nAutoPar; // Closing parenthesis can be overwritten
1548 bool ScInputHandler::CursorAtClosingPar()
1550 // Test if the cursor is before a closing parenthesis
1551 // Selection from SetReference has been removed before
1552 EditView* pActiveView = pTopView ? pTopView : pTableView;
1553 if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode )
1555 ESelection aSel = pActiveView->GetSelection();
1556 sal_Int32 nPos = aSel.nStartPos;
1557 OUString aFormula = mpEditEngine->GetText(0);
1558 if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' )
1559 return true;
1561 return false;
1564 void ScInputHandler::SkipClosingPar()
1566 // this is called when a ')' is typed and the cursor is before a ')'
1567 // that can be overwritten -> just set the cursor behind the ')'
1569 EditView* pActiveView = pTopView ? pTopView : pTableView;
1570 if (pActiveView)
1572 ESelection aSel = pActiveView->GetSelection();
1573 ++aSel.nStartPos;
1574 ++aSel.nEndPos;
1576 // this is in a formula (only one paragraph), so the selection
1577 // can be used directly for the TopView
1579 if ( pTopView )
1580 pTopView->SetSelection( aSel );
1581 if ( pTableView )
1582 pTableView->SetSelection( aSel );
1585 OSL_ENSURE(nAutoPar, "SkipClosingPar: count is wrong");
1586 --nAutoPar;
1589 // Auto input
1591 void ScInputHandler::GetColData()
1593 if ( pActiveViewSh )
1595 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1597 if ( pColumnData )
1598 pColumnData->clear();
1599 else
1600 pColumnData.reset( new ScTypedCaseStrSet );
1602 std::vector<ScTypedStrData> aEntries;
1603 rDoc.GetDataEntries(
1604 aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aEntries, true);
1605 if (!aEntries.empty())
1606 pColumnData->insert(aEntries.begin(), aEntries.end());
1608 miAutoPosColumn = pColumnData->end();
1612 void ScInputHandler::UseColData() // When typing
1614 EditView* pActiveView = pTopView ? pTopView : pTableView;
1615 if ( pActiveView && pColumnData )
1617 // Only change when cursor is at the end
1618 ESelection aSel = pActiveView->GetSelection();
1619 aSel.Adjust();
1621 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
1622 if ( aSel.nEndPara+1 == nParCnt )
1624 sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara );
1625 if ( aSel.nEndPos == nParLen )
1627 OUString aText = GetEditText(mpEditEngine.get());
1628 if (!aText.isEmpty())
1630 OUString aNew;
1631 miAutoPosColumn = pColumnData->end();
1632 miAutoPosColumn = findText(*pColumnData, miAutoPosColumn, aText, aNew, false);
1633 if (miAutoPosColumn != pColumnData->end())
1635 // Strings can contain line endings (e.g. due to dBase import),
1636 // which would result in multiple paragraphs here, which is not desirable.
1637 //! Then GetExactMatch doesn't work either
1638 lcl_RemoveLineEnd( aNew );
1640 // Keep paragraph, just append the rest
1641 //! Exact replacement in EnterHandler !!!
1642 // One Space between paragraphs:
1643 sal_Int32 nEdLen = mpEditEngine->GetTextLen() + nParCnt - 1;
1644 OUString aIns = aNew.copy(nEdLen);
1646 // Selection must be "backwards", so the cursor stays behind the last
1647 // typed character
1648 ESelection aSelection( aSel.nEndPara, aSel.nEndPos + aIns.getLength(),
1649 aSel.nEndPara, aSel.nEndPos );
1651 // When editing in input line, apply to both edit views
1652 if ( pTableView )
1654 pTableView->InsertText( aIns );
1655 pTableView->SetSelection( aSelection );
1657 if ( pTopView )
1659 pTopView->InsertText( aIns );
1660 pTopView->SetSelection( aSelection );
1663 aAutoSearch = aText; // To keep searching - nAutoPos is set
1665 if (aText.getLength() == aNew.getLength())
1667 // If the inserted text is found, consume TAB only if there's more coming
1668 OUString aDummy;
1669 ScTypedCaseStrSet::const_iterator itNextPos =
1670 findText(*pColumnData, miAutoPosColumn, aText, aDummy, false);
1671 bUseTab = itNextPos != pColumnData->end();
1673 else
1674 bUseTab = true;
1682 void ScInputHandler::NextAutoEntry( bool bBack )
1684 EditView* pActiveView = pTopView ? pTopView : pTableView;
1685 if ( pActiveView && pColumnData )
1687 if (miAutoPosColumn != pColumnData->end() && !aAutoSearch.isEmpty())
1689 // Is the selection still valid (could be changed via the mouse)?
1690 ESelection aSel = pActiveView->GetSelection();
1691 aSel.Adjust();
1692 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
1693 if ( aSel.nEndPara+1 == nParCnt && aSel.nStartPara == aSel.nEndPara )
1695 OUString aText = GetEditText(mpEditEngine.get());
1696 sal_Int32 nSelLen = aSel.nEndPos - aSel.nStartPos;
1697 sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara );
1698 if ( aSel.nEndPos == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen )
1700 OUString aNew;
1701 ScTypedCaseStrSet::const_iterator itNew =
1702 findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack);
1704 if (itNew != pColumnData->end())
1706 // match found!
1707 miAutoPosColumn = itNew;
1708 bInOwnChange = true; // disable ModifyHdl (reset below)
1710 lcl_RemoveLineEnd( aNew );
1711 OUString aIns = aNew.copy(aAutoSearch.getLength());
1713 // when editing in input line, apply to both edit views
1714 if ( pTableView )
1716 pTableView->DeleteSelected();
1717 pTableView->InsertText( aIns );
1718 pTableView->SetSelection( ESelection(
1719 aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
1720 aSel.nEndPara, aSel.nStartPos ) );
1722 if ( pTopView )
1724 pTopView->DeleteSelected();
1725 pTopView->InsertText( aIns );
1726 pTopView->SetSelection( ESelection(
1727 aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
1728 aSel.nEndPara, aSel.nStartPos ) );
1731 bInOwnChange = false;
1738 // For Tab, HideCursor was always called first
1739 if (pActiveView)
1740 pActiveView->ShowCursor();
1743 // Highlight parentheses
1744 void ScInputHandler::UpdateParenthesis()
1746 // Find parentheses
1747 //TODO: Can we disable parentheses highlighting per parentheses?
1748 bool bFound = false;
1749 if ( bFormulaMode && eMode != SC_INPUT_TOP )
1751 if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom
1753 ESelection aSel = pTableView->GetSelection();
1754 if (aSel.nStartPos)
1756 // Examine character left to the cursor
1757 sal_Int32 nPos = aSel.nStartPos - 1;
1758 OUString aFormula = mpEditEngine->GetText(0);
1759 sal_Unicode c = aFormula[nPos];
1760 if ( c == '(' || c == ')' )
1762 sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos );
1763 if ( nOther != -1 )
1765 SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
1766 aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
1768 //! Distinguish if cell is already highlighted!!!!
1769 if (bParenthesisShown)
1771 // Remove old highlighting
1772 sal_Int32 nCount = mpEditEngine->GetParagraphCount();
1773 for (sal_Int32 i=0; i<nCount; i++)
1774 mpEditEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
1777 ESelection aSelThis( 0,nPos, 0,nPos+1 );
1778 mpEditEngine->QuickSetAttribs( aSet, aSelThis );
1779 ESelection aSelOther( 0,nOther, 0,nOther+1 );
1780 mpEditEngine->QuickSetAttribs( aSet, aSelOther );
1782 // Dummy InsertText for Update and Paint (selection is empty)
1783 pTableView->InsertText( EMPTY_OUSTRING );
1785 bFound = true;
1790 // mark parenthesis right of cursor if it will be overwritten (nAutoPar)
1791 // with different color (COL_LIGHTBLUE) ??
1795 // Remove old highlighting, if no new one is set
1796 if ( bParenthesisShown && !bFound && pTableView )
1798 sal_Int32 nCount = mpEditEngine->GetParagraphCount();
1799 for (sal_Int32 i=0; i<nCount; i++)
1800 pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
1803 bParenthesisShown = bFound;
1806 void ScInputHandler::ViewShellGone(const ScTabViewShell* pViewSh) // Executed synchronously!
1808 if ( pViewSh == pActiveViewSh )
1810 pLastState.reset();
1811 pLastPattern = nullptr;
1814 if ( pViewSh == pRefViewSh )
1816 //! The input from the EnterHandler does not arrive anymore
1817 // We end the EditMode anyways
1818 EnterHandler();
1819 bFormulaMode = false;
1820 pRefViewSh = nullptr;
1821 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
1822 SC_MOD()->SetRefInputHdl(nullptr);
1823 if (pInputWin)
1824 pInputWin->SetFormulaMode(false);
1825 UpdateAutoCorrFlag();
1828 pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
1830 if ( pActiveViewSh && pActiveViewSh == pViewSh )
1832 OSL_FAIL("pActiveViewSh is gone");
1833 pActiveViewSh = nullptr;
1836 if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
1837 UpdateRefDevice(); // Don't keep old document's printer as RefDevice
1840 void ScInputHandler::UpdateActiveView()
1842 ImplCreateEditEngine();
1844 // #i20588# Don't rely on focus to find the active edit view. Instead, the
1845 // active pane at the start of editing is now stored (GetEditActivePart).
1846 // GetActiveWin (the currently active pane) fails for ref input across the
1847 // panes of a split view.
1849 vcl::Window* pShellWin = pActiveViewSh ?
1850 pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData().GetEditActivePart() ) :
1851 nullptr;
1853 sal_uInt16 nCount = mpEditEngine->GetViewCount();
1854 if (nCount > 0)
1856 pTableView = mpEditEngine->GetView();
1857 for (sal_uInt16 i=1; i<nCount; i++)
1859 EditView* pThis = mpEditEngine->GetView(i);
1860 vcl::Window* pWin = pThis->GetWindow();
1861 if ( pWin==pShellWin )
1862 pTableView = pThis;
1865 else
1866 pTableView = nullptr;
1868 // setup the pTableView editeng for tiled rendering to get cursor and selections
1869 if (pTableView && pActiveViewSh)
1871 if (comphelper::LibreOfficeKit::isActive())
1873 pTableView->RegisterViewShell(pActiveViewSh);
1877 if (pInputWin && (eMode == SC_INPUT_TOP || eMode == SC_INPUT_TABLE))
1879 // tdf#71409: Always create the edit engine instance for the input
1880 // window, in order to properly manage accessibility events.
1881 pTopView = pInputWin->GetEditView();
1882 if (eMode != SC_INPUT_TOP)
1883 pTopView = nullptr;
1885 else
1886 pTopView = nullptr;
1889 void ScInputHandler::SetInputWindow( ScInputWindow* pNew )
1891 pInputWin = pNew;
1894 void ScInputHandler::StopInputWinEngine( bool bAll )
1896 if (pInputWin && !pInputWin->IsDisposed())
1897 pInputWin->StopEditEngine( bAll );
1899 pTopView = nullptr; // invalid now
1902 EditView* ScInputHandler::GetActiveView()
1904 UpdateActiveView();
1905 return pTopView ? pTopView : pTableView;
1908 void ScInputHandler::ForgetLastPattern()
1910 pLastPattern = nullptr;
1911 if ( !pLastState && pActiveViewSh )
1912 pActiveViewSh->UpdateInputHandler( true ); // Get status again
1913 else
1914 NotifyChange( pLastState.get(), true );
1917 void ScInputHandler::UpdateAdjust( sal_Unicode cTyped )
1919 SvxAdjust eSvxAdjust;
1920 switch (eAttrAdjust)
1922 case SvxCellHorJustify::Standard:
1924 bool bNumber = false;
1925 if (cTyped) // Restarted
1926 bNumber = (cTyped>='0' && cTyped<='9'); // Only ciphers are numbers
1927 else if ( pActiveViewSh )
1929 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1930 bNumber = ( rDoc.GetCellType( aCursorPos ) == CELLTYPE_VALUE );
1932 eSvxAdjust = bNumber ? SvxAdjust::Right : SvxAdjust::Left;
1934 break;
1935 case SvxCellHorJustify::Block:
1936 eSvxAdjust = SvxAdjust::Block;
1937 break;
1938 case SvxCellHorJustify::Center:
1939 eSvxAdjust = SvxAdjust::Center;
1940 break;
1941 case SvxCellHorJustify::Right:
1942 eSvxAdjust = SvxAdjust::Right;
1943 break;
1944 default: // SvxCellHorJustify::Left
1945 eSvxAdjust = SvxAdjust::Left;
1946 break;
1949 bool bAsianVertical = pLastPattern &&
1950 pLastPattern->GetItem( ATTR_STACKED ).GetValue() &&
1951 pLastPattern->GetItem( ATTR_VERTICAL_ASIAN ).GetValue();
1952 if ( bAsianVertical )
1954 // Always edit at top of cell -> LEFT when editing vertically
1955 eSvxAdjust = SvxAdjust::Left;
1958 pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
1959 mpEditEngine->SetDefaults( *pEditDefaults );
1961 if ( pActiveViewSh )
1963 pActiveViewSh->GetViewData().SetEditAdjust( eSvxAdjust );
1965 mpEditEngine->SetVertical( bAsianVertical );
1968 void ScInputHandler::RemoveAdjust()
1970 // Delete hard alignment attributes
1971 bool bUndo = mpEditEngine->IsUndoEnabled();
1972 if ( bUndo )
1973 mpEditEngine->EnableUndo( false );
1975 // Non-default paragraph attributes (e.g. from clipboard)
1976 // must be turned into character attributes
1977 mpEditEngine->RemoveParaAttribs();
1979 if ( bUndo )
1980 mpEditEngine->EnableUndo( true );
1984 void ScInputHandler::RemoveRangeFinder()
1986 // Delete pRangeFindList and colors
1987 mpEditEngine->SetUpdateMode(false);
1988 sal_Int32 nCount = mpEditEngine->GetParagraphCount(); // Could just have been inserted
1989 for (sal_Int32 i=0; i<nCount; i++)
1990 mpEditEngine->RemoveCharAttribs( i, EE_CHAR_COLOR );
1991 mpEditEngine->SetUpdateMode(true);
1993 EditView* pActiveView = pTopView ? pTopView : pTableView;
1994 pActiveView->ShowCursor( false );
1996 DeleteRangeFinder(); // Deletes the list and the labels on the table
1999 bool ScInputHandler::StartTable( sal_Unicode cTyped, bool bFromCommand, bool bInputActivated,
2000 ScEditEngineDefaulter* pTopEngine )
2002 bool bNewTable = false;
2004 if (bModified || !ValidCol(aCursorPos.Col()))
2005 return false;
2007 if (pActiveViewSh)
2009 ImplCreateEditEngine();
2010 UpdateActiveView();
2011 SyncViews();
2013 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2015 const ScMarkData& rMark = pActiveViewSh->GetViewData().GetMarkData();
2016 ScEditableTester aTester;
2017 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
2018 aTester.TestSelection( &rDoc, rMark );
2019 else
2020 aTester.TestSelectedBlock(
2021 &rDoc, aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Col(), aCursorPos.Row(), rMark );
2023 bool bStartInputMode = true;
2025 if (!aTester.IsEditable())
2027 bProtected = true;
2028 // We allow read-only input mode activation regardless
2029 // whether it's part of an array or not or whether explicit cell
2030 // activation is requested (double-click or F2) or a click in input
2031 // line.
2032 bool bShowError = (!bInputActivated || !aTester.GetMessageId() || strcmp(aTester.GetMessageId(), STR_PROTECTIONERR) != 0) &&
2033 !pActiveViewSh->GetViewData().GetDocShell()->IsReadOnly();
2034 if (bShowError)
2036 eMode = SC_INPUT_NONE;
2037 StopInputWinEngine( true );
2038 UpdateFormulaMode();
2039 if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) )
2041 // Prevent repeated error messages for the same cell from command events
2042 // (for keyboard events, multiple messages are wanted).
2043 // Set the flag before showing the error message because the command handler
2044 // for the next IME command may be called when showing the dialog.
2045 if ( bFromCommand )
2046 bCommandErrorShown = true;
2048 pActiveViewSh->GetActiveWin()->GrabFocus();
2049 pActiveViewSh->ErrorMessage(aTester.GetMessageId());
2051 bStartInputMode = false;
2055 if (bStartInputMode)
2057 // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise)
2058 mpEditEngine->SetUpdateMode( false );
2060 // Take over attributes in EditEngine
2061 const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(),
2062 aCursorPos.Row(),
2063 aCursorPos.Tab() );
2064 if (pPattern != pLastPattern)
2066 // Percent format?
2067 const SfxItemSet& rAttrSet = pPattern->GetItemSet();
2068 const SfxPoolItem* pItem;
2070 if ( SfxItemState::SET == rAttrSet.GetItemState( ATTR_VALUE_FORMAT, true, &pItem ) )
2072 sal_uInt32 nFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
2073 bCellHasPercentFormat = ( SvNumFormatType::PERCENT ==
2074 rDoc.GetFormatTable()->GetType( nFormat ) );
2076 else
2077 bCellHasPercentFormat = false; // Default: no percent
2079 // Validity specified?
2080 if ( SfxItemState::SET == rAttrSet.GetItemState( ATTR_VALIDDATA, true, &pItem ) )
2081 nValidation = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
2082 else
2083 nValidation = 0;
2085 // EditEngine Defaults
2086 // In no case SetParaAttribs, because the EditEngine might already
2087 // be filled (for Edit cells).
2088 // SetParaAttribs would change the content.
2090 //! The SetDefaults is now (since MUST/src602
2091 //! EditEngine changes) implemented as a SetParaAttribs.
2092 //! Any problems?
2094 pPattern->FillEditItemSet( pEditDefaults.get() );
2095 mpEditEngine->SetDefaults( *pEditDefaults );
2096 pLastPattern = pPattern;
2097 bLastIsSymbol = pPattern->IsSymbolFont();
2099 // Background color must be known for automatic font color.
2100 // For transparent cell background, the document background color must be used.
2102 Color aBackCol = pPattern->GetItem( ATTR_BACKGROUND ).GetColor();
2103 ScModule* pScMod = SC_MOD();
2104 if ( aBackCol.GetTransparency() > 0 ||
2105 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2106 aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
2107 mpEditEngine->SetBackgroundColor( aBackCol );
2109 // Adjustment
2110 eAttrAdjust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
2111 if ( eAttrAdjust == SvxCellHorJustify::Repeat &&
2112 pPattern->GetItem(ATTR_LINEBREAK).GetValue() )
2114 // #i31843# "repeat" with "line breaks" is treated as default alignment
2115 eAttrAdjust = SvxCellHorJustify::Standard;
2119 if (pTopEngine)
2121 // Necessary to sync SvxAutoCorrect behavior. This has to be
2122 // done before InitRangeFinder() below.
2123 MergeLanguageAttributes( *pTopEngine);
2126 // UpdateSpellSettings enables online spelling if needed
2127 // -> also call if attributes are unchanged
2128 UpdateSpellSettings( true ); // uses pLastPattern
2130 // Fill EditEngine
2131 OUString aStr;
2132 if (bTextValid)
2134 mpEditEngine->SetText(aCurrentText);
2135 aStr = aCurrentText;
2136 bTextValid = false;
2137 aCurrentText.clear();
2139 else
2140 aStr = GetEditText(mpEditEngine.get());
2142 if (aStr.startsWith("{=") && aStr.endsWith("}") ) // Matrix formula?
2144 aStr = aStr.copy(1, aStr.getLength() -2);
2145 mpEditEngine->SetText(aStr);
2146 if ( pInputWin )
2147 pInputWin->SetTextString(aStr);
2150 UpdateAdjust( cTyped );
2152 if ( bAutoComplete )
2153 GetColData();
2155 if ( !aStr.isEmpty() && ( aStr[0] == '=' || aStr[0] == '+' || aStr[0] == '-' ) &&
2156 !cTyped && !bCreatingFuncView )
2157 InitRangeFinder(aStr); // Formula is being edited -> RangeFinder
2159 bNewTable = true; // -> PostEditView Call
2163 if (!bProtected && pInputWin)
2164 pInputWin->SetOkCancelMode();
2166 return bNewTable;
2169 void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter& rDestEngine ) const
2171 const SfxItemSet& rSrcSet = mpEditEngine->GetDefaults();
2172 rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE ));
2173 rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CJK ));
2174 rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CTL ));
2177 static void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel )
2179 OSL_ENSURE( rSel.nStartPara==0 && rSel.nEndPara==0, "SetTopSelection: Para != 0" );
2181 EditEngine* pEngine = pEditView->GetEditEngine();
2182 sal_Int32 nCount = pEngine->GetParagraphCount();
2183 if (nCount > 1)
2185 sal_Int32 nParLen = pEngine->GetTextLen(rSel.nStartPara);
2186 while (rSel.nStartPos > nParLen && rSel.nStartPara+1 < nCount)
2188 rSel.nStartPos -= nParLen + 1; // Including space from line break
2189 nParLen = pEngine->GetTextLen(++rSel.nStartPara);
2192 nParLen = pEngine->GetTextLen(rSel.nEndPara);
2193 while (rSel.nEndPos > nParLen && rSel.nEndPara+1 < nCount)
2195 rSel.nEndPos -= nParLen + 1; // Including space from line break
2196 nParLen = pEngine->GetTextLen(++rSel.nEndPara);
2200 ESelection aSel = pEditView->GetSelection();
2202 if ( rSel.nStartPara != aSel.nStartPara || rSel.nEndPara != aSel.nEndPara
2203 || rSel.nStartPos != aSel.nStartPos || rSel.nEndPos != aSel.nEndPos )
2204 pEditView->SetSelection( rSel );
2207 void ScInputHandler::SyncViews( const EditView* pSourceView )
2209 if (pSourceView)
2211 bool bSelectionForTopView = false;
2212 if (pTopView && pTopView != pSourceView)
2213 bSelectionForTopView = true;
2214 bool bSelectionForTableView = false;
2215 if (pTableView && pTableView != pSourceView)
2216 bSelectionForTableView = true;
2217 if (bSelectionForTopView || bSelectionForTableView)
2219 ESelection aSel(pSourceView->GetSelection());
2220 if (bSelectionForTopView)
2221 pTopView->SetSelection(aSel);
2222 if (bSelectionForTableView)
2223 lcl_SetTopSelection(pTableView, aSel);
2226 // Only sync selection from topView if we are actually editing there
2227 else if (pTopView && pTableView)
2229 ESelection aSel(pTopView->GetSelection());
2230 lcl_SetTopSelection( pTableView, aSel );
2234 IMPL_LINK_NOARG(ScInputHandler, ModifyHdl, LinkParamNone*, void)
2236 if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) &&
2237 mpEditEngine && mpEditEngine->GetUpdateMode() && pInputWin )
2239 // Update input line from ModifyHdl for changes that are not
2240 // wrapped by DataChanging/DataChanged calls (like Drag&Drop)
2241 OUString aText(ScEditUtil::GetMultilineString(*mpEditEngine));
2242 lcl_RemoveTabs(aText);
2243 pInputWin->SetTextString(aText);
2248 * @return true means new view created
2250 bool ScInputHandler::DataChanging( sal_Unicode cTyped, bool bFromCommand )
2252 if (pActiveViewSh)
2253 pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
2254 bInOwnChange = true; // disable ModifyHdl (reset in DataChanged)
2256 if ( eMode == SC_INPUT_NONE )
2257 return StartTable( cTyped, bFromCommand, false, nullptr );
2258 else
2259 return false;
2262 void ScInputHandler::DataChanged( bool bFromTopNotify, bool bSetModified )
2264 ImplCreateEditEngine();
2266 if (eMode==SC_INPUT_NONE)
2267 eMode = SC_INPUT_TYPE;
2269 if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify )
2271 // table EditEngine is formatted below, input line needs formatting after paste
2272 // #i20282# not when called from the input line's modify handler
2273 pTopView->GetEditEngine()->QuickFormatDoc( true );
2275 // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it
2276 // can't safely access the EditEngine's current view, so the cursor has to be
2277 // shown again here.
2278 pTopView->ShowCursor();
2281 if (bSetModified)
2282 bModified = true;
2283 bSelIsRef = false;
2285 if ( pRangeFindList && !bInRangeUpdate )
2286 RemoveRangeFinder(); // Delete attributes and labels
2288 UpdateParenthesis(); // Highlight parentheses anew
2290 if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE)
2292 OUString aText;
2293 if (pInputWin)
2294 aText = ScEditUtil::GetMultilineString(*mpEditEngine);
2295 else
2296 aText = GetEditText(mpEditEngine.get());
2297 lcl_RemoveTabs(aText);
2299 if ( pInputWin )
2300 pInputWin->SetTextString( aText );
2302 if (comphelper::LibreOfficeKit::isActive())
2304 if (pActiveViewSh)
2305 pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8().getStr());
2309 // If the cursor is before the end of a paragraph, parts are being pushed to
2310 // the right (independently from the eMode) -> Adapt View!
2311 // If the cursor is at the end, the StatusHandler of the ViewData is sufficient.
2313 // First make sure the status handler is called now if the cursor
2314 // is outside the visible area
2315 mpEditEngine->QuickFormatDoc();
2317 EditView* pActiveView = pTopView ? pTopView : pTableView;
2318 if (pActiveView && pActiveViewSh)
2320 ScViewData& rViewData = pActiveViewSh->GetViewData();
2322 bool bNeedGrow = ( rViewData.GetEditAdjust() != SvxAdjust::Left ); // Always right-aligned
2323 if (!bNeedGrow)
2325 // Cursor before the end?
2326 ESelection aSel = pActiveView->GetSelection();
2327 aSel.Adjust();
2328 bNeedGrow = ( aSel.nEndPos != mpEditEngine->GetTextLen(aSel.nEndPara) );
2330 if (!bNeedGrow)
2332 bNeedGrow = rViewData.GetDocument()->IsLayoutRTL( rViewData.GetTabNo() );
2334 if (bNeedGrow)
2336 // Adjust inplace view
2337 rViewData.EditGrowY();
2338 rViewData.EditGrowX();
2342 UpdateFormulaMode();
2343 bTextValid = false; // Changes only in the EditEngine
2344 bInOwnChange = false;
2347 void ScInputHandler::UpdateFormulaMode()
2349 SfxApplication* pSfxApp = SfxGetpApp();
2351 bool bIsFormula = !bProtected && mpEditEngine->GetParagraphCount() == 1;
2352 if (bIsFormula)
2354 const OUString& rText = mpEditEngine->GetText(0);
2355 bIsFormula = !rText.isEmpty() &&
2356 (rText[0] == '=' || rText[0] == '+' || rText[0] == '-');
2359 // formula mode in online is not usable in collaborative mode,
2360 // this is a workaround for disabling formula mode in online
2361 // when there is more than a single view
2362 if (comphelper::LibreOfficeKit::isActive()
2363 && SfxViewShell::GetActiveShells(/*only visible shells*/ false) > 1)
2365 // we look for not visible shells, too, since this method can be
2366 // invoked by a TabViewShell ctor and at such a stage the view
2367 // is not yet visible,
2368 bIsFormula = false;
2371 if ( bIsFormula )
2373 if (!bFormulaMode)
2375 bFormulaMode = true;
2376 pRefViewSh = pActiveViewSh;
2377 pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2378 SC_MOD()->SetRefInputHdl(this);
2379 if (pInputWin)
2380 pInputWin->SetFormulaMode(true);
2382 if ( bAutoComplete )
2383 GetFormulaData();
2385 UpdateParenthesis();
2386 UpdateAutoCorrFlag();
2389 else // Deactivate
2391 if (bFormulaMode)
2393 ShowRefFrame();
2394 bFormulaMode = false;
2395 pRefViewSh = nullptr;
2396 pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2397 SC_MOD()->SetRefInputHdl(nullptr);
2398 if (pInputWin)
2399 pInputWin->SetFormulaMode(false);
2400 UpdateAutoCorrFlag();
2405 void ScInputHandler::ShowRefFrame()
2407 // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat
2408 // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh.
2409 // A local variable is used instead.
2410 ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
2411 if ( pRefViewSh && pRefViewSh != pVisibleSh )
2413 bool bFound = false;
2414 SfxViewFrame* pRefFrame = pRefViewSh->GetViewFrame();
2415 SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst();
2416 while ( pOneFrame && !bFound )
2418 if ( pOneFrame == pRefFrame )
2419 bFound = true;
2420 pOneFrame = SfxViewFrame::GetNext( *pOneFrame );
2423 if (bFound)
2425 // We count on Activate working synchronously here
2426 // (pActiveViewSh is set while doing so)
2427 pRefViewSh->SetActive(); // Appear and SetViewFrame
2429 // pLastState is set correctly in the NotifyChange from the Activate
2431 else
2433 OSL_FAIL("ViewFrame for reference input is not here anymore");
2438 void ScInputHandler::RemoveSelection()
2440 EditView* pActiveView = pTopView ? pTopView : pTableView;
2441 if (!pActiveView)
2442 return;
2444 ESelection aSel = pActiveView->GetSelection();
2445 aSel.nStartPara = aSel.nEndPara;
2446 aSel.nStartPos = aSel.nEndPos;
2447 if (pTableView)
2448 pTableView->SetSelection( aSel );
2449 if (pTopView)
2450 pTopView->SetSelection( aSel );
2453 void ScInputHandler::InvalidateAttribs()
2455 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
2456 if (pViewFrm)
2458 SfxBindings& rBindings = pViewFrm->GetBindings();
2460 rBindings.Invalidate( SID_ATTR_CHAR_FONT );
2461 rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
2462 rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
2464 rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
2465 rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
2466 rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
2467 rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
2468 rBindings.Invalidate( SID_ULINE_VAL_NONE );
2469 rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
2470 rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
2471 rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
2473 rBindings.Invalidate( SID_HYPERLINK_GETLINK );
2475 rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
2476 rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
2477 rBindings.Invalidate( SID_SET_SUB_SCRIPT );
2478 rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
2479 rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
2483 // --------------- public methods --------------------------------------------
2485 void ScInputHandler::SetMode( ScInputMode eNewMode, const OUString* pInitText, ScEditEngineDefaulter* pTopEngine )
2487 if ( eMode == eNewMode )
2488 return;
2490 ImplCreateEditEngine();
2492 if (bProtected)
2494 eMode = SC_INPUT_NONE;
2495 StopInputWinEngine( true );
2496 if (pActiveViewSh)
2497 pActiveViewSh->GetActiveWin()->GrabFocus();
2498 return;
2501 if (eNewMode != SC_INPUT_NONE && pActiveViewSh)
2502 // Disable paste mode when edit mode starts.
2503 pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
2505 bInOwnChange = true; // disable ModifyHdl (reset below)
2507 ScInputMode eOldMode = eMode;
2508 eMode = eNewMode;
2509 if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode)
2510 StopInputWinEngine( false );
2512 if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE)
2514 if (eOldMode == SC_INPUT_NONE) // not if switching between modes
2516 if (StartTable(0, false, eMode == SC_INPUT_TABLE, pTopEngine))
2518 if (pActiveViewSh)
2519 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
2523 if (pInitText)
2525 mpEditEngine->SetText(*pInitText);
2526 bModified = true;
2529 sal_Int32 nPara = mpEditEngine->GetParagraphCount()-1;
2530 sal_Int32 nLen = mpEditEngine->GetText(nPara).getLength();
2531 sal_uInt16 nCount = mpEditEngine->GetViewCount();
2533 for (sal_uInt16 i=0; i<nCount; i++)
2535 if ( eMode == SC_INPUT_TABLE && eOldMode == SC_INPUT_TOP )
2537 // Keep Selection
2539 else
2541 mpEditEngine->GetView(i)->
2542 SetSelection( ESelection( nPara, nLen, nPara, nLen ) );
2544 mpEditEngine->GetView(i)->ShowCursor(false);
2548 UpdateActiveView();
2549 if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE)
2551 if (pTableView)
2552 pTableView->SetEditEngineUpdateMode(true);
2554 else
2556 if (pTopView)
2557 pTopView->SetEditEngineUpdateMode(true);
2560 if (eNewMode != eOldMode)
2561 UpdateFormulaMode();
2563 bInOwnChange = false;
2567 * @return true if rString only contains digits (no autocorrect then)
2569 static bool lcl_IsNumber(const OUString& rString)
2571 sal_Int32 nLen = rString.getLength();
2572 for (sal_Int32 i=0; i<nLen; i++)
2574 sal_Unicode c = rString[i];
2575 if ( c < '0' || c > '9' )
2576 return false;
2578 return true;
2581 static void lcl_SelectionToEnd( EditView* pView )
2583 if ( pView )
2585 EditEngine* pEngine = pView->GetEditEngine();
2586 sal_Int32 nParCnt = pEngine->GetParagraphCount();
2587 if ( nParCnt == 0 )
2588 nParCnt = 1;
2589 ESelection aSel( nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); // empty selection, cursor at the end
2590 pView->SetSelection( aSel );
2594 void ScInputHandler::EnterHandler( ScEnterMode nBlockMode )
2596 if (!mbDocumentDisposing && comphelper::LibreOfficeKit::isActive()
2597 && pActiveViewSh != SfxViewShell::Current())
2598 return;
2600 // Macro calls for validity can cause a lot of problems, so inhibit
2601 // nested calls of EnterHandler().
2602 if (bInEnterHandler) return;
2603 bInEnterHandler = true;
2604 bInOwnChange = true; // disable ModifyHdl (reset below)
2606 ImplCreateEditEngine();
2608 bool bMatrix = ( nBlockMode == ScEnterMode::MATRIX );
2610 SfxApplication* pSfxApp = SfxGetpApp();
2611 std::unique_ptr<EditTextObject> pObject;
2612 std::unique_ptr<ScPatternAttr> pCellAttrs;
2613 bool bForget = false; // Remove due to validity?
2615 OUString aString = GetEditText(mpEditEngine.get());
2616 OUString aPreAutoCorrectString(aString);
2617 EditView* pActiveView = pTopView ? pTopView : pTableView;
2618 if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString))
2620 if (pColumnData && miAutoPosColumn != pColumnData->end())
2622 // #i47125# If AutoInput appended something, do the final AutoCorrect
2623 // with the cursor at the end of the input.
2624 lcl_SelectionToEnd(pTopView);
2625 lcl_SelectionToEnd(pTableView);
2628 vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr;
2630 if (pTopView)
2631 pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views
2632 if (pTableView)
2633 pTableView->CompleteAutoCorrect(pFrameWin);
2634 aString = GetEditText(mpEditEngine.get());
2636 lcl_RemoveTabs(aString);
2637 lcl_RemoveTabs(aPreAutoCorrectString);
2639 // Test if valid (always with simple string)
2640 if ( bModified && nValidation && pActiveViewSh )
2642 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2643 const ScValidationData* pData = pDoc->GetValidationEntry( nValidation );
2644 if (pData && pData->HasErrMsg())
2646 // #i67990# don't use pLastPattern in EnterHandler
2647 const ScPatternAttr* pPattern = pDoc->GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
2649 bool bOk;
2651 if (pData->GetDataMode() == SC_VALID_CUSTOM)
2653 bOk = pData->IsDataValidCustom( aString, *pPattern, aCursorPos, ScValidationData::CustomValidationPrivateAccess() );
2655 else
2657 bOk = pData->IsDataValid( aString, *pPattern, aCursorPos );
2660 if (!bOk)
2662 if ( pActiveViewSh ) // If it came from MouseButtonDown
2663 pActiveViewSh->StopMarking(); // (the InfoBox consumes the MouseButtonUp)
2665 vcl::Window* pParent = nullptr;
2666 if (pActiveViewSh)
2667 pParent = &pActiveViewSh->GetViewFrame()->GetWindow();
2668 else
2669 pParent = Application::GetDefDialogParent();
2671 if (pData->DoError(pParent ? pParent->GetFrameWeld() : nullptr, aString, aCursorPos))
2672 bForget = true; // Do not take over input
2677 // Check for input into DataPilot table
2678 if ( bModified && pActiveViewSh && !bForget )
2680 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2681 ScDPObject* pDPObj = pDoc->GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
2682 if ( pDPObj )
2684 // Any input within the DataPilot table is either a valid renaming
2685 // or an invalid action - normal cell input is always aborted
2686 pActiveViewSh->DataPilotInput( aCursorPos, aString );
2687 bForget = true;
2691 std::vector<editeng::MisspellRanges> aMisspellRanges;
2692 mpEditEngine->CompleteOnlineSpelling();
2693 bool bSpellErrors = !bFormulaMode && mpEditEngine->HasOnlineSpellErrors();
2694 if ( bSpellErrors )
2696 // #i3820# If the spell checker flags numerical input as error,
2697 // it still has to be treated as number, not EditEngine object.
2698 if ( pActiveViewSh )
2700 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2701 // #i67990# don't use pLastPattern in EnterHandler
2702 const ScPatternAttr* pPattern = pDoc->GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
2703 if (pPattern)
2705 SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
2706 // without conditional format, as in ScColumn::SetString
2707 sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
2708 double nVal;
2709 if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) )
2711 bSpellErrors = false; // ignore the spelling errors
2717 // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc).
2718 // SetUpdateMode must come after CompleteOnlineSpelling.
2719 // The view is hidden in any case below (Broadcast).
2720 mpEditEngine->SetUpdateMode( false );
2722 if ( bModified && !bForget ) // What is being entered (text/object)?
2724 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
2725 if ( nParCnt == 0 )
2726 nParCnt = 1;
2728 bool bUniformAttribs = true;
2729 SfxItemSet aPara1Attribs = mpEditEngine->GetAttribs(0, 0, mpEditEngine->GetTextLen(0));
2730 for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara)
2732 SfxItemSet aPara2Attribs = mpEditEngine->GetAttribs(nPara, 0, mpEditEngine->GetTextLen(nPara));
2733 if (!(aPara1Attribs == aPara2Attribs))
2735 // Paragraph format different from that of the 1st paragraph.
2736 bUniformAttribs = false;
2737 break;
2741 ESelection aSel( 0, 0, nParCnt-1, mpEditEngine->GetTextLen(nParCnt-1) );
2742 SfxItemSet aOldAttribs = mpEditEngine->GetAttribs( aSel );
2743 const SfxPoolItem* pItem = nullptr;
2745 // Find common (cell) attributes before RemoveAdjust
2746 if ( pActiveViewSh && bUniformAttribs )
2748 std::unique_ptr<SfxItemSet> pCommonAttrs;
2749 for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++)
2751 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
2752 if ( eState == SfxItemState::SET &&
2753 nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING &&
2754 nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS &&
2755 *pItem != pEditDefaults->Get(nId) )
2757 if ( !pCommonAttrs )
2758 pCommonAttrs.reset(new SfxItemSet( mpEditEngine->GetEmptyItemSet() ));
2759 pCommonAttrs->Put( *pItem );
2763 if ( pCommonAttrs )
2765 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2766 pCellAttrs = std::make_unique<ScPatternAttr>(pDoc->GetPool());
2767 pCellAttrs->GetFromEditItemSet( pCommonAttrs.get() );
2771 // Clear ParaAttribs (including adjustment)
2772 RemoveAdjust();
2774 bool bAttrib = false; // Formatting present?
2776 // check if EditObject is needed
2777 if (nParCnt > 1)
2778 bAttrib = true;
2779 else
2781 for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++)
2783 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
2784 if (eState == SfxItemState::DONTCARE)
2785 bAttrib = true;
2786 else if (eState == SfxItemState::SET)
2788 // Keep same items in EditEngine as in ScEditAttrTester
2789 if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING ||
2790 nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS )
2792 if ( *pItem != pEditDefaults->Get(nId) )
2793 bAttrib = true;
2798 // Contains fields?
2799 SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, false );
2800 if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
2801 bAttrib = true;
2803 // Not converted characters?
2804 SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, false );
2805 if ( eConvState == SfxItemState::DONTCARE || eConvState == SfxItemState::SET )
2806 bAttrib = true;
2808 // Always recognize formulas as formulas
2809 // We still need the preceding test due to cell attributes
2812 if (bSpellErrors)
2813 mpEditEngine->GetAllMisspellRanges(aMisspellRanges);
2815 if (bMatrix)
2816 bAttrib = false;
2818 if (bAttrib)
2820 mpEditEngine->ClearSpellErrors();
2821 pObject = mpEditEngine->CreateTextObject();
2823 else if (bAutoComplete) // Adjust Upper/Lower case
2825 // Perform case-matching only when the typed text is partial.
2826 if (pColumnData && aAutoSearch.getLength() < aString.getLength())
2827 aString = getExactMatch(*pColumnData, aString);
2831 // Don't rely on ShowRefFrame switching the active view synchronously
2832 // execute the function directly on the correct view's bindings instead
2833 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
2834 ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
2836 if (bFormulaMode)
2838 ShowRefFrame();
2840 if (pExecuteSh)
2842 pExecuteSh->SetTabNo(aCursorPos.Tab());
2843 pExecuteSh->ActiveGrabFocus();
2846 bFormulaMode = false;
2847 pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2848 SC_MOD()->SetRefInputHdl(nullptr);
2849 if (pInputWin)
2850 pInputWin->SetFormulaMode(false);
2851 UpdateAutoCorrFlag();
2853 pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
2854 DeleteRangeFinder();
2855 ResetAutoPar();
2857 bool bOldMod = bModified;
2859 bModified = false;
2860 bSelIsRef = false;
2861 eMode = SC_INPUT_NONE;
2862 StopInputWinEngine(true);
2864 // Text input (through number formats) or ApplySelectionPattern modify
2865 // the cell's attributes, so pLastPattern is no longer valid
2866 pLastPattern = nullptr;
2868 if (bOldMod && !bProtected && !bForget)
2870 bool bInsertPreCorrectedString = true;
2871 // No typographic quotes in formulas
2872 if (aString.startsWith("="))
2874 SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get().GetAutoCorrect();
2875 if ( pAuto )
2877 bInsertPreCorrectedString = false;
2878 OUString aReplace(pAuto->GetStartDoubleQuote());
2879 if( aReplace.isEmpty() )
2880 aReplace = ScGlobal::pLocaleData->getDoubleQuotationMarkStart();
2881 if( aReplace != "\"" )
2882 aString = aString.replaceAll( aReplace, "\"" );
2884 aReplace = OUString(pAuto->GetEndDoubleQuote());
2885 if( aReplace.isEmpty() )
2886 aReplace = ScGlobal::pLocaleData->getDoubleQuotationMarkEnd();
2887 if( aReplace != "\"" )
2888 aString = aString.replaceAll( aReplace, "\"" );
2890 aReplace = OUString(pAuto->GetStartSingleQuote());
2891 if( aReplace.isEmpty() )
2892 aReplace = ScGlobal::pLocaleData->getQuotationMarkStart();
2893 if( aReplace != "'" )
2894 aString = aString.replaceAll( aReplace, "'" );
2896 aReplace = OUString(pAuto->GetEndSingleQuote());
2897 if( aReplace.isEmpty() )
2898 aReplace = ScGlobal::pLocaleData->getQuotationMarkEnd();
2899 if( aReplace != "'" )
2900 aString = aString.replaceAll( aReplace, "'");
2904 pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint ) );
2906 if ( pExecuteSh )
2908 SfxBindings& rBindings = pExecuteSh->GetViewFrame()->GetBindings();
2910 sal_uInt16 nId = FID_INPUTLINE_ENTER;
2911 if ( nBlockMode == ScEnterMode::BLOCK )
2912 nId = FID_INPUTLINE_BLOCK;
2913 else if ( nBlockMode == ScEnterMode::MATRIX )
2914 nId = FID_INPUTLINE_MATRIX;
2916 const SfxPoolItem* aArgs[2];
2917 aArgs[1] = nullptr;
2919 if ( bInsertPreCorrectedString && aString != aPreAutoCorrectString )
2921 ScInputStatusItem aItem(FID_INPUTLINE_STATUS,
2922 aCursorPos, aCursorPos, aCursorPos,
2923 aPreAutoCorrectString, pObject.get());
2924 aArgs[0] = &aItem;
2925 rBindings.Execute(nId, aArgs);
2928 ScInputStatusItem aItemCorrected(FID_INPUTLINE_STATUS,
2929 aCursorPos, aCursorPos, aCursorPos,
2930 aString, pObject.get());
2931 if ( !aMisspellRanges.empty() )
2932 aItemCorrected.SetMisspellRanges(&aMisspellRanges);
2934 aArgs[0] = &aItemCorrected;
2935 rBindings.Execute(nId, aArgs);
2938 pLastState.reset(); // pLastState still contains the old text
2940 else
2941 pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
2943 if ( bOldMod && pExecuteSh && pCellAttrs && !bForget )
2945 // Combine with input?
2946 pExecuteSh->ApplySelectionPattern( *pCellAttrs, true );
2947 pExecuteSh->AdjustBlockHeight();
2950 HideTip();
2951 HideTipBelow();
2953 nFormSelStart = nFormSelEnd = 0;
2954 aFormText.clear();
2956 bInOwnChange = false;
2957 bInEnterHandler = false;
2960 void ScInputHandler::CancelHandler()
2962 bInOwnChange = true; // Also without FormulaMode due to FunctionsAutoPilot
2964 ImplCreateEditEngine();
2966 bModified = false;
2968 // Don't rely on ShowRefFrame switching the active view synchronously
2969 // execute the function directly on the correct view's bindings instead
2970 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
2971 ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
2973 if (bFormulaMode)
2975 ShowRefFrame();
2976 if (pExecuteSh)
2978 pExecuteSh->SetTabNo(aCursorPos.Tab());
2979 pExecuteSh->ActiveGrabFocus();
2981 bFormulaMode = false;
2982 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2983 SC_MOD()->SetRefInputHdl(nullptr);
2984 if (pInputWin)
2985 pInputWin->SetFormulaMode(false);
2986 UpdateAutoCorrFlag();
2988 pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
2989 DeleteRangeFinder();
2990 ResetAutoPar();
2992 eMode = SC_INPUT_NONE;
2993 StopInputWinEngine( true );
2994 if (pExecuteSh)
2995 pExecuteSh->StopEditShell();
2997 aCursorPos.Set(MAXCOL+1,0,0); // Invalid flag
2998 mpEditEngine->SetText(OUString());
3000 if ( !pLastState && pExecuteSh )
3001 pExecuteSh->UpdateInputHandler( true ); // Update status again
3002 else
3003 NotifyChange( pLastState.get(), true );
3005 nFormSelStart = nFormSelEnd = 0;
3006 aFormText.clear();
3008 bInOwnChange = false;
3011 bool ScInputHandler::IsModalMode( const SfxObjectShell* pDocSh )
3013 // References to unnamed document; that doesn't work
3014 return bFormulaMode && pRefViewSh
3015 && pRefViewSh->GetViewData().GetDocument()->GetDocumentShell() != pDocSh
3016 && !pDocSh->HasName();
3019 void ScInputHandler::AddRefEntry()
3021 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
3022 UpdateActiveView();
3023 if (!pTableView && !pTopView)
3024 return; // E.g. FillMode
3026 DataChanging(); // Cannot be new
3028 RemoveSelection();
3029 OUString aText = GetEditText(mpEditEngine.get());
3030 sal_Unicode cLastChar = 0;
3031 sal_Int32 nPos = aText.getLength() - 1;
3032 while (nPos >= 0 && ((cLastChar = aText[nPos]) == ' ')) //checking space
3033 --nPos;
3035 bool bAppendSeparator = (cLastChar != '(' && cLastChar != cSep && cLastChar != '=');
3036 if (bAppendSeparator)
3038 if (pTableView)
3039 pTableView->InsertText( OUString(cSep) );
3040 if (pTopView)
3041 pTopView->InsertText( OUString(cSep) );
3044 DataChanged();
3047 void ScInputHandler::SetReference( const ScRange& rRef, const ScDocument* pDoc )
3049 HideTip();
3051 const ScDocument* pThisDoc = nullptr;
3052 bool bOtherDoc = (pRefViewSh && ((pThisDoc = pRefViewSh->GetViewData().GetDocument()) != pDoc));
3053 if (bOtherDoc && !pDoc->GetDocumentShell()->HasName())
3055 // References to unnamed document; that doesn't work
3056 // SetReference should not be called, then
3057 return;
3059 if (!pThisDoc)
3060 pThisDoc = pDoc;
3062 UpdateActiveView();
3063 if (!pTableView && !pTopView)
3064 return; // E.g. FillMode
3066 // Never overwrite the "="!
3067 EditView* pActiveView = pTopView ? pTopView : pTableView;
3068 ESelection aSel = pActiveView->GetSelection();
3069 aSel.Adjust();
3070 if ( aSel.nStartPara == 0 && aSel.nStartPos == 0 )
3071 return;
3073 DataChanging(); // Cannot be new
3075 // Turn around selection if backwards (TODO: Do we really need to do that?)
3076 if (pTableView)
3078 ESelection aTabSel = pTableView->GetSelection();
3079 if (aTabSel.nStartPos > aTabSel.nEndPos && aTabSel.nStartPara == aTabSel.nEndPara)
3081 aTabSel.Adjust();
3082 pTableView->SetSelection(aTabSel);
3085 if (pTopView)
3087 ESelection aTopSel = pTopView->GetSelection();
3088 if (aTopSel.nStartPos > aTopSel.nEndPos && aTopSel.nStartPara == aTopSel.nEndPara)
3090 aTopSel.Adjust();
3091 pTopView->SetSelection(aTopSel);
3095 // Create string from reference, in the syntax of the document being edited.
3096 OUString aRefStr;
3097 const ScAddress::Details aAddrDetails( pThisDoc, aCursorPos );
3098 if (bOtherDoc)
3100 // Reference to other document
3101 OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab");
3103 // Always 3D and absolute.
3104 OUString aTmp(rRef.Format( ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, pDoc, aAddrDetails));
3106 SfxObjectShell* pObjSh = pDoc->GetDocumentShell();
3107 // #i75893# convert escaped URL of the document to something user friendly
3108 OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
3110 switch(aAddrDetails.eConv)
3112 case formula::FormulaGrammar::CONV_XL_A1 :
3113 case formula::FormulaGrammar::CONV_XL_OOX :
3114 case formula::FormulaGrammar::CONV_XL_R1C1 :
3115 aRefStr = "[\'" + aFileName + "']";
3116 break;
3117 case formula::FormulaGrammar::CONV_OOO :
3118 default:
3119 aRefStr = "\'" + aFileName + "'#";
3120 break;
3122 aRefStr += aTmp;
3124 else
3126 if ( rRef.aStart.Tab() != aCursorPos.Tab() ||
3127 rRef.aStart.Tab() != rRef.aEnd.Tab() )
3128 // pointer-selected => absolute sheet reference
3129 aRefStr = rRef.Format(ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, pDoc, aAddrDetails);
3130 else
3131 aRefStr = rRef.Format(ScRefFlags::VALID, pDoc, aAddrDetails);
3134 if (pTableView || pTopView)
3136 if (pTableView)
3137 pTableView->InsertText( aRefStr, true );
3138 if (pTopView)
3139 pTopView->InsertText( aRefStr, true );
3141 DataChanged();
3144 bSelIsRef = true;
3147 void ScInputHandler::InsertFunction( const OUString& rFuncName, bool bAddPar )
3149 if ( eMode == SC_INPUT_NONE )
3151 OSL_FAIL("InsertFunction, not during input mode");
3152 return;
3155 UpdateActiveView();
3156 if (!pTableView && !pTopView)
3157 return; // E.g. FillMode
3159 DataChanging(); // Cannot be new
3161 OUString aText = rFuncName;
3162 if (bAddPar)
3163 aText += "()";
3165 if (pTableView)
3167 pTableView->InsertText( aText );
3168 if (bAddPar)
3170 ESelection aSel = pTableView->GetSelection();
3171 --aSel.nStartPos;
3172 --aSel.nEndPos;
3173 pTableView->SetSelection(aSel);
3176 if (pTopView)
3178 pTopView->InsertText( aText );
3179 if (bAddPar)
3181 ESelection aSel = pTopView->GetSelection();
3182 --aSel.nStartPos;
3183 --aSel.nEndPos;
3184 pTopView->SetSelection(aSel);
3188 DataChanged();
3190 if (bAddPar)
3191 AutoParAdded();
3194 void ScInputHandler::ClearText()
3196 if ( eMode == SC_INPUT_NONE )
3198 OSL_FAIL("ClearText, not during input mode");
3199 return;
3202 UpdateActiveView();
3203 if (!pTableView && !pTopView)
3204 return; // E.g. FillMode
3206 DataChanging(); // Cannot be new
3208 if (pTableView)
3210 pTableView->GetEditEngine()->SetText( "" );
3211 pTableView->SetSelection( ESelection(0,0, 0,0) );
3213 if (pTopView)
3215 pTopView->GetEditEngine()->SetText( "" );
3216 pTopView->SetSelection( ESelection(0,0, 0,0) );
3219 DataChanged();
3222 bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false */ )
3224 if (!bOptLoaded)
3226 bAutoComplete = SC_MOD()->GetAppOptions().GetAutoComplete();
3227 bOptLoaded = true;
3230 vcl::KeyCode aCode = rKEvt.GetKeyCode();
3231 sal_uInt16 nModi = aCode.GetModifier();
3232 bool bShift = aCode.IsShift();
3233 bool bControl = aCode.IsMod1();
3234 bool bAlt = aCode.IsMod2();
3235 sal_uInt16 nCode = aCode.GetCode();
3236 sal_Unicode nChar = rKEvt.GetCharCode();
3238 if (bAlt && !bControl && nCode != KEY_RETURN)
3239 // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not.
3240 return false;
3242 if (!bControl && nCode == KEY_TAB)
3244 // Normal TAB moves the cursor right.
3245 EnterHandler();
3247 if (pActiveViewSh)
3248 pActiveViewSh->FindNextUnprot( bShift, true );
3249 return true;
3252 bool bInputLine = ( eMode==SC_INPUT_TOP );
3254 bool bUsed = false;
3255 bool bSkip = false;
3256 bool bDoEnter = false;
3258 switch ( nCode )
3260 case KEY_RETURN:
3261 // New line when in the input line and Shift/Ctrl-Enter is pressed,
3262 // or when in a cell and Ctrl-Enter is pressed.
3263 if ((pInputWin && bInputLine && bControl != bShift) || (!bInputLine && bControl && !bShift))
3265 bDoEnter = true;
3267 else if (nModi == 0 && nTipVisible && pFormulaData && miAutoPosFormula != pFormulaData->end())
3269 PasteFunctionData();
3270 bUsed = true;
3272 else if ( nModi == 0 && nTipVisible && !aManualTip.isEmpty() )
3274 PasteManualTip();
3275 bUsed = true;
3277 else
3279 ScEnterMode nMode = ScEnterMode::NORMAL;
3280 if ( bShift && bControl )
3281 nMode = ScEnterMode::MATRIX;
3282 else if ( bAlt )
3283 nMode = ScEnterMode::BLOCK;
3284 EnterHandler( nMode );
3286 if (pActiveViewSh)
3287 pActiveViewSh->MoveCursorEnter( bShift && !bControl );
3289 bUsed = true;
3291 break;
3292 case KEY_TAB:
3293 if (bControl && !bAlt)
3295 if (pFormulaData && nTipVisible && miAutoPosFormula != pFormulaData->end())
3297 // Iterate
3298 NextFormulaEntry( bShift );
3299 bUsed = true;
3301 else if (pColumnData && bUseTab && miAutoPosColumn != pColumnData->end())
3303 // Iterate through AutoInput entries
3304 NextAutoEntry( bShift );
3305 bUsed = true;
3308 break;
3309 case KEY_ESCAPE:
3310 if ( nTipVisible )
3312 HideTip();
3313 bUsed = true;
3315 else if( nTipVisibleSec )
3317 HideTipBelow();
3318 bUsed = true;
3320 else if (eMode != SC_INPUT_NONE)
3322 CancelHandler();
3323 bUsed = true;
3325 else
3326 bSkip = true;
3327 break;
3328 case KEY_F2:
3329 if ( !bShift && !bControl && !bAlt && eMode == SC_INPUT_TABLE )
3331 eMode = SC_INPUT_TYPE;
3332 bUsed = true;
3334 break;
3337 // Only execute cursor keys if already in EditMode
3338 // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator)
3339 bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt);
3340 bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Treat Insert like Cursorkeys
3341 if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) ||
3342 ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) )
3344 HideTip();
3345 HideTipBelow();
3347 if (bSelIsRef)
3349 RemoveSelection();
3350 bSelIsRef = false;
3353 UpdateActiveView();
3354 bool bNewView = DataChanging( nChar );
3356 if (bProtected) // Protected cell?
3357 bUsed = true; // Don't forward KeyEvent
3358 else // Changes allowed
3360 if (bNewView ) // Create anew
3362 if (pActiveViewSh)
3363 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
3364 UpdateActiveView();
3365 if (eMode==SC_INPUT_NONE)
3366 if (pTableView || pTopView)
3368 OUString aStrLoP;
3370 if ( bStartEdit && bCellHasPercentFormat && ((nChar >= '0' && nChar <= '9') || nChar == '-') )
3371 aStrLoP = "%";
3373 if (pTableView)
3375 pTableView->GetEditEngine()->SetText( aStrLoP );
3376 if ( !aStrLoP.isEmpty() )
3377 pTableView->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
3379 // Don't call SetSelection if the string is empty anyway,
3380 // to avoid breaking the bInitial handling in ScViewData::EditGrowY
3382 if (pTopView)
3384 pTopView->GetEditEngine()->SetText( aStrLoP );
3385 if ( !aStrLoP.isEmpty() )
3386 pTopView->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
3389 SyncViews();
3392 if (pTableView || pTopView)
3394 if (bDoEnter)
3396 if (pTableView)
3397 if( pTableView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
3398 bUsed = true;
3399 if (pTopView)
3400 if( pTopView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
3401 bUsed = true;
3403 else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() )
3405 SkipClosingPar();
3406 bUsed = true;
3408 else
3410 if (pTableView)
3412 if (pTopView)
3413 pTableView->SetControlWord(pTableView->GetControlWord() | EVControlBits::SINGLELINEPASTE);
3415 vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr;
3416 if ( pTableView->PostKeyEvent( rKEvt, pFrameWin ) )
3417 bUsed = true;
3419 pTableView->SetControlWord(pTableView->GetControlWord() & ~EVControlBits::SINGLELINEPASTE);
3421 if (pTopView)
3423 if ( bUsed && rKEvt.GetKeyCode().GetFunction() == KeyFuncType::CUT )
3424 pTopView->DeleteSelected();
3425 else if ( pTopView->PostKeyEvent( rKEvt ) )
3426 bUsed = true;
3430 // AutoInput:
3431 if ( bUsed && bAutoComplete )
3433 bUseTab = false;
3434 if (pFormulaData)
3435 miAutoPosFormula = pFormulaData->end(); // do not search further
3436 if (pColumnData)
3437 miAutoPosColumn = pColumnData->end();
3439 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
3440 if ( nChar && nChar != 8 && nChar != 127 && // no 'backspace', no 'delete'
3441 KeyFuncType::CUT != eFunc) // and no 'CTRL-X'
3443 if (bFormulaMode)
3444 UseFormulaData();
3445 else
3446 UseColData();
3450 // When the selection is changed manually or an opening parenthesis
3451 // is typed, stop overwriting parentheses
3452 if ( bUsed && nChar == '(' )
3453 ResetAutoPar();
3455 if ( KEY_INSERT == nCode )
3457 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
3458 if (pViewFrm)
3459 pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
3461 if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) )
3463 ShowTipCursor();
3465 if( bUsed && bFormulaMode && nCode == KEY_BACKSPACE )
3467 UseFormulaData();
3472 // #i114511# don't count cursor keys as modification
3473 bool bSetModified = !bCursorKey;
3474 DataChanged(false, bSetModified); // also calls UpdateParenthesis()
3475 InvalidateAttribs(); //! in DataChanged?
3479 if (pTopView && eMode != SC_INPUT_NONE)
3480 SyncViews();
3482 return bUsed;
3485 void ScInputHandler::InputCommand( const CommandEvent& rCEvt )
3487 if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
3489 // For CommandEventId::CursorPos, do as little as possible, because
3490 // with remote VCL, even a ShowCursor will generate another event.
3491 if ( eMode != SC_INPUT_NONE )
3493 UpdateActiveView();
3494 if (pTableView || pTopView)
3496 if (pTableView)
3497 pTableView->Command( rCEvt );
3498 else if (pTopView) // call only once
3499 pTopView->Command( rCEvt );
3503 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
3505 if ( eMode != SC_INPUT_NONE )
3507 UpdateActiveView();
3508 if (pTableView || pTopView)
3510 if (pTableView)
3511 pTableView->Command( rCEvt );
3512 else if (pTopView) // call only once
3513 pTopView->Command( rCEvt );
3517 else
3519 if (!bOptLoaded)
3521 bAutoComplete = SC_MOD()->GetAppOptions().GetAutoComplete();
3522 bOptLoaded = true;
3525 HideTip();
3526 HideTipBelow();
3528 if ( bSelIsRef )
3530 RemoveSelection();
3531 bSelIsRef = false;
3534 UpdateActiveView();
3535 bool bNewView = DataChanging( 0, true );
3537 if (!bProtected) // changes allowed
3539 if (bNewView) // create new edit view
3541 if (pActiveViewSh)
3542 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
3543 UpdateActiveView();
3544 if (eMode==SC_INPUT_NONE)
3545 if (pTableView || pTopView)
3547 OUString aStrLoP;
3548 if (pTableView)
3550 pTableView->GetEditEngine()->SetText( aStrLoP );
3551 pTableView->SetSelection( ESelection(0,0, 0,0) );
3553 if (pTopView)
3555 pTopView->GetEditEngine()->SetText( aStrLoP );
3556 pTopView->SetSelection( ESelection(0,0, 0,0) );
3559 SyncViews();
3562 if (pTableView || pTopView)
3564 if (pTableView)
3565 pTableView->Command( rCEvt );
3566 if (pTopView)
3567 pTopView->Command( rCEvt );
3569 if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
3571 // AutoInput after ext text input
3573 if (pFormulaData)
3574 miAutoPosFormula = pFormulaData->end();
3575 if (pColumnData)
3576 miAutoPosColumn = pColumnData->end();
3578 if (bFormulaMode)
3579 UseFormulaData();
3580 else
3581 UseColData();
3585 DataChanged(); // calls UpdateParenthesis()
3586 InvalidateAttribs(); //! in DataChanged ?
3589 if (pTopView && eMode != SC_INPUT_NONE)
3590 SyncViews();
3594 void ScInputHandler::NotifyChange( const ScInputHdlState* pState,
3595 bool bForce, ScTabViewShell* pSourceSh,
3596 bool bStopEditing)
3598 // If the call originates from a macro call in the EnterHandler,
3599 // return immediately and don't mess up the status
3600 if (bInEnterHandler)
3601 return;
3603 bool bRepeat = (pState == pLastState.get());
3604 if (!bRepeat && pState && pLastState)
3605 bRepeat = (*pState == *pLastState);
3606 if (bRepeat && !bForce)
3607 return;
3609 bInOwnChange = true; // disable ModifyHdl (reset below)
3611 if ( pState && !pLastState ) // Enable again
3612 bForce = true;
3614 bool bHadObject = pLastState && pLastState->GetEditData();
3616 //! Before EditEngine gets eventually created (so it gets the right pools)
3617 if ( pSourceSh )
3618 pActiveViewSh = pSourceSh;
3619 else
3620 pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
3622 ImplCreateEditEngine();
3624 if ( pState != pLastState.get() )
3626 pLastState.reset( pState ? new ScInputHdlState( *pState ) : nullptr);
3629 if ( pState && pActiveViewSh )
3631 ScModule* pScMod = SC_MOD();
3633 ScTabViewShell* pScTabViewShell = pScMod ? dynamic_cast<ScTabViewShell*>(pScMod->GetViewShell()) : nullptr;
3635 // Also take foreign reference input into account here (e.g. FunctionsAutoPilot),
3636 // FormEditData, if we're switching from Help to Calc:
3637 if ( !bFormulaMode && !pScMod->IsFormulaMode() &&
3638 ( !pScTabViewShell || !pScTabViewShell->GetFormEditData() ) )
3640 bool bIgnore = false;
3641 if ( bModified )
3643 if (pState->GetPos() != aCursorPos)
3645 if (!bProtected)
3646 EnterHandler();
3648 else
3649 bIgnore = true;
3652 if ( !bIgnore )
3654 const ScAddress& rSPos = pState->GetStartPos();
3655 const ScAddress& rEPos = pState->GetEndPos();
3656 const EditTextObject* pData = pState->GetEditData();
3657 OUString aString = pState->GetString();
3658 bool bTxtMod = false;
3659 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
3660 ScDocument& rDoc = pDocSh->GetDocument();
3662 aCursorPos = pState->GetPos();
3664 if ( pData )
3665 bTxtMod = true;
3666 else if ( bHadObject )
3667 bTxtMod = true;
3668 else if ( bTextValid )
3669 bTxtMod = ( aString != aCurrentText );
3670 else
3671 bTxtMod = ( aString != GetEditText(mpEditEngine.get()) );
3673 if ( bTxtMod || bForce )
3675 if (pData)
3677 mpEditEngine->SetText( *pData );
3678 if (pInputWin)
3679 aString = ScEditUtil::GetMultilineString(*mpEditEngine);
3680 else
3681 aString = GetEditText(mpEditEngine.get());
3682 lcl_RemoveTabs(aString);
3683 bTextValid = false;
3684 aCurrentText.clear();
3686 else
3688 aCurrentText = aString;
3689 bTextValid = true; //! To begin with remember as a string
3692 if ( pInputWin )
3693 pInputWin->SetTextString(aString);
3695 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
3696 pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8().getStr());
3699 if ( pInputWin || comphelper::LibreOfficeKit::isActive()) // Named range input
3701 OUString aPosStr;
3702 const ScAddress::Details aAddrDetails( &rDoc, aCursorPos );
3704 // Is the range a name?
3705 //! Find by Timer?
3706 if ( pActiveViewSh )
3707 pActiveViewSh->GetViewData().GetDocument()->
3708 GetRangeAtBlock( ScRange( rSPos, rEPos ), &aPosStr );
3710 if ( aPosStr.isEmpty() ) // Not a name -> format
3712 ScRefFlags nFlags = ScRefFlags::ZERO;
3713 if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 )
3714 nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS;
3715 if ( rSPos != rEPos )
3717 ScRange r(rSPos, rEPos);
3718 applyStartToEndFlags(nFlags);
3719 aPosStr = r.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
3721 else
3722 aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
3725 if (pInputWin)
3727 // Disable the accessible VALUE_CHANGE event
3728 bool bIsSuppressed = pInputWin->IsAccessibilityEventsSuppressed(false);
3729 pInputWin->SetAccessibilityEventsSuppressed(true);
3730 pInputWin->SetPosString(aPosStr);
3731 pInputWin->SetAccessibilityEventsSuppressed(bIsSuppressed);
3732 pInputWin->SetSumAssignMode();
3735 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
3736 pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS, aPosStr.toUtf8().getStr());
3739 if (bStopEditing)
3740 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
3742 // As long as the content is not edited, turn off online spelling.
3743 // Online spelling is turned back on in StartTable, after setting
3744 // the right language from cell attributes.
3746 EEControlBits nCntrl = mpEditEngine->GetControlWord();
3747 if ( nCntrl & EEControlBits::ONLINESPELLING )
3748 mpEditEngine->SetControlWord( nCntrl & ~EEControlBits::ONLINESPELLING );
3750 bModified = false;
3751 bSelIsRef = false;
3752 bProtected = false;
3753 bCommandErrorShown = false;
3757 if ( pInputWin)
3759 // Do not enable if RefDialog is open
3760 if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen())
3762 if ( !pInputWin->IsEnabled())
3764 pDelayTimer->Stop();
3765 pInputWin->Enable();
3768 else if(pScMod->IsRefDialogOpen())
3769 { // Because every document has its own InputWin,
3770 // we should start Timer again, because the input line may
3771 // still be active
3772 if ( !pDelayTimer->IsActive() )
3773 pDelayTimer->Start();
3777 else // !pState || !pActiveViewSh
3779 if ( !pDelayTimer->IsActive() )
3780 pDelayTimer->Start();
3783 HideTip();
3784 HideTipBelow();
3785 bInOwnChange = false;
3788 void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust )
3790 eAttrAdjust = eJust;
3791 UpdateAdjust( 0 );
3794 void ScInputHandler::ResetDelayTimer()
3796 if( pDelayTimer->IsActive() )
3798 pDelayTimer->Stop();
3799 if ( pInputWin )
3800 pInputWin->Enable();
3804 IMPL_LINK_NOARG( ScInputHandler, DelayTimer, Timer*, void )
3806 if ( nullptr == pLastState || SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen())
3808 //! New method at ScModule to query if function autopilot is open
3809 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
3810 if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
3812 if ( pInputWin)
3814 pInputWin->EnableButtons( false );
3815 pInputWin->Disable();
3818 else if ( !bFormulaMode ) // Keep formula e.g. for help
3820 bInOwnChange = true; // disable ModifyHdl (reset below)
3822 pActiveViewSh = nullptr;
3823 mpEditEngine->SetText( EMPTY_OUSTRING );
3824 if ( pInputWin )
3826 pInputWin->SetPosString( EMPTY_OUSTRING );
3827 pInputWin->SetTextString( EMPTY_OUSTRING );
3828 pInputWin->Disable();
3831 bInOwnChange = false;
3836 void ScInputHandler::InputSelection( const EditView* pView )
3838 SyncViews( pView );
3839 ShowTipCursor();
3840 UpdateParenthesis(); // Selection changed -> update parentheses highlighting
3842 // When the selection is changed manually, stop overwriting parentheses
3843 ResetAutoPar();
3846 void ScInputHandler::InputChanged( const EditView* pView, bool bFromNotify )
3848 UpdateActiveView();
3850 // #i20282# DataChanged needs to know if this is from the input line's modify handler
3851 bool bFromTopNotify = ( bFromNotify && pView == pTopView );
3853 bool bNewView = DataChanging(); //FIXME: Is this at all possible?
3854 aCurrentText = pView->GetEditEngine()->GetText(); // Also remember the string
3855 mpEditEngine->SetText( aCurrentText );
3856 DataChanged( bFromTopNotify );
3857 bTextValid = true; // Is set to false in DataChanged
3859 if ( pActiveViewSh )
3861 ScViewData& rViewData = pActiveViewSh->GetViewData();
3862 if ( bNewView )
3863 rViewData.GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
3865 rViewData.EditGrowY();
3866 rViewData.EditGrowX();
3869 SyncViews( pView );
3872 const OUString& ScInputHandler::GetEditString()
3874 if (mpEditEngine)
3876 aCurrentText = mpEditEngine->GetText(); // Always new from Engine
3877 bTextValid = true;
3880 return aCurrentText;
3883 Size ScInputHandler::GetTextSize()
3885 Size aSize;
3886 if ( mpEditEngine )
3887 aSize = Size( mpEditEngine->CalcTextWidth(), mpEditEngine->GetTextHeight() );
3889 return aSize;
3892 bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine )
3894 bool bRet = false;
3895 if (mpEditEngine)
3897 // Contains field?
3898 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
3899 SfxItemSet aSet = mpEditEngine->GetAttribs( ESelection(0,0,nParCnt,0) );
3900 SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
3901 if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
3903 // Copy content
3904 std::unique_ptr<EditTextObject> pObj = mpEditEngine->CreateTextObject();
3905 rDestEngine.SetText(*pObj);
3906 pObj.reset();
3908 // Delete attributes
3909 for (sal_Int32 i=0; i<nParCnt; i++)
3910 rDestEngine.RemoveCharAttribs( i );
3912 // Combine paragraphs
3913 while ( nParCnt > 1 )
3915 sal_Int32 nLen = rDestEngine.GetTextLen( 0 );
3916 ESelection aSel( 0,nLen, 1,0 );
3917 rDestEngine.QuickInsertText( OUString(' '), aSel ); // Replace line break with space
3918 --nParCnt;
3921 bRet = true;
3924 return bRet;
3928 * Methods for FunctionAutoPilot:
3929 * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
3931 void ScInputHandler::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd )
3933 rStart = nFormSelStart;
3934 rEnd = nFormSelEnd;
3937 EditView* ScInputHandler::GetFuncEditView()
3939 UpdateActiveView(); // Due to pTableView
3941 EditView* pView = nullptr;
3942 if ( pInputWin )
3944 pInputWin->MakeDialogEditView();
3945 pView = pInputWin->GetEditView();
3947 else
3949 if ( eMode != SC_INPUT_TABLE )
3951 bCreatingFuncView = true; // Don't display RangeFinder
3952 SetMode( SC_INPUT_TABLE );
3953 bCreatingFuncView = false;
3954 if ( pTableView )
3955 pTableView->GetEditEngine()->SetText( EMPTY_OUSTRING );
3957 pView = pTableView;
3960 return pView;
3963 void ScInputHandler::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd )
3965 if ( nStart <= nEnd )
3967 nFormSelStart = nStart;
3968 nFormSelEnd = nEnd;
3970 else
3972 nFormSelEnd = nStart;
3973 nFormSelStart = nEnd;
3976 EditView* pView = GetFuncEditView();
3977 if (pView)
3978 pView->SetSelection( ESelection(0,nStart, 0,nEnd) );
3980 bModified = true;
3983 void ScInputHandler::InputReplaceSelection( const OUString& rStr )
3985 if (!pRefViewSh)
3986 pRefViewSh = pActiveViewSh;
3988 OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken...");
3990 sal_Int32 nOldLen = nFormSelEnd - nFormSelStart;
3991 sal_Int32 nNewLen = rStr.getLength();
3993 OUStringBuffer aBuf(aFormText);
3994 if (nOldLen)
3995 aBuf.remove(nFormSelStart, nOldLen);
3996 if (nNewLen)
3997 aBuf.insert(nFormSelStart, rStr);
3999 aFormText = aBuf.makeStringAndClear();
4001 nFormSelEnd = nFormSelStart + nNewLen;
4003 EditView* pView = GetFuncEditView();
4004 if (pView)
4006 pView->SetEditEngineUpdateMode( false );
4007 pView->GetEditEngine()->SetText( aFormText );
4008 pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) );
4009 pView->SetEditEngineUpdateMode( true );
4011 bModified = true;
4014 void ScInputHandler::InputTurnOffWinEngine()
4016 bInOwnChange = true; // disable ModifyHdl (reset below)
4018 eMode = SC_INPUT_NONE;
4019 /* TODO: it would be better if there was some way to reset the input bar
4020 * engine instead of deleting and having it recreate through
4021 * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
4022 * fdo#72278 without reintroducing fdo#69971. */
4023 StopInputWinEngine(true);
4025 bInOwnChange = false;
4029 * ScInputHdlState
4031 ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos,
4032 const ScAddress& rStartPos,
4033 const ScAddress& rEndPos,
4034 const OUString& rString,
4035 const EditTextObject* pData )
4036 : aCursorPos ( rCurPos ),
4037 aStartPos ( rStartPos ),
4038 aEndPos ( rEndPos ),
4039 aString ( rString ),
4040 pEditData ( pData ? pData->Clone() : nullptr )
4044 ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy )
4046 *this = rCpy;
4049 ScInputHdlState::~ScInputHdlState()
4053 bool ScInputHdlState::operator==( const ScInputHdlState& r ) const
4055 return ( (aStartPos == r.aStartPos)
4056 && (aEndPos == r.aEndPos)
4057 && (aCursorPos == r.aCursorPos)
4058 && (aString == r.aString)
4059 && ScGlobal::EETextObjEqual( pEditData.get(), r.pEditData.get() ) );
4062 ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r )
4064 if (this != &r)
4066 aCursorPos = r.aCursorPos;
4067 aStartPos = r.aStartPos;
4068 aEndPos = r.aEndPos;
4069 aString = r.aString;
4070 pEditData.reset();
4071 if (r.pEditData)
4072 pEditData = r.pEditData->Clone();
4074 return *this;
4077 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */