summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
authorDennis Francis <dennis.francis@collabora.com>2021-10-13 20:24:50 +0530
committerDennis Francis <dennis.francis@collabora.com>2021-10-18 13:20:02 +0200
commit4cfa840ecaa5a094a32a60a60b295858980109df (patch)
tree3d8edbafe8609dbbc4e70465fc07bb54e5fc5218 /sc
parentb2e4123cfd597ef19eebcb1d2a98c058eea990b2 (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')
-rw-r--r--sc/qa/unit/tiledrendering/tiledrendering.cxx14
-rw-r--r--sc/source/ui/app/inputhdl.cxx101
-rw-r--r--sc/source/ui/inc/inputhdl.hxx6
-rw-r--r--sc/source/ui/view/tabvwsh4.cxx5
4 files changed, 101 insertions, 25 deletions
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 3cfde4952b16..ac0ca40d241d 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -2712,12 +2712,12 @@ void ScTiledRenderingTest::testAutoInputExactMatch()
ScAddress aA8(0, 7, 0);
lcl_typeCharsInCell("S", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "S" in A8
- // Should not autocomplete as there are multiple matches starting with "S".
- CPPUNIT_ASSERT_EQUAL_MESSAGE("1: A8 should have just S (should not autocomplete)", OUString("S"), pDoc->GetString(aA8));
+ // Should show the partial completion "i".
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("1: A8 should have partial completion Si", OUString("Si"), pDoc->GetString(aA8));
lcl_typeCharsInCell("Si", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Si" in A8
- // Should not autocomplete as there are multiple matches starting with "Si".
- CPPUNIT_ASSERT_EQUAL_MESSAGE("2: A8 should not autocomplete", OUString("Si"), pDoc->GetString(aA8));
+ // Should not show any suggestions.
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("2: A8 should not show suggestions", OUString("Si"), pDoc->GetString(aA8));
lcl_typeCharsInCell("Sim", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Sim" in A8
// Should autocomplete to "Simple" which is the only match.
@@ -2727,9 +2727,9 @@ void ScTiledRenderingTest::testAutoInputExactMatch()
// Should autocomplete to "Sing" which is the only match.
CPPUNIT_ASSERT_EQUAL_MESSAGE("4: A8 should autocomplete", OUString("Sing"), pDoc->GetString(aA8));
- lcl_typeCharsInCell("Cas", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Cas" in A8
- // Should not autocomplete as there are multiple matches starting with "Cas".
- CPPUNIT_ASSERT_EQUAL_MESSAGE("5: A8 should not autocomplete", OUString("Cas"), pDoc->GetString(aA8));
+ lcl_typeCharsInCell("C", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "C" in A8
+ // Should show the partial completion "as".
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("5: A8 should have partial completion Cas", OUString("Cas"), pDoc->GetString(aA8));
lcl_typeCharsInCell("Cast", aA8.Col(), aA8.Row(), pView, pModelObj); // Type "Cast" in A8
// Should autocomplete to "Castle" which is the only match.
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