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 .
22 #include <string_view>
24 #include <inputhdl.hxx>
25 #include <scitems.hxx>
26 #include <editeng/eeitem.hxx>
28 #include <sfx2/app.hxx>
29 #include <editeng/acorrcfg.hxx>
30 #include <formula/errorcodes.hxx>
31 #include <editeng/adjustitem.hxx>
32 #include <editeng/brushitem.hxx>
33 #include <svtools/colorcfg.hxx>
34 #include <editeng/colritem.hxx>
35 #include <editeng/editobj.hxx>
36 #include <editeng/editstat.hxx>
37 #include <editeng/editview.hxx>
38 #include <editeng/langitem.hxx>
39 #include <editeng/svxacorr.hxx>
40 #include <editeng/unolingu.hxx>
41 #include <editeng/wghtitem.hxx>
42 #include <editeng/justifyitem.hxx>
43 #include <editeng/misspellrange.hxx>
44 #include <sfx2/bindings.hxx>
45 #include <sfx2/viewfrm.hxx>
46 #include <sfx2/docfile.hxx>
47 #include <sfx2/printer.hxx>
48 #include <svl/numformat.hxx>
49 #include <svl/zforlist.hxx>
50 #include <svx/svxids.hrc>
51 #include <unotools/localedatawrapper.hxx>
52 #include <unotools/charclass.hxx>
54 #include <vcl/help.hxx>
55 #include <vcl/jsdialog/executor.hxx>
56 #include <vcl/commandevent.hxx>
57 #include <vcl/cursor.hxx>
58 #include <vcl/settings.hxx>
59 #include <vcl/svapp.hxx>
60 #include <tools/urlobj.hxx>
61 #include <formula/formulahelper.hxx>
62 #include <formula/funcvarargs.h>
63 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
64 #include <comphelper/lok.hxx>
65 #include <osl/diagnose.h>
68 #include <inputwin.hxx>
69 #include <tabvwsh.hxx>
72 #include <formulaopt.hxx>
73 #include <uiitems.hxx>
76 #include <globstr.hrc>
77 #include <scresid.hxx>
78 #include <patattr.hxx>
79 #include <viewdata.hxx>
80 #include <document.hxx>
81 #include <docpool.hxx>
82 #include <editutil.hxx>
83 #include <appoptio.hxx>
84 #include <docoptio.hxx>
85 #include <validat.hxx>
86 #include <rfindlst.hxx>
87 #include <inputopt.hxx>
88 #include <simpleformulacalc.hxx>
89 #include <compiler.hxx>
90 #include <editable.hxx>
91 #include <funcdesc.hxx>
92 #include <markdata.hxx>
93 #include <tokenarray.hxx>
94 #include <gridwin.hxx>
96 #include <fillinfo.hxx>
98 // Maximum Ranges in RangeFinder
99 #define RANGEFIND_MAX 128
101 using namespace formula
;
105 ScTypedCaseStrSet::const_iterator
findText(
106 const ScTypedCaseStrSet
& rDataSet
, ScTypedCaseStrSet::const_iterator
const & itPos
,
107 const OUString
& rStart
, OUString
& rResult
, bool bBack
)
109 auto lIsMatch
= [&rStart
](const ScTypedStrData
& rData
) {
110 return (rData
.GetStringType() != ScTypedStrData::Value
) && ScGlobal::GetTransliteration().isMatch(rStart
, rData
.GetString()); };
112 if (bBack
) // Backwards
114 ScTypedCaseStrSet::const_reverse_iterator it
= rDataSet
.rbegin(), itEnd
= rDataSet
.rend();
115 if (itPos
!= rDataSet
.end())
117 size_t nPos
= std::distance(rDataSet
.begin(), itPos
);
118 size_t nRPos
= rDataSet
.size() - 1 - nPos
;
119 std::advance(it
, nRPos
);
123 it
= std::find_if(it
, itEnd
, lIsMatch
);
126 rResult
= it
->GetString();
127 return (++it
).base(); // convert the reverse iterator back to iterator.
132 ScTypedCaseStrSet::const_iterator it
= rDataSet
.begin(), itEnd
= rDataSet
.end();
135 it
= std::next(itPos
);
138 it
= std::find_if(it
, itEnd
, lIsMatch
);
141 rResult
= it
->GetString();
146 return rDataSet
.end(); // no matching text found
149 OUString
getExactMatch(const ScTypedCaseStrSet
& rDataSet
, const OUString
& rString
)
151 auto it
= std::find_if(rDataSet
.begin(), rDataSet
.end(),
152 [&rString
](const ScTypedStrData
& rData
) {
153 return (rData
.GetStringType() != ScTypedStrData::Value
)
154 && ScGlobal::GetTransliteration().isEqual(rData
.GetString(), rString
);
156 if (it
!= rDataSet
.end())
157 return it
->GetString();
161 // This assumes that rResults is a sorted ring w.r.t ScTypedStrData::LessCaseInsensitive() or
162 // in the reverse direction, whose origin is specified by nRingOrigin.
163 sal_Int32
getLongestCommonPrefixLength(const std::vector
<OUString
>& rResults
, std::u16string_view aUserEntry
, sal_Int32 nRingOrigin
)
165 sal_Int32 nResults
= rResults
.size();
170 return rResults
[0].getLength();
172 sal_Int32 nMinLen
= aUserEntry
.size();
173 sal_Int32 nLastIdx
= nRingOrigin
? nRingOrigin
- 1 : nResults
- 1;
174 const OUString
& rFirst
= rResults
[nRingOrigin
];
175 const OUString
& rLast
= rResults
[nLastIdx
];
176 const sal_Int32 nMaxLen
= std::min(rFirst
.getLength(), rLast
.getLength());
178 for (sal_Int32 nLen
= nMaxLen
; nLen
> nMinLen
; --nLen
)
180 if (ScGlobal::GetTransliteration().isMatch(rFirst
.copy(0, nLen
), rLast
))
187 ScTypedCaseStrSet::const_iterator
findTextAll(
188 const ScTypedCaseStrSet
& rDataSet
, ScTypedCaseStrSet::const_iterator
const & itPos
,
189 const OUString
& rStart
, ::std::vector
< OUString
> &rResultVec
, bool bBack
, sal_Int32
* pLongestPrefixLen
= nullptr)
191 rResultVec
.clear(); // clear contents
193 if (!rDataSet
.size())
194 return rDataSet
.end();
196 sal_Int32 nRingOrigin
= 0;
198 ScTypedCaseStrSet::const_iterator retit
;
199 if ( bBack
) // Backwards
201 ScTypedCaseStrSet::const_reverse_iterator it
, itEnd
;
202 if ( itPos
== rDataSet
.end() )
204 it
= rDataSet
.rend();
210 it
= rDataSet
.rbegin();
211 size_t nPos
= std::distance(rDataSet
.begin(), itPos
);
212 size_t nRPos
= rDataSet
.size() - 1 - nPos
; // if itPos == rDataSet.end(), then nRPos = -1
213 std::advance(it
, nRPos
);
214 if ( it
== rDataSet
.rend() )
215 it
= rDataSet
.rbegin();
218 bool bFirstTime
= true;
220 while ( it
!= itEnd
|| bFirstTime
)
223 if ( it
== rDataSet
.rend() ) // go to the first if reach the end
225 it
= rDataSet
.rbegin();
226 nRingOrigin
= nCount
;
231 const ScTypedStrData
& rData
= *it
;
232 if ( rData
.GetStringType() == ScTypedStrData::Value
)
236 if ( !ScGlobal::GetTransliteration().isMatch(rStart
, rData
.GetString()) )
240 rResultVec
.push_back(rData
.GetString()); // set the match data
241 if ( nCount
== 0 ) // convert the reverse iterator back to iterator.
243 // actually we want to do "retit = it;".
244 retit
= rDataSet
.begin();
245 size_t nRPos
= std::distance(rDataSet
.rbegin(), it
);
246 size_t nPos
= rDataSet
.size() - 1 - nRPos
;
247 std::advance(retit
, nPos
);
254 ScTypedCaseStrSet::const_iterator it
, itEnd
;
256 if ( it
== rDataSet
.end() )
257 it
= --rDataSet
.end();
259 bool bFirstTime
= true;
261 while ( it
!= itEnd
|| bFirstTime
)
264 if ( it
== rDataSet
.end() ) // go to the first if reach the end
266 it
= rDataSet
.begin();
267 nRingOrigin
= nCount
;
272 const ScTypedStrData
& rData
= *it
;
273 if ( rData
.GetStringType() == ScTypedStrData::Value
)
277 if ( !ScGlobal::GetTransliteration().isMatch(rStart
, rData
.GetString()) )
281 rResultVec
.push_back(rData
.GetString()); // set the match data
283 retit
= it
; // remember first match iterator
288 if (pLongestPrefixLen
)
290 if (nRingOrigin
>= static_cast<sal_Int32
>(nCount
))
292 // All matches were picked when rDataSet was read in one direction.
295 // rResultsVec is a sorted ring with nRingOrigin "origin".
296 // The direction of sorting is not important for getLongestCommonPrefixLength.
297 *pLongestPrefixLen
= getLongestCommonPrefixLength(rResultVec
, rStart
, nRingOrigin
);
300 if ( nCount
> 0 ) // at least one function has matched
302 return rDataSet
.end(); // no matching text found
307 void ScInputHandler::SendReferenceMarks( const SfxViewShell
* pViewShell
,
308 const std::vector
<ReferenceMark
>& rReferenceMarks
)
315 std::stringstream ss
;
317 ss
<< "{ \"marks\": [ ";
319 for ( size_t i
= 0; i
< rReferenceMarks
.size(); i
++ )
321 if ( rReferenceMarks
[i
].Is() )
326 ss
<< "{ \"rectangle\": \""
327 << rReferenceMarks
[i
].nX
<< ", "
328 << rReferenceMarks
[i
].nY
<< ", "
329 << rReferenceMarks
[i
].nWidth
<< ", "
330 << rReferenceMarks
[i
].nHeight
<< "\", "
331 "\"color\": \"" << rReferenceMarks
[i
].aColor
.AsRGBHexString() << "\", "
332 "\"part\": \"" << rReferenceMarks
[i
].nTab
<< "\" } ";
340 OString aPayload
= ss
.str().c_str();
341 pViewShell
->libreOfficeKitViewCallback(
342 LOK_CALLBACK_REFERENCE_MARKS
, aPayload
.getStr() );
345 static inline void incPos( const sal_Unicode c
, sal_Int32
& rPos
, ESelection
& rSel
)
359 void ScInputHandler::InitRangeFinder( const OUString
& rFormula
)
362 if ( !pActiveViewSh
|| !SC_MOD()->GetInputOptions().GetRangeFinder() )
364 ScDocShell
* pDocSh
= pActiveViewSh
->GetViewData().GetDocShell();
365 ScDocument
& rDoc
= pDocSh
->GetDocument();
366 const sal_Unicode cSheetSep
= rDoc
.GetSheetSeparator();
368 OUString aDelimiters
= ScEditUtil::ModifyDelimiters(" !~%\"\t\n");
369 // delimiters (in addition to ScEditUtil): only characters that are
370 // allowed in formulas next to references and the quotation mark (so
371 // string constants can be skipped)
373 sal_Int32 nColon
= aDelimiters
.indexOf( ':' );
375 aDelimiters
= aDelimiters
.replaceAt( nColon
, 1, u
""); // Delimiter without colon
376 sal_Int32 nDot
= aDelimiters
.indexOf(cSheetSep
);
378 aDelimiters
= aDelimiters
.replaceAt( nDot
, 1 , u
""); // Delimiter without dot
380 const sal_Unicode
* pChar
= rFormula
.getStr();
381 sal_Int32 nLen
= rFormula
.getLength();
383 sal_Int32 nStart
= 0;
385 sal_uInt16 nCount
= 0;
387 while ( nPos
< nLen
&& nCount
< RANGEFIND_MAX
)
390 while ( nPos
<nLen
&& ScGlobal::UnicodeStrChr( aDelimiters
.getStr(), pChar
[nPos
] ) )
392 if ( pChar
[nPos
] == '"' ) // String
394 incPos( pChar
[nPos
], nPos
, aSel
);
395 while (nPos
<nLen
&& pChar
[nPos
] != '"') // Skip until end
396 incPos( pChar
[nPos
], nPos
, aSel
);
398 incPos( pChar
[nPos
], nPos
, aSel
); // Separator or closing quote
401 // Text between separators. We only consider within one line/paragraph.
402 aSel
.nStartPara
= aSel
.nEndPara
;
403 aSel
.nStartPos
= aSel
.nEndPos
;
407 bool bSingleQuoted
= false;
410 // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1"
411 // Literal single quotes in sheet names are masked by another single quote
412 if (pChar
[nPos
] == '\'')
414 bSingleQuoted
= !bSingleQuoted
;
416 else if (!bSingleQuoted
) // Get everything in single quotes, including separators
418 if (ScGlobal::UnicodeStrChr(aDelimiters
.getStr(), pChar
[nPos
]))
421 incPos( pChar
[nPos
], nPos
, aSel
);
425 // for R1C1 '-' in R[-]... or C[-]... are not delimiters
426 // Nothing heroic here to ensure that there are '[]' around a negative
427 // integer. we need to clean up this code.
428 if( nPos
< nLen
&& nPos
> 0 &&
429 '-' == pChar
[nPos
] && '[' == pChar
[nPos
-1] &&
430 formula::FormulaGrammar::CONV_XL_R1C1
== rDoc
.GetAddressConvention() )
432 incPos( pChar
[nPos
], nPos
, aSel
);
438 OUString aTest
= rFormula
.copy( nStart
, nPos
-nStart
);
439 const ScAddress::Details
aAddrDetails( rDoc
, aCursorPos
);
440 ScRefFlags nFlags
= aRange
.ParseAny( aTest
, rDoc
, aAddrDetails
);
441 if ( nFlags
& ScRefFlags::VALID
)
443 // Set tables if not specified
444 if ( (nFlags
& ScRefFlags::TAB_3D
) == ScRefFlags::ZERO
)
445 aRange
.aStart
.SetTab( pActiveViewSh
->GetViewData().GetTabNo() );
446 if ( (nFlags
& ScRefFlags::TAB2_3D
) == ScRefFlags::ZERO
)
447 aRange
.aEnd
.SetTab( aRange
.aStart
.Tab() );
449 if ( ( nFlags
& (ScRefFlags::COL2_VALID
|ScRefFlags::ROW2_VALID
|ScRefFlags::TAB2_VALID
) ) ==
452 // #i73766# if a single ref was parsed, set the same "abs" flags for ref2,
453 // so Format doesn't output a double ref because of different flags.
454 ScRefFlags nAbsFlags
= nFlags
& (ScRefFlags::COL_ABS
|ScRefFlags::ROW_ABS
|ScRefFlags::TAB_ABS
);
455 applyStartToEndFlags(nFlags
, nAbsFlags
);
460 mpEditEngine
->SetUpdateLayout( false );
461 pRangeFindList
.reset(new ScRangeFindList( pDocSh
->GetTitle() ));
464 Color nColor
= pRangeFindList
->Insert( ScRangeFindData( aRange
, nFlags
, aSel
));
466 SfxItemSet
aSet( mpEditEngine
->GetEmptyItemSet() );
467 aSet
.Put( SvxColorItem( nColor
, EE_CHAR_COLOR
) );
468 mpEditEngine
->QuickSetAttribs( aSet
, aSel
);
473 // Do not skip last separator; could be a quote (?)
476 UpdateLokReferenceMarks();
480 mpEditEngine
->SetUpdateLayout( true );
482 pDocSh
->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder
) );
486 ReferenceMark
ScInputHandler::GetReferenceMark( const ScViewData
& rViewData
, ScDocShell
* pDocSh
,
487 tools::Long nX1
, tools::Long nX2
, tools::Long nY1
, tools::Long nY2
,
488 tools::Long nTab
, const Color
& rColor
)
490 ScSplitPos eWhich
= rViewData
.GetActivePart();
492 // This method is LOK specific.
493 if (comphelper::LibreOfficeKit::isCompatFlagSet(
494 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs
))
496 SCCOL nCol1
= nX1
, nCol2
= nX2
;
497 SCROW nRow1
= nY1
, nRow2
= nY2
;
498 ScDocument
& rDoc
= pDocSh
->GetDocument();
500 PutInOrder(nCol1
, nCol2
);
501 PutInOrder(nRow1
, nRow2
);
503 if (nCol1
== nCol2
&& nRow1
== nRow2
)
504 rDoc
.ExtendMerge(nCol1
, nRow1
, nCol2
, nRow2
, nTab
);
505 else if (rDoc
.HasAttrib(nCol2
, nRow2
, nTab
, HasAttrFlags::Merged
))
506 rDoc
.ExtendMerge(nCol2
, nRow2
, nCol2
, nRow2
, nTab
);
508 Point aTopLeft
= rViewData
.GetPrintTwipsPos(nCol1
, nRow1
);
509 Point aBottomRight
= rViewData
.GetPrintTwipsPos(nCol2
+ 1, nRow2
+ 1);
510 tools::Long nSizeX
= aBottomRight
.X() - aTopLeft
.X() - 1;
511 tools::Long nSizeY
= aBottomRight
.Y() - aTopLeft
.Y() - 1;
513 return ReferenceMark(aTopLeft
.X(), aTopLeft
.Y(), nSizeX
, nSizeY
, nTab
, rColor
);
516 Point aScrPos
= rViewData
.GetScrPos( nX1
, nY1
, eWhich
);
517 tools::Long nScrX
= aScrPos
.X();
518 tools::Long nScrY
= aScrPos
.Y();
520 double nPPTX
= rViewData
.GetPPTX();
521 double nPPTY
= rViewData
.GetPPTY();
523 Fraction aZoomX
= rViewData
.GetZoomX();
524 Fraction aZoomY
= rViewData
.GetZoomY();
526 ScTableInfo aTabInfo
;
527 pDocSh
->GetDocument().FillInfo( aTabInfo
, nX1
, nY1
, nX2
, nY2
,
528 nTab
, nPPTX
, nPPTY
, false, false );
530 ScOutputData
aOutputData( nullptr, OUTTYPE_WINDOW
, aTabInfo
,
531 &( pDocSh
->GetDocument() ), nTab
,
537 return aOutputData
.FillReferenceMark( nX1
, nY1
, nX2
, nY2
,
541 void ScInputHandler::UpdateLokReferenceMarks()
543 if ( !comphelper::LibreOfficeKit::isActive())
546 ScTabViewShell
* pShell
= pActiveViewSh
? pActiveViewSh
547 : dynamic_cast<ScTabViewShell
*>(SfxViewShell::Current());
552 ScViewData
& rViewData
= pShell
->GetViewData();
553 ScDocShell
* pDocSh
= rViewData
.GetDocShell();
554 ScRangeFindList
* pRangeFinder
= GetRangeFindList();
556 if ( !pRangeFinder
&& !rViewData
.IsRefMode() )
559 sal_uInt16 nAdditionalMarks
= 0;
560 std::vector
<ReferenceMark
> aReferenceMarks( 1 );
562 if ( rViewData
.IsRefMode() )
564 nAdditionalMarks
= 1;
566 const svtools::ColorConfig
& rColorCfg
= SC_MOD()->GetColorConfig();
567 Color
aRefColor( rColorCfg
.GetColorValue( svtools::CALCREFERENCE
).nColor
);
568 tools::Long nX1
= rViewData
.GetRefStartX();
569 tools::Long nX2
= rViewData
.GetRefEndX();
570 tools::Long nY1
= rViewData
.GetRefStartY();
571 tools::Long nY2
= rViewData
.GetRefEndY();
572 tools::Long nTab
= rViewData
.GetRefStartZ();
574 if (rViewData
.GetRefEndZ() == rViewData
.GetTabNo())
575 nTab
= rViewData
.GetRefEndZ();
577 PutInOrder(nX1
, nX2
);
578 PutInOrder(nY1
, nY2
);
580 aReferenceMarks
[0] = ScInputHandler::GetReferenceMark( rViewData
, pDocSh
,
585 sal_uInt16 nCount
= pRangeFinder
?
586 ( static_cast<sal_uInt16
>( pRangeFinder
->Count() ) + nAdditionalMarks
) : nAdditionalMarks
;
587 aReferenceMarks
.resize( nCount
);
589 if ( nCount
&& pRangeFinder
&& !pRangeFinder
->IsHidden() &&
590 pRangeFinder
->GetDocName() == pDocSh
->GetTitle() )
592 for (sal_uInt16 i
= 0; i
< nCount
- nAdditionalMarks
; i
++)
594 ScRangeFindData
& rData
= pRangeFinder
->GetObject( i
);
595 ScRange aRef
= rData
.aRef
;
598 tools::Long nX1
= aRef
.aStart
.Col();
599 tools::Long nX2
= aRef
.aEnd
.Col();
600 tools::Long nY1
= aRef
.aStart
.Row();
601 tools::Long nY2
= aRef
.aEnd
.Row();
602 tools::Long nTab
= aRef
.aStart
.Tab();
604 aReferenceMarks
[i
+ nAdditionalMarks
] = ScInputHandler::GetReferenceMark( rViewData
, pDocSh
,
606 nTab
, rData
.nColor
);
608 ScInputHandler::SendReferenceMarks( pShell
, aReferenceMarks
);
613 ScInputHandler::SendReferenceMarks( pShell
, aReferenceMarks
);
618 aReferenceMarks
.clear();
619 ScInputHandler::SendReferenceMarks( pShell
, aReferenceMarks
);
623 void ScInputHandler::SetDocumentDisposing( bool b
)
625 mbDocumentDisposing
= b
;
628 static void lcl_Replace( EditView
* pView
, const OUString
& rNewStr
, const ESelection
& rOldSel
)
633 ESelection aOldSel
= pView
->GetSelection();
634 if (aOldSel
.HasRange())
635 pView
->SetSelection( ESelection( aOldSel
.nEndPara
, aOldSel
.nEndPos
,
636 aOldSel
.nEndPara
, aOldSel
.nEndPos
) );
638 EditEngine
* pEngine
= pView
->GetEditEngine();
639 pEngine
->QuickInsertText( rNewStr
, rOldSel
);
641 // Dummy InsertText for Update and Paint
642 // To do that we need to cancel the selection from above (before QuickInsertText)
643 pView
->InsertText( OUString() );
645 const sal_Int32 nPara
= pEngine
->GetParagraphCount() - 1;
646 const sal_Int32 nLen
= pEngine
->GetTextLen(nPara
);
647 ESelection
aSel( nPara
, nLen
, nPara
, nLen
);
648 pView
->SetSelection( aSel
); // Set cursor to the end
651 void ScInputHandler::UpdateRange( sal_uInt16 nIndex
, const ScRange
& rNew
)
653 ScTabViewShell
* pDocView
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
654 if ( pDocView
&& pRangeFindList
&& nIndex
< pRangeFindList
->Count() )
656 ScRangeFindData
& rData
= pRangeFindList
->GetObject( nIndex
);
657 Color nNewColor
= pRangeFindList
->FindColor( rNew
, nIndex
);
659 ScRange aJustified
= rNew
;
660 aJustified
.PutInOrder(); // Always display Ref in the Formula the right way
661 ScDocument
& rDoc
= pDocView
->GetViewData().GetDocument();
662 const ScAddress::Details
aAddrDetails( rDoc
, aCursorPos
);
663 OUString
aNewStr(aJustified
.Format(rDoc
, rData
.nFlags
, aAddrDetails
));
664 SfxItemSet
aSet( mpEditEngine
->GetEmptyItemSet() );
668 lcl_Replace( pTopView
, aNewStr
, rData
.maSel
);
669 lcl_Replace( pTableView
, aNewStr
, rData
.maSel
);
671 // We are within one paragraph.
672 const sal_Int32 nDiff
= aNewStr
.getLength() - (rData
.maSel
.nEndPos
- rData
.maSel
.nStartPos
);
673 rData
.maSel
.nEndPos
+= nDiff
;
675 aSet
.Put( SvxColorItem( nNewColor
, EE_CHAR_COLOR
) );
676 mpEditEngine
->QuickSetAttribs( aSet
, rData
.maSel
);
678 bInRangeUpdate
= true;
680 bInRangeUpdate
= false;
683 rData
.nColor
= nNewColor
;
687 const size_t nCount
= pRangeFindList
->Count();
688 for (size_t i
= nIndex
+ 1; i
< nCount
; ++i
)
690 ScRangeFindData
& rNext
= pRangeFindList
->GetObject( i
);
691 if (rNext
.maSel
.nStartPara
!= rData
.maSel
.nStartPara
)
694 rNext
.maSel
.nStartPos
+= nDiff
;
695 rNext
.maSel
.nEndPos
+= nDiff
;
699 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
700 pActiveView
->ShowCursor( false );
704 OSL_FAIL("UpdateRange: we're missing something");
708 void ScInputHandler::DeleteRangeFinder()
710 ScTabViewShell
* pPaintView
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
711 if ( pRangeFindList
&& pPaintView
)
713 ScDocShell
* pDocSh
= pActiveViewSh
->GetViewData().GetDocShell();
714 pRangeFindList
->SetHidden(true);
715 pDocSh
->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder
) ); // Steal
716 pRangeFindList
.reset();
720 static OUString
GetEditText(const EditEngine
* pEng
)
722 return ScEditUtil::GetMultilineString(*pEng
);
725 static void lcl_RemoveTabs(OUString
& rStr
)
727 rStr
= rStr
.replace('\t', ' ');
730 static void lcl_RemoveLineEnd(OUString
& rStr
)
732 rStr
= convertLineEnd(rStr
, LINEEND_LF
);
733 rStr
= rStr
.replace('\n', ' ');
736 static sal_Int32
lcl_MatchParenthesis( const OUString
& rStr
, sal_Int32 nPos
)
739 sal_Unicode c1
, c2
= 0;
780 sal_Int32 nLen
= rStr
.getLength();
781 const sal_Unicode
* p0
= rStr
.getStr();
782 const sal_Unicode
* p
;
783 const sal_Unicode
* p1
;
784 sal_uInt16 nQuotes
= 0;
785 if ( nPos
< nLen
/ 2 )
800 // Odd number of quotes that we find ourselves in a string
801 bool bLookInString
= ((nQuotes
% 2) != 0);
802 bool bInString
= bLookInString
;
804 p1
= (nDir
< 0 ? p0
: p0
+ nLen
) ;
805 sal_uInt16 nLevel
= 1;
806 while ( p
!= p1
&& nLevel
)
811 bInString
= !bInString
;
812 if ( bLookInString
&& !bInString
)
813 p
= p1
; // That's it then
815 else if ( bInString
== bLookInString
)
825 return static_cast<sal_Int32
>(p
- p0
);
828 ScInputHandler::ScInputHandler()
829 : pInputWin( nullptr ),
830 pTableView( nullptr ),
832 pTipVisibleParent( nullptr ),
833 nTipVisible( nullptr ),
834 pTipVisibleSecParent( nullptr ),
835 nTipVisibleSec( nullptr ),
838 nCellPercentFormatDecSep( 0 ),
840 eMode( SC_INPUT_NONE
),
845 bFormulaMode( false ),
846 bInRangeUpdate( false ),
847 bParenthesisShown( false ),
848 bCreatingFuncView( false ),
849 bInEnterHandler( false ),
850 bCommandErrorShown( false ),
851 bInOwnChange( false ),
853 bLastIsSymbol( false ),
854 mbDocumentDisposing(false),
855 mbPartialPrefix(false),
856 mbEditingExistingContent(false),
858 eAttrAdjust( SvxCellHorJustify::Standard
),
861 pRefViewSh( nullptr ),
862 pLastPattern( nullptr )
864 // The InputHandler is constructed with the view, so SfxViewShell::Current
865 // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange.
866 pActiveViewSh
= nullptr;
868 // Bindings (only still used for Invalidate) are retrieved if needed on demand
870 pDelayTimer
.reset( new Timer( "ScInputHandlerDelay timer" ) );
871 pDelayTimer
->SetTimeout( 500 ); // 500 ms delay
872 pDelayTimer
->SetInvokeHandler( LINK( this, ScInputHandler
, DelayTimer
) );
875 ScInputHandler::~ScInputHandler()
877 // If this is the application InputHandler, the dtor is called after SfxApplication::Main,
878 // thus we can't rely on any Sfx functions
879 if (!mbDocumentDisposing
) // inplace
880 EnterHandler(); // Finish input
882 if (SC_MOD()->GetRefInputHdl() == this)
883 SC_MOD()->SetRefInputHdl(nullptr);
885 if ( pInputWin
&& pInputWin
->GetInputHandler() == this )
886 pInputWin
->SetInputHandler( nullptr );
889 void ScInputHandler::SetRefScale( const Fraction
& rX
, const Fraction
& rY
)
891 if ( rX
!= aScaleX
|| rY
!= aScaleY
)
897 MapMode
aMode( MapUnit::Map100thMM
, Point(), aScaleX
, aScaleY
);
898 mpEditEngine
->SetRefMapMode( aMode
);
903 void ScInputHandler::UpdateRefDevice()
908 bool bTextWysiwyg
= SC_MOD()->GetInputOptions().GetTextWysiwyg();
909 bool bInPlace
= pActiveViewSh
&& pActiveViewSh
->GetViewFrame()->GetFrame().IsInPlace();
910 EEControlBits nCtrl
= mpEditEngine
->GetControlWord();
911 if ( bTextWysiwyg
|| bInPlace
)
912 nCtrl
|= EEControlBits::FORMAT100
; // EditEngine default: always format for 100%
914 nCtrl
&= ~EEControlBits::FORMAT100
; // when formatting for screen, use the actual MapMode
915 mpEditEngine
->SetControlWord( nCtrl
);
916 if ( bTextWysiwyg
&& pActiveViewSh
)
917 mpEditEngine
->SetRefDevice( pActiveViewSh
->GetViewData().GetDocument().GetPrinter() );
919 mpEditEngine
->SetRefDevice( nullptr );
921 MapMode
aMode( MapUnit::Map100thMM
, Point(), aScaleX
, aScaleY
);
922 mpEditEngine
->SetRefMapMode( aMode
);
924 // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev,
925 // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL).
926 if ( !( bTextWysiwyg
&& pActiveViewSh
) )
928 mpEditEngine
->GetRefDevice()->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() );
932 void ScInputHandler::ImplCreateEditEngine()
939 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
940 mpEditEngine
= std::make_unique
<ScFieldEditEngine
>(&rDoc
, rDoc
.GetEnginePool(), rDoc
.GetEditPool());
943 mpEditEngine
= std::make_unique
<ScFieldEditEngine
>(nullptr, EditEngine::CreatePool().get(), nullptr, true);
945 mpEditEngine
->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine
->GetWordDelimiters() ) );
946 UpdateRefDevice(); // also sets MapMode
947 mpEditEngine
->SetPaperSize( Size( 1000000, 1000000 ) );
948 pEditDefaults
.reset( new SfxItemSet( mpEditEngine
->GetEmptyItemSet() ) );
950 mpEditEngine
->SetControlWord( mpEditEngine
->GetControlWord() | EEControlBits::AUTOCORRECT
);
951 mpEditEngine
->SetReplaceLeadingSingleQuotationMark( false );
952 mpEditEngine
->SetModifyHdl( LINK( this, ScInputHandler
, ModifyHdl
) );
955 void ScInputHandler::UpdateAutoCorrFlag()
957 EEControlBits nCntrl
= mpEditEngine
->GetControlWord();
958 EEControlBits nOld
= nCntrl
;
960 // Don't use pLastPattern here (may be invalid because of AutoStyle)
961 bool bDisable
= bLastIsSymbol
|| bFormulaMode
;
963 nCntrl
&= ~EEControlBits::AUTOCORRECT
;
965 nCntrl
|= EEControlBits::AUTOCORRECT
;
967 if ( nCntrl
!= nOld
)
968 mpEditEngine
->SetControlWord(nCntrl
);
971 void ScInputHandler::UpdateSpellSettings( bool bFromStartTab
)
973 if ( !pActiveViewSh
)
976 ScViewData
& rViewData
= pActiveViewSh
->GetViewData();
977 bool bOnlineSpell
= rViewData
.GetDocument().GetDocOptions().IsAutoSpell();
979 // SetDefaultLanguage is independent of the language attributes,
980 // ScGlobal::GetEditDefaultLanguage is always used.
981 // It must be set every time in case the office language was changed.
983 mpEditEngine
->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
985 // if called for changed options, update flags only if already editing
986 // if called from StartTable, always update flags
988 if ( bFromStartTab
|| eMode
!= SC_INPUT_NONE
)
990 EEControlBits nCntrl
= mpEditEngine
->GetControlWord();
991 EEControlBits nOld
= nCntrl
;
993 nCntrl
|= EEControlBits::ONLINESPELLING
;
995 nCntrl
&= ~EEControlBits::ONLINESPELLING
;
996 // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
997 if ( pLastPattern
&& pLastPattern
->IsSymbolFont() )
998 nCntrl
&= ~EEControlBits::AUTOCORRECT
;
1000 nCntrl
|= EEControlBits::AUTOCORRECT
;
1001 if ( nCntrl
!= nOld
)
1002 mpEditEngine
->SetControlWord(nCntrl
);
1004 ScDocument
& rDoc
= rViewData
.GetDocument();
1005 rDoc
.ApplyAsianEditSettings( *mpEditEngine
);
1006 mpEditEngine
->SetDefaultHorizontalTextDirection(
1007 rDoc
.GetEditTextDirection( rViewData
.GetTabNo() ) );
1008 mpEditEngine
->SetFirstWordCapitalization( false );
1011 // Language is set separately, so the speller is needed only if online spelling is active
1012 if ( bOnlineSpell
) {
1013 css::uno::Reference
<css::linguistic2::XSpellChecker1
> xXSpellChecker1( LinguMgr::GetSpellChecker() );
1014 mpEditEngine
->SetSpeller( xXSpellChecker1
);
1017 bool bHyphen
= pLastPattern
&& pLastPattern
->GetItem(ATTR_HYPHENATE
).GetValue();
1019 css::uno::Reference
<css::linguistic2::XHyphenator
> xXHyphenator( LinguMgr::GetHyphenator() );
1020 mpEditEngine
->SetHyphenator( xXHyphenator
);
1024 // Function/Range names etc. as Tip help
1026 // The other types are defined in ScDocument::GetFormulaEntries
1027 void ScInputHandler::GetFormulaData()
1029 if ( !pActiveViewSh
)
1032 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
1035 pFormulaData
->clear();
1038 pFormulaData
.reset( new ScTypedCaseStrSet
);
1041 if( pFormulaDataPara
)
1042 pFormulaDataPara
->clear();
1044 pFormulaDataPara
.reset( new ScTypedCaseStrSet
);
1046 const OUString
aParenthesesReplacement( cParenthesesReplacement
);
1047 const ScFunctionList
* pFuncList
= ScGlobal::GetStarCalcFunctionList();
1048 const sal_uInt32 nListCount
= pFuncList
->GetCount();
1049 const InputHandlerFunctionNames
& rFunctionNames
= ScGlobal::GetInputHandlerFunctionNames();
1050 *pFormulaData
= rFunctionNames
.maFunctionData
;
1051 *pFormulaDataPara
= rFunctionNames
.maFunctionDataPara
;
1052 maFormulaChar
= rFunctionNames
.maFunctionChar
;
1054 // Increase suggestion priority of MRU formulas
1055 const ScAppOptions
& rOpt
= SC_MOD()->GetAppOptions();
1056 const sal_uInt16 nMRUCount
= rOpt
.GetLRUFuncListCount();
1057 const sal_uInt16
* pMRUList
= rOpt
.GetLRUFuncList();
1058 for (sal_uInt16 i
= 0; i
< nMRUCount
; i
++)
1060 const sal_uInt16 nId
= pMRUList
[i
];
1061 for (sal_uInt32 j
= 0; j
< nListCount
; j
++)
1063 const ScFuncDesc
* pDesc
= pFuncList
->GetFunction(j
);
1064 if (pDesc
->nFIndex
== nId
&& pDesc
->mxFuncName
)
1066 const OUString aEntry
= *pDesc
->mxFuncName
+ aParenthesesReplacement
;;
1067 const ScTypedStrData
aData(aEntry
, 0.0, 0.0, ScTypedStrData::Standard
);
1068 auto it
= pFormulaData
->find(aData
);
1069 if (it
!= pFormulaData
->end())
1070 pFormulaData
->erase(it
);
1071 pFormulaData
->insert(ScTypedStrData(aEntry
, 0.0, 0.0, ScTypedStrData::MRU
));
1072 break; // Stop searching
1076 miAutoPosFormula
= pFormulaData
->end();
1078 // tdf#142031 - collect all the characters for the formula suggestion auto input
1079 ScTypedCaseStrSet aStrSet
;
1080 rDoc
.GetFormulaEntries( aStrSet
);
1081 for (auto iter
= aStrSet
.begin(); iter
!= aStrSet
.end(); ++iter
)
1083 const OUString aFuncName
= ScGlobal::getCharClass().uppercase((*iter
).GetString());
1084 // fdo#75264 fill maFormulaChar with all characters used in formula names
1085 for (sal_Int32 j
= 0; j
< aFuncName
.getLength(); j
++)
1086 maFormulaChar
.insert(aFuncName
[j
]);
1088 pFormulaData
->insert(aStrSet
.begin(), aStrSet
.end());
1089 pFormulaDataPara
->insert(aStrSet
.begin(), aStrSet
.end());
1092 IMPL_LINK( ScInputHandler
, ShowHideTipVisibleParentListener
, VclWindowEvent
&, rEvent
, void )
1094 if (rEvent
.GetId() == VclEventId::ObjectDying
|| rEvent
.GetId() == VclEventId::WindowHide
1095 || rEvent
.GetId() == VclEventId::WindowLoseFocus
|| rEvent
.GetId() == VclEventId::ControlLoseFocus
)
1099 IMPL_LINK( ScInputHandler
, ShowHideTipVisibleSecParentListener
, VclWindowEvent
&, rEvent
, void )
1101 if (rEvent
.GetId() == VclEventId::ObjectDying
|| rEvent
.GetId() == VclEventId::WindowHide
1102 || rEvent
.GetId() == VclEventId::WindowLoseFocus
|| rEvent
.GetId() == VclEventId::ControlLoseFocus
)
1106 void ScInputHandler::HideTip()
1110 pTipVisibleParent
->RemoveEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleParentListener
) );
1111 Help::HidePopover(pTipVisibleParent
, nTipVisible
);
1112 nTipVisible
= nullptr;
1113 pTipVisibleParent
= nullptr;
1117 void ScInputHandler::HideTipBelow()
1119 if ( nTipVisibleSec
)
1121 pTipVisibleSecParent
->RemoveEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleSecParentListener
) );
1122 Help::HidePopover(pTipVisibleSecParent
, nTipVisibleSec
);
1123 nTipVisibleSec
= nullptr;
1124 pTipVisibleSecParent
= nullptr;
1132 bool lcl_hasSingleToken(std::u16string_view s
, sal_Unicode c
)
1134 return !s
.empty() && s
.find(c
) == std::u16string_view::npos
;
1139 void ScInputHandler::ShowArgumentsTip( OUString
& rSelText
)
1141 if (comphelper::LibreOfficeKit::isActive())
1146 if ( !pActiveViewSh
)
1149 ScDocShell
* pDocSh
= pActiveViewSh
->GetViewData().GetDocShell();
1150 const sal_Unicode cSep
= ScCompiler::GetNativeSymbolChar(ocSep
);
1151 const sal_Unicode cSheetSep
= pDocSh
->GetDocument().GetSheetSeparator();
1152 FormulaHelper
aHelper(ScGlobal::GetStarCalcFunctionMgr());
1153 bool bFound
= false;
1157 sal_Int32 nLeftParentPos
= lcl_MatchParenthesis( rSelText
, rSelText
.getLength()-1 );
1158 if( nLeftParentPos
!= -1 )
1160 sal_Int32 nNextFStart
= aHelper
.GetFunctionStart( rSelText
, nLeftParentPos
, true);
1161 const IFunctionDescription
* ppFDesc
;
1162 ::std::vector
< OUString
> aArgs
;
1163 if( aHelper
.GetNextFunc( rSelText
, false, nNextFStart
, nullptr, &ppFDesc
, &aArgs
) )
1165 if( !ppFDesc
->getFunctionName().isEmpty() )
1167 sal_Int32 nArgPos
= aHelper
.GetArgStart( rSelText
, nNextFStart
, 0 );
1168 sal_uInt16 nArgs
= static_cast<sal_uInt16
>(ppFDesc
->getParameterCount());
1169 OUString
aFuncName( ppFDesc
->getFunctionName() + "(");
1171 ScTypedCaseStrSet::const_iterator it
=
1172 findText(*pFormulaDataPara
, pFormulaDataPara
->end(), aFuncName
, aNew
, false);
1173 if (it
!= pFormulaDataPara
->end())
1176 sal_uInt16 nActive
= 0;
1177 for( sal_uInt16 i
=0; i
< nArgs
; i
++ )
1179 sal_Int32 nLength
= aArgs
[i
].getLength();
1180 if( nArgPos
<= rSelText
.getLength()-1 )
1189 sal_Int32 nStartPosition
= 0;
1190 sal_Int32 nEndPosition
= 0;
1192 if( lcl_hasSingleToken(aNew
, cSep
) )
1194 for (sal_Int32 i
= 0; i
< aNew
.getLength(); ++i
)
1196 sal_Unicode cNext
= aNew
[i
];
1199 nStartPosition
= i
+1;
1203 else if( lcl_hasSingleToken(aNew
, cSheetSep
) )
1205 sal_uInt16 nCount
= 0;
1206 for (sal_Int32 i
= 0; i
< aNew
.getLength(); ++i
)
1208 sal_Unicode cNext
= aNew
[i
];
1211 nStartPosition
= i
+1;
1213 else if( cNext
== cSep
)
1217 if( nCount
== nActive
)
1221 nStartPosition
= nEndPosition
+1;
1227 sal_uInt16 nCount
= 0;
1228 for (sal_Int32 i
= 0; i
< aNew
.getLength(); ++i
)
1230 sal_Unicode cNext
= aNew
[i
];
1233 nStartPosition
= i
+1;
1235 else if( cNext
== cSep
)
1239 if( nCount
== nActive
)
1243 nStartPosition
= nEndPosition
+1;
1245 else if( cNext
== cSheetSep
)
1252 if (nStartPosition
> 0)
1254 nArgs
= ppFDesc
->getParameterCount();
1255 sal_Int16 nVarArgsSet
= 0;
1256 if ( nArgs
>= PAIRED_VAR_ARGS
)
1259 nArgs
-= PAIRED_VAR_ARGS
- nVarArgsSet
;
1261 else if ( nArgs
>= VAR_ARGS
)
1264 nArgs
-= VAR_ARGS
- nVarArgsSet
;
1266 if ( nVarArgsSet
> 0 && nActive
> nArgs
)
1267 nActive
= nArgs
- (nActive
- nArgs
) % nVarArgsSet
;
1268 aNew
= OUString::Concat(aNew
.subView(0, nStartPosition
)) +
1270 aNew
.subView(nStartPosition
) +
1272 ppFDesc
->getParameterDescription(nActive
-1);
1273 if (eMode
!= SC_INPUT_TOP
)
1275 ShowTipBelow( aNew
);
1286 ShowTipBelow( aNew
);
1300 void ScInputHandler::ShowTipCursor()
1304 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1306 /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1307 if ( !(bFormulaMode
&& pActiveView
&& pFormulaDataPara
&& mpEditEngine
->GetParagraphCount() == 1) )
1310 OUString aParagraph
= mpEditEngine
->GetText( 0 );
1311 ESelection aSel
= pActiveView
->GetSelection();
1314 if ( aParagraph
.getLength() < aSel
.nEndPos
)
1317 if ( aSel
.nEndPos
> 0 )
1319 OUString
aSelText( aParagraph
.copy( 0, aSel
.nEndPos
));
1321 ShowArgumentsTip( aSelText
);
1325 void ScInputHandler::ShowTip( const OUString
& rText
)
1327 // aManualTip needs to be set afterwards from outside
1332 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1337 if (pInputWin
&& pInputWin
->GetEditView() == pActiveView
)
1339 pTipVisibleParent
= pInputWin
->GetEditWindow();
1340 aPos
= pInputWin
->GetCursorScreenPixelPos();
1344 pTipVisibleParent
= pActiveView
->GetWindow();
1345 if (vcl::Cursor
* pCur
= pActiveView
->GetCursor())
1346 aPos
= pTipVisibleParent
->LogicToPixel( pCur
->GetPos() );
1347 aPos
= pTipVisibleParent
->OutputToScreenPixel( aPos
);
1350 tools::Rectangle
aRect( aPos
, aPos
);
1351 QuickHelpFlags
const nAlign
= QuickHelpFlags::Left
|QuickHelpFlags::Bottom
;
1352 nTipVisible
= Help::ShowPopover(pTipVisibleParent
, aRect
, rText
, nAlign
);
1353 pTipVisibleParent
->AddEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleParentListener
) );
1356 void ScInputHandler::ShowTipBelow( const OUString
& rText
)
1360 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1365 if (pInputWin
&& pInputWin
->GetEditView() == pActiveView
)
1367 pTipVisibleSecParent
= pInputWin
->GetEditWindow();
1368 aPos
= pInputWin
->GetCursorScreenPixelPos(true);
1372 pTipVisibleSecParent
= pActiveView
->GetWindow();
1373 if (vcl::Cursor
* pCur
= pActiveView
->GetCursor())
1375 Point aLogicPos
= pCur
->GetPos();
1376 aLogicPos
.AdjustY(pCur
->GetHeight() );
1377 aPos
= pTipVisibleSecParent
->LogicToPixel( aLogicPos
);
1379 aPos
= pTipVisibleSecParent
->OutputToScreenPixel( aPos
);
1382 tools::Rectangle
aRect( aPos
, aPos
);
1383 QuickHelpFlags
const nAlign
= QuickHelpFlags::Left
| QuickHelpFlags::Top
| QuickHelpFlags::NoEvadePointer
;
1384 nTipVisibleSec
= Help::ShowPopover(pTipVisibleSecParent
, aRect
, rText
, nAlign
);
1385 pTipVisibleSecParent
->AddEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleSecParentListener
) );
1388 bool ScInputHandler::GetFuncName( OUString
& aStart
, OUString
& aResult
)
1390 if ( aStart
.isEmpty() )
1393 aStart
= ScGlobal::getCharClass().uppercase( aStart
);
1394 sal_Int32 nPos
= aStart
.getLength() - 1;
1395 sal_Unicode c
= aStart
[ nPos
];
1396 // fdo#75264 use maFormulaChar to check if characters are used in function names
1397 ::std::set
< sal_Unicode
>::const_iterator p
= maFormulaChar
.find( c
);
1398 if ( p
== maFormulaChar
.end() )
1399 return false; // last character is not part of any function name, quit
1401 ::std::vector
<sal_Unicode
> aTemp
{ c
};
1402 for(sal_Int32 i
= nPos
- 1; i
>= 0; --i
)
1405 p
= maFormulaChar
.find( c
);
1407 if (p
== maFormulaChar
.end())
1410 aTemp
.push_back( c
);
1413 ::std::vector
<sal_Unicode
>::reverse_iterator rIt
= aTemp
.rbegin();
1414 aResult
= OUString( *rIt
++ );
1415 while ( rIt
!= aTemp
.rend() )
1416 aResult
+= OUStringChar( *rIt
++ );
1422 /// Rid ourselves of unwanted " quoted json characters.
1423 OString
escapeJSON(const OUString
&aStr
)
1425 OUString aEscaped
= aStr
;
1426 aEscaped
= aEscaped
.replaceAll("\n", " ");
1427 aEscaped
= aEscaped
.replaceAll("\"", "'");
1428 return OUStringToOString(aEscaped
, RTL_TEXTENCODING_UTF8
);
1432 void ScInputHandler::ShowFuncList( const ::std::vector
< OUString
> & rFuncStrVec
)
1434 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
1435 if (comphelper::LibreOfficeKit::isActive())
1437 if (rFuncStrVec
.size() && pViewShell
&& pViewShell
->isLOKMobilePhone())
1439 auto aPos
= pFormulaData
->begin();
1440 sal_uInt32 nCurIndex
= std::distance(aPos
, miAutoPosFormula
);
1441 const sal_uInt32 nSize
= pFormulaData
->size();
1443 OUString aFuncNameStr
;
1444 OUString aDescFuncNameStr
;
1445 OStringBuffer aPayload
;
1446 aPayload
.append("[ ");
1447 for (const OUString
& rFunc
: rFuncStrVec
)
1449 if ( rFunc
[rFunc
.getLength()-1] == cParenthesesReplacement
)
1451 aFuncNameStr
= rFunc
.copy(0, rFunc
.getLength()-1);
1455 aFuncNameStr
= rFunc
;
1458 FormulaHelper
aHelper(ScGlobal::GetStarCalcFunctionMgr());
1459 aDescFuncNameStr
= aFuncNameStr
+ "()";
1460 sal_Int32 nNextFStart
= 0;
1461 const IFunctionDescription
* ppFDesc
;
1462 ::std::vector
< OUString
> aArgs
;
1463 OUString eqPlusFuncName
= "=" + aDescFuncNameStr
;
1464 if ( aHelper
.GetNextFunc( eqPlusFuncName
, false, nNextFStart
, nullptr, &ppFDesc
, &aArgs
) )
1466 if ( !ppFDesc
->getFunctionName().isEmpty() )
1468 aPayload
.append("{");
1469 aPayload
.append("\"index\": ");
1470 aPayload
.append(static_cast<sal_Int64
>(nCurIndex
));
1471 aPayload
.append(", ");
1472 aPayload
.append("\"signature\": \"");
1473 aPayload
.append(escapeJSON(ppFDesc
->getSignature()));
1474 aPayload
.append("\", ");
1475 aPayload
.append("\"description\": \"");
1476 aPayload
.append(escapeJSON(ppFDesc
->getDescription()));
1477 aPayload
.append("\"}, ");
1481 if (nCurIndex
== nSize
)
1484 sal_Int32 nLen
= aPayload
.getLength();
1485 aPayload
[nLen
- 2] = ' ';
1486 aPayload
[nLen
- 1] = ']';
1488 OString s
= aPayload
.makeStringAndClear();
1489 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST
, s
.getStr());
1491 // not tunnel tooltips in the lok case
1495 OUStringBuffer aTipStr
;
1496 OUString aFuncNameStr
;
1497 OUString aDescFuncNameStr
;
1498 ::std::vector
<OUString
>::const_iterator itStr
= rFuncStrVec
.begin();
1499 sal_Int32 nMaxFindNumber
= 3;
1500 sal_Int32 nRemainFindNumber
= nMaxFindNumber
;
1501 for ( ; itStr
!= rFuncStrVec
.end(); ++itStr
)
1503 const OUString
& rFunc
= *itStr
;
1504 if ( rFunc
[rFunc
.getLength()-1] == cParenthesesReplacement
)
1506 aFuncNameStr
= rFunc
.copy(0, rFunc
.getLength()-1);
1510 aFuncNameStr
= rFunc
;
1512 if ( itStr
== rFuncStrVec
.begin() )
1515 aDescFuncNameStr
= aFuncNameStr
+ "()";
1519 aTipStr
.append(", ");
1521 aTipStr
.append(aFuncNameStr
);
1522 if ( itStr
== rFuncStrVec
.begin() )
1523 aTipStr
.append("]");
1524 if ( --nRemainFindNumber
<= 0 )
1527 sal_Int32 nRemainNumber
= rFuncStrVec
.size() - nMaxFindNumber
;
1528 if ( nRemainFindNumber
== 0 && nRemainNumber
> 0 )
1530 OUString
aMessage( ScResId( STR_FUNCTIONS_FOUND
) );
1531 aMessage
= aMessage
.replaceFirst("%2", OUString::number(nRemainNumber
));
1532 aMessage
= aMessage
.replaceFirst("%1", aTipStr
);
1535 FormulaHelper
aHelper(ScGlobal::GetStarCalcFunctionMgr());
1536 sal_Int32 nNextFStart
= 0;
1537 const IFunctionDescription
* ppFDesc
;
1538 ::std::vector
< OUString
> aArgs
;
1539 OUString eqPlusFuncName
= "=" + aDescFuncNameStr
;
1540 if ( aHelper
.GetNextFunc( eqPlusFuncName
, false, nNextFStart
, nullptr, &ppFDesc
, &aArgs
) )
1542 if ( !ppFDesc
->getFunctionName().isEmpty() )
1544 aTipStr
.append(" : " + ppFDesc
->getDescription());
1547 ShowTip( aTipStr
.makeStringAndClear() );
1550 void ScInputHandler::UseFormulaData()
1552 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1554 /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1555 if ( !(pActiveView
&& pFormulaData
&& mpEditEngine
->GetParagraphCount() == 1) )
1558 OUString aParagraph
= mpEditEngine
->GetText( 0 );
1559 ESelection aSel
= pActiveView
->GetSelection();
1562 // Due to differences between table and input cell (e.g clipboard with line breaks),
1563 // the selection may not be in line with the EditEngine anymore.
1564 // Just return without any indication as to why.
1565 if ( aSel
.nEndPos
> aParagraph
.getLength() )
1568 if ( aParagraph
.getLength() > aSel
.nEndPos
&&
1569 ( ScGlobal::getCharClass().isLetterNumeric( aParagraph
, aSel
.nEndPos
) ||
1570 aParagraph
[ aSel
.nEndPos
] == '_' ||
1571 aParagraph
[ aSel
.nEndPos
] == '.' ||
1572 aParagraph
[ aSel
.nEndPos
] == '$' ) )
1575 // Is the cursor at the end of a word?
1576 if ( aSel
.nEndPos
<= 0 )
1579 OUString
aSelText( aParagraph
.copy( 0, aSel
.nEndPos
));
1582 if ( GetFuncName( aSelText
, aText
) )
1584 // function name is incomplete:
1585 // show matching functions name as tip above cell
1586 ::std::vector
<OUString
> aNewVec
;
1587 miAutoPosFormula
= pFormulaData
->end();
1588 miAutoPosFormula
= findTextAll(*pFormulaData
, miAutoPosFormula
, aText
, aNewVec
, false);
1589 if (miAutoPosFormula
!= pFormulaData
->end())
1591 // check if partial function name is not between quotes
1592 sal_Unicode cBetweenQuotes
= 0;
1593 for ( int n
= 0; n
< aSelText
.getLength(); n
++ )
1597 if (aSelText
[n
] == cBetweenQuotes
)
1600 else if ( aSelText
[ n
] == '"' )
1601 cBetweenQuotes
= '"';
1602 else if ( aSelText
[ n
] == '\'' )
1603 cBetweenQuotes
= '\'';
1605 if ( cBetweenQuotes
)
1606 return; // we're between quotes
1608 ShowFuncList(aNewVec
);
1609 aAutoSearch
= aText
;
1614 // function name is complete:
1615 // show tip below the cell with function name and arguments of function
1616 ShowArgumentsTip( aSelText
);
1619 void ScInputHandler::NextFormulaEntry( bool bBack
)
1621 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1622 if ( pActiveView
&& pFormulaData
)
1624 ::std::vector
<OUString
> aNewVec
;
1625 ScTypedCaseStrSet::const_iterator itNew
= findTextAll(*pFormulaData
, miAutoPosFormula
, aAutoSearch
, aNewVec
, bBack
);
1626 if (itNew
!= pFormulaData
->end())
1628 miAutoPosFormula
= itNew
;
1629 ShowFuncList( aNewVec
);
1633 // For Tab we always call HideCursor first
1635 pActiveView
->ShowCursor();
1640 void completeFunction( EditView
* pView
, const OUString
& rInsert
, bool& rParInserted
)
1645 ESelection aSel
= pView
->GetSelection();
1647 bool bNoInitialLetter
= false;
1648 OUString aOld
= pView
->GetEditEngine()->GetText(0);
1649 // in case we want just insert a function and not completing
1650 if ( comphelper::LibreOfficeKit::isActive() )
1652 ESelection aSelRange
= aSel
;
1653 --aSelRange
.nStartPos
;
1654 --aSelRange
.nEndPos
;
1655 pView
->SetSelection(aSelRange
);
1656 pView
->SelectCurrentWord();
1660 bNoInitialLetter
= true;
1661 aSelRange
.nStartPos
= 1;
1662 aSelRange
.nEndPos
= 1;
1663 pView
->SetSelection(aSelRange
);
1665 else if ( pView
->GetSelected().startsWith("()") )
1667 bNoInitialLetter
= true;
1668 ++aSelRange
.nStartPos
;
1669 ++aSelRange
.nEndPos
;
1670 pView
->SetSelection(aSelRange
);
1674 if(!bNoInitialLetter
)
1676 const sal_Int32 nMinLen
= std::max(aSel
.nEndPos
- aSel
.nStartPos
, sal_Int32(1));
1677 // Since transliteration service is used to test for match, the replaced string could be
1678 // longer than rInsert, so in order to find longest match before the cursor, test whole
1679 // string from start to current cursor position (don't limit to length of rInsert)
1680 // Disclaimer: I really don't know if a match longer than rInsert is actually possible,
1681 // so the above is based on assumptions how "transliteration" might possibly work. If
1682 // it's in fact impossible, an optimization would be useful to limit aSel.nStartPos to
1683 // std::max(sal_Int32(0), aSel.nEndPos - rInsert.getLength()).
1685 pView
->SetSelection(aSel
);
1686 const OUString aAll
= pView
->GetSelected();
1688 for (sal_Int32 n
= aAll
.getLength(); n
>= nMinLen
&& aMatch
.isEmpty(); --n
)
1690 const OUString aTest
= aAll
.copy(aAll
.getLength() - n
); // n trailing chars
1691 if (ScGlobal::GetTransliteration().isMatch(aTest
, rInsert
))
1692 aMatch
= aTest
; // Found => break the loop
1695 aSel
.nStartPos
= aSel
.nEndPos
- aMatch
.getLength();
1696 pView
->SetSelection(aSel
);
1699 OUString aInsStr
= rInsert
;
1700 sal_Int32 nInsLen
= aInsStr
.getLength();
1701 bool bDoParen
= ( nInsLen
> 1 && aInsStr
[nInsLen
-2] == '('
1702 && aInsStr
[nInsLen
-1] == ')' );
1705 // Do not insert parentheses after function names if there already are some
1706 // (e.g. if the function name was edited).
1707 ESelection aWordSel
= pView
->GetSelection();
1709 // aWordSel.EndPos points one behind string if word at end
1710 if (aWordSel
.nEndPos
< aOld
.getLength())
1712 sal_Unicode cNext
= aOld
[aWordSel
.nEndPos
];
1716 aInsStr
= aInsStr
.copy( 0, nInsLen
- 2 ); // Skip parentheses
1721 pView
->InsertText( aInsStr
);
1723 if ( bDoParen
) // Put cursor between parentheses
1725 aSel
= pView
->GetSelection();
1728 pView
->SetSelection(aSel
);
1730 rParInserted
= true;
1736 void ScInputHandler::PasteFunctionData()
1738 if (pFormulaData
&& miAutoPosFormula
!= pFormulaData
->end())
1740 const ScTypedStrData
& rData
= *miAutoPosFormula
;
1741 OUString aInsert
= rData
.GetString();
1742 if (aInsert
[aInsert
.getLength()-1] == cParenthesesReplacement
)
1743 aInsert
= OUString::Concat(aInsert
.subView( 0, aInsert
.getLength()-1)) + "()";
1744 bool bParInserted
= false;
1746 DataChanging(); // Cannot be new
1747 completeFunction( pTopView
, aInsert
, bParInserted
);
1748 completeFunction( pTableView
, aInsert
, bParInserted
);
1758 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1759 if (comphelper::LibreOfficeKit::isActive() && pTopView
&& pInputWin
)
1760 pInputWin
->TextGrabFocus();
1762 pActiveView
->ShowCursor();
1765 void ScInputHandler::LOKPasteFunctionData(const OUString
& rFunctionName
)
1767 // in case we have no top view try to create it
1768 if (!pTopView
&& pInputWin
)
1770 ScInputMode eCurMode
= eMode
;
1771 SetMode(SC_INPUT_TOP
);
1776 EditView
* pEditView
= pTopView
? pTopView
: pTableView
;
1778 if (!pActiveViewSh
|| !pEditView
)
1783 const EditEngine
* pEditEngine
= pEditView
->GetEditEngine();
1786 aFormula
= pEditEngine
->GetText(0);
1787 /* TODO: LOK: are you sure you want '+' and '-' let start formulas with
1788 * function names? That was meant for "data typist" numeric keyboard
1790 bEdit
= aFormula
.getLength() > 1 && (aFormula
[0] == '=' || aFormula
[0] == '+' || aFormula
[0] == '-');
1795 OUString
aNewFormula('=');
1796 if ( aFormula
.startsWith("=") )
1797 aNewFormula
= aFormula
;
1799 InputReplaceSelection( aNewFormula
);
1805 ScTypedCaseStrSet::const_iterator aPos
= findText(*pFormulaData
, pFormulaData
->begin(), rFunctionName
, aNew
, /* backward = */false);
1807 if (aPos
!= pFormulaData
->end())
1809 miAutoPosFormula
= aPos
;
1810 PasteFunctionData();
1815 void ScTabViewShell::LOKSendFormulabarUpdate(EditView
* pActiveView
,
1816 const OUString
& rText
,
1817 const ESelection
& rSelection
)
1819 OUString aSelection
;
1822 aSelection
= OUString::number(pActiveView
->GetPosWithField(0, rSelection
.nStartPos
)) + ";" +
1823 OUString::number(pActiveView
->GetPosWithField(0, rSelection
.nEndPos
)) + ";" +
1824 OUString::number(rSelection
.nStartPara
) + ";" + OUString::number(rSelection
.nEndPara
);
1828 aSelection
= OUString::number(rSelection
.nStartPos
) + ";" + OUString::number(rSelection
.nEndPos
) + ";" +
1829 OUString::number(rSelection
.nStartPara
) + ";" + OUString::number(rSelection
.nEndPara
);
1832 sal_uInt64 nCurrentShellId
= reinterpret_cast<sal_uInt64
>(this);
1834 // We can get three updates per keystroke, StartExtTextInput, ExtTextInput and PostExtTextInput
1835 // Skip duplicate updates. Be conservative and don't skip duplicates that are 5+ seconds
1837 std::chrono::steady_clock::time_point now
= std::chrono::steady_clock::now();
1838 if (maSendFormulabarUpdate
.m_nShellId
== nCurrentShellId
&&
1839 maSendFormulabarUpdate
.m_aText
== rText
&&
1840 maSendFormulabarUpdate
.m_aSelection
== aSelection
&&
1841 std::chrono::duration_cast
<std::chrono::seconds
>(
1842 now
- maSendFormulabarUpdate
.m_nTimeStamp
) < std::chrono::seconds(5))
1847 maSendFormulabarUpdate
.m_nShellId
= nCurrentShellId
;
1848 maSendFormulabarUpdate
.m_aText
= rText
;
1849 maSendFormulabarUpdate
.m_aSelection
= aSelection
;
1850 maSendFormulabarUpdate
.m_nTimeStamp
= now
;
1851 maSendFormulabarUpdate
.Send();
1854 void ScTabViewShell::SendFormulabarUpdate::Send()
1856 std::unique_ptr
<jsdialog::ActionDataMap
> pData
= std::make_unique
<jsdialog::ActionDataMap
>();
1857 (*pData
)["action_type"] = "setText";
1858 (*pData
)["text"] = m_aText
;
1859 (*pData
)["selection"] = m_aSelection
;
1860 std::string sWindowId
= std::to_string(m_nShellId
) + "formulabar";
1861 jsdialog::SendAction(sWindowId
, "sc_input_window", std::move(pData
));
1864 // Calculate selection and display as tip help
1865 static OUString
lcl_Calculate( const OUString
& rFormula
, ScDocument
& rDoc
, const ScAddress
&rPos
)
1867 //TODO: Merge with ScFormulaDlg::CalcValue and move into Document!
1868 // Quotation marks for Strings are only inserted here.
1870 if(rFormula
.isEmpty())
1873 std::optional
<ScSimpleFormulaCalculator
> pCalc( std::in_place
, rDoc
, rPos
, rFormula
, false );
1875 // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range
1876 // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own
1877 bool bColRowName
= pCalc
->HasColRowName();
1880 // ColRowName in RPN code?
1881 if ( pCalc
->GetCode()->GetCodeLen() <= 1 )
1882 { // ==1: Single one is as a Parameter always a Range
1883 // ==0: It might be one, if ...
1884 OUString aBraced
= "(" + rFormula
+ ")";
1885 pCalc
.emplace( rDoc
, rPos
, aBraced
, false );
1888 bColRowName
= false;
1891 FormulaError nErrCode
= pCalc
->GetErrCode();
1892 if ( nErrCode
!= FormulaError::NONE
)
1893 return ScGlobal::GetErrorString(nErrCode
);
1895 SvNumberFormatter
& aFormatter
= *rDoc
.GetFormatTable();
1897 if ( pCalc
->IsValue() )
1899 double n
= pCalc
->GetValue();
1900 sal_uInt32 nFormat
= aFormatter
.GetStandardFormat( n
, 0,
1901 pCalc
->GetFormatType(), ScGlobal::eLnge
);
1902 aFormatter
.GetInputLineString( n
, nFormat
, aValue
);
1903 //! display OutputString but insert InputLineString
1907 OUString aStr
= pCalc
->GetString().getString();
1908 sal_uInt32 nFormat
= aFormatter
.GetStandardFormat(
1909 pCalc
->GetFormatType(), ScGlobal::eLnge
);
1911 const Color
* pColor
;
1912 aFormatter
.GetOutputString( aStr
, nFormat
,
1916 aValue
= "\"" + aValue
+ "\"";
1917 //! Escape quotation marks in String??
1921 if ( bColRowName
|| (aTestRange
.Parse(rFormula
, rDoc
) & ScRefFlags::VALID
) )
1927 void ScInputHandler::FormulaPreview()
1930 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1931 if ( pActiveView
&& pActiveViewSh
)
1933 OUString aPart
= pActiveView
->GetSelected();
1934 if (aPart
.isEmpty())
1935 aPart
= mpEditEngine
->GetText(0);
1936 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
1937 aValue
= lcl_Calculate( aPart
, rDoc
, aCursorPos
);
1940 if (!aValue
.isEmpty())
1942 ShowTip( aValue
); // Display as QuickHelp
1943 aManualTip
= aValue
; // Set after ShowTip
1945 miAutoPosFormula
= pFormulaData
->end();
1947 miAutoPosColumn
= pColumnData
->end();
1951 void ScInputHandler::PasteManualTip()
1953 // Three dots at the end -> Range reference -> do not insert
1954 // FIXME: Once we have matrix constants, we can change this
1955 sal_Int32 nTipLen
= aManualTip
.getLength();
1956 sal_uInt32
const nTipLen2(sal::static_int_cast
<sal_uInt32
>(nTipLen
));
1957 if ( nTipLen
&& ( nTipLen
< 3 || aManualTip
.subView( nTipLen2
-3 ) != u
"..." ) )
1959 DataChanging(); // Cannot be new
1961 OUString aInsert
= aManualTip
;
1962 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1963 if (!pActiveView
->HasSelection())
1965 // Nothing selected -> select everything
1966 sal_Int32 nOldLen
= mpEditEngine
->GetTextLen(0);
1967 ESelection
aAllSel( 0, 0, 0, nOldLen
);
1969 pTopView
->SetSelection( aAllSel
);
1971 pTableView
->SetSelection( aAllSel
);
1974 ESelection aSel
= pActiveView
->GetSelection();
1976 OSL_ENSURE( !aSel
.nStartPara
&& !aSel
.nEndPara
, "Too many paragraphs in Formula" );
1977 if ( !aSel
.nStartPos
) // Selection from the start?
1979 if ( aSel
.nEndPos
== mpEditEngine
->GetTextLen(0) )
1981 // Everything selected -> skip quotation marks
1982 if ( aInsert
[0] == '"' )
1983 aInsert
= aInsert
.copy(1);
1984 sal_Int32 nInsLen
= aInsert
.getLength();
1985 if ( aInsert
.endsWith("\"") )
1986 aInsert
= aInsert
.copy( 0, nInsLen
-1 );
1988 else if ( aSel
.nEndPos
)
1990 // Not everything selected -> do not overwrite equality sign
1991 //FIXME: Even double equality signs??
1994 pTopView
->SetSelection( aSel
);
1996 pTableView
->SetSelection( aSel
);
2000 pTopView
->InsertText( aInsert
, true );
2002 pTableView
->InsertText( aInsert
, true );
2010 void ScInputHandler::ResetAutoPar()
2015 void ScInputHandler::AutoParAdded()
2017 ++nAutoPar
; // Closing parenthesis can be overwritten
2020 bool ScInputHandler::CursorAtClosingPar()
2022 // Test if the cursor is before a closing parenthesis
2023 // Selection from SetReference has been removed before
2024 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2025 if ( pActiveView
&& !pActiveView
->HasSelection() && bFormulaMode
)
2027 ESelection aSel
= pActiveView
->GetSelection();
2028 sal_Int32 nPos
= aSel
.nStartPos
;
2029 OUString aFormula
= mpEditEngine
->GetText(0);
2030 if ( nPos
< aFormula
.getLength() && aFormula
[nPos
] == ')' )
2036 void ScInputHandler::SkipClosingPar()
2038 // this is called when a ')' is typed and the cursor is before a ')'
2039 // that can be overwritten -> just set the cursor behind the ')'
2041 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2044 ESelection aSel
= pActiveView
->GetSelection();
2048 // this is in a formula (only one paragraph), so the selection
2049 // can be used directly for the TopView
2052 pTopView
->SetSelection( aSel
);
2054 pTableView
->SetSelection( aSel
);
2057 OSL_ENSURE(nAutoPar
, "SkipClosingPar: count is wrong");
2063 void ScInputHandler::GetColData()
2065 if ( !pActiveViewSh
)
2068 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
2071 pColumnData
->clear();
2073 pColumnData
.reset( new ScTypedCaseStrSet
);
2075 std::vector
<ScTypedStrData
> aEntries
;
2076 rDoc
.GetDataEntries(
2077 aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab(), aEntries
);
2078 if (!aEntries
.empty())
2079 pColumnData
->insert(aEntries
.begin(), aEntries
.end());
2081 miAutoPosColumn
= pColumnData
->end();
2084 void ScInputHandler::UseColData() // When typing
2086 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2087 if ( !(pActiveView
&& pColumnData
) )
2090 // Only change when cursor is at the end
2091 ESelection aSel
= pActiveView
->GetSelection();
2094 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
2095 if ( aSel
.nEndPara
+1 != nParCnt
)
2098 sal_Int32 nParLen
= mpEditEngine
->GetTextLen( aSel
.nEndPara
);
2099 if ( aSel
.nEndPos
!= nParLen
)
2102 OUString aText
= GetEditText(mpEditEngine
.get());
2103 if (aText
.isEmpty())
2106 std::vector
< OUString
> aResultVec
;
2108 sal_Int32 nLongestPrefixLen
= 0;
2109 miAutoPosColumn
= pColumnData
->end();
2110 mbPartialPrefix
= false;
2111 miAutoPosColumn
= findTextAll(*pColumnData
, miAutoPosColumn
, aText
, aResultVec
, false, &nLongestPrefixLen
);
2113 if (nLongestPrefixLen
<= 0 || aResultVec
.empty())
2116 if (aResultVec
.size() > 1)
2118 mbPartialPrefix
= true;
2119 bUseTab
= true; // Allow Ctrl (+ Shift + ) + TAB cycling.
2120 miAutoPosColumn
= pColumnData
->end();
2122 // Display the rest of longest common prefix as suggestion.
2123 aNew
= aResultVec
[0].copy(0, nLongestPrefixLen
);
2127 aNew
= aResultVec
[0];
2130 // Strings can contain line endings (e.g. due to dBase import),
2131 // which would result in multiple paragraphs here, which is not desirable.
2132 //! Then GetExactMatch doesn't work either
2133 lcl_RemoveLineEnd( aNew
);
2135 // Keep paragraph, just append the rest
2136 //! Exact replacement in EnterHandler !!!
2137 // One Space between paragraphs:
2138 sal_Int32 nEdLen
= mpEditEngine
->GetTextLen() + nParCnt
- 1;
2139 OUString aIns
= aNew
.copy(nEdLen
);
2141 // Selection must be "backwards", so the cursor stays behind the last
2143 ESelection
aSelection( aSel
.nEndPara
, aSel
.nEndPos
+ aIns
.getLength(),
2144 aSel
.nEndPara
, aSel
.nEndPos
);
2146 // When editing in input line, apply to both edit views
2149 pTableView
->InsertText( aIns
);
2150 pTableView
->SetSelection( aSelection
);
2154 pTopView
->InsertText( aIns
);
2155 pTopView
->SetSelection( aSelection
);
2158 aAutoSearch
= aText
; // To keep searching - nAutoPos is set
2161 void ScInputHandler::NextAutoEntry( bool bBack
)
2163 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2164 if ( pActiveView
&& pColumnData
)
2166 if (!aAutoSearch
.isEmpty())
2168 // Is the selection still valid (could be changed via the mouse)?
2169 ESelection aSel
= pActiveView
->GetSelection();
2171 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
2172 if ( aSel
.nEndPara
+1 == nParCnt
&& aSel
.nStartPara
== aSel
.nEndPara
)
2174 OUString aText
= GetEditText(mpEditEngine
.get());
2175 sal_Int32 nSelLen
= aSel
.nEndPos
- aSel
.nStartPos
;
2176 sal_Int32 nParLen
= mpEditEngine
->GetTextLen( aSel
.nEndPara
);
2177 if ( aSel
.nEndPos
== nParLen
&& aText
.getLength() == aAutoSearch
.getLength() + nSelLen
)
2180 ScTypedCaseStrSet::const_iterator itNew
=
2181 findText(*pColumnData
, miAutoPosColumn
, aAutoSearch
, aNew
, bBack
);
2183 if (itNew
!= pColumnData
->end())
2186 miAutoPosColumn
= itNew
;
2187 bInOwnChange
= true; // disable ModifyHdl (reset below)
2188 mbPartialPrefix
= false;
2190 lcl_RemoveLineEnd( aNew
);
2191 OUString aIns
= aNew
.copy(aAutoSearch
.getLength());
2193 // when editing in input line, apply to both edit views
2196 pTableView
->DeleteSelected();
2197 pTableView
->InsertText( aIns
);
2198 pTableView
->SetSelection( ESelection(
2199 aSel
.nEndPara
, aSel
.nStartPos
+ aIns
.getLength(),
2200 aSel
.nEndPara
, aSel
.nStartPos
) );
2204 pTopView
->DeleteSelected();
2205 pTopView
->InsertText( aIns
);
2206 pTopView
->SetSelection( ESelection(
2207 aSel
.nEndPara
, aSel
.nStartPos
+ aIns
.getLength(),
2208 aSel
.nEndPara
, aSel
.nStartPos
) );
2211 bInOwnChange
= false;
2218 // For Tab, HideCursor was always called first
2220 pActiveView
->ShowCursor();
2223 // Highlight parentheses
2224 void ScInputHandler::UpdateParenthesis()
2227 //TODO: Can we disable parentheses highlighting per parentheses?
2228 bool bFound
= false;
2229 if ( bFormulaMode
&& eMode
!= SC_INPUT_TOP
)
2231 if ( pTableView
&& !pTableView
->HasSelection() ) // Selection is always at the bottom
2233 ESelection aSel
= pTableView
->GetSelection();
2236 // Examine character left to the cursor
2237 sal_Int32 nPos
= aSel
.nStartPos
- 1;
2238 OUString aFormula
= mpEditEngine
->GetText(aSel
.nStartPara
);
2239 sal_Unicode c
= aFormula
[nPos
];
2240 if ( c
== '(' || c
== ')' )
2242 // Note this matches only within one paragraph.
2243 sal_Int32 nOther
= lcl_MatchParenthesis( aFormula
, nPos
);
2246 SfxItemSet
aSet( mpEditEngine
->GetEmptyItemSet() );
2247 aSet
.Put( SvxWeightItem( WEIGHT_BOLD
, EE_CHAR_WEIGHT
) );
2249 //! Distinguish if cell is already highlighted!!!!
2250 if (bParenthesisShown
)
2252 // Remove old highlighting
2253 sal_Int32 nCount
= mpEditEngine
->GetParagraphCount();
2254 for (sal_Int32 i
=0; i
<nCount
; i
++)
2255 mpEditEngine
->RemoveCharAttribs( i
, EE_CHAR_WEIGHT
);
2258 ESelection
aSelThis( aSel
.nStartPara
, nPos
, aSel
.nStartPara
, nPos
+1);
2259 mpEditEngine
->QuickSetAttribs( aSet
, aSelThis
);
2260 ESelection
aSelOther( aSel
.nStartPara
, nOther
, aSel
.nStartPara
, nOther
+1);
2261 mpEditEngine
->QuickSetAttribs( aSet
, aSelOther
);
2263 // Dummy InsertText for Update and Paint (selection is empty)
2264 pTableView
->InsertText( OUString() );
2271 // mark parenthesis right of cursor if it will be overwritten (nAutoPar)
2272 // with different color (COL_LIGHTBLUE) ??
2276 // Remove old highlighting, if no new one is set
2277 if ( bParenthesisShown
&& !bFound
&& pTableView
)
2279 sal_Int32 nCount
= mpEditEngine
->GetParagraphCount();
2280 for (sal_Int32 i
=0; i
<nCount
; i
++)
2281 pTableView
->RemoveCharAttribs( i
, EE_CHAR_WEIGHT
);
2284 bParenthesisShown
= bFound
;
2287 void ScInputHandler::ViewShellGone(const ScTabViewShell
* pViewSh
) // Executed synchronously!
2289 if ( pViewSh
== pActiveViewSh
)
2292 pLastPattern
= nullptr;
2295 if ( pViewSh
== pRefViewSh
)
2297 //! The input from the EnterHandler does not arrive anymore
2298 // We end the EditMode anyways
2300 bFormulaMode
= false;
2301 pRefViewSh
= nullptr;
2302 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2303 SC_MOD()->SetRefInputHdl(nullptr);
2305 pInputWin
->SetFormulaMode(false);
2306 UpdateAutoCorrFlag();
2309 pActiveViewSh
= dynamic_cast<ScTabViewShell
*>( SfxViewShell::Current() );
2311 if ( pActiveViewSh
&& pActiveViewSh
== pViewSh
)
2313 OSL_FAIL("pActiveViewSh is gone");
2314 pActiveViewSh
= nullptr;
2317 if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
2318 UpdateRefDevice(); // Don't keep old document's printer as RefDevice
2321 void ScInputHandler::UpdateActiveView()
2323 ImplCreateEditEngine();
2325 // #i20588# Don't rely on focus to find the active edit view. Instead, the
2326 // active pane at the start of editing is now stored (GetEditActivePart).
2327 // GetActiveWin (the currently active pane) fails for ref input across the
2328 // panes of a split view.
2330 vcl::Window
* pShellWin
= pActiveViewSh
?
2331 pActiveViewSh
->GetWindowByPos( pActiveViewSh
->GetViewData().GetEditActivePart() ) :
2334 sal_uInt16 nCount
= mpEditEngine
->GetViewCount();
2337 pTableView
= mpEditEngine
->GetView();
2338 for (sal_uInt16 i
=1; i
<nCount
; i
++)
2340 EditView
* pThis
= mpEditEngine
->GetView(i
);
2341 vcl::Window
* pWin
= pThis
->GetWindow();
2342 if ( pWin
==pShellWin
)
2347 pTableView
= nullptr;
2349 // setup the pTableView editeng for tiled rendering to get cursor and selections
2350 if (pTableView
&& pActiveViewSh
)
2352 if (comphelper::LibreOfficeKit::isActive())
2354 pTableView
->RegisterViewShell(pActiveViewSh
);
2358 if (pInputWin
&& (eMode
== SC_INPUT_TOP
|| eMode
== SC_INPUT_TABLE
))
2360 // tdf#71409: Always create the edit engine instance for the input
2361 // window, in order to properly manage accessibility events.
2362 pTopView
= pInputWin
->GetEditView();
2363 if (eMode
!= SC_INPUT_TOP
)
2370 void ScInputHandler::SetInputWindow( ScInputWindow
* pNew
)
2375 void ScInputHandler::StopInputWinEngine( bool bAll
)
2377 if (pInputWin
&& !pInputWin
->isDisposed())
2378 pInputWin
->StopEditEngine( bAll
);
2380 pTopView
= nullptr; // invalid now
2383 EditView
* ScInputHandler::GetActiveView()
2386 return pTopView
? pTopView
: pTableView
;
2389 void ScInputHandler::ForgetLastPattern()
2391 pLastPattern
= nullptr;
2392 if ( !pLastState
&& pActiveViewSh
)
2393 pActiveViewSh
->UpdateInputHandler( true ); // Get status again
2395 NotifyChange( pLastState
.get(), true );
2398 void ScInputHandler::UpdateAdjust( sal_Unicode cTyped
)
2400 SvxAdjust eSvxAdjust
;
2401 switch (eAttrAdjust
)
2403 case SvxCellHorJustify::Standard
:
2405 bool bNumber
= false;
2406 if (cTyped
) // Restarted
2407 bNumber
= (cTyped
>='0' && cTyped
<='9'); // Only ciphers are numbers
2408 else if ( pActiveViewSh
)
2410 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
2411 bNumber
= ( rDoc
.GetCellType( aCursorPos
) == CELLTYPE_VALUE
);
2413 eSvxAdjust
= bNumber
? SvxAdjust::Right
: SvxAdjust::Left
;
2416 case SvxCellHorJustify::Block
:
2417 eSvxAdjust
= SvxAdjust::Block
;
2419 case SvxCellHorJustify::Center
:
2420 eSvxAdjust
= SvxAdjust::Center
;
2422 case SvxCellHorJustify::Right
:
2423 eSvxAdjust
= SvxAdjust::Right
;
2425 default: // SvxCellHorJustify::Left
2426 eSvxAdjust
= SvxAdjust::Left
;
2430 bool bAsianVertical
= pLastPattern
&&
2431 pLastPattern
->GetItem( ATTR_STACKED
).GetValue() &&
2432 pLastPattern
->GetItem( ATTR_VERTICAL_ASIAN
).GetValue();
2433 if ( bAsianVertical
)
2435 // Always edit at top of cell -> LEFT when editing vertically
2436 eSvxAdjust
= SvxAdjust::Left
;
2439 pEditDefaults
->Put( SvxAdjustItem( eSvxAdjust
, EE_PARA_JUST
) );
2440 mpEditEngine
->SetDefaults( *pEditDefaults
);
2442 if ( pActiveViewSh
)
2444 pActiveViewSh
->GetViewData().SetEditAdjust( eSvxAdjust
);
2446 mpEditEngine
->SetVertical( bAsianVertical
);
2449 void ScInputHandler::RemoveAdjust()
2451 // Delete hard alignment attributes
2452 bool bUndo
= mpEditEngine
->IsUndoEnabled();
2454 mpEditEngine
->EnableUndo( false );
2456 // Non-default paragraph attributes (e.g. from clipboard)
2457 // must be turned into character attributes
2458 mpEditEngine
->RemoveParaAttribs();
2461 mpEditEngine
->EnableUndo( true );
2465 void ScInputHandler::RemoveRangeFinder()
2467 // Delete pRangeFindList and colors
2468 mpEditEngine
->SetUpdateLayout(false);
2469 sal_Int32 nCount
= mpEditEngine
->GetParagraphCount(); // Could just have been inserted
2470 for (sal_Int32 i
=0; i
<nCount
; i
++)
2471 mpEditEngine
->RemoveCharAttribs( i
, EE_CHAR_COLOR
);
2472 mpEditEngine
->SetUpdateLayout(true);
2474 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2475 pActiveView
->ShowCursor( false );
2477 DeleteRangeFinder(); // Deletes the list and the labels on the table
2480 bool ScInputHandler::StartTable( sal_Unicode cTyped
, bool bFromCommand
, bool bInputActivated
,
2481 ScEditEngineDefaulter
* pTopEngine
)
2483 bool bNewTable
= false;
2490 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
2492 if (!rDoc
.ValidCol(aCursorPos
.Col()))
2495 ImplCreateEditEngine();
2500 const ScMarkData
& rMark
= pActiveViewSh
->GetViewData().GetMarkData();
2501 ScEditableTester aTester
;
2502 if ( rMark
.IsMarked() || rMark
.IsMultiMarked() )
2503 aTester
.TestSelection( rDoc
, rMark
);
2505 aTester
.TestSelectedBlock(
2506 rDoc
, aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Col(), aCursorPos
.Row(), rMark
);
2508 bool bStartInputMode
= true;
2510 if (!aTester
.IsEditable())
2513 // We allow read-only input mode activation regardless
2514 // whether it's part of an array or not or whether explicit cell
2515 // activation is requested (double-click or F2) or a click in input
2517 bool bShowError
= (!bInputActivated
|| !aTester
.GetMessageId() || aTester
.GetMessageId() != STR_PROTECTIONERR
) &&
2518 !pActiveViewSh
->GetViewData().GetDocShell()->IsReadOnly();
2521 eMode
= SC_INPUT_NONE
;
2522 StopInputWinEngine( true );
2523 UpdateFormulaMode();
2524 if ( pActiveViewSh
&& ( !bFromCommand
|| !bCommandErrorShown
) )
2526 // Prevent repeated error messages for the same cell from command events
2527 // (for keyboard events, multiple messages are wanted).
2528 // Set the flag before showing the error message because the command handler
2529 // for the next IME command may be called when showing the dialog.
2531 bCommandErrorShown
= true;
2533 pActiveViewSh
->GetActiveWin()->GrabFocus();
2534 pActiveViewSh
->ErrorMessage(aTester
.GetMessageId());
2536 bStartInputMode
= false;
2540 if (bStartInputMode
)
2542 // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise)
2543 mpEditEngine
->SetUpdateLayout( false );
2545 // Take over attributes in EditEngine
2546 const ScPatternAttr
* pPattern
= rDoc
.GetPattern( aCursorPos
.Col(),
2549 if (pPattern
!= pLastPattern
)
2552 const SfxItemSet
& rAttrSet
= pPattern
->GetItemSet();
2554 if ( const SfxUInt32Item
* pItem
= rAttrSet
.GetItemIfSet( ATTR_VALUE_FORMAT
) )
2556 sal_uInt32 nFormat
= pItem
->GetValue();
2557 if (SvNumFormatType::PERCENT
== rDoc
.GetFormatTable()->GetType( nFormat
))
2558 nCellPercentFormatDecSep
= rDoc
.GetFormatTable()->GetFormatDecimalSep( nFormat
).toChar();
2560 nCellPercentFormatDecSep
= 0;
2563 nCellPercentFormatDecSep
= 0; // Default: no percent
2565 // Validity specified?
2566 if ( const SfxUInt32Item
* pItem
= rAttrSet
.GetItemIfSet( ATTR_VALIDDATA
) )
2567 nValidation
= pItem
->GetValue();
2571 // EditEngine Defaults
2572 // In no case SetParaAttribs, because the EditEngine might already
2573 // be filled (for Edit cells).
2574 // SetParaAttribs would change the content.
2576 //! The SetDefaults is now (since MUST/src602
2577 //! EditEngine changes) implemented as a SetParaAttribs.
2580 pPattern
->FillEditItemSet( pEditDefaults
.get() );
2581 mpEditEngine
->SetDefaults( *pEditDefaults
);
2582 pLastPattern
= pPattern
;
2583 bLastIsSymbol
= pPattern
->IsSymbolFont();
2585 // Background color must be known for automatic font color.
2586 // For transparent cell background, the document background color must be used.
2588 Color aBackCol
= pPattern
->GetItem( ATTR_BACKGROUND
).GetColor();
2589 ScModule
* pScMod
= SC_MOD();
2590 if ( aBackCol
.IsTransparent() ||
2591 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2592 aBackCol
= pScMod
->GetColorConfig().GetColorValue(svtools::DOCCOLOR
).nColor
;
2593 mpEditEngine
->SetBackgroundColor( aBackCol
);
2596 eAttrAdjust
= pPattern
->GetItem(ATTR_HOR_JUSTIFY
).GetValue();
2597 if ( eAttrAdjust
== SvxCellHorJustify::Repeat
&&
2598 pPattern
->GetItem(ATTR_LINEBREAK
).GetValue() )
2600 // #i31843# "repeat" with "line breaks" is treated as default alignment
2601 eAttrAdjust
= SvxCellHorJustify::Standard
;
2607 // Necessary to sync SvxAutoCorrect behavior. This has to be
2608 // done before InitRangeFinder() below.
2609 MergeLanguageAttributes( *pTopEngine
);
2612 // UpdateSpellSettings enables online spelling if needed
2613 // -> also call if attributes are unchanged
2614 UpdateSpellSettings( true ); // uses pLastPattern
2620 mpEditEngine
->SetTextCurrentDefaults(aCurrentText
);
2621 aStr
= aCurrentText
;
2623 aCurrentText
.clear();
2626 aStr
= GetEditText(mpEditEngine
.get());
2628 // cTyped!=0 is overtyping, not editing.
2629 mbEditingExistingContent
= !cTyped
&& !aStr
.isEmpty();
2631 if (aStr
.startsWith("{=") && aStr
.endsWith("}") ) // Matrix formula?
2633 aStr
= aStr
.copy(1, aStr
.getLength() -2);
2634 mpEditEngine
->SetTextCurrentDefaults(aStr
);
2636 pInputWin
->SetTextString(aStr
, true);
2639 UpdateAdjust( cTyped
);
2641 if ( SC_MOD()->GetAppOptions().GetAutoComplete() )
2644 if (!cTyped
&& !bCreatingFuncView
&& StartsLikeFormula(aStr
))
2645 InitRangeFinder(aStr
); // Formula is being edited -> RangeFinder
2647 bNewTable
= true; // -> PostEditView Call
2651 if (!bProtected
&& pInputWin
)
2652 pInputWin
->SetOkCancelMode();
2657 void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter
& rDestEngine
) const
2659 const SfxItemSet
& rSrcSet
= mpEditEngine
->GetDefaults();
2660 rDestEngine
.SetDefaultItem( rSrcSet
.Get( EE_CHAR_LANGUAGE
));
2661 rDestEngine
.SetDefaultItem( rSrcSet
.Get( EE_CHAR_LANGUAGE_CJK
));
2662 rDestEngine
.SetDefaultItem( rSrcSet
.Get( EE_CHAR_LANGUAGE_CTL
));
2665 static void lcl_SetTopSelection( EditView
* pEditView
, ESelection
& rSel
)
2667 OSL_ENSURE( rSel
.nStartPara
==0 && rSel
.nEndPara
==0, "SetTopSelection: Para != 0" );
2669 EditEngine
* pEngine
= pEditView
->GetEditEngine();
2670 sal_Int32 nCount
= pEngine
->GetParagraphCount();
2673 sal_Int32 nParLen
= pEngine
->GetTextLen(rSel
.nStartPara
);
2674 while (rSel
.nStartPos
> nParLen
&& rSel
.nStartPara
+1 < nCount
)
2676 rSel
.nStartPos
-= nParLen
+ 1; // Including space from line break
2677 nParLen
= pEngine
->GetTextLen(++rSel
.nStartPara
);
2680 nParLen
= pEngine
->GetTextLen(rSel
.nEndPara
);
2681 while (rSel
.nEndPos
> nParLen
&& rSel
.nEndPara
+1 < nCount
)
2683 rSel
.nEndPos
-= nParLen
+ 1; // Including space from line break
2684 nParLen
= pEngine
->GetTextLen(++rSel
.nEndPara
);
2688 ESelection aSel
= pEditView
->GetSelection();
2690 if ( rSel
.nStartPara
!= aSel
.nStartPara
|| rSel
.nEndPara
!= aSel
.nEndPara
2691 || rSel
.nStartPos
!= aSel
.nStartPos
|| rSel
.nEndPos
!= aSel
.nEndPos
)
2692 pEditView
->SetSelection( rSel
);
2695 void ScInputHandler::SyncViews( const EditView
* pSourceView
)
2699 bool bSelectionForTopView
= false;
2700 if (pTopView
&& pTopView
!= pSourceView
)
2701 bSelectionForTopView
= true;
2702 bool bSelectionForTableView
= false;
2703 if (pTableView
&& pTableView
!= pSourceView
)
2704 bSelectionForTableView
= true;
2705 if (bSelectionForTopView
|| bSelectionForTableView
)
2707 ESelection
aSel(pSourceView
->GetSelection());
2708 if (bSelectionForTopView
)
2709 pTopView
->SetSelection(aSel
);
2710 if (bSelectionForTableView
)
2711 lcl_SetTopSelection(pTableView
, aSel
);
2714 // Only sync selection from topView if we are actually editing there
2715 else if (pTopView
&& pTableView
)
2717 ESelection
aSel(pTopView
->GetSelection());
2718 lcl_SetTopSelection( pTableView
, aSel
);
2722 IMPL_LINK_NOARG(ScInputHandler
, ModifyHdl
, LinkParamNone
*, void)
2724 if ( !bInOwnChange
&& ( eMode
==SC_INPUT_TYPE
|| eMode
==SC_INPUT_TABLE
) &&
2725 mpEditEngine
&& mpEditEngine
->IsUpdateLayout() && pInputWin
)
2727 // Update input line from ModifyHdl for changes that are not
2728 // wrapped by DataChanging/DataChanged calls (like Drag&Drop)
2729 OUString
aText(ScEditUtil::GetMultilineString(*mpEditEngine
));
2730 lcl_RemoveTabs(aText
);
2731 pInputWin
->SetTextString(aText
, true);
2736 * @return true means new view created
2738 bool ScInputHandler::DataChanging( sal_Unicode cTyped
, bool bFromCommand
)
2741 pActiveViewSh
->GetViewData().SetPasteMode( ScPasteFlags::NONE
);
2742 bInOwnChange
= true; // disable ModifyHdl (reset in DataChanged)
2744 if ( eMode
== SC_INPUT_NONE
)
2745 return StartTable( cTyped
, bFromCommand
, false, nullptr );
2750 void ScInputHandler::DataChanged( bool bFromTopNotify
, bool bSetModified
)
2752 ImplCreateEditEngine();
2754 if (eMode
==SC_INPUT_NONE
)
2755 eMode
= SC_INPUT_TYPE
;
2757 if ( eMode
== SC_INPUT_TOP
&& pTopView
&& !bFromTopNotify
)
2759 // table EditEngine is formatted below, input line needs formatting after paste
2760 // #i20282# not when called from the input line's modify handler
2761 pTopView
->GetEditEngine()->QuickFormatDoc( true );
2763 // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it
2764 // can't safely access the EditEngine's current view, so the cursor has to be
2765 // shown again here.
2766 pTopView
->ShowCursor();
2773 if ( pRangeFindList
&& !bInRangeUpdate
)
2774 RemoveRangeFinder(); // Delete attributes and labels
2776 UpdateParenthesis(); // Highlight parentheses anew
2778 const bool bUpdateKit
= comphelper::LibreOfficeKit::isActive() && pActiveViewSh
&& pInputWin
;
2780 if (eMode
==SC_INPUT_TYPE
|| eMode
==SC_INPUT_TABLE
)
2784 aText
= ScEditUtil::GetMultilineString(*mpEditEngine
);
2786 aText
= GetEditText(mpEditEngine
.get());
2787 lcl_RemoveTabs(aText
);
2791 // If we will end up updating LoKit at the end, we can skip it here
2792 pInputWin
->SetTextString(aText
, !bUpdateKit
);
2795 if (comphelper::LibreOfficeKit::isActive())
2799 // TODO: deprecated?
2800 pActiveViewSh
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA
, aText
.toUtf8().getStr());
2805 // If the cursor is before the end of a paragraph, parts are being pushed to
2806 // the right (independently from the eMode) -> Adapt View!
2807 // If the cursor is at the end, the StatusHandler of the ViewData is sufficient.
2809 // First make sure the status handler is called now if the cursor
2810 // is outside the visible area
2811 mpEditEngine
->QuickFormatDoc();
2813 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2815 if (pActiveView
&& pActiveViewSh
)
2817 ScViewData
& rViewData
= pActiveViewSh
->GetViewData();
2819 bool bNeedGrow
= ( rViewData
.GetEditAdjust() != SvxAdjust::Left
); // Always right-aligned
2822 // Cursor before the end?
2823 aSel
= pActiveView
->GetSelection();
2825 bNeedGrow
= ( aSel
.nEndPos
!= mpEditEngine
->GetTextLen(aSel
.nEndPara
) );
2829 bNeedGrow
= rViewData
.GetDocument().IsLayoutRTL( rViewData
.GetTabNo() );
2833 // Adjust inplace view
2834 rViewData
.EditGrowY();
2835 rViewData
.EditGrowX();
2843 aSel
= pActiveView
->GetSelection();
2845 pActiveViewSh
->LOKSendFormulabarUpdate(pActiveView
,
2846 ScEditUtil::GetMultilineString(*mpEditEngine
),
2850 UpdateFormulaMode();
2851 bTextValid
= false; // Changes only in the EditEngine
2852 bInOwnChange
= false;
2855 bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr
) const
2857 // For new input '+' and '-' may start the dreaded "lazy data typist"
2858 // formula input, editing existing formula content can only start with '='.
2859 return !rStr
.empty() && (rStr
[0] == '=' || (!mbEditingExistingContent
&& (rStr
[0] == '+' || rStr
[0] == '-')));
2862 void ScInputHandler::UpdateFormulaMode()
2864 SfxApplication
* pSfxApp
= SfxGetpApp();
2866 bool bIsFormula
= !bProtected
;
2869 const OUString
& rText
= mpEditEngine
->GetText(0);
2870 bIsFormula
= StartsLikeFormula(rText
);
2877 bFormulaMode
= true;
2878 pRefViewSh
= pActiveViewSh
;
2879 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2880 ScModule
* pMod
= SC_MOD();
2881 pMod
->SetRefInputHdl(this);
2883 pInputWin
->SetFormulaMode(true);
2885 // in LOK, we always need to perform the GetFormulaData() call so
2886 // that the formula insertion works
2887 if (comphelper::LibreOfficeKit::isActive() || pMod
->GetAppOptions().GetAutoComplete())
2890 UpdateParenthesis();
2891 UpdateAutoCorrFlag();
2899 bFormulaMode
= false;
2900 pRefViewSh
= nullptr;
2901 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2902 SC_MOD()->SetRefInputHdl(nullptr);
2904 pInputWin
->SetFormulaMode(false);
2905 UpdateAutoCorrFlag();
2910 void ScInputHandler::ShowRefFrame()
2912 // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat
2913 // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh.
2914 // A local variable is used instead.
2915 ScTabViewShell
* pVisibleSh
= dynamic_cast<ScTabViewShell
*>( SfxViewShell::Current() );
2916 if ( !(pRefViewSh
&& pRefViewSh
!= pVisibleSh
) )
2919 bool bFound
= false;
2920 SfxViewFrame
* pRefFrame
= pRefViewSh
->GetViewFrame();
2921 SfxViewFrame
* pOneFrame
= SfxViewFrame::GetFirst();
2922 while ( pOneFrame
&& !bFound
)
2924 if ( pOneFrame
== pRefFrame
)
2926 pOneFrame
= SfxViewFrame::GetNext( *pOneFrame
);
2931 // We count on Activate working synchronously here
2932 // (pActiveViewSh is set while doing so)
2933 pRefViewSh
->SetActive(); // Appear and SetViewFrame
2935 // pLastState is set correctly in the NotifyChange from the Activate
2939 OSL_FAIL("ViewFrame for reference input is not here anymore");
2943 void ScInputHandler::RemoveSelection()
2945 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2949 ESelection aSel
= pActiveView
->GetSelection();
2950 aSel
.nStartPara
= aSel
.nEndPara
;
2951 aSel
.nStartPos
= aSel
.nEndPos
;
2953 pTableView
->SetSelection( aSel
);
2955 pTopView
->SetSelection( aSel
);
2958 void ScInputHandler::InvalidateAttribs()
2960 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
2964 SfxBindings
& rBindings
= pViewFrm
->GetBindings();
2966 rBindings
.Invalidate( SID_ATTR_CHAR_FONT
);
2967 rBindings
.Invalidate( SID_ATTR_CHAR_FONTHEIGHT
);
2968 rBindings
.Invalidate( SID_ATTR_CHAR_COLOR
);
2970 rBindings
.Invalidate( SID_ATTR_CHAR_WEIGHT
);
2971 rBindings
.Invalidate( SID_ATTR_CHAR_POSTURE
);
2972 rBindings
.Invalidate( SID_ATTR_CHAR_UNDERLINE
);
2973 rBindings
.Invalidate( SID_ATTR_CHAR_OVERLINE
);
2974 rBindings
.Invalidate( SID_ULINE_VAL_NONE
);
2975 rBindings
.Invalidate( SID_ULINE_VAL_SINGLE
);
2976 rBindings
.Invalidate( SID_ULINE_VAL_DOUBLE
);
2977 rBindings
.Invalidate( SID_ULINE_VAL_DOTTED
);
2979 rBindings
.Invalidate( SID_HYPERLINK_GETLINK
);
2981 rBindings
.Invalidate( SID_ATTR_CHAR_KERNING
);
2982 rBindings
.Invalidate( SID_SET_SUPER_SCRIPT
);
2983 rBindings
.Invalidate( SID_SET_SUB_SCRIPT
);
2984 rBindings
.Invalidate( SID_ATTR_CHAR_STRIKEOUT
);
2985 rBindings
.Invalidate( SID_ATTR_CHAR_SHADOWED
);
2987 rBindings
.Invalidate( SID_SAVEDOC
);
2988 rBindings
.Invalidate( SID_DOC_MODIFIED
);
2991 // --------------- public methods --------------------------------------------
2993 void ScInputHandler::SetMode( ScInputMode eNewMode
, const OUString
* pInitText
, ScEditEngineDefaulter
* pTopEngine
)
2995 if ( eMode
== eNewMode
)
2998 ImplCreateEditEngine();
3002 eMode
= SC_INPUT_NONE
;
3003 StopInputWinEngine( true );
3005 pActiveViewSh
->GetActiveWin()->GrabFocus();
3009 if (eNewMode
!= SC_INPUT_NONE
&& pActiveViewSh
)
3010 // Disable paste mode when edit mode starts.
3011 pActiveViewSh
->GetViewData().SetPasteMode( ScPasteFlags::NONE
);
3013 bInOwnChange
= true; // disable ModifyHdl (reset below)
3015 ScInputMode eOldMode
= eMode
;
3017 if (eOldMode
== SC_INPUT_TOP
&& eNewMode
!= eOldMode
)
3018 StopInputWinEngine( false );
3020 if (eMode
==SC_INPUT_TOP
|| eMode
==SC_INPUT_TABLE
)
3022 if (eOldMode
== SC_INPUT_NONE
) // not if switching between modes
3024 if (StartTable(0, false, eMode
== SC_INPUT_TABLE
, pTopEngine
))
3027 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
3033 mpEditEngine
->SetTextCurrentDefaults(*pInitText
);
3037 sal_Int32 nPara
= mpEditEngine
->GetParagraphCount()-1;
3038 sal_Int32 nLen
= mpEditEngine
->GetText(nPara
).getLength();
3039 sal_uInt16 nCount
= mpEditEngine
->GetViewCount();
3041 for (sal_uInt16 i
=0; i
<nCount
; i
++)
3043 if ( eMode
== SC_INPUT_TABLE
&& eOldMode
== SC_INPUT_TOP
)
3049 mpEditEngine
->GetView(i
)->
3050 SetSelection( ESelection( nPara
, nLen
, nPara
, nLen
) );
3052 mpEditEngine
->GetView(i
)->ShowCursor(false);
3057 if (eMode
==SC_INPUT_TABLE
|| eMode
==SC_INPUT_TYPE
)
3060 pTableView
->SetEditEngineUpdateLayout(true);
3065 pTopView
->SetEditEngineUpdateLayout(true);
3068 if (eNewMode
!= eOldMode
)
3069 UpdateFormulaMode();
3071 bInOwnChange
= false;
3075 * @return true if rString only contains digits (no autocorrect then)
3077 static bool lcl_IsNumber(std::u16string_view aString
)
3079 size_t nLen
= aString
.size();
3080 for (size_t i
=0; i
<nLen
; i
++)
3082 sal_Unicode c
= aString
[i
];
3083 if ( c
< '0' || c
> '9' )
3089 static void lcl_SelectionToEnd( EditView
* pView
)
3093 EditEngine
* pEngine
= pView
->GetEditEngine();
3094 sal_Int32 nParCnt
= pEngine
->GetParagraphCount();
3097 ESelection
aSel( nParCnt
-1, pEngine
->GetTextLen(nParCnt
-1) ); // empty selection, cursor at the end
3098 pView
->SetSelection( aSel
);
3102 void ScInputHandler::EnterHandler( ScEnterMode nBlockMode
, bool bBeforeSavingInLOK
)
3104 if (!mbDocumentDisposing
&& comphelper::LibreOfficeKit::isActive()
3105 && pActiveViewSh
!= SfxViewShell::Current())
3108 // Macro calls for validity can cause a lot of problems, so inhibit
3109 // nested calls of EnterHandler().
3110 if (bInEnterHandler
) return;
3111 bInEnterHandler
= true;
3112 bInOwnChange
= true; // disable ModifyHdl (reset below)
3113 mbPartialPrefix
= false;
3115 ImplCreateEditEngine();
3117 bool bMatrix
= ( nBlockMode
== ScEnterMode::MATRIX
);
3119 SfxApplication
* pSfxApp
= SfxGetpApp();
3120 std::unique_ptr
<EditTextObject
> pObject
;
3121 std::unique_ptr
<ScPatternAttr
> pCellAttrs
;
3122 bool bForget
= false; // Remove due to validity?
3124 OUString aString
= GetEditText(mpEditEngine
.get());
3125 OUString
aPreAutoCorrectString(aString
);
3126 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
3127 if (bModified
&& pActiveView
&& !aString
.isEmpty() && !lcl_IsNumber(aString
))
3129 if (pColumnData
&& miAutoPosColumn
!= pColumnData
->end())
3131 // #i47125# If AutoInput appended something, do the final AutoCorrect
3132 // with the cursor at the end of the input.
3133 lcl_SelectionToEnd(pTopView
);
3134 lcl_SelectionToEnd(pTableView
);
3137 vcl::Window
* pFrameWin
= pActiveViewSh
? pActiveViewSh
->GetFrameWin() : nullptr;
3140 pTopView
->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views
3142 pTableView
->CompleteAutoCorrect(pFrameWin
);
3143 aString
= GetEditText(mpEditEngine
.get());
3145 lcl_RemoveTabs(aString
);
3146 lcl_RemoveTabs(aPreAutoCorrectString
);
3148 if (aString
.indexOf('\n') != -1)
3150 // Cell contains line breaks, enable wrapping
3151 ScLineBreakCell
aBreakItem(true);
3152 pActiveViewSh
->ApplyAttr(aBreakItem
);
3154 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
3156 pViewFrm
->GetBindings().Invalidate(SID_ATTR_ALIGN_LINEBREAK
);
3159 // Test if valid (always with simple string)
3160 if (bModified
&& nValidation
&& pActiveViewSh
)
3162 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3163 const ScValidationData
* pData
= rDoc
.GetValidationEntry( nValidation
);
3164 if (pData
&& pData
->HasErrMsg())
3166 // #i67990# don't use pLastPattern in EnterHandler
3167 const ScPatternAttr
* pPattern
= rDoc
.GetPattern( aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab() );
3171 if (pData
->GetDataMode() == SC_VALID_CUSTOM
)
3173 bOk
= pData
->IsDataValidCustom( aString
, *pPattern
, aCursorPos
, ScValidationData::CustomValidationPrivateAccess() );
3177 bOk
= pData
->IsDataValid( aString
, *pPattern
, aCursorPos
);
3182 pActiveViewSh
->StopMarking(); // (the InfoBox consumes the MouseButtonUp)
3184 // tdf#125917 Release the grab that a current mouse-down event being handled
3185 // by ScTabView has put on the mouse via its SelectionEngine.
3186 // Otherwise the warning box cannot interact with the mouse
3187 if (ScTabView
* pView
= pActiveViewSh
->GetViewData().GetView())
3189 if (ScViewSelectionEngine
* pSelEngine
= pView
->GetSelEngine())
3190 pSelEngine
->ReleaseMouse();
3193 if (bBeforeSavingInLOK
)
3195 // Invalid entry but not applied to the document model.
3196 // Exit to complete the "save", leaving the edit view as it is
3197 // for the user to continue after save.
3198 bInOwnChange
= false;
3199 bInEnterHandler
= false;
3203 if (pData
->DoError(pActiveViewSh
->GetFrameWeld(), aString
, aCursorPos
))
3204 bForget
= true; // Do not take over input
3209 // Check for input into DataPilot table
3210 if ( bModified
&& pActiveViewSh
&& !bForget
)
3212 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3213 ScDPObject
* pDPObj
= rDoc
.GetDPAtCursor( aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab() );
3216 // Any input within the DataPilot table is either a valid renaming
3217 // or an invalid action - normal cell input is always aborted
3218 pActiveViewSh
->DataPilotInput( aCursorPos
, aString
);
3223 std::vector
<editeng::MisspellRanges
> aMisspellRanges
;
3224 // UpdateLayout must be true during CompleteOnlineSpelling
3225 const bool bUpdateLayout
= mpEditEngine
->SetUpdateLayout( true );
3226 mpEditEngine
->CompleteOnlineSpelling();
3227 bool bSpellErrors
= !bFormulaMode
&& mpEditEngine
->HasOnlineSpellErrors();
3230 // #i3820# If the spell checker flags numerical input as error,
3231 // it still has to be treated as number, not EditEngine object.
3232 if ( pActiveViewSh
)
3234 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3235 // #i67990# don't use pLastPattern in EnterHandler
3236 const ScPatternAttr
* pPattern
= rDoc
.GetPattern( aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab() );
3239 SvNumberFormatter
* pFormatter
= rDoc
.GetFormatTable();
3240 // without conditional format, as in ScColumn::SetString
3241 sal_uInt32 nFormat
= pPattern
->GetNumberFormat( pFormatter
);
3243 if ( pFormatter
->IsNumberFormat( aString
, nFormat
, nVal
) )
3245 bSpellErrors
= false; // ignore the spelling errors
3251 // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc).
3252 // SetUpdateLayout must come after CompleteOnlineSpelling.
3253 // The view is hidden in any case below (Broadcast).
3254 mpEditEngine
->SetUpdateLayout( false );
3256 if ( bModified
&& !bForget
) // What is being entered (text/object)?
3258 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
3262 bool bUniformAttribs
= true;
3263 SfxItemSet aPara1Attribs
= mpEditEngine
->GetAttribs(0, 0, mpEditEngine
->GetTextLen(0));
3264 for (sal_Int32 nPara
= 1; nPara
< nParCnt
; ++nPara
)
3266 SfxItemSet aPara2Attribs
= mpEditEngine
->GetAttribs(nPara
, 0, mpEditEngine
->GetTextLen(nPara
));
3267 if (!(aPara1Attribs
== aPara2Attribs
))
3269 // Paragraph format different from that of the 1st paragraph.
3270 bUniformAttribs
= false;
3275 ESelection
aSel( 0, 0, nParCnt
-1, mpEditEngine
->GetTextLen(nParCnt
-1) );
3276 SfxItemSet aOldAttribs
= mpEditEngine
->GetAttribs( aSel
);
3277 const SfxPoolItem
* pItem
= nullptr;
3279 // Find common (cell) attributes before RemoveAdjust
3280 if ( pActiveViewSh
&& bUniformAttribs
)
3282 std::optional
<SfxItemSet
> pCommonAttrs
;
3283 for (sal_uInt16 nId
= EE_CHAR_START
; nId
<= EE_CHAR_END
; nId
++)
3285 SfxItemState eState
= aOldAttribs
.GetItemState( nId
, false, &pItem
);
3286 if ( eState
== SfxItemState::SET
&&
3287 nId
!= EE_CHAR_ESCAPEMENT
&& nId
!= EE_CHAR_PAIRKERNING
&&
3288 nId
!= EE_CHAR_KERNING
&& nId
!= EE_CHAR_XMLATTRIBS
&&
3289 *pItem
!= pEditDefaults
->Get(nId
) )
3291 if ( !pCommonAttrs
)
3292 pCommonAttrs
.emplace( mpEditEngine
->GetEmptyItemSet() );
3293 pCommonAttrs
->Put( *pItem
);
3299 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3300 pCellAttrs
= std::make_unique
<ScPatternAttr
>(rDoc
.GetPool());
3301 pCellAttrs
->GetFromEditItemSet( &*pCommonAttrs
);
3305 // Clear ParaAttribs (including adjustment)
3308 bool bAttrib
= false; // Formatting present?
3310 // check if EditObject is needed
3315 for (sal_uInt16 nId
= EE_CHAR_START
; nId
<= EE_CHAR_END
&& !bAttrib
; nId
++)
3317 SfxItemState eState
= aOldAttribs
.GetItemState( nId
, false, &pItem
);
3318 if (eState
== SfxItemState::DONTCARE
)
3320 else if (eState
== SfxItemState::SET
)
3322 // Keep same items in EditEngine as in ScEditAttrTester
3323 if ( nId
== EE_CHAR_ESCAPEMENT
|| nId
== EE_CHAR_PAIRKERNING
||
3324 nId
== EE_CHAR_KERNING
|| nId
== EE_CHAR_XMLATTRIBS
)
3326 if ( *pItem
!= pEditDefaults
->Get(nId
) )
3333 SfxItemState eFieldState
= aOldAttribs
.GetItemState( EE_FEATURE_FIELD
, false );
3334 if ( eFieldState
== SfxItemState::DONTCARE
|| eFieldState
== SfxItemState::SET
)
3337 // Not converted characters?
3338 SfxItemState eConvState
= aOldAttribs
.GetItemState( EE_FEATURE_NOTCONV
, false );
3339 if ( eConvState
== SfxItemState::DONTCARE
|| eConvState
== SfxItemState::SET
)
3342 // Always recognize formulas as formulas
3343 // We still need the preceding test due to cell attributes
3347 mpEditEngine
->GetAllMisspellRanges(aMisspellRanges
);
3354 mpEditEngine
->ClearSpellErrors();
3355 pObject
= mpEditEngine
->CreateTextObject();
3357 else if (SC_MOD()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case
3359 // Perform case-matching only when the typed text is partial.
3360 if (pColumnData
&& aAutoSearch
.getLength() < aString
.getLength())
3361 aString
= getExactMatch(*pColumnData
, aString
);
3365 // Don't rely on ShowRefFrame switching the active view synchronously
3366 // execute the function directly on the correct view's bindings instead
3367 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3368 ScTabViewShell
* pExecuteSh
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
3376 pExecuteSh
->SetTabNo(aCursorPos
.Tab());
3377 pExecuteSh
->ActiveGrabFocus();
3380 bFormulaMode
= false;
3381 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
3382 SC_MOD()->SetRefInputHdl(nullptr);
3384 pInputWin
->SetFormulaMode(false);
3385 UpdateAutoCorrFlag();
3387 pRefViewSh
= nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3388 DeleteRangeFinder();
3391 bool bOldMod
= bModified
;
3395 eMode
= SC_INPUT_NONE
;
3396 StopInputWinEngine(true);
3398 // Text input (through number formats) or ApplySelectionPattern modify
3399 // the cell's attributes, so pLastPattern is no longer valid
3400 pLastPattern
= nullptr;
3402 if (bOldMod
&& !bProtected
&& !bForget
)
3404 bool bInsertPreCorrectedString
= true;
3405 // No typographic quotes in formulas
3406 if (aString
.startsWith("="))
3408 SvxAutoCorrect
* pAuto
= SvxAutoCorrCfg::Get().GetAutoCorrect();
3411 bInsertPreCorrectedString
= false;
3412 OUString
aReplace(pAuto
->GetStartDoubleQuote());
3413 if( aReplace
.isEmpty() )
3414 aReplace
= ScGlobal::getLocaleData().getDoubleQuotationMarkStart();
3415 if( aReplace
!= "\"" )
3416 aString
= aString
.replaceAll( aReplace
, "\"" );
3418 aReplace
= OUString(pAuto
->GetEndDoubleQuote());
3419 if( aReplace
.isEmpty() )
3420 aReplace
= ScGlobal::getLocaleData().getDoubleQuotationMarkEnd();
3421 if( aReplace
!= "\"" )
3422 aString
= aString
.replaceAll( aReplace
, "\"" );
3424 aReplace
= OUString(pAuto
->GetStartSingleQuote());
3425 if( aReplace
.isEmpty() )
3426 aReplace
= ScGlobal::getLocaleData().getQuotationMarkStart();
3427 if( aReplace
!= "'" )
3428 aString
= aString
.replaceAll( aReplace
, "'" );
3430 aReplace
= OUString(pAuto
->GetEndSingleQuote());
3431 if( aReplace
.isEmpty() )
3432 aReplace
= ScGlobal::getLocaleData().getQuotationMarkEnd();
3433 if( aReplace
!= "'" )
3434 aString
= aString
.replaceAll( aReplace
, "'");
3438 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint
) );
3442 SfxBindings
& rBindings
= pExecuteSh
->GetViewFrame()->GetBindings();
3444 sal_uInt16 nId
= FID_INPUTLINE_ENTER
;
3445 if ( nBlockMode
== ScEnterMode::BLOCK
)
3446 nId
= FID_INPUTLINE_BLOCK
;
3447 else if ( nBlockMode
== ScEnterMode::MATRIX
)
3448 nId
= FID_INPUTLINE_MATRIX
;
3450 const SfxPoolItem
* aArgs
[2];
3453 if ( bInsertPreCorrectedString
&& aString
!= aPreAutoCorrectString
)
3455 ScInputStatusItem
aItem(FID_INPUTLINE_STATUS
,
3456 aCursorPos
, aCursorPos
, aCursorPos
,
3457 aPreAutoCorrectString
, pObject
.get());
3459 rBindings
.Execute(nId
, aArgs
);
3462 ScInputStatusItem
aItemCorrected(FID_INPUTLINE_STATUS
,
3463 aCursorPos
, aCursorPos
, aCursorPos
,
3464 aString
, pObject
.get());
3465 if ( !aMisspellRanges
.empty() )
3466 aItemCorrected
.SetMisspellRanges(&aMisspellRanges
);
3468 aArgs
[0] = &aItemCorrected
;
3469 rBindings
.Execute(nId
, aArgs
);
3472 pLastState
.reset(); // pLastState still contains the old text
3475 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScKillEditView
) );
3477 if ( bOldMod
&& pExecuteSh
&& pCellAttrs
&& !bForget
)
3479 // Combine with input?
3480 pExecuteSh
->ApplySelectionPattern( *pCellAttrs
, true );
3481 pExecuteSh
->AdjustBlockHeight();
3487 nFormSelStart
= nFormSelEnd
= 0;
3490 mbEditingExistingContent
= false;
3491 bInOwnChange
= false;
3492 bInEnterHandler
= false;
3494 mpEditEngine
->SetUpdateLayout( true );
3497 void ScInputHandler::CancelHandler()
3499 bInOwnChange
= true; // Also without FormulaMode due to FunctionsAutoPilot
3501 ImplCreateEditEngine();
3504 mbPartialPrefix
= false;
3505 mbEditingExistingContent
= false;
3507 // Don't rely on ShowRefFrame switching the active view synchronously
3508 // execute the function directly on the correct view's bindings instead
3509 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3510 ScTabViewShell
* pExecuteSh
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
3517 pExecuteSh
->SetTabNo(aCursorPos
.Tab());
3518 pExecuteSh
->ActiveGrabFocus();
3520 bFormulaMode
= false;
3521 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
3522 SC_MOD()->SetRefInputHdl(nullptr);
3524 pInputWin
->SetFormulaMode(false);
3525 UpdateAutoCorrFlag();
3527 pRefViewSh
= nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3528 DeleteRangeFinder();
3531 eMode
= SC_INPUT_NONE
;
3532 StopInputWinEngine( true );
3533 SCCOL
nMaxCol(MAXCOL
);
3536 pExecuteSh
->StopEditShell();
3537 nMaxCol
= pExecuteSh
->GetViewData().GetDocument().MaxCol();
3540 aCursorPos
.Set(nMaxCol
+1,0,0); // Invalid flag
3541 mpEditEngine
->SetTextCurrentDefaults(OUString());
3543 if ( !pLastState
&& pExecuteSh
)
3544 pExecuteSh
->UpdateInputHandler( true ); // Update status again
3546 NotifyChange( pLastState
.get(), true );
3548 nFormSelStart
= nFormSelEnd
= 0;
3551 bInOwnChange
= false;
3553 if ( comphelper::LibreOfficeKit::isActive() && pExecuteSh
)
3556 std::vector
<ReferenceMark
> aReferenceMarks
;
3557 ScInputHandler::SendReferenceMarks( pActiveViewSh
, aReferenceMarks
);
3561 bool ScInputHandler::IsModalMode( const SfxObjectShell
* pDocSh
)
3563 // References to unnamed document; that doesn't work
3564 return bFormulaMode
&& pRefViewSh
3565 && pRefViewSh
->GetViewData().GetDocument().GetDocumentShell() != pDocSh
3566 && !pDocSh
->HasName();
3569 void ScInputHandler::AddRefEntry()
3571 const sal_Unicode cSep
= ScCompiler::GetNativeSymbolChar(ocSep
);
3573 if (!pTableView
&& !pTopView
)
3574 return; // E.g. FillMode
3576 DataChanging(); // Cannot be new
3579 OUString aText
= GetEditText(mpEditEngine
.get());
3580 sal_Unicode cLastChar
= 0;
3581 sal_Int32 nPos
= aText
.getLength() - 1;
3582 while (nPos
>= 0) //checking space
3584 cLastChar
= aText
[nPos
];
3585 if (cLastChar
!= ' ')
3590 bool bAppendSeparator
= (cLastChar
!= '(' && cLastChar
!= cSep
&& cLastChar
!= '=');
3591 if (bAppendSeparator
)
3594 pTableView
->InsertText( OUString(cSep
) );
3596 pTopView
->InsertText( OUString(cSep
) );
3602 void ScInputHandler::SetReference( const ScRange
& rRef
, const ScDocument
& rDoc
)
3606 const ScDocument
* pThisDoc
= nullptr;
3608 pThisDoc
= &pRefViewSh
->GetViewData().GetDocument();
3609 bool bOtherDoc
= (pThisDoc
!= &rDoc
);
3610 if (bOtherDoc
&& !rDoc
.GetDocumentShell()->HasName())
3612 // References to unnamed document; that doesn't work
3613 // SetReference should not be called, then
3620 if (!pTableView
&& !pTopView
)
3621 return; // E.g. FillMode
3623 // Never overwrite the "="!
3624 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
3625 ESelection aSel
= pActiveView
->GetSelection();
3627 if ( aSel
.nStartPara
== 0 && aSel
.nStartPos
== 0 )
3630 DataChanging(); // Cannot be new
3632 // Turn around selection if backwards.
3635 ESelection aTabSel
= pTableView
->GetSelection();
3636 if (aTabSel
.nStartPos
> aTabSel
.nEndPos
&& aTabSel
.nStartPara
== aTabSel
.nEndPara
)
3639 pTableView
->SetSelection(aTabSel
);
3644 ESelection aTopSel
= pTopView
->GetSelection();
3645 if (aTopSel
.nStartPos
> aTopSel
.nEndPos
&& aTopSel
.nStartPara
== aTopSel
.nEndPara
)
3648 pTopView
->SetSelection(aTopSel
);
3652 // Create string from reference, in the syntax of the document being edited.
3654 const ScAddress::Details
aAddrDetails( *pThisDoc
, aCursorPos
);
3657 // Reference to other document
3658 OSL_ENSURE(rRef
.aStart
.Tab()==rRef
.aEnd
.Tab(), "nStartTab!=nEndTab");
3660 // Always 3D and absolute.
3661 OUString
aTmp(rRef
.Format(rDoc
, ScRefFlags::VALID
| ScRefFlags::TAB_ABS_3D
, aAddrDetails
));
3663 SfxObjectShell
* pObjSh
= rDoc
.GetDocumentShell();
3664 // #i75893# convert escaped URL of the document to something user friendly
3665 OUString aFileName
= pObjSh
->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous
);
3667 switch(aAddrDetails
.eConv
)
3669 case formula::FormulaGrammar::CONV_XL_A1
:
3670 case formula::FormulaGrammar::CONV_XL_OOX
:
3671 case formula::FormulaGrammar::CONV_XL_R1C1
:
3672 aRefStr
= "[\'" + aFileName
+ "']";
3674 case formula::FormulaGrammar::CONV_OOO
:
3676 aRefStr
= "\'" + aFileName
+ "'#";
3683 if ( rRef
.aStart
.Tab() != aCursorPos
.Tab() ||
3684 rRef
.aStart
.Tab() != rRef
.aEnd
.Tab() )
3685 // pointer-selected => absolute sheet reference
3686 aRefStr
= rRef
.Format(rDoc
, ScRefFlags::VALID
| ScRefFlags::TAB_ABS_3D
, aAddrDetails
);
3688 aRefStr
= rRef
.Format(rDoc
, ScRefFlags::VALID
, aAddrDetails
);
3690 bool bLOKShowSelect
= true;
3691 if(comphelper::LibreOfficeKit::isActive() && pRefViewSh
->GetViewData().GetRefTabNo() != pRefViewSh
->GetViewData().GetTabNo())
3692 bLOKShowSelect
= false;
3694 if (pTableView
|| pTopView
)
3697 pTableView
->InsertText( aRefStr
, true, bLOKShowSelect
);
3699 pTopView
->InsertText( aRefStr
, true, bLOKShowSelect
);
3707 void ScInputHandler::InsertFunction( const OUString
& rFuncName
, bool bAddPar
)
3709 if ( eMode
== SC_INPUT_NONE
)
3711 OSL_FAIL("InsertFunction, not during input mode");
3716 if (!pTableView
&& !pTopView
)
3717 return; // E.g. FillMode
3719 DataChanging(); // Cannot be new
3721 OUString aText
= rFuncName
;
3727 pTableView
->InsertText( aText
);
3730 ESelection aSel
= pTableView
->GetSelection();
3733 pTableView
->SetSelection(aSel
);
3738 pTopView
->InsertText( aText
);
3741 ESelection aSel
= pTopView
->GetSelection();
3744 pTopView
->SetSelection(aSel
);
3754 void ScInputHandler::ClearText()
3756 if ( eMode
== SC_INPUT_NONE
)
3758 OSL_FAIL("ClearText, not during input mode");
3763 if (!pTableView
&& !pTopView
)
3764 return; // E.g. FillMode
3766 DataChanging(); // Cannot be new
3770 pTableView
->GetEditEngine()->SetText( "" );
3771 pTableView
->SetSelection( ESelection(0,0, 0,0) );
3775 pTopView
->GetEditEngine()->SetText( "" );
3776 pTopView
->SetSelection( ESelection(0,0, 0,0) );
3782 bool ScInputHandler::KeyInput( const KeyEvent
& rKEvt
, bool bStartEdit
/* = false */ )
3784 vcl::KeyCode aCode
= rKEvt
.GetKeyCode();
3785 sal_uInt16 nModi
= aCode
.GetModifier();
3786 bool bShift
= aCode
.IsShift();
3787 bool bControl
= aCode
.IsMod1();
3788 bool bAlt
= aCode
.IsMod2();
3789 sal_uInt16 nCode
= aCode
.GetCode();
3790 sal_Unicode nChar
= rKEvt
.GetCharCode();
3792 if (bAlt
&& !bControl
&& nCode
!= KEY_RETURN
)
3793 // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not.
3796 // There is a partial autocomplete suggestion.
3797 // Allow its completion with right arrow key (without modifiers).
3798 if (mbPartialPrefix
&& nCode
== KEY_RIGHT
&& !bControl
&& !bShift
&& !bAlt
&&
3799 (pTopView
|| pTableView
))
3802 pTopView
->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
));
3804 pTableView
->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
));
3806 mbPartialPrefix
= false;
3808 // Indicate that this event has been consumed and ScTabViewShell should not act on this.
3812 if (!bControl
&& nCode
== KEY_TAB
)
3814 // Normal TAB moves the cursor right.
3818 pActiveViewSh
->FindNextUnprot( bShift
, true );
3822 bool bInputLine
= ( eMode
==SC_INPUT_TOP
);
3826 bool bDoEnter
= false;
3831 // New line when in the input line and Shift/Ctrl-Enter is pressed,
3832 // or when in a cell and Ctrl-Enter is pressed.
3833 if ((pInputWin
&& bInputLine
&& bControl
!= bShift
) || (!bInputLine
&& bControl
&& !bShift
))
3837 else if (nModi
== 0 && nTipVisible
&& pFormulaData
&& miAutoPosFormula
!= pFormulaData
->end())
3839 PasteFunctionData();
3842 else if ( nModi
== 0 && nTipVisible
&& !aManualTip
.isEmpty() )
3849 ScEnterMode nMode
= ScEnterMode::NORMAL
;
3850 if ( bShift
&& bControl
)
3851 nMode
= ScEnterMode::MATRIX
;
3853 nMode
= ScEnterMode::BLOCK
;
3854 EnterHandler( nMode
);
3857 pActiveViewSh
->MoveCursorEnter( bShift
&& !bControl
);
3863 if (bControl
&& !bAlt
)
3865 if (pFormulaData
&& nTipVisible
&& miAutoPosFormula
!= pFormulaData
->end())
3868 NextFormulaEntry( bShift
);
3871 else if (pColumnData
&& bUseTab
)
3873 // Iterate through AutoInput entries
3874 NextAutoEntry( bShift
);
3885 else if( nTipVisibleSec
)
3890 else if (eMode
!= SC_INPUT_NONE
)
3899 if ( !bShift
&& !bControl
&& !bAlt
&& eMode
== SC_INPUT_TABLE
)
3901 eMode
= SC_INPUT_TYPE
;
3907 // Only execute cursor keys if already in EditMode
3908 // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator)
3909 bool bCursorKey
= EditEngine::DoesKeyMoveCursor(rKEvt
);
3910 bool bInsKey
= ( nCode
== KEY_INSERT
&& !nModi
); // Treat Insert like Cursorkeys
3911 if ( !bUsed
&& !bSkip
&& ( bDoEnter
|| EditEngine::DoesKeyChangeText(rKEvt
) ||
3912 ( eMode
!= SC_INPUT_NONE
&& ( bCursorKey
|| bInsKey
) ) ) )
3924 bool bNewView
= DataChanging( nChar
);
3926 if (bProtected
) // Protected cell?
3927 bUsed
= true; // Don't forward KeyEvent
3928 else // Changes allowed
3930 if (bNewView
) // Create anew
3933 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
3935 if (eMode
==SC_INPUT_NONE
)
3936 if (pTableView
|| pTopView
)
3940 if (bStartEdit
&& nCellPercentFormatDecSep
!= 0 &&
3941 ((nChar
>= '0' && nChar
<= '9') || nChar
== '-' || nChar
== nCellPercentFormatDecSep
))
3948 pTableView
->GetEditEngine()->SetText( aStrLoP
);
3949 if ( !aStrLoP
.isEmpty() )
3950 pTableView
->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
3952 // Don't call SetSelection if the string is empty anyway,
3953 // to avoid breaking the bInitial handling in ScViewData::EditGrowY
3957 pTopView
->GetEditEngine()->SetText( aStrLoP
);
3958 if ( !aStrLoP
.isEmpty() )
3959 pTopView
->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
3965 if (pTableView
|| pTopView
)
3970 if( pTableView
->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN
) ) ) )
3973 if( pTopView
->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN
) ) ) )
3976 else if ( nAutoPar
&& nChar
== ')' && CursorAtClosingPar() )
3986 pTableView
->SetControlWord(pTableView
->GetControlWord() | EVControlBits::SINGLELINEPASTE
);
3988 vcl::Window
* pFrameWin
= pActiveViewSh
? pActiveViewSh
->GetFrameWin() : nullptr;
3989 if ( pTableView
->PostKeyEvent( rKEvt
, pFrameWin
) )
3992 pTableView
->SetControlWord(pTableView
->GetControlWord() & ~EVControlBits::SINGLELINEPASTE
);
3996 if ( bUsed
&& rKEvt
.GetKeyCode().GetFunction() == KeyFuncType::CUT
)
3997 pTopView
->DeleteSelected();
3998 else if ( pTopView
->PostKeyEvent( rKEvt
) )
4004 if ( bUsed
&& SC_MOD()->GetAppOptions().GetAutoComplete() )
4008 miAutoPosFormula
= pFormulaData
->end(); // do not search further
4010 miAutoPosColumn
= pColumnData
->end();
4012 KeyFuncType eFunc
= rKEvt
.GetKeyCode().GetFunction();
4013 if ( nChar
&& nChar
!= 8 && nChar
!= 127 && // no 'backspace', no 'delete'
4014 KeyFuncType::CUT
!= eFunc
) // and no 'CTRL-X'
4023 // When the selection is changed manually or an opening parenthesis
4024 // is typed, stop overwriting parentheses
4025 if ( bUsed
&& nChar
== '(' )
4028 if ( KEY_INSERT
== nCode
)
4030 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
4032 pViewFrm
->GetBindings().Invalidate( SID_ATTR_INSERT
);
4034 if( bUsed
&& bFormulaMode
&& ( bCursorKey
|| bInsKey
|| nCode
== KEY_DELETE
|| nCode
== KEY_BACKSPACE
) )
4038 if( bUsed
&& bFormulaMode
&& nCode
== KEY_BACKSPACE
)
4045 // #i114511# don't count cursor keys as modification
4046 bool bSetModified
= !bCursorKey
;
4047 DataChanged(false, bSetModified
); // also calls UpdateParenthesis()
4049 // In the LOK case, we want to set the document modified state
4050 // right away at the start of the edit, so that the content is
4051 // saved even when the user leaves the document before hitting
4053 if (comphelper::LibreOfficeKit::isActive() && bSetModified
&& pActiveViewSh
&& !pActiveViewSh
->GetViewData().GetDocShell()->IsModified())
4054 pActiveViewSh
->GetViewData().GetDocShell()->SetModified();
4056 InvalidateAttribs(); //! in DataChanged?
4060 if (pTopView
&& eMode
!= SC_INPUT_NONE
)
4066 OUString
ScInputHandler::GetSurroundingText()
4068 if (eMode
!= SC_INPUT_NONE
)
4071 if (pTableView
|| pTopView
)
4074 return pTableView
->GetSurroundingText();
4075 else if (pTopView
) // call only once
4076 return pTopView
->GetSurroundingText();
4082 Selection
ScInputHandler::GetSurroundingTextSelection()
4084 if (eMode
!= SC_INPUT_NONE
)
4087 if (pTableView
|| pTopView
)
4090 return pTableView
->GetSurroundingTextSelection();
4091 else if (pTopView
) // call only once
4092 return pTopView
->GetSurroundingTextSelection();
4095 return Selection(0, 0);
4098 bool ScInputHandler::DeleteSurroundingText(const Selection
& rSelection
)
4100 if (eMode
!= SC_INPUT_NONE
)
4103 if (pTableView
|| pTopView
)
4106 return pTableView
->DeleteSurroundingText(rSelection
);
4107 else if (pTopView
) // call only once
4108 return pTopView
->DeleteSurroundingText(rSelection
);
4114 void ScInputHandler::InputCommand( const CommandEvent
& rCEvt
)
4116 if ( rCEvt
.GetCommand() == CommandEventId::CursorPos
)
4118 // For CommandEventId::CursorPos, do as little as possible, because
4119 // with remote VCL, even a ShowCursor will generate another event.
4120 if ( eMode
!= SC_INPUT_NONE
)
4123 if (pTableView
|| pTopView
)
4126 pTableView
->Command( rCEvt
);
4127 else if (pTopView
) // call only once
4128 pTopView
->Command( rCEvt
);
4132 else if ( rCEvt
.GetCommand() == CommandEventId::QueryCharPosition
)
4134 if ( eMode
!= SC_INPUT_NONE
)
4137 if (pTableView
|| pTopView
)
4140 pTableView
->Command( rCEvt
);
4141 else if (pTopView
) // call only once
4142 pTopView
->Command( rCEvt
);
4158 bool bNewView
= DataChanging( 0, true );
4160 if (!bProtected
) // changes allowed
4162 if (bNewView
) // create new edit view
4165 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
4167 if (eMode
==SC_INPUT_NONE
)
4168 if (pTableView
|| pTopView
)
4172 pTableView
->GetEditEngine()->SetText( "" );
4173 pTableView
->SetSelection( ESelection(0,0, 0,0) );
4177 pTopView
->GetEditEngine()->SetText( "" );
4178 pTopView
->SetSelection( ESelection(0,0, 0,0) );
4184 if (pTableView
|| pTopView
)
4187 pTableView
->Command( rCEvt
);
4189 pTopView
->Command( rCEvt
);
4191 if ( rCEvt
.GetCommand() == CommandEventId::EndExtTextInput
)
4193 // AutoInput after ext text input
4196 miAutoPosFormula
= pFormulaData
->end();
4198 miAutoPosColumn
= pColumnData
->end();
4207 DataChanged(); // calls UpdateParenthesis()
4208 InvalidateAttribs(); //! in DataChanged ?
4211 if (pTopView
&& eMode
!= SC_INPUT_NONE
)
4216 void ScInputHandler::NotifyChange( const ScInputHdlState
* pState
,
4217 bool bForce
, ScTabViewShell
* pSourceSh
,
4220 // If the call originates from a macro call in the EnterHandler,
4221 // return immediately and don't mess up the status
4222 if (bInEnterHandler
)
4225 bool bRepeat
= (pState
== pLastState
.get());
4226 if (!bRepeat
&& pState
&& pLastState
)
4227 bRepeat
= (*pState
== *pLastState
);
4228 if (bRepeat
&& !bForce
)
4231 bInOwnChange
= true; // disable ModifyHdl (reset below)
4233 if ( pState
&& !pLastState
) // Enable again
4236 bool bHadObject
= pLastState
&& pLastState
->GetEditData();
4238 //! Before EditEngine gets eventually created (so it gets the right pools)
4240 pActiveViewSh
= pSourceSh
;
4242 pActiveViewSh
= dynamic_cast<ScTabViewShell
*>( SfxViewShell::Current() );
4244 ImplCreateEditEngine();
4246 if ( pState
!= pLastState
.get() )
4248 pLastState
.reset( pState
? new ScInputHdlState( *pState
) : nullptr);
4251 if ( pState
&& pActiveViewSh
)
4253 ScModule
* pScMod
= SC_MOD();
4255 ScTabViewShell
* pScTabViewShell
= dynamic_cast<ScTabViewShell
*>(pScMod
->GetViewShell());
4257 // Also take foreign reference input into account here (e.g. FunctionsAutoPilot),
4258 // FormEditData, if we're switching from Help to Calc:
4259 if ( !bFormulaMode
&& !pScMod
->IsFormulaMode() &&
4260 ( !pScTabViewShell
|| !pScTabViewShell
->GetFormEditData() ) )
4262 bool bIgnore
= false;
4265 if (pState
->GetPos() != aCursorPos
)
4276 const ScAddress
& rSPos
= pState
->GetStartPos();
4277 const ScAddress
& rEPos
= pState
->GetEndPos();
4278 const EditTextObject
* pData
= pState
->GetEditData();
4279 OUString aString
= pState
->GetString();
4280 bool bTxtMod
= false;
4281 ScDocShell
* pDocSh
= pActiveViewSh
->GetViewData().GetDocShell();
4282 ScDocument
& rDoc
= pDocSh
->GetDocument();
4284 aCursorPos
= pState
->GetPos();
4288 else if ( bHadObject
)
4290 else if ( bTextValid
)
4291 bTxtMod
= ( aString
!= aCurrentText
);
4293 bTxtMod
= ( aString
!= GetEditText(mpEditEngine
.get()) );
4295 if ( bTxtMod
|| bForce
)
4299 mpEditEngine
->SetTextCurrentDefaults( *pData
);
4301 aString
= ScEditUtil::GetMultilineString(*mpEditEngine
);
4303 aString
= GetEditText(mpEditEngine
.get());
4304 lcl_RemoveTabs(aString
);
4306 aCurrentText
.clear();
4310 aCurrentText
= aString
;
4311 bTextValid
= true; //! To begin with remember as a string
4314 const bool bUpdateKit
= comphelper::LibreOfficeKit::isActive() && pActiveViewSh
;
4318 // If we will end up updating LoKit after this, we can skip it here
4319 pInputWin
->SetTextString(aString
, !bUpdateKit
);
4325 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
4326 ESelection aSel
= pActiveView
? pActiveView
->GetSelection() : ESelection();
4328 // if we switched content completely - don't send huge numbers
4329 if (aSel
.nStartPara
== EE_PARA_NOT_FOUND
)
4330 aSel
.nStartPara
= 0;
4332 if (aSel
.nEndPara
== EE_PARA_NOT_FOUND
)
4335 pActiveViewSh
->LOKSendFormulabarUpdate(pActiveView
, aString
, aSel
);
4336 // TODO: deprecated?
4337 pActiveViewSh
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA
, aString
.toUtf8().getStr());
4341 if ( pInputWin
|| comphelper::LibreOfficeKit::isActive()) // Named range input
4344 bool bSheetLocal
= false;
4345 const ScAddress::Details
aAddrDetails( rDoc
, aCursorPos
);
4347 // Is the range a name?
4349 if ( pActiveViewSh
)
4350 pActiveViewSh
->GetViewData().GetDocument().
4351 GetRangeAtBlock( ScRange( rSPos
, rEPos
), aPosStr
, &bSheetLocal
);
4353 if ( aPosStr
.isEmpty() ) // Not a name -> format
4355 ScRefFlags nFlags
= ScRefFlags::ZERO
;
4356 if( aAddrDetails
.eConv
== formula::FormulaGrammar::CONV_XL_R1C1
)
4357 nFlags
|= ScRefFlags::COL_ABS
| ScRefFlags::ROW_ABS
;
4358 if ( rSPos
!= rEPos
)
4360 ScRange
r(rSPos
, rEPos
);
4361 applyStartToEndFlags(nFlags
);
4362 aPosStr
= r
.Format(rDoc
, ScRefFlags::VALID
| nFlags
, aAddrDetails
);
4365 aPosStr
= aCursorPos
.Format(ScRefFlags::VALID
| nFlags
, &rDoc
, aAddrDetails
);
4367 else if (bSheetLocal
)
4370 if (rDoc
.GetName( rSPos
.Tab(), aName
))
4371 aPosStr
= ScPosWnd::createLocalRangeName( aPosStr
, aName
);
4376 // Disable the accessible VALUE_CHANGE event
4377 bool bIsSuppressed
= pInputWin
->IsAccessibilityEventsSuppressed(false);
4378 pInputWin
->SetAccessibilityEventsSuppressed(true);
4379 pInputWin
->SetPosString(aPosStr
);
4380 pInputWin
->SetAccessibilityEventsSuppressed(bIsSuppressed
);
4381 pInputWin
->SetSumAssignMode();
4384 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh
)
4385 pActiveViewSh
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS
, aPosStr
.toUtf8().getStr());
4389 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView
) );
4391 // As long as the content is not edited, turn off online spelling.
4392 // Online spelling is turned back on in StartTable, after setting
4393 // the right language from cell attributes.
4395 EEControlBits nCntrl
= mpEditEngine
->GetControlWord();
4396 if ( nCntrl
& EEControlBits::ONLINESPELLING
)
4397 mpEditEngine
->SetControlWord( nCntrl
& ~EEControlBits::ONLINESPELLING
);
4403 bCommandErrorShown
= false;
4409 // Do not enable if RefDialog is open
4410 if(!pScMod
->IsFormulaMode()&& !pScMod
->IsRefDialogOpen())
4412 if ( !pInputWin
->IsEnabled())
4414 pDelayTimer
->Stop();
4415 pInputWin
->Enable();
4418 else if(pScMod
->IsRefDialogOpen())
4419 { // Because every document has its own InputWin,
4420 // we should start Timer again, because the input line may
4422 if ( !pDelayTimer
->IsActive() )
4423 pDelayTimer
->Start();
4427 else // !pState || !pActiveViewSh
4429 if ( !pDelayTimer
->IsActive() )
4430 pDelayTimer
->Start();
4435 bInOwnChange
= false;
4438 void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust
)
4440 eAttrAdjust
= eJust
;
4444 void ScInputHandler::ResetDelayTimer()
4446 if( pDelayTimer
->IsActive() )
4448 pDelayTimer
->Stop();
4450 pInputWin
->Enable();
4454 IMPL_LINK_NOARG( ScInputHandler
, DelayTimer
, Timer
*, void )
4456 if ( !(nullptr == pLastState
|| SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen()))
4459 //! New method at ScModule to query if function autopilot is open
4460 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
4461 if ( pViewFrm
&& pViewFrm
->GetChildWindow( SID_OPENDLG_FUNCTION
) )
4465 pInputWin
->EnableButtons( false );
4466 pInputWin
->Disable();
4469 else if ( !bFormulaMode
) // Keep formula e.g. for help
4471 bInOwnChange
= true; // disable ModifyHdl (reset below)
4473 pActiveViewSh
= nullptr;
4474 mpEditEngine
->SetTextCurrentDefaults( OUString() );
4477 pInputWin
->SetPosString( OUString() );
4478 pInputWin
->SetTextString(OUString(), true);
4479 pInputWin
->Disable();
4482 bInOwnChange
= false;
4486 void ScInputHandler::InputSelection( const EditView
* pView
)
4490 UpdateParenthesis(); // Selection changed -> update parentheses highlighting
4492 // When the selection is changed manually, stop overwriting parentheses
4495 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh
)
4497 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
4498 ESelection aSel
= pActiveView
? pActiveView
->GetSelection() : ESelection();
4499 pActiveViewSh
->LOKSendFormulabarUpdate(pActiveView
, GetEditString(), aSel
);
4503 void ScInputHandler::InputChanged( const EditView
* pView
, bool bFromNotify
)
4510 // #i20282# DataChanged needs to know if this is from the input line's modify handler
4511 bool bFromTopNotify
= ( bFromNotify
&& pView
== pTopView
);
4513 bool bNewView
= DataChanging(); //FIXME: Is this at all possible?
4514 aCurrentText
= pView
->GetEditEngine()->GetText(); // Also remember the string
4515 mpEditEngine
->SetTextCurrentDefaults( aCurrentText
);
4516 DataChanged( bFromTopNotify
);
4517 bTextValid
= true; // Is set to false in DataChanged
4519 if ( pActiveViewSh
)
4521 ScViewData
& rViewData
= pActiveViewSh
->GetViewData();
4523 rViewData
.GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
4525 rViewData
.EditGrowY();
4526 rViewData
.EditGrowX();
4532 const OUString
& ScInputHandler::GetEditString()
4536 aCurrentText
= mpEditEngine
->GetText(); // Always new from Engine
4540 return aCurrentText
;
4543 Size
ScInputHandler::GetTextSize()
4547 aSize
= Size( mpEditEngine
->CalcTextWidth(), mpEditEngine
->GetTextHeight() );
4552 bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter
& rDestEngine
)
4558 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
4559 SfxItemSet aSet
= mpEditEngine
->GetAttribs( ESelection(0,0,nParCnt
,0) );
4560 SfxItemState eFieldState
= aSet
.GetItemState( EE_FEATURE_FIELD
, false );
4561 if ( eFieldState
== SfxItemState::DONTCARE
|| eFieldState
== SfxItemState::SET
)
4564 std::unique_ptr
<EditTextObject
> pObj
= mpEditEngine
->CreateTextObject();
4565 rDestEngine
.SetTextCurrentDefaults(*pObj
);
4568 // Delete attributes
4569 for (sal_Int32 i
=0; i
<nParCnt
; i
++)
4570 rDestEngine
.RemoveCharAttribs( i
);
4572 // Combine paragraphs
4573 while ( nParCnt
> 1 )
4575 sal_Int32 nLen
= rDestEngine
.GetTextLen( 0 );
4576 ESelection
aSel( 0,nLen
, 1,0 );
4577 rDestEngine
.QuickInsertText( OUString(' '), aSel
); // Replace line break with space
4588 * Methods for FunctionAutoPilot:
4589 * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
4591 void ScInputHandler::InputGetSelection( sal_Int32
& rStart
, sal_Int32
& rEnd
)
4593 rStart
= nFormSelStart
;
4597 EditView
* ScInputHandler::GetFuncEditView()
4599 UpdateActiveView(); // Due to pTableView
4601 EditView
* pView
= nullptr;
4604 pInputWin
->MakeDialogEditView();
4605 pView
= pInputWin
->GetEditView();
4609 if ( eMode
!= SC_INPUT_TABLE
)
4611 bCreatingFuncView
= true; // Don't display RangeFinder
4612 SetMode( SC_INPUT_TABLE
);
4613 bCreatingFuncView
= false;
4615 pTableView
->GetEditEngine()->SetText( OUString() );
4623 void ScInputHandler::InputSetSelection( sal_Int32 nStart
, sal_Int32 nEnd
)
4625 if ( nStart
<= nEnd
)
4627 nFormSelStart
= nStart
;
4632 nFormSelEnd
= nStart
;
4633 nFormSelStart
= nEnd
;
4636 EditView
* pView
= GetFuncEditView();
4638 pView
->SetSelection( ESelection(0,nStart
, 0,nEnd
) );
4643 void ScInputHandler::InputReplaceSelection( std::u16string_view aStr
)
4646 pRefViewSh
= pActiveViewSh
;
4648 OSL_ENSURE(nFormSelEnd
>=nFormSelStart
,"Selection broken...");
4650 sal_Int32 nOldLen
= nFormSelEnd
- nFormSelStart
;
4651 sal_Int32 nNewLen
= aStr
.size();
4653 OUStringBuffer
aBuf(aFormText
);
4655 aBuf
.remove(nFormSelStart
, nOldLen
);
4657 aBuf
.insert(nFormSelStart
, aStr
);
4659 aFormText
= aBuf
.makeStringAndClear();
4661 nFormSelEnd
= nFormSelStart
+ nNewLen
;
4663 EditView
* pView
= GetFuncEditView();
4666 pView
->SetEditEngineUpdateLayout( false );
4667 pView
->GetEditEngine()->SetText( aFormText
);
4668 pView
->SetSelection( ESelection(0,nFormSelStart
, 0,nFormSelEnd
) );
4669 pView
->SetEditEngineUpdateLayout( true );
4674 void ScInputHandler::InputTurnOffWinEngine()
4676 bInOwnChange
= true; // disable ModifyHdl (reset below)
4678 eMode
= SC_INPUT_NONE
;
4679 /* TODO: it would be better if there was some way to reset the input bar
4680 * engine instead of deleting and having it recreate through
4681 * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
4682 * fdo#72278 without reintroducing fdo#69971. */
4683 StopInputWinEngine(true);
4685 bInOwnChange
= false;
4691 ScInputHdlState::ScInputHdlState( const ScAddress
& rCurPos
,
4692 const ScAddress
& rStartPos
,
4693 const ScAddress
& rEndPos
,
4695 const EditTextObject
* pData
)
4696 : aCursorPos ( rCurPos
),
4697 aStartPos ( rStartPos
),
4698 aEndPos ( rEndPos
),
4699 aString (std::move( _aString
)),
4700 pEditData ( pData
? pData
->Clone() : nullptr )
4704 ScInputHdlState::ScInputHdlState( const ScInputHdlState
& rCpy
)
4709 ScInputHdlState::~ScInputHdlState()
4713 bool ScInputHdlState::operator==( const ScInputHdlState
& r
) const
4715 return ( (aStartPos
== r
.aStartPos
)
4716 && (aEndPos
== r
.aEndPos
)
4717 && (aCursorPos
== r
.aCursorPos
)
4718 && (aString
== r
.aString
)
4719 && ScGlobal::EETextObjEqual( pEditData
.get(), r
.pEditData
.get() ) );
4722 ScInputHdlState
& ScInputHdlState::operator=( const ScInputHdlState
& r
)
4726 aCursorPos
= r
.aCursorPos
;
4727 aStartPos
= r
.aStartPos
;
4728 aEndPos
= r
.aEndPos
;
4729 aString
= r
.aString
;
4732 pEditData
= r
.pEditData
->Clone();
4737 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */