/* -*- 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;
}
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.