/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// This assumes that rResults is a sorted ring w.r.t ScTypedStrData::LessCaseInsensitive() or // in the reverse direction, whose origin is specified by nRingOrigin.
sal_Int32 getLongestCommonPrefixLength(const std::vector<OUString>& rResults, std::u16string_view aUserEntry, sal_Int32 nRingOrigin)
{
sal_Int32 nResults = rResults.size(); if (!nResults) return 0;
if (nResults == 1) return rResults[0].getLength();
sal_Int32 nRingOrigin = 0;
size_t nCount = 0;
ScTypedCaseStrSet::const_iterator retit; if ( bBack ) // Backwards
{
ScTypedCaseStrSet::const_reverse_iterator it, itEnd; if ( itPos == rDataSet.end() )
{
it = rDataSet.rend();
--it;
itEnd = it;
} else
{
it = rDataSet.rbegin();
size_t nPos = std::distance(rDataSet.begin(), itPos);
size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1
std::advance(it, nRPos); if ( it == rDataSet.rend() )
it = rDataSet.rbegin();
itEnd = it;
} bool bFirstTime = true;
while ( it != itEnd || bFirstTime )
{
++it; if ( it == rDataSet.rend() ) // go to the first if reach the end
{
it = rDataSet.rbegin();
nRingOrigin = nCount;
}
if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) ) // not a match continue;
rResultVec.push_back(rData.GetString()); // set the match data if ( nCount == 0 ) // convert the reverse iterator back to iterator.
{ // actually we want to do "retit = it;".
retit = rDataSet.begin();
size_t nRPos = std::distance(rDataSet.rbegin(), it);
size_t nPos = rDataSet.size() - 1 - nRPos;
std::advance(retit, nPos);
}
++nCount;
}
} else// Forwards
{
ScTypedCaseStrSet::const_iterator it, itEnd;
it = itPos; if ( it == rDataSet.end() )
it = --rDataSet.end();
itEnd = it; bool bFirstTime = true;
while ( it != itEnd || bFirstTime )
{
++it; if ( it == rDataSet.end() ) // go to the first if reach the end
{
it = rDataSet.begin();
nRingOrigin = nCount;
}
if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) ) // not a match continue;
rResultVec.push_back(rData.GetString()); // set the match data if ( nCount == 0 )
retit = it; // remember first match iterator
++nCount;
}
}
if (pLongestPrefixLen)
{ if (nRingOrigin >= static_cast<sal_Int32>(nCount))
{ // All matches were picked when rDataSet was read in one direction.
nRingOrigin = 0;
} // rResultsVec is a sorted ring with nRingOrigin "origin". // The direction of sorting is not important for getLongestCommonPrefixLength.
*pLongestPrefixLen = getLongestCommonPrefixLength(rResultVec, rStart, nRingOrigin);
}
if ( nCount > 0 ) // at least one function has matched return retit; return rDataSet.end(); // no matching text found
}
OUString aDelimiters = ScEditUtil::ModifyDelimiters(u" !~%\"\t\n"_ustr); // delimiters (in addition to ScEditUtil): only characters that are // allowed in formulas next to references and the quotation mark (so // string constants can be skipped)
// Text between separators. We only consider within one line/paragraph.
aSel.CollapseToEnd();
nStart = nPos;
handle_r1c1:
{ bool bSingleQuoted = false; while (nPos < nLen)
{ // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1" // Literal single quotes in sheet names are masked by another single quote if (pChar[nPos] == '\'')
{
bSingleQuoted = !bSingleQuoted;
} elseif (!bSingleQuoted) // Get everything in single quotes, including separators
{ if (ScGlobal::UnicodeStrChr(aDelimiters.getStr(), pChar[nPos])) break;
}
incPos( pChar[nPos], nPos, aSel);
}
}
// for R1C1 '-' in R[-]... or C[-]... are not delimiters // Nothing heroic here to ensure that there are '[]' around a negative // integer. we need to clean up this code. if( nPos < nLen && nPos > 0 && '-' == pChar[nPos] && '[' == pChar[nPos-1] &&
formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() )
{
incPos( pChar[nPos], nPos, aSel); goto handle_r1c1;
}
if ( nPos > nStart )
{
OUString aTest = rFormula.copy( nStart, nPos-nStart ); const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
ScRefFlags nFlags = aRange.ParseAny( aTest, rDoc, aAddrDetails ); if ( nFlags & ScRefFlags::VALID )
{ // Set tables if not specified if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
aRange.aStart.SetTab( pActiveViewSh->GetViewData().GetTabNo() ); if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
aRange.aEnd.SetTab( aRange.aStart.Tab() );
if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) ==
ScRefFlags::ZERO )
{ // #i73766# if a single ref was parsed, set the same "abs" flags for ref2, // so Format doesn't output a double ref because of different flags.
ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS);
applyStartToEndFlags(nFlags, nAbsFlags);
}
// Dummy InsertText for Update and Paint // To do that we need to cancel the selection from above (before QuickInsertText)
pView->InsertText( OUString() );
pView->SetSelection(ESelection::AtEnd()); // Set cursor to the end
}
// We are within one paragraph. const sal_Int32 nDiff = aNewStr.getLength() - (rData.maSel.end.nIndex - rData.maSel.start.nIndex);
rData.maSel.end.nIndex += nDiff;
ScInputHandler::~ScInputHandler()
{ // If this is the application InputHandler, the dtor is called after SfxApplication::Main, // thus we can't rely on any Sfx functions if (!mbDocumentDisposing) // inplace
EnterHandler(); // Finish input
if (ScModule* mod = ScModule::get(); mod->GetRefInputHdl() == this)
mod->SetRefInputHdl(nullptr);
if ( pInputWin && pInputWin->GetInputHandler() == this )
pInputWin->SetInputHandler( nullptr );
}
// SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev, // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL). if ( !( bTextWysiwyg && pActiveViewSh ) )
{
mpEditEngine->GetRefDevice()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
}
}
void ScInputHandler::ImplCreateEditEngine()
{ if ( mpEditEngine ) return;
// we cannot create a properly initialised EditEngine until we have a document
assert( pActiveViewSh );
ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell().GetDocument();
mpEditEngine = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool());
mpEditEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine->GetWordDelimiters() ) );
UpdateRefDevice(); // also sets MapMode
mpEditEngine->SetPaperSize( Size( 1000000, 1000000 ) );
pEditDefaults.reset( new SfxItemSet( mpEditEngine->GetEmptyItemSet() ) );
// Don't use pLastPattern here (may be invalid because of AutoStyle) bool bDisable = bLastIsSymbol || bFormulaMode; if ( bDisable )
nCntrl &= ~EEControlBits::AUTOCORRECT; else
nCntrl |= EEControlBits::AUTOCORRECT;
if ( nCntrl != nOld )
mpEditEngine->SetControlWord(nCntrl);
}
// SetDefaultLanguage is independent of the language attributes, // ScGlobal::GetEditDefaultLanguage is always used. // It must be set every time in case the office language was changed.
// Language is set separately, so the speller is needed only if online spelling is active if ( bOnlineSpell ) {
css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
mpEditEngine->SetSpeller( xXSpellChecker1 );
}
// Increase suggestion priority of MRU formulas const ScAppOptions& rOpt = ScModule::get()->GetAppOptions(); const sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount(); const sal_uInt16* pMRUList = rOpt.GetLRUFuncList(); for (sal_uInt16 i = 0; i < nMRUCount; i++)
{ const sal_uInt16 nId = pMRUList[i]; for (sal_uInt32 j = 0; j < nListCount; j++)
{ const ScFuncDesc* pDesc = pFuncList->GetFunction(j); if (pDesc->nFIndex == nId && pDesc->mxFuncName)
{ const OUString aEntry = *pDesc->mxFuncName + aParenthesesReplacement;; const ScTypedStrData aData(aEntry, 0.0, 0.0, ScTypedStrData::Standard); auto it = pFormulaData->find(aData); if (it != pFormulaData->end())
pFormulaData->erase(it);
pFormulaData->insert(ScTypedStrData(aEntry, 0.0, 0.0, ScTypedStrData::MRU)); break; // Stop searching
}
}
}
miAutoPosFormula = pFormulaData->end();
// tdf#142031 - collect all the characters for the formula suggestion auto input
ScTypedCaseStrSet aStrSet;
rDoc.GetFormulaEntries( aStrSet ); for (auto iter = aStrSet.begin(); iter != aStrSet.end(); ++iter)
{ const OUString aFuncName = ScGlobal::getCharClass().uppercase((*iter).GetString()); // fdo#75264 fill maFormulaChar with all characters used in formula names for (sal_Int32 j = 0; j < aFuncName.getLength(); j++)
maFormulaChar.insert(aFuncName[j]);
}
pFormulaData->insert(aStrSet.begin(), aStrSet.end());
pFormulaDataPara->insert(aStrSet.begin(), aStrSet.end());
}
/* TODO: MLFORMULA: this should work also with multi-line formulas. */ if ( !(bFormulaMode && pActiveView && pFormulaDataPara && mpEditEngine->GetParagraphCount() == 1) ) return;
aStart = ScGlobal::getCharClass().uppercase( aStart );
sal_Int32 nPos = aStart.getLength() - 1;
sal_Unicode c = aStart[ nPos ]; // fdo#75264 use maFormulaChar to check if characters are used in function names
::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c ); if ( p == maFormulaChar.end() ) returnfalse; // last character is not part of any function name, quit
::std::vector<sal_Unicode> aTemp { c }; for(sal_Int32 i = nPos - 1; i >= 0; --i)
{
c = aStart[ i ];
p = maFormulaChar.find( c );
if (p == maFormulaChar.end()) break;
aTemp.push_back( c );
}
::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin();
aResult = OUString( *rIt++ ); while ( rIt != aTemp.rend() )
aResult += OUStringChar( *rIt++ );
/* TODO: MLFORMULA: this should work also with multi-line formulas. */ if ( !(pActiveView && pFormulaData && mpEditEngine->GetParagraphCount() == 1) ) return;
// Due to differences between table and input cell (e.g clipboard with line breaks), // the selection may not be in line with the EditEngine anymore. // Just return without any indication as to why. if (aSel.end.nIndex > aParagraph.getLength()) return;
OUString aText; if ( GetFuncName( aSelText, aText ) )
{ // function name is incomplete: // show matching functions name as tip above cell
::std::vector<OUString> aNewVec;
miAutoPosFormula = pFormulaData->end();
miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false); if (miAutoPosFormula != pFormulaData->end())
{ // check if partial function name is not between quotes
sal_Unicode cBetweenQuotes = 0; for ( int n = 0; n < aSelText.getLength(); n++ )
{ if (cBetweenQuotes)
{ if (aSelText[n] == cBetweenQuotes)
cBetweenQuotes = 0;
} elseif ( aSelText[ n ] == '"' )
cBetweenQuotes = '"'; elseif ( aSelText[ n ] == '\'' )
cBetweenQuotes = '\'';
} if ( cBetweenQuotes ) return; // we're between quotes
bool bNoInitialLetter = false;
OUString aOld = pView->getEditEngine().GetText(0); // in case we want just insert a function and not completing if ( comphelper::LibreOfficeKit::isActive() )
{
ESelection aSelRange = aSel;
--aSelRange.start.nIndex;
--aSelRange.end.nIndex;
pView->SetSelection(aSelRange);
pView->SelectCurrentWord();
if(!bNoInitialLetter)
{ const sal_Int32 nMinLen = std::max(aSel.end.nIndex - aSel.start.nIndex, sal_Int32(1)); // Since transliteration service is used to test for match, the replaced string could be // longer than rInsert, so in order to find longest match before the cursor, test whole // string from start to current cursor position (don't limit to length of rInsert) // Disclaimer: I really don't know if a match longer than rInsert is actually possible, // so the above is based on assumptions how "transliteration" might possibly work. If // it's in fact impossible, an optimization would be useful to limit aSel.start.nPos to // std::max(sal_Int32(0), aSel.end.nIndex - rInsert.getLength()).
aSel.start.nIndex = 0;
pView->SetSelection(aSel); const OUString aAll = pView->GetSelected();
OUString aMatch; for (sal_Int32 n = aAll.getLength(); n >= nMinLen && aMatch.isEmpty(); --n)
{ const OUString aTest = aAll.copy(aAll.getLength() - n); // n trailing chars if (ScGlobal::GetTransliteration().isMatch(aTest, rInsert))
aMatch = aTest; // Found => break the loop
}
OUString aInsStr = rInsert;
sal_Int32 nInsLen = aInsStr.getLength(); bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '('
&& aInsStr[nInsLen-1] == ')' ); if ( bDoParen )
{ // Do not insert parentheses after function names if there already are some // (e.g. if the function name was edited).
ESelection aWordSel = pView->GetSelection();
// aWordSel.EndPos points one behind string if word at end if (aWordSel.end.nIndex < aOld.getLength())
{
sal_Unicode cNext = aOld[aWordSel.end.nIndex]; if ( cNext == '(' )
{
bDoParen = false;
aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses
}
}
}
pView->InsertText( aInsStr );
if ( bDoParen ) // Put cursor between parentheses
{
aSel = pView->GetSelection();
--aSel.start.nIndex;
--aSel.end.nIndex;
pView->SetSelection(aSel);
DataChanging(); // Cannot be new
completeFunction( pTopView, aInsert, bParInserted );
completeFunction( pTableView, aInsert, bParInserted );
DataChanged();
ShowTipCursor();
if (bParInserted)
AutoParAdded();
}
HideTip();
EditView* pActiveView = pTopView ? pTopView : pTableView; if (comphelper::LibreOfficeKit::isActive() && pTopView && pInputWin)
pInputWin->TextGrabFocus(); if (pActiveView)
pActiveView->ShowCursor();
}
void ScInputHandler::LOKPasteFunctionData(const OUString& rFunctionName)
{ // in case we have no top view try to create it if (!pTopView && pInputWin)
{
ScInputMode eCurMode = eMode;
SetMode(SC_INPUT_TOP); if (!pTopView)
SetMode(eCurMode);
}
// We can get three updates per keystroke, StartExtTextInput, ExtTextInput and PostExtTextInput // Skip duplicate updates. Be conservative and don't skip duplicates that are 5+ seconds // apart.
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); if (maSendFormulabarUpdate.m_nShellId == nCurrentShellId &&
maSendFormulabarUpdate.m_aText == rText &&
maSendFormulabarUpdate.m_aSelection == aSelection &&
std::chrono::duration_cast<std::chrono::seconds>(
now - maSendFormulabarUpdate.m_nTimeStamp) < std::chrono::seconds(5))
{ return;
}
// Calculate selection and display as tip help static OUString lcl_Calculate( const OUString& rFormula, ScDocument& rDoc, constScAddress &rPos )
{ //TODO: Merge with ScFormulaDlg::CalcValue and move into Document! // Quotation marks for Strings are only inserted here.
// FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own bool bColRowName = pCalc->HasColRowName(); if ( bColRowName )
{ // ColRowName in RPN code? if ( pCalc->GetCode()->GetCodeLen() <= 1 )
{ // ==1: Single one is as a Parameter always a Range // ==0: It might be one, if ...
OUString aBraced = "(" + rFormula + ")";
pCalc.emplace( rDoc, rPos, aBraced, false );
} else
bColRowName = false;
}
if (!aValue.isEmpty())
{
ShowTip( aValue ); // Display as QuickHelp
aManualTip = aValue; // Set after ShowTip if (pFormulaData)
miAutoPosFormula = pFormulaData->end(); if (pColumnData)
miAutoPosColumn = pColumnData->end();
}
}
void ScInputHandler::PasteManualTip()
{ // Three dots at the end -> Range reference -> do not insert // FIXME: Once we have matrix constants, we can change this
sal_Int32 nTipLen = aManualTip.getLength();
sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen)); if ( nTipLen && ( nTipLen < 3 || aManualTip.subView( nTipLen2-3 ) != u"..." ) )
{
DataChanging(); // Cannot be new
void ScInputHandler::AutoParAdded()
{
++nAutoPar; // Closing parenthesis can be overwritten
}
bool ScInputHandler::CursorAtClosingPar()
{ // Test if the cursor is before a closing parenthesis // Selection from SetReference has been removed before
EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode )
{
ESelection aSel = pActiveView->GetSelection();
sal_Int32 nPos = aSel.start.nIndex;
OUString aFormula = mpEditEngine->GetText(0); if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' ) returntrue;
} returnfalse;
}
void ScInputHandler::SkipClosingPar()
{ // this is called when a ')' is typed and the cursor is before a ')' // that can be overwritten -> just set the cursor behind the ')'
// Display the rest of longest common prefix as suggestion.
aNew = aResultVec[0].copy(0, nLongestPrefixLen);
} else
{
aNew = aResultVec[0];
}
// Strings can contain line endings (e.g. due to dBase import), // which would result in multiple paragraphs here, which is not desirable. //! Then GetExactMatch doesn't work either
lcl_RemoveLineEnd( aNew );
// Keep paragraph, just append the rest //! Exact replacement in EnterHandler !!! // One Space between paragraphs:
sal_Int32 nEdLen = mpEditEngine->GetTextLen() + nParCnt - 1;
OUString aIns = aNew.copy(nEdLen);
// Selection must be "backwards", so the cursor stays behind the last // typed character
ESelection aSelection( aSel.end.nPara, aSel.end.nIndex + aIns.getLength(),
aSel.end.nPara, aSel.end.nIndex );
// When editing in input line, apply to both edit views if ( pTableView )
{
pTableView->InsertText( aIns );
pTableView->SetSelection( aSelection );
} if ( pTopView )
{
pTopView->InsertText( aIns );
pTopView->SetSelection( aSelection );
}
aAutoSearch = aText; // To keep searching - nAutoPos is set
}
void ScInputHandler::NextAutoEntry( bool bBack )
{
EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView && pColumnData )
{ if (!aAutoSearch.isEmpty())
{ // Is the selection still valid (could be changed via the mouse)?
ESelection aSel = pActiveView->GetSelection();
aSel.Adjust();
sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); if ( aSel.end.nPara+1 == nParCnt && aSel.start.nPara == aSel.end.nPara )
{
OUString aText = GetEditText(mpEditEngine.get());
sal_Int32 nSelLen = aSel.end.nIndex - aSel.start.nIndex;
sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.end.nPara ); if ( aSel.end.nIndex == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen )
{
OUString aNew;
ScTypedCaseStrSet::const_iterator itNew =
findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack);
// when editing in input line, apply to both edit views if ( pTableView )
{
pTableView->DeleteSelected();
pTableView->InsertText( aIns );
pTableView->SetSelection( ESelection(
aSel.end.nPara, aSel.start.nIndex + aIns.getLength(),
aSel.end.nPara, aSel.start.nIndex ) );
} if ( pTopView )
{
pTopView->DeleteSelected();
pTopView->InsertText( aIns );
pTopView->SetSelection( ESelection(
aSel.end.nPara, aSel.start.nIndex + aIns.getLength(),
aSel.end.nPara, aSel.start.nIndex ) );
}
bInOwnChange = false;
}
}
}
}
}
// For Tab, HideCursor was always called first if (pActiveView)
pActiveView->ShowCursor();
}
// Highlight parentheses void ScInputHandler::UpdateParenthesis()
{ // Find parentheses //TODO: Can we disable parentheses highlighting per parentheses? bool bFound = false; if ( bFormulaMode && eMode != SC_INPUT_TOP )
{ if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom
{
ESelection aSel = pTableView->GetSelection(); if (aSel.start.nIndex)
{ // Examine character left to the cursor
sal_Int32 nPos = aSel.start.nIndex - 1;
OUString aFormula = mpEditEngine->GetText(aSel.start.nPara);
sal_Unicode c = aFormula[nPos]; if ( c == '(' || c == ')' )
{ // Note this matches only within one paragraph.
sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos ); if ( nOther != -1 )
{
SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
//! Distinguish if cell is already highlighted!!!! if (bParenthesisShown)
{ // Remove old highlighting
sal_Int32 nCount = mpEditEngine->GetParagraphCount(); for (sal_Int32 i=0; i<nCount; i++)
mpEditEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
}
// Dummy InsertText for Update and Paint (selection is empty)
pTableView->InsertText( OUString() );
bFound = true;
}
}
}
// mark parenthesis right of cursor if it will be overwritten (nAutoPar) // with different color (COL_LIGHTBLUE) ??
}
}
// Remove old highlighting, if no new one is set if ( bParenthesisShown && !bFound && pTableView )
{
sal_Int32 nCount = mpEditEngine->GetParagraphCount(); for (sal_Int32 i=0; i<nCount; i++)
pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
}
ScModule* mod = ScModule::get(); if ( pViewSh == pRefViewSh )
{ //! The input from the EnterHandler does not arrive anymore // We end the EditMode anyways
EnterHandler();
bFormulaMode = false;
pRefViewSh = nullptr;
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
mod->SetRefInputHdl(nullptr); if (pInputWin)
pInputWin->SetFormulaMode(false);
UpdateAutoCorrFlag();
}
// #i20588# Don't rely on focus to find the active edit view. Instead, the // active pane at the start of editing is now stored (GetEditActivePart). // GetActiveWin (the currently active pane) fails for ref input across the // panes of a split view.
// setup the pTableView editeng for tiled rendering to get cursor and selections if (pTableView && pActiveViewSh)
{ if (comphelper::LibreOfficeKit::isActive())
{
pTableView->RegisterViewShell(pActiveViewSh);
}
}
if (pInputWin && (eMode == SC_INPUT_TOP || eMode == SC_INPUT_TABLE))
{ // tdf#71409: Always create the edit engine instance for the input // window, in order to properly manage accessibility events.
pTopView = pInputWin->GetEditView(); if (eMode != SC_INPUT_TOP)
pTopView = nullptr;
} else
pTopView = nullptr;
}
void ScInputHandler::RemoveAdjust()
{ // Delete hard alignment attributes bool bUndo = mpEditEngine->IsUndoEnabled(); if ( bUndo )
mpEditEngine->EnableUndo( false );
// Non-default paragraph attributes (e.g. from clipboard) // must be turned into character attributes
mpEditEngine->RemoveParaAttribs();
if ( bUndo )
mpEditEngine->EnableUndo( true );
}
void ScInputHandler::RemoveRangeFinder()
{ // Delete pRangeFindList and colors
mpEditEngine->SetUpdateLayout(false);
sal_Int32 nCount = mpEditEngine->GetParagraphCount(); // Could just have been inserted for (sal_Int32 i=0; i<nCount; i++)
mpEditEngine->RemoveCharAttribs( i, EE_CHAR_COLOR );
mpEditEngine->SetUpdateLayout(true);
if (!aTester.IsEditable())
{
bProtected = true; // We allow read-only input mode activation regardless // whether it's part of an array or not or whether explicit cell // activation is requested (double-click or F2) or a click in input // line. bool bShowError = (!bInputActivated || !aTester.GetMessageId() || aTester.GetMessageId() != STR_PROTECTIONERR) &&
!pActiveViewSh->GetViewData().GetDocShell().IsReadOnly(); if (bShowError)
{
eMode = SC_INPUT_NONE;
StopInputWinEngine( true );
UpdateFormulaMode(); if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) )
{ // Prevent repeated error messages for the same cell from command events // (for keyboard events, multiple messages are wanted). // Set the flag before showing the error message because the command handler // for the next IME command may be called when showing the dialog. if ( bFromCommand )
bCommandErrorShown = true;
// EditEngine Defaults // In no case SetParaAttribs, because the EditEngine might already // be filled (for Edit cells). // SetParaAttribs would change the content.
//! The SetDefaults is now (since MUST/src602 //! EditEngine changes) implemented as a SetParaAttribs. //! Any problems?
// Adjustment
eAttrAdjust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue(); if ( eAttrAdjust == SvxCellHorJustify::Repeat &&
pPattern->GetItem(ATTR_LINEBREAK).GetValue() )
{ // #i31843# "repeat" with "line breaks" is treated as default alignment
eAttrAdjust = SvxCellHorJustify::Standard;
}
}
if (pTopEngine)
{ // Necessary to sync SvxAutoCorrect behavior. This has to be // done before InitRangeFinder() below.
MergeLanguageAttributes( *pTopEngine);
}
// UpdateSpellSettings enables online spelling if needed // -> also call if attributes are unchanged
UpdateSpellSettings( true ); // uses pLastPattern
if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify )
{ // table EditEngine is formatted below, input line needs formatting after paste // #i20282# not when called from the input line's modify handler
pTopView->getEditEngine().QuickFormatDoc( true );
// #i23720# QuickFormatDoc hides the cursor, but can't show it again because it // can't safely access the EditEngine's current view, so the cursor has to be // shown again here.
pTopView->ShowCursor();
}
if (bSetModified)
bModified = true;
bSelIsRef = false;
if ( pRangeFindList && !bInRangeUpdate )
RemoveRangeFinder(); // Delete attributes and labels
if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE)
{
OUString aText; if (pInputWin)
aText = ScEditUtil::GetMultilineString(*mpEditEngine); else
aText = GetEditText(mpEditEngine.get());
lcl_RemoveTabs(aText);
if (pInputWin)
{ // If we will end up updating LoKit at the end, we can skip it here
pInputWin->SetTextString(aText, !bUpdateKit);
}
}
// If the cursor is before the end of a paragraph, parts are being pushed to // the right (independently from the eMode) -> Adapt View! // If the cursor is at the end, the StatusHandler of the ViewData is sufficient. // // First make sure the status handler is called now if the cursor // is outside the visible area
mpEditEngine->QuickFormatDoc();
UpdateFormulaMode();
bTextValid = false; // Changes only in the EditEngine
bInOwnChange = false;
}
bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr ) const
{ // For new input '+' and '-' may start the dreaded "lazy data typist" // formula input, editing existing formula content can only start with '='. return !rStr.empty() && (rStr[0] == '=' || (!mbEditingExistingContent && (rStr[0] == '+' || rStr[0] == '-')));
}
if ( bIsFormula )
{ if (!bFormulaMode)
{
pActiveViewSh->GetViewData().SetEditHighlight(true);
bFormulaMode = true;
pRefViewSh = pActiveViewSh;
pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
ScModule* pMod = ScModule::get();
pMod->SetRefInputHdl(this); if (pInputWin)
pInputWin->SetFormulaMode(true);
// in LOK, we always need to perform the GetFormulaData() call so // that the formula insertion works if (comphelper::LibreOfficeKit::isActive() || pMod->GetAppOptions().GetAutoComplete())
GetFormulaData();
void ScInputHandler::ShowRefFrame()
{ // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh. // A local variable is used instead.
ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); if ( !(pRefViewSh && pRefViewSh != pVisibleSh) ) return;
if (bFound)
{ // We count on Activate working synchronously here // (pActiveViewSh is set while doing so)
pRefViewSh->SetActive(); // Appear and SetViewFrame
// pLastState is set correctly in the NotifyChange from the Activate
} else
{
OSL_FAIL("ViewFrame for reference input is not here anymore");
}
}
if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE)
{ if (eOldMode == SC_INPUT_NONE) // not if switching between modes
{ if (StartTable(0, false, eMode == SC_INPUT_TABLE, pTopEngine))
{
pActiveViewSh->GetViewData().GetDocShell().PostEditView(*mpEditEngine, aCursorPos);
}
}
if (pInitText)
{
mpEditEngine->SetTextCurrentDefaults(*pInitText);
bModified = true;
}
// Macro calls for validity can cause a lot of problems, so inhibit // nested calls of EnterHandler(). if (bInEnterHandler) return;
bInEnterHandler = true;
bInOwnChange = true; // disable ModifyHdl (reset below)
mbPartialPrefix = false;
ImplCreateEditEngine();
OUString aString = GetEditText(mpEditEngine.get());
OUString aPreAutoCorrectString(aString);
EditView* pActiveView = pTopView ? pTopView : pTableView; if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString))
{ if (pColumnData && miAutoPosColumn != pColumnData->end())
{ // #i47125# If AutoInput appended something, do the final AutoCorrect // with the cursor at the end of the input.
lcl_SelectionToEnd(pTopView);
lcl_SelectionToEnd(pTableView);
}
if (pTopView)
pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views if (pTableView)
pTableView->CompleteAutoCorrect(pFrameWin);
aString = GetEditText(mpEditEngine.get());
}
lcl_RemoveTabs(aString);
lcl_RemoveTabs(aPreAutoCorrectString);
// Test if valid (always with simple string) if (bModified && nValidation)
{
ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); const ScValidationData* pData = rDoc.GetValidationEntry( nValidation ); if (pData)
{ // #i67990# don't use pLastPattern in EnterHandler const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
bool bOk;
if (pData->GetDataMode() == SC_VALID_CUSTOM)
{
bOk = pData->IsDataValidCustom( aString, *pPattern, aCursorPos, ScValidationData::CustomValidationPrivateAccess() );
} else
{
bOk = pData->IsDataValid( aString, *pPattern, aCursorPos );
}
if (!bOk)
{
pActiveViewSh->StopMarking(); // (the InfoBox consumes the MouseButtonUp)
// tdf#125917 Release the grab that a current mouse-down event being handled // by ScTabView has put on the mouse via its SelectionEngine. // Otherwise the warning box cannot interact with the mouse if (ScTabView* pView = pActiveViewSh->GetViewData().GetView())
{ if (ScViewSelectionEngine* pSelEngine = pView->GetSelEngine())
pSelEngine->ReleaseMouse();
}
if (bBeforeSavingInLOK)
{ // Invalid entry but not applied to the document model. // Exit to complete the "save", leaving the edit view as it is // for the user to continue after save.
bInOwnChange = false;
bInEnterHandler = false; return;
}
// Check for input into DataPilot table if ( bModified && !bForget )
{
ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
ScDPObject* pDPObj = rDoc.GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); if ( pDPObj )
{ // Any input within the DataPilot table is either a valid renaming // or an invalid action - normal cell input is always aborted
pActiveViewSh->DataPilotInput( aCursorPos, aString );
bForget = true;
}
}
std::vector<editeng::MisspellRanges> aMisspellRanges; // UpdateLayout must be true during CompleteOnlineSpelling constbool bUpdateLayout = mpEditEngine->SetUpdateLayout( true );
mpEditEngine->CompleteOnlineSpelling(); bool bSpellErrors = !bFormulaMode && mpEditEngine->HasOnlineSpellErrors(); if ( bSpellErrors )
{ // #i3820# If the spell checker flags numerical input as error, // it still has to be treated as number, not EditEngine object.
ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); // #i67990# don't use pLastPattern in EnterHandler const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); if (pPattern)
{
SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); // without conditional format, as in ScColumn::SetString
sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter ); double nVal; if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) )
{
bSpellErrors = false; // ignore the spelling errors
}
}
}
// After RemoveAdjust, the EditView must not be repainted (has wrong font size etc). // SetUpdateLayout must come after CompleteOnlineSpelling. // The view is hidden in any case below (Broadcast).
mpEditEngine->SetUpdateLayout( false );
if ( bModified && !bForget ) // What is being entered (text/object)?
{
sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); if ( nParCnt == 0 )
nParCnt = 1;
bool bUniformAttribs = true;
SfxItemSet aPara1Attribs = mpEditEngine->GetAttribs(0, 0, mpEditEngine->GetTextLen(0)); for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara)
{
SfxItemSet aPara2Attribs = mpEditEngine->GetAttribs(nPara, 0, mpEditEngine->GetTextLen(nPara)); if (!(aPara1Attribs == aPara2Attribs))
{ // Paragraph format different from that of the 1st paragraph.
bUniformAttribs = false; break;
}
}
// Always recognize formulas as formulas // We still need the preceding test due to cell attributes
}
if (bSpellErrors)
mpEditEngine->GetAllMisspellRanges(aMisspellRanges);
if (bMatrix)
bAttrib = false;
if (bAttrib)
{
mpEditEngine->ClearSpellErrors();
pObject = mpEditEngine->CreateTextObject();
} elseif (ScModule::get()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case
{ // Perform case-matching only when the typed text is partial. if (pColumnData && aAutoSearch.getLength() < aString.getLength())
aString = getExactMatch(*pColumnData, aString);
}
}
// Don't rely on ShowRefFrame switching the active view synchronously // execute the function directly on the correct view's bindings instead // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
if (bFormulaMode)
{
ShowRefFrame();
if (pExecuteSh)
{
pExecuteSh->SetTabNo(aCursorPos.Tab());
pExecuteSh->ActiveGrabFocus();
}
bFormulaMode = false;
pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
ScModule::get()->SetRefInputHdl(nullptr); if (pInputWin)
pInputWin->SetFormulaMode(false);
UpdateAutoCorrFlag();
}
pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
DeleteRangeFinder();
ResetAutoPar();
// Text input (through number formats) or ApplySelectionPattern modify // the cell's attributes, so pLastPattern is no longer valid
pLastPattern = nullptr;
// Don't rely on ShowRefFrame switching the active view synchronously // execute the function directly on the correct view's bindings instead // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
if (bFormulaMode)
{
ShowRefFrame(); if (pExecuteSh)
{
pExecuteSh->SetTabNo(aCursorPos.Tab());
pExecuteSh->ActiveGrabFocus();
}
bFormulaMode = false;
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
ScModule::get()->SetRefInputHdl(nullptr); if (pInputWin)
pInputWin->SetFormulaMode(false);
UpdateAutoCorrFlag();
}
pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
DeleteRangeFinder();
ResetAutoPar();
const ScDocument* pThisDoc = nullptr; if (pRefViewSh)
pThisDoc = &pRefViewSh->GetViewData().GetDocument(); bool bOtherDoc = (pThisDoc != &rDoc); if (bOtherDoc && !rDoc.GetDocumentShell()->HasName())
{ // References to unnamed document; that doesn't work // SetReference should not be called, then return;
} if (!pThisDoc)
pThisDoc = &rDoc;
UpdateActiveView(); if (!pTableView && !pTopView) return; // E.g. FillMode
// Never overwrite the "="!
EditView* pActiveView = pTopView ? pTopView : pTableView;
ESelection aSel = pActiveView->GetSelection();
aSel.Adjust(); if (aSel.start.nPara == 0 && aSel.start.nIndex == 0) return;
DataChanging(); // Cannot be new
// Turn around selection if backwards. if (pTableView)
{
ESelection aTabSel = pTableView->GetSelection(); if (aTabSel.start.nIndex > aTabSel.end.nIndex && aTabSel.start.nPara == aTabSel.end.nPara)
{
aTabSel.Adjust();
pTableView->SetSelection(aTabSel);
}
} if (pTopView)
{
ESelection aTopSel = pTopView->GetSelection(); if (aTopSel.start.nIndex > aTopSel.end.nIndex && aTopSel.start.nPara == aTopSel.end.nPara)
{
aTopSel.Adjust();
pTopView->SetSelection(aTopSel);
}
}
// Create string from reference, in the syntax of the document being edited.
OUString aRefStr; const ScAddress::Details aAddrDetails( *pThisDoc, aCursorPos ); if (bOtherDoc)
{ // Reference to other document
OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab");
// Always 3D and absolute.
OUString aTmp(rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails));
ScDocShell* pObjSh = rDoc.GetDocumentShell(); // #i75893# convert escaped URL of the document to something user friendly
OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
if (bAlt && !bControl && nCode != KEY_RETURN) // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not. returnfalse;
// There is a partial autocomplete suggestion. // Allow its completion with right arrow key (without modifiers). if (mbPartialPrefix && nCode == KEY_RIGHT && !bControl && !bShift && !bAlt &&
(pTopView || pTableView))
{ if (pTopView)
pTopView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH)); if (pTableView)
pTableView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH));
mbPartialPrefix = false;
// Indicate that this event has been consumed and ScTabViewShell should not act on this. returntrue;
}
if (!bControl && nCode == KEY_TAB)
{ // Normal TAB moves the cursor right.
EnterHandler();
if (pActiveViewSh)
pActiveViewSh->FindNextUnprot( bShift, true );
if (pTableView)
{
pTableView->getEditEngine().SetText( aStrLoP ); if ( !aStrLoP.isEmpty() )
pTableView->SetSelection(ESelection()); // before the '%'
// Don't call SetSelection if the string is empty anyway, // to avoid breaking the bInitial handling in ScViewData::EditGrowY
} if (pTopView)
{
pTopView->getEditEngine().SetText( aStrLoP ); if ( !aStrLoP.isEmpty() )
pTopView->SetSelection(ESelection()); // before the '%'
}
}
SyncViews();
}
// AutoInput: if (bUsed && ScModule::get()->GetAppOptions().GetAutoComplete())
{
bUseTab = false; if (pFormulaData)
miAutoPosFormula = pFormulaData->end(); // do not search further if (pColumnData)
miAutoPosColumn = pColumnData->end();
KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction(); if ( nChar && nChar != 8 && nChar != 127 && // no 'backspace', no 'delete'
KeyFuncType::CUT != eFunc) // and no 'CTRL-X'
{ if (bFormulaMode)
UseFormulaData(); else
UseColData();
}
}
// When the selection is changed manually or an opening parenthesis // is typed, stop overwriting parentheses if ( bUsed && nChar == '(' )
ResetAutoPar();
// #i114511# don't count cursor keys as modification bool bSetModified = !bCursorKey; // tdf#81913 - don't delete range finder since cursor keys don't count as modifications
bInRangeUpdate = bCursorKey;
DataChanged(false, bSetModified); // also calls UpdateParenthesis()
bInRangeUpdate = false;
// In the LOK case, we want to set the document modified state // right away at the start of the edit, so that the content is // saved even when the user leaves the document before hitting // Enter if (comphelper::LibreOfficeKit::isActive() && bSetModified && pActiveViewSh && !pActiveViewSh->GetViewData().GetDocShell().IsModified())
pActiveViewSh->GetViewData().GetDocShell().SetModified();
InvalidateAttribs(); //! in DataChanged?
}
}
if (pTopView && eMode != SC_INPUT_NONE)
SyncViews();
return bUsed;
}
OUString ScInputHandler::GetSurroundingText()
{ if (eMode != SC_INPUT_NONE)
{
UpdateActiveView(); if (pTableView || pTopView)
{ if (pTableView) return pTableView->GetSurroundingText(); elseif (pTopView) // call only once return pTopView->GetSurroundingText();
}
} return OUString();
}
Selection ScInputHandler::GetSurroundingTextSelection()
{ if (eMode != SC_INPUT_NONE)
{
UpdateActiveView(); if (pTableView || pTopView)
{ if (pTableView) return pTableView->GetSurroundingTextSelection(); elseif (pTopView) // call only once return pTopView->GetSurroundingTextSelection();
}
} return Selection(0, 0);
}
bool ScInputHandler::DeleteSurroundingText(const Selection& rSelection)
{ if (eMode != SC_INPUT_NONE)
{
UpdateActiveView(); if (pTableView || pTopView)
{ if (pTableView) return pTableView->DeleteSurroundingText(rSelection); elseif (pTopView) // call only once return pTopView->DeleteSurroundingText(rSelection);
}
} returnfalse;
}
void ScInputHandler::InputCommand( const CommandEvent& rCEvt )
{ if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
{ // For CommandEventId::CursorPos, do as little as possible, because // with remote VCL, even a ShowCursor will generate another event. if ( eMode != SC_INPUT_NONE )
{
UpdateActiveView(); if (pTableView || pTopView)
{ if (pTableView)
pTableView->Command( rCEvt ); elseif (pTopView) // call only once
pTopView->Command( rCEvt );
}
}
} elseif ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
{ if ( eMode != SC_INPUT_NONE )
{
UpdateActiveView(); if (pTableView || pTopView)
{ if (pTableView)
pTableView->Command( rCEvt ); elseif (pTopView) // call only once
pTopView->Command( rCEvt );
}
}
} else
{
HideTip();
HideTipBelow();
if ( bSelIsRef )
{
RemoveSelection();
bSelIsRef = false;
}
void ScInputHandler::NotifyChange( const ScInputHdlState* pState, bool bForce, ScTabViewShell* pSourceSh, bool bStopEditing)
{ // If the call originates from a macro call in the EnterHandler, // return immediately and don't mess up the status if (bInEnterHandler) return;
//! Before EditEngine gets eventually created (so it gets the right pools) if ( pSourceSh )
pActiveViewSh = pSourceSh; else
pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
if (pActiveViewSh)
ImplCreateEditEngine();
if ( pState != pLastState.get() )
{
pLastState.reset(getLastState(pState));
}
// Also take foreign reference input into account here (e.g. FunctionsAutoPilot), // FormEditData, if we're switching from Help to Calc: if ( !bFormulaMode && !pScMod->IsFormulaMode() &&
( !pScTabViewShell || !pScTabViewShell->GetFormEditData() ) )
{ bool bIgnore = false; if ( bModified )
{ if (pState->GetPos() != aCursorPos)
{ if (!bProtected)
EnterHandler();
} else
bIgnore = true;
}
if ( pInputWin || comphelper::LibreOfficeKit::isActive()) // Named range input
{
OUString aPosStr; bool bSheetLocal = false; const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
// Is the range a name? //! Find by Timer? if ( pActiveViewSh )
pActiveViewSh->GetViewData().GetDocument().
GetRangeAtBlock( ScRange( rSPos, rEPos ), aPosStr, &bSheetLocal);
if ( aPosStr.isEmpty() ) // Not a name -> format
{
ScRefFlags nFlags = ScRefFlags::ZERO; if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 )
nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; if ( rSPos != rEPos )
{
ScRange r(rSPos, rEPos);
applyStartToEndFlags(nFlags);
aPosStr = r.Format(rDoc, ScRefFlags::VALID | nFlags, aAddrDetails);
} else
aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
} elseif (bSheetLocal)
{
OUString aName; if (rDoc.GetName( rSPos.Tab(), aName))
aPosStr = ScPosWnd::createLocalRangeName( aPosStr, aName);
}
if (pInputWin)
{
pInputWin->SetPosString(aPosStr);
pInputWin->SetSumAssignMode();
}
if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS, aPosStr.toUtf8());
}
if (bStopEditing) {
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
// As long as the content is not edited, turn off online spelling. // Online spelling is turned back on in StartTable, after setting // the right language from cell attributes.
if ( pInputWin)
{ // Do not enable if RefDialog is open if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen())
{ if ( !pInputWin->IsEnabled())
{
pDelayTimer->Stop();
pInputWin->Enable();
}
} elseif(pScMod->IsRefDialogOpen())
{ // Because every document has its own InputWin, // we should start Timer again, because the input line may // still be active if ( !pDelayTimer->IsActive() )
pDelayTimer->Start();
}
}
} else// !pState || !pActiveViewSh
{ if ( !pDelayTimer->IsActive() )
pDelayTimer->Start();
}
// Don't hide function tooltip in LOK, a remote user might be using tip. if (bStopEditing)
HideTip();
HideTipBelow();
bInOwnChange = false;
}
//! New method at ScModule to query if function autopilot is open
SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
{ if ( pInputWin)
{
pInputWin->EnableButtons( false );
pInputWin->Disable();
}
} elseif ( !bFormulaMode ) // Keep formula e.g. for help
{
bInOwnChange = true; // disable ModifyHdl (reset below)
// #i20282# DataChanged needs to know if this is from the input line's modify handler bool bFromTopNotify = ( bFromNotify && pView == pTopView );
bool bNewView = DataChanging(); //FIXME: Is this at all possible?
aCurrentText = pView->getEditEngine().GetText(); // Also remember the string
mpEditEngine->SetTextCurrentDefaults( aCurrentText );
DataChanged( bFromTopNotify );
bTextValid = true; // Is set to false in DataChanged
if ( pActiveViewSh )
{
ScViewData& rViewData = pActiveViewSh->GetViewData(); if ( bNewView )
rViewData.GetDocShell().PostEditView(*mpEditEngine, aCursorPos);
rViewData.EditGrowY();
rViewData.EditGrowX();
}
SyncViews( pView );
}
const OUString& ScInputHandler::GetEditString()
{ if (mpEditEngine)
{
aCurrentText = mpEditEngine->GetText(); // Always new from Engine
bTextValid = true;
}
eMode = SC_INPUT_NONE; /* TODO: it would be better if there was some way to reset the input bar * engine instead of deleting and having it recreate through * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
* fdo#72278 without reintroducing fdo#69971. */
StopInputWinEngine(true);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.