desktop/source/lib/init.cxx | 2 sw/inc/IDocumentMarkAccess.hxx | 7 sw/inc/crsrsh.hxx | 6 sw/inc/textcontentcontrol.hxx | 1 sw/qa/extras/uiwriter/data/tdf151548_tabNavigation.docm |binary sw/qa/extras/uiwriter/uiwriter4.cxx | 42 +++++ sw/source/core/crsr/bookmark.cxx | 4 sw/source/core/crsr/crbm.cxx | 8 - sw/source/core/crsr/crstrvl.cxx | 126 ++++++++++++++++ sw/source/core/doc/docbm.cxx | 29 ++- sw/source/core/docnode/nodes.cxx | 21 ++ sw/source/core/inc/MarkManager.hxx | 5 sw/source/core/txtnode/atrref.cxx | 3 sw/source/core/txtnode/attrcontentcontrol.cxx | 6 sw/source/ui/vba/vbaformfield.cxx | 10 - sw/source/uibase/docvw/edtwin.cxx | 37 +++- sw/source/uibase/wrtsh/wrtsh1.cxx | 4 17 files changed, 275 insertions(+), 36 deletions(-)
New commits: commit 0799de843c8fa27569f88be1cf60ad6e7a9577cf Author: Aron Budea <aron.bu...@collabora.com> AuthorDate: Sat Dec 17 13:16:21 2022 +0100 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Thu Feb 9 10:27:20 2023 +0100 LanguageTool: don't crash if REST protocol isn't set Change-Id: I91ea4cc8823aae617bb00fdaeca8d4c10b1f5fc0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144376 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Andras Timar <andras.ti...@collabora.com> diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 9e36d464b75f..aab552b1c192 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -7188,7 +7188,7 @@ void setLanguageToolConfig() if (aEnabled != "true") return; OUString aBaseUrl = OStringToOUString(pBaseUrlString, RTL_TEXTENCODING_UTF8); - OUString aRestProtocol = OStringToOUString(pRestProtocol, RTL_TEXTENCODING_UTF8); + OUString aRestProtocol = pRestProtocol ? OStringToOUString(pRestProtocol, RTL_TEXTENCODING_UTF8) : ""; try { SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get(); commit 0a57e6c25d6bcf6e93ab2da03a0bf560ac7fb560 Author: Justin Luth <justin.l...@collabora.com> AuthorDate: Thu Jan 26 14:50:19 2023 -0500 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Thu Feb 9 10:26:33 2023 +0100 tdf#151548 sw content controls: keyboard navigation with tab key Combine content controls with legacy formfield controls in keyboard tab navigation. MS Word (I tested 2010) is extremely irrational and inconsistent in its behaviour, so I modeled my implementation on the specification and general logic, and not at all on "compatible misbehaviour". There is a third category of form control (activeX rich content), but these are mapped to internal LO controls that are only exposed at VCL level, and don't pass the keystrokes back to SW. Plus, they are not inline, but fly controls. However, it is still a TODO to handle these if reasonably possible. Change-Id: I1fef34d05a779e9d4f549987238435acb6c043d2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146219 Tested-by: Jenkins Reviewed-by: Justin Luth <jl...@mail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/inc/IDocumentMarkAccess.hxx b/sw/inc/IDocumentMarkAccess.hxx index 3dc7baf696ab..42e7ba71b7d1 100644 --- a/sw/inc/IDocumentMarkAccess.hxx +++ b/sw/inc/IDocumentMarkAccess.hxx @@ -325,6 +325,9 @@ class IDocumentMarkAccess */ virtual const_iterator_t getFieldmarksEnd() const =0; + /// returns the number of IFieldmarks. + virtual sal_Int32 getFieldmarksCount() const = 0; + /// get Fieldmark for CH_TXT_ATR_FIELDSTART/CH_TXT_ATR_FIELDEND at rPos virtual ::sw::mark::IFieldmark* getFieldmarkAt(const SwPosition& rPos) const =0; virtual ::sw::mark::IFieldmark* getFieldmarkFor(const SwPosition& pos) const =0; diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 31cad2cd6e71..e4d00fd4a6bb 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -717,6 +717,8 @@ public: bool GotoFormatContentControl(const SwFormatContentControl& rContentControl); + void GotoFormControl(bool bNext); + static SwTextField* GetTextFieldAtPos( const SwPosition* pPos, ::sw::GetTextAttrMode eMode); diff --git a/sw/inc/textcontentcontrol.hxx b/sw/inc/textcontentcontrol.hxx index 3fb7ea124b99..b3926bd25ce9 100644 --- a/sw/inc/textcontentcontrol.hxx +++ b/sw/inc/textcontentcontrol.hxx @@ -64,6 +64,7 @@ public: size_t GetCount() const { return m_aContentControls.size(); } bool IsEmpty() const { return m_aContentControls.empty(); } SwTextContentControl* Get(size_t nIndex); + SwTextContentControl* UnsortedGet(size_t nIndex); void dumpAsXml(xmlTextWriterPtr pWriter) const; }; diff --git a/sw/qa/extras/uiwriter/data/tdf151548_tabNavigation.docm b/sw/qa/extras/uiwriter/data/tdf151548_tabNavigation.docm new file mode 100644 index 000000000000..1b173e2041c2 Binary files /dev/null and b/sw/qa/extras/uiwriter/data/tdf151548_tabNavigation.docm differ diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx b/sw/qa/extras/uiwriter/uiwriter4.cxx index b7f05b3960e6..c8e99868a790 100644 --- a/sw/qa/extras/uiwriter/uiwriter4.cxx +++ b/sw/qa/extras/uiwriter/uiwriter4.cxx @@ -1491,6 +1491,46 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf95699) pFieldMark->GetFieldname()); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf151548_tabNavigation) +{ + // given a form-protected doc with 4 unchecked legacy fieldmark checkboxes (and several modern + // content controls which all have a tabstop of -1 to disable tabstop navigation to them) + // we want to test that tab navigation completes and loops around to continue at the beginning. + createSwDoc("tdf151548_tabNavigation.docm"); + SwDoc* pDoc = getSwDoc(); + SwXTextDocument* pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + + IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(4), pMarkAccess->getFieldmarksCount()); + + // Tab and toggle 4 times, verifying beforehand that the state was unchecked + for (auto it = pMarkAccess->getFieldmarksBegin(); it != pMarkAccess->getFieldmarksEnd(); ++it) + { + sw::mark::ICheckboxFieldmark* pCheckBox + = dynamic_cast<::sw::mark::ICheckboxFieldmark*>(*it); + CPPUNIT_ASSERT(!pCheckBox->IsChecked()); + + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 32, KEY_SPACE); // toggle checkbox on + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB); // move to next control + Scheduler::ProcessEventsToIdle(); + } + + // Tab 4 more times, verifying beforehand that the checkbox had been toggle on, then toggles off + // meaning that looping is working, and no other controls are reacting to the tab key. + for (auto it = pMarkAccess->getFieldmarksBegin(); it != pMarkAccess->getFieldmarksEnd(); ++it) + { + sw::mark::ICheckboxFieldmark* pCheckBox + = dynamic_cast<::sw::mark::ICheckboxFieldmark*>(*it); + + CPPUNIT_ASSERT(pCheckBox->IsChecked()); + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 32, KEY_SPACE); // toggle checkbox off + Scheduler::ProcessEventsToIdle(); + + CPPUNIT_ASSERT(!pCheckBox->IsChecked()); + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB); // move to next control + } +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf104032) { // Open the document with FORMCHECKBOX field, select it and copy to clipboard diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index db0ec5b1ae1f..9cf9f77a4d76 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -864,6 +864,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 ) { SwTextField const*const pTextField(rField.GetTextField()); diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx index 1bb156f07959..3a0383324d9f 100644 --- a/sw/source/core/doc/docbm.cxx +++ b/sw/source/core/doc/docbm.cxx @@ -1419,6 +1419,8 @@ namespace sw::mark IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksEnd() const { return m_vFieldmarks.end(); } + sal_Int32 MarkManager::getFieldmarksCount() const { return m_vFieldmarks.size(); } + // finds the first that is starting after IDocumentMarkAccess::const_iterator_t MarkManager::findFirstBookmarkStartsAfter(const SwPosition& rPos) const diff --git a/sw/source/core/inc/MarkManager.hxx b/sw/source/core/inc/MarkManager.hxx index b741ff7c7526..a3cf79d1281d 100644 --- a/sw/source/core/inc/MarkManager.hxx +++ b/sw/source/core/inc/MarkManager.hxx @@ -93,6 +93,7 @@ namespace sw::mark { // Fieldmarks virtual const_iterator_t getFieldmarksBegin() const override; virtual const_iterator_t getFieldmarksEnd() const override; + virtual sal_Int32 getFieldmarksCount() const override; virtual ::sw::mark::IFieldmark* getFieldmarkAt(const SwPosition& rPos) const override; virtual ::sw::mark::IFieldmark* getFieldmarkFor(const SwPosition& rPos) const override; virtual sw::mark::IFieldmark* getFieldmarkBefore(const SwPosition& rPos, bool bLoop) const override; diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index 233b6acf75d7..c77926aa141e 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -809,6 +809,12 @@ SwTextContentControl* SwContentControlManager::Get(size_t nIndex) return m_aContentControls[nIndex]; } +SwTextContentControl* SwContentControlManager::UnsortedGet(size_t nIndex) +{ + assert(nIndex < m_aContentControls.size()); + return m_aContentControls[nIndex]; +} + void SwContentControlManager::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlManager")); diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index 4473d6119bc5..4b9b81915805 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -2097,8 +2097,11 @@ KEYINPUT_CHECKTABLE_INSDEL: } case KEY_TAB: { - - if (rSh.IsFormProtected() || rSh.GetCurrentFieldmark() || rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + // Rich text contentControls accept tabs and fieldmarks and other rich text, + // so first act on cases that are not a content control + SwTextContentControl* pTextContentControl = rSh.CursorInsideContentControl(); + if ((rSh.IsFormProtected() && !pTextContentControl) || + rSh.GetCurrentFieldmark() || rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) { eKeyState = SwKeyState::GotoNextFieldMark; } @@ -2139,6 +2142,21 @@ KEYINPUT_CHECKTABLE_INSDEL: eNextKeyState = SwKeyState::NextCell; } } + else if (pTextContentControl) + { + auto pCC = pTextContentControl->GetContentControl().GetContentControl(); + if (pCC) + { + switch (pCC->GetType()) + { + case SwContentControlType::RICH_TEXT: + eKeyState = SwKeyState::InsTab; + break; + default: + eKeyState = SwKeyState::GotoNextFieldMark; + } + } + } else { eKeyState = SwKeyState::InsTab; @@ -2156,7 +2174,9 @@ KEYINPUT_CHECKTABLE_INSDEL: break; case KEY_TAB | KEY_SHIFT: { - if (rSh.IsFormProtected() || rSh.GetCurrentFieldmark()|| rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) + SwTextContentControl* pTextContentControl = rSh.CursorInsideContentControl(); + if ((rSh.IsFormProtected() && !pTextContentControl) || + rSh.GetCurrentFieldmark()|| rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT) { eKeyState = SwKeyState::GotoPrevFieldMark; } @@ -2190,6 +2210,10 @@ KEYINPUT_CHECKTABLE_INSDEL: eNextKeyState = SwKeyState::PrevCell; } } + else if (pTextContentControl) + { + eKeyState = SwKeyState::GotoPrevFieldMark; + } else { eKeyState = SwKeyState::End; @@ -2630,18 +2654,13 @@ KEYINPUT_CHECKTABLE_INSDEL: case SwKeyState::GotoNextFieldMark: { - const sw::mark::IFieldmark* pFieldmark - = rSh.GetFieldmarkAfter(/*bLoop=*/true); - if(pFieldmark) rSh.GotoFieldmark(pFieldmark); + rSh.GotoFormControl(/*bNext=*/true); } break; case SwKeyState::GotoPrevFieldMark: { - const sw::mark::IFieldmark* pFieldmark - = rSh.GetFieldmarkBefore(/*bLoop=*/true); - if( pFieldmark ) - rSh.GotoFieldmark(pFieldmark); + rSh.GotoFormControl(/*bNext=*/false); } break; commit dbfc5ad19ed71a0cc95d6e6455377102fa02521c Author: Justin Luth <justin.l...@collabora.com> AuthorDate: Mon Jan 23 11:18:44 2023 -0500 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Thu Feb 9 10:26:10 2023 +0100 related tdf#151548 formfield navigation: loop to beginning/end When reaching the end of the form using keyboard navigation, the next tabstop should return the user to the beginning of the form. This patch adds that to the existing legacy formfield navigation. I'll wait with a unit test until I have added activeX/contentControls into the mix. Change-Id: I24a15a60f5a0a2721f512cca50397efddcbf7e4b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146035 Tested-by: Jenkins Reviewed-by: Justin Luth <jl...@mail.com> diff --git a/sw/inc/IDocumentMarkAccess.hxx b/sw/inc/IDocumentMarkAccess.hxx index 20f876d16495..3dc7baf696ab 100644 --- a/sw/inc/IDocumentMarkAccess.hxx +++ b/sw/inc/IDocumentMarkAccess.hxx @@ -328,8 +328,8 @@ class IDocumentMarkAccess /// get Fieldmark for CH_TXT_ATR_FIELDSTART/CH_TXT_ATR_FIELDEND at rPos virtual ::sw::mark::IFieldmark* getFieldmarkAt(const SwPosition& rPos) const =0; virtual ::sw::mark::IFieldmark* getFieldmarkFor(const SwPosition& pos) const =0; - virtual ::sw::mark::IFieldmark* getFieldmarkBefore(const SwPosition& pos) const =0; - virtual ::sw::mark::IFieldmark* getFieldmarkAfter(const SwPosition& pos) const =0; + virtual sw::mark::IFieldmark* getFieldmarkBefore(const SwPosition& pos, bool bLoop) const =0; + virtual sw::mark::IFieldmark* getFieldmarkAfter(const SwPosition& pos, bool bLoop) const =0; virtual ::sw::mark::IFieldmark* getDropDownFor(const SwPosition& pos) const=0; virtual std::vector<::sw::mark::IFieldmark*> getNoTextFieldmarksIn(const SwPaM &rPaM) const=0; diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 2e9a9f0f1e62..31cad2cd6e71 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -579,8 +579,8 @@ public: bool IsFormProtected(); ::sw::mark::IFieldmark* GetCurrentFieldmark(); - ::sw::mark::IFieldmark* GetFieldmarkAfter(); - ::sw::mark::IFieldmark* GetFieldmarkBefore(); + sw::mark::IFieldmark* GetFieldmarkAfter(bool bLoop); + sw::mark::IFieldmark* GetFieldmarkBefore(bool bLoop); bool GotoFieldmark( const ::sw::mark::IFieldmark* const pMark ); // update Cursr, i.e. reset it into content should only be called when the diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx b/sw/qa/extras/uiwriter/uiwriter4.cxx index 77b42667ee87..b7f05b3960e6 100644 --- a/sw/qa/extras/uiwriter/uiwriter4.cxx +++ b/sw/qa/extras/uiwriter/uiwriter4.cxx @@ -1486,7 +1486,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest4, testTdf95699) pMarkAccess = aClipboard.getIDocumentMarkAccess(); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pMarkAccess->getAllMarksCount()); ::sw::mark::IFieldmark* pFieldMark - = pMarkAccess->getFieldmarkAfter(SwPosition(pDoc->GetNodes().GetEndOfExtras())); + = pMarkAccess->getFieldmarkAfter(SwPosition(pDoc->GetNodes().GetEndOfExtras()), false); CPPUNIT_ASSERT_EQUAL(OUString("vnd.oasis.opendocument.field.FORMCHECKBOX"), pFieldMark->GetFieldname()); } diff --git a/sw/source/core/crsr/crbm.cxx b/sw/source/core/crsr/crbm.cxx index 4e0b808c7fba..53d2538538d8 100644 --- a/sw/source/core/crsr/crbm.cxx +++ b/sw/source/core/crsr/crbm.cxx @@ -290,16 +290,16 @@ bool SwCursorShell::IsFormProtected() return getIDocumentMarkAccess()->getFieldmarkFor(pos); } -::sw::mark::IFieldmark* SwCursorShell::GetFieldmarkAfter() +sw::mark::IFieldmark* SwCursorShell::GetFieldmarkAfter(bool bLoop) { SwPosition pos(*GetCursor()->GetPoint()); - return getIDocumentMarkAccess()->getFieldmarkAfter(pos); + return getIDocumentMarkAccess()->getFieldmarkAfter(pos, bLoop); } -::sw::mark::IFieldmark* SwCursorShell::GetFieldmarkBefore() +sw::mark::IFieldmark* SwCursorShell::GetFieldmarkBefore(bool bLoop) { SwPosition pos(*GetCursor()->GetPoint()); - return getIDocumentMarkAccess()->getFieldmarkBefore(pos); + return getIDocumentMarkAccess()->getFieldmarkBefore(pos, bLoop); } bool SwCursorShell::GotoFieldmark(::sw::mark::IFieldmark const * const pMark) diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx index ac837965cd5c..1bb156f07959 100644 --- a/sw/source/core/doc/docbm.cxx +++ b/sw/source/core/doc/docbm.cxx @@ -312,7 +312,8 @@ namespace }; - IMark* lcl_getMarkAfter(const MarkManager::container_t& rMarks, const SwPosition& rPos) + IMark* lcl_getMarkAfter(const MarkManager::container_t& rMarks, const SwPosition& rPos, + bool bLoop) { auto const pMarkAfter = upper_bound( rMarks.begin(), @@ -320,11 +321,17 @@ namespace rPos, CompareIMarkStartsAfter()); if(pMarkAfter == rMarks.end()) + { + if (bLoop && rMarks.begin() != rMarks.end()) + return *rMarks.begin(); + return nullptr; + } return *pMarkAfter; }; - IMark* lcl_getMarkBefore(const MarkManager::container_t& rMarks, const SwPosition& rPos) + IMark* lcl_getMarkBefore(const MarkManager::container_t& rMarks, const SwPosition& rPos, + bool bLoop) { // candidates from which to choose the mark before MarkManager::container_t vCandidates; @@ -342,7 +349,13 @@ namespace back_inserter(vCandidates), [&rPos] (const ::sw::mark::MarkBase *const pMark) { return !(pMark->GetMarkEnd() < rPos); } ); // no candidate left => we are in front of the first mark or there are none - if(vCandidates.empty()) return nullptr; + if(vCandidates.empty()) + { + if (bLoop && rMarks.begin() != rMarks.end()) + return *(rMarks.end() - 1); + + return nullptr; + } // return the highest (last) candidate using mark end ordering return *max_element(vCandidates.begin(), vCandidates.end(), &lcl_MarkOrderingByEnd); } @@ -1654,11 +1667,11 @@ namespace sw::mark return aRet; } - IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos) const - { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos)); } + IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos, bool bLoop) const + { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos, bLoop)); } - IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos) const - { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos)); } + IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos, bool bLoop) const + { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos, bLoop)); } IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksBegin() const { diff --git a/sw/source/core/inc/MarkManager.hxx b/sw/source/core/inc/MarkManager.hxx index 1599996ae055..b741ff7c7526 100644 --- a/sw/source/core/inc/MarkManager.hxx +++ b/sw/source/core/inc/MarkManager.hxx @@ -95,8 +95,8 @@ namespace sw::mark { virtual const_iterator_t getFieldmarksEnd() const override; virtual ::sw::mark::IFieldmark* getFieldmarkAt(const SwPosition& rPos) const override; virtual ::sw::mark::IFieldmark* getFieldmarkFor(const SwPosition& rPos) const override; - virtual ::sw::mark::IFieldmark* getFieldmarkBefore(const SwPosition& rPos) const override; - virtual ::sw::mark::IFieldmark* getFieldmarkAfter(const SwPosition& rPos) const override; + virtual sw::mark::IFieldmark* getFieldmarkBefore(const SwPosition& rPos, bool bLoop) const override; + virtual sw::mark::IFieldmark* getFieldmarkAfter(const SwPosition& rPos, bool bLoop) const override; virtual ::sw::mark::IFieldmark* getDropDownFor(const SwPosition &rPos) const override; virtual std::vector<::sw::mark::IFieldmark*> getNoTextFieldmarksIn(const SwPaM &rPaM) const override; diff --git a/sw/source/ui/vba/vbaformfield.cxx b/sw/source/ui/vba/vbaformfield.cxx index 8afaeb9d9480..74424825ef59 100644 --- a/sw/source/ui/vba/vbaformfield.cxx +++ b/sw/source/ui/vba/vbaformfield.cxx @@ -72,13 +72,14 @@ uno::Any SAL_CALL SwVbaFormField::Previous() if (!pMarkAccess) return uno::Any(); - sw::mark::IFieldmark* pFieldMark = pMarkAccess->getFieldmarkBefore(m_rFormField.GetMarkPos()); + sw::mark::IFieldmark* pFieldMark = pMarkAccess->getFieldmarkBefore(m_rFormField.GetMarkPos(), + /*bLoop=*/false); // DateFields are a LO specialty, and do not exist natively in MS documents. Ignore if added... auto pDateField = dynamic_cast<sw::mark::IDateFieldmark*>(pFieldMark); while (pDateField) { - pFieldMark = pMarkAccess->getFieldmarkBefore(pDateField->GetMarkPos()); + pFieldMark = pMarkAccess->getFieldmarkBefore(pDateField->GetMarkPos(), /*bLoop=*/false); pDateField = dynamic_cast<sw::mark::IDateFieldmark*>(pFieldMark); } @@ -99,13 +100,14 @@ uno::Any SAL_CALL SwVbaFormField::Next() if (!pMarkAccess) return uno::Any(); - sw::mark::IFieldmark* pFieldMark = pMarkAccess->getFieldmarkAfter(m_rFormField.GetMarkPos()); + sw::mark::IFieldmark* pFieldMark = pMarkAccess->getFieldmarkAfter(m_rFormField.GetMarkPos(), + /*bLoop=*/false); // DateFields are a LO specialty, and do not exist natively in MS documents. Ignore if added... auto pDateField = dynamic_cast<sw::mark::IDateFieldmark*>(pFieldMark); while (pDateField) { - pFieldMark = pMarkAccess->getFieldmarkAfter(pDateField->GetMarkPos()); + pFieldMark = pMarkAccess->getFieldmarkAfter(pDateField->GetMarkPos(), /*bLoop=*/false); pDateField = dynamic_cast<sw::mark::IDateFieldmark*>(pFieldMark); } diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index a52f6a15dd1e..4473d6119bc5 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -2630,14 +2630,16 @@ KEYINPUT_CHECKTABLE_INSDEL: case SwKeyState::GotoNextFieldMark: { - ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkAfter(); + const sw::mark::IFieldmark* pFieldmark + = rSh.GetFieldmarkAfter(/*bLoop=*/true); if(pFieldmark) rSh.GotoFieldmark(pFieldmark); } break; case SwKeyState::GotoPrevFieldMark: { - ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkBefore(); + const sw::mark::IFieldmark* pFieldmark + = rSh.GetFieldmarkBefore(/*bLoop=*/true); if( pFieldmark ) rSh.GotoFieldmark(pFieldmark); } diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx index b989343266cb..ab5882a1147b 100644 --- a/sw/source/uibase/wrtsh/wrtsh1.cxx +++ b/sw/source/uibase/wrtsh/wrtsh1.cxx @@ -2002,7 +2002,7 @@ SwWrtShell::SwWrtShell( SwWrtShell& rSh, vcl::Window *_pWin, SwView &rShell ) // place the cursor on the first field... IFieldmark *pBM = nullptr; - if ( IsFormProtected() && ( pBM = GetFieldmarkAfter( ) ) !=nullptr ) { + if (IsFormProtected() && (pBM = GetFieldmarkAfter(/*bLoop=*/false)) !=nullptr) { GotoFieldmark(pBM); } } @@ -2020,7 +2020,7 @@ SwWrtShell::SwWrtShell( SwDoc& rDoc, vcl::Window *_pWin, SwView &rShell, // place the cursor on the first field... IFieldmark *pBM = nullptr; - if ( IsFormProtected() && ( pBM = GetFieldmarkAfter( ) ) !=nullptr ) { + if (IsFormProtected() && (pBM = GetFieldmarkAfter(/*bLoop=*/false)) !=nullptr) { GotoFieldmark(pBM); } } commit 50133e6149b0a0b0eee6438e724d570d5994860c Author: Pranam Lashkari <lpra...@collabora.com> AuthorDate: Mon Jan 23 22:34:29 2023 +0530 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Thu Feb 9 10:25:52 2023 +0100 sw: lok: avoid creating callback json if kit is not active Signed-off-by: Pranam Lashkari <lpra...@collabora.com> Change-Id: I60ae5a37acdbe4ea01b730702cc499833ce5b77c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146036 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146102 Tested-by: Jenkins diff --git a/sw/source/core/crsr/bookmark.cxx b/sw/source/core/crsr/bookmark.cxx index c43dd04c2496..faaec4b28377 100644 --- a/sw/source/core/crsr/bookmark.cxx +++ b/sw/source/core/crsr/bookmark.cxx @@ -422,7 +422,7 @@ namespace sw::mark void Bookmark::sendLOKDeleteCallback() { - if (GetMarkPos().GetDoc().IsClipBoard()) + if (!comphelper::LibreOfficeKit::isActive() || GetMarkPos().GetDoc().IsClipBoard()) return; SfxViewShell* pViewShell = SfxViewShell::Current(); @@ -586,7 +586,7 @@ namespace sw::mark TextFieldmark::~TextFieldmark() { - if (GetMarkPos().GetDoc().IsClipBoard()) + if (!comphelper::LibreOfficeKit::isActive() || GetMarkPos().GetDoc().IsClipBoard()) return; SfxViewShell* pViewShell = SfxViewShell::Current(); diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index 7fcba8858721..16fb8eb5ecd1 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -23,6 +23,7 @@ #include <tools/json_writer.hxx> #include <LibreOfficeKit/LibreOfficeKitEnums.h> #include <sfx2/viewsh.hxx> +#include <comphelper/lok.hxx> #include <node.hxx> #include <doc.hxx> @@ -2444,7 +2445,7 @@ void SwNodes::RemoveNode( SwNodeOffset nDelPos, SwNodeOffset nSz, bool bDel ) } SwSectionNode* pSectionNode = pNode->GetSectionNode(); - if (pSectionNode && !GetDoc().IsClipBoard() && SfxViewShell::Current()) + if (comphelper::LibreOfficeKit::isActive() && pSectionNode && !GetDoc().IsClipBoard() && SfxViewShell::Current()) { OUString fieldCommand = pSectionNode->GetSection().GetSectionName(); tools::JsonWriter aJson; diff --git a/sw/source/core/txtnode/atrref.cxx b/sw/source/core/txtnode/atrref.cxx index 0c2bb85bd1ae..8ba4f624727a 100644 --- a/sw/source/core/txtnode/atrref.cxx +++ b/sw/source/core/txtnode/atrref.cxx @@ -27,6 +27,7 @@ #include <sfx2/viewsh.hxx> #include <tools/json_writer.hxx> #include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <comphelper/lok.hxx> #include <doc.hxx> #include <ndtxt.hxx> @@ -106,7 +107,7 @@ SwTextRefMark::SwTextRefMark( SwFormatRefMark& rAttr, SwTextRefMark::~SwTextRefMark() { - if (GetTextNode().GetDoc().IsClipBoard()) + if (!comphelper::LibreOfficeKit::isActive() || GetTextNode().GetDoc().IsClipBoard()) return; SfxViewShell* pViewShell = SfxViewShell::Current(); commit f669d47c1e9e6d29b1ee640ab8953b968006dba8 Author: Pranam Lashkari <lpra...@collabora.com> AuthorDate: Mon Jan 23 19:55:58 2023 +0530 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Thu Feb 9 10:25:38 2023 +0100 sw: send LOK call back on section deletion this helps with zotero when user deletes or undo a bibliography stored as a section without this online will be unaware of any such changes made by user Signed-off-by: Pranam Lashkari <lpra...@collabora.com> Change-Id: I6a8ae3924d548e97299a8d74f8c93c3345d1a430 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146007 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146101 Tested-by: Jenkins diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx index afef83e76df5..7fcba8858721 100644 --- a/sw/source/core/docnode/nodes.cxx +++ b/sw/source/core/docnode/nodes.cxx @@ -20,6 +20,10 @@ #include <stdlib.h> #include <libxml/xmlwriter.h> #include <osl/diagnose.h> +#include <tools/json_writer.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <sfx2/viewsh.hxx> + #include <node.hxx> #include <doc.hxx> #include <IDocumentUndoRedo.hxx> @@ -2438,6 +2442,22 @@ void SwNodes::RemoveNode( SwNodeOffset nDelPos, SwNodeOffset nSz, bool bDel ) // 'Extra Redlines' array pTableNode->RemoveRedlines(); } + + SwSectionNode* pSectionNode = pNode->GetSectionNode(); + if (pSectionNode && !GetDoc().IsClipBoard() && SfxViewShell::Current()) + { + OUString fieldCommand = pSectionNode->GetSection().GetSectionName(); + tools::JsonWriter aJson; + aJson.put("commandName", ".uno:DeleteSection"); + aJson.put("success", true); + { + auto result = aJson.startNode("result"); + aJson.put("DeleteSection", fieldCommand); + } + + SfxViewShell::Current()->libreOfficeKitViewCallback(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.extractData()); + + } } SwNodeOffset nEnd = nDelPos + nSz;