Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / sc / source / ui / app / inputhdl.cxx
blobf268dbf574a2f048f4d60a7c3a73cc11ea3fc9b2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "inputhdl.hxx"
21 #include "scitems.hxx"
22 #include <editeng/eeitem.hxx>
24 #include <sfx2/app.hxx>
25 #include <editeng/acorrcfg.hxx>
26 #include <svx/algitem.hxx>
27 #include <editeng/adjustitem.hxx>
28 #include <editeng/brushitem.hxx>
29 #include <svtools/colorcfg.hxx>
30 #include <editeng/colritem.hxx>
31 #include <editeng/editobj.hxx>
32 #include <editeng/editstat.hxx>
33 #include <editeng/editview.hxx>
34 #include <editeng/escapementitem.hxx>
35 #include <editeng/forbiddencharacterstable.hxx>
36 #include <editeng/langitem.hxx>
37 #include <editeng/svxacorr.hxx>
38 #include <editeng/unolingu.hxx>
39 #include <editeng/wghtitem.hxx>
40 #include <editeng/justifyitem.hxx>
41 #include <editeng/misspellrange.hxx>
42 #include <sfx2/bindings.hxx>
43 #include <sfx2/viewfrm.hxx>
44 #include <sfx2/dispatch.hxx>
45 #include <sfx2/docfile.hxx>
46 #include <sfx2/printer.hxx>
47 #include <svl/zforlist.hxx>
48 #include <unotools/localedatawrapper.hxx>
49 #include <vcl/help.hxx>
50 #include <vcl/cursor.hxx>
51 #include <vcl/settings.hxx>
52 #include <tools/urlobj.hxx>
53 #include <comphelper/string.hxx>
54 #include <formula/formulahelper.hxx>
55 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
56 #include <comphelper/lok.hxx>
58 #include "inputwin.hxx"
59 #include "tabvwsh.hxx"
60 #include "docsh.hxx"
61 #include "scmod.hxx"
62 #include "uiitems.hxx"
63 #include "global.hxx"
64 #include "sc.hrc"
65 #include "globstr.hrc"
66 #include "patattr.hxx"
67 #include "viewdata.hxx"
68 #include "document.hxx"
69 #include "docpool.hxx"
70 #include "editutil.hxx"
71 #include "appoptio.hxx"
72 #include "docoptio.hxx"
73 #include "validat.hxx"
74 #include "userlist.hxx"
75 #include "rfindlst.hxx"
76 #include "inputopt.hxx"
77 #include "simpleformulacalc.hxx"
78 #include "compiler.hxx"
79 #include "editable.hxx"
80 #include "funcdesc.hxx"
81 #include "markdata.hxx"
82 #include "tokenarray.hxx"
83 #include <gridwin.hxx>
85 // Maximum Ranges in RangeFinder
86 #define RANGEFIND_MAX 64
88 using namespace formula;
90 bool ScInputHandler::bOptLoaded = false; // Evaluate App options
91 bool ScInputHandler::bAutoComplete = false; // Is set in KeyInput
93 extern sal_uInt16 nEditAdjust; //! Member of ViewData
95 namespace {
97 // Formula data replacement character for a pair of parentheses at end of
98 // function name, to force sorting parentheses before all other characters.
99 // Collation may treat parentheses differently.
100 const sal_Unicode cParenthesesReplacement = 0x0001;
102 sal_Unicode lcl_getSheetSeparator(ScDocument* pDoc)
104 ScCompiler aComp(pDoc, ScAddress());
105 aComp.SetGrammar(pDoc->GetGrammar());
106 return aComp.GetNativeAddressSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
109 ScTypedCaseStrSet::const_iterator findText(
110 const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator itPos,
111 const OUString& rStart, OUString& rResult, bool bBack)
113 if (bBack) // Backwards
115 ScTypedCaseStrSet::const_reverse_iterator it = rDataSet.rbegin(), itEnd = rDataSet.rend();
116 if (itPos != rDataSet.end())
118 size_t nPos = std::distance(rDataSet.begin(), itPos);
119 size_t nRPos = rDataSet.size() - 1 - nPos;
120 std::advance(it, nRPos);
121 ++it;
124 for (; it != itEnd; ++it)
126 const ScTypedStrData& rData = *it;
127 if (rData.GetStringType() == ScTypedStrData::Value)
128 // skip values
129 continue;
131 if (!ScGlobal::GetpTransliteration()->isMatch(rStart, rData.GetString()))
132 // not a match
133 continue;
135 rResult = rData.GetString();
136 return (++it).base(); // convert the reverse iterator back to iterator.
139 else // Forwards
141 ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end();
142 if (itPos != rDataSet.end())
144 it = itPos;
145 ++it;
148 for (; it != itEnd; ++it)
150 const ScTypedStrData& rData = *it;
151 if (rData.GetStringType() == ScTypedStrData::Value)
152 // skip values
153 continue;
155 if (!ScGlobal::GetpTransliteration()->isMatch(rStart, rData.GetString()))
156 // not a match
157 continue;
159 rResult = rData.GetString();
160 return it;
164 return rDataSet.end(); // no matching text found
167 OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rString)
169 ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end();
170 for (; it != itEnd; ++it)
172 const ScTypedStrData& rData = *it;
173 if (rData.GetStringType() == ScTypedStrData::Value)
174 continue;
176 if (!ScGlobal::GetpTransliteration()->isEqual(rData.GetString(), rString))
177 continue;
179 return rData.GetString();
181 return rString;
184 ScTypedCaseStrSet::const_iterator findTextAll(
185 const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator itPos,
186 const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack)
188 rResultVec.clear(); // clear contents
190 size_t nCount = 0;
191 ScTypedCaseStrSet::const_iterator retit;
192 if ( bBack ) // Backwards
194 ScTypedCaseStrSet::const_reverse_iterator it, itEnd;
195 if ( itPos == rDataSet.end() )
197 it = rDataSet.rend();
198 --it;
199 itEnd = it;
201 else
203 it = rDataSet.rbegin();
204 size_t nPos = std::distance(rDataSet.begin(), itPos);
205 size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1
206 std::advance(it, nRPos);
207 if ( it == rDataSet.rend() )
208 it = rDataSet.rbegin();
209 itEnd = it;
211 bool bFirstTime = true;
213 while ( it != itEnd || bFirstTime )
215 ++it;
216 if ( it == rDataSet.rend() ) // go to the first if reach the end
217 it = rDataSet.rbegin();
219 if ( bFirstTime )
220 bFirstTime = false;
221 const ScTypedStrData& rData = *it;
222 if ( rData.GetStringType() == ScTypedStrData::Value )
223 // skip values
224 continue;
226 if ( !ScGlobal::GetpTransliteration()->isMatch(rStart, rData.GetString()) )
227 // not a match
228 continue;
230 rResultVec.push_back(rData.GetString()); // set the match data
231 if ( nCount == 0 ) // convert the reverse iterator back to iterator.
233 // actually we want to do "retit = it;".
234 retit = rDataSet.begin();
235 size_t nRPos = std::distance(rDataSet.rbegin(), it);
236 size_t nPos = rDataSet.size() - 1 - nRPos;
237 std::advance(retit, nPos);
239 ++nCount;
242 else // Forwards
244 ScTypedCaseStrSet::const_iterator it, itEnd;
245 it = itPos;
246 if ( it == rDataSet.end() )
247 it = rDataSet.begin();
248 itEnd = it;
249 bool bFirstTime = true;
251 while ( it != itEnd || bFirstTime )
253 ++it;
254 if ( it == rDataSet.end() ) // go to the first if reach the end
255 it = rDataSet.begin();
257 if ( bFirstTime )
258 bFirstTime = false;
259 const ScTypedStrData& rData = *it;
260 if ( rData.GetStringType() == ScTypedStrData::Value )
261 // skip values
262 continue;
264 if ( !ScGlobal::GetpTransliteration()->isMatch(rStart, rData.GetString()) )
265 // not a match
266 continue;
268 rResultVec.push_back(rData.GetString()); // set the match data
269 if ( nCount == 0 )
270 retit = it; // remember first match iterator
271 ++nCount;
275 if ( nCount > 0 ) // at least one function has matched
276 return retit;
277 return rDataSet.end(); // no matching text found
280 void removeChars(OUString& rStr, sal_Unicode c)
282 OUStringBuffer aBuf(rStr);
283 for (sal_Int32 i = 0, n = aBuf.getLength(); i < n; ++i)
285 if (aBuf[i] == c)
286 aBuf[i] = ' ';
288 rStr = aBuf.makeStringAndClear();
293 void ScInputHandler::InitRangeFinder( const OUString& rFormula )
295 DeleteRangeFinder();
296 if ( !pActiveViewSh || !SC_MOD()->GetInputOptions().GetRangeFinder() )
297 return;
298 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
299 ScDocument& rDoc = pDocSh->GetDocument();
300 const sal_Unicode cSheetSep = lcl_getSheetSeparator(&rDoc);
302 OUString aDelimiters = ScEditUtil::ModifyDelimiters(" !\"");
303 // delimiters (in addition to ScEditUtil): only characters that are
304 // allowed in formulas next to references and the quotation mark (so
305 // string constants can be skipped)
307 sal_Int32 nColon = aDelimiters.indexOf( ':' );
308 if ( nColon != -1 )
309 aDelimiters = aDelimiters.replaceAt( nColon, 1, ""); // Delimiter without colon
310 sal_Int32 nDot = aDelimiters.indexOf(cSheetSep);
311 if ( nDot != -1 )
312 aDelimiters = aDelimiters.replaceAt( nDot, 1 , ""); // Delimiter without dot
314 const sal_Unicode* pChar = rFormula.getStr();
315 sal_Int32 nLen = rFormula.getLength();
316 sal_Int32 nPos = 0;
317 sal_Int32 nStart = 0;
318 sal_uInt16 nCount = 0;
319 ScRange aRange;
320 while ( nPos < nLen && nCount < RANGEFIND_MAX )
322 // Skip separator
323 while ( nPos<nLen && ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) )
325 if ( pChar[nPos] == '"' ) // String
327 ++nPos;
328 while (nPos<nLen && pChar[nPos] != '"') // Skip until end
329 ++nPos;
331 ++nPos; // Separator or closing quote
334 // Text zwischen Trennern
335 nStart = nPos;
336 handle_r1c1:
337 while ( nPos<nLen && !ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) )
338 ++nPos;
340 // for R1C1 '-' in R[-]... or C[-]... are not delimiters
341 // Nothing heroic here to ensure that there are '[]' around a negative
342 // integer. we need to clean up this code.
343 if( nPos < nLen && nPos > 0 &&
344 '-' == pChar[nPos] && '[' == pChar[nPos-1] &&
345 formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() )
347 nPos++;
348 goto handle_r1c1;
351 if ( nPos > nStart )
353 OUString aTest = rFormula.copy( nStart, nPos-nStart );
354 const ScAddress::Details aAddrDetails( &rDoc, aCursorPos );
355 ScRefFlags nFlags = aRange.ParseAny( aTest, &rDoc, aAddrDetails );
356 if ( nFlags & ScRefFlags::VALID )
358 // Set tables if not specified
359 if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
360 aRange.aStart.SetTab( pActiveViewSh->GetViewData().GetTabNo() );
361 if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
362 aRange.aEnd.SetTab( aRange.aStart.Tab() );
364 if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) ==
365 ScRefFlags::ZERO )
367 // #i73766# if a single ref was parsed, set the same "abs" flags for ref2,
368 // so Format doesn't output a double ref because of different flags.
369 ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS);
370 applyStartToEndFlags(nFlags, nAbsFlags);
373 if (!nCount)
375 pEngine->SetUpdateMode( false );
376 pRangeFindList = new ScRangeFindList( pDocSh->GetTitle() );
379 ColorData nColorData = pRangeFindList->Insert( ScRangeFindData( aRange, nFlags, nStart, nPos ) );
381 ESelection aSel( 0, nStart, 0, nPos );
382 SfxItemSet aSet( pEngine->GetEmptyItemSet() );
383 aSet.Put( SvxColorItem( Color( nColorData ),
384 EE_CHAR_COLOR ) );
385 pEngine->QuickSetAttribs( aSet, aSel );
386 ++nCount;
390 // Do not skip last separator; could be a quote (?)
393 if (nCount)
395 pEngine->SetUpdateMode( true );
397 pDocSh->Broadcast( SfxSimpleHint( SC_HINT_SHOWRANGEFINDER ) );
401 void ScInputHandler::SetDocumentDisposing( bool b )
403 mbDocumentDisposing = b;
406 static void lcl_Replace( EditView* pView, const OUString& rNewStr, const ESelection& rOldSel )
408 if ( pView )
410 ESelection aOldSel = pView->GetSelection();
411 if (aOldSel.HasRange())
412 pView->SetSelection( ESelection( aOldSel.nEndPara, aOldSel.nEndPos,
413 aOldSel.nEndPara, aOldSel.nEndPos ) );
415 EditEngine* pEngine = pView->GetEditEngine();
416 pEngine->QuickInsertText( rNewStr, rOldSel );
418 // Dummy InsertText for Update and Paint
419 // To do that we need to cancel the selection from above (before QuickInsertText)
420 pView->InsertText( EMPTY_OUSTRING );
422 sal_Int32 nLen = pEngine->GetTextLen(0);
423 ESelection aSel( 0, nLen, 0, nLen );
424 pView->SetSelection( aSel ); // Set cursor to the end
428 void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew )
430 ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh;
431 if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() )
433 ScRangeFindData& rData = pRangeFindList->GetObject( nIndex );
434 sal_Int32 nOldStart = rData.nSelStart;
435 sal_Int32 nOldEnd = rData.nSelEnd;
436 ColorData nNewColor = pRangeFindList->FindColor( rNew, nIndex );
438 ScRange aJustified = rNew;
439 aJustified.PutInOrder(); // Always display Ref in the Formula the right way
440 ScDocument* pDoc = pDocView->GetViewData().GetDocument();
441 const ScAddress::Details aAddrDetails( pDoc, aCursorPos );
442 OUString aNewStr(aJustified.Format(rData.nFlags, pDoc, aAddrDetails));
443 ESelection aOldSel( 0, nOldStart, 0, nOldEnd );
444 SfxItemSet aSet( pEngine->GetEmptyItemSet() );
446 DataChanging();
448 lcl_Replace( pTopView, aNewStr, aOldSel );
449 lcl_Replace( pTableView, aNewStr, aOldSel );
450 aSet.Put( SvxColorItem( Color( nNewColor ), EE_CHAR_COLOR ) );
451 pEngine->QuickSetAttribs( aSet, aOldSel );
453 bInRangeUpdate = true;
454 DataChanged();
455 bInRangeUpdate = false;
457 long nDiff = aNewStr.getLength() - (long)(nOldEnd-nOldStart);
459 rData.aRef = rNew;
460 rData.nSelEnd = rData.nSelEnd + nDiff;
461 rData.nColorData = nNewColor;
463 sal_uInt16 nCount = (sal_uInt16) pRangeFindList->Count();
464 for (sal_uInt16 i=nIndex+1; i<nCount; i++)
466 ScRangeFindData& rNext = pRangeFindList->GetObject( i );
467 rNext.nSelStart = rNext.nSelStart + nDiff;
468 rNext.nSelEnd = rNext.nSelEnd + nDiff;
471 EditView* pActiveView = pTopView ? pTopView : pTableView;
472 pActiveView->ShowCursor( false );
474 else
476 OSL_FAIL("UpdateRange: we're missing something");
480 void ScInputHandler::DeleteRangeFinder()
482 ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh;
483 if ( pRangeFindList && pPaintView )
485 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
486 pRangeFindList->SetHidden(true);
487 pDocSh->Broadcast( SfxSimpleHint( SC_HINT_SHOWRANGEFINDER ) ); // Steal
488 DELETEZ(pRangeFindList);
492 inline OUString GetEditText(EditEngine* pEng)
494 return ScEditUtil::GetSpaceDelimitedString(*pEng);
497 static void lcl_RemoveTabs(OUString& rStr)
499 removeChars(rStr, '\t');
502 static void lcl_RemoveLineEnd(OUString& rStr)
504 rStr = convertLineEnd(rStr, LINEEND_LF);
505 removeChars(rStr, '\n');
508 static sal_Int32 lcl_MatchParenthesis( const OUString& rStr, sal_Int32 nPos )
510 int nDir;
511 sal_Unicode c1, c2 = 0;
512 c1 = rStr[nPos];
513 switch ( c1 )
515 case '(' :
516 c2 = ')';
517 nDir = 1;
518 break;
519 case ')' :
520 c2 = '(';
521 nDir = -1;
522 break;
523 case '<' :
524 c2 = '>';
525 nDir = 1;
526 break;
527 case '>' :
528 c2 = '<';
529 nDir = -1;
530 break;
531 case '{' :
532 c2 = '}';
533 nDir = 1;
534 break;
535 case '}' :
536 c2 = '{';
537 nDir = -1;
538 break;
539 case '[' :
540 c2 = ']';
541 nDir = 1;
542 break;
543 case ']' :
544 c2 = '[';
545 nDir = -1;
546 break;
547 default:
548 nDir = 0;
550 if ( !nDir )
551 return -1;
552 sal_Int32 nLen = rStr.getLength();
553 const sal_Unicode* p0 = rStr.getStr();
554 const sal_Unicode* p;
555 const sal_Unicode* p1;
556 sal_uInt16 nQuotes = 0;
557 if ( nPos < nLen / 2 )
559 p = p0;
560 p1 = p0 + nPos;
562 else
564 p = p0 + nPos;
565 p1 = p0 + nLen;
567 while ( p < p1 )
569 if ( *p++ == '\"' )
570 nQuotes++;
572 // Odd number of quotes that we find ourselves in a string
573 bool bLookInString = ((nQuotes % 2) != 0);
574 bool bInString = bLookInString;
575 p = p0 + nPos;
576 p1 = (nDir < 0 ? p0 : p0 + nLen) ;
577 sal_uInt16 nLevel = 1;
578 while ( p != p1 && nLevel )
580 p += nDir;
581 if ( *p == '\"' )
583 bInString = !bInString;
584 if ( bLookInString && !bInString )
585 p = p1; // That's it then
587 else if ( bInString == bLookInString )
589 if ( *p == c1 )
590 nLevel++;
591 else if ( *p == c2 )
592 nLevel--;
595 if ( nLevel )
596 return -1;
597 return (sal_Int32) (p - p0);
600 ScInputHandler::ScInputHandler()
601 : pInputWin( nullptr ),
602 pEngine( nullptr ),
603 pTableView( nullptr ),
604 pTopView( nullptr ),
605 pColumnData( nullptr ),
606 pFormulaData( nullptr ),
607 pFormulaDataPara( nullptr ),
608 pTipVisibleParent( nullptr ),
609 nTipVisible( 0 ),
610 pTipVisibleSecParent( nullptr ),
611 nTipVisibleSec( 0 ),
612 nFormSelStart( 0 ),
613 nFormSelEnd( 0 ),
614 nAutoPar( 0 ),
615 eMode( SC_INPUT_NONE ),
616 bUseTab( false ),
617 bTextValid( true ),
618 bModified( false ),
619 bSelIsRef( false ),
620 bFormulaMode( false ),
621 bInRangeUpdate( false ),
622 bParenthesisShown( false ),
623 bCreatingFuncView( false ),
624 bInEnterHandler( false ),
625 bCommandErrorShown( false ),
626 bInOwnChange( false ),
627 bProtected( false ),
628 bCellHasPercentFormat( false ),
629 bLastIsSymbol( false ),
630 mbDocumentDisposing(false),
631 nValidation( 0 ),
632 eAttrAdjust( SVX_HOR_JUSTIFY_STANDARD ),
633 aScaleX( 1,1 ),
634 aScaleY( 1,1 ),
635 pRefViewSh( nullptr ),
636 pLastPattern( nullptr ),
637 pEditDefaults( nullptr ),
638 pLastState( nullptr ),
639 pDelayTimer( nullptr ),
640 pRangeFindList( nullptr ),
641 maFormulaChar()
643 // The InputHandler is constructed with the view, so SfxViewShell::Current
644 // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange.
645 pActiveViewSh = nullptr;
647 // Bindings (only still used for Invalidate) are retrieved if needed on demand
650 ScInputHandler::~ScInputHandler()
652 // If this is the application InputHandler, the dtor is called after SfxApplication::Main,
653 // thus we can't rely on any Sfx functions
654 if (!mbDocumentDisposing) // inplace
655 EnterHandler(); // Finish input
657 if (SC_MOD()->GetRefInputHdl() == this)
658 SC_MOD()->SetRefInputHdl(nullptr);
660 if ( pInputWin && pInputWin->GetInputHandler() == this )
661 pInputWin->SetInputHandler( nullptr );
663 delete pRangeFindList;
664 delete pEditDefaults;
665 delete pEngine;
666 delete pLastState;
667 delete pDelayTimer;
668 delete pColumnData;
669 delete pFormulaData;
670 delete pFormulaDataPara;
673 void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY )
675 if ( rX != aScaleX || rY != aScaleY )
677 aScaleX = rX;
678 aScaleY = rY;
679 if (pEngine)
681 MapMode aMode( MAP_100TH_MM, Point(), aScaleX, aScaleY );
682 pEngine->SetRefMapMode( aMode );
687 void ScInputHandler::UpdateRefDevice()
689 if (!pEngine)
690 return;
692 bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
693 bool bInPlace = pActiveViewSh && pActiveViewSh->GetViewFrame()->GetFrame().IsInPlace();
694 EEControlBits nCtrl = pEngine->GetControlWord();
695 if ( bTextWysiwyg || bInPlace )
696 nCtrl |= EEControlBits::FORMAT100; // EditEngine default: always format for 100%
697 else
698 nCtrl &= ~EEControlBits::FORMAT100; // when formatting for screen, use the actual MapMode
699 pEngine->SetControlWord( nCtrl );
700 if ( bTextWysiwyg && pActiveViewSh )
701 pEngine->SetRefDevice( pActiveViewSh->GetViewData().GetDocument()->GetPrinter() );
702 else
703 pEngine->SetRefDevice( nullptr );
705 MapMode aMode( MAP_100TH_MM, Point(), aScaleX, aScaleY );
706 pEngine->SetRefMapMode( aMode );
708 // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev,
709 // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL).
710 if ( !( bTextWysiwyg && pActiveViewSh ) )
712 pEngine->GetRefDevice()->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() );
716 void ScInputHandler::ImplCreateEditEngine()
718 if ( !pEngine )
720 if ( pActiveViewSh )
722 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
723 pEngine = new ScFieldEditEngine(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool());
725 else
726 pEngine = new ScFieldEditEngine(nullptr, EditEngine::CreatePool(), nullptr, true);
727 pEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( pEngine->GetWordDelimiters() ) );
728 UpdateRefDevice(); // also sets MapMode
729 pEngine->SetPaperSize( Size( 1000000, 1000000 ) );
730 pEditDefaults = new SfxItemSet( pEngine->GetEmptyItemSet() );
732 pEngine->SetControlWord( pEngine->GetControlWord() | EEControlBits::AUTOCORRECT );
733 pEngine->SetReplaceLeadingSingleQuotationMark( false );
734 pEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) );
738 void ScInputHandler::UpdateAutoCorrFlag()
740 EEControlBits nCntrl = pEngine->GetControlWord();
741 EEControlBits nOld = nCntrl;
743 // Don't use pLastPattern here (may be invalid because of AutoStyle)
744 bool bDisable = bLastIsSymbol || bFormulaMode;
745 if ( bDisable )
746 nCntrl &= ~EEControlBits::AUTOCORRECT;
747 else
748 nCntrl |= EEControlBits::AUTOCORRECT;
750 if ( nCntrl != nOld )
751 pEngine->SetControlWord(nCntrl);
754 void ScInputHandler::UpdateSpellSettings( bool bFromStartTab )
756 if ( pActiveViewSh )
758 ScViewData& rViewData = pActiveViewSh->GetViewData();
759 bool bOnlineSpell = rViewData.GetDocument()->GetDocOptions().IsAutoSpell();
761 // SetDefaultLanguage is independent of the language attributes,
762 // ScGlobal::GetEditDefaultLanguage is always used.
763 // It must be set every time in case the office language was changed.
765 pEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
767 // if called for changed options, update flags only if already editing
768 // if called from StartTable, always update flags
770 if ( bFromStartTab || eMode != SC_INPUT_NONE )
772 EEControlBits nCntrl = pEngine->GetControlWord();
773 EEControlBits nOld = nCntrl;
774 if( bOnlineSpell )
775 nCntrl |= EEControlBits::ONLINESPELLING;
776 else
777 nCntrl &= ~EEControlBits::ONLINESPELLING;
778 // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
779 if ( pLastPattern && pLastPattern->IsSymbolFont() )
780 nCntrl &= ~EEControlBits::AUTOCORRECT;
781 else
782 nCntrl |= EEControlBits::AUTOCORRECT;
783 if ( nCntrl != nOld )
784 pEngine->SetControlWord(nCntrl);
786 ScDocument* pDoc = rViewData.GetDocument();
787 pDoc->ApplyAsianEditSettings( *pEngine );
788 pEngine->SetDefaultHorizontalTextDirection(
789 (EEHorizontalTextDirection)pDoc->GetEditTextDirection( rViewData.GetTabNo() ) );
790 pEngine->SetFirstWordCapitalization( false );
793 // Language is set separately, so the speller is needed only if online spelling is active
794 if ( bOnlineSpell ) {
795 css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
796 pEngine->SetSpeller( xXSpellChecker1 );
799 bool bHyphen = pLastPattern && static_cast<const SfxBoolItem&>(pLastPattern->GetItem(ATTR_HYPHENATE)).GetValue();
800 if ( bHyphen ) {
801 css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
802 pEngine->SetHyphenator( xXHyphenator );
807 // Function/Range names etc. as Tip help
809 // The other types are defined in ScDocument::GetFormulaEntries
810 void ScInputHandler::GetFormulaData()
812 if ( pActiveViewSh )
814 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
816 if ( pFormulaData )
817 pFormulaData->clear();
818 else
820 pFormulaData = new ScTypedCaseStrSet;
823 if( pFormulaDataPara )
824 pFormulaDataPara->clear();
825 else
826 pFormulaDataPara = new ScTypedCaseStrSet;
828 const OUString aParenthesesReplacement( cParenthesesReplacement);
829 const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
830 sal_uLong nListCount = pFuncList->GetCount();
831 for(sal_uLong i=0;i<nListCount;i++)
833 const ScFuncDesc* pDesc = pFuncList->GetFunction( i );
834 if ( pDesc->pFuncName )
836 const sal_Unicode* pName = pDesc->pFuncName->getStr();
837 const sal_Int32 nLen = pDesc->pFuncName->getLength();
838 // fdo#75264 fill maFormulaChar with all characters used in formula names
839 for ( sal_Int32 j = 0; j < nLen; j++ )
841 sal_Unicode c = pName[ j ];
842 maFormulaChar.insert( c );
844 OUString aFuncName = *pDesc->pFuncName + aParenthesesReplacement;
845 pFormulaData->insert(ScTypedStrData(aFuncName, 0.0, ScTypedStrData::Standard));
846 pDesc->initArgumentInfo();
847 OUString aEntry = pDesc->getSignature();
848 pFormulaDataPara->insert(ScTypedStrData(aEntry, 0.0, ScTypedStrData::Standard));
851 miAutoPosFormula = pFormulaData->end();
852 rDoc.GetFormulaEntries( *pFormulaData );
853 rDoc.GetFormulaEntries( *pFormulaDataPara );
857 IMPL_LINK_TYPED( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent&, rEvent, void )
859 if( rEvent.GetId() == VCLEVENT_OBJECT_DYING || rEvent.GetId() == VCLEVENT_WINDOW_HIDE )
860 HideTip();
863 IMPL_LINK_TYPED( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent&, rEvent, void )
865 if( rEvent.GetId() == VCLEVENT_OBJECT_DYING || rEvent.GetId() == VCLEVENT_WINDOW_HIDE )
866 HideTipBelow();
869 void ScInputHandler::HideTip()
871 if ( nTipVisible )
873 pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
874 Help::HidePopover(pTipVisibleParent, nTipVisible );
875 nTipVisible = 0;
876 pTipVisibleParent = nullptr;
878 aManualTip.clear();
880 void ScInputHandler::HideTipBelow()
882 if ( nTipVisibleSec )
884 pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
885 Help::HidePopover(pTipVisibleSecParent, nTipVisibleSec);
886 nTipVisibleSec = 0;
887 pTipVisibleSecParent = nullptr;
889 aManualTip.clear();
892 void ScInputHandler::ShowArgumentsTip( OUString& rSelText )
894 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
895 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
896 const sal_Unicode cSheetSep = lcl_getSheetSeparator(&pDocSh->GetDocument());
897 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
898 bool bFound = false;
899 while( !bFound )
901 rSelText += ")";
902 sal_Int32 nLeftParentPos = lcl_MatchParenthesis( rSelText, rSelText.getLength()-1 );
903 if( nLeftParentPos != -1 )
905 sal_Int32 nNextFStart = aHelper.GetFunctionStart( rSelText, nLeftParentPos, true);
906 const IFunctionDescription* ppFDesc;
907 ::std::vector< OUString> aArgs;
908 if( aHelper.GetNextFunc( rSelText, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
910 if( !ppFDesc->getFunctionName().isEmpty() )
912 sal_Int32 nArgPos = aHelper.GetArgStart( rSelText, nNextFStart, 0 );
913 sal_uInt16 nArgs = static_cast<sal_uInt16>(ppFDesc->getParameterCount());
914 OUString aFuncName( ppFDesc->getFunctionName() + "(");
915 OUString aNew;
916 ScTypedCaseStrSet::const_iterator it =
917 findText(*pFormulaDataPara, pFormulaDataPara->end(), aFuncName, aNew, false);
918 if (it != pFormulaDataPara->end())
920 bool bFlag = false;
921 sal_uInt16 nActive = 0;
922 for( sal_uInt16 i=0; i < nArgs; i++ )
924 sal_Int32 nLength = aArgs[i].getLength();
925 if( nArgPos <= rSelText.getLength()-1 )
927 nActive = i+1;
928 bFlag = true;
930 nArgPos+=nLength+1;
932 if( bFlag )
934 sal_Int32 nCountSemicolon = comphelper::string::getTokenCount(aNew, cSep) - 1;
935 sal_Int32 nCountDot = comphelper::string::getTokenCount(aNew, cSheetSep) - 1;
936 sal_Int32 nStartPosition = 0;
937 sal_Int32 nEndPosition = 0;
939 if( !nCountSemicolon )
941 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
943 sal_Unicode cNext = aNew[i];
944 if( cNext == '(' )
946 nStartPosition = i+1;
950 else if( !nCountDot )
952 sal_uInt16 nCount = 0;
953 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
955 sal_Unicode cNext = aNew[i];
956 if( cNext == '(' )
958 nStartPosition = i+1;
960 else if( cNext == cSep )
962 nCount ++;
963 nEndPosition = i;
964 if( nCount == nActive )
966 break;
968 nStartPosition = nEndPosition+1;
972 else
974 sal_uInt16 nCount = 0;
975 for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
977 sal_Unicode cNext = aNew[i];
978 if( cNext == '(' )
980 nStartPosition = i+1;
982 else if( cNext == cSep )
984 nCount ++;
985 nEndPosition = i;
986 if( nCount == nActive )
988 break;
990 nStartPosition = nEndPosition+1;
992 else if( cNext == cSheetSep )
994 continue;
999 if (nStartPosition > 0)
1001 OUStringBuffer aBuf;
1002 aBuf.append(aNew.copy(0, nStartPosition));
1003 aBuf.append(static_cast<sal_Unicode>(0x25BA));
1004 aBuf.append(aNew.copy(nStartPosition));
1005 nArgs = ppFDesc->getParameterCount();
1006 sal_Int16 nVarArgsSet = 0;
1007 if ( nArgs >= PAIRED_VAR_ARGS )
1009 nVarArgsSet = 2;
1010 nArgs -= PAIRED_VAR_ARGS - nVarArgsSet;
1012 else if ( nArgs >= VAR_ARGS )
1014 nVarArgsSet = 1;
1015 nArgs -= VAR_ARGS - nVarArgsSet;
1017 if ( nVarArgsSet > 0 && nActive > nArgs )
1018 nActive = nArgs - (nActive - nArgs) % nVarArgsSet;
1019 aBuf.append( " : " );
1020 aBuf.append( ppFDesc->getParameterDescription(nActive-1) );
1021 aNew = aBuf.makeStringAndClear();
1022 ShowTipBelow( aNew );
1023 bFound = true;
1026 else
1028 ShowTipBelow( aNew );
1029 bFound = true;
1035 else
1037 break;
1042 void ScInputHandler::ShowTipCursor()
1044 HideTip();
1045 HideTipBelow();
1046 EditView* pActiveView = pTopView ? pTopView : pTableView;
1048 if ( bFormulaMode && pActiveView && pFormulaDataPara && pEngine->GetParagraphCount() == 1 )
1050 OUString aParagraph = pEngine->GetText( 0 );
1051 ESelection aSel = pActiveView->GetSelection();
1052 aSel.Adjust();
1054 if ( aParagraph.getLength() < aSel.nEndPos )
1055 return;
1057 if ( aSel.nEndPos > 0 )
1059 OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
1061 ShowArgumentsTip( aSelText );
1066 void ScInputHandler::ShowTip( const OUString& rText )
1068 // aManualTip needs to be set afterwards from outside
1070 HideTip();
1071 HideTipBelow();
1073 EditView* pActiveView = pTopView ? pTopView : pTableView;
1074 if (pActiveView)
1076 Point aPos;
1077 pTipVisibleParent = pActiveView->GetWindow();
1078 vcl::Cursor* pCur = pActiveView->GetCursor();
1079 if (pCur)
1080 aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() );
1081 aPos = pTipVisibleParent->OutputToScreenPixel( aPos );
1082 Rectangle aRect( aPos, aPos );
1084 QuickHelpFlags nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
1085 nTipVisible = Help::ShowPopover(pTipVisibleParent, aRect, rText, nAlign);
1086 pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
1090 void ScInputHandler::ShowTipBelow( const OUString& rText )
1092 HideTipBelow();
1094 EditView* pActiveView = pTopView ? pTopView : pTableView;
1095 if ( pActiveView )
1097 Point aPos;
1098 pTipVisibleSecParent = pActiveView->GetWindow();
1099 vcl::Cursor* pCur = pActiveView->GetCursor();
1100 if ( pCur )
1102 Point aLogicPos = pCur->GetPos();
1103 aLogicPos.Y() += pCur->GetHeight();
1104 aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos );
1106 aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos );
1107 Rectangle aRect( aPos, aPos );
1108 QuickHelpFlags nAlign = QuickHelpFlags::Left | QuickHelpFlags::Top | QuickHelpFlags::NoEvadePointer;
1109 nTipVisibleSec = Help::ShowPopover(pTipVisibleSecParent, aRect, rText, nAlign);
1110 pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
1114 bool ScInputHandler::GetFuncName( OUString& aStart, OUString& aResult )
1116 if ( aStart.isEmpty() )
1117 return false;
1119 aStart = ScGlobal::pCharClass->uppercase( aStart );
1120 sal_Int32 nPos = aStart.getLength() - 1;
1121 sal_Unicode c = aStart[ nPos ];
1122 // fdo#75264 use maFormulaChar to check if characters are used in function names
1123 ::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c );
1124 if ( p == maFormulaChar.end() )
1125 return false; // last character is not part of any function name, quit
1127 ::std::vector<sal_Unicode> aTemp;
1128 aTemp.push_back( c );
1129 for(sal_Int32 i = nPos - 1; i >= 0; --i)
1131 c = aStart[ i ];
1132 p = maFormulaChar.find( c );
1134 if (p == maFormulaChar.end())
1135 break;
1137 aTemp.push_back( c );
1140 ::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin();
1141 aResult = OUString( *rIt++ );
1142 while ( rIt != aTemp.rend() )
1143 aResult += OUString( *rIt++ );
1145 return true;
1148 void ScInputHandler::ShowFuncList( const ::std::vector< OUString > & rFuncStrVec )
1150 OUString aTipStr;
1151 OUString aFuncNameStr;
1152 OUString aDescFuncNameStr;
1153 ::std::vector<OUString>::const_iterator itStr = rFuncStrVec.begin();
1154 sal_Int32 nMaxFindNumber = 3;
1155 sal_Int32 nRemainFindNumber = nMaxFindNumber;
1156 for ( ; itStr != rFuncStrVec.end(); ++itStr )
1158 const OUString& rFunc = *itStr;
1159 if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
1161 aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
1163 else
1165 aFuncNameStr = rFunc;
1167 if ( itStr == rFuncStrVec.begin() )
1169 aTipStr = "[";
1170 aDescFuncNameStr = aFuncNameStr + "()";
1172 else
1174 aTipStr = aTipStr + ", ";
1176 aTipStr = aTipStr + aFuncNameStr;
1177 if ( itStr == rFuncStrVec.begin() )
1178 aTipStr += "]";
1179 if ( --nRemainFindNumber <= 0 )
1180 break;
1182 sal_Int32 nRemainNumber = rFuncStrVec.size() - nMaxFindNumber;
1183 if ( nRemainFindNumber == 0 && nRemainNumber > 0 )
1185 OUString aBufStr( aTipStr );
1186 OUString aMessage( ScGlobal::GetRscString( STR_FUNCTIONS_FOUND ) );
1187 aMessage = aMessage.replaceFirst("%2", OUString::number(nRemainNumber));
1188 aMessage = aMessage.replaceFirst("%1", aBufStr);
1189 aTipStr = aMessage;
1191 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1192 sal_Int32 nNextFStart = 0;
1193 const IFunctionDescription* ppFDesc;
1194 ::std::vector< OUString > aArgs;
1195 OUString eqPlusFuncName = "=" + aDescFuncNameStr;
1196 if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1198 if ( !ppFDesc->getFunctionName().isEmpty() )
1200 aTipStr += " : " + ppFDesc->getDescription();
1203 ShowTip( aTipStr );
1206 void ScInputHandler::UseFormulaData()
1208 EditView* pActiveView = pTopView ? pTopView : pTableView;
1210 // Formulas may only have 1 paragraph
1211 if ( pActiveView && pFormulaData && pEngine->GetParagraphCount() == 1 )
1213 OUString aParagraph = pEngine->GetText( 0 );
1214 ESelection aSel = pActiveView->GetSelection();
1215 aSel.Adjust();
1217 // Due to differences between table and input cell (e.g clipboard with line breaks),
1218 // the selection may not be in line with the EditEngine anymore.
1219 // Just return without any indication as to why.
1220 if ( aSel.nEndPos > aParagraph.getLength() )
1221 return;
1223 if ( aParagraph.getLength() > aSel.nEndPos &&
1224 ( ScGlobal::pCharClass->isLetterNumeric( aParagraph, aSel.nEndPos ) ||
1225 aParagraph[ aSel.nEndPos ] == '_' ||
1226 aParagraph[ aSel.nEndPos ] == '.' ||
1227 aParagraph[ aSel.nEndPos ] == '$' ) )
1228 return;
1230 // Is the cursor at the end of a word?
1231 if ( aSel.nEndPos > 0 )
1233 OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
1235 OUString aText;
1236 if ( GetFuncName( aSelText, aText ) )
1238 // function name is incomplete:
1239 // show matching functions name as tip above cell
1240 ::std::vector<OUString> aNewVec;
1241 miAutoPosFormula = pFormulaData->end();
1242 miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false);
1243 if (miAutoPosFormula != pFormulaData->end())
1245 // check if partial function name is not between quotes
1246 sal_Unicode cBetweenQuotes = 0;
1247 for ( int n = 0; n < aSelText.getLength(); n++ )
1249 if (cBetweenQuotes)
1251 if (aSelText[n] == cBetweenQuotes)
1252 cBetweenQuotes = 0;
1254 else if ( aSelText[ n ] == '"' )
1255 cBetweenQuotes = '"';
1256 else if ( aSelText[ n ] == '\'' )
1257 cBetweenQuotes = '\'';
1259 if ( cBetweenQuotes )
1260 return; // we're between quotes
1262 ShowFuncList(aNewVec);
1263 aAutoSearch = aText;
1265 return;
1268 // function name is complete:
1269 // show tip below the cell with function name and arguments of function
1270 ShowArgumentsTip( aSelText );
1275 void ScInputHandler::NextFormulaEntry( bool bBack )
1277 EditView* pActiveView = pTopView ? pTopView : pTableView;
1278 if ( pActiveView && pFormulaData )
1280 ::std::vector<OUString> aNewVec;
1281 ScTypedCaseStrSet::const_iterator itNew = findTextAll(*pFormulaData, miAutoPosFormula, aAutoSearch, aNewVec, bBack);
1282 if (itNew != pFormulaData->end())
1284 miAutoPosFormula = itNew;
1285 ShowFuncList( aNewVec );
1289 // For Tab we always call HideCursor first
1290 if (pActiveView)
1291 pActiveView->ShowCursor();
1294 namespace {
1296 bool needToExtendSelection(const OUString& rSelectedText, const OUString& rInsertText)
1298 return !rInsertText.startsWithIgnoreAsciiCase(rSelectedText);
1301 void completeFunction( EditView* pView, const OUString& rInsert, bool& rParInserted )
1303 if (pView)
1305 ESelection aSel = pView->GetSelection();
1306 --aSel.nStartPos;
1307 --aSel.nEndPos;
1308 pView->SetSelection(aSel);
1309 pView->SelectCurrentWord();
1311 // a dot and underscore are word separators so we need special
1312 // treatment for any formula containing a dot or underscore
1313 if(rInsert.indexOf(".") != -1 || rInsert.indexOf("_") != -1)
1315 // need to make sure that we replace also the part before the dot
1316 // go through the word to find the match with the insert string
1317 aSel = pView->GetSelection();
1318 ESelection aOldSelection = aSel;
1319 OUString aSelectedText = pView->GetSelected();
1320 if ( needToExtendSelection( aSelectedText, rInsert ) )
1322 while(needToExtendSelection(aSelectedText, rInsert))
1324 assert(aSel.nStartPos > 0);
1325 --aSel.nStartPos;
1326 aSel.nEndPos = aSel.nStartPos;
1327 pView->SetSelection(aSel);
1328 pView->SelectCurrentWord();
1329 aSelectedText = pView->GetSelected();
1331 aSel.nStartPos = aSel.nEndPos - ( aSelectedText.getLength() - 1 );
1333 else
1335 aSel.nStartPos = aSel.nEndPos - aSelectedText.getLength();
1337 aSel.nEndPos = aOldSelection.nEndPos;
1338 pView->SetSelection(aSel);
1341 OUString aInsStr = rInsert;
1342 sal_Int32 nInsLen = aInsStr.getLength();
1343 bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '('
1344 && aInsStr[nInsLen-1] == ')' );
1345 if ( bDoParen )
1347 // Do not insert parentheses after function names if there already are some
1348 // (e.g. if the function name was edited).
1349 ESelection aWordSel = pView->GetSelection();
1350 OUString aOld = pView->GetEditEngine()->GetText(0);
1352 // aWordSel.EndPos points one behind string if word at end
1353 if (aWordSel.nEndPos < aOld.getLength())
1355 sal_Unicode cNext = aOld[aWordSel.nEndPos];
1356 if ( cNext == '(' )
1358 bDoParen = false;
1359 aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses
1364 pView->InsertText( aInsStr );
1366 if ( bDoParen ) // Put cursor between parentheses
1368 aSel = pView->GetSelection();
1369 --aSel.nStartPos;
1370 --aSel.nEndPos;
1371 pView->SetSelection(aSel);
1373 rParInserted = true;
1380 void ScInputHandler::PasteFunctionData()
1382 if (pFormulaData && miAutoPosFormula != pFormulaData->end())
1384 const ScTypedStrData& rData = *miAutoPosFormula;
1385 OUString aInsert = rData.GetString();
1386 if (aInsert[aInsert.getLength()-1] == cParenthesesReplacement)
1387 aInsert = aInsert.copy( 0, aInsert.getLength()-1) + "()";
1388 bool bParInserted = false;
1390 DataChanging(); // Cannot be new
1391 completeFunction( pTopView, aInsert, bParInserted );
1392 completeFunction( pTableView, aInsert, bParInserted );
1393 DataChanged();
1394 ShowTipCursor();
1396 if (bParInserted)
1397 AutoParAdded();
1400 HideTip();
1402 EditView* pActiveView = pTopView ? pTopView : pTableView;
1403 if (pActiveView)
1404 pActiveView->ShowCursor();
1407 // Calculate selection and display as tip help
1408 static OUString lcl_Calculate( const OUString& rFormula, ScDocument* pDoc, const ScAddress &rPos )
1410 //TODO: Merge with ScFormulaDlg::CalcValue and move into Document!
1411 // Quotation marks for Strings are only inserted here.
1413 if(rFormula.isEmpty())
1414 return OUString();
1416 std::unique_ptr<ScSimpleFormulaCalculator> pCalc( new ScSimpleFormulaCalculator( pDoc, rPos, rFormula, false ) );
1418 // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range
1419 // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own
1420 bool bColRowName = pCalc->HasColRowName();
1421 if ( bColRowName )
1423 // ColRowName in RPN code?
1424 if ( pCalc->GetCode()->GetCodeLen() <= 1 )
1425 { // ==1: Single one is as a Parameter always a Range
1426 // ==0: It might be one, if ...
1427 OUStringBuffer aBraced;
1428 aBraced.append('(');
1429 aBraced.append(rFormula);
1430 aBraced.append(')');
1431 pCalc.reset( new ScSimpleFormulaCalculator( pDoc, rPos, aBraced.makeStringAndClear(), false ) );
1433 else
1434 bColRowName = false;
1437 sal_uInt16 nErrCode = pCalc->GetErrCode();
1438 if ( nErrCode != 0 )
1439 return ScGlobal::GetErrorString(nErrCode);
1441 SvNumberFormatter& aFormatter = *(pDoc->GetFormatTable());
1442 OUString aValue;
1443 if ( pCalc->IsValue() )
1445 double n = pCalc->GetValue();
1446 sal_uLong nFormat = aFormatter.GetStandardFormat( n, 0,
1447 pCalc->GetFormatType(), ScGlobal::eLnge );
1448 aFormatter.GetInputLineString( n, nFormat, aValue );
1449 //! display OutputString but insert InputLineString
1451 else
1453 OUString aStr = pCalc->GetString().getString();
1454 sal_uLong nFormat = aFormatter.GetStandardFormat(
1455 pCalc->GetFormatType(), ScGlobal::eLnge);
1457 Color* pColor;
1458 aFormatter.GetOutputString( aStr, nFormat,
1459 aValue, &pColor );
1462 aValue = "\"" + aValue + "\"";
1463 //! Escape quotation marks in String??
1466 ScRange aTestRange;
1467 if ( bColRowName || (aTestRange.Parse(rFormula) & ScRefFlags::VALID) )
1468 aValue = aValue + " ...";
1470 return aValue;
1473 void ScInputHandler::FormulaPreview()
1475 OUString aValue;
1476 EditView* pActiveView = pTopView ? pTopView : pTableView;
1477 if ( pActiveView && pActiveViewSh )
1479 OUString aPart = pActiveView->GetSelected();
1480 if (aPart.isEmpty())
1481 aPart = pEngine->GetText(0);
1482 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1483 aValue = lcl_Calculate( aPart, &rDoc, aCursorPos );
1486 if (!aValue.isEmpty())
1488 ShowTip( aValue ); // Display as QuickHelp
1489 aManualTip = aValue; // Set after ShowTip
1490 if (pFormulaData)
1491 miAutoPosFormula = pFormulaData->end();
1492 if (pColumnData)
1493 miAutoPosColumn = pColumnData->end();
1497 void ScInputHandler::PasteManualTip()
1499 // Three dots at the end -> Range reference -> do not insert
1500 // FIXME: Once we have matrix constants, we can change this
1501 sal_Int32 nTipLen = aManualTip.getLength();
1502 sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen));
1503 if ( nTipLen && ( nTipLen < 3 || aManualTip.copy( nTipLen2-3 ) != "..." ) )
1505 DataChanging(); // Cannot be new
1507 OUString aInsert = aManualTip;
1508 EditView* pActiveView = pTopView ? pTopView : pTableView;
1509 if (!pActiveView->HasSelection())
1511 // Nothing selected -> select everything
1512 sal_Int32 nOldLen = pEngine->GetTextLen(0);
1513 ESelection aAllSel( 0, 0, 0, nOldLen );
1514 if ( pTopView )
1515 pTopView->SetSelection( aAllSel );
1516 if ( pTableView )
1517 pTableView->SetSelection( aAllSel );
1520 ESelection aSel = pActiveView->GetSelection();
1521 aSel.Adjust();
1522 OSL_ENSURE( !aSel.nStartPara && !aSel.nEndPara, "Too many paragraphs in Formula" );
1523 if ( !aSel.nStartPos ) // Selection from the start?
1525 if ( aSel.nEndPos == pEngine->GetTextLen(0) )
1527 // Everything selected -> skip quotation marks
1528 if ( aInsert[0] == '"' )
1529 aInsert = aInsert.copy(1);
1530 sal_Int32 nInsLen = aInsert.getLength();
1531 if ( aInsert.endsWith("\"") )
1532 aInsert = aInsert.copy( 0, nInsLen-1 );
1534 else if ( aSel.nEndPos )
1536 // Not everything selected -> do not overwrite equality sign
1537 //FIXME: Even double equality signs??
1538 aSel.nStartPos = 1;
1539 if ( pTopView )
1540 pTopView->SetSelection( aSel );
1541 if ( pTableView )
1542 pTableView->SetSelection( aSel );
1545 if ( pTopView )
1546 pTopView->InsertText( aInsert, true );
1547 if ( pTableView )
1548 pTableView->InsertText( aInsert, true );
1550 DataChanged();
1553 HideTip();
1556 void ScInputHandler::ResetAutoPar()
1558 nAutoPar = 0;
1561 void ScInputHandler::AutoParAdded()
1563 ++nAutoPar; // Closing parenthesis can be overwritten
1566 bool ScInputHandler::CursorAtClosingPar()
1568 // Test if the cursor is before a closing parenthesis
1569 // Selection from SetReference has been removed before
1570 EditView* pActiveView = pTopView ? pTopView : pTableView;
1571 if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode )
1573 ESelection aSel = pActiveView->GetSelection();
1574 sal_Int32 nPos = aSel.nStartPos;
1575 OUString aFormula = pEngine->GetText(0);
1576 if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' )
1577 return true;
1579 return false;
1582 void ScInputHandler::SkipClosingPar()
1584 // this is called when a ')' is typed and the cursor is before a ')'
1585 // that can be overwritten -> just set the cursor behind the ')'
1587 EditView* pActiveView = pTopView ? pTopView : pTableView;
1588 if (pActiveView)
1590 ESelection aSel = pActiveView->GetSelection();
1591 ++aSel.nStartPos;
1592 ++aSel.nEndPos;
1594 // this is in a formula (only one paragraph), so the selection
1595 // can be used directly for the TopView
1597 if ( pTopView )
1598 pTopView->SetSelection( aSel );
1599 if ( pTableView )
1600 pTableView->SetSelection( aSel );
1603 OSL_ENSURE(nAutoPar, "SkipClosingPar: count is wrong");
1604 --nAutoPar;
1607 // Auto input
1609 void ScInputHandler::GetColData()
1611 if ( pActiveViewSh )
1613 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1615 if ( pColumnData )
1616 pColumnData->clear();
1617 else
1618 pColumnData = new ScTypedCaseStrSet;
1620 std::vector<ScTypedStrData> aEntries;
1621 rDoc.GetDataEntries(
1622 aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aEntries, true);
1623 if (!aEntries.empty())
1624 pColumnData->insert(aEntries.begin(), aEntries.end());
1626 miAutoPosColumn = pColumnData->end();
1630 void ScInputHandler::UseColData() // When typing
1632 EditView* pActiveView = pTopView ? pTopView : pTableView;
1633 if ( pActiveView && pColumnData )
1635 // Only change when cursor is at the end
1636 ESelection aSel = pActiveView->GetSelection();
1637 aSel.Adjust();
1639 sal_Int32 nParCnt = pEngine->GetParagraphCount();
1640 if ( aSel.nEndPara+1 == nParCnt )
1642 sal_Int32 nParLen = pEngine->GetTextLen( aSel.nEndPara );
1643 if ( aSel.nEndPos == nParLen )
1645 OUString aText = GetEditText(pEngine);
1646 if (!aText.isEmpty())
1648 OUString aNew;
1649 miAutoPosColumn = pColumnData->end();
1650 miAutoPosColumn = findText(*pColumnData, miAutoPosColumn, aText, aNew, false);
1651 if (miAutoPosColumn != pColumnData->end())
1653 // Strings can contain line endings (e.g. due to dBase import),
1654 // which would result in multiple paragraphs here, which is not desirable.
1655 //! Then GetExactMatch doesn't work either
1656 lcl_RemoveLineEnd( aNew );
1658 // Keep paragraph, just append the rest
1659 //! Exact replacement in EnterHandler !!!
1660 // One Space between paragraphs:
1661 sal_Int32 nEdLen = pEngine->GetTextLen() + nParCnt - 1;
1662 OUString aIns = aNew.copy(nEdLen);
1664 // Selection must be "backwards", so the cursor stays behind the last
1665 // typed character
1666 ESelection aSelection( aSel.nEndPara, aSel.nEndPos + aIns.getLength(),
1667 aSel.nEndPara, aSel.nEndPos );
1669 // When editing in input line, apply to both edit views
1670 if ( pTableView )
1672 pTableView->InsertText( aIns );
1673 pTableView->SetSelection( aSelection );
1675 if ( pTopView )
1677 pTopView->InsertText( aIns );
1678 pTopView->SetSelection( aSelection );
1681 aAutoSearch = aText; // To keep searching - nAutoPos is set
1683 if (aText.getLength() == aNew.getLength())
1685 // If the inserted text is found, consume TAB only if there's more coming
1686 OUString aDummy;
1687 ScTypedCaseStrSet::const_iterator itNextPos =
1688 findText(*pColumnData, miAutoPosColumn, aText, aDummy, false);
1689 bUseTab = itNextPos != pColumnData->end();
1691 else
1692 bUseTab = true;
1700 void ScInputHandler::NextAutoEntry( bool bBack )
1702 EditView* pActiveView = pTopView ? pTopView : pTableView;
1703 if ( pActiveView && pColumnData )
1705 if (miAutoPosColumn != pColumnData->end() && !aAutoSearch.isEmpty())
1707 // Is the selection still valid (could be changed via the mouse)?
1708 ESelection aSel = pActiveView->GetSelection();
1709 aSel.Adjust();
1710 sal_Int32 nParCnt = pEngine->GetParagraphCount();
1711 if ( aSel.nEndPara+1 == nParCnt && aSel.nStartPara == aSel.nEndPara )
1713 OUString aText = GetEditText(pEngine);
1714 sal_Int32 nSelLen = aSel.nEndPos - aSel.nStartPos;
1715 sal_Int32 nParLen = pEngine->GetTextLen( aSel.nEndPara );
1716 if ( aSel.nEndPos == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen )
1718 OUString aNew;
1719 ScTypedCaseStrSet::const_iterator itNew =
1720 findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack);
1722 if (itNew != pColumnData->end())
1724 // match found!
1725 miAutoPosColumn = itNew;
1726 bInOwnChange = true; // disable ModifyHdl (reset below)
1728 lcl_RemoveLineEnd( aNew );
1729 OUString aIns = aNew.copy(aAutoSearch.getLength());
1731 // when editing in input line, apply to both edit views
1732 if ( pTableView )
1734 pTableView->DeleteSelected();
1735 pTableView->InsertText( aIns );
1736 pTableView->SetSelection( ESelection(
1737 aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
1738 aSel.nEndPara, aSel.nStartPos ) );
1740 if ( pTopView )
1742 pTopView->DeleteSelected();
1743 pTopView->InsertText( aIns );
1744 pTopView->SetSelection( ESelection(
1745 aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
1746 aSel.nEndPara, aSel.nStartPos ) );
1749 bInOwnChange = false;
1756 // For Tab, HideCursor was always called first
1757 if (pActiveView)
1758 pActiveView->ShowCursor();
1761 // Highlight parentheses
1762 void ScInputHandler::UpdateParenthesis()
1764 // Find parentheses
1765 //TODO: Can we disable parentheses highlighting per parentheses?
1766 bool bFound = false;
1767 if ( bFormulaMode && eMode != SC_INPUT_TOP )
1769 if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom
1771 ESelection aSel = pTableView->GetSelection();
1772 if (aSel.nStartPos)
1774 // Examine character left to the cursor
1775 sal_Int32 nPos = aSel.nStartPos - 1;
1776 OUString aFormula = pEngine->GetText(0);
1777 sal_Unicode c = aFormula[nPos];
1778 if ( c == '(' || c == ')' )
1780 sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos );
1781 if ( nOther != -1 )
1783 SfxItemSet aSet( pEngine->GetEmptyItemSet() );
1784 aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
1786 //! Distinguish if cell is already highlighted!!!!
1787 if (bParenthesisShown)
1789 // Remove old highlighting
1790 sal_Int32 nCount = pEngine->GetParagraphCount();
1791 for (sal_Int32 i=0; i<nCount; i++)
1792 pEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
1795 ESelection aSelThis( 0,nPos, 0,nPos+1 );
1796 pEngine->QuickSetAttribs( aSet, aSelThis );
1797 ESelection aSelOther( 0,nOther, 0,nOther+1 );
1798 pEngine->QuickSetAttribs( aSet, aSelOther );
1800 // Dummy InsertText for Update and Paint (selection is empty)
1801 pTableView->InsertText( EMPTY_OUSTRING );
1803 bFound = true;
1808 // mark parenthesis right of cursor if it will be overwritten (nAutoPar)
1809 // with different color (COL_LIGHTBLUE) ??
1813 // Remove old highlighting, if no new one is set
1814 if ( bParenthesisShown && !bFound && pTableView )
1816 sal_Int32 nCount = pEngine->GetParagraphCount();
1817 for (sal_Int32 i=0; i<nCount; i++)
1818 pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
1821 bParenthesisShown = bFound;
1824 void ScInputHandler::ViewShellGone(ScTabViewShell* pViewSh) // Executed synchronously!
1826 if ( pViewSh == pActiveViewSh )
1828 delete pLastState;
1829 pLastState = nullptr;
1830 pLastPattern = nullptr;
1833 if ( pViewSh == pRefViewSh )
1835 //! The input from the EnterHandler does not arrive anymore
1836 // We end the EditMode anyways
1837 EnterHandler();
1838 bFormulaMode = false;
1839 pRefViewSh = nullptr;
1840 SfxGetpApp()->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) );
1841 SC_MOD()->SetRefInputHdl(nullptr);
1842 if (pInputWin)
1843 pInputWin->SetFormulaMode(false);
1844 UpdateAutoCorrFlag();
1847 pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
1849 if ( pActiveViewSh && pActiveViewSh == pViewSh )
1851 OSL_FAIL("pActiveViewSh is gone");
1852 pActiveViewSh = nullptr;
1855 if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
1856 UpdateRefDevice(); // Don't keep old document's printer as RefDevice
1859 void ScInputHandler::UpdateActiveView()
1861 ImplCreateEditEngine();
1863 // #i20588# Don't rely on focus to find the active edit view. Instead, the
1864 // active pane at the start of editing is now stored (GetEditActivePart).
1865 // GetActiveWin (the currently active pane) fails for ref input across the
1866 // panes of a split view.
1868 vcl::Window* pShellWin = pActiveViewSh ?
1869 pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData().GetEditActivePart() ) :
1870 nullptr;
1872 sal_uInt16 nCount = pEngine->GetViewCount();
1873 if (nCount > 0)
1875 pTableView = pEngine->GetView();
1876 for (sal_uInt16 i=1; i<nCount; i++)
1878 EditView* pThis = pEngine->GetView(i);
1879 vcl::Window* pWin = pThis->GetWindow();
1880 if ( pWin==pShellWin )
1881 pTableView = pThis;
1884 else
1885 pTableView = nullptr;
1887 // setup the pTableView editeng for tiled rendering to get cursor and selections
1888 if (pActiveViewSh && pTableView)
1890 ScDocShell* pDocShell = pActiveViewSh->GetViewData().GetDocShell();
1891 if (comphelper::LibreOfficeKit::isActive())
1893 ScDrawLayer *pDrawLayer = pDocShell->GetDocument().GetDrawLayer();
1894 pTableView->registerLibreOfficeKitCallback(pDrawLayer);
1898 if (pInputWin && eMode == SC_INPUT_TOP )
1899 pTopView = pInputWin->GetEditView();
1900 else
1901 pTopView = nullptr;
1904 void ScInputHandler::SetInputWindow( ScInputWindow* pNew )
1906 pInputWin = pNew;
1909 void ScInputHandler::StopInputWinEngine( bool bAll )
1911 if (pInputWin)
1912 pInputWin->StopEditEngine( bAll );
1914 pTopView = nullptr; // invalid now
1917 EditView* ScInputHandler::GetActiveView()
1919 UpdateActiveView();
1920 return pTopView ? pTopView : pTableView;
1923 void ScInputHandler::ForgetLastPattern()
1925 pLastPattern = nullptr;
1926 if ( !pLastState && pActiveViewSh )
1927 pActiveViewSh->UpdateInputHandler( true ); // Get status again
1928 else
1929 NotifyChange( pLastState, true );
1932 void ScInputHandler::UpdateAdjust( sal_Unicode cTyped )
1934 SvxAdjust eSvxAdjust;
1935 switch (eAttrAdjust)
1937 case SVX_HOR_JUSTIFY_STANDARD:
1939 bool bNumber = false;
1940 if (cTyped) // Restarted
1941 bNumber = (cTyped>='0' && cTyped<='9'); // Ony ciphers are numbers
1942 else if ( pActiveViewSh )
1944 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1945 bNumber = ( rDoc.GetCellType( aCursorPos ) == CELLTYPE_VALUE );
1947 eSvxAdjust = bNumber ? SVX_ADJUST_RIGHT : SVX_ADJUST_LEFT;
1949 break;
1950 case SVX_HOR_JUSTIFY_BLOCK:
1951 eSvxAdjust = SVX_ADJUST_BLOCK;
1952 break;
1953 case SVX_HOR_JUSTIFY_CENTER:
1954 eSvxAdjust = SVX_ADJUST_CENTER;
1955 break;
1956 case SVX_HOR_JUSTIFY_RIGHT:
1957 eSvxAdjust = SVX_ADJUST_RIGHT;
1958 break;
1959 default: // SVX_HOR_JUSTIFY_LEFT
1960 eSvxAdjust = SVX_ADJUST_LEFT;
1961 break;
1964 bool bAsianVertical = pLastPattern &&
1965 static_cast<const SfxBoolItem&>(pLastPattern->GetItem( ATTR_STACKED )).GetValue() &&
1966 static_cast<const SfxBoolItem&>(pLastPattern->GetItem( ATTR_VERTICAL_ASIAN )).GetValue();
1967 if ( bAsianVertical )
1969 // Always edit at top of cell -> LEFT when editing vertically
1970 eSvxAdjust = SVX_ADJUST_LEFT;
1973 pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
1974 pEngine->SetDefaults( *pEditDefaults );
1976 nEditAdjust = sal::static_int_cast<sal_uInt16>(eSvxAdjust); //! set at ViewData or with PostEditView
1978 pEngine->SetVertical( bAsianVertical );
1981 void ScInputHandler::RemoveAdjust()
1983 // Delete hard alignement attributes
1984 bool bUndo = pEngine->IsUndoEnabled();
1985 if ( bUndo )
1986 pEngine->EnableUndo( false );
1988 // Non-default paragraph attributes (e.g. from clipboard)
1989 // must be turned into character attributes
1990 pEngine->RemoveParaAttribs();
1992 if ( bUndo )
1993 pEngine->EnableUndo( true );
1997 void ScInputHandler::RemoveRangeFinder()
1999 // Delete pRangeFindList and colors
2000 pEngine->SetUpdateMode(false);
2001 sal_Int32 nCount = pEngine->GetParagraphCount(); // Could just have been inserted
2002 for (sal_Int32 i=0; i<nCount; i++)
2003 pEngine->RemoveCharAttribs( i, EE_CHAR_COLOR );
2004 pEngine->SetUpdateMode(true);
2006 EditView* pActiveView = pTopView ? pTopView : pTableView;
2007 pActiveView->ShowCursor( false );
2009 DeleteRangeFinder(); // Deletes the list and the labels on the table
2012 bool ScInputHandler::StartTable( sal_Unicode cTyped, bool bFromCommand, bool bInputActivated )
2014 bool bNewTable = false;
2016 if (bModified || !ValidCol(aCursorPos.Col()))
2017 return false;
2019 if (pActiveViewSh)
2021 ImplCreateEditEngine();
2022 UpdateActiveView();
2023 SyncViews();
2025 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2027 const ScMarkData& rMark = pActiveViewSh->GetViewData().GetMarkData();
2028 ScEditableTester aTester;
2029 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
2030 aTester.TestSelection( &rDoc, rMark );
2031 else
2032 aTester.TestSelectedBlock(
2033 &rDoc, aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Col(), aCursorPos.Row(), rMark );
2035 bool bStartInputMode = true;
2037 if (!aTester.IsEditable())
2039 bProtected = true;
2040 // We allow read-only input mode activation regardless
2041 // whether it's part of an array or not or whether explicit cell
2042 // activation is requested (double-click or F2) or a click in input
2043 // line.
2044 bool bShowError = (!bInputActivated || aTester.GetMessageId() != STR_PROTECTIONERR) &&
2045 !pActiveViewSh->GetViewData().GetDocShell()->IsReadOnly();
2046 if (bShowError)
2048 eMode = SC_INPUT_NONE;
2049 StopInputWinEngine( true );
2050 UpdateFormulaMode();
2051 if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) )
2053 // Prevent repeated error messages for the same cell from command events
2054 // (for keyboard events, multiple messages are wanted).
2055 // Set the flag before showing the error message because the command handler
2056 // for the next IME command may be called when showing the dialog.
2057 if ( bFromCommand )
2058 bCommandErrorShown = true;
2060 pActiveViewSh->GetActiveWin()->GrabFocus();
2061 pActiveViewSh->ErrorMessage(aTester.GetMessageId());
2063 bStartInputMode = false;
2067 if (bStartInputMode)
2069 // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise)
2070 pEngine->SetUpdateMode( false );
2072 // Take over attributes in EditEngine
2073 const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(),
2074 aCursorPos.Row(),
2075 aCursorPos.Tab() );
2076 if (pPattern != pLastPattern)
2078 // Percent format?
2079 const SfxItemSet& rAttrSet = pPattern->GetItemSet();
2080 const SfxPoolItem* pItem;
2082 if ( SfxItemState::SET == rAttrSet.GetItemState( ATTR_VALUE_FORMAT, true, &pItem ) )
2084 sal_uLong nFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
2085 bCellHasPercentFormat = ( css::util::NumberFormat::PERCENT ==
2086 rDoc.GetFormatTable()->GetType( nFormat ) );
2088 else
2089 bCellHasPercentFormat = false; // Default: no percent
2091 // Validity specified?
2092 if ( SfxItemState::SET == rAttrSet.GetItemState( ATTR_VALIDDATA, true, &pItem ) )
2093 nValidation = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
2094 else
2095 nValidation = 0;
2097 // EditEngine Defaults
2098 // In no case SetParaAttribs, because the EditEngine might already
2099 // be filled (for Edit cells).
2100 // SetParaAttribs would change the content.
2102 //! The SetDefaults is now (since MUST/src602
2103 //! EditEngine changes) implemented as a SetParaAttribs.
2104 //! Any problems?
2106 pPattern->FillEditItemSet( pEditDefaults );
2107 pEngine->SetDefaults( *pEditDefaults );
2108 pLastPattern = pPattern;
2109 bLastIsSymbol = pPattern->IsSymbolFont();
2111 // Background color must be known for automatic font color.
2112 // For transparent cell background, the document background color must be used.
2114 Color aBackCol = static_cast<const SvxBrushItem&>(
2115 pPattern->GetItem( ATTR_BACKGROUND )).GetColor();
2116 ScModule* pScMod = SC_MOD();
2117 if ( aBackCol.GetTransparency() > 0 ||
2118 Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2119 aBackCol.SetColor( pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
2120 pEngine->SetBackgroundColor( aBackCol );
2122 // Adjustment
2123 eAttrAdjust = (SvxCellHorJustify)static_cast<const SvxHorJustifyItem&>(pPattern->
2124 GetItem(ATTR_HOR_JUSTIFY)).GetValue();
2125 if ( eAttrAdjust == SVX_HOR_JUSTIFY_REPEAT &&
2126 static_cast<const SfxBoolItem&>(pPattern->GetItem(ATTR_LINEBREAK)).GetValue() )
2128 // #i31843# "repeat" with "line breaks" is treated as default alignement
2129 eAttrAdjust = SVX_HOR_JUSTIFY_STANDARD;
2133 // UpdateSpellSettings enables online spelling if needed
2134 // -> also call if attributes are unchanged
2135 UpdateSpellSettings( true ); // uses pLastPattern
2137 // Fill EditEngine
2138 OUString aStr;
2139 if (bTextValid)
2141 pEngine->SetText(aCurrentText);
2142 aStr = aCurrentText;
2143 bTextValid = false;
2144 aCurrentText.clear();
2146 else
2147 aStr = GetEditText(pEngine);
2149 if (aStr.startsWith("{=") && aStr.endsWith("}") ) // Matrix formula?
2151 aStr = aStr.copy(1, aStr.getLength() -2);
2152 pEngine->SetText(aStr);
2153 if ( pInputWin )
2154 pInputWin->SetTextString(aStr);
2157 UpdateAdjust( cTyped );
2159 if ( bAutoComplete )
2160 GetColData();
2162 if ( !aStr.isEmpty() && ( aStr[0] == '=' || aStr[0] == '+' || aStr[0] == '-' ) &&
2163 !cTyped && !bCreatingFuncView )
2164 InitRangeFinder(aStr); // Formula is being edited -> RangeFinder
2166 bNewTable = true; // -> PostEditView Call
2170 if (!bProtected && pInputWin)
2171 pInputWin->SetOkCancelMode();
2173 return bNewTable;
2176 static void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel )
2178 OSL_ENSURE( rSel.nStartPara==0 && rSel.nEndPara==0, "SetTopSelection: Para != 0" );
2180 EditEngine* pEngine = pEditView->GetEditEngine();
2181 sal_Int32 nCount = pEngine->GetParagraphCount();
2182 if (nCount > 1)
2184 sal_Int32 nParLen = pEngine->GetTextLen(rSel.nStartPara);
2185 while (rSel.nStartPos > nParLen && rSel.nStartPara+1 < nCount)
2187 rSel.nStartPos -= nParLen + 1; // Including space from line break
2188 nParLen = pEngine->GetTextLen(++rSel.nStartPara);
2191 nParLen = pEngine->GetTextLen(rSel.nEndPara);
2192 while (rSel.nEndPos > nParLen && rSel.nEndPara+1 < nCount)
2194 rSel.nEndPos -= nParLen + 1; // Including space from line break
2195 nParLen = pEngine->GetTextLen(++rSel.nEndPara);
2199 ESelection aSel = pEditView->GetSelection();
2201 if ( rSel.nStartPara != aSel.nStartPara || rSel.nEndPara != aSel.nEndPara
2202 || rSel.nStartPos != aSel.nStartPos || rSel.nEndPos != aSel.nEndPos )
2203 pEditView->SetSelection( rSel );
2206 void ScInputHandler::SyncViews( EditView* pSourceView )
2208 if (pSourceView)
2210 bool bSelectionForTopView = false;
2211 if (pTopView && pTopView != pSourceView)
2212 bSelectionForTopView = true;
2213 bool bSelectionForTableView = false;
2214 if (pTableView && pTableView != pSourceView)
2215 bSelectionForTableView = true;
2216 if (bSelectionForTopView || bSelectionForTableView)
2218 ESelection aSel(pSourceView->GetSelection());
2219 if (bSelectionForTopView)
2220 pTopView->SetSelection(aSel);
2221 if (bSelectionForTableView)
2222 lcl_SetTopSelection(pTableView, aSel);
2225 // Only sync selection from topView if we are actually editing there
2226 else if (pTopView && pTableView)
2228 ESelection aSel(pTopView->GetSelection());
2229 lcl_SetTopSelection( pTableView, aSel );
2233 IMPL_LINK_NOARG_TYPED(ScInputHandler, ModifyHdl, LinkParamNone*, void)
2235 if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) &&
2236 pEngine && pEngine->GetUpdateMode() && pInputWin )
2238 // Update input line from ModifyHdl for changes that are not
2239 // wrapped by DataChanging/DataChanged calls (like Drag&Drop)
2240 OUString aText(ScEditUtil::GetMultilineString(*pEngine));
2241 lcl_RemoveTabs(aText);
2242 pInputWin->SetTextString(aText);
2247 * @return true means new view created
2249 bool ScInputHandler::DataChanging( sal_Unicode cTyped, bool bFromCommand )
2251 if (pActiveViewSh)
2252 pActiveViewSh->GetViewData().SetPasteMode( SC_PASTE_NONE );
2253 bInOwnChange = true; // disable ModifyHdl (reset in DataChanged)
2255 if ( eMode == SC_INPUT_NONE )
2256 return StartTable( cTyped, bFromCommand, false );
2257 else
2258 return false;
2261 void ScInputHandler::DataChanged( bool bFromTopNotify, bool bSetModified )
2263 ImplCreateEditEngine();
2265 if (eMode==SC_INPUT_NONE)
2266 eMode = SC_INPUT_TYPE;
2268 if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify )
2270 // table EditEngine is formatted below, input line needs formatting after paste
2271 // #i20282# not when called from the input line's modify handler
2272 pTopView->GetEditEngine()->QuickFormatDoc( true );
2274 // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it
2275 // can't safely access the EditEngine's current view, so the cursor has to be
2276 // shown again here.
2277 pTopView->ShowCursor();
2280 if (bSetModified)
2281 bModified = true;
2282 bSelIsRef = false;
2284 if ( pRangeFindList && !bInRangeUpdate )
2285 RemoveRangeFinder(); // Delete attributes and labels
2287 UpdateParenthesis(); // Highlight parentheses anew
2289 if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE)
2291 OUString aText;
2292 if (pInputWin)
2293 aText = ScEditUtil::GetMultilineString(*pEngine);
2294 else
2295 aText = GetEditText(pEngine);
2296 lcl_RemoveTabs(aText);
2298 if ( pInputWin )
2299 pInputWin->SetTextString( aText );
2301 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
2302 ScDocument& rDoc = pDocSh->GetDocument();
2303 if ( comphelper::LibreOfficeKit::isActive() )
2304 rDoc.GetDrawLayer()->libreOfficeKitCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8().getStr());
2307 // If the cursor is before the end of a paragraph, parts are being pushed to
2308 // the right (independently from the eMode) -> Adapt View!
2309 // If the cursor is at the end, the StatusHandler of the ViewData is sufficient.
2311 // First make sure the status handler is called now if the cursor
2312 // is outside the visible area
2313 pEngine->QuickFormatDoc();
2315 EditView* pActiveView = pTopView ? pTopView : pTableView;
2316 if (pActiveView && pActiveViewSh)
2318 ScViewData& rViewData = pActiveViewSh->GetViewData();
2320 bool bNeedGrow = ( nEditAdjust != SVX_ADJUST_LEFT ); // Always right-aligned
2321 if (!bNeedGrow)
2323 // Cursor before the end?
2324 ESelection aSel = pActiveView->GetSelection();
2325 aSel.Adjust();
2326 bNeedGrow = ( aSel.nEndPos != pEngine->GetTextLen(aSel.nEndPara) );
2328 if (!bNeedGrow)
2330 bNeedGrow = rViewData.GetDocument()->IsLayoutRTL( rViewData.GetTabNo() );
2332 if (bNeedGrow)
2334 // Adjust inplace view
2335 rViewData.EditGrowY();
2336 rViewData.EditGrowX();
2340 UpdateFormulaMode();
2341 bTextValid = false; // Changes only in the EditEngine
2342 bInOwnChange = false;
2345 void ScInputHandler::UpdateFormulaMode()
2347 SfxApplication* pSfxApp = SfxGetpApp();
2349 bool bIsFormula = !bProtected && pEngine->GetParagraphCount() == 1;
2350 if (bIsFormula)
2352 const OUString& rText = pEngine->GetText(0);
2353 bIsFormula = !rText.isEmpty() &&
2354 (rText[0] == '=' || rText[0] == '+' || rText[0] == '-');
2357 if ( bIsFormula )
2359 if (!bFormulaMode)
2361 bFormulaMode = true;
2362 pRefViewSh = pActiveViewSh;
2363 pSfxApp->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) );
2364 SC_MOD()->SetRefInputHdl(this);
2365 if (pInputWin)
2366 pInputWin->SetFormulaMode(true);
2368 if ( bAutoComplete )
2369 GetFormulaData();
2371 UpdateParenthesis();
2372 UpdateAutoCorrFlag();
2375 else // Deactivate
2377 if (bFormulaMode)
2379 ShowRefFrame();
2380 bFormulaMode = false;
2381 pRefViewSh = nullptr;
2382 pSfxApp->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) );
2383 SC_MOD()->SetRefInputHdl(nullptr);
2384 if (pInputWin)
2385 pInputWin->SetFormulaMode(false);
2386 UpdateAutoCorrFlag();
2391 void ScInputHandler::ShowRefFrame()
2393 // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat
2394 // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh.
2395 // A local variable is used instead.
2396 ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
2397 if ( pRefViewSh && pRefViewSh != pVisibleSh )
2399 bool bFound = false;
2400 SfxViewFrame* pRefFrame = pRefViewSh->GetViewFrame();
2401 SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst();
2402 while ( pOneFrame && !bFound )
2404 if ( pOneFrame == pRefFrame )
2405 bFound = true;
2406 pOneFrame = SfxViewFrame::GetNext( *pOneFrame );
2409 if (bFound)
2411 // We count on Activate working synchronously here
2412 // (pActiveViewSh is set while doing so)
2413 pRefViewSh->SetActive(); // Appear and SetViewFrame
2415 // pLastState is set correctly in the NotifyChange from the Activate
2417 else
2419 OSL_FAIL("ViewFrame for reference input is not here anymore");
2424 void ScInputHandler::RemoveSelection()
2426 EditView* pActiveView = pTopView ? pTopView : pTableView;
2427 if (!pActiveView)
2428 return;
2430 ESelection aSel = pActiveView->GetSelection();
2431 aSel.nStartPara = aSel.nEndPara;
2432 aSel.nStartPos = aSel.nEndPos;
2433 if (pTableView)
2434 pTableView->SetSelection( aSel );
2435 if (pTopView)
2436 pTopView->SetSelection( aSel );
2439 void ScInputHandler::InvalidateAttribs()
2441 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
2442 if (pViewFrm)
2444 SfxBindings& rBindings = pViewFrm->GetBindings();
2446 rBindings.Invalidate( SID_ATTR_CHAR_FONT );
2447 rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
2448 rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
2450 rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
2451 rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
2452 rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
2453 rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
2454 rBindings.Invalidate( SID_ULINE_VAL_NONE );
2455 rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
2456 rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
2457 rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
2459 rBindings.Invalidate( SID_HYPERLINK_GETLINK );
2461 rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
2462 rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
2463 rBindings.Invalidate( SID_SET_SUB_SCRIPT );
2464 rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
2465 rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
2469 // --------------- public methods --------------------------------------------
2471 void ScInputHandler::SetMode( ScInputMode eNewMode, const OUString* pInitText )
2473 if ( eMode == eNewMode )
2474 return;
2476 ImplCreateEditEngine();
2478 if (bProtected)
2480 eMode = SC_INPUT_NONE;
2481 StopInputWinEngine( true );
2482 if (pActiveViewSh)
2483 pActiveViewSh->GetActiveWin()->GrabFocus();
2484 return;
2487 if (eNewMode != SC_INPUT_NONE && pActiveViewSh)
2488 // Disable paste mode when edit mode starts.
2489 pActiveViewSh->GetViewData().SetPasteMode( SC_PASTE_NONE );
2491 bInOwnChange = true; // disable ModifyHdl (reset below)
2493 ScInputMode eOldMode = eMode;
2494 eMode = eNewMode;
2495 if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode)
2496 StopInputWinEngine( false );
2498 if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE)
2500 if (eOldMode == SC_INPUT_NONE) // not if switching between modes
2502 if (StartTable(0, false, eMode == SC_INPUT_TABLE))
2504 if (pActiveViewSh)
2505 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( pEngine, aCursorPos );
2509 if (pInitText)
2511 pEngine->SetText(*pInitText);
2512 bModified = true;
2515 sal_Int32 nPara = pEngine->GetParagraphCount()-1;
2516 sal_Int32 nLen = pEngine->GetText(nPara).getLength();
2517 sal_uInt16 nCount = pEngine->GetViewCount();
2519 for (sal_uInt16 i=0; i<nCount; i++)
2521 if ( eMode == SC_INPUT_TABLE && eOldMode == SC_INPUT_TOP )
2523 // Keep Selection
2525 else
2527 pEngine->GetView(i)->
2528 SetSelection( ESelection( nPara, nLen, nPara, nLen ) );
2530 pEngine->GetView(i)->ShowCursor(false);
2534 UpdateActiveView();
2535 if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE)
2537 if (pTableView)
2538 pTableView->SetEditEngineUpdateMode(true);
2540 else
2542 if (pTopView)
2543 pTopView->SetEditEngineUpdateMode(true);
2546 if (eNewMode != eOldMode)
2547 UpdateFormulaMode();
2549 bInOwnChange = false;
2553 * @return true if rString only contains digits (no autocorrect then)
2555 static bool lcl_IsNumber(const OUString& rString)
2557 sal_Int32 nLen = rString.getLength();
2558 for (sal_Int32 i=0; i<nLen; i++)
2560 sal_Unicode c = rString[i];
2561 if ( c < '0' || c > '9' )
2562 return false;
2564 return true;
2567 static void lcl_SelectionToEnd( EditView* pView )
2569 if ( pView )
2571 EditEngine* pEngine = pView->GetEditEngine();
2572 sal_Int32 nParCnt = pEngine->GetParagraphCount();
2573 if ( nParCnt == 0 )
2574 nParCnt = 1;
2575 ESelection aSel( nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); // empty selection, cursor at the end
2576 pView->SetSelection( aSel );
2580 void ScInputHandler::EnterHandler( ScEnterMode nBlockMode )
2582 // Macro calls for validity can cause a lot of problems, so inhibit
2583 // nested calls of EnterHandler().
2584 if (bInEnterHandler) return;
2585 bInEnterHandler = true;
2586 bInOwnChange = true; // disable ModifyHdl (reset below)
2588 ImplCreateEditEngine();
2590 bool bMatrix = ( nBlockMode == ScEnterMode::MATRIX );
2592 SfxApplication* pSfxApp = SfxGetpApp();
2593 EditTextObject* pObject = nullptr;
2594 ScPatternAttr* pCellAttrs = nullptr;
2595 bool bForget = false; // Remove due to validity?
2597 OUString aString = GetEditText(pEngine);
2598 EditView* pActiveView = pTopView ? pTopView : pTableView;
2599 if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString))
2601 if (pColumnData && miAutoPosColumn != pColumnData->end())
2603 // #i47125# If AutoInput appended something, do the final AutoCorrect
2604 // with the cursor at the end of the input.
2605 lcl_SelectionToEnd(pTopView);
2606 lcl_SelectionToEnd(pTableView);
2609 vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr;
2611 if (pTopView)
2612 pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views
2613 if (pTableView)
2614 pTableView->CompleteAutoCorrect(pFrameWin);
2615 aString = GetEditText(pEngine);
2617 lcl_RemoveTabs(aString);
2619 // Test if valid (always with simple string)
2620 if ( bModified && nValidation && pActiveViewSh )
2622 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2623 const ScValidationData* pData = pDoc->GetValidationEntry( nValidation );
2624 if (pData && pData->HasErrMsg())
2626 // #i67990# don't use pLastPattern in EnterHandler
2627 const ScPatternAttr* pPattern = pDoc->GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
2628 bool bOk = pData->IsDataValid( aString, *pPattern, aCursorPos );
2630 if (!bOk)
2632 if ( pActiveViewSh ) // If it came from MouseButtonDown
2633 pActiveViewSh->StopMarking(); // (the InfoBox consumes the MouseButtonUp)
2635 //FIXME: We still run into problems if the input is triggered by activating another View
2636 vcl::Window* pParent = Application::GetDefDialogParent();
2637 if ( pData->DoError( pParent, aString, aCursorPos ) )
2638 bForget = true; // Do not take over input
2643 // Check for input into DataPilot table
2644 if ( bModified && pActiveViewSh && !bForget )
2646 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2647 ScDPObject* pDPObj = pDoc->GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
2648 if ( pDPObj )
2650 // Any input within the DataPilot table is either a valid renaming
2651 // or an invalid action - normal cell input is always aborted
2652 pActiveViewSh->DataPilotInput( aCursorPos, aString );
2653 bForget = true;
2657 std::vector<editeng::MisspellRanges> aMisspellRanges;
2658 pEngine->CompleteOnlineSpelling();
2659 bool bSpellErrors = !bFormulaMode && pEngine->HasOnlineSpellErrors();
2660 if ( bSpellErrors )
2662 // #i3820# If the spell checker flags numerical input as error,
2663 // it still has to be treated as number, not EditEngine object.
2664 if ( pActiveViewSh )
2666 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2667 // #i67990# don't use pLastPattern in EnterHandler
2668 const ScPatternAttr* pPattern = pDoc->GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
2669 if (pPattern)
2671 SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
2672 // without conditional format, as in ScColumn::SetString
2673 sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
2674 double nVal;
2675 if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) )
2677 bSpellErrors = false; // ignore the spelling errors
2683 // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc).
2684 // SetUpdateMode must come after CompleteOnlineSpelling.
2685 // The view is hidden in any case below (Broadcast).
2686 pEngine->SetUpdateMode( false );
2688 if ( bModified && !bForget ) // What is being entered (text/object)?
2690 sal_Int32 nParCnt = pEngine->GetParagraphCount();
2691 if ( nParCnt == 0 )
2692 nParCnt = 1;
2694 bool bUniformAttribs = true;
2695 SfxItemSet aPara1Attribs = pEngine->GetAttribs(0, 0, pEngine->GetTextLen(0));
2696 for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara)
2698 SfxItemSet aPara2Attribs = pEngine->GetAttribs(nPara, 0, pEngine->GetTextLen(nPara));
2699 if (!(aPara1Attribs == aPara2Attribs))
2701 // Paragraph format different from that of the 1st paragraph.
2702 bUniformAttribs = false;
2703 break;
2707 ESelection aSel( 0, 0, nParCnt-1, pEngine->GetTextLen(nParCnt-1) );
2708 SfxItemSet aOldAttribs = pEngine->GetAttribs( aSel );
2709 const SfxPoolItem* pItem = nullptr;
2711 // Find common (cell) attributes before RemoveAdjust
2712 if ( pActiveViewSh && bUniformAttribs )
2714 SfxItemSet* pCommonAttrs = nullptr;
2715 for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++)
2717 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
2718 if ( eState == SfxItemState::SET &&
2719 nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING &&
2720 nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS &&
2721 *pItem != pEditDefaults->Get(nId) )
2723 if ( !pCommonAttrs )
2724 pCommonAttrs = new SfxItemSet( pEngine->GetEmptyItemSet() );
2725 pCommonAttrs->Put( *pItem );
2729 if ( pCommonAttrs )
2731 ScDocument* pDoc = pActiveViewSh->GetViewData().GetDocument();
2732 pCellAttrs = new ScPatternAttr( pDoc->GetPool() );
2733 pCellAttrs->GetFromEditItemSet( pCommonAttrs );
2734 delete pCommonAttrs;
2738 // Clear ParaAttribs (including adjustment)
2739 RemoveAdjust();
2741 bool bAttrib = false; // Formatting present?
2743 // check if EditObject is needed
2744 if (nParCnt > 1)
2745 bAttrib = true;
2746 else
2748 for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++)
2750 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
2751 if (eState == SfxItemState::DONTCARE)
2752 bAttrib = true;
2753 else if (eState == SfxItemState::SET)
2755 // Keep same items in EditEngine as in ScEditAttrTester
2756 if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING ||
2757 nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS )
2759 if ( *pItem != pEditDefaults->Get(nId) )
2760 bAttrib = true;
2765 // Contains fields?
2766 SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, false );
2767 if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
2768 bAttrib = true;
2770 // Not converted characters?
2771 SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, false );
2772 if ( eConvState == SfxItemState::DONTCARE || eConvState == SfxItemState::SET )
2773 bAttrib = true;
2775 // Always recognize formulas as formulas
2776 // We still need the preceding test due to cell attributes
2779 if (bSpellErrors)
2780 pEngine->GetAllMisspellRanges(aMisspellRanges);
2782 if (bMatrix)
2783 bAttrib = false;
2785 if (bAttrib)
2787 pEngine->ClearSpellErrors();
2788 pObject = pEngine->CreateTextObject();
2790 else if (bAutoComplete) // Adjust Upper/Lower case
2792 // Perform case-matching only when the typed text is partial.
2793 if (pColumnData && aAutoSearch.getLength() < aString.getLength())
2794 aString = getExactMatch(*pColumnData, aString);
2798 // Don't rely on ShowRefFrame switching the active view synchronously
2799 // execute the function directly on the correct view's bindings instead
2800 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
2801 ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
2803 if (bFormulaMode)
2805 ShowRefFrame();
2807 if (pExecuteSh)
2809 pExecuteSh->SetTabNo(aCursorPos.Tab());
2810 pExecuteSh->ActiveGrabFocus();
2813 bFormulaMode = false;
2814 pSfxApp->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) );
2815 SC_MOD()->SetRefInputHdl(nullptr);
2816 if (pInputWin)
2817 pInputWin->SetFormulaMode(false);
2818 UpdateAutoCorrFlag();
2820 pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
2821 DeleteRangeFinder();
2822 ResetAutoPar();
2824 bool bOldMod = bModified;
2826 bModified = false;
2827 bSelIsRef = false;
2828 eMode = SC_INPUT_NONE;
2829 StopInputWinEngine(true);
2831 // Text input (through number formats) or ApplySelectionPattern modify
2832 // the cell's attributes, so pLastPattern is no longer valid
2833 pLastPattern = nullptr;
2835 if (bOldMod && !bProtected && !bForget)
2837 // No typographic quotes in formulas
2838 if (aString.startsWith("="))
2840 SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get().GetAutoCorrect();
2841 if ( pAuto )
2843 OUString aReplace(pAuto->GetStartDoubleQuote());
2844 if( aReplace.isEmpty() )
2845 aReplace = ScGlobal::pLocaleData->getDoubleQuotationMarkStart();
2846 if( aReplace != "\"" )
2847 aString = aString.replaceAll( aReplace, "\"" );
2849 aReplace = OUString(pAuto->GetEndDoubleQuote());
2850 if( aReplace.isEmpty() )
2851 aReplace = ScGlobal::pLocaleData->getDoubleQuotationMarkEnd();
2852 if( aReplace != "\"" )
2853 aString = aString.replaceAll( aReplace, "\"" );
2855 aReplace = OUString(pAuto->GetStartSingleQuote());
2856 if( aReplace.isEmpty() )
2857 aReplace = ScGlobal::pLocaleData->getQuotationMarkStart();
2858 if( aReplace != "'" )
2859 aString = aString.replaceAll( aReplace, "'" );
2861 aReplace = OUString(pAuto->GetEndSingleQuote());
2862 if( aReplace.isEmpty() )
2863 aReplace = ScGlobal::pLocaleData->getQuotationMarkEnd();
2864 if( aReplace != "'" )
2865 aString = aString.replaceAll( aReplace, "'");
2869 pSfxApp->Broadcast( SfxSimpleHint( FID_KILLEDITVIEW_NOPAINT ) );
2871 if ( pExecuteSh )
2873 SfxBindings& rBindings = pExecuteSh->GetViewFrame()->GetBindings();
2875 sal_uInt16 nId = FID_INPUTLINE_ENTER;
2876 if ( nBlockMode == ScEnterMode::BLOCK )
2877 nId = FID_INPUTLINE_BLOCK;
2878 else if ( nBlockMode == ScEnterMode::MATRIX )
2879 nId = FID_INPUTLINE_MATRIX;
2881 ScInputStatusItem aItem( FID_INPUTLINE_STATUS,
2882 aCursorPos, aCursorPos, aCursorPos,
2883 aString, pObject );
2885 if (!aMisspellRanges.empty())
2886 aItem.SetMisspellRanges(&aMisspellRanges);
2888 const SfxPoolItem* aArgs[2];
2889 aArgs[0] = &aItem;
2890 aArgs[1] = nullptr;
2891 rBindings.Execute( nId, aArgs );
2894 delete pLastState; // pLastState still contains the old text
2895 pLastState = nullptr;
2897 else
2898 pSfxApp->Broadcast( SfxSimpleHint( FID_KILLEDITVIEW ) );
2900 if ( bOldMod && pExecuteSh && pCellAttrs && !bForget )
2902 // Combine with input?
2903 pExecuteSh->ApplySelectionPattern( *pCellAttrs, true );
2904 pExecuteSh->AdjustBlockHeight();
2907 delete pCellAttrs;
2908 delete pObject;
2910 HideTip();
2911 HideTipBelow();
2913 nFormSelStart = nFormSelEnd = 0;
2914 aFormText.clear();
2916 bInOwnChange = false;
2917 bInEnterHandler = false;
2920 void ScInputHandler::CancelHandler()
2922 bInOwnChange = true; // Also without FormulaMode due to FunctionsAutoPilot
2924 ImplCreateEditEngine();
2926 bModified = false;
2928 // Don't rely on ShowRefFrame switching the active view synchronously
2929 // execute the function directly on the correct view's bindings instead
2930 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
2931 ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
2933 if (bFormulaMode)
2935 ShowRefFrame();
2936 if (pExecuteSh)
2938 pExecuteSh->SetTabNo(aCursorPos.Tab());
2939 pExecuteSh->ActiveGrabFocus();
2941 bFormulaMode = false;
2942 SfxGetpApp()->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) );
2943 SC_MOD()->SetRefInputHdl(nullptr);
2944 if (pInputWin)
2945 pInputWin->SetFormulaMode(false);
2946 UpdateAutoCorrFlag();
2948 pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
2949 DeleteRangeFinder();
2950 ResetAutoPar();
2952 eMode = SC_INPUT_NONE;
2953 StopInputWinEngine( true );
2954 if (pExecuteSh)
2955 pExecuteSh->StopEditShell();
2957 aCursorPos.Set(MAXCOL+1,0,0); // Invalid flag
2958 pEngine->SetText(OUString());
2960 if ( !pLastState && pExecuteSh )
2961 pExecuteSh->UpdateInputHandler( true ); // Update status again
2962 else
2963 NotifyChange( pLastState, true );
2965 nFormSelStart = nFormSelEnd = 0;
2966 aFormText.clear();
2968 bInOwnChange = false;
2971 bool ScInputHandler::IsModalMode( SfxObjectShell* pDocSh )
2973 // References to unnamed document; that doesn't work
2974 return bFormulaMode && pRefViewSh
2975 && pRefViewSh->GetViewData().GetDocument()->GetDocumentShell() != pDocSh
2976 && !pDocSh->HasName();
2979 void ScInputHandler::AddRefEntry()
2981 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2982 UpdateActiveView();
2983 if (!pTableView && !pTopView)
2984 return; // E.g. FillMode
2986 DataChanging(); // Cannot be new
2988 RemoveSelection();
2989 OUString aText = GetEditText(pEngine);
2990 sal_Unicode cLastChar = 0;
2991 sal_Int32 nPos = aText.getLength() - 1;
2992 while (nPos >= 0 && ((cLastChar = aText[nPos]) == ' ')) //checking space
2993 --nPos;
2995 bool bAppendSeparator = (cLastChar != '(' && cLastChar != cSep && cLastChar != '=');
2996 if (bAppendSeparator)
2998 if (pTableView)
2999 pTableView->InsertText( OUString(cSep) );
3000 if (pTopView)
3001 pTopView->InsertText( OUString(cSep) );
3004 DataChanged();
3007 void ScInputHandler::SetReference( const ScRange& rRef, ScDocument* pDoc )
3009 HideTip();
3011 bool bOtherDoc = ( pRefViewSh &&
3012 pRefViewSh->GetViewData().GetDocument() != pDoc );
3013 if (bOtherDoc)
3014 if (!pDoc->GetDocumentShell()->HasName())
3016 // References to unnamed document; that doesn't work
3017 // SetReference should not be called, then
3018 return;
3021 UpdateActiveView();
3022 if (!pTableView && !pTopView)
3023 return; // E.g. FillMode
3025 // Never overwrite the "="!
3026 EditView* pActiveView = pTopView ? pTopView : pTableView;
3027 ESelection aSel = pActiveView->GetSelection();
3028 aSel.Adjust();
3029 if ( aSel.nStartPara == 0 && aSel.nStartPos == 0 )
3030 return;
3032 DataChanging(); // Cannot be new
3034 // Turn around selection if backwards (TODO: Do we really need to do that?)
3035 if (pTableView)
3037 ESelection aTabSel = pTableView->GetSelection();
3038 if (aTabSel.nStartPos > aTabSel.nEndPos && aTabSel.nStartPara == aTabSel.nEndPara)
3040 aTabSel.Adjust();
3041 pTableView->SetSelection(aTabSel);
3044 if (pTopView)
3046 ESelection aTopSel = pTopView->GetSelection();
3047 if (aTopSel.nStartPos > aTopSel.nEndPos && aTopSel.nStartPara == aTopSel.nEndPara)
3049 aTopSel.Adjust();
3050 pTopView->SetSelection(aTopSel);
3054 // Create string from reference
3055 OUString aRefStr;
3056 const ScAddress::Details aAddrDetails( pDoc, aCursorPos );
3057 if (bOtherDoc)
3059 // Reference to other document
3060 OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab");
3062 OUString aTmp(rRef.Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc, aAddrDetails)); // Always 3D
3064 SfxObjectShell* pObjSh = pDoc->GetDocumentShell();
3065 // #i75893# convert escaped URL of the document to something user friendly
3066 OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS );
3068 switch(aAddrDetails.eConv)
3070 case formula::FormulaGrammar::CONV_XL_A1 :
3071 case formula::FormulaGrammar::CONV_XL_OOX :
3072 case formula::FormulaGrammar::CONV_XL_R1C1 :
3073 aRefStr = "[\'";
3074 aRefStr += aFileName;
3075 aRefStr += "']";
3076 break;
3077 case formula::FormulaGrammar::CONV_OOO :
3078 default:
3079 aRefStr = "\'";
3080 aRefStr += aFileName;
3081 aRefStr += "'#";
3082 break;
3084 aRefStr += aTmp;
3086 else
3088 if ( rRef.aStart.Tab() != aCursorPos.Tab() ||
3089 rRef.aStart.Tab() != rRef.aEnd.Tab() )
3090 aRefStr = rRef.Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc, aAddrDetails);
3091 else
3092 aRefStr = rRef.Format(ScRefFlags::VALID, pDoc, aAddrDetails);
3095 if (pTableView || pTopView)
3097 if (pTableView)
3098 pTableView->InsertText( aRefStr, true );
3099 if (pTopView)
3100 pTopView->InsertText( aRefStr, true );
3102 DataChanged();
3105 bSelIsRef = true;
3108 void ScInputHandler::InsertFunction( const OUString& rFuncName, bool bAddPar )
3110 if ( eMode == SC_INPUT_NONE )
3112 OSL_FAIL("InsertFunction, nicht im Eingabemodus");
3113 return;
3116 UpdateActiveView();
3117 if (!pTableView && !pTopView)
3118 return; // E.g. FillMode
3120 DataChanging(); // Cannot be new
3122 OUString aText = rFuncName;
3123 if (bAddPar)
3124 aText += "()";
3126 if (pTableView)
3128 pTableView->InsertText( aText );
3129 if (bAddPar)
3131 ESelection aSel = pTableView->GetSelection();
3132 --aSel.nStartPos;
3133 --aSel.nEndPos;
3134 pTableView->SetSelection(aSel);
3137 if (pTopView)
3139 pTopView->InsertText( aText );
3140 if (bAddPar)
3142 ESelection aSel = pTopView->GetSelection();
3143 --aSel.nStartPos;
3144 --aSel.nEndPos;
3145 pTopView->SetSelection(aSel);
3149 DataChanged();
3151 if (bAddPar)
3152 AutoParAdded();
3155 void ScInputHandler::ClearText()
3157 if ( eMode == SC_INPUT_NONE )
3159 OSL_FAIL("ClearText, nicht im Eingabemodus");
3160 return;
3163 UpdateActiveView();
3164 if (!pTableView && !pTopView)
3165 return; // E.g. FillMode
3167 DataChanging(); // Cannot be new
3169 if (pTableView)
3171 pTableView->GetEditEngine()->SetText( "" );
3172 pTableView->SetSelection( ESelection(0,0, 0,0) );
3174 if (pTopView)
3176 pTopView->GetEditEngine()->SetText( "" );
3177 pTopView->SetSelection( ESelection(0,0, 0,0) );
3180 DataChanged();
3183 bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false */ )
3185 if (!bOptLoaded)
3187 bAutoComplete = SC_MOD()->GetAppOptions().GetAutoComplete();
3188 bOptLoaded = true;
3191 vcl::KeyCode aCode = rKEvt.GetKeyCode();
3192 sal_uInt16 nModi = aCode.GetModifier();
3193 bool bShift = aCode.IsShift();
3194 bool bControl = aCode.IsMod1();
3195 bool bAlt = aCode.IsMod2();
3196 sal_uInt16 nCode = aCode.GetCode();
3197 sal_Unicode nChar = rKEvt.GetCharCode();
3199 if (bAlt && !bControl && nCode != KEY_RETURN)
3200 // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not.
3201 return false;
3203 if (!bControl && nCode == KEY_TAB)
3205 // Normal TAB moves the cursor right.
3206 EnterHandler();
3208 if (pActiveViewSh)
3209 pActiveViewSh->FindNextUnprot( bShift );
3210 return true;
3213 bool bInputLine = ( eMode==SC_INPUT_TOP );
3215 bool bUsed = false;
3216 bool bSkip = false;
3217 bool bDoEnter = false;
3219 switch ( nCode )
3221 case KEY_RETURN:
3222 // New line when in the input line and Shift/Ctrl-Enter is pressed,
3223 // or when in a cell and Ctrl-Enter is pressed.
3224 if ((pInputWin && bInputLine && bControl != bShift) || (!bInputLine && bControl && !bShift))
3226 bDoEnter = true;
3228 else if (nModi == 0 && nTipVisible && pFormulaData && miAutoPosFormula != pFormulaData->end())
3230 PasteFunctionData();
3231 bUsed = true;
3233 else if ( nModi == 0 && nTipVisible && !aManualTip.isEmpty() )
3235 PasteManualTip();
3236 bUsed = true;
3238 else
3240 ScEnterMode nMode = ScEnterMode::NORMAL;
3241 if ( bShift && bControl )
3242 nMode = ScEnterMode::MATRIX;
3243 else if ( bAlt )
3244 nMode = ScEnterMode::BLOCK;
3245 EnterHandler( nMode );
3247 if (pActiveViewSh)
3248 pActiveViewSh->MoveCursorEnter( bShift && !bControl );
3250 bUsed = true;
3252 break;
3253 case KEY_TAB:
3254 if (bControl && !bAlt)
3256 if (pFormulaData && nTipVisible && miAutoPosFormula != pFormulaData->end())
3258 // Iterate
3259 NextFormulaEntry( bShift );
3260 bUsed = true;
3262 else if (pColumnData && bUseTab && miAutoPosColumn != pColumnData->end())
3264 // Iterate through AutoInput entries
3265 NextAutoEntry( bShift );
3266 bUsed = true;
3269 break;
3270 case KEY_ESCAPE:
3271 if ( nTipVisible )
3273 HideTip();
3274 bUsed = true;
3276 else if( nTipVisibleSec )
3278 HideTipBelow();
3279 bUsed = true;
3281 else if (eMode != SC_INPUT_NONE)
3283 CancelHandler();
3284 bUsed = true;
3286 else
3287 bSkip = true;
3288 break;
3289 case KEY_F2:
3290 if ( !bShift && !bControl && !bAlt && eMode == SC_INPUT_TABLE )
3292 eMode = SC_INPUT_TYPE;
3293 bUsed = true;
3295 break;
3298 // Only execute cursor keys if already in EditMode
3299 // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator)
3300 bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt);
3301 bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Treat Insert like Cursorkeys
3302 if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) ||
3303 ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) )
3305 HideTip();
3306 HideTipBelow();
3308 if (bSelIsRef)
3310 RemoveSelection();
3311 bSelIsRef = false;
3314 UpdateActiveView();
3315 bool bNewView = DataChanging( nChar );
3317 if (bProtected) // Protected cell?
3318 bUsed = true; // Don't forward KeyEvent
3319 else // Changes allowed
3321 if (bNewView ) // Create anew
3323 if (pActiveViewSh)
3324 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( pEngine, aCursorPos );
3325 UpdateActiveView();
3326 if (eMode==SC_INPUT_NONE)
3327 if (pTableView || pTopView)
3329 OUString aStrLoP;
3331 if ( bStartEdit && bCellHasPercentFormat && ((nChar >= '0' && nChar <= '9') || nChar == '-') )
3332 aStrLoP = "%";
3334 if (pTableView)
3336 pTableView->GetEditEngine()->SetText( aStrLoP );
3337 if ( !aStrLoP.isEmpty() )
3338 pTableView->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
3340 // Don't call SetSelection if the string is empty anyway,
3341 // to avoid breaking the bInitial handling in ScViewData::EditGrowY
3343 if (pTopView)
3345 pTopView->GetEditEngine()->SetText( aStrLoP );
3346 if ( !aStrLoP.isEmpty() )
3347 pTopView->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
3350 SyncViews();
3353 if (pTableView || pTopView)
3355 if (bDoEnter)
3357 if (pTableView)
3358 if( pTableView->PostKeyEvent( KeyEvent( CHAR_CR, vcl::KeyCode(KEY_RETURN) ) ) )
3359 bUsed = true;
3360 if (pTopView)
3361 if( pTopView->PostKeyEvent( KeyEvent( CHAR_CR, vcl::KeyCode(KEY_RETURN) ) ) )
3362 bUsed = true;
3364 else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() )
3366 SkipClosingPar();
3367 bUsed = true;
3369 else
3371 if (pTableView)
3373 vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr;
3374 if ( pTableView->PostKeyEvent( rKEvt, pFrameWin ) )
3375 bUsed = true;
3377 if (pTopView)
3378 if ( pTopView->PostKeyEvent( rKEvt ) )
3379 bUsed = true;
3382 // AutoInput:
3383 if ( bUsed && bAutoComplete )
3385 bUseTab = false;
3386 if (pFormulaData)
3387 miAutoPosFormula = pFormulaData->end(); // do not search further
3388 if (pColumnData)
3389 miAutoPosColumn = pColumnData->end();
3391 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
3392 if ( nChar && nChar != 8 && nChar != 127 && // no 'backspace', no 'delete'
3393 KeyFuncType::CUT != eFunc) // and no 'CTRL-X'
3395 if (bFormulaMode)
3396 UseFormulaData();
3397 else
3398 UseColData();
3402 // When the selection is changed manually or an opening parenthesis
3403 // is typed, stop overwriting parentheses
3404 if ( bUsed && nChar == '(' )
3405 ResetAutoPar();
3407 if ( KEY_INSERT == nCode )
3409 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
3410 if (pViewFrm)
3411 pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
3413 if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) )
3415 ShowTipCursor();
3417 if( bUsed && bFormulaMode && nCode == KEY_BACKSPACE )
3419 if (bFormulaMode)
3420 UseFormulaData();
3421 else
3422 UseColData();
3427 // #i114511# don't count cursor keys as modification
3428 bool bSetModified = !bCursorKey;
3429 DataChanged(false, bSetModified); // also calls UpdateParenthesis()
3430 InvalidateAttribs(); //! in DataChanged?
3434 if (pTopView && eMode != SC_INPUT_NONE)
3435 SyncViews();
3437 return bUsed;
3440 void ScInputHandler::InputCommand( const CommandEvent& rCEvt )
3442 if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
3444 // For CommandEventId::CursorPos, do as little as possible, because
3445 // with remote VCL, even a ShowCursor will generate another event.
3446 if ( eMode != SC_INPUT_NONE )
3448 UpdateActiveView();
3449 if (pTableView || pTopView)
3451 if (pTableView)
3452 pTableView->Command( rCEvt );
3453 else if (pTopView) // call only once
3454 pTopView->Command( rCEvt );
3458 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
3460 if ( eMode != SC_INPUT_NONE )
3462 UpdateActiveView();
3463 if (pTableView || pTopView)
3465 if (pTableView)
3466 pTableView->Command( rCEvt );
3467 else if (pTopView) // call only once
3468 pTopView->Command( rCEvt );
3472 else
3474 if (!bOptLoaded)
3476 bAutoComplete = SC_MOD()->GetAppOptions().GetAutoComplete();
3477 bOptLoaded = true;
3480 HideTip();
3481 HideTipBelow();
3483 if ( bSelIsRef )
3485 RemoveSelection();
3486 bSelIsRef = false;
3489 UpdateActiveView();
3490 bool bNewView = DataChanging( 0, true );
3492 if (!bProtected) // changes allowed
3494 if (bNewView) // create new edit view
3496 if (pActiveViewSh)
3497 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( pEngine, aCursorPos );
3498 UpdateActiveView();
3499 if (eMode==SC_INPUT_NONE)
3500 if (pTableView || pTopView)
3502 OUString aStrLoP;
3503 if (pTableView)
3505 pTableView->GetEditEngine()->SetText( aStrLoP );
3506 pTableView->SetSelection( ESelection(0,0, 0,0) );
3508 if (pTopView)
3510 pTopView->GetEditEngine()->SetText( aStrLoP );
3511 pTopView->SetSelection( ESelection(0,0, 0,0) );
3514 SyncViews();
3517 if (pTableView || pTopView)
3519 if (pTableView)
3520 pTableView->Command( rCEvt );
3521 if (pTopView)
3522 pTopView->Command( rCEvt );
3524 if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
3526 // AutoInput after ext text input
3528 if (pFormulaData)
3529 miAutoPosFormula = pFormulaData->end();
3530 if (pColumnData)
3531 miAutoPosColumn = pColumnData->end();
3533 if (bFormulaMode)
3534 UseFormulaData();
3535 else
3536 UseColData();
3540 DataChanged(); // calls UpdateParenthesis()
3541 InvalidateAttribs(); //! in DataChanged ?
3544 if (pTopView && eMode != SC_INPUT_NONE)
3545 SyncViews();
3549 void ScInputHandler::NotifyChange( const ScInputHdlState* pState,
3550 bool bForce, ScTabViewShell* pSourceSh,
3551 bool bStopEditing)
3553 // If the call originates from a macro call in the EnterHandler,
3554 // return immediately and don't mess up the status
3555 if (bInEnterHandler)
3556 return;
3558 bool bRepeat = (pState == pLastState);
3559 if (!bRepeat && pState && pLastState)
3560 bRepeat = (*pState == *pLastState);
3561 if (bRepeat && !bForce)
3562 return;
3564 bInOwnChange = true; // disable ModifyHdl (reset below)
3566 if ( pState && !pLastState ) // Enable again
3567 bForce = true;
3569 bool bHadObject = pLastState && pLastState->GetEditData();
3571 //! Before EditEngine gets eventually created (so it gets the right pools)
3572 if ( pSourceSh )
3573 pActiveViewSh = pSourceSh;
3574 else
3575 pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
3577 ImplCreateEditEngine();
3579 if ( pState != pLastState )
3581 delete pLastState;
3582 pLastState = pState ? new ScInputHdlState( *pState ) : nullptr;
3585 if ( pState && pActiveViewSh )
3587 ScModule* pScMod = SC_MOD();
3589 if ( pState )
3592 // Also take foreign reference input into account here (e.g. FunctionsAutoPilot),
3593 // FormEditData, if we're switching from Help to Calc:
3594 if ( !bFormulaMode && !pScMod->IsFormulaMode() && !pScMod->GetFormEditData() )
3596 bool bIgnore = false;
3597 if ( bModified )
3599 if (pState->GetPos() != aCursorPos)
3601 if (!bProtected)
3602 EnterHandler();
3604 else
3605 bIgnore = true;
3608 if ( !bIgnore )
3610 const ScAddress& rSPos = pState->GetStartPos();
3611 const ScAddress& rEPos = pState->GetEndPos();
3612 const EditTextObject* pData = pState->GetEditData();
3613 OUString aString = pState->GetString();
3614 bool bTxtMod = false;
3615 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
3616 ScDocument& rDoc = pDocSh->GetDocument();
3618 aCursorPos = pState->GetPos();
3620 if ( pData )
3621 bTxtMod = true;
3622 else if ( bHadObject )
3623 bTxtMod = true;
3624 else if ( bTextValid )
3625 bTxtMod = ( !aString.equals(aCurrentText) );
3626 else
3627 bTxtMod = ( !aString.equals(GetEditText(pEngine)) );
3629 if ( bTxtMod || bForce )
3631 if (pData)
3633 pEngine->SetText( *pData );
3634 if (pInputWin)
3635 aString = ScEditUtil::GetMultilineString(*pEngine);
3636 else
3637 aString = GetEditText(pEngine);
3638 lcl_RemoveTabs(aString);
3639 bTextValid = false;
3640 aCurrentText.clear();
3642 else
3644 aCurrentText = aString;
3645 bTextValid = true; //! To begin with remember as a string
3648 if ( pInputWin )
3649 pInputWin->SetTextString(aString);
3650 else if ( comphelper::LibreOfficeKit::isActive() )
3651 rDoc.GetDrawLayer()->libreOfficeKitCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8().getStr());
3654 if ( pInputWin ) // Named range input
3656 OUString aPosStr;
3657 const ScAddress::Details aAddrDetails( &rDoc, aCursorPos );
3659 // Is the range a name?
3660 //! Find by Timer?
3661 if ( pActiveViewSh )
3662 pActiveViewSh->GetViewData().GetDocument()->
3663 GetRangeAtBlock( ScRange( rSPos, rEPos ), &aPosStr );
3665 if ( aPosStr.isEmpty() ) // Not a name -> format
3667 ScRefFlags nFlags = ScRefFlags::ZERO;
3668 if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 )
3669 nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS;
3670 if ( rSPos != rEPos )
3672 ScRange r(rSPos, rEPos);
3673 applyStartToEndFlags(nFlags);
3674 aPosStr = r.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
3676 else
3677 aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
3680 // Disable the accessible VALUE_CHANGE event
3681 bool bIsSuppressed = pInputWin->IsAccessibilityEventsSuppressed(false);
3682 pInputWin->SetAccessibilityEventsSuppressed(true);
3683 pInputWin->SetPosString(aPosStr);
3684 pInputWin->SetAccessibilityEventsSuppressed(bIsSuppressed);
3685 pInputWin->SetSumAssignMode();
3688 if (bStopEditing)
3689 SfxGetpApp()->Broadcast( SfxSimpleHint( FID_KILLEDITVIEW ) );
3691 // As long as the content is not edited, turn off online spelling.
3692 // Online spelling is turned back on in StartTable, after setting
3693 // the right language from cell attributes.
3695 EEControlBits nCntrl = pEngine->GetControlWord();
3696 if ( nCntrl & EEControlBits::ONLINESPELLING )
3697 pEngine->SetControlWord( nCntrl & ~EEControlBits::ONLINESPELLING );
3699 bModified = false;
3700 bSelIsRef = false;
3701 bProtected = false;
3702 bCommandErrorShown = false;
3707 if ( pInputWin)
3709 // Do not enable if RefDialog is open
3710 if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen())
3712 if ( !pInputWin->IsEnabled())
3714 pInputWin->Enable();
3715 if(pDelayTimer )
3717 DELETEZ( pDelayTimer );
3721 else if(pScMod->IsRefDialogOpen())
3722 { // Because every document has its own InputWin,
3723 // we should start Timer again, because the input line may
3724 // still be active
3725 if ( !pDelayTimer )
3727 pDelayTimer = new Timer;
3728 pDelayTimer->SetTimeout( 500 ); // 500 ms delay
3729 pDelayTimer->SetTimeoutHdl( LINK( this, ScInputHandler, DelayTimer ) );
3730 pDelayTimer->Start();
3735 else // !pState || !pActiveViewSh
3737 if ( !pDelayTimer )
3739 pDelayTimer = new Timer;
3740 pDelayTimer->SetTimeout( 500 ); // 500 ms delay
3741 pDelayTimer->SetTimeoutHdl( LINK( this, ScInputHandler, DelayTimer ) );
3742 pDelayTimer->Start();
3746 HideTip();
3747 HideTipBelow();
3748 bInOwnChange = false;
3751 void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust )
3753 eAttrAdjust = eJust;
3754 UpdateAdjust( 0 );
3757 void ScInputHandler::ResetDelayTimer()
3759 if(pDelayTimer!=nullptr)
3761 DELETEZ( pDelayTimer );
3763 if ( pInputWin)
3765 pInputWin->Enable();
3770 IMPL_LINK_TYPED( ScInputHandler, DelayTimer, Timer*, pTimer, void )
3772 if ( pTimer == pDelayTimer )
3774 DELETEZ( pDelayTimer );
3776 if ( nullptr == pLastState || SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen())
3778 //! New method at ScModule to query if function autopilot is open
3779 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
3780 if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
3782 if ( pInputWin)
3784 pInputWin->EnableButtons( false );
3785 pInputWin->Disable();
3788 else if ( !bFormulaMode ) // Keep formula e.g. for help
3790 bInOwnChange = true; // disable ModifyHdl (reset below)
3792 pActiveViewSh = nullptr;
3793 pEngine->SetText( EMPTY_OUSTRING );
3794 if ( pInputWin )
3796 pInputWin->SetPosString( EMPTY_OUSTRING );
3797 pInputWin->SetTextString( EMPTY_OUSTRING );
3798 pInputWin->Disable();
3801 bInOwnChange = false;
3807 void ScInputHandler::InputSelection( EditView* pView )
3809 SyncViews( pView );
3810 ShowTipCursor();
3811 UpdateParenthesis(); // Selection changed -> update parentheses highlighting
3813 // When the selection is changed manually, stop overwriting parentheses
3814 ResetAutoPar();
3817 void ScInputHandler::InputChanged( EditView* pView, bool bFromNotify )
3819 UpdateActiveView();
3821 // #i20282# DataChanged needs to know if this is from the input line's modify handler
3822 bool bFromTopNotify = ( bFromNotify && pView == pTopView );
3824 bool bNewView = DataChanging(); //FIXME: Is this at all possible?
3825 aCurrentText = pView->GetEditEngine()->GetText(); // Also remember the string
3826 pEngine->SetText( aCurrentText );
3827 DataChanged( bFromTopNotify );
3828 bTextValid = true; // Is set to false in DataChanged
3830 if ( pActiveViewSh )
3832 ScViewData& rViewData = pActiveViewSh->GetViewData();
3833 if ( bNewView )
3834 rViewData.GetDocShell()->PostEditView( pEngine, aCursorPos );
3836 rViewData.EditGrowY();
3837 rViewData.EditGrowX();
3840 SyncViews( pView );
3843 const OUString& ScInputHandler::GetEditString()
3845 if (pEngine)
3847 aCurrentText = pEngine->GetText(); // Always new from Engine
3848 bTextValid = true;
3851 return aCurrentText;
3854 Size ScInputHandler::GetTextSize()
3856 Size aSize;
3857 if ( pEngine )
3858 aSize = Size( pEngine->CalcTextWidth(), pEngine->GetTextHeight() );
3860 return aSize;
3863 bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine )
3865 bool bRet = false;
3866 if (pEngine)
3868 // Contains field?
3869 sal_Int32 nParCnt = pEngine->GetParagraphCount();
3870 SfxItemSet aSet = pEngine->GetAttribs( ESelection(0,0,nParCnt,0) );
3871 SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
3872 if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
3874 // Copy content
3875 EditTextObject* pObj = pEngine->CreateTextObject();
3876 rDestEngine.SetText(*pObj);
3877 delete pObj;
3879 // Delete attributes
3880 for (sal_Int32 i=0; i<nParCnt; i++)
3881 rDestEngine.RemoveCharAttribs( i );
3883 // Combine paragraphs
3884 while ( nParCnt > 1 )
3886 sal_Int32 nLen = rDestEngine.GetTextLen( 0 );
3887 ESelection aSel( 0,nLen, 1,0 );
3888 rDestEngine.QuickInsertText( OUString(' '), aSel ); // Replace line break with space
3889 --nParCnt;
3892 bRet = true;
3895 return bRet;
3899 * Methods for FunctionAutoPilot:
3900 * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
3902 void ScInputHandler::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd )
3904 rStart = nFormSelStart;
3905 rEnd = nFormSelEnd;
3908 EditView* ScInputHandler::GetFuncEditView()
3910 UpdateActiveView(); // Due to pTableView
3912 EditView* pView = nullptr;
3913 if ( pInputWin )
3915 pInputWin->MakeDialogEditView();
3916 pView = pInputWin->GetEditView();
3918 else
3920 if ( eMode != SC_INPUT_TABLE )
3922 bCreatingFuncView = true; // Don't display RangeFinder
3923 SetMode( SC_INPUT_TABLE );
3924 bCreatingFuncView = false;
3925 if ( pTableView )
3926 pTableView->GetEditEngine()->SetText( EMPTY_OUSTRING );
3928 pView = pTableView;
3931 return pView;
3934 void ScInputHandler::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd )
3936 if ( nStart <= nEnd )
3938 nFormSelStart = nStart;
3939 nFormSelEnd = nEnd;
3941 else
3943 nFormSelEnd = nStart;
3944 nFormSelStart = nEnd;
3947 EditView* pView = GetFuncEditView();
3948 if (pView)
3949 pView->SetSelection( ESelection(0,nStart, 0,nEnd) );
3951 bModified = true;
3954 void ScInputHandler::InputReplaceSelection( const OUString& rStr )
3956 if (!pRefViewSh)
3957 pRefViewSh = pActiveViewSh;
3959 OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken...");
3961 sal_Int32 nOldLen = nFormSelEnd - nFormSelStart;
3962 sal_Int32 nNewLen = rStr.getLength();
3964 OUStringBuffer aBuf(aFormText);
3965 if (nOldLen)
3966 aBuf.remove(nFormSelStart, nOldLen);
3967 if (nNewLen)
3968 aBuf.insert(nFormSelStart, rStr);
3970 aFormText = aBuf.makeStringAndClear();
3972 nFormSelEnd = nFormSelStart + nNewLen;
3974 EditView* pView = GetFuncEditView();
3975 if (pView)
3977 pView->SetEditEngineUpdateMode( false );
3978 pView->GetEditEngine()->SetText( aFormText );
3979 pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) );
3980 pView->SetEditEngineUpdateMode( true );
3982 bModified = true;
3985 void ScInputHandler::InputTurnOffWinEngine()
3987 bInOwnChange = true; // disable ModifyHdl (reset below)
3989 eMode = SC_INPUT_NONE;
3990 /* TODO: it would be better if there was some way to reset the input bar
3991 * engine instead of deleting and having it recreate through
3992 * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
3993 * fdo#72278 without reintroducing fdo#69971. */
3994 StopInputWinEngine(true);
3996 bInOwnChange = false;
4000 * ScInputHdlState
4002 ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos,
4003 const ScAddress& rStartPos,
4004 const ScAddress& rEndPos,
4005 const OUString& rString,
4006 const EditTextObject* pData )
4007 : aCursorPos ( rCurPos ),
4008 aStartPos ( rStartPos ),
4009 aEndPos ( rEndPos ),
4010 aString ( rString ),
4011 pEditData ( pData ? pData->Clone() : nullptr )
4015 ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy )
4016 : pEditData ( nullptr )
4018 *this = rCpy;
4021 ScInputHdlState::~ScInputHdlState()
4023 delete pEditData;
4026 bool ScInputHdlState::operator==( const ScInputHdlState& r ) const
4028 return ( (aStartPos == r.aStartPos)
4029 && (aEndPos == r.aEndPos)
4030 && (aCursorPos == r.aCursorPos)
4031 && (aString == r.aString)
4032 && ScGlobal::EETextObjEqual( pEditData, r.pEditData ) );
4035 ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r )
4037 delete pEditData;
4039 aCursorPos = r.aCursorPos;
4040 aStartPos = r.aStartPos;
4041 aEndPos = r.aEndPos;
4042 aString = r.aString;
4043 pEditData = r.pEditData ? r.pEditData->Clone() : nullptr;
4045 return *this;
4048 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */