diff options
author | Dennis Francis <dennis.francis@collabora.com> | 2021-10-13 20:24:50 +0530 |
---|---|---|
committer | Dennis Francis <dennis.francis@collabora.com> | 2021-10-18 13:20:02 +0200 |
commit | 4cfa840ecaa5a094a32a60a60b295858980109df (patch) | |
tree | 3d8edbafe8609dbbc4e70465fc07bb54e5fc5218 /sc/source | |
parent | b2e4123cfd597ef19eebcb1d2a98c058eea990b2 (diff) |
tdf#145198: Bash like autocompletion for Calc autoinput
Refer ESC minutes section "Calc auto-complete behavior changes" at
https://lists.freedesktop.org/archives/libreoffice/2021-October/087911.html
for context.
Consider an example of the following data in a column:
ABCD123xyz
ABCD345qwel
ABCD123pqr
ABCD123xyz
PQR
1. When user types A, it will show the partial suggestion BCD.
2. User can accept the suggestion with the right arrow key and then the
cursor will be placed after the letter D, waiting for more input. User
can choose to not accept the suggestion either by typing more or by
ending the edit mode by pressing Esc key.
3. If the user accepts the suggestion BCD by right arrow key, and
types 1, it will show a partial suggestion of 23. User can accept
this by pressing the right arrow key.
4. If the user accepts the suggestion in the 3rd step and types x it will
show the final suggestion yz. Again user can choose to accept or decline
the suggestion as mentioned in the 1st step.
The tiledrendering test ScTiledRenderingTest::testAutoInputExactMatch()
is amended to match the new behaviour.
Change-Id: Ib2cfc16af71483790384e70eb7332f864cf744c5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123578
Tested-by: Jenkins
Reviewed-by: Dennis Francis <dennis.francis@collabora.com>
Diffstat (limited to 'sc/source')
-rw-r--r-- | sc/source/ui/app/inputhdl.cxx | 101 | ||||
-rw-r--r-- | sc/source/ui/inc/inputhdl.hxx | 6 | ||||
-rw-r--r-- | sc/source/ui/view/tabvwsh4.cxx | 5 |
3 files changed, 94 insertions, 18 deletions
diff --git a/sc/source/ui/app/inputhdl.cxx b/sc/source/ui/app/inputhdl.cxx index 9c553cec8614..97c86b34b00d 100644 --- a/sc/source/ui/app/inputhdl.cxx +++ b/sc/source/ui/app/inputhdl.cxx @@ -162,15 +162,42 @@ OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rStrin return rString; } +// 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, const OUString& rUserEntry, sal_Int32 nRingOrigin) +{ + sal_Int32 nResults = rResults.size(); + if (!nResults) + return 0; + + if (nResults == 1) + return rResults[0].getLength(); + + sal_Int32 nMinLen = rUserEntry.getLength(); + sal_Int32 nLastIdx = nRingOrigin ? nRingOrigin - 1 : nResults - 1; + const OUString& rFirst = rResults[nRingOrigin]; + const OUString& rLast = rResults[nLastIdx]; + const sal_Int32 nMaxLen = std::min(rFirst.getLength(), rLast.getLength()); + + for (sal_Int32 nLen = nMaxLen; nLen > nMinLen; --nLen) + { + if (ScGlobal::GetTransliteration().isMatch(rFirst.copy(0, nLen), rLast)) + return nLen; + } + + return nMinLen; +} + ScTypedCaseStrSet::const_iterator findTextAll( const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos, - const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack, size_t nMax = 0) + const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack, sal_Int32* pLongestPrefixLen = nullptr) { rResultVec.clear(); // clear contents if (!rDataSet.size()) return rDataSet.end(); + sal_Int32 nRingOrigin = 0; size_t nCount = 0; ScTypedCaseStrSet::const_iterator retit; if ( bBack ) // Backwards @@ -198,7 +225,10 @@ ScTypedCaseStrSet::const_iterator findTextAll( { ++it; if ( it == rDataSet.rend() ) // go to the first if reach the end + { it = rDataSet.rbegin(); + nRingOrigin = nCount; + } if ( bFirstTime ) bFirstTime = false; @@ -221,8 +251,6 @@ ScTypedCaseStrSet::const_iterator findTextAll( std::advance(retit, nPos); } ++nCount; - if (nMax > 0 && nCount >= nMax) - break; } } else // Forwards @@ -238,7 +266,10 @@ ScTypedCaseStrSet::const_iterator findTextAll( { ++it; if ( it == rDataSet.end() ) // go to the first if reach the end + { it = rDataSet.begin(); + nRingOrigin = nCount; + } if ( bFirstTime ) bFirstTime = false; @@ -255,11 +286,21 @@ ScTypedCaseStrSet::const_iterator findTextAll( if ( nCount == 0 ) retit = it; // remember first match iterator ++nCount; - if (nMax > 0 && nCount >= nMax) - break; } } + 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 @@ -788,6 +829,7 @@ ScInputHandler::ScInputHandler() bProtected( false ), bLastIsSymbol( false ), mbDocumentDisposing(false), + mbPartialPrefix(false), nValidation( 0 ), eAttrAdjust( SvxCellHorJustify::Standard ), aScaleX( 1,1 ), @@ -1971,24 +2013,28 @@ void ScInputHandler::UseColData() // When typing std::vector< OUString > aResultVec; OUString aNew; + sal_Int32 nLongestPrefixLen = 0; miAutoPosColumn = pColumnData->end(); - miAutoPosColumn = findTextAll(*pColumnData, miAutoPosColumn, aText, aResultVec, false, 2); - bool bShowCompletion = (aResultVec.size() == 1); - bUseTab = (aResultVec.size() == 2); - if (bUseTab) + mbPartialPrefix = false; + miAutoPosColumn = findTextAll(*pColumnData, miAutoPosColumn, aText, aResultVec, false, &nLongestPrefixLen); + + if (nLongestPrefixLen <= 0 || aResultVec.empty()) + return; + + if (aResultVec.size() > 1) { - // Allow cycling through possible matches using shortcut. - // Make miAutoPosColumn invalid so that Ctrl+TAB provides the first matching one. + mbPartialPrefix = true; + bUseTab = true; // Allow Ctrl (+ Shift + ) + TAB cycling. miAutoPosColumn = pColumnData->end(); - aAutoSearch = aText; - return; - } - if (!bShowCompletion) - return; + // Display the rest of longest common prefix as suggestion. + aNew = aResultVec[0].copy(0, nLongestPrefixLen); + } + else + { + aNew = aResultVec[0]; + } - assert(miAutoPosColumn != pColumnData->end()); - 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 @@ -2047,6 +2093,7 @@ void ScInputHandler::NextAutoEntry( bool bBack ) // match found! miAutoPosColumn = itNew; bInOwnChange = true; // disable ModifyHdl (reset below) + mbPartialPrefix = false; lcl_RemoveLineEnd( aNew ); OUString aIns = aNew.copy(aAutoSearch.getLength()); @@ -2942,6 +2989,7 @@ void ScInputHandler::EnterHandler( ScEnterMode nBlockMode ) if (bInEnterHandler) return; bInEnterHandler = true; bInOwnChange = true; // disable ModifyHdl (reset below) + mbPartialPrefix = false; ImplCreateEditEngine(); @@ -3306,6 +3354,7 @@ void ScInputHandler::CancelHandler() ImplCreateEditEngine(); bModified = false; + mbPartialPrefix = false; // Don't rely on ShowRefFrame switching the active view synchronously // execute the function directly on the correct view's bindings instead @@ -3598,6 +3647,22 @@ bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not. return false; + // 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. + return true; + } + if (!bControl && nCode == KEY_TAB) { // Normal TAB moves the cursor right. diff --git a/sc/source/ui/inc/inputhdl.hxx b/sc/source/ui/inc/inputhdl.hxx index aabac21240a0..fb3880e97a69 100644 --- a/sc/source/ui/inc/inputhdl.hxx +++ b/sc/source/ui/inc/inputhdl.hxx @@ -103,6 +103,9 @@ private: bool bProtected:1; bool bLastIsSymbol:1; bool mbDocumentDisposing:1; + /// To indicate if there is a partial prefix completion. + bool mbPartialPrefix:1; + sal_uLong nValidation; SvxCellHorJustify eAttrAdjust; @@ -266,6 +269,9 @@ public: bool IsInEnterHandler() const { return bInEnterHandler; } bool IsInOwnChange() const { return bInOwnChange; } + /// Returns true if there is a partial autocomplete suggestion. + bool HasPartialComplete() const { return mbPartialPrefix; }; + bool IsModalMode( const SfxObjectShell* pDocSh ); void ForgetLastPattern(); diff --git a/sc/source/ui/view/tabvwsh4.cxx b/sc/source/ui/view/tabvwsh4.cxx index e32bc217b090..8130ac4c0e12 100644 --- a/sc/source/ui/view/tabvwsh4.cxx +++ b/sc/source/ui/view/tabvwsh4.cxx @@ -1240,6 +1240,11 @@ bool ScTabViewShell::TabKeyInput(const KeyEvent& rKEvt) default: bIsType = true; } + else if (nCode == KEY_RIGHT && !bControl && !bShift && !bAlt) + { + ScInputHandler* pHdl = pScMod->GetInputHdl(this); + bIsType = pHdl && pHdl->HasPartialComplete(); + } if( bIsType ) bUsed = pScMod->InputKeyEvent( rKEvt ); // input |