summaryrefslogtreecommitdiff
path: root/sw/source/core/crsr/crstrvl.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/crsr/crstrvl.cxx')
-rw-r--r--sw/source/core/crsr/crstrvl.cxx137
1 files changed, 130 insertions, 7 deletions
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
index 73d12ce79c3a..74fe9b15ad57 100644
--- a/sw/source/core/crsr/crstrvl.cxx
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -883,9 +883,6 @@ bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& rCont
sal_Int32 nEnd = *pTextContentControl->End() - 1;
pCursor->GetMark()->nContent.Assign(pTextNode, nEnd);
- // Assume that once the placeholder is selected, the content is no longer the placeholder.
- pContentControl->SetShowingPlaceHolder(false);
-
bRet = !pCursor->IsSelOvr();
if (bRet)
{
@@ -897,6 +894,132 @@ bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& rCont
return bRet;
}
+/**
+ * Go to the next (or previous) form control, based first on tabIndex and then paragraph position,
+ * where a tabIndex of 1 is first, 0 is last, and -1 is excluded.
+ */
+void SwCursorShell::GotoFormControl(bool bNext)
+{
+ // (note: this only applies to modern content controls and legacy fieldmarks,
+ // since activeX richText controls aren't exposed to SW keystrokes)
+
+ struct FormControlSort
+ {
+ bool operator()(std::pair<const SwPosition&, sal_uInt32> rLHS,
+ std::pair<const SwPosition&, sal_uInt32> rRHS) const
+ {
+ assert(rLHS.second && rRHS.second && "tabIndex zero must be changed to SAL_MAX_UINT32");
+ //first compare tabIndexes where 1 has the priority.
+ if (rLHS.second < rRHS.second)
+ return true;
+ if (rLHS.second > rRHS.second)
+ return false;
+
+ // when tabIndexes are equal (and they usually are) then sort by paragraph position
+ return rLHS.first < rRHS.first;
+ }
+ };
+ std::map<std::pair<SwPosition, sal_uInt32>,
+ std::pair<SwTextContentControl*, sw::mark::IFieldmark*>, FormControlSort> aFormMap;
+
+ // add all of the eligible modern Content Controls into a sorted map
+ SwContentControlManager& rManager = GetDoc()->GetContentControlManager();
+ for (size_t i = 0; i < rManager.GetCount(); ++i)
+ {
+ SwTextContentControl* pTCC = rManager.UnsortedGet(i);
+ if (!pTCC || !pTCC->GetTextNode())
+ continue;
+ auto pCC = pTCC->GetContentControl().GetContentControl();
+
+ // -1 indicates the control should not participate in keyboard tab navigation
+ if (pCC && pCC->GetTabIndex() == SAL_MAX_UINT32)
+ continue;
+
+ const SwPosition nPos(*pTCC->GetTextNode(), pTCC->GetStart());
+
+ // since 0 is the lowest priority (1 is the highest), and -1 has already been excluded,
+ // use SAL_MAX_UINT32 as zero's tabIndex so that automatic sorting is correct.
+ sal_uInt32 nTabIndex = pCC && pCC->GetTabIndex() ? pCC->GetTabIndex() : SAL_MAX_UINT32;
+
+ const std::pair<SwTextContentControl*, sw::mark::IFieldmark*> pFormControl(pTCC, nullptr);
+ aFormMap[std::make_pair(nPos, nTabIndex)] = pFormControl;
+ }
+
+ if (aFormMap.begin() == aFormMap.end())
+ {
+ // only legacy fields exist. Avoid reprocessing everything and use legacy code path.
+ GotoFieldmark(bNext ? GetFieldmarkAfter(/*Loop=*/true) : GetFieldmarkBefore(/*Loop=*/true));
+ return;
+ }
+
+ // add all of the legacy form field controls into the sorted map
+ IDocumentMarkAccess* pMarkAccess = GetDoc()->getIDocumentMarkAccess();
+ for (auto it = pMarkAccess->getFieldmarksBegin(); it != pMarkAccess->getFieldmarksEnd(); ++it)
+ {
+ auto pFieldMark = dynamic_cast<sw::mark::IFieldmark*>(*it);
+ assert(pFieldMark);
+ std::pair<SwTextContentControl*, sw::mark::IFieldmark*> pFormControl(nullptr, pFieldMark);
+ // legacy form fields do not have (functional) tabIndexes - use lowest priority for them
+ aFormMap[std::make_pair((*it)->GetMarkStart(), SAL_MAX_UINT32)] = pFormControl;
+ }
+
+ if (aFormMap.begin() == aFormMap.end())
+ return;
+
+ // Identify the current location in the document, and the current tab index priority
+
+ // A content control could contain a Fieldmark, so check for legacy fieldmarks first
+ sw::mark::IFieldmark* pFieldMark = GetCurrentFieldmark();
+ SwTextContentControl* pTCC = !pFieldMark ? CursorInsideContentControl() : nullptr;
+
+ auto pCC = pTCC ? pTCC->GetContentControl().GetContentControl() : nullptr;
+ const sal_Int32 nCurTabIndex = pCC && pCC->GetTabIndex() ? pCC->GetTabIndex() : SAL_MAX_UINT32;
+
+ SwPosition nCurPos(*GetCursor()->GetPoint());
+ if (pFieldMark)
+ nCurPos = pFieldMark->GetMarkStart();
+ else if (pTCC && pTCC->GetTextNode())
+ nCurPos = SwPosition(*pTCC->GetTextNode(), pTCC->GetStart());
+
+ // Find the previous (or next) tab control and navigate to it
+ const std::pair<SwPosition, sal_uInt32> nOldPos(nCurPos, nCurTabIndex);
+
+ // lower_bound acts like find, and returns a pointer to nFindPos if it exists,
+ // otherwise it will point to the previous entry.
+ auto aNewPos = aFormMap.lower_bound(nOldPos);
+ if (bNext && aNewPos != aFormMap.end())
+ ++aNewPos;
+ else if (!bNext && aNewPos != aFormMap.end() && aNewPos->first == nOldPos)
+ {
+ // Found the current position - need to return previous
+ if (aNewPos == aFormMap.begin())
+ aNewPos = aFormMap.end(); // prepare to loop around
+ else
+ --aNewPos;
+ }
+
+ if (aNewPos == aFormMap.end())
+ {
+ // Loop around to the other side
+ if (bNext)
+ aNewPos = aFormMap.begin();
+ else
+ --aNewPos;
+ }
+
+ // the entry contains a pointer to either a Content Control (first) or Fieldmark (second)
+ if (aNewPos->second.first)
+ {
+ auto& rFCC = static_cast<SwFormatContentControl&>(aNewPos->second.first->GetAttr());
+ GotoFormatContentControl(rFCC);
+ }
+ else
+ {
+ assert(aNewPos->second.second);
+ GotoFieldmark(aNewPos->second.second);
+ }
+}
+
bool SwCursorShell::GotoFormatField( const SwFormatField& rField )
{
bool bRet = false;
@@ -1002,7 +1125,7 @@ bool SwCursorShell::CursorInsideInputField() const
return false;
}
-bool SwCursorShell::CursorInsideContentControl() const
+SwTextContentControl* SwCursorShell::CursorInsideContentControl() const
{
for (SwPaM& rCursor : GetCursor()->GetRingContainer())
{
@@ -1014,13 +1137,13 @@ bool SwCursorShell::CursorInsideContentControl() const
}
sal_Int32 nIndex = pStart->nContent.GetIndex();
- if (pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT))
+ if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT))
{
- return true;
+ return static_txtattr_cast<SwTextContentControl*>(pAttr);
}
}
- return false;
+ return nullptr;
}
bool SwCursorShell::PosInsideInputField( const SwPosition& rPos )