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 <unotools/localedatawrapper.hxx>
51 #include <unotools/charclass.hxx>
53 #include <vcl/help.hxx>
54 #include <vcl/jsdialog/executor.hxx>
55 #include <vcl/commandevent.hxx>
56 #include <vcl/cursor.hxx>
57 #include <vcl/settings.hxx>
58 #include <vcl/svapp.hxx>
59 #include <tools/urlobj.hxx>
60 #include <tools/json_writer.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() );
341 pViewShell
->libreOfficeKitViewCallback(
342 LOK_CALLBACK_REFERENCE_MARKS
, aPayload
);
345 static inline void incPos( const sal_Unicode c
, sal_Int32
& rPos
, ESelection
& rSel
)
359 void ScInputHandler::InitRangeFinder( const OUString
& rFormula
)
362 if (!pActiveViewSh
|| !ScModule::get()->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(u
" !~%\"\t\n"_ustr
);
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
.CollapseToEnd();
406 bool bSingleQuoted
= false;
409 // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1"
410 // Literal single quotes in sheet names are masked by another single quote
411 if (pChar
[nPos
] == '\'')
413 bSingleQuoted
= !bSingleQuoted
;
415 else if (!bSingleQuoted
) // Get everything in single quotes, including separators
417 if (ScGlobal::UnicodeStrChr(aDelimiters
.getStr(), pChar
[nPos
]))
420 incPos( pChar
[nPos
], nPos
, aSel
);
424 // for R1C1 '-' in R[-]... or C[-]... are not delimiters
425 // Nothing heroic here to ensure that there are '[]' around a negative
426 // integer. we need to clean up this code.
427 if( nPos
< nLen
&& nPos
> 0 &&
428 '-' == pChar
[nPos
] && '[' == pChar
[nPos
-1] &&
429 formula::FormulaGrammar::CONV_XL_R1C1
== rDoc
.GetAddressConvention() )
431 incPos( pChar
[nPos
], nPos
, aSel
);
437 OUString aTest
= rFormula
.copy( nStart
, nPos
-nStart
);
438 const ScAddress::Details
aAddrDetails( rDoc
, aCursorPos
);
439 ScRefFlags nFlags
= aRange
.ParseAny( aTest
, rDoc
, aAddrDetails
);
440 if ( nFlags
& ScRefFlags::VALID
)
442 // Set tables if not specified
443 if ( (nFlags
& ScRefFlags::TAB_3D
) == ScRefFlags::ZERO
)
444 aRange
.aStart
.SetTab( pActiveViewSh
->GetViewData().GetTabNo() );
445 if ( (nFlags
& ScRefFlags::TAB2_3D
) == ScRefFlags::ZERO
)
446 aRange
.aEnd
.SetTab( aRange
.aStart
.Tab() );
448 if ( ( nFlags
& (ScRefFlags::COL2_VALID
|ScRefFlags::ROW2_VALID
|ScRefFlags::TAB2_VALID
) ) ==
451 // #i73766# if a single ref was parsed, set the same "abs" flags for ref2,
452 // so Format doesn't output a double ref because of different flags.
453 ScRefFlags nAbsFlags
= nFlags
& (ScRefFlags::COL_ABS
|ScRefFlags::ROW_ABS
|ScRefFlags::TAB_ABS
);
454 applyStartToEndFlags(nFlags
, nAbsFlags
);
459 mpEditEngine
->SetUpdateLayout( false );
460 pRangeFindList
.reset(new ScRangeFindList( pDocSh
->GetTitle() ));
463 Color nColor
= pRangeFindList
->Insert( ScRangeFindData( aRange
, nFlags
, aSel
));
465 SfxItemSet
aSet( mpEditEngine
->GetEmptyItemSet() );
466 aSet
.Put( SvxColorItem( nColor
, EE_CHAR_COLOR
) );
467 mpEditEngine
->QuickSetAttribs( aSet
, aSel
);
472 // Do not skip last separator; could be a quote (?)
475 UpdateLokReferenceMarks();
479 mpEditEngine
->SetUpdateLayout( true );
481 pDocSh
->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder
) );
485 ReferenceMark
ScInputHandler::GetReferenceMark( const ScViewData
& rViewData
, ScDocShell
* pDocSh
,
486 tools::Long nX1
, tools::Long nX2
, tools::Long nY1
, tools::Long nY2
,
487 tools::Long nTab
, const Color
& rColor
)
489 ScSplitPos eWhich
= rViewData
.GetActivePart();
491 // This method is LOK specific.
492 if (comphelper::LibreOfficeKit::isCompatFlagSet(
493 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs
))
495 SCCOL nCol1
= nX1
, nCol2
= nX2
;
496 SCROW nRow1
= nY1
, nRow2
= nY2
;
497 ScDocument
& rDoc
= pDocSh
->GetDocument();
499 PutInOrder(nCol1
, nCol2
);
500 PutInOrder(nRow1
, nRow2
);
502 if (nCol1
== nCol2
&& nRow1
== nRow2
)
503 rDoc
.ExtendMerge(nCol1
, nRow1
, nCol2
, nRow2
, nTab
);
504 else if (rDoc
.HasAttrib(nCol2
, nRow2
, nTab
, HasAttrFlags::Merged
))
505 rDoc
.ExtendMerge(nCol2
, nRow2
, nCol2
, nRow2
, nTab
);
507 Point aTopLeft
= rViewData
.GetPrintTwipsPos(nCol1
, nRow1
);
508 Point aBottomRight
= rViewData
.GetPrintTwipsPos(nCol2
+ 1, nRow2
+ 1);
509 tools::Long nSizeX
= aBottomRight
.X() - aTopLeft
.X() - 1;
510 tools::Long nSizeY
= aBottomRight
.Y() - aTopLeft
.Y() - 1;
512 return ReferenceMark(aTopLeft
.X(), aTopLeft
.Y(), nSizeX
, nSizeY
, nTab
, rColor
);
515 Point aScrPos
= rViewData
.GetScrPos( nX1
, nY1
, eWhich
);
516 tools::Long nScrX
= aScrPos
.X();
517 tools::Long nScrY
= aScrPos
.Y();
519 double nPPTX
= rViewData
.GetPPTX();
520 double nPPTY
= rViewData
.GetPPTY();
522 Fraction aZoomX
= rViewData
.GetZoomX();
523 Fraction aZoomY
= rViewData
.GetZoomY();
525 ScTableInfo
aTabInfo(nY1
, nY2
, true);
526 pDocSh
->GetDocument().FillInfo( aTabInfo
, nX1
, nY1
, nX2
, nY2
,
527 nTab
, nPPTX
, nPPTY
, false, false );
529 ScOutputData
aOutputData( nullptr, OUTTYPE_WINDOW
, aTabInfo
,
530 &( pDocSh
->GetDocument() ), nTab
,
536 return aOutputData
.FillReferenceMark( nX1
, nY1
, nX2
, nY2
,
540 void ScInputHandler::UpdateLokReferenceMarks()
542 if ( !comphelper::LibreOfficeKit::isActive())
545 ScTabViewShell
* pShell
= pActiveViewSh
? pActiveViewSh
546 : dynamic_cast<ScTabViewShell
*>(SfxViewShell::Current());
551 ScViewData
& rViewData
= pShell
->GetViewData();
552 ScDocShell
* pDocSh
= rViewData
.GetDocShell();
553 ScRangeFindList
* pRangeFinder
= GetRangeFindList();
555 if ( !pRangeFinder
&& !rViewData
.IsRefMode() )
558 sal_uInt16 nAdditionalMarks
= 0;
559 std::vector
<ReferenceMark
> aReferenceMarks( 1 );
561 if ( rViewData
.IsRefMode() )
563 nAdditionalMarks
= 1;
565 const svtools::ColorConfig
& rColorCfg
= ScModule::get()->GetColorConfig();
566 Color
aRefColor( rColorCfg
.GetColorValue( svtools::CALCREFERENCE
).nColor
);
567 tools::Long nX1
= rViewData
.GetRefStartX();
568 tools::Long nX2
= rViewData
.GetRefEndX();
569 tools::Long nY1
= rViewData
.GetRefStartY();
570 tools::Long nY2
= rViewData
.GetRefEndY();
571 tools::Long nTab
= rViewData
.GetRefStartZ();
573 if (rViewData
.GetRefEndZ() == rViewData
.GetTabNo())
574 nTab
= rViewData
.GetRefEndZ();
576 PutInOrder(nX1
, nX2
);
577 PutInOrder(nY1
, nY2
);
579 aReferenceMarks
[0] = ScInputHandler::GetReferenceMark( rViewData
, pDocSh
,
584 sal_uInt16 nCount
= pRangeFinder
?
585 ( static_cast<sal_uInt16
>( pRangeFinder
->Count() ) + nAdditionalMarks
) : nAdditionalMarks
;
586 aReferenceMarks
.resize( nCount
);
588 if ( nCount
&& pRangeFinder
&& !pRangeFinder
->IsHidden() &&
589 pRangeFinder
->GetDocName() == pDocSh
->GetTitle() )
591 for (sal_uInt16 i
= 0; i
< nCount
- nAdditionalMarks
; i
++)
593 ScRangeFindData
& rData
= pRangeFinder
->GetObject( i
);
594 ScRange aRef
= rData
.aRef
;
597 tools::Long nX1
= aRef
.aStart
.Col();
598 tools::Long nX2
= aRef
.aEnd
.Col();
599 tools::Long nY1
= aRef
.aStart
.Row();
600 tools::Long nY2
= aRef
.aEnd
.Row();
601 tools::Long nTab
= aRef
.aStart
.Tab();
603 aReferenceMarks
[i
+ nAdditionalMarks
] = ScInputHandler::GetReferenceMark( rViewData
, pDocSh
,
605 nTab
, rData
.nColor
);
607 ScInputHandler::SendReferenceMarks( pShell
, aReferenceMarks
);
612 ScInputHandler::SendReferenceMarks( pShell
, aReferenceMarks
);
617 aReferenceMarks
.clear();
618 ScInputHandler::SendReferenceMarks( pShell
, aReferenceMarks
);
622 void ScInputHandler::SetDocumentDisposing( bool b
)
624 mbDocumentDisposing
= b
;
627 static void lcl_Replace( EditView
* pView
, const OUString
& rNewStr
, const ESelection
& rOldSel
)
632 ESelection aOldSel
= pView
->GetSelection();
633 if (aOldSel
.HasRange())
634 pView
->SetSelection(ESelection(aOldSel
.end
));
636 EditEngine
& rEngine
= pView
->getEditEngine();
637 rEngine
.QuickInsertText( rNewStr
, rOldSel
);
639 // Dummy InsertText for Update and Paint
640 // To do that we need to cancel the selection from above (before QuickInsertText)
641 pView
->InsertText( OUString() );
643 pView
->SetSelection(ESelection::AtEnd()); // Set cursor to the end
646 void ScInputHandler::UpdateRange( sal_uInt16 nIndex
, const ScRange
& rNew
)
648 ScTabViewShell
* pDocView
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
649 if ( pDocView
&& pRangeFindList
&& nIndex
< pRangeFindList
->Count() )
651 ScRangeFindData
& rData
= pRangeFindList
->GetObject( nIndex
);
652 Color nNewColor
= pRangeFindList
->FindColor( rNew
, nIndex
);
654 ScRange aJustified
= rNew
;
655 aJustified
.PutInOrder(); // Always display Ref in the Formula the right way
656 ScDocument
& rDoc
= pDocView
->GetViewData().GetDocument();
657 const ScAddress::Details
aAddrDetails( rDoc
, aCursorPos
);
658 OUString
aNewStr(aJustified
.Format(rDoc
, rData
.nFlags
, aAddrDetails
));
659 SfxItemSet
aSet( mpEditEngine
->GetEmptyItemSet() );
663 lcl_Replace( pTopView
, aNewStr
, rData
.maSel
);
664 lcl_Replace( pTableView
, aNewStr
, rData
.maSel
);
666 // We are within one paragraph.
667 const sal_Int32 nDiff
= aNewStr
.getLength() - (rData
.maSel
.end
.nIndex
- rData
.maSel
.start
.nIndex
);
668 rData
.maSel
.end
.nIndex
+= nDiff
;
670 aSet
.Put( SvxColorItem( nNewColor
, EE_CHAR_COLOR
) );
671 mpEditEngine
->QuickSetAttribs( aSet
, rData
.maSel
);
673 bInRangeUpdate
= true;
675 bInRangeUpdate
= false;
678 rData
.nColor
= nNewColor
;
682 const size_t nCount
= pRangeFindList
->Count();
683 for (size_t i
= nIndex
+ 1; i
< nCount
; ++i
)
685 ScRangeFindData
& rNext
= pRangeFindList
->GetObject( i
);
686 if (rNext
.maSel
.start
.nPara
!= rData
.maSel
.start
.nPara
)
689 rNext
.maSel
.start
.nIndex
+= nDiff
;
690 rNext
.maSel
.end
.nIndex
+= nDiff
;
694 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
695 pActiveView
->ShowCursor( false );
699 OSL_FAIL("UpdateRange: we're missing something");
703 void ScInputHandler::DeleteRangeFinder()
705 ScTabViewShell
* pPaintView
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
706 if ( pRangeFindList
&& pPaintView
)
708 ScDocShell
* pDocSh
= pActiveViewSh
->GetViewData().GetDocShell();
709 pRangeFindList
->SetHidden(true);
710 pDocSh
->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder
) ); // Steal
711 pRangeFindList
.reset();
715 static OUString
GetEditText(const EditEngine
* pEng
)
717 return ScEditUtil::GetMultilineString(*pEng
);
720 static void lcl_RemoveTabs(OUString
& rStr
)
722 rStr
= rStr
.replace('\t', ' ');
725 static void lcl_RemoveLineEnd(OUString
& rStr
)
727 rStr
= convertLineEnd(rStr
, LINEEND_LF
);
728 rStr
= rStr
.replace('\n', ' ');
731 static sal_Int32
lcl_MatchParenthesis( const OUString
& rStr
, sal_Int32 nPos
)
734 sal_Unicode c1
, c2
= 0;
775 sal_Int32 nLen
= rStr
.getLength();
776 const sal_Unicode
* p0
= rStr
.getStr();
777 const sal_Unicode
* p
;
778 const sal_Unicode
* p1
;
779 sal_uInt16 nQuotes
= 0;
780 if ( nPos
< nLen
/ 2 )
795 // Odd number of quotes that we find ourselves in a string
796 bool bLookInString
= ((nQuotes
% 2) != 0);
797 bool bInString
= bLookInString
;
799 p1
= (nDir
< 0 ? p0
: p0
+ nLen
) ;
800 sal_uInt16 nLevel
= 1;
801 while ( p
!= p1
&& nLevel
)
806 bInString
= !bInString
;
807 if ( bLookInString
&& !bInString
)
808 p
= p1
; // That's it then
810 else if ( bInString
== bLookInString
)
820 return static_cast<sal_Int32
>(p
- p0
);
823 ScInputHandler::ScInputHandler()
824 : pInputWin( nullptr ),
825 pTableView( nullptr ),
827 pTipVisibleParent( nullptr ),
828 nTipVisible( nullptr ),
829 pTipVisibleSecParent( nullptr ),
830 nTipVisibleSec( nullptr ),
833 nCellPercentFormatDecSep( 0 ),
835 eMode( SC_INPUT_NONE
),
840 bFormulaMode( false ),
841 bInRangeUpdate( false ),
842 bParenthesisShown( false ),
843 bCreatingFuncView( false ),
844 bInEnterHandler( false ),
845 bCommandErrorShown( false ),
846 bInOwnChange( false ),
848 bLastIsSymbol( false ),
849 mbDocumentDisposing(false),
850 mbPartialPrefix(false),
851 mbEditingExistingContent(false),
853 eAttrAdjust( SvxCellHorJustify::Standard
),
856 pRefViewSh( nullptr ),
857 pLastPattern( nullptr )
859 // The InputHandler is constructed with the view, so SfxViewShell::Current
860 // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange.
861 pActiveViewSh
= nullptr;
863 // Bindings (only still used for Invalidate) are retrieved if needed on demand
865 pDelayTimer
.reset( new Timer( "ScInputHandlerDelay timer" ) );
866 pDelayTimer
->SetTimeout( 500 ); // 500 ms delay
867 pDelayTimer
->SetInvokeHandler( LINK( this, ScInputHandler
, DelayTimer
) );
870 ScInputHandler::~ScInputHandler()
872 // If this is the application InputHandler, the dtor is called after SfxApplication::Main,
873 // thus we can't rely on any Sfx functions
874 if (!mbDocumentDisposing
) // inplace
875 EnterHandler(); // Finish input
877 if (ScModule
* mod
= ScModule::get(); mod
->GetRefInputHdl() == this)
878 mod
->SetRefInputHdl(nullptr);
880 if ( pInputWin
&& pInputWin
->GetInputHandler() == this )
881 pInputWin
->SetInputHandler( nullptr );
884 void ScInputHandler::SetRefScale( const Fraction
& rX
, const Fraction
& rY
)
886 if ( rX
!= aScaleX
|| rY
!= aScaleY
)
892 MapMode
aMode( MapUnit::Map100thMM
, Point(), aScaleX
, aScaleY
);
893 mpEditEngine
->SetRefMapMode( aMode
);
898 void ScInputHandler::UpdateRefDevice()
903 bool bTextWysiwyg
= ScModule::get()->GetInputOptions().GetTextWysiwyg();
904 if ( bTextWysiwyg
&& pActiveViewSh
)
905 mpEditEngine
->SetRefDevice( pActiveViewSh
->GetViewData().GetDocument().GetPrinter() );
907 mpEditEngine
->SetRefDevice( nullptr );
909 MapMode
aMode( MapUnit::Map100thMM
, Point(), aScaleX
, aScaleY
);
910 mpEditEngine
->SetRefMapMode( aMode
);
912 // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev,
913 // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL).
914 if ( !( bTextWysiwyg
&& pActiveViewSh
) )
916 mpEditEngine
->GetRefDevice()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
920 void ScInputHandler::ImplCreateEditEngine()
925 // we cannot create a properly initialised EditEngine until we have a document
926 assert( pActiveViewSh
);
927 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
928 mpEditEngine
= std::make_unique
<ScFieldEditEngine
>(&rDoc
, rDoc
.GetEnginePool(), rDoc
.GetEditPool());
929 mpEditEngine
->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine
->GetWordDelimiters() ) );
930 UpdateRefDevice(); // also sets MapMode
931 mpEditEngine
->SetPaperSize( Size( 1000000, 1000000 ) );
932 pEditDefaults
.reset( new SfxItemSet( mpEditEngine
->GetEmptyItemSet() ) );
934 mpEditEngine
->SetControlWord( mpEditEngine
->GetControlWord() | EEControlBits::AUTOCORRECT
);
935 mpEditEngine
->SetReplaceLeadingSingleQuotationMark( false );
936 mpEditEngine
->SetModifyHdl( LINK( this, ScInputHandler
, ModifyHdl
) );
939 void ScInputHandler::UpdateAutoCorrFlag()
941 EEControlBits nCntrl
= mpEditEngine
->GetControlWord();
942 EEControlBits nOld
= nCntrl
;
944 // Don't use pLastPattern here (may be invalid because of AutoStyle)
945 bool bDisable
= bLastIsSymbol
|| bFormulaMode
;
947 nCntrl
&= ~EEControlBits::AUTOCORRECT
;
949 nCntrl
|= EEControlBits::AUTOCORRECT
;
951 if ( nCntrl
!= nOld
)
952 mpEditEngine
->SetControlWord(nCntrl
);
955 void ScInputHandler::UpdateSpellSettings( bool bFromStartTab
)
957 if ( !pActiveViewSh
)
960 ScViewData
& rViewData
= pActiveViewSh
->GetViewData();
961 bool bOnlineSpell
= pActiveViewSh
->IsAutoSpell();
963 // SetDefaultLanguage is independent of the language attributes,
964 // ScGlobal::GetEditDefaultLanguage is always used.
965 // It must be set every time in case the office language was changed.
967 mpEditEngine
->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
969 // if called for changed options, update flags only if already editing
970 // if called from StartTable, always update flags
972 if ( bFromStartTab
|| eMode
!= SC_INPUT_NONE
)
974 EEControlBits nCntrl
= mpEditEngine
->GetControlWord();
975 EEControlBits nOld
= nCntrl
;
977 nCntrl
|= EEControlBits::ONLINESPELLING
;
979 nCntrl
&= ~EEControlBits::ONLINESPELLING
;
980 // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
981 if ( pLastPattern
&& pLastPattern
->IsSymbolFont() )
982 nCntrl
&= ~EEControlBits::AUTOCORRECT
;
984 nCntrl
|= EEControlBits::AUTOCORRECT
;
985 if ( nCntrl
!= nOld
)
986 mpEditEngine
->SetControlWord(nCntrl
);
988 ScDocument
& rDoc
= rViewData
.GetDocument();
989 rDoc
.ApplyAsianEditSettings( *mpEditEngine
);
990 mpEditEngine
->SetDefaultHorizontalTextDirection(
991 rDoc
.GetEditTextDirection( rViewData
.GetTabNo() ) );
992 mpEditEngine
->SetFirstWordCapitalization( false );
995 // Language is set separately, so the speller is needed only if online spelling is active
996 if ( bOnlineSpell
) {
997 css::uno::Reference
<css::linguistic2::XSpellChecker1
> xXSpellChecker1( LinguMgr::GetSpellChecker() );
998 mpEditEngine
->SetSpeller( xXSpellChecker1
);
1001 bool bHyphen
= pLastPattern
&& pLastPattern
->GetItem(ATTR_HYPHENATE
).GetValue();
1003 css::uno::Reference
<css::linguistic2::XHyphenator
> xXHyphenator( LinguMgr::GetHyphenator() );
1004 mpEditEngine
->SetHyphenator( xXHyphenator
);
1008 // Function/Range names etc. as Tip help
1010 // The other types are defined in ScDocument::GetFormulaEntries
1011 void ScInputHandler::GetFormulaData()
1013 if ( !pActiveViewSh
)
1016 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
1019 pFormulaData
->clear();
1022 pFormulaData
.reset( new ScTypedCaseStrSet
);
1025 if( pFormulaDataPara
)
1026 pFormulaDataPara
->clear();
1028 pFormulaDataPara
.reset( new ScTypedCaseStrSet
);
1030 const OUString
aParenthesesReplacement( cParenthesesReplacement
);
1031 const ScFunctionList
* pFuncList
= ScGlobal::GetStarCalcFunctionList();
1032 const sal_uInt32 nListCount
= pFuncList
->GetCount();
1033 const InputHandlerFunctionNames
& rFunctionNames
= ScGlobal::GetInputHandlerFunctionNames();
1034 *pFormulaData
= rFunctionNames
.maFunctionData
;
1035 *pFormulaDataPara
= rFunctionNames
.maFunctionDataPara
;
1036 maFormulaChar
= rFunctionNames
.maFunctionChar
;
1038 // Increase suggestion priority of MRU formulas
1039 const ScAppOptions
& rOpt
= ScModule::get()->GetAppOptions();
1040 const sal_uInt16 nMRUCount
= rOpt
.GetLRUFuncListCount();
1041 const sal_uInt16
* pMRUList
= rOpt
.GetLRUFuncList();
1042 for (sal_uInt16 i
= 0; i
< nMRUCount
; i
++)
1044 const sal_uInt16 nId
= pMRUList
[i
];
1045 for (sal_uInt32 j
= 0; j
< nListCount
; j
++)
1047 const ScFuncDesc
* pDesc
= pFuncList
->GetFunction(j
);
1048 if (pDesc
->nFIndex
== nId
&& pDesc
->mxFuncName
)
1050 const OUString aEntry
= *pDesc
->mxFuncName
+ aParenthesesReplacement
;;
1051 const ScTypedStrData
aData(aEntry
, 0.0, 0.0, ScTypedStrData::Standard
);
1052 auto it
= pFormulaData
->find(aData
);
1053 if (it
!= pFormulaData
->end())
1054 pFormulaData
->erase(it
);
1055 pFormulaData
->insert(ScTypedStrData(aEntry
, 0.0, 0.0, ScTypedStrData::MRU
));
1056 break; // Stop searching
1060 miAutoPosFormula
= pFormulaData
->end();
1062 // tdf#142031 - collect all the characters for the formula suggestion auto input
1063 ScTypedCaseStrSet aStrSet
;
1064 rDoc
.GetFormulaEntries( aStrSet
);
1065 for (auto iter
= aStrSet
.begin(); iter
!= aStrSet
.end(); ++iter
)
1067 const OUString aFuncName
= ScGlobal::getCharClass().uppercase((*iter
).GetString());
1068 // fdo#75264 fill maFormulaChar with all characters used in formula names
1069 for (sal_Int32 j
= 0; j
< aFuncName
.getLength(); j
++)
1070 maFormulaChar
.insert(aFuncName
[j
]);
1072 pFormulaData
->insert(aStrSet
.begin(), aStrSet
.end());
1073 pFormulaDataPara
->insert(aStrSet
.begin(), aStrSet
.end());
1076 IMPL_LINK( ScInputHandler
, ShowHideTipVisibleParentListener
, VclWindowEvent
&, rEvent
, void )
1078 if (rEvent
.GetId() == VclEventId::ObjectDying
|| rEvent
.GetId() == VclEventId::WindowHide
1079 || rEvent
.GetId() == VclEventId::WindowLoseFocus
|| rEvent
.GetId() == VclEventId::ControlLoseFocus
)
1083 IMPL_LINK( ScInputHandler
, ShowHideTipVisibleSecParentListener
, VclWindowEvent
&, rEvent
, void )
1085 if (rEvent
.GetId() == VclEventId::ObjectDying
|| rEvent
.GetId() == VclEventId::WindowHide
1086 || rEvent
.GetId() == VclEventId::WindowLoseFocus
|| rEvent
.GetId() == VclEventId::ControlLoseFocus
)
1090 void ScInputHandler::HideTip()
1094 pTipVisibleParent
->RemoveEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleParentListener
) );
1095 Help::HidePopover(pTipVisibleParent
, nTipVisible
);
1096 nTipVisible
= nullptr;
1097 pTipVisibleParent
= nullptr;
1101 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
1102 if (comphelper::LibreOfficeKit::isActive() && pViewShell
)
1103 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST
, "hidetip"_ostr
);
1105 void ScInputHandler::HideTipBelow()
1107 if ( nTipVisibleSec
)
1109 pTipVisibleSecParent
->RemoveEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleSecParentListener
) );
1110 Help::HidePopover(pTipVisibleSecParent
, nTipVisibleSec
);
1111 nTipVisibleSec
= nullptr;
1112 pTipVisibleSecParent
= nullptr;
1120 bool lcl_hasSingleToken(std::u16string_view s
, sal_Unicode c
)
1122 return !s
.empty() && s
.find(c
) == std::u16string_view::npos
;
1127 void ScInputHandler::ShowArgumentsTip( OUString
& rSelText
)
1129 if ( !pActiveViewSh
)
1132 ScDocShell
* pDocSh
= pActiveViewSh
->GetViewData().GetDocShell();
1133 const sal_Unicode cSep
= ScCompiler::GetNativeSymbolChar(ocSep
);
1134 const sal_Unicode cSheetSep
= pDocSh
->GetDocument().GetSheetSeparator();
1135 FormulaHelper
aHelper(ScGlobal::GetStarCalcFunctionMgr());
1136 bool bFound
= false;
1140 sal_Int32 nLeftParentPos
= lcl_MatchParenthesis( rSelText
, rSelText
.getLength()-1 );
1141 if( nLeftParentPos
!= -1 )
1143 sal_Int32 nNextFStart
= aHelper
.GetFunctionStart( rSelText
, nLeftParentPos
, true);
1144 const IFunctionDescription
* ppFDesc
;
1145 ::std::vector
< OUString
> aArgs
;
1146 if( aHelper
.GetNextFunc( rSelText
, false, nNextFStart
, nullptr, &ppFDesc
, &aArgs
) )
1148 if( !ppFDesc
->getFunctionName().isEmpty() )
1150 sal_Int32 nArgPos
= aHelper
.GetArgStart( rSelText
, nNextFStart
, 0 );
1151 sal_uInt16 nArgs
= static_cast<sal_uInt16
>(ppFDesc
->getParameterCount());
1152 OUString
aFuncName( ppFDesc
->getFunctionName() + "(");
1154 ScTypedCaseStrSet::const_iterator it
=
1155 findText(*pFormulaDataPara
, pFormulaDataPara
->end(), aFuncName
, aNew
, false);
1156 if (it
!= pFormulaDataPara
->end())
1159 sal_uInt16 nActive
= 0;
1160 for( sal_uInt16 i
=0; i
< nArgs
; i
++ )
1162 sal_Int32 nLength
= aArgs
[i
].getLength();
1163 if( nArgPos
<= rSelText
.getLength()-1 )
1172 sal_Int32 nStartPosition
= 0;
1173 sal_Int32 nEndPosition
= 0;
1175 if( lcl_hasSingleToken(aNew
, cSep
) )
1177 for (sal_Int32 i
= 0; i
< aNew
.getLength(); ++i
)
1179 sal_Unicode cNext
= aNew
[i
];
1182 nStartPosition
= i
+1;
1186 else if( lcl_hasSingleToken(aNew
, cSheetSep
) )
1188 sal_uInt16 nCount
= 0;
1189 for (sal_Int32 i
= 0; i
< aNew
.getLength(); ++i
)
1191 sal_Unicode cNext
= aNew
[i
];
1194 nStartPosition
= i
+1;
1196 else if( cNext
== cSep
)
1200 if( nCount
== nActive
)
1204 nStartPosition
= nEndPosition
+1;
1210 sal_uInt16 nCount
= 0;
1211 for (sal_Int32 i
= 0; i
< aNew
.getLength(); ++i
)
1213 sal_Unicode cNext
= aNew
[i
];
1216 nStartPosition
= i
+1;
1218 else if( cNext
== cSep
)
1222 if( nCount
== nActive
)
1226 nStartPosition
= nEndPosition
+1;
1228 else if( cNext
== cSheetSep
)
1235 if (nStartPosition
> 0)
1237 nArgs
= ppFDesc
->getParameterCount();
1238 sal_Int16 nVarArgsSet
= 0;
1239 if ( nArgs
>= PAIRED_VAR_ARGS
)
1242 nArgs
-= PAIRED_VAR_ARGS
- nVarArgsSet
;
1244 else if ( nArgs
>= VAR_ARGS
)
1247 nArgs
-= VAR_ARGS
- nVarArgsSet
;
1249 if ( nVarArgsSet
> 0 && nActive
> nArgs
)
1250 nActive
= nArgs
- (nActive
- nArgs
) % nVarArgsSet
;
1251 aNew
= OUString::Concat(aNew
.subView(0, nStartPosition
)) +
1253 aNew
.subView(nStartPosition
) +
1255 ppFDesc
->getParameterDescription(nActive
-1);
1256 if (eMode
!= SC_INPUT_TOP
)
1258 ShowTipBelow( aNew
);
1269 ShowTipBelow( aNew
);
1273 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
1274 if (comphelper::LibreOfficeKit::isActive() && pViewShell
&& pViewShell
->isLOKDesktop())
1276 tools::JsonWriter writer
;
1277 writer
.put("type", "formulausage");
1278 writer
.put("text", aNew
);
1279 OString sFunctionUsageTip
= writer
.finishAndGetAsOString();
1280 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_TOOLTIP
, sFunctionUsageTip
);
1293 void ScInputHandler::ShowTipCursor()
1297 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1299 /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1300 if ( !(bFormulaMode
&& pActiveView
&& pFormulaDataPara
&& mpEditEngine
->GetParagraphCount() == 1) )
1303 OUString aParagraph
= mpEditEngine
->GetText( 0 );
1304 ESelection aSel
= pActiveView
->GetSelection();
1307 if (aParagraph
.getLength() < aSel
.end
.nIndex
)
1310 if (aSel
.end
.nIndex
> 0)
1312 OUString
aSelText(aParagraph
.copy(0, aSel
.end
.nIndex
));
1314 ShowArgumentsTip( aSelText
);
1318 void ScInputHandler::ShowTip( const OUString
& rText
)
1320 // aManualTip needs to be set afterwards from outside
1325 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1330 if (pInputWin
&& pInputWin
->GetEditView() == pActiveView
)
1332 pTipVisibleParent
= pInputWin
->GetEditWindow();
1333 aPos
= pInputWin
->GetCursorScreenPixelPos();
1337 pTipVisibleParent
= pActiveView
->GetWindow();
1338 if (vcl::Cursor
* pCur
= pActiveView
->GetCursor())
1339 aPos
= pTipVisibleParent
->LogicToPixel( pCur
->GetPos() );
1340 aPos
= pTipVisibleParent
->OutputToScreenPixel( aPos
);
1343 tools::Rectangle
aRect( aPos
, aPos
);
1344 QuickHelpFlags
const nAlign
= QuickHelpFlags::Left
|QuickHelpFlags::Bottom
;
1345 nTipVisible
= Help::ShowPopover(pTipVisibleParent
, aRect
, rText
, nAlign
);
1346 pTipVisibleParent
->AddEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleParentListener
) );
1349 void ScInputHandler::ShowTipBelow( const OUString
& rText
)
1353 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1358 if (pInputWin
&& pInputWin
->GetEditView() == pActiveView
)
1360 pTipVisibleSecParent
= pInputWin
->GetEditWindow();
1361 aPos
= pInputWin
->GetCursorScreenPixelPos(true);
1365 pTipVisibleSecParent
= pActiveView
->GetWindow();
1366 if (vcl::Cursor
* pCur
= pActiveView
->GetCursor())
1368 Point aLogicPos
= pCur
->GetPos();
1369 aLogicPos
.AdjustY(pCur
->GetHeight() );
1370 aPos
= pTipVisibleSecParent
->LogicToPixel( aLogicPos
);
1372 aPos
= pTipVisibleSecParent
->OutputToScreenPixel( aPos
);
1375 tools::Rectangle
aRect( aPos
, aPos
);
1376 QuickHelpFlags
const nAlign
= QuickHelpFlags::Left
| QuickHelpFlags::Top
| QuickHelpFlags::NoEvadePointer
;
1377 nTipVisibleSec
= Help::ShowPopover(pTipVisibleSecParent
, aRect
, rText
, nAlign
);
1378 pTipVisibleSecParent
->AddEventListener( LINK( this, ScInputHandler
, ShowHideTipVisibleSecParentListener
) );
1381 bool ScInputHandler::GetFuncName( OUString
& aStart
, OUString
& aResult
)
1383 if ( aStart
.isEmpty() )
1386 aStart
= ScGlobal::getCharClass().uppercase( aStart
);
1387 sal_Int32 nPos
= aStart
.getLength() - 1;
1388 sal_Unicode c
= aStart
[ nPos
];
1389 // fdo#75264 use maFormulaChar to check if characters are used in function names
1390 ::std::set
< sal_Unicode
>::const_iterator p
= maFormulaChar
.find( c
);
1391 if ( p
== maFormulaChar
.end() )
1392 return false; // last character is not part of any function name, quit
1394 ::std::vector
<sal_Unicode
> aTemp
{ c
};
1395 for(sal_Int32 i
= nPos
- 1; i
>= 0; --i
)
1398 p
= maFormulaChar
.find( c
);
1400 if (p
== maFormulaChar
.end())
1403 aTemp
.push_back( c
);
1406 ::std::vector
<sal_Unicode
>::reverse_iterator rIt
= aTemp
.rbegin();
1407 aResult
= OUString( *rIt
++ );
1408 while ( rIt
!= aTemp
.rend() )
1409 aResult
+= OUStringChar( *rIt
++ );
1415 /// Rid ourselves of unwanted " quoted json characters.
1416 OString
escapeJSON(const OUString
&aStr
)
1418 OUString aEscaped
= aStr
;
1419 aEscaped
= aEscaped
.replaceAll("\n", " ");
1420 aEscaped
= aEscaped
.replaceAll("\"", "'");
1421 return OUStringToOString(aEscaped
, RTL_TEXTENCODING_UTF8
);
1425 void ScInputHandler::ShowFuncList( const ::std::vector
< OUString
> & rFuncStrVec
)
1427 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
1428 if (comphelper::LibreOfficeKit::isActive())
1430 if (rFuncStrVec
.size() && pViewShell
)
1432 auto aPos
= pFormulaData
->begin();
1433 sal_uInt32 nCurIndex
= std::distance(aPos
, miAutoPosFormula
);
1434 const sal_uInt32 nSize
= pFormulaData
->size();
1436 OUString aFuncNameStr
;
1437 OUString aDescFuncNameStr
;
1438 OStringBuffer
aPayload("[ ");
1439 for (const OUString
& rFunc
: rFuncStrVec
)
1441 if ( rFunc
[rFunc
.getLength()-1] == cParenthesesReplacement
)
1443 aFuncNameStr
= rFunc
.copy(0, rFunc
.getLength()-1);
1447 aFuncNameStr
= rFunc
;
1450 FormulaHelper
aHelper(ScGlobal::GetStarCalcFunctionMgr());
1451 aDescFuncNameStr
= aFuncNameStr
+ "()";
1452 sal_Int32 nNextFStart
= 0;
1453 const IFunctionDescription
* ppFDesc
;
1454 ::std::vector
< OUString
> aArgs
;
1455 OUString eqPlusFuncName
= "=" + aDescFuncNameStr
;
1456 if ( aHelper
.GetNextFunc( eqPlusFuncName
, false, nNextFStart
, nullptr, &ppFDesc
, &aArgs
) )
1458 if ( !ppFDesc
->getFunctionName().isEmpty() )
1462 + OString::number(static_cast<sal_Int64
>(nCurIndex
))
1465 + escapeJSON(ppFDesc
->getSignature())
1467 "\"description\": \""
1468 + escapeJSON(ppFDesc
->getDescription())
1469 + "\", \"namedRange\": false }, ");
1475 + OString::number(static_cast<sal_Int64
>(nCurIndex
))
1478 + escapeJSON(aFuncNameStr
)
1480 "\"description\": \""
1481 + escapeJSON(OUString())
1482 + "\", \"namedRange\": true }, ");
1486 if (nCurIndex
== nSize
)
1489 sal_Int32 nLen
= aPayload
.getLength();
1492 aPayload
[nLen
- 1] = ']';
1496 aPayload
[nLen
- 2] = ' ';
1497 aPayload
[nLen
- 1] = ']';
1500 OString s
= aPayload
.makeStringAndClear();
1501 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST
, s
);
1506 OUStringBuffer aTipStr
;
1507 OUString aFuncNameStr
;
1508 OUString aDescFuncNameStr
;
1509 ::std::vector
<OUString
>::const_iterator itStr
= rFuncStrVec
.begin();
1510 sal_Int32 nMaxFindNumber
= 3;
1511 sal_Int32 nRemainFindNumber
= nMaxFindNumber
;
1512 for ( ; itStr
!= rFuncStrVec
.end(); ++itStr
)
1514 const OUString
& rFunc
= *itStr
;
1515 if ( rFunc
[rFunc
.getLength()-1] == cParenthesesReplacement
)
1517 aFuncNameStr
= rFunc
.copy(0, rFunc
.getLength()-1);
1521 aFuncNameStr
= rFunc
;
1523 if ( itStr
== rFuncStrVec
.begin() )
1526 aDescFuncNameStr
= aFuncNameStr
+ "()";
1530 aTipStr
.append(", ");
1532 aTipStr
.append(aFuncNameStr
);
1533 if ( itStr
== rFuncStrVec
.begin() )
1534 aTipStr
.append("]");
1535 if ( --nRemainFindNumber
<= 0 )
1538 sal_Int32 nRemainNumber
= rFuncStrVec
.size() - nMaxFindNumber
;
1539 if ( nRemainFindNumber
== 0 && nRemainNumber
> 0 )
1541 OUString
aMessage( ScResId( STR_FUNCTIONS_FOUND
) );
1542 aMessage
= aMessage
.replaceFirst("%2", OUString::number(nRemainNumber
));
1543 aMessage
= aMessage
.replaceFirst("%1", aTipStr
);
1546 FormulaHelper
aHelper(ScGlobal::GetStarCalcFunctionMgr());
1547 sal_Int32 nNextFStart
= 0;
1548 const IFunctionDescription
* ppFDesc
;
1549 ::std::vector
< OUString
> aArgs
;
1550 OUString eqPlusFuncName
= "=" + aDescFuncNameStr
;
1551 if ( aHelper
.GetNextFunc( eqPlusFuncName
, false, nNextFStart
, nullptr, &ppFDesc
, &aArgs
) )
1553 if ( !ppFDesc
->getFunctionName().isEmpty() )
1555 aTipStr
.append(" : " + ppFDesc
->getDescription());
1558 ShowTip( aTipStr
.makeStringAndClear() );
1561 void ScInputHandler::UseFormulaData()
1563 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1565 /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1566 if ( !(pActiveView
&& pFormulaData
&& mpEditEngine
->GetParagraphCount() == 1) )
1569 OUString aParagraph
= mpEditEngine
->GetText( 0 );
1570 ESelection aSel
= pActiveView
->GetSelection();
1573 // Due to differences between table and input cell (e.g clipboard with line breaks),
1574 // the selection may not be in line with the EditEngine anymore.
1575 // Just return without any indication as to why.
1576 if (aSel
.end
.nIndex
> aParagraph
.getLength())
1579 if ( aParagraph
.getLength() > aSel
.end
.nIndex
&&
1580 ( ScGlobal::getCharClass().isLetterNumeric( aParagraph
, aSel
.end
.nIndex
) ||
1581 aParagraph
[ aSel
.end
.nIndex
] == '_' ||
1582 aParagraph
[ aSel
.end
.nIndex
] == '.' ||
1583 aParagraph
[ aSel
.end
.nIndex
] == '$' ) )
1586 // Is the cursor at the end of a word?
1587 if (aSel
.end
.nIndex
<= 0)
1590 OUString
aSelText(aParagraph
.copy(0, aSel
.end
.nIndex
));
1593 if ( GetFuncName( aSelText
, aText
) )
1595 // function name is incomplete:
1596 // show matching functions name as tip above cell
1597 ::std::vector
<OUString
> aNewVec
;
1598 miAutoPosFormula
= pFormulaData
->end();
1599 miAutoPosFormula
= findTextAll(*pFormulaData
, miAutoPosFormula
, aText
, aNewVec
, false);
1600 if (miAutoPosFormula
!= pFormulaData
->end())
1602 // check if partial function name is not between quotes
1603 sal_Unicode cBetweenQuotes
= 0;
1604 for ( int n
= 0; n
< aSelText
.getLength(); n
++ )
1608 if (aSelText
[n
] == cBetweenQuotes
)
1611 else if ( aSelText
[ n
] == '"' )
1612 cBetweenQuotes
= '"';
1613 else if ( aSelText
[ n
] == '\'' )
1614 cBetweenQuotes
= '\'';
1616 if ( cBetweenQuotes
)
1617 return; // we're between quotes
1619 ShowFuncList(aNewVec
);
1620 aAutoSearch
= aText
;
1625 // function name is complete:
1626 // show tip below the cell with function name and arguments of function
1627 ShowArgumentsTip( aSelText
);
1630 void ScInputHandler::NextFormulaEntry( bool bBack
)
1632 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1633 if ( pActiveView
&& pFormulaData
)
1635 ::std::vector
<OUString
> aNewVec
;
1636 ScTypedCaseStrSet::const_iterator itNew
= findTextAll(*pFormulaData
, miAutoPosFormula
, aAutoSearch
, aNewVec
, bBack
);
1637 if (itNew
!= pFormulaData
->end())
1639 miAutoPosFormula
= itNew
;
1640 ShowFuncList( aNewVec
);
1644 // For Tab we always call HideCursor first
1646 pActiveView
->ShowCursor();
1651 void completeFunction( EditView
* pView
, const OUString
& rInsert
, bool& rParInserted
)
1656 ESelection aSel
= pView
->GetSelection();
1658 bool bNoInitialLetter
= false;
1659 OUString aOld
= pView
->getEditEngine().GetText(0);
1660 // in case we want just insert a function and not completing
1661 if ( comphelper::LibreOfficeKit::isActive() )
1663 ESelection aSelRange
= aSel
;
1664 --aSelRange
.start
.nIndex
;
1665 --aSelRange
.end
.nIndex
;
1666 pView
->SetSelection(aSelRange
);
1667 pView
->SelectCurrentWord();
1671 bNoInitialLetter
= true;
1672 aSelRange
.start
.nIndex
= 1;
1673 aSelRange
.end
.nIndex
= 1;
1674 pView
->SetSelection(aSelRange
);
1676 else if ( pView
->GetSelected().startsWith("()") )
1678 bNoInitialLetter
= true;
1679 ++aSelRange
.start
.nIndex
;
1680 ++aSelRange
.end
.nIndex
;
1681 pView
->SetSelection(aSelRange
);
1685 if(!bNoInitialLetter
)
1687 const sal_Int32 nMinLen
= std::max(aSel
.end
.nIndex
- aSel
.start
.nIndex
, sal_Int32(1));
1688 // Since transliteration service is used to test for match, the replaced string could be
1689 // longer than rInsert, so in order to find longest match before the cursor, test whole
1690 // string from start to current cursor position (don't limit to length of rInsert)
1691 // Disclaimer: I really don't know if a match longer than rInsert is actually possible,
1692 // so the above is based on assumptions how "transliteration" might possibly work. If
1693 // it's in fact impossible, an optimization would be useful to limit aSel.start.nPos to
1694 // std::max(sal_Int32(0), aSel.end.nIndex - rInsert.getLength()).
1695 aSel
.start
.nIndex
= 0;
1696 pView
->SetSelection(aSel
);
1697 const OUString aAll
= pView
->GetSelected();
1699 for (sal_Int32 n
= aAll
.getLength(); n
>= nMinLen
&& aMatch
.isEmpty(); --n
)
1701 const OUString aTest
= aAll
.copy(aAll
.getLength() - n
); // n trailing chars
1702 if (ScGlobal::GetTransliteration().isMatch(aTest
, rInsert
))
1703 aMatch
= aTest
; // Found => break the loop
1706 aSel
.start
.nIndex
= aSel
.end
.nIndex
- aMatch
.getLength();
1707 pView
->SetSelection(aSel
);
1710 OUString aInsStr
= rInsert
;
1711 sal_Int32 nInsLen
= aInsStr
.getLength();
1712 bool bDoParen
= ( nInsLen
> 1 && aInsStr
[nInsLen
-2] == '('
1713 && aInsStr
[nInsLen
-1] == ')' );
1716 // Do not insert parentheses after function names if there already are some
1717 // (e.g. if the function name was edited).
1718 ESelection aWordSel
= pView
->GetSelection();
1720 // aWordSel.EndPos points one behind string if word at end
1721 if (aWordSel
.end
.nIndex
< aOld
.getLength())
1723 sal_Unicode cNext
= aOld
[aWordSel
.end
.nIndex
];
1727 aInsStr
= aInsStr
.copy( 0, nInsLen
- 2 ); // Skip parentheses
1732 pView
->InsertText( aInsStr
);
1734 if ( bDoParen
) // Put cursor between parentheses
1736 aSel
= pView
->GetSelection();
1737 --aSel
.start
.nIndex
;
1739 pView
->SetSelection(aSel
);
1741 rParInserted
= true;
1747 void ScInputHandler::PasteFunctionData()
1749 if (pFormulaData
&& miAutoPosFormula
!= pFormulaData
->end())
1751 const ScTypedStrData
& rData
= *miAutoPosFormula
;
1752 OUString aInsert
= rData
.GetString();
1753 if (aInsert
[aInsert
.getLength()-1] == cParenthesesReplacement
)
1754 aInsert
= OUString::Concat(aInsert
.subView( 0, aInsert
.getLength()-1)) + "()";
1755 bool bParInserted
= false;
1757 DataChanging(); // Cannot be new
1758 completeFunction( pTopView
, aInsert
, bParInserted
);
1759 completeFunction( pTableView
, aInsert
, bParInserted
);
1769 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1770 if (comphelper::LibreOfficeKit::isActive() && pTopView
&& pInputWin
)
1771 pInputWin
->TextGrabFocus();
1773 pActiveView
->ShowCursor();
1776 void ScInputHandler::LOKPasteFunctionData(const OUString
& rFunctionName
)
1778 // in case we have no top view try to create it
1779 if (!pTopView
&& pInputWin
)
1781 ScInputMode eCurMode
= eMode
;
1782 SetMode(SC_INPUT_TOP
);
1787 EditView
* pEditView
= pTopView
? pTopView
: pTableView
;
1789 if (!pActiveViewSh
|| !pEditView
)
1794 EditEngine
const& rEditEngine
= pEditView
->getEditEngine();
1796 aFormula
= rEditEngine
.GetText(0);
1797 /* TODO: LOK: are you sure you want '+' and '-' let start formulas with
1798 * function names? That was meant for "data typist" numeric keyboard
1800 bEdit
= aFormula
.getLength() > 1 && (aFormula
[0] == '=' || aFormula
[0] == '+' || aFormula
[0] == '-');
1805 OUString
aNewFormula('=');
1806 if ( aFormula
.startsWith("=") )
1807 aNewFormula
= aFormula
;
1809 InputReplaceSelection( aNewFormula
);
1815 ScTypedCaseStrSet::const_iterator aPos
= findText(*pFormulaData
, pFormulaData
->begin(), rFunctionName
, aNew
, /* backward = */false);
1817 if (aPos
!= pFormulaData
->end())
1819 miAutoPosFormula
= aPos
;
1820 PasteFunctionData();
1825 void ScTabViewShell::LOKSendFormulabarUpdate(EditView
* pActiveView
,
1826 const OUString
& rText
,
1827 const ESelection
& rSelection
)
1829 OUString aSelection
;
1832 aSelection
= OUString::number(pActiveView
->GetPosWithField(0, rSelection
.start
.nIndex
)) + ";" +
1833 OUString::number(pActiveView
->GetPosWithField(0, rSelection
.end
.nIndex
)) + ";" +
1834 OUString::number(rSelection
.start
.nPara
) + ";" + OUString::number(rSelection
.end
.nPara
);
1838 aSelection
= OUString::number(rSelection
.start
.nIndex
) + ";" + OUString::number(rSelection
.end
.nIndex
) + ";" +
1839 OUString::number(rSelection
.start
.nPara
) + ";" + OUString::number(rSelection
.end
.nPara
);
1842 sal_uInt64 nCurrentShellId
= reinterpret_cast<sal_uInt64
>(this);
1844 // We can get three updates per keystroke, StartExtTextInput, ExtTextInput and PostExtTextInput
1845 // Skip duplicate updates. Be conservative and don't skip duplicates that are 5+ seconds
1847 std::chrono::steady_clock::time_point now
= std::chrono::steady_clock::now();
1848 if (maSendFormulabarUpdate
.m_nShellId
== nCurrentShellId
&&
1849 maSendFormulabarUpdate
.m_aText
== rText
&&
1850 maSendFormulabarUpdate
.m_aSelection
== aSelection
&&
1851 std::chrono::duration_cast
<std::chrono::seconds
>(
1852 now
- maSendFormulabarUpdate
.m_nTimeStamp
) < std::chrono::seconds(5))
1857 maSendFormulabarUpdate
.m_nShellId
= nCurrentShellId
;
1858 maSendFormulabarUpdate
.m_aText
= rText
;
1859 maSendFormulabarUpdate
.m_aSelection
= aSelection
;
1860 maSendFormulabarUpdate
.m_nTimeStamp
= now
;
1861 maSendFormulabarUpdate
.Send();
1864 void ScTabViewShell::SendFormulabarUpdate::Send()
1866 std::unique_ptr
<jsdialog::ActionDataMap
> pData
= std::make_unique
<jsdialog::ActionDataMap
>();
1867 (*pData
)["action_type"_ostr
] = "setText";
1868 (*pData
)["text"_ostr
] = m_aText
;
1869 (*pData
)["selection"_ostr
] = m_aSelection
;
1870 OUString sWindowId
= OUString::number(m_nShellId
) + "formulabar";
1871 jsdialog::SendAction(sWindowId
, u
"sc_input_window"_ustr
, std::move(pData
));
1874 // Calculate selection and display as tip help
1875 static OUString
lcl_Calculate( const OUString
& rFormula
, ScDocument
& rDoc
, const ScAddress
&rPos
)
1877 //TODO: Merge with ScFormulaDlg::CalcValue and move into Document!
1878 // Quotation marks for Strings are only inserted here.
1880 if(rFormula
.isEmpty())
1883 std::optional
<ScSimpleFormulaCalculator
> pCalc( std::in_place
, rDoc
, rPos
, rFormula
, false );
1885 // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range
1886 // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own
1887 bool bColRowName
= pCalc
->HasColRowName();
1890 // ColRowName in RPN code?
1891 if ( pCalc
->GetCode()->GetCodeLen() <= 1 )
1892 { // ==1: Single one is as a Parameter always a Range
1893 // ==0: It might be one, if ...
1894 OUString aBraced
= "(" + rFormula
+ ")";
1895 pCalc
.emplace( rDoc
, rPos
, aBraced
, false );
1898 bColRowName
= false;
1901 FormulaError nErrCode
= pCalc
->GetErrCode();
1902 if ( nErrCode
!= FormulaError::NONE
)
1903 return ScGlobal::GetErrorString(nErrCode
);
1905 SvNumberFormatter
& aFormatter
= *rDoc
.GetFormatTable();
1907 if ( pCalc
->IsValue() )
1909 double n
= pCalc
->GetValue();
1910 sal_uInt32 nFormat
= aFormatter
.GetStandardFormat( n
, 0,
1911 pCalc
->GetFormatType(), ScGlobal::eLnge
);
1912 aValue
= aFormatter
.GetInputLineString( n
, nFormat
);
1913 //! display OutputString but insert InputLineString
1917 OUString aStr
= pCalc
->GetString().getString();
1918 sal_uInt32 nFormat
= aFormatter
.GetStandardFormat(
1919 pCalc
->GetFormatType(), ScGlobal::eLnge
);
1921 const Color
* pColor
;
1922 aFormatter
.GetOutputString( aStr
, nFormat
,
1926 aValue
= "\"" + aValue
+ "\"";
1927 //! Escape quotation marks in String??
1931 if ( bColRowName
|| (aTestRange
.Parse(rFormula
, rDoc
) & ScRefFlags::VALID
) )
1937 void ScInputHandler::FormulaPreview()
1940 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1941 if ( pActiveView
&& pActiveViewSh
)
1943 OUString aPart
= pActiveView
->GetSelected();
1944 if (aPart
.isEmpty())
1945 aPart
= mpEditEngine
->GetText(0);
1946 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
1947 aValue
= lcl_Calculate( aPart
, rDoc
, aCursorPos
);
1950 if (!aValue
.isEmpty())
1952 ShowTip( aValue
); // Display as QuickHelp
1953 aManualTip
= aValue
; // Set after ShowTip
1955 miAutoPosFormula
= pFormulaData
->end();
1957 miAutoPosColumn
= pColumnData
->end();
1961 void ScInputHandler::PasteManualTip()
1963 // Three dots at the end -> Range reference -> do not insert
1964 // FIXME: Once we have matrix constants, we can change this
1965 sal_Int32 nTipLen
= aManualTip
.getLength();
1966 sal_uInt32
const nTipLen2(sal::static_int_cast
<sal_uInt32
>(nTipLen
));
1967 if ( nTipLen
&& ( nTipLen
< 3 || aManualTip
.subView( nTipLen2
-3 ) != u
"..." ) )
1969 DataChanging(); // Cannot be new
1971 OUString aInsert
= aManualTip
;
1972 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
1973 if (!pActiveView
->HasSelection())
1975 // Nothing selected -> select everything
1976 sal_Int32 nOldLen
= mpEditEngine
->GetTextLen(0);
1977 ESelection
aAllSel( 0, 0, 0, nOldLen
);
1979 pTopView
->SetSelection( aAllSel
);
1981 pTableView
->SetSelection( aAllSel
);
1984 ESelection aSel
= pActiveView
->GetSelection();
1986 OSL_ENSURE( !aSel
.start
.nPara
&& !aSel
.end
.nPara
, "Too many paragraphs in Formula" );
1987 if ( !aSel
.start
.nIndex
) // Selection from the start?
1989 if ( aSel
.end
.nIndex
== mpEditEngine
->GetTextLen(0) )
1991 // Everything selected -> skip quotation marks
1992 if ( aInsert
[0] == '"' )
1993 aInsert
= aInsert
.copy(1);
1994 sal_Int32 nInsLen
= aInsert
.getLength();
1995 if ( aInsert
.endsWith("\"") )
1996 aInsert
= aInsert
.copy( 0, nInsLen
-1 );
1998 else if ( aSel
.end
.nIndex
)
2000 // Not everything selected -> do not overwrite equality sign
2001 //FIXME: Even double equality signs??
2002 aSel
.start
.nIndex
= 1;
2004 pTopView
->SetSelection( aSel
);
2006 pTableView
->SetSelection( aSel
);
2010 pTopView
->InsertText( aInsert
, true );
2012 pTableView
->InsertText( aInsert
, true );
2020 void ScInputHandler::ResetAutoPar()
2025 void ScInputHandler::AutoParAdded()
2027 ++nAutoPar
; // Closing parenthesis can be overwritten
2030 bool ScInputHandler::CursorAtClosingPar()
2032 // Test if the cursor is before a closing parenthesis
2033 // Selection from SetReference has been removed before
2034 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2035 if ( pActiveView
&& !pActiveView
->HasSelection() && bFormulaMode
)
2037 ESelection aSel
= pActiveView
->GetSelection();
2038 sal_Int32 nPos
= aSel
.start
.nIndex
;
2039 OUString aFormula
= mpEditEngine
->GetText(0);
2040 if ( nPos
< aFormula
.getLength() && aFormula
[nPos
] == ')' )
2046 void ScInputHandler::SkipClosingPar()
2048 // this is called when a ')' is typed and the cursor is before a ')'
2049 // that can be overwritten -> just set the cursor behind the ')'
2051 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2054 ESelection aSel
= pActiveView
->GetSelection();
2055 ++aSel
.start
.nIndex
;
2058 // this is in a formula (only one paragraph), so the selection
2059 // can be used directly for the TopView
2062 pTopView
->SetSelection( aSel
);
2064 pTableView
->SetSelection( aSel
);
2067 OSL_ENSURE(nAutoPar
, "SkipClosingPar: count is wrong");
2073 void ScInputHandler::GetColData()
2075 if ( !pActiveViewSh
)
2078 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
2081 pColumnData
->clear();
2083 pColumnData
.reset( new ScTypedCaseStrSet
);
2085 std::vector
<ScTypedStrData
> aEntries
;
2086 rDoc
.GetDataEntries(
2087 aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab(), aEntries
);
2088 if (!aEntries
.empty())
2089 pColumnData
->insert(aEntries
.begin(), aEntries
.end());
2091 miAutoPosColumn
= pColumnData
->end();
2094 void ScInputHandler::UseColData() // When typing
2096 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2097 if ( !(pActiveView
&& pColumnData
) )
2100 // Only change when cursor is at the end
2101 ESelection aSel
= pActiveView
->GetSelection();
2104 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
2105 if (aSel
.end
.nPara
+ 1 != nParCnt
)
2108 sal_Int32 nParLen
= mpEditEngine
->GetTextLen(aSel
.end
.nPara
);
2109 if (aSel
.end
.nIndex
!= nParLen
)
2112 OUString aText
= GetEditText(mpEditEngine
.get());
2113 if (aText
.isEmpty())
2116 std::vector
< OUString
> aResultVec
;
2118 sal_Int32 nLongestPrefixLen
= 0;
2119 miAutoPosColumn
= pColumnData
->end();
2120 mbPartialPrefix
= false;
2121 miAutoPosColumn
= findTextAll(*pColumnData
, miAutoPosColumn
, aText
, aResultVec
, false, &nLongestPrefixLen
);
2123 if (nLongestPrefixLen
<= 0 || aResultVec
.empty())
2126 if (aResultVec
.size() > 1)
2128 mbPartialPrefix
= true;
2129 bUseTab
= true; // Allow Ctrl (+ Shift + ) + TAB cycling.
2130 miAutoPosColumn
= pColumnData
->end();
2132 // Display the rest of longest common prefix as suggestion.
2133 aNew
= aResultVec
[0].copy(0, nLongestPrefixLen
);
2137 aNew
= aResultVec
[0];
2140 // Strings can contain line endings (e.g. due to dBase import),
2141 // which would result in multiple paragraphs here, which is not desirable.
2142 //! Then GetExactMatch doesn't work either
2143 lcl_RemoveLineEnd( aNew
);
2145 // Keep paragraph, just append the rest
2146 //! Exact replacement in EnterHandler !!!
2147 // One Space between paragraphs:
2148 sal_Int32 nEdLen
= mpEditEngine
->GetTextLen() + nParCnt
- 1;
2149 OUString aIns
= aNew
.copy(nEdLen
);
2151 // Selection must be "backwards", so the cursor stays behind the last
2153 ESelection
aSelection( aSel
.end
.nPara
, aSel
.end
.nIndex
+ aIns
.getLength(),
2154 aSel
.end
.nPara
, aSel
.end
.nIndex
);
2156 // When editing in input line, apply to both edit views
2159 pTableView
->InsertText( aIns
);
2160 pTableView
->SetSelection( aSelection
);
2164 pTopView
->InsertText( aIns
);
2165 pTopView
->SetSelection( aSelection
);
2168 aAutoSearch
= aText
; // To keep searching - nAutoPos is set
2171 void ScInputHandler::NextAutoEntry( bool bBack
)
2173 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2174 if ( pActiveView
&& pColumnData
)
2176 if (!aAutoSearch
.isEmpty())
2178 // Is the selection still valid (could be changed via the mouse)?
2179 ESelection aSel
= pActiveView
->GetSelection();
2181 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
2182 if ( aSel
.end
.nPara
+1 == nParCnt
&& aSel
.start
.nPara
== aSel
.end
.nPara
)
2184 OUString aText
= GetEditText(mpEditEngine
.get());
2185 sal_Int32 nSelLen
= aSel
.end
.nIndex
- aSel
.start
.nIndex
;
2186 sal_Int32 nParLen
= mpEditEngine
->GetTextLen( aSel
.end
.nPara
);
2187 if ( aSel
.end
.nIndex
== nParLen
&& aText
.getLength() == aAutoSearch
.getLength() + nSelLen
)
2190 ScTypedCaseStrSet::const_iterator itNew
=
2191 findText(*pColumnData
, miAutoPosColumn
, aAutoSearch
, aNew
, bBack
);
2193 if (itNew
!= pColumnData
->end())
2196 miAutoPosColumn
= itNew
;
2197 bInOwnChange
= true; // disable ModifyHdl (reset below)
2198 mbPartialPrefix
= false;
2200 lcl_RemoveLineEnd( aNew
);
2201 OUString aIns
= aNew
.copy(aAutoSearch
.getLength());
2203 // when editing in input line, apply to both edit views
2206 pTableView
->DeleteSelected();
2207 pTableView
->InsertText( aIns
);
2208 pTableView
->SetSelection( ESelection(
2209 aSel
.end
.nPara
, aSel
.start
.nIndex
+ aIns
.getLength(),
2210 aSel
.end
.nPara
, aSel
.start
.nIndex
) );
2214 pTopView
->DeleteSelected();
2215 pTopView
->InsertText( aIns
);
2216 pTopView
->SetSelection( ESelection(
2217 aSel
.end
.nPara
, aSel
.start
.nIndex
+ aIns
.getLength(),
2218 aSel
.end
.nPara
, aSel
.start
.nIndex
) );
2221 bInOwnChange
= false;
2228 // For Tab, HideCursor was always called first
2230 pActiveView
->ShowCursor();
2233 // Highlight parentheses
2234 void ScInputHandler::UpdateParenthesis()
2237 //TODO: Can we disable parentheses highlighting per parentheses?
2238 bool bFound
= false;
2239 if ( bFormulaMode
&& eMode
!= SC_INPUT_TOP
)
2241 if ( pTableView
&& !pTableView
->HasSelection() ) // Selection is always at the bottom
2243 ESelection aSel
= pTableView
->GetSelection();
2244 if (aSel
.start
.nIndex
)
2246 // Examine character left to the cursor
2247 sal_Int32 nPos
= aSel
.start
.nIndex
- 1;
2248 OUString aFormula
= mpEditEngine
->GetText(aSel
.start
.nPara
);
2249 sal_Unicode c
= aFormula
[nPos
];
2250 if ( c
== '(' || c
== ')' )
2252 // Note this matches only within one paragraph.
2253 sal_Int32 nOther
= lcl_MatchParenthesis( aFormula
, nPos
);
2256 SfxItemSet
aSet( mpEditEngine
->GetEmptyItemSet() );
2257 aSet
.Put( SvxWeightItem( WEIGHT_BOLD
, EE_CHAR_WEIGHT
) );
2259 //! Distinguish if cell is already highlighted!!!!
2260 if (bParenthesisShown
)
2262 // Remove old highlighting
2263 sal_Int32 nCount
= mpEditEngine
->GetParagraphCount();
2264 for (sal_Int32 i
=0; i
<nCount
; i
++)
2265 mpEditEngine
->RemoveCharAttribs( i
, EE_CHAR_WEIGHT
);
2268 ESelection
aSelThis(aSel
.start
.nPara
, nPos
, aSel
.start
.nPara
, nPos
+ 1);
2269 mpEditEngine
->QuickSetAttribs( aSet
, aSelThis
);
2270 ESelection
aSelOther(aSel
.start
.nPara
, nOther
, aSel
.start
.nPara
, nOther
+ 1);
2271 mpEditEngine
->QuickSetAttribs( aSet
, aSelOther
);
2273 // Dummy InsertText for Update and Paint (selection is empty)
2274 pTableView
->InsertText( OUString() );
2281 // mark parenthesis right of cursor if it will be overwritten (nAutoPar)
2282 // with different color (COL_LIGHTBLUE) ??
2286 // Remove old highlighting, if no new one is set
2287 if ( bParenthesisShown
&& !bFound
&& pTableView
)
2289 sal_Int32 nCount
= mpEditEngine
->GetParagraphCount();
2290 for (sal_Int32 i
=0; i
<nCount
; i
++)
2291 pTableView
->RemoveCharAttribs( i
, EE_CHAR_WEIGHT
);
2294 bParenthesisShown
= bFound
;
2297 void ScInputHandler::ViewShellGone(const ScTabViewShell
* pViewSh
) // Executed synchronously!
2299 if ( pViewSh
== pActiveViewSh
)
2302 pLastPattern
= nullptr;
2305 ScModule
* mod
= ScModule::get();
2306 if ( pViewSh
== pRefViewSh
)
2308 //! The input from the EnterHandler does not arrive anymore
2309 // We end the EditMode anyways
2311 bFormulaMode
= false;
2312 pRefViewSh
= nullptr;
2313 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2314 mod
->SetRefInputHdl(nullptr);
2316 pInputWin
->SetFormulaMode(false);
2317 UpdateAutoCorrFlag();
2320 pActiveViewSh
= dynamic_cast<ScTabViewShell
*>( SfxViewShell::Current() );
2322 if ( pActiveViewSh
&& pActiveViewSh
== pViewSh
)
2324 OSL_FAIL("pActiveViewSh is gone");
2325 pActiveViewSh
= nullptr;
2328 if (mod
->GetInputOptions().GetTextWysiwyg())
2329 UpdateRefDevice(); // Don't keep old document's printer as RefDevice
2332 void ScInputHandler::UpdateActiveView()
2334 ImplCreateEditEngine();
2336 // #i20588# Don't rely on focus to find the active edit view. Instead, the
2337 // active pane at the start of editing is now stored (GetEditActivePart).
2338 // GetActiveWin (the currently active pane) fails for ref input across the
2339 // panes of a split view.
2341 vcl::Window
* pShellWin
= pActiveViewSh
?
2342 pActiveViewSh
->GetWindowByPos( pActiveViewSh
->GetViewData().GetEditActivePart() ) :
2345 sal_uInt16 nCount
= mpEditEngine
->GetViewCount();
2348 pTableView
= mpEditEngine
->GetView();
2349 for (sal_uInt16 i
=1; i
<nCount
; i
++)
2351 EditView
* pThis
= mpEditEngine
->GetView(i
);
2352 vcl::Window
* pWin
= pThis
->GetWindow();
2353 if ( pWin
==pShellWin
)
2358 pTableView
= nullptr;
2360 // setup the pTableView editeng for tiled rendering to get cursor and selections
2361 if (pTableView
&& pActiveViewSh
)
2363 if (comphelper::LibreOfficeKit::isActive())
2365 pTableView
->RegisterViewShell(pActiveViewSh
);
2369 if (pInputWin
&& (eMode
== SC_INPUT_TOP
|| eMode
== SC_INPUT_TABLE
))
2371 // tdf#71409: Always create the edit engine instance for the input
2372 // window, in order to properly manage accessibility events.
2373 pTopView
= pInputWin
->GetEditView();
2374 if (eMode
!= SC_INPUT_TOP
)
2381 void ScInputHandler::SetInputWindow( ScInputWindow
* pNew
)
2386 void ScInputHandler::StopInputWinEngine( bool bAll
)
2388 if (pInputWin
&& !pInputWin
->isDisposed())
2389 pInputWin
->StopEditEngine( bAll
);
2391 pTopView
= nullptr; // invalid now
2394 EditView
* ScInputHandler::GetActiveView()
2397 return pTopView
? pTopView
: pTableView
;
2400 void ScInputHandler::ForgetLastPattern()
2402 pLastPattern
= nullptr;
2403 if ( !pLastState
&& pActiveViewSh
)
2404 pActiveViewSh
->UpdateInputHandler( true ); // Get status again
2406 NotifyChange( pLastState
.get(), true );
2409 void ScInputHandler::UpdateAdjust( sal_Unicode cTyped
)
2411 SvxAdjust eSvxAdjust
;
2412 switch (eAttrAdjust
)
2414 case SvxCellHorJustify::Standard
:
2416 bool bNumber
= false;
2417 if (cTyped
) // Restarted
2418 bNumber
= (cTyped
>='0' && cTyped
<='9'); // Only ciphers are numbers
2419 else if ( pActiveViewSh
)
2421 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
2422 bNumber
= ( rDoc
.GetCellType( aCursorPos
) == CELLTYPE_VALUE
);
2424 eSvxAdjust
= bNumber
? SvxAdjust::Right
: SvxAdjust::Left
;
2427 case SvxCellHorJustify::Block
:
2428 eSvxAdjust
= SvxAdjust::Block
;
2430 case SvxCellHorJustify::Center
:
2431 eSvxAdjust
= SvxAdjust::Center
;
2433 case SvxCellHorJustify::Right
:
2434 eSvxAdjust
= SvxAdjust::Right
;
2436 default: // SvxCellHorJustify::Left
2437 eSvxAdjust
= SvxAdjust::Left
;
2441 bool bAsianVertical
= pLastPattern
&&
2442 pLastPattern
->GetItem( ATTR_STACKED
).GetValue() &&
2443 pLastPattern
->GetItem( ATTR_VERTICAL_ASIAN
).GetValue();
2444 if ( bAsianVertical
)
2446 // Always edit at top of cell -> LEFT when editing vertically
2447 eSvxAdjust
= SvxAdjust::Left
;
2450 pEditDefaults
->Put( SvxAdjustItem( eSvxAdjust
, EE_PARA_JUST
) );
2451 mpEditEngine
->SetDefaults( *pEditDefaults
);
2453 if ( pActiveViewSh
)
2455 pActiveViewSh
->GetViewData().SetEditAdjust( eSvxAdjust
);
2457 mpEditEngine
->SetVertical( bAsianVertical
);
2460 void ScInputHandler::RemoveAdjust()
2462 // Delete hard alignment attributes
2463 bool bUndo
= mpEditEngine
->IsUndoEnabled();
2465 mpEditEngine
->EnableUndo( false );
2467 // Non-default paragraph attributes (e.g. from clipboard)
2468 // must be turned into character attributes
2469 mpEditEngine
->RemoveParaAttribs();
2472 mpEditEngine
->EnableUndo( true );
2476 void ScInputHandler::RemoveRangeFinder()
2478 // Delete pRangeFindList and colors
2479 mpEditEngine
->SetUpdateLayout(false);
2480 sal_Int32 nCount
= mpEditEngine
->GetParagraphCount(); // Could just have been inserted
2481 for (sal_Int32 i
=0; i
<nCount
; i
++)
2482 mpEditEngine
->RemoveCharAttribs( i
, EE_CHAR_COLOR
);
2483 mpEditEngine
->SetUpdateLayout(true);
2485 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2486 pActiveView
->ShowCursor( false );
2488 DeleteRangeFinder(); // Deletes the list and the labels on the table
2491 bool ScInputHandler::StartTable( sal_Unicode cTyped
, bool bFromCommand
, bool bInputActivated
,
2492 ScEditEngineDefaulter
* pTopEngine
)
2494 bool bNewTable
= false;
2501 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocShell()->GetDocument();
2503 if (!rDoc
.ValidCol(aCursorPos
.Col()))
2506 ImplCreateEditEngine();
2511 const ScMarkData
& rMark
= pActiveViewSh
->GetViewData().GetMarkData();
2512 ScEditableTester aTester
;
2513 if ( rMark
.IsMarked() || rMark
.IsMultiMarked() )
2514 aTester
.TestSelection( rDoc
, rMark
);
2516 aTester
.TestSelectedBlock(
2517 rDoc
, aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Col(), aCursorPos
.Row(), rMark
);
2519 bool bStartInputMode
= !(pActiveViewSh
->GetViewShell() && pActiveViewSh
->GetViewShell()->IsLokReadOnlyView());
2521 if (!aTester
.IsEditable())
2524 // We allow read-only input mode activation regardless
2525 // whether it's part of an array or not or whether explicit cell
2526 // activation is requested (double-click or F2) or a click in input
2528 bool bShowError
= (!bInputActivated
|| !aTester
.GetMessageId() || aTester
.GetMessageId() != STR_PROTECTIONERR
) &&
2529 !pActiveViewSh
->GetViewData().GetDocShell()->IsReadOnly();
2532 eMode
= SC_INPUT_NONE
;
2533 StopInputWinEngine( true );
2534 UpdateFormulaMode();
2535 if ( pActiveViewSh
&& ( !bFromCommand
|| !bCommandErrorShown
) )
2537 // Prevent repeated error messages for the same cell from command events
2538 // (for keyboard events, multiple messages are wanted).
2539 // Set the flag before showing the error message because the command handler
2540 // for the next IME command may be called when showing the dialog.
2542 bCommandErrorShown
= true;
2544 pActiveViewSh
->GetActiveWin()->GrabFocus();
2545 pActiveViewSh
->ErrorMessage(aTester
.GetMessageId());
2547 bStartInputMode
= false;
2551 if (bStartInputMode
)
2553 // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise)
2554 mpEditEngine
->SetUpdateLayout( false );
2556 // Take over attributes in EditEngine
2557 const ScPatternAttr
* pPattern
= rDoc
.GetPattern( aCursorPos
.Col(),
2560 if (!ScPatternAttr::areSame(pPattern
, pLastPattern
))
2563 const SfxItemSet
& rAttrSet
= pPattern
->GetItemSet();
2565 if ( const SfxUInt32Item
* pItem
= rAttrSet
.GetItemIfSet( ATTR_VALUE_FORMAT
) )
2567 sal_uInt32 nFormat
= pItem
->GetValue();
2568 if (SvNumFormatType::PERCENT
== rDoc
.GetFormatTable()->GetType( nFormat
))
2569 nCellPercentFormatDecSep
= rDoc
.GetFormatTable()->GetFormatDecimalSep( nFormat
).toChar();
2571 nCellPercentFormatDecSep
= 0;
2574 nCellPercentFormatDecSep
= 0; // Default: no percent
2576 // Validity specified?
2577 if ( const SfxUInt32Item
* pItem
= rAttrSet
.GetItemIfSet( ATTR_VALIDDATA
) )
2578 nValidation
= pItem
->GetValue();
2582 // EditEngine Defaults
2583 // In no case SetParaAttribs, because the EditEngine might already
2584 // be filled (for Edit cells).
2585 // SetParaAttribs would change the content.
2587 //! The SetDefaults is now (since MUST/src602
2588 //! EditEngine changes) implemented as a SetParaAttribs.
2591 pPattern
->FillEditItemSet( pEditDefaults
.get() );
2592 mpEditEngine
->SetDefaults( *pEditDefaults
);
2593 pLastPattern
= pPattern
;
2594 bLastIsSymbol
= pPattern
->IsSymbolFont();
2596 // Background color must be known for automatic font color.
2597 // For transparent cell background, the document background color must be used.
2599 Color aBackCol
= pPattern
->GetItem( ATTR_BACKGROUND
).GetColor();
2600 ScModule
* pScMod
= ScModule::get();
2601 if ( aBackCol
.IsTransparent() ||
2602 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2603 aBackCol
= pScMod
->GetColorConfig().GetColorValue(svtools::DOCCOLOR
).nColor
;
2604 mpEditEngine
->SetBackgroundColor( aBackCol
);
2607 eAttrAdjust
= pPattern
->GetItem(ATTR_HOR_JUSTIFY
).GetValue();
2608 if ( eAttrAdjust
== SvxCellHorJustify::Repeat
&&
2609 pPattern
->GetItem(ATTR_LINEBREAK
).GetValue() )
2611 // #i31843# "repeat" with "line breaks" is treated as default alignment
2612 eAttrAdjust
= SvxCellHorJustify::Standard
;
2618 // Necessary to sync SvxAutoCorrect behavior. This has to be
2619 // done before InitRangeFinder() below.
2620 MergeLanguageAttributes( *pTopEngine
);
2623 // UpdateSpellSettings enables online spelling if needed
2624 // -> also call if attributes are unchanged
2625 UpdateSpellSettings( true ); // uses pLastPattern
2631 mpEditEngine
->SetTextCurrentDefaults(aCurrentText
);
2632 aStr
= aCurrentText
;
2634 aCurrentText
.clear();
2637 aStr
= GetEditText(mpEditEngine
.get());
2639 // cTyped!=0 is overtyping, not editing.
2640 mbEditingExistingContent
= !cTyped
&& !aStr
.isEmpty();
2642 if (aStr
.startsWith("{=") && aStr
.endsWith("}") ) // Matrix formula?
2644 aStr
= aStr
.copy(1, aStr
.getLength() -2);
2645 mpEditEngine
->SetTextCurrentDefaults(aStr
);
2647 pInputWin
->SetTextString(aStr
, true);
2650 UpdateAdjust( cTyped
);
2652 if (ScModule::get()->GetAppOptions().GetAutoComplete())
2655 if (!cTyped
&& !bCreatingFuncView
&& StartsLikeFormula(aStr
))
2656 InitRangeFinder(aStr
); // Formula is being edited -> RangeFinder
2658 bNewTable
= true; // -> PostEditView Call
2662 if (!bProtected
&& pInputWin
)
2663 pInputWin
->SetOkCancelMode();
2668 void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter
& rDestEngine
) const
2670 const SfxItemSet
& rSrcSet
= mpEditEngine
->GetDefaults();
2671 rDestEngine
.SetDefaultItem( rSrcSet
.Get( EE_CHAR_LANGUAGE
));
2672 rDestEngine
.SetDefaultItem( rSrcSet
.Get( EE_CHAR_LANGUAGE_CJK
));
2673 rDestEngine
.SetDefaultItem( rSrcSet
.Get( EE_CHAR_LANGUAGE_CTL
));
2676 static void lcl_SetTopSelection( EditView
* pEditView
, ESelection
& rSel
)
2678 OSL_ENSURE( rSel
.start
.nPara
==0 && rSel
.end
.nPara
==0, "SetTopSelection: Para != 0" );
2680 EditEngine
& rEngine
= pEditView
->getEditEngine();
2681 sal_Int32 nCount
= rEngine
.GetParagraphCount();
2684 sal_Int32 nParLen
= rEngine
.GetTextLen(rSel
.start
.nPara
);
2685 while (rSel
.start
.nIndex
> nParLen
&& rSel
.start
.nPara
+ 1 < nCount
)
2687 rSel
.start
.nIndex
-= nParLen
+ 1; // Including space from line break
2688 nParLen
= rEngine
.GetTextLen(++rSel
.start
.nPara
);
2691 nParLen
= rEngine
.GetTextLen(rSel
.end
.nPara
);
2692 while (rSel
.end
.nIndex
> nParLen
&& rSel
.end
.nPara
+ 1 < nCount
)
2694 rSel
.end
.nIndex
-= nParLen
+ 1; // Including space from line break
2695 nParLen
= rEngine
.GetTextLen(++rSel
.end
.nPara
);
2699 ESelection aSel
= pEditView
->GetSelection();
2702 pEditView
->SetSelection( rSel
);
2705 void ScInputHandler::SyncViews( const EditView
* pSourceView
)
2709 bool bSelectionForTopView
= false;
2710 if (pTopView
&& pTopView
!= pSourceView
)
2711 bSelectionForTopView
= true;
2712 bool bSelectionForTableView
= false;
2713 if (pTableView
&& pTableView
!= pSourceView
)
2714 bSelectionForTableView
= true;
2715 if (bSelectionForTopView
|| bSelectionForTableView
)
2717 ESelection
aSel(pSourceView
->GetSelection());
2718 if (bSelectionForTopView
)
2719 pTopView
->SetSelection(aSel
);
2720 if (bSelectionForTableView
)
2721 lcl_SetTopSelection(pTableView
, aSel
);
2724 // Only sync selection from topView if we are actually editing there
2725 else if (pTopView
&& pTableView
)
2727 ESelection
aSel(pTopView
->GetSelection());
2728 lcl_SetTopSelection( pTableView
, aSel
);
2732 IMPL_LINK_NOARG(ScInputHandler
, ModifyHdl
, LinkParamNone
*, void)
2734 if ( !bInOwnChange
&& ( eMode
==SC_INPUT_TYPE
|| eMode
==SC_INPUT_TABLE
) &&
2735 mpEditEngine
&& mpEditEngine
->IsUpdateLayout() && pInputWin
)
2737 // Update input line from ModifyHdl for changes that are not
2738 // wrapped by DataChanging/DataChanged calls (like Drag&Drop)
2739 OUString
aText(ScEditUtil::GetMultilineString(*mpEditEngine
));
2740 lcl_RemoveTabs(aText
);
2741 pInputWin
->SetTextString(aText
, true);
2746 * @return true means new view created
2748 bool ScInputHandler::DataChanging( sal_Unicode cTyped
, bool bFromCommand
)
2751 pActiveViewSh
->GetViewData().SetPasteMode( ScPasteFlags::NONE
);
2752 bInOwnChange
= true; // disable ModifyHdl (reset in DataChanged)
2754 if ( eMode
== SC_INPUT_NONE
)
2755 return StartTable( cTyped
, bFromCommand
, false, nullptr );
2760 void ScInputHandler::DataChanged( bool bFromTopNotify
, bool bSetModified
)
2762 ImplCreateEditEngine();
2764 if (eMode
==SC_INPUT_NONE
)
2765 eMode
= SC_INPUT_TYPE
;
2767 if ( eMode
== SC_INPUT_TOP
&& pTopView
&& !bFromTopNotify
)
2769 // table EditEngine is formatted below, input line needs formatting after paste
2770 // #i20282# not when called from the input line's modify handler
2771 pTopView
->getEditEngine().QuickFormatDoc( true );
2773 // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it
2774 // can't safely access the EditEngine's current view, so the cursor has to be
2775 // shown again here.
2776 pTopView
->ShowCursor();
2783 if ( pRangeFindList
&& !bInRangeUpdate
)
2784 RemoveRangeFinder(); // Delete attributes and labels
2786 UpdateParenthesis(); // Highlight parentheses anew
2788 const bool bUpdateKit
= comphelper::LibreOfficeKit::isActive() && pActiveViewSh
&& pInputWin
;
2790 if (eMode
==SC_INPUT_TYPE
|| eMode
==SC_INPUT_TABLE
)
2794 aText
= ScEditUtil::GetMultilineString(*mpEditEngine
);
2796 aText
= GetEditText(mpEditEngine
.get());
2797 lcl_RemoveTabs(aText
);
2801 // If we will end up updating LoKit at the end, we can skip it here
2802 pInputWin
->SetTextString(aText
, !bUpdateKit
);
2806 // If the cursor is before the end of a paragraph, parts are being pushed to
2807 // the right (independently from the eMode) -> Adapt View!
2808 // If the cursor is at the end, the StatusHandler of the ViewData is sufficient.
2810 // First make sure the status handler is called now if the cursor
2811 // is outside the visible area
2812 mpEditEngine
->QuickFormatDoc();
2814 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2816 if (pActiveView
&& pActiveViewSh
)
2818 ScViewData
& rViewData
= pActiveViewSh
->GetViewData();
2820 bool bNeedGrow
= ( rViewData
.GetEditAdjust() != SvxAdjust::Left
); // Always right-aligned
2823 // Cursor before the end?
2824 aSel
= pActiveView
->GetSelection();
2826 bNeedGrow
= (aSel
.end
.nIndex
!= mpEditEngine
->GetTextLen(aSel
.end
.nPara
));
2830 bNeedGrow
= rViewData
.GetDocument().IsLayoutRTL( rViewData
.GetTabNo() );
2834 // Adjust inplace view
2835 rViewData
.EditGrowY();
2836 rViewData
.EditGrowX();
2844 aSel
= pActiveView
->GetSelection();
2846 OUString aText
= ScEditUtil::GetMultilineString(*mpEditEngine
);
2847 pActiveViewSh
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA
, aText
.toUtf8());
2848 pActiveViewSh
->LOKSendFormulabarUpdate(pActiveView
,
2853 UpdateFormulaMode();
2854 bTextValid
= false; // Changes only in the EditEngine
2855 bInOwnChange
= false;
2858 bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr
) const
2860 // For new input '+' and '-' may start the dreaded "lazy data typist"
2861 // formula input, editing existing formula content can only start with '='.
2862 return !rStr
.empty() && (rStr
[0] == '=' || (!mbEditingExistingContent
&& (rStr
[0] == '+' || rStr
[0] == '-')));
2865 void ScInputHandler::UpdateFormulaMode()
2867 SfxApplication
* pSfxApp
= SfxGetpApp();
2869 bool bIsFormula
= !bProtected
;
2872 const OUString aText
= mpEditEngine
->GetText(0);
2873 bIsFormula
= StartsLikeFormula(aText
);
2880 pActiveViewSh
->GetViewData().SetEditHighlight(true);
2881 bFormulaMode
= true;
2882 pRefViewSh
= pActiveViewSh
;
2883 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2884 ScModule
* pMod
= ScModule::get();
2885 pMod
->SetRefInputHdl(this);
2887 pInputWin
->SetFormulaMode(true);
2889 // in LOK, we always need to perform the GetFormulaData() call so
2890 // that the formula insertion works
2891 if (comphelper::LibreOfficeKit::isActive() || pMod
->GetAppOptions().GetAutoComplete())
2894 UpdateParenthesis();
2895 UpdateAutoCorrFlag();
2902 pActiveViewSh
->GetViewData().SetEditHighlight(false);
2904 bFormulaMode
= false;
2905 pRefViewSh
= nullptr;
2906 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
2907 ScModule::get()->SetRefInputHdl(nullptr);
2909 pInputWin
->SetFormulaMode(false);
2910 UpdateAutoCorrFlag();
2915 void ScInputHandler::ShowRefFrame()
2917 // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat
2918 // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh.
2919 // A local variable is used instead.
2920 ScTabViewShell
* pVisibleSh
= dynamic_cast<ScTabViewShell
*>( SfxViewShell::Current() );
2921 if ( !(pRefViewSh
&& pRefViewSh
!= pVisibleSh
) )
2924 bool bFound
= false;
2925 SfxViewFrame
& rRefFrame
= pRefViewSh
->GetViewFrame();
2926 SfxViewFrame
* pOneFrame
= SfxViewFrame::GetFirst();
2927 while ( pOneFrame
&& !bFound
)
2929 if ( pOneFrame
== &rRefFrame
)
2931 pOneFrame
= SfxViewFrame::GetNext( *pOneFrame
);
2936 // We count on Activate working synchronously here
2937 // (pActiveViewSh is set while doing so)
2938 pRefViewSh
->SetActive(); // Appear and SetViewFrame
2940 // pLastState is set correctly in the NotifyChange from the Activate
2944 OSL_FAIL("ViewFrame for reference input is not here anymore");
2948 void ScInputHandler::RemoveSelection()
2950 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
2954 ESelection aSel
= pActiveView
->GetSelection();
2955 aSel
.CollapseToEnd();
2957 pTableView
->SetSelection( aSel
);
2959 pTopView
->SetSelection( aSel
);
2962 void ScInputHandler::InvalidateAttribs()
2964 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
2968 SfxBindings
& rBindings
= pViewFrm
->GetBindings();
2970 rBindings
.Invalidate( SID_ATTR_CHAR_FONT
);
2971 rBindings
.Invalidate( SID_ATTR_CHAR_FONTHEIGHT
);
2972 rBindings
.Invalidate( SID_ATTR_CHAR_COLOR
);
2974 rBindings
.Invalidate( SID_ATTR_CHAR_WEIGHT
);
2975 rBindings
.Invalidate( SID_ATTR_CHAR_POSTURE
);
2976 rBindings
.Invalidate( SID_ATTR_CHAR_UNDERLINE
);
2977 rBindings
.Invalidate( SID_ATTR_CHAR_OVERLINE
);
2978 rBindings
.Invalidate( SID_ULINE_VAL_NONE
);
2979 rBindings
.Invalidate( SID_ULINE_VAL_SINGLE
);
2980 rBindings
.Invalidate( SID_ULINE_VAL_DOUBLE
);
2981 rBindings
.Invalidate( SID_ULINE_VAL_DOTTED
);
2983 rBindings
.Invalidate( SID_HYPERLINK_GETLINK
);
2985 rBindings
.Invalidate( SID_ATTR_CHAR_KERNING
);
2986 rBindings
.Invalidate( SID_SET_SUPER_SCRIPT
);
2987 rBindings
.Invalidate( SID_SET_SUB_SCRIPT
);
2988 rBindings
.Invalidate( SID_ATTR_CHAR_STRIKEOUT
);
2989 rBindings
.Invalidate( SID_ATTR_CHAR_SHADOWED
);
2991 rBindings
.Invalidate( SID_SAVEDOC
);
2992 rBindings
.Invalidate( SID_DOC_MODIFIED
);
2995 // --------------- public methods --------------------------------------------
2997 void ScInputHandler::SetMode( ScInputMode eNewMode
, const OUString
* pInitText
, ScEditEngineDefaulter
* pTopEngine
)
2999 if ( eMode
== eNewMode
)
3002 ImplCreateEditEngine();
3006 eMode
= SC_INPUT_NONE
;
3007 StopInputWinEngine( true );
3009 pActiveViewSh
->GetActiveWin()->GrabFocus();
3013 if (eNewMode
!= SC_INPUT_NONE
&& pActiveViewSh
)
3014 // Disable paste mode when edit mode starts.
3015 pActiveViewSh
->GetViewData().SetPasteMode( ScPasteFlags::NONE
);
3017 bInOwnChange
= true; // disable ModifyHdl (reset below)
3019 ScInputMode eOldMode
= eMode
;
3021 if (eOldMode
== SC_INPUT_TOP
&& eNewMode
!= eOldMode
)
3022 StopInputWinEngine( false );
3024 if (eMode
==SC_INPUT_TOP
|| eMode
==SC_INPUT_TABLE
)
3026 if (eOldMode
== SC_INPUT_NONE
) // not if switching between modes
3028 if (StartTable(0, false, eMode
== SC_INPUT_TABLE
, pTopEngine
))
3031 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
3037 mpEditEngine
->SetTextCurrentDefaults(*pInitText
);
3041 sal_Int32 nPara
= mpEditEngine
->GetParagraphCount()-1;
3042 sal_Int32 nLen
= mpEditEngine
->GetText(nPara
).getLength();
3043 sal_uInt16 nCount
= mpEditEngine
->GetViewCount();
3045 for (sal_uInt16 i
=0; i
<nCount
; i
++)
3047 if ( eMode
== SC_INPUT_TABLE
&& eOldMode
== SC_INPUT_TOP
)
3053 mpEditEngine
->GetView(i
)->SetSelection(ESelection(nPara
, nLen
));
3055 mpEditEngine
->GetView(i
)->ShowCursor(false);
3060 if (eMode
==SC_INPUT_TABLE
|| eMode
==SC_INPUT_TYPE
)
3063 pTableView
->SetEditEngineUpdateLayout(true);
3064 pActiveViewSh
->GetViewData().SetEditHighlight(true);
3069 pTopView
->SetEditEngineUpdateLayout(true);
3072 if (eNewMode
!= eOldMode
)
3073 UpdateFormulaMode();
3075 bInOwnChange
= false;
3079 * @return true if rString only contains digits (no autocorrect then)
3081 static bool lcl_IsNumber(std::u16string_view aString
)
3083 size_t nLen
= aString
.size();
3084 for (size_t i
=0; i
<nLen
; i
++)
3086 sal_Unicode c
= aString
[i
];
3087 if ( c
< '0' || c
> '9' )
3093 static void lcl_SelectionToEnd( EditView
* pView
)
3096 pView
->SetSelection(ESelection::AtEnd());
3099 void ScInputHandler::EnterHandler( ScEnterMode nBlockMode
, bool bBeforeSavingInLOK
)
3101 if (!mbDocumentDisposing
&& comphelper::LibreOfficeKit::isActive()
3102 && 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
->GetFrameWin();
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 // Test if valid (always with simple string)
3149 if (bModified
&& nValidation
)
3151 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3152 const ScValidationData
* pData
= rDoc
.GetValidationEntry( nValidation
);
3155 // #i67990# don't use pLastPattern in EnterHandler
3156 const ScPatternAttr
* pPattern
= rDoc
.GetPattern( aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab() );
3160 if (pData
->GetDataMode() == SC_VALID_CUSTOM
)
3162 bOk
= pData
->IsDataValidCustom( aString
, *pPattern
, aCursorPos
, ScValidationData::CustomValidationPrivateAccess() );
3166 bOk
= pData
->IsDataValid( aString
, *pPattern
, aCursorPos
);
3171 pActiveViewSh
->StopMarking(); // (the InfoBox consumes the MouseButtonUp)
3173 // tdf#125917 Release the grab that a current mouse-down event being handled
3174 // by ScTabView has put on the mouse via its SelectionEngine.
3175 // Otherwise the warning box cannot interact with the mouse
3176 if (ScTabView
* pView
= pActiveViewSh
->GetViewData().GetView())
3178 if (ScViewSelectionEngine
* pSelEngine
= pView
->GetSelEngine())
3179 pSelEngine
->ReleaseMouse();
3182 if (bBeforeSavingInLOK
)
3184 // Invalid entry but not applied to the document model.
3185 // Exit to complete the "save", leaving the edit view as it is
3186 // for the user to continue after save.
3187 bInOwnChange
= false;
3188 bInEnterHandler
= false;
3192 if (pData
->DoError(pActiveViewSh
->GetFrameWeld(), aString
, aCursorPos
))
3193 bForget
= true; // Do not take over input
3199 // Check for input into DataPilot table
3200 if ( bModified
&& !bForget
)
3202 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3203 ScDPObject
* pDPObj
= rDoc
.GetDPAtCursor( aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab() );
3206 // Any input within the DataPilot table is either a valid renaming
3207 // or an invalid action - normal cell input is always aborted
3208 pActiveViewSh
->DataPilotInput( aCursorPos
, aString
);
3213 std::vector
<editeng::MisspellRanges
> aMisspellRanges
;
3214 // UpdateLayout must be true during CompleteOnlineSpelling
3215 const bool bUpdateLayout
= mpEditEngine
->SetUpdateLayout( true );
3216 mpEditEngine
->CompleteOnlineSpelling();
3217 bool bSpellErrors
= !bFormulaMode
&& mpEditEngine
->HasOnlineSpellErrors();
3220 // #i3820# If the spell checker flags numerical input as error,
3221 // it still has to be treated as number, not EditEngine object.
3222 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3223 // #i67990# don't use pLastPattern in EnterHandler
3224 const ScPatternAttr
* pPattern
= rDoc
.GetPattern( aCursorPos
.Col(), aCursorPos
.Row(), aCursorPos
.Tab() );
3227 SvNumberFormatter
* pFormatter
= rDoc
.GetFormatTable();
3228 // without conditional format, as in ScColumn::SetString
3229 sal_uInt32 nFormat
= pPattern
->GetNumberFormat( pFormatter
);
3231 if ( pFormatter
->IsNumberFormat( aString
, nFormat
, nVal
) )
3233 bSpellErrors
= false; // ignore the spelling errors
3238 // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc).
3239 // SetUpdateLayout must come after CompleteOnlineSpelling.
3240 // The view is hidden in any case below (Broadcast).
3241 mpEditEngine
->SetUpdateLayout( false );
3243 if ( bModified
&& !bForget
) // What is being entered (text/object)?
3245 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
3249 bool bUniformAttribs
= true;
3250 SfxItemSet aPara1Attribs
= mpEditEngine
->GetAttribs(0, 0, mpEditEngine
->GetTextLen(0));
3251 for (sal_Int32 nPara
= 1; nPara
< nParCnt
; ++nPara
)
3253 SfxItemSet aPara2Attribs
= mpEditEngine
->GetAttribs(nPara
, 0, mpEditEngine
->GetTextLen(nPara
));
3254 if (!(aPara1Attribs
== aPara2Attribs
))
3256 // Paragraph format different from that of the 1st paragraph.
3257 bUniformAttribs
= false;
3262 SfxItemSet aOldAttribs
= mpEditEngine
->GetAttribs(ESelection::All());
3263 const SfxPoolItem
* pItem
= nullptr;
3265 // Find common (cell) attributes before RemoveAdjust
3266 if ( bUniformAttribs
)
3268 std::optional
<SfxItemSet
> pCommonAttrs
;
3269 for (sal_uInt16 nId
= EE_CHAR_START
; nId
<= EE_CHAR_END
; nId
++)
3271 SfxItemState eState
= aOldAttribs
.GetItemState( nId
, false, &pItem
);
3272 if ( eState
== SfxItemState::SET
&&
3273 nId
!= EE_CHAR_ESCAPEMENT
&& nId
!= EE_CHAR_PAIRKERNING
&&
3274 nId
!= EE_CHAR_KERNING
&& nId
!= EE_CHAR_XMLATTRIBS
&&
3275 *pItem
!= pEditDefaults
->Get(nId
) )
3277 if ( !pCommonAttrs
)
3278 pCommonAttrs
.emplace( mpEditEngine
->GetEmptyItemSet() );
3279 pCommonAttrs
->Put( *pItem
);
3285 ScDocument
& rDoc
= pActiveViewSh
->GetViewData().GetDocument();
3286 pCellAttrs
= std::make_unique
<ScPatternAttr
>(rDoc
.getCellAttributeHelper());
3287 pCellAttrs
->GetFromEditItemSet( &*pCommonAttrs
);
3291 // Clear ParaAttribs (including adjustment)
3294 bool bAttrib
= false; // Formatting present?
3296 // check if EditObject is needed
3301 for (sal_uInt16 nId
= EE_CHAR_START
; nId
<= EE_CHAR_END
&& !bAttrib
; nId
++)
3303 SfxItemState eState
= aOldAttribs
.GetItemState( nId
, false, &pItem
);
3304 if (eState
== SfxItemState::INVALID
)
3306 else if (eState
== SfxItemState::SET
)
3308 // Keep same items in EditEngine as in ScEditAttrTester
3309 if ( nId
== EE_CHAR_ESCAPEMENT
|| nId
== EE_CHAR_PAIRKERNING
||
3310 nId
== EE_CHAR_KERNING
|| nId
== EE_CHAR_XMLATTRIBS
)
3312 if ( *pItem
!= pEditDefaults
->Get(nId
) )
3319 SfxItemState eFieldState
= aOldAttribs
.GetItemState( EE_FEATURE_FIELD
, false );
3320 if ( eFieldState
== SfxItemState::INVALID
|| eFieldState
== SfxItemState::SET
)
3323 // Not converted characters?
3324 SfxItemState eConvState
= aOldAttribs
.GetItemState( EE_FEATURE_NOTCONV
, false );
3325 if ( eConvState
== SfxItemState::INVALID
|| eConvState
== SfxItemState::SET
)
3328 // Always recognize formulas as formulas
3329 // We still need the preceding test due to cell attributes
3333 mpEditEngine
->GetAllMisspellRanges(aMisspellRanges
);
3340 mpEditEngine
->ClearSpellErrors();
3341 pObject
= mpEditEngine
->CreateTextObject();
3343 else if (ScModule::get()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case
3345 // Perform case-matching only when the typed text is partial.
3346 if (pColumnData
&& aAutoSearch
.getLength() < aString
.getLength())
3347 aString
= getExactMatch(*pColumnData
, aString
);
3351 // Don't rely on ShowRefFrame switching the active view synchronously
3352 // execute the function directly on the correct view's bindings instead
3353 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3354 ScTabViewShell
* pExecuteSh
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
3362 pExecuteSh
->SetTabNo(aCursorPos
.Tab());
3363 pExecuteSh
->ActiveGrabFocus();
3366 bFormulaMode
= false;
3367 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
3368 ScModule::get()->SetRefInputHdl(nullptr);
3370 pInputWin
->SetFormulaMode(false);
3371 UpdateAutoCorrFlag();
3373 pRefViewSh
= nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3374 DeleteRangeFinder();
3377 bool bOldMod
= bModified
;
3381 eMode
= SC_INPUT_NONE
;
3382 StopInputWinEngine(true);
3384 // Text input (through number formats) or ApplySelectionPattern modify
3385 // the cell's attributes, so pLastPattern is no longer valid
3386 pLastPattern
= nullptr;
3388 if (bOldMod
&& !bProtected
&& !bForget
)
3390 bool bInsertPreCorrectedString
= true;
3391 // No typographic quotes in formulas
3392 if (aString
.startsWith("="))
3394 SvxAutoCorrect
* pAuto
= SvxAutoCorrCfg::Get().GetAutoCorrect();
3397 bInsertPreCorrectedString
= false;
3398 OUString
aReplace(pAuto
->GetStartDoubleQuote());
3399 if( aReplace
.isEmpty() )
3400 aReplace
= ScGlobal::getLocaleData().getDoubleQuotationMarkStart();
3401 if( aReplace
!= "\"" )
3402 aString
= aString
.replaceAll( aReplace
, "\"" );
3404 aReplace
= OUString(pAuto
->GetEndDoubleQuote());
3405 if( aReplace
.isEmpty() )
3406 aReplace
= ScGlobal::getLocaleData().getDoubleQuotationMarkEnd();
3407 if( aReplace
!= "\"" )
3408 aString
= aString
.replaceAll( aReplace
, "\"" );
3410 aReplace
= OUString(pAuto
->GetStartSingleQuote());
3411 if( aReplace
.isEmpty() )
3412 aReplace
= ScGlobal::getLocaleData().getQuotationMarkStart();
3413 if( aReplace
!= "'" )
3414 aString
= aString
.replaceAll( aReplace
, "'" );
3416 aReplace
= OUString(pAuto
->GetEndSingleQuote());
3417 if( aReplace
.isEmpty() )
3418 aReplace
= ScGlobal::getLocaleData().getQuotationMarkEnd();
3419 if( aReplace
!= "'" )
3420 aString
= aString
.replaceAll( aReplace
, "'");
3424 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint
) );
3428 SfxBindings
& rBindings
= pExecuteSh
->GetViewFrame().GetBindings();
3430 sal_uInt16 nId
= FID_INPUTLINE_ENTER
;
3431 if ( nBlockMode
== ScEnterMode::BLOCK
)
3432 nId
= FID_INPUTLINE_BLOCK
;
3433 else if ( nBlockMode
== ScEnterMode::MATRIX
)
3434 nId
= FID_INPUTLINE_MATRIX
;
3436 const SfxPoolItem
* aArgs
[2];
3439 if ( bInsertPreCorrectedString
&& aString
!= aPreAutoCorrectString
)
3441 ScInputStatusItem
aItem(FID_INPUTLINE_STATUS
,
3442 aCursorPos
, aCursorPos
, aCursorPos
,
3443 aPreAutoCorrectString
, pObject
.get());
3445 rBindings
.Execute(nId
, aArgs
);
3448 ScInputStatusItem
aItemCorrected(FID_INPUTLINE_STATUS
,
3449 aCursorPos
, aCursorPos
, aCursorPos
,
3450 aString
, pObject
.get());
3451 if ( !aMisspellRanges
.empty() )
3452 aItemCorrected
.SetMisspellRanges(&aMisspellRanges
);
3454 aArgs
[0] = &aItemCorrected
;
3455 rBindings
.Execute(nId
, aArgs
);
3458 pLastState
.reset(); // pLastState still contains the old text
3461 pSfxApp
->Broadcast( SfxHint( SfxHintId::ScKillEditView
) );
3463 if ( bOldMod
&& pExecuteSh
&& pCellAttrs
&& !bForget
)
3465 // Combine with input?
3466 pExecuteSh
->ApplySelectionPattern( *pCellAttrs
, true );
3467 pExecuteSh
->AdjustBlockHeight();
3473 nFormSelStart
= nFormSelEnd
= 0;
3476 mbEditingExistingContent
= false;
3477 bInOwnChange
= false;
3478 bInEnterHandler
= false;
3480 mpEditEngine
->SetUpdateLayout( true );
3483 void ScInputHandler::CancelHandler()
3485 bInOwnChange
= true; // Also without FormulaMode due to FunctionsAutoPilot
3487 ImplCreateEditEngine();
3490 mbPartialPrefix
= false;
3491 mbEditingExistingContent
= false;
3493 // Don't rely on ShowRefFrame switching the active view synchronously
3494 // execute the function directly on the correct view's bindings instead
3495 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3496 ScTabViewShell
* pExecuteSh
= pRefViewSh
? pRefViewSh
: pActiveViewSh
;
3503 pExecuteSh
->SetTabNo(aCursorPos
.Tab());
3504 pExecuteSh
->ActiveGrabFocus();
3506 bFormulaMode
= false;
3507 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged
) );
3508 ScModule::get()->SetRefInputHdl(nullptr);
3510 pInputWin
->SetFormulaMode(false);
3511 UpdateAutoCorrFlag();
3513 pRefViewSh
= nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3514 DeleteRangeFinder();
3517 eMode
= SC_INPUT_NONE
;
3518 StopInputWinEngine( true );
3519 SCCOL
nMaxCol(MAXCOL
);
3522 pExecuteSh
->StopEditShell();
3523 nMaxCol
= pExecuteSh
->GetViewData().GetDocument().MaxCol();
3526 aCursorPos
.Set(nMaxCol
+1,0,0); // Invalid flag
3527 mpEditEngine
->SetTextCurrentDefaults(OUString());
3529 if ( !pLastState
&& pExecuteSh
)
3530 pExecuteSh
->UpdateInputHandler( true ); // Update status again
3532 NotifyChange( pLastState
.get(), true );
3534 nFormSelStart
= nFormSelEnd
= 0;
3537 bInOwnChange
= false;
3539 if ( comphelper::LibreOfficeKit::isActive() && pExecuteSh
)
3542 std::vector
<ReferenceMark
> aReferenceMarks
;
3543 ScInputHandler::SendReferenceMarks( pActiveViewSh
, aReferenceMarks
);
3547 bool ScInputHandler::IsModalMode( const SfxObjectShell
* pDocSh
)
3549 // References to unnamed document; that doesn't work
3550 return bFormulaMode
&& pRefViewSh
3551 && pRefViewSh
->GetViewData().GetDocument().GetDocumentShell() != pDocSh
3552 && !pDocSh
->HasName();
3555 void ScInputHandler::AddRefEntry()
3557 const sal_Unicode cSep
= ScCompiler::GetNativeSymbolChar(ocSep
);
3559 if (!pTableView
&& !pTopView
)
3560 return; // E.g. FillMode
3562 DataChanging(); // Cannot be new
3565 OUString aText
= GetEditText(mpEditEngine
.get());
3566 sal_Unicode cLastChar
= 0;
3567 sal_Int32 nPos
= aText
.getLength() - 1;
3568 while (nPos
>= 0) //checking space
3570 cLastChar
= aText
[nPos
];
3571 if (cLastChar
!= ' ')
3576 bool bAppendSeparator
= (cLastChar
!= '(' && cLastChar
!= cSep
&& cLastChar
!= '=');
3577 if (bAppendSeparator
)
3580 pTableView
->InsertText( OUString(cSep
) );
3582 pTopView
->InsertText( OUString(cSep
) );
3588 void ScInputHandler::SetReference( const ScRange
& rRef
, const ScDocument
& rDoc
)
3592 const ScDocument
* pThisDoc
= nullptr;
3594 pThisDoc
= &pRefViewSh
->GetViewData().GetDocument();
3595 bool bOtherDoc
= (pThisDoc
!= &rDoc
);
3596 if (bOtherDoc
&& !rDoc
.GetDocumentShell()->HasName())
3598 // References to unnamed document; that doesn't work
3599 // SetReference should not be called, then
3606 if (!pTableView
&& !pTopView
)
3607 return; // E.g. FillMode
3609 // Never overwrite the "="!
3610 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
3611 ESelection aSel
= pActiveView
->GetSelection();
3613 if (aSel
.start
.nPara
== 0 && aSel
.start
.nIndex
== 0)
3616 DataChanging(); // Cannot be new
3618 // Turn around selection if backwards.
3621 ESelection aTabSel
= pTableView
->GetSelection();
3622 if (aTabSel
.start
.nIndex
> aTabSel
.end
.nIndex
&& aTabSel
.start
.nPara
== aTabSel
.end
.nPara
)
3625 pTableView
->SetSelection(aTabSel
);
3630 ESelection aTopSel
= pTopView
->GetSelection();
3631 if (aTopSel
.start
.nIndex
> aTopSel
.end
.nIndex
&& aTopSel
.start
.nPara
== aTopSel
.end
.nPara
)
3634 pTopView
->SetSelection(aTopSel
);
3638 // Create string from reference, in the syntax of the document being edited.
3640 const ScAddress::Details
aAddrDetails( *pThisDoc
, aCursorPos
);
3643 // Reference to other document
3644 OSL_ENSURE(rRef
.aStart
.Tab()==rRef
.aEnd
.Tab(), "nStartTab!=nEndTab");
3646 // Always 3D and absolute.
3647 OUString
aTmp(rRef
.Format(rDoc
, ScRefFlags::VALID
| ScRefFlags::TAB_ABS_3D
, aAddrDetails
));
3649 ScDocShell
* pObjSh
= rDoc
.GetDocumentShell();
3650 // #i75893# convert escaped URL of the document to something user friendly
3651 OUString aFileName
= pObjSh
->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous
);
3653 switch(aAddrDetails
.eConv
)
3655 case formula::FormulaGrammar::CONV_XL_A1
:
3656 case formula::FormulaGrammar::CONV_XL_OOX
:
3657 case formula::FormulaGrammar::CONV_XL_R1C1
:
3658 aRefStr
= "[\'" + aFileName
+ "']";
3660 case formula::FormulaGrammar::CONV_OOO
:
3662 aRefStr
= "\'" + aFileName
+ "'#";
3669 if ( rRef
.aStart
.Tab() != aCursorPos
.Tab() ||
3670 rRef
.aStart
.Tab() != rRef
.aEnd
.Tab() )
3671 // pointer-selected => absolute sheet reference
3672 aRefStr
= rRef
.Format(rDoc
, ScRefFlags::VALID
| ScRefFlags::TAB_ABS_3D
, aAddrDetails
);
3674 aRefStr
= rRef
.Format(rDoc
, ScRefFlags::VALID
, aAddrDetails
);
3676 bool bLOKShowSelect
= true;
3677 if(comphelper::LibreOfficeKit::isActive() && pRefViewSh
->GetViewData().GetRefTabNo() != pRefViewSh
->GetViewData().GetTabNo())
3678 bLOKShowSelect
= false;
3680 if (pTableView
|| pTopView
)
3683 pTableView
->InsertText( aRefStr
, true, bLOKShowSelect
);
3685 pTopView
->InsertText( aRefStr
, true, bLOKShowSelect
);
3693 void ScInputHandler::InsertFunction( const OUString
& rFuncName
, bool bAddPar
)
3695 if ( eMode
== SC_INPUT_NONE
)
3697 OSL_FAIL("InsertFunction, not during input mode");
3702 if (!pTableView
&& !pTopView
)
3703 return; // E.g. FillMode
3705 DataChanging(); // Cannot be new
3707 OUString aText
= rFuncName
;
3713 pTableView
->InsertText( aText
);
3716 ESelection aSel
= pTableView
->GetSelection();
3717 --aSel
.start
.nIndex
;
3719 pTableView
->SetSelection(aSel
);
3724 pTopView
->InsertText( aText
);
3727 ESelection aSel
= pTopView
->GetSelection();
3728 --aSel
.start
.nIndex
;
3730 pTopView
->SetSelection(aSel
);
3740 void ScInputHandler::ClearText()
3742 if ( eMode
== SC_INPUT_NONE
)
3744 OSL_FAIL("ClearText, not during input mode");
3749 if (!pTableView
&& !pTopView
)
3750 return; // E.g. FillMode
3752 DataChanging(); // Cannot be new
3756 pTableView
->getEditEngine().SetText( u
""_ustr
);
3757 pTableView
->SetSelection(ESelection());
3761 pTopView
->getEditEngine().SetText( u
""_ustr
);
3762 pTopView
->SetSelection(ESelection());
3768 bool ScInputHandler::KeyInput( const KeyEvent
& rKEvt
, bool bStartEdit
/* = false */ )
3770 vcl::KeyCode aCode
= rKEvt
.GetKeyCode();
3771 sal_uInt16 nModi
= aCode
.GetModifier();
3772 bool bShift
= aCode
.IsShift();
3773 bool bControl
= aCode
.IsMod1();
3774 bool bAlt
= aCode
.IsMod2();
3775 sal_uInt16 nCode
= aCode
.GetCode();
3776 sal_Unicode nChar
= rKEvt
.GetCharCode();
3778 if (bAlt
&& !bControl
&& nCode
!= KEY_RETURN
)
3779 // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not.
3782 // There is a partial autocomplete suggestion.
3783 // Allow its completion with right arrow key (without modifiers).
3784 if (mbPartialPrefix
&& nCode
== KEY_RIGHT
&& !bControl
&& !bShift
&& !bAlt
&&
3785 (pTopView
|| pTableView
))
3788 pTopView
->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
));
3790 pTableView
->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH
));
3792 mbPartialPrefix
= false;
3794 // Indicate that this event has been consumed and ScTabViewShell should not act on this.
3798 if (!bControl
&& nCode
== KEY_TAB
)
3800 // Normal TAB moves the cursor right.
3804 pActiveViewSh
->FindNextUnprot( bShift
, true );
3806 ScModule
* pScMod
= ScModule::get();
3807 const ScInputOptions
& rOpt
= pScMod
->GetInputOptions();
3808 const bool bKit
= comphelper::LibreOfficeKit::isActive();
3810 if ( (rOpt
.GetMoveKeepEdit() && !bKit
)
3811 || (pActiveViewSh
&& pActiveViewSh
->GetMoveKeepEdit() && bKit
) )
3812 pScMod
->SetInputMode( SC_INPUT_TABLE
);
3817 bool bInputLine
= ( eMode
==SC_INPUT_TOP
);
3821 bool bDoEnter
= false;
3826 // New line when in the input line and Shift/Ctrl-Enter is pressed,
3827 // or when in a cell and Ctrl-Enter is pressed.
3828 if ((pInputWin
&& bInputLine
&& bControl
!= bShift
) || (!bInputLine
&& bControl
&& !bShift
))
3832 else if (nModi
== 0 && nTipVisible
&& pFormulaData
&& miAutoPosFormula
!= pFormulaData
->end())
3834 PasteFunctionData();
3837 else if ( nModi
== 0 && nTipVisible
&& !aManualTip
.isEmpty() )
3844 ScEnterMode nMode
= ScEnterMode::NORMAL
;
3845 if ( bShift
&& bControl
)
3846 nMode
= ScEnterMode::MATRIX
;
3848 nMode
= ScEnterMode::BLOCK
;
3849 EnterHandler( nMode
);
3852 pActiveViewSh
->MoveCursorEnter( bShift
&& !bControl
);
3854 ScModule
* pScMod
= ScModule::get();
3855 const ScInputOptions
& rOpt
= pScMod
->GetInputOptions();
3856 const bool bKit
= comphelper::LibreOfficeKit::isActive();
3858 if ( (rOpt
.GetMoveKeepEdit() && !bKit
)
3859 || (pActiveViewSh
&& pActiveViewSh
->GetMoveKeepEdit() && bKit
) )
3860 pScMod
->SetInputMode( SC_INPUT_TABLE
);
3866 if (bControl
&& !bAlt
)
3868 if (pFormulaData
&& nTipVisible
&& miAutoPosFormula
!= pFormulaData
->end())
3871 NextFormulaEntry( bShift
);
3874 else if (pColumnData
&& bUseTab
)
3876 // Iterate through AutoInput entries
3877 NextAutoEntry( bShift
);
3888 else if( nTipVisibleSec
)
3893 else if (eMode
!= SC_INPUT_NONE
)
3902 if ( !bShift
&& !bControl
&& !bAlt
&& eMode
== SC_INPUT_TABLE
)
3904 eMode
= SC_INPUT_TYPE
;
3910 // Only execute cursor keys if already in EditMode
3911 // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator)
3912 bool bCursorKey
= EditEngine::DoesKeyMoveCursor(rKEvt
);
3913 bool bInsKey
= ( nCode
== KEY_INSERT
&& !nModi
); // Treat Insert like Cursorkeys
3914 if ( !bUsed
&& !bSkip
&& ( bDoEnter
|| EditEngine::DoesKeyChangeText(rKEvt
) ||
3915 ( eMode
!= SC_INPUT_NONE
&& ( bCursorKey
|| bInsKey
) ) ) )
3927 bool bNewView
= DataChanging( nChar
);
3929 if (bProtected
|| (pActiveViewSh
&& pActiveViewSh
->GetViewShell() && pActiveViewSh
->GetViewShell()->IsLokReadOnlyView())) // Protected cell?
3930 bUsed
= true; // Don't forward KeyEvent
3931 else // Changes allowed
3933 if (bNewView
) // Create anew
3936 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
3938 if (eMode
==SC_INPUT_NONE
)
3939 if (pTableView
|| pTopView
)
3943 if (bStartEdit
&& nCellPercentFormatDecSep
!= 0 &&
3944 ((nChar
>= '0' && nChar
<= '9') || nChar
== '-' || nChar
== nCellPercentFormatDecSep
))
3951 pTableView
->getEditEngine().SetText( aStrLoP
);
3952 if ( !aStrLoP
.isEmpty() )
3953 pTableView
->SetSelection(ESelection()); // before the '%'
3955 // Don't call SetSelection if the string is empty anyway,
3956 // to avoid breaking the bInitial handling in ScViewData::EditGrowY
3960 pTopView
->getEditEngine().SetText( aStrLoP
);
3961 if ( !aStrLoP
.isEmpty() )
3962 pTopView
->SetSelection(ESelection()); // before the '%'
3968 if (pTableView
|| pTopView
)
3973 if( pTableView
->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN
) ) ) )
3976 if( pTopView
->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN
) ) ) )
3979 else if ( nAutoPar
&& nChar
== ')' && CursorAtClosingPar() )
3989 pTableView
->SetControlWord(pTableView
->GetControlWord() | EVControlBits::SINGLELINEPASTE
);
3991 vcl::Window
* pFrameWin
= pActiveViewSh
? pActiveViewSh
->GetFrameWin() : nullptr;
3992 if ( pTableView
->PostKeyEvent( rKEvt
, pFrameWin
) )
3995 pTableView
->SetControlWord(pTableView
->GetControlWord() & ~EVControlBits::SINGLELINEPASTE
);
3999 if ( bUsed
&& rKEvt
.GetKeyCode().GetFunction() == KeyFuncType::CUT
)
4000 pTopView
->DeleteSelected();
4001 else if ( pTopView
->PostKeyEvent( rKEvt
) )
4007 if (bUsed
&& ScModule::get()->GetAppOptions().GetAutoComplete())
4011 miAutoPosFormula
= pFormulaData
->end(); // do not search further
4013 miAutoPosColumn
= pColumnData
->end();
4015 KeyFuncType eFunc
= rKEvt
.GetKeyCode().GetFunction();
4016 if ( nChar
&& nChar
!= 8 && nChar
!= 127 && // no 'backspace', no 'delete'
4017 KeyFuncType::CUT
!= eFunc
) // and no 'CTRL-X'
4026 // When the selection is changed manually or an opening parenthesis
4027 // is typed, stop overwriting parentheses
4028 if ( bUsed
&& nChar
== '(' )
4031 if ( KEY_INSERT
== nCode
)
4033 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
4035 pViewFrm
->GetBindings().Invalidate( SID_ATTR_INSERT
);
4037 if( bUsed
&& bFormulaMode
&& ( bCursorKey
|| bInsKey
|| nCode
== KEY_DELETE
|| nCode
== KEY_BACKSPACE
) )
4041 if( bUsed
&& bFormulaMode
&& nCode
== KEY_BACKSPACE
)
4048 // #i114511# don't count cursor keys as modification
4049 bool bSetModified
= !bCursorKey
;
4050 // tdf#81913 - don't delete range finder since cursor keys don't count as modifications
4051 bInRangeUpdate
= bCursorKey
;
4052 DataChanged(false, bSetModified
); // also calls UpdateParenthesis()
4053 bInRangeUpdate
= false;
4055 // In the LOK case, we want to set the document modified state
4056 // right away at the start of the edit, so that the content is
4057 // saved even when the user leaves the document before hitting
4059 if (comphelper::LibreOfficeKit::isActive() && bSetModified
&& pActiveViewSh
&& !pActiveViewSh
->GetViewData().GetDocShell()->IsModified())
4060 pActiveViewSh
->GetViewData().GetDocShell()->SetModified();
4062 InvalidateAttribs(); //! in DataChanged?
4066 if (pTopView
&& eMode
!= SC_INPUT_NONE
)
4072 OUString
ScInputHandler::GetSurroundingText()
4074 if (eMode
!= SC_INPUT_NONE
)
4077 if (pTableView
|| pTopView
)
4080 return pTableView
->GetSurroundingText();
4081 else if (pTopView
) // call only once
4082 return pTopView
->GetSurroundingText();
4088 Selection
ScInputHandler::GetSurroundingTextSelection()
4090 if (eMode
!= SC_INPUT_NONE
)
4093 if (pTableView
|| pTopView
)
4096 return pTableView
->GetSurroundingTextSelection();
4097 else if (pTopView
) // call only once
4098 return pTopView
->GetSurroundingTextSelection();
4101 return Selection(0, 0);
4104 bool ScInputHandler::DeleteSurroundingText(const Selection
& rSelection
)
4106 if (eMode
!= SC_INPUT_NONE
)
4109 if (pTableView
|| pTopView
)
4112 return pTableView
->DeleteSurroundingText(rSelection
);
4113 else if (pTopView
) // call only once
4114 return pTopView
->DeleteSurroundingText(rSelection
);
4120 void ScInputHandler::InputCommand( const CommandEvent
& rCEvt
)
4122 if ( rCEvt
.GetCommand() == CommandEventId::CursorPos
)
4124 // For CommandEventId::CursorPos, do as little as possible, because
4125 // with remote VCL, even a ShowCursor will generate another event.
4126 if ( eMode
!= SC_INPUT_NONE
)
4129 if (pTableView
|| pTopView
)
4132 pTableView
->Command( rCEvt
);
4133 else if (pTopView
) // call only once
4134 pTopView
->Command( rCEvt
);
4138 else if ( rCEvt
.GetCommand() == CommandEventId::QueryCharPosition
)
4140 if ( eMode
!= SC_INPUT_NONE
)
4143 if (pTableView
|| pTopView
)
4146 pTableView
->Command( rCEvt
);
4147 else if (pTopView
) // call only once
4148 pTopView
->Command( rCEvt
);
4164 bool bNewView
= DataChanging( 0, true );
4166 if (!bProtected
&& pActiveViewSh
&& !(pActiveViewSh
->GetViewShell() && pActiveViewSh
->GetViewShell()->IsLokReadOnlyView())) // changes allowed
4168 if (bNewView
) // create new edit view
4170 pActiveViewSh
->GetViewData().GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
4172 if (eMode
==SC_INPUT_NONE
)
4173 if (pTableView
|| pTopView
)
4177 pTableView
->getEditEngine().SetText( u
""_ustr
);
4178 pTableView
->SetSelection(ESelection());
4182 pTopView
->getEditEngine().SetText( u
""_ustr
);
4183 pTopView
->SetSelection(ESelection());
4189 if (pTableView
|| pTopView
)
4192 pTableView
->Command( rCEvt
);
4194 pTopView
->Command( rCEvt
);
4196 if ( rCEvt
.GetCommand() == CommandEventId::EndExtTextInput
)
4198 // AutoInput after ext text input
4201 miAutoPosFormula
= pFormulaData
->end();
4203 miAutoPosColumn
= pColumnData
->end();
4212 DataChanged(); // calls UpdateParenthesis()
4213 InvalidateAttribs(); //! in DataChanged ?
4216 if (pTopView
&& eMode
!= SC_INPUT_NONE
)
4221 static ScInputHdlState
* getLastState(const ScInputHdlState
* pState
)
4225 return new ScInputHdlState(*pState
);
4228 void ScInputHandler::NotifyChange( const ScInputHdlState
* pState
,
4229 bool bForce
, ScTabViewShell
* pSourceSh
,
4232 // If the call originates from a macro call in the EnterHandler,
4233 // return immediately and don't mess up the status
4234 if (bInEnterHandler
)
4237 bool bRepeat
= (pState
== pLastState
.get());
4238 if (!bRepeat
&& pState
&& pLastState
)
4239 bRepeat
= (*pState
== *pLastState
);
4240 if (bRepeat
&& !bForce
)
4243 bInOwnChange
= true; // disable ModifyHdl (reset below)
4245 if ( pState
&& !pLastState
) // Enable again
4248 bool bHadObject
= pLastState
&& pLastState
->GetEditData();
4250 //! Before EditEngine gets eventually created (so it gets the right pools)
4252 pActiveViewSh
= pSourceSh
;
4254 pActiveViewSh
= dynamic_cast<ScTabViewShell
*>( SfxViewShell::Current() );
4257 ImplCreateEditEngine();
4259 if ( pState
!= pLastState
.get() )
4261 pLastState
.reset(getLastState(pState
));
4264 if ( pState
&& pActiveViewSh
)
4266 ScModule
* pScMod
= ScModule::get();
4268 ScTabViewShell
* pScTabViewShell
= dynamic_cast<ScTabViewShell
*>(pScMod
->GetViewShell());
4270 // Also take foreign reference input into account here (e.g. FunctionsAutoPilot),
4271 // FormEditData, if we're switching from Help to Calc:
4272 if ( !bFormulaMode
&& !pScMod
->IsFormulaMode() &&
4273 ( !pScTabViewShell
|| !pScTabViewShell
->GetFormEditData() ) )
4275 bool bIgnore
= false;
4278 if (pState
->GetPos() != aCursorPos
)
4289 const ScAddress
& rSPos
= pState
->GetStartPos();
4290 const ScAddress
& rEPos
= pState
->GetEndPos();
4291 const EditTextObject
* pData
= pState
->GetEditData();
4292 OUString aString
= pState
->GetString();
4293 bool bTxtMod
= false;
4294 ScDocShell
* pDocSh
= pActiveViewSh
->GetViewData().GetDocShell();
4295 ScDocument
& rDoc
= pDocSh
->GetDocument();
4297 aCursorPos
= pState
->GetPos();
4301 else if ( bHadObject
)
4303 else if ( bTextValid
)
4304 bTxtMod
= ( aString
!= aCurrentText
);
4306 bTxtMod
= ( aString
!= GetEditText(mpEditEngine
.get()) );
4308 if ( bTxtMod
|| bForce
)
4312 mpEditEngine
->SetTextCurrentDefaults( *pData
);
4314 aString
= ScEditUtil::GetMultilineString(*mpEditEngine
);
4316 aString
= GetEditText(mpEditEngine
.get());
4317 lcl_RemoveTabs(aString
);
4319 aCurrentText
.clear();
4323 aCurrentText
= aString
;
4324 bTextValid
= true; //! To begin with remember as a string
4327 const bool bUpdateKit
= comphelper::LibreOfficeKit::isActive() && pActiveViewSh
;
4331 // If we will end up updating LoKit after this, we can skip it here
4332 pInputWin
->SetTextString(aString
, !bUpdateKit
);
4338 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
4339 ESelection aSel
= pActiveView
? pActiveView
->GetSelection() : ESelection();
4341 // if we switched content completely - don't send huge numbers
4342 if (aSel
.start
.nPara
== EE_PARA_MAX
)
4343 aSel
.start
.nPara
= 0;
4345 if (aSel
.end
.nPara
== EE_PARA_MAX
)
4348 pActiveViewSh
->LOKSendFormulabarUpdate(pActiveView
, aString
, aSel
);
4349 pActiveViewSh
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA
, aString
.toUtf8());
4353 if ( pInputWin
|| comphelper::LibreOfficeKit::isActive()) // Named range input
4356 bool bSheetLocal
= false;
4357 const ScAddress::Details
aAddrDetails( rDoc
, aCursorPos
);
4359 // Is the range a name?
4361 if ( pActiveViewSh
)
4362 pActiveViewSh
->GetViewData().GetDocument().
4363 GetRangeAtBlock( ScRange( rSPos
, rEPos
), aPosStr
, &bSheetLocal
);
4365 if ( aPosStr
.isEmpty() ) // Not a name -> format
4367 ScRefFlags nFlags
= ScRefFlags::ZERO
;
4368 if( aAddrDetails
.eConv
== formula::FormulaGrammar::CONV_XL_R1C1
)
4369 nFlags
|= ScRefFlags::COL_ABS
| ScRefFlags::ROW_ABS
;
4370 if ( rSPos
!= rEPos
)
4372 ScRange
r(rSPos
, rEPos
);
4373 applyStartToEndFlags(nFlags
);
4374 aPosStr
= r
.Format(rDoc
, ScRefFlags::VALID
| nFlags
, aAddrDetails
);
4377 aPosStr
= aCursorPos
.Format(ScRefFlags::VALID
| nFlags
, &rDoc
, aAddrDetails
);
4379 else if (bSheetLocal
)
4382 if (rDoc
.GetName( rSPos
.Tab(), aName
))
4383 aPosStr
= ScPosWnd::createLocalRangeName( aPosStr
, aName
);
4388 pInputWin
->SetPosString(aPosStr
);
4389 pInputWin
->SetSumAssignMode();
4392 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh
)
4393 pActiveViewSh
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS
, aPosStr
.toUtf8());
4397 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView
) );
4399 // As long as the content is not edited, turn off online spelling.
4400 // Online spelling is turned back on in StartTable, after setting
4401 // the right language from cell attributes.
4403 EEControlBits nCntrl
= mpEditEngine
->GetControlWord();
4404 if ( nCntrl
& EEControlBits::ONLINESPELLING
)
4405 mpEditEngine
->SetControlWord( nCntrl
& ~EEControlBits::ONLINESPELLING
);
4411 bCommandErrorShown
= false;
4417 // Do not enable if RefDialog is open
4418 if(!pScMod
->IsFormulaMode()&& !pScMod
->IsRefDialogOpen())
4420 if ( !pInputWin
->IsEnabled())
4422 pDelayTimer
->Stop();
4423 pInputWin
->Enable();
4426 else if(pScMod
->IsRefDialogOpen())
4427 { // Because every document has its own InputWin,
4428 // we should start Timer again, because the input line may
4430 if ( !pDelayTimer
->IsActive() )
4431 pDelayTimer
->Start();
4435 else // !pState || !pActiveViewSh
4437 if ( !pDelayTimer
->IsActive() )
4438 pDelayTimer
->Start();
4441 // Don't hide function tooltip in LOK, a remote user might be using tip.
4445 bInOwnChange
= false;
4448 void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust
)
4450 eAttrAdjust
= eJust
;
4454 void ScInputHandler::ResetDelayTimer()
4456 if( pDelayTimer
->IsActive() )
4458 pDelayTimer
->Stop();
4460 pInputWin
->Enable();
4464 IMPL_LINK_NOARG( ScInputHandler
, DelayTimer
, Timer
*, void )
4466 if (!(nullptr == pLastState
|| ScModule::get()->IsFormulaMode() || ScModule::get()->IsRefDialogOpen()))
4469 //! New method at ScModule to query if function autopilot is open
4470 SfxViewFrame
* pViewFrm
= SfxViewFrame::Current();
4471 if ( pViewFrm
&& pViewFrm
->GetChildWindow( SID_OPENDLG_FUNCTION
) )
4475 pInputWin
->EnableButtons( false );
4476 pInputWin
->Disable();
4479 else if ( !bFormulaMode
) // Keep formula e.g. for help
4481 bInOwnChange
= true; // disable ModifyHdl (reset below)
4483 pActiveViewSh
= nullptr;
4484 mpEditEngine
->SetTextCurrentDefaults( OUString() );
4487 pInputWin
->SetPosString( OUString() );
4488 pInputWin
->SetTextString(OUString(), true);
4489 pInputWin
->Disable();
4492 bInOwnChange
= false;
4496 void ScInputHandler::InputSelection( const EditView
* pView
)
4500 UpdateParenthesis(); // Selection changed -> update parentheses highlighting
4502 // When the selection is changed manually, stop overwriting parentheses
4505 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh
)
4507 EditView
* pActiveView
= pTopView
? pTopView
: pTableView
;
4508 ESelection aSel
= pActiveView
? pActiveView
->GetSelection() : ESelection();
4509 pActiveViewSh
->LOKSendFormulabarUpdate(pActiveView
, GetEditString(), aSel
);
4513 void ScInputHandler::InputChanged( const EditView
* pView
, bool bFromNotify
)
4520 // #i20282# DataChanged needs to know if this is from the input line's modify handler
4521 bool bFromTopNotify
= ( bFromNotify
&& pView
== pTopView
);
4523 bool bNewView
= DataChanging(); //FIXME: Is this at all possible?
4524 aCurrentText
= pView
->getEditEngine().GetText(); // Also remember the string
4525 mpEditEngine
->SetTextCurrentDefaults( aCurrentText
);
4526 DataChanged( bFromTopNotify
);
4527 bTextValid
= true; // Is set to false in DataChanged
4529 if ( pActiveViewSh
)
4531 ScViewData
& rViewData
= pActiveViewSh
->GetViewData();
4533 rViewData
.GetDocShell()->PostEditView( mpEditEngine
.get(), aCursorPos
);
4535 rViewData
.EditGrowY();
4536 rViewData
.EditGrowX();
4542 const OUString
& ScInputHandler::GetEditString()
4546 aCurrentText
= mpEditEngine
->GetText(); // Always new from Engine
4550 return aCurrentText
;
4553 Size
ScInputHandler::GetTextSize()
4557 aSize
= Size( mpEditEngine
->CalcTextWidth(), mpEditEngine
->GetTextHeight() );
4562 bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter
& rDestEngine
)
4568 SfxItemSet aSet
= mpEditEngine
->GetAttribs(ESelection::All());
4569 SfxItemState eFieldState
= aSet
.GetItemState( EE_FEATURE_FIELD
, false );
4570 if ( eFieldState
== SfxItemState::INVALID
|| eFieldState
== SfxItemState::SET
)
4573 std::unique_ptr
<EditTextObject
> pObj
= mpEditEngine
->CreateTextObject();
4574 rDestEngine
.SetTextCurrentDefaults(*pObj
);
4577 sal_Int32 nParCnt
= mpEditEngine
->GetParagraphCount();
4578 // Delete attributes
4579 for (sal_Int32 i
=0; i
<nParCnt
; i
++)
4580 rDestEngine
.RemoveCharAttribs( i
);
4582 // Combine paragraphs
4583 while ( nParCnt
> 1 )
4585 sal_Int32 nLen
= rDestEngine
.GetTextLen( 0 );
4586 ESelection
aSel( 0,nLen
, 1,0 );
4587 rDestEngine
.QuickInsertText( OUString(' '), aSel
); // Replace line break with space
4598 * Methods for FunctionAutoPilot:
4599 * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
4601 void ScInputHandler::InputGetSelection( sal_Int32
& rStart
, sal_Int32
& rEnd
)
4603 rStart
= nFormSelStart
;
4607 EditView
* ScInputHandler::GetFuncEditView()
4609 UpdateActiveView(); // Due to pTableView
4611 EditView
* pView
= nullptr;
4614 pInputWin
->MakeDialogEditView();
4615 pView
= pInputWin
->GetEditView();
4619 if ( eMode
!= SC_INPUT_TABLE
)
4621 bCreatingFuncView
= true; // Don't display RangeFinder
4622 SetMode( SC_INPUT_TABLE
);
4623 bCreatingFuncView
= false;
4625 pTableView
->getEditEngine().SetText( OUString() );
4633 void ScInputHandler::InputSetSelection( sal_Int32 nStart
, sal_Int32 nEnd
)
4635 if ( nStart
<= nEnd
)
4637 nFormSelStart
= nStart
;
4642 nFormSelEnd
= nStart
;
4643 nFormSelStart
= nEnd
;
4646 EditView
* pView
= GetFuncEditView();
4648 pView
->SetSelection( ESelection(0,nStart
, 0,nEnd
) );
4653 void ScInputHandler::InputReplaceSelection( std::u16string_view aStr
)
4656 pRefViewSh
= pActiveViewSh
;
4658 OSL_ENSURE(nFormSelEnd
>=nFormSelStart
,"Selection broken...");
4660 sal_Int32 nOldLen
= nFormSelEnd
- nFormSelStart
;
4661 sal_Int32 nNewLen
= aStr
.size();
4663 OUStringBuffer
aBuf(aFormText
);
4665 aBuf
.remove(nFormSelStart
, nOldLen
);
4667 aBuf
.insert(nFormSelStart
, aStr
);
4669 aFormText
= aBuf
.makeStringAndClear();
4671 nFormSelEnd
= nFormSelStart
+ nNewLen
;
4673 EditView
* pView
= GetFuncEditView();
4676 pView
->SetEditEngineUpdateLayout( false );
4677 pView
->getEditEngine().SetText( aFormText
);
4678 pView
->SetSelection( ESelection(0,nFormSelStart
, 0,nFormSelEnd
) );
4679 pView
->SetEditEngineUpdateLayout( true );
4684 void ScInputHandler::InputTurnOffWinEngine()
4686 bInOwnChange
= true; // disable ModifyHdl (reset below)
4688 eMode
= SC_INPUT_NONE
;
4689 /* TODO: it would be better if there was some way to reset the input bar
4690 * engine instead of deleting and having it recreate through
4691 * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
4692 * fdo#72278 without reintroducing fdo#69971. */
4693 StopInputWinEngine(true);
4695 bInOwnChange
= false;
4701 ScInputHdlState::ScInputHdlState( const ScAddress
& rCurPos
,
4702 const ScAddress
& rStartPos
,
4703 const ScAddress
& rEndPos
,
4705 const EditTextObject
* pData
)
4706 : aCursorPos ( rCurPos
),
4707 aStartPos ( rStartPos
),
4708 aEndPos ( rEndPos
),
4709 aString (std::move( _aString
)),
4710 pEditData ( pData
? pData
->Clone() : nullptr )
4714 ScInputHdlState::ScInputHdlState( const ScInputHdlState
& rCpy
)
4719 ScInputHdlState::~ScInputHdlState()
4723 bool ScInputHdlState::operator==( const ScInputHdlState
& r
) const
4725 return ( (aStartPos
== r
.aStartPos
)
4726 && (aEndPos
== r
.aEndPos
)
4727 && (aCursorPos
== r
.aCursorPos
)
4728 && (aString
== r
.aString
)
4729 && ScGlobal::EETextObjEqual( pEditData
.get(), r
.pEditData
.get() ) );
4732 ScInputHdlState
& ScInputHdlState::operator=( const ScInputHdlState
& r
)
4736 aCursorPos
= r
.aCursorPos
;
4737 aStartPos
= r
.aStartPos
;
4738 aEndPos
= r
.aEndPos
;
4739 aString
= r
.aString
;
4742 pEditData
= r
.pEditData
->Clone();
4747 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */