1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
65 #include <uiitems.hxx>
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
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
);
127 it
= std::find_if(it
, itEnd
, lIsMatch
);
130 rResult
= it
->GetString();
131 return (++it
).base(); // convert the reverse iterator back to iterator.
136 ScTypedCaseStrSet::const_iterator it
= rDataSet
.begin(), itEnd
= rDataSet
.end();
139 it
= std::next(itPos
);
142 it
= std::find_if(it
, itEnd
, lIsMatch
);
145 rResult
= it
->GetString();
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();
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
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();
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();
192 bool bFirstTime
= true;
194 while ( it
!= itEnd
|| bFirstTime
)
197 if ( it
== rDataSet
.rend() ) // go to the first if reach the end
198 it
= rDataSet
.rbegin();
202 const ScTypedStrData
& rData
= *it
;
203 if ( rData
.GetStringType() == ScTypedStrData::Value
)
207 if ( !ScGlobal::GetpTransliteration()->isMatch(rStart
, rData
.GetString()) )
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
);
225 ScTypedCaseStrSet::const_iterator it
, itEnd
;
227 if ( it
== rDataSet
.end() )
228 it
= rDataSet
.begin();
230 bool bFirstTime
= true;
232 while ( it
!= itEnd
|| bFirstTime
)
235 if ( it
== rDataSet
.end() ) // go to the first if reach the end
236 it
= rDataSet
.begin();
240 const ScTypedStrData
& rData
= *it
;
241 if ( rData
.GetStringType() == ScTypedStrData::Value
)
245 if ( !ScGlobal::GetpTransliteration()->isMatch(rStart
, rData
.GetString()) )
249 rResultVec
.push_back(rData
.GetString()); // set the match data
251 retit
= it
; // remember first match iterator
256 if ( nCount
> 0 ) // at least one function has matched
258 return rDataSet
.end(); // no matching text found
263 void ScInputHandler::InitRangeFinder( const OUString
& rFormula
)
266 if ( !pActiveViewSh
|| !SC_MOD()->GetInputOptions().GetRangeFinder() )
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( ':' );
279 aDelimiters
= aDelimiters
.replaceAt( nColon
, 1, ""); // Delimiter without colon
280 sal_Int32 nDot
= aDelimiters
.indexOf(cSheetSep
);
282 aDelimiters
= aDelimiters
.replaceAt( nDot
, 1 , ""); // Delimiter without dot
284 const sal_Unicode
* pChar
= rFormula
.getStr();
285 sal_Int32 nLen
= rFormula
.getLength();
287 sal_Int32 nStart
= 0;
288 sal_uInt16 nCount
= 0;
290 while ( nPos
< nLen
&& nCount
< RANGEFIND_MAX
)
293 while ( nPos
<nLen
&& ScGlobal::UnicodeStrChr( aDelimiters
.getStr(), pChar
[nPos
] ) )
295 if ( pChar
[nPos
] == '"' ) // String
298 while (nPos
<nLen
&& pChar
[nPos
] != '"') // Skip until end
301 ++nPos
; // Separator or closing quote
304 // text between separators
308 bool bSingleQuoted
= false;
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
]))
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() )
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
) ) ==
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
);
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
);
375 // Do not skip last separator; could be a quote (?)
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
)
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() );
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;
440 bInRangeUpdate
= false;
442 long nDiff
= aNewStr
.getLength() - static_cast<long>(nOldEnd
-nOldStart
);
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 );
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
)
496 sal_Unicode c1
, c2
= 0;
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 )
557 // Odd number of quotes that we find ourselves in a string
558 bool bLookInString
= ((nQuotes
% 2) != 0);
559 bool bInString
= bLookInString
;
561 p1
= (nDir
< 0 ? p0
: p0
+ nLen
) ;
562 sal_uInt16 nLevel
= 1;
563 while ( p
!= p1
&& nLevel
)
568 bInString
= !bInString
;
569 if ( bLookInString
&& !bInString
)
570 p
= p1
; // That's it then
572 else if ( bInString
== bLookInString
)
582 return static_cast<sal_Int32
>(p
- p0
);
585 ScInputHandler::ScInputHandler()
586 : pInputWin( nullptr ),
587 pTableView( nullptr ),
589 pTipVisibleParent( nullptr ),
590 nTipVisible( nullptr ),
591 pTipVisibleSecParent( nullptr ),
592 nTipVisibleSec( nullptr ),
596 eMode( SC_INPUT_NONE
),
601 bFormulaMode( false ),
602 bInRangeUpdate( false ),
603 bParenthesisShown( false ),
604 bCreatingFuncView( false ),
605 bInEnterHandler( false ),
606 bCommandErrorShown( false ),
607 bInOwnChange( false ),
609 bCellHasPercentFormat( false ),
610 bLastIsSymbol( false ),
611 mbDocumentDisposing(false),
613 eAttrAdjust( SvxCellHorJustify::Standard
),
616 pRefViewSh( nullptr ),
617 pLastPattern( nullptr ),
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
)
653 MapMode
aMode( MapUnit::Map100thMM
, Point(), aScaleX
, aScaleY
);
654 mpEditEngine
->SetRefMapMode( aMode
);
659 void ScInputHandler::UpdateRefDevice()
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%
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() );
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()
694 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
695 mpEditEngine
= std::make_unique
<ScFieldEditEngine
>(&rDoc
, rDoc
.GetEnginePool(), rDoc
.GetEditPool());
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
;
719 nCntrl
&= ~EEControlBits::AUTOCORRECT
;
721 nCntrl
|= EEControlBits::AUTOCORRECT
;
723 if ( nCntrl
!= nOld
)
724 mpEditEngine
->SetControlWord(nCntrl
);
727 void ScInputHandler::UpdateSpellSettings( bool bFromStartTab
)
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
;
748 nCntrl
|= EEControlBits::ONLINESPELLING
;
750 nCntrl
&= ~EEControlBits::ONLINESPELLING
;
751 // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
752 if ( pLastPattern
&& pLastPattern
->IsSymbolFont() )
753 nCntrl
&= ~EEControlBits::AUTOCORRECT
;
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();
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()
787 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
790 pFormulaData
->clear();
793 pFormulaData
.reset( new ScTypedCaseStrSet
);
796 if( pFormulaDataPara
)
797 pFormulaDataPara
->clear();
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
)
837 IMPL_LINK( ScInputHandler
, ShowHideTipVisibleSecParentListener
, VclWindowEvent
&, rEvent
, void )
839 if (rEvent
.GetId() == VclEventId::ObjectDying
|| rEvent
.GetId() == VclEventId::WindowHide
840 || rEvent
.GetId() == VclEventId::WindowLoseFocus
)
844 void ScInputHandler::HideTip()
848 pTipVisibleParent
->RemoveEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleParentListener
) );
849 Help::HidePopover(pTipVisibleParent
, nTipVisible
);
850 nTipVisible
= nullptr;
851 pTipVisibleParent
= nullptr;
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;
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());
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() + "(");
901 ScTypedCaseStrSet::const_iterator it
=
902 findText(*pFormulaDataPara
, pFormulaDataPara
->end(), aFuncName
, aNew
, false);
903 if (it
!= pFormulaDataPara
->end())
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 )
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
];
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
];
941 nStartPosition
= i
+1;
943 else if( cNext
== cSep
)
947 if( nCount
== nActive
)
951 nStartPosition
= nEndPosition
+1;
957 sal_uInt16 nCount
= 0;
958 for (sal_Int32 i
= 0; i
< aNew
.getLength(); ++i
)
960 sal_Unicode cNext
= aNew
[i
];
963 nStartPosition
= i
+1;
965 else if( cNext
== cSep
)
969 if( nCount
== nActive
)
973 nStartPosition
= nEndPosition
+1;
975 else if( cNext
== cSheetSep
)
982 if (nStartPosition
> 0)
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
)
993 nArgs
-= PAIRED_VAR_ARGS
- nVarArgsSet
;
995 else if ( nArgs
>= VAR_ARGS
)
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
);
1011 ShowTipBelow( aNew
);
1025 void ScInputHandler::ShowTipCursor()
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();
1037 if ( aParagraph
.getLength() < aSel
.nEndPos
)
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
1056 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1060 pTipVisibleParent
= pActiveView
->GetWindow();
1061 vcl::Cursor
* pCur
= pActiveView
->GetCursor();
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
)
1077 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1081 pTipVisibleSecParent
= pActiveView
->GetWindow();
1082 vcl::Cursor
* pCur
= pActiveView
->GetCursor();
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() )
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
)
1115 p
= maFormulaChar
.find( c
);
1117 if (p
== maFormulaChar
.end())
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
++ );
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);
1148 aFuncNameStr
= rFunc
;
1150 if ( itStr
== rFuncStrVec
.begin() )
1153 aDescFuncNameStr
= aFuncNameStr
+ "()";
1157 aTipStr
.append(", ");
1159 aTipStr
.append(aFuncNameStr
);
1160 if ( itStr
== rFuncStrVec
.begin() )
1161 aTipStr
.append("]");
1162 if ( --nRemainFindNumber
<= 0 )
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());
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();
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() )
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
] == '$' ) )
1212 // Is the cursor at the end of a word?
1213 if ( aSel
.nEndPos
> 0 )
1215 OUString
aSelText( aParagraph
.copy( 0, aSel
.nEndPos
));
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
++ )
1233 if (aSelText
[n
] == cBetweenQuotes
)
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
;
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
1273 pActiveView
->ShowCursor();
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
)
1287 ESelection aSel
= pView
->GetSelection();
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);
1308 aSel
.nEndPos
= aSel
.nStartPos
;
1309 pView
->SetSelection(aSel
);
1310 pView
->SelectCurrentWord();
1311 aSelectedText
= pView
->GetSelected();
1313 aSel
.nStartPos
= aSel
.nEndPos
- ( aSelectedText
.getLength() - 1 );
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] == ')' );
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
];
1341 aInsStr
= aInsStr
.copy( 0, nInsLen
- 2 ); // Skip parentheses
1346 pView
->InsertText( aInsStr
);
1348 if ( bDoParen
) // Put cursor between parentheses
1350 aSel
= pView
->GetSelection();
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
);
1384 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
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())
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();
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 ) );
1416 bColRowName
= false;
1419 FormulaError nErrCode
= pCalc
->GetErrCode();
1420 if ( nErrCode
!= FormulaError::NONE
)
1421 return ScGlobal::GetErrorString(nErrCode
);
1423 SvNumberFormatter
& aFormatter
= *(pDoc
->GetFormatTable());
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
1435 OUString aStr
= pCalc
->GetString().getString();
1436 sal_uInt32 nFormat
= aFormatter
.GetStandardFormat(
1437 pCalc
->GetFormatType(), ScGlobal::eLnge
);
1440 aFormatter
.GetOutputString( aStr
, nFormat
,
1444 aValue
= "\"" + aValue
+ "\"";
1445 //! Escape quotation marks in String??
1449 if ( bColRowName
|| (aTestRange
.Parse(rFormula
) & ScRefFlags::VALID
) )
1450 aValue
= aValue
+ " ...";
1455 void ScInputHandler::FormulaPreview()
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
1473 miAutoPosFormula
= pFormulaData
->end();
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
);
1497 pTopView
->SetSelection( aAllSel
);
1499 pTableView
->SetSelection( aAllSel
);
1502 ESelection aSel
= pActiveView
->GetSelection();
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??
1522 pTopView
->SetSelection( aSel
);
1524 pTableView
->SetSelection( aSel
);
1528 pTopView
->InsertText( aInsert
, true );
1530 pTableView
->InsertText( aInsert
, true );
1538 void ScInputHandler::ResetAutoPar()
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
] == ')' )
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
;
1572 ESelection aSel
= pActiveView
->GetSelection();
1576 // this is in a formula (only one paragraph), so the selection
1577 // can be used directly for the TopView
1580 pTopView
->SetSelection( aSel
);
1582 pTableView
->SetSelection( aSel
);
1585 OSL_ENSURE(nAutoPar
, "SkipClosingPar: count is wrong");
1591 void ScInputHandler::GetColData()
1593 if ( pActiveViewSh
)
1595 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
1598 pColumnData
->clear();
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();
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())
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
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
1654 pTableView
->InsertText( aIns
);
1655 pTableView
->SetSelection( aSelection
);
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
1669 ScTypedCaseStrSet::const_iterator itNextPos
=
1670 findText(*pColumnData
, miAutoPosColumn
, aText
, aDummy
, false);
1671 bUseTab
= itNextPos
!= pColumnData
->end();
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();
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
)
1701 ScTypedCaseStrSet::const_iterator itNew
=
1702 findText(*pColumnData
, miAutoPosColumn
, aAutoSearch
, aNew
, bBack
);
1704 if (itNew
!= pColumnData
->end())
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
1716 pTableView
->DeleteSelected();
1717 pTableView
->InsertText( aIns
);
1718 pTableView
->SetSelection( ESelection(
1719 aSel
.nEndPara
, aSel
.nStartPos
+ aIns
.getLength(),
1720 aSel
.nEndPara
, aSel
.nStartPos
) );
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
1740 pActiveView
->ShowCursor();
1743 // Highlight parentheses
1744 void ScInputHandler::UpdateParenthesis()
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();
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
);
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
);
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
)
1811 pLastPattern
= nullptr;
1814 if ( pViewSh
== pRefViewSh
)
1816 //! The input from the EnterHandler does not arrive anymore
1817 // We end the EditMode anyways
1819 bFormulaMode
= false;
1820 pRefViewSh
= nullptr;
1821 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
1822 SC_MOD()->SetRefInputHdl(nullptr);
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() ) :
1853 sal_uInt16 nCount
= mpEditEngine
->GetViewCount();
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
)
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
)
1889 void ScInputHandler::SetInputWindow( ScInputWindow
* 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()
1905 return pTopView
? pTopView
: pTableView
;
1908 void ScInputHandler::ForgetLastPattern()
1910 pLastPattern
= nullptr;
1911 if ( !pLastState
&& pActiveViewSh
)
1912 pActiveViewSh
->UpdateInputHandler( true ); // Get status again
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
;
1935 case SvxCellHorJustify::Block
:
1936 eSvxAdjust
= SvxAdjust::Block
;
1938 case SvxCellHorJustify::Center
:
1939 eSvxAdjust
= SvxAdjust::Center
;
1941 case SvxCellHorJustify::Right
:
1942 eSvxAdjust
= SvxAdjust::Right
;
1944 default: // SvxCellHorJustify::Left
1945 eSvxAdjust
= SvxAdjust::Left
;
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();
1973 mpEditEngine
->EnableUndo( false );
1975 // Non-default paragraph attributes (e.g. from clipboard)
1976 // must be turned into character attributes
1977 mpEditEngine
->RemoveParaAttribs();
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()))
2009 ImplCreateEditEngine();
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
);
2020 aTester
.TestSelectedBlock(
2021 &rDoc
, aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Col(), aCursorPos
.Row(), rMark
);
2023 bool bStartInputMode
= true;
2025 if (!aTester
.IsEditable())
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
2032 bool bShowError
= (!bInputActivated
|| !aTester
.GetMessageId() || strcmp(aTester
.GetMessageId(), STR_PROTECTIONERR
) != 0) &&
2033 !pActiveViewSh
->GetViewData().GetDocShell()->IsReadOnly();
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.
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(),
2064 if (pPattern
!= pLastPattern
)
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
) );
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();
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.
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
);
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
;
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
2134 mpEditEngine
->SetText(aCurrentText
);
2135 aStr
= aCurrentText
;
2137 aCurrentText
.clear();
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
);
2147 pInputWin
->SetTextString(aStr
);
2150 UpdateAdjust( cTyped
);
2152 if ( bAutoComplete
)
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();
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();
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
)
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
)
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 );
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();
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
)
2294 aText
= ScEditUtil::GetMultilineString(*mpEditEngine
);
2296 aText
= GetEditText(mpEditEngine
.get());
2297 lcl_RemoveTabs(aText
);
2300 pInputWin
->SetTextString( aText
);
2302 if (comphelper::LibreOfficeKit::isActive())
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
2325 // Cursor before the end?
2326 ESelection aSel
= pActiveView
->GetSelection();
2328 bNeedGrow
= ( aSel
.nEndPos
!= mpEditEngine
->GetTextLen(aSel
.nEndPara
) );
2332 bNeedGrow
= rViewData
.GetDocument()->IsLayoutRTL( rViewData
.GetTabNo() );
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;
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,
2375 bFormulaMode
= true;
2376 pRefViewSh
= pActiveViewSh
;
2377 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2378 SC_MOD()->SetRefInputHdl(this);
2380 pInputWin
->SetFormulaMode(true);
2382 if ( bAutoComplete
)
2385 UpdateParenthesis();
2386 UpdateAutoCorrFlag();
2394 bFormulaMode
= false;
2395 pRefViewSh
= nullptr;
2396 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2397 SC_MOD()->SetRefInputHdl(nullptr);
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
)
2420 pOneFrame
= SfxViewFrame::GetNext( *pOneFrame
);
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
2433 OSL_FAIL("ViewFrame for reference input is not here anymore");
2438 void ScInputHandler::RemoveSelection()
2440 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2444 ESelection aSel
= pActiveView
->GetSelection();
2445 aSel
.nStartPara
= aSel
.nEndPara
;
2446 aSel
.nStartPos
= aSel
.nEndPos
;
2448 pTableView
->SetSelection( aSel
);
2450 pTopView
->SetSelection( aSel
);
2453 void ScInputHandler::InvalidateAttribs()
2455 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
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
)
2490 ImplCreateEditEngine();
2494 eMode
= SC_INPUT_NONE
;
2495 StopInputWinEngine( true );
2497 pActiveViewSh
->GetActiveWin()->GrabFocus();
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
;
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
))
2519 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
2525 mpEditEngine
->SetText(*pInitText
);
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
)
2541 mpEditEngine
->GetView(i
)->
2542 SetSelection( ESelection( nPara
, nLen
, nPara
, nLen
) );
2544 mpEditEngine
->GetView(i
)->ShowCursor(false);
2549 if (eMode
==SC_INPUT_TABLE
|| eMode
==SC_INPUT_TYPE
)
2552 pTableView
->SetEditEngineUpdateMode(true);
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' )
2581 static void lcl_SelectionToEnd( EditView
* pView
)
2585 EditEngine
* pEngine
= pView
->GetEditEngine();
2586 sal_Int32 nParCnt
= pEngine
->GetParagraphCount();
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())
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;
2631 pTopView
->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views
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() );
2651 if (pData
->GetDataMode() == SC_VALID_CUSTOM
)
2653 bOk
= pData
->IsDataValidCustom( aString
, *pPattern
, aCursorPos
, ScValidationData::CustomValidationPrivateAccess() );
2657 bOk
= pData
->IsDataValid( aString
, *pPattern
, aCursorPos
);
2662 if ( pActiveViewSh
) // If it came from MouseButtonDown
2663 pActiveViewSh
->StopMarking(); // (the InfoBox consumes the MouseButtonUp)
2665 vcl::Window
* pParent
= nullptr;
2667 pParent
= &pActiveViewSh
->GetViewFrame()->GetWindow();
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() );
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
);
2691 std::vector
<editeng::MisspellRanges
> aMisspellRanges
;
2692 mpEditEngine
->CompleteOnlineSpelling();
2693 bool bSpellErrors
= !bFormulaMode
&& mpEditEngine
->HasOnlineSpellErrors();
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() );
2705 SvNumberFormatter
* pFormatter
= pDoc
->GetFormatTable();
2706 // without conditional format, as in ScColumn::SetString
2707 sal_uInt32 nFormat
= pPattern
->GetNumberFormat( pFormatter
);
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();
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;
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
);
2765 ScDocument
* pDoc
= pActiveViewSh
->GetViewData().GetDocument();
2766 pCellAttrs
= std::make_unique
<ScPatternAttr
>(pDoc
->GetPool());
2767 pCellAttrs
->GetFromEditItemSet( pCommonAttrs
.get() );
2771 // Clear ParaAttribs (including adjustment)
2774 bool bAttrib
= false; // Formatting present?
2776 // check if EditObject is needed
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
)
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
) )
2799 SfxItemState eFieldState
= aOldAttribs
.GetItemState( EE_FEATURE_FIELD
, false );
2800 if ( eFieldState
== SfxItemState::DONTCARE
|| eFieldState
== SfxItemState::SET
)
2803 // Not converted characters?
2804 SfxItemState eConvState
= aOldAttribs
.GetItemState( EE_FEATURE_NOTCONV
, false );
2805 if ( eConvState
== SfxItemState::DONTCARE
|| eConvState
== SfxItemState::SET
)
2808 // Always recognize formulas as formulas
2809 // We still need the preceding test due to cell attributes
2813 mpEditEngine
->GetAllMisspellRanges(aMisspellRanges
);
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
;
2842 pExecuteSh
->SetTabNo(aCursorPos
.Tab());
2843 pExecuteSh
->ActiveGrabFocus();
2846 bFormulaMode
= false;
2847 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2848 SC_MOD()->SetRefInputHdl(nullptr);
2850 pInputWin
->SetFormulaMode(false);
2851 UpdateAutoCorrFlag();
2853 pRefViewSh
= nullptr; // Also without FormulaMode due to FunctionsAutoPilot
2854 DeleteRangeFinder();
2857 bool bOldMod
= bModified
;
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();
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
) );
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];
2919 if ( bInsertPreCorrectedString
&& aString
!= aPreAutoCorrectString
)
2921 ScInputStatusItem
aItem(FID_INPUTLINE_STATUS
,
2922 aCursorPos
, aCursorPos
, aCursorPos
,
2923 aPreAutoCorrectString
, pObject
.get());
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
2941 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScKillEditView
) );
2943 if ( bOldMod
&& pExecuteSh
&& pCellAttrs
&& !bForget
)
2945 // Combine with input?
2946 pExecuteSh
->ApplySelectionPattern( *pCellAttrs
, true );
2947 pExecuteSh
->AdjustBlockHeight();
2953 nFormSelStart
= nFormSelEnd
= 0;
2956 bInOwnChange
= false;
2957 bInEnterHandler
= false;
2960 void ScInputHandler::CancelHandler()
2962 bInOwnChange
= true; // Also without FormulaMode due to FunctionsAutoPilot
2964 ImplCreateEditEngine();
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
;
2978 pExecuteSh
->SetTabNo(aCursorPos
.Tab());
2979 pExecuteSh
->ActiveGrabFocus();
2981 bFormulaMode
= false;
2982 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2983 SC_MOD()->SetRefInputHdl(nullptr);
2985 pInputWin
->SetFormulaMode(false);
2986 UpdateAutoCorrFlag();
2988 pRefViewSh
= nullptr; // Also without FormulaMode due to FunctionsAutoPilot
2989 DeleteRangeFinder();
2992 eMode
= SC_INPUT_NONE
;
2993 StopInputWinEngine( true );
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
3003 NotifyChange( pLastState
.get(), true );
3005 nFormSelStart
= nFormSelEnd
= 0;
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
);
3023 if (!pTableView
&& !pTopView
)
3024 return; // E.g. FillMode
3026 DataChanging(); // Cannot be new
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
3035 bool bAppendSeparator
= (cLastChar
!= '(' && cLastChar
!= cSep
&& cLastChar
!= '=');
3036 if (bAppendSeparator
)
3039 pTableView
->InsertText( OUString(cSep
) );
3041 pTopView
->InsertText( OUString(cSep
) );
3047 void ScInputHandler::SetReference( const ScRange
& rRef
, const ScDocument
* pDoc
)
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
3063 if (!pTableView
&& !pTopView
)
3064 return; // E.g. FillMode
3066 // Never overwrite the "="!
3067 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
3068 ESelection aSel
= pActiveView
->GetSelection();
3070 if ( aSel
.nStartPara
== 0 && aSel
.nStartPos
== 0 )
3073 DataChanging(); // Cannot be new
3075 // Turn around selection if backwards (TODO: Do we really need to do that?)
3078 ESelection aTabSel
= pTableView
->GetSelection();
3079 if (aTabSel
.nStartPos
> aTabSel
.nEndPos
&& aTabSel
.nStartPara
== aTabSel
.nEndPara
)
3082 pTableView
->SetSelection(aTabSel
);
3087 ESelection aTopSel
= pTopView
->GetSelection();
3088 if (aTopSel
.nStartPos
> aTopSel
.nEndPos
&& aTopSel
.nStartPara
== aTopSel
.nEndPara
)
3091 pTopView
->SetSelection(aTopSel
);
3095 // Create string from reference, in the syntax of the document being edited.
3097 const ScAddress::Details
aAddrDetails( pThisDoc
, aCursorPos
);
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
+ "']";
3117 case formula::FormulaGrammar::CONV_OOO
:
3119 aRefStr
= "\'" + aFileName
+ "'#";
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
);
3131 aRefStr
= rRef
.Format(ScRefFlags::VALID
, pDoc
, aAddrDetails
);
3134 if (pTableView
|| pTopView
)
3137 pTableView
->InsertText( aRefStr
, true );
3139 pTopView
->InsertText( aRefStr
, true );
3147 void ScInputHandler::InsertFunction( const OUString
& rFuncName
, bool bAddPar
)
3149 if ( eMode
== SC_INPUT_NONE
)
3151 OSL_FAIL("InsertFunction, not during input mode");
3156 if (!pTableView
&& !pTopView
)
3157 return; // E.g. FillMode
3159 DataChanging(); // Cannot be new
3161 OUString aText
= rFuncName
;
3167 pTableView
->InsertText( aText
);
3170 ESelection aSel
= pTableView
->GetSelection();
3173 pTableView
->SetSelection(aSel
);
3178 pTopView
->InsertText( aText
);
3181 ESelection aSel
= pTopView
->GetSelection();
3184 pTopView
->SetSelection(aSel
);
3194 void ScInputHandler::ClearText()
3196 if ( eMode
== SC_INPUT_NONE
)
3198 OSL_FAIL("ClearText, not during input mode");
3203 if (!pTableView
&& !pTopView
)
3204 return; // E.g. FillMode
3206 DataChanging(); // Cannot be new
3210 pTableView
->GetEditEngine()->SetText( "" );
3211 pTableView
->SetSelection( ESelection(0,0, 0,0) );
3215 pTopView
->GetEditEngine()->SetText( "" );
3216 pTopView
->SetSelection( ESelection(0,0, 0,0) );
3222 bool ScInputHandler::KeyInput( const KeyEvent
& rKEvt
, bool bStartEdit
/* = false */ )
3226 bAutoComplete
= SC_MOD()->GetAppOptions().GetAutoComplete();
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.
3242 if (!bControl
&& nCode
== KEY_TAB
)
3244 // Normal TAB moves the cursor right.
3248 pActiveViewSh
->FindNextUnprot( bShift
, true );
3252 bool bInputLine
= ( eMode
==SC_INPUT_TOP
);
3256 bool bDoEnter
= false;
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
))
3267 else if (nModi
== 0 && nTipVisible
&& pFormulaData
&& miAutoPosFormula
!= pFormulaData
->end())
3269 PasteFunctionData();
3272 else if ( nModi
== 0 && nTipVisible
&& !aManualTip
.isEmpty() )
3279 ScEnterMode nMode
= ScEnterMode::NORMAL
;
3280 if ( bShift
&& bControl
)
3281 nMode
= ScEnterMode::MATRIX
;
3283 nMode
= ScEnterMode::BLOCK
;
3284 EnterHandler( nMode
);
3287 pActiveViewSh
->MoveCursorEnter( bShift
&& !bControl
);
3293 if (bControl
&& !bAlt
)
3295 if (pFormulaData
&& nTipVisible
&& miAutoPosFormula
!= pFormulaData
->end())
3298 NextFormulaEntry( bShift
);
3301 else if (pColumnData
&& bUseTab
&& miAutoPosColumn
!= pColumnData
->end())
3303 // Iterate through AutoInput entries
3304 NextAutoEntry( bShift
);
3315 else if( nTipVisibleSec
)
3320 else if (eMode
!= SC_INPUT_NONE
)
3329 if ( !bShift
&& !bControl
&& !bAlt
&& eMode
== SC_INPUT_TABLE
)
3331 eMode
= SC_INPUT_TYPE
;
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
) ) ) )
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
3363 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
3365 if (eMode
==SC_INPUT_NONE
)
3366 if (pTableView
|| pTopView
)
3370 if ( bStartEdit
&& bCellHasPercentFormat
&& ((nChar
>= '0' && nChar
<= '9') || nChar
== '-') )
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
3384 pTopView
->GetEditEngine()->SetText( aStrLoP
);
3385 if ( !aStrLoP
.isEmpty() )
3386 pTopView
->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
3392 if (pTableView
|| pTopView
)
3397 if( pTableView
->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN
) ) ) )
3400 if( pTopView
->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN
) ) ) )
3403 else if ( nAutoPar
&& nChar
== ')' && CursorAtClosingPar() )
3413 pTableView
->SetControlWord(pTableView
->GetControlWord() | EVControlBits::SINGLELINEPASTE
);
3415 vcl::Window
* pFrameWin
= pActiveViewSh
? pActiveViewSh
->GetFrameWin() : nullptr;
3416 if ( pTableView
->PostKeyEvent( rKEvt
, pFrameWin
) )
3419 pTableView
->SetControlWord(pTableView
->GetControlWord() & ~EVControlBits::SINGLELINEPASTE
);
3423 if ( bUsed
&& rKEvt
.GetKeyCode().GetFunction() == KeyFuncType::CUT
)
3424 pTopView
->DeleteSelected();
3425 else if ( pTopView
->PostKeyEvent( rKEvt
) )
3431 if ( bUsed
&& bAutoComplete
)
3435 miAutoPosFormula
= pFormulaData
->end(); // do not search further
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'
3450 // When the selection is changed manually or an opening parenthesis
3451 // is typed, stop overwriting parentheses
3452 if ( bUsed
&& nChar
== '(' )
3455 if ( KEY_INSERT
== nCode
)
3457 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
3459 pViewFrm
->GetBindings().Invalidate( SID_ATTR_INSERT
);
3461 if( bUsed
&& bFormulaMode
&& ( bCursorKey
|| bInsKey
|| nCode
== KEY_DELETE
|| nCode
== KEY_BACKSPACE
) )
3465 if( bUsed
&& bFormulaMode
&& nCode
== KEY_BACKSPACE
)
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
)
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
)
3494 if (pTableView
|| pTopView
)
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
)
3508 if (pTableView
|| pTopView
)
3511 pTableView
->Command( rCEvt
);
3512 else if (pTopView
) // call only once
3513 pTopView
->Command( rCEvt
);
3521 bAutoComplete
= SC_MOD()->GetAppOptions().GetAutoComplete();
3535 bool bNewView
= DataChanging( 0, true );
3537 if (!bProtected
) // changes allowed
3539 if (bNewView
) // create new edit view
3542 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
3544 if (eMode
==SC_INPUT_NONE
)
3545 if (pTableView
|| pTopView
)
3550 pTableView
->GetEditEngine()->SetText( aStrLoP
);
3551 pTableView
->SetSelection( ESelection(0,0, 0,0) );
3555 pTopView
->GetEditEngine()->SetText( aStrLoP
);
3556 pTopView
->SetSelection( ESelection(0,0, 0,0) );
3562 if (pTableView
|| pTopView
)
3565 pTableView
->Command( rCEvt
);
3567 pTopView
->Command( rCEvt
);
3569 if ( rCEvt
.GetCommand() == CommandEventId::EndExtTextInput
)
3571 // AutoInput after ext text input
3574 miAutoPosFormula
= pFormulaData
->end();
3576 miAutoPosColumn
= pColumnData
->end();
3585 DataChanged(); // calls UpdateParenthesis()
3586 InvalidateAttribs(); //! in DataChanged ?
3589 if (pTopView
&& eMode
!= SC_INPUT_NONE
)
3594 void ScInputHandler::NotifyChange( const ScInputHdlState
* pState
,
3595 bool bForce
, ScTabViewShell
* pSourceSh
,
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
)
3603 bool bRepeat
= (pState
== pLastState
.get());
3604 if (!bRepeat
&& pState
&& pLastState
)
3605 bRepeat
= (*pState
== *pLastState
);
3606 if (bRepeat
&& !bForce
)
3609 bInOwnChange
= true; // disable ModifyHdl (reset below)
3611 if ( pState
&& !pLastState
) // Enable again
3614 bool bHadObject
= pLastState
&& pLastState
->GetEditData();
3616 //! Before EditEngine gets eventually created (so it gets the right pools)
3618 pActiveViewSh
= pSourceSh
;
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;
3643 if (pState
->GetPos() != aCursorPos
)
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();
3666 else if ( bHadObject
)
3668 else if ( bTextValid
)
3669 bTxtMod
= ( aString
!= aCurrentText
);
3671 bTxtMod
= ( aString
!= GetEditText(mpEditEngine
.get()) );
3673 if ( bTxtMod
|| bForce
)
3677 mpEditEngine
->SetText( *pData
);
3679 aString
= ScEditUtil::GetMultilineString(*mpEditEngine
);
3681 aString
= GetEditText(mpEditEngine
.get());
3682 lcl_RemoveTabs(aString
);
3684 aCurrentText
.clear();
3688 aCurrentText
= aString
;
3689 bTextValid
= true; //! To begin with remember as a string
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
3702 const ScAddress::Details
aAddrDetails( &rDoc
, aCursorPos
);
3704 // Is the range a name?
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
);
3722 aPosStr
= aCursorPos
.Format(ScRefFlags::VALID
| nFlags
, &rDoc
, aAddrDetails
);
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());
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
);
3753 bCommandErrorShown
= false;
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
3772 if ( !pDelayTimer
->IsActive() )
3773 pDelayTimer
->Start();
3777 else // !pState || !pActiveViewSh
3779 if ( !pDelayTimer
->IsActive() )
3780 pDelayTimer
->Start();
3785 bInOwnChange
= false;
3788 void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust
)
3790 eAttrAdjust
= eJust
;
3794 void ScInputHandler::ResetDelayTimer()
3796 if( pDelayTimer
->IsActive() )
3798 pDelayTimer
->Stop();
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
) )
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
);
3826 pInputWin
->SetPosString( EMPTY_OUSTRING
);
3827 pInputWin
->SetTextString( EMPTY_OUSTRING
);
3828 pInputWin
->Disable();
3831 bInOwnChange
= false;
3836 void ScInputHandler::InputSelection( const EditView
* pView
)
3840 UpdateParenthesis(); // Selection changed -> update parentheses highlighting
3842 // When the selection is changed manually, stop overwriting parentheses
3846 void ScInputHandler::InputChanged( const EditView
* pView
, bool bFromNotify
)
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();
3863 rViewData
.GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
3865 rViewData
.EditGrowY();
3866 rViewData
.EditGrowX();
3872 const OUString
& ScInputHandler::GetEditString()
3876 aCurrentText
= mpEditEngine
->GetText(); // Always new from Engine
3880 return aCurrentText
;
3883 Size
ScInputHandler::GetTextSize()
3887 aSize
= Size( mpEditEngine
->CalcTextWidth(), mpEditEngine
->GetTextHeight() );
3892 bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter
& rDestEngine
)
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
)
3904 std::unique_ptr
<EditTextObject
> pObj
= mpEditEngine
->CreateTextObject();
3905 rDestEngine
.SetText(*pObj
);
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
3928 * Methods for FunctionAutoPilot:
3929 * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
3931 void ScInputHandler::InputGetSelection( sal_Int32
& rStart
, sal_Int32
& rEnd
)
3933 rStart
= nFormSelStart
;
3937 EditView
* ScInputHandler::GetFuncEditView()
3939 UpdateActiveView(); // Due to pTableView
3941 EditView
* pView
= nullptr;
3944 pInputWin
->MakeDialogEditView();
3945 pView
= pInputWin
->GetEditView();
3949 if ( eMode
!= SC_INPUT_TABLE
)
3951 bCreatingFuncView
= true; // Don't display RangeFinder
3952 SetMode( SC_INPUT_TABLE
);
3953 bCreatingFuncView
= false;
3955 pTableView
->GetEditEngine()->SetText( EMPTY_OUSTRING
);
3963 void ScInputHandler::InputSetSelection( sal_Int32 nStart
, sal_Int32 nEnd
)
3965 if ( nStart
<= nEnd
)
3967 nFormSelStart
= nStart
;
3972 nFormSelEnd
= nStart
;
3973 nFormSelStart
= nEnd
;
3976 EditView
* pView
= GetFuncEditView();
3978 pView
->SetSelection( ESelection(0,nStart
, 0,nEnd
) );
3983 void ScInputHandler::InputReplaceSelection( const OUString
& rStr
)
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
);
3995 aBuf
.remove(nFormSelStart
, nOldLen
);
3997 aBuf
.insert(nFormSelStart
, rStr
);
3999 aFormText
= aBuf
.makeStringAndClear();
4001 nFormSelEnd
= nFormSelStart
+ nNewLen
;
4003 EditView
* pView
= GetFuncEditView();
4006 pView
->SetEditEngineUpdateMode( false );
4007 pView
->GetEditEngine()->SetText( aFormText
);
4008 pView
->SetSelection( ESelection(0,nFormSelStart
, 0,nFormSelEnd
) );
4009 pView
->SetEditEngineUpdateMode( 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;
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
)
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
)
4066 aCursorPos
= r
.aCursorPos
;
4067 aStartPos
= r
.aStartPos
;
4068 aEndPos
= r
.aEndPos
;
4069 aString
= r
.aString
;
4072 pEditData
= r
.pEditData
->Clone();
4077 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */