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;

Reply via email to