sw/qa/core/txtnode/data/page-crossref-update.odt |binary sw/qa/core/txtnode/txtnode.cxx | 32 +++++++++++++++++++++++ sw/source/core/txtnode/atrfld.cxx | 13 +++++++++ sw/source/uibase/inc/wrtsh.hxx | 5 ++- sw/source/uibase/wrtsh/select.cxx | 4 ++ 5 files changed, 53 insertions(+), 1 deletion(-)
New commits: commit a05ec67b2481025d8a5b1de79eae5381b1b3acb5 Author: Miklos Vajna <[email protected]> AuthorDate: Mon Oct 20 08:43:04 2025 +0200 Commit: Caolán McNamara <[email protected]> CommitDate: Mon Oct 20 10:08:23 2025 +0200 tdf#165472 sw: fix missing page cross-reference update on hidden sect change Open the bugdoc, change the "book" set variable field's value at the document start from 0 to 1, go to the end of the document, the page cross-reference field says "page 3", but once you click on it, you end up on page 2, which is not correct. This is a regression from commit 1dcd3f1b9fc0d888e047e854a20e9ab2e0afd544 (sw: fix not needed invalidation of custom field on each keypress, 2021-05-19), where the old use-case was that typing in a LOK client resulted in repainting the custom fields in the footer on each keystroke, because typing a key changes the doc stats (one more character), but then by the time a repaint happens, we don't know anymore what field types needs updating, so we repaint all doc stat fields. So repainting those fields on every keystroke is certainly an overkill, but at the same time an automatic update of page cross-references on section visibility change is also a reasonable request. Fix this by separating the "update on idle after any cursor change" old use-case and the "close dialog and enter text edit/standard mode" new use-case. Doing this wider update of fields is enough to do in the second case, so we get page number updates and the typing performance is not back to poor. This works because the idle case goes via the vcl scheduler, but the explicit field update goes via this codepath: #23 0x00007ffff2f088b1 in SfxBindings::Update (this=0x4c14b20, nId=21185) at sfx2/source/control/bindings.cxx:357 #24 0x00007fffba614118 in SwWrtShell::Invalidate (this=0x4c5c980) at sw/source/uibase/wrtsh/select.cxx:66 #25 0x00007fffba615fb9 in SwWrtShell::EnterStdMode (this=0x4c5c980) at sw/source/uibase/wrtsh/select.cxx:595 #26 0x00007fff917e1af3 in SwFieldEditDlg::ImplDestroy (this=0x4c43a30) at sw/source/ui/fldui/fldedt.cxx:224 #27 0x00007fff917e1b50 in SwFieldEditDlg::~SwFieldEditDlg (this=0x4c43a30, __in_chrg=<optimized out>) at sw/source/ui/fldui/fldedt.cxx:229 Change-Id: I96f48f46f44a1d6d1bde5ede282cd1cf847a6164 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192680 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Caolán McNamara <[email protected]> diff --git a/sw/qa/core/txtnode/data/page-crossref-update.odt b/sw/qa/core/txtnode/data/page-crossref-update.odt new file mode 100644 index 000000000000..004ea48f3362 Binary files /dev/null and b/sw/qa/core/txtnode/data/page-crossref-update.odt differ diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx index e537a8b94c8a..48e00c25cba1 100644 --- a/sw/qa/core/txtnode/txtnode.cxx +++ b/sw/qa/core/txtnode/txtnode.cxx @@ -46,6 +46,7 @@ #include <docufld.hxx> #include <IDocumentFieldsAccess.hxx> #include <MarkManager.hxx> +#include <fldmgr.hxx> /// Covers sw/source/core/txtnode/ fixes. class SwCoreTxtnodeTest : public SwModelTestBase @@ -642,6 +643,37 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testDOCXCommentImport) CPPUNIT_ASSERT(it != pMarkAccess->getAnnotationMarksEnd()); } +CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testPageCrossrefUpdate) +{ + // Given a document with a "book" document variable set to 0, conditional content on pages 1-2, + // bookmark on page 3, reference to that bookmark on page 4: + createSwDoc("page-crossref-update.odt"); + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + pWrtShell->SttEndDoc(/*bStt=*/true); + // Go to the doc variable field. + pWrtShell->GoNextBookmark(); + + // When performing the equivalent of what SwFieldEditDlg would do to set "book" to "1": + SwFieldMgr aMgr(pWrtShell); + aMgr.UpdateCurField(0, "book", u"1"_ustr); + SwDoc* pDoc = getSwDoc(); + pWrtShell->SetEnteringStdMode(true); + pDoc->getIDocumentStatistics().GetUpdatedDocStat(/*bCompleteAsync=*/true, /*bFields=*/false); + pWrtShell->SetEnteringStdMode(false); + Scheduler::ProcessEventsToIdle(); + + // Then make sure the reference to the bookmark on (now) page 2 expands to the correct value: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + OUString aExpand = getXPath( + pXmlDoc, "/root/page[3]/body/section/txt/SwParaPortion/SwLineLayout/SwFieldPortion", + "expand"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2 + // - Actual : 3 + // i.e. the reference to that bookmark was not updated for the new page number. + CPPUNIT_ASSERT_EQUAL(u"2"_ustr, aExpand); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/txtnode/atrfld.cxx b/sw/source/core/txtnode/atrfld.cxx index 12d72a2cb47d..dd5cbe26a5ea 100644 --- a/sw/source/core/txtnode/atrfld.cxx +++ b/sw/source/core/txtnode/atrfld.cxx @@ -42,6 +42,8 @@ #include <fieldhint.hxx> #include <sal/log.hxx> #include <osl/diagnose.h> +#include <docsh.hxx> +#include <wrtsh.hxx> // constructor for default item in attribute-pool @@ -391,6 +393,17 @@ void SwFormatField::ForceUpdateTextNode() // Force notify was added for conditional text fields, // at least the below fields need no forced notify. bool bNeedForced = lcl_NeedsForcedUpdate(*mpTextField->GetFormatField().GetField()); + if (!bNeedForced) + { + // Check if this is an idle update after any doc stat change or an explicit UI update. + SwDocShell* pDocShell = pTextNd->GetDoc().GetDocShell(); + SwWrtShell* pWrtShell = pDocShell ? pDocShell->GetWrtShell() : nullptr; + if (pWrtShell && pWrtShell->IsEnteringStdMode()) + { + // The UI invoked the update, update even doc info fields. + bNeedForced = true; + } + } mpTextField->ExpandTextField(bNeedForced); } void SwFormatField::UpdateDocPos(const SwTwips nDocPos) diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx index ba66490b3086..f49c50f57e5f 100644 --- a/sw/source/uibase/inc/wrtsh.hxx +++ b/sw/source/uibase/inc/wrtsh.hxx @@ -426,7 +426,7 @@ typedef bool (SwWrtShell::*FNSimpleMove)(); SW_DLLPUBLIC void GotoMark( const ::sw::mark::MarkBase* const pMark ); bool GotoMark( const ::sw::mark::MarkBase* const pMark, bool bSelect ); SW_DLLPUBLIC void GotoMark( const OUString& rName ); - bool GoNextBookmark(); // true when there still was one + SW_DLLPUBLIC bool GoNextBookmark(); // true when there still was one bool GoPrevBookmark(); bool GotoFieldmark(::sw::mark::Fieldmark const * const pMark); @@ -521,6 +521,8 @@ typedef bool (SwWrtShell::*FNSimpleMove)(); virtual void InfoReadOnlyDialog(bool bAsync) const override; virtual bool WarnHiddenSectionDialog() const override; virtual bool WarnSwitchToDesignModeDialog() const override; + void SetEnteringStdMode(bool bEnteringStdMode) { m_bEnteringStdMode = bEnteringStdMode; } + bool IsEnteringStdMode() const { return m_bEnteringStdMode; } std::optional<OString> getLOKPayload(int nType, int nViewId) const; @@ -608,6 +610,7 @@ private: bool m_bIsInClickToEdit:1; bool m_bClearMark :1; // don't delete selection for ChartAutoPilot bool m_bRetainSelection :1; // Do not remove selections + bool m_bEnteringStdMode = false; Point m_aStart; Link<SwWrtShell&,void> m_aSelTableLink; diff --git a/sw/source/uibase/wrtsh/select.cxx b/sw/source/uibase/wrtsh/select.cxx index 2eee5e26a172..3cd721b1d0e1 100644 --- a/sw/source/uibase/wrtsh/select.cxx +++ b/sw/source/uibase/wrtsh/select.cxx @@ -48,6 +48,7 @@ #include <bitmaps.hlst> #include <svx/svdview.hxx> +#include <comphelper/scopeguard.hxx> namespace com::sun::star::util { struct SearchOptions2; @@ -567,6 +568,9 @@ void SwWrtShell::ExtSelLn(const Point *pPt, bool ) void SwWrtShell::EnterStdMode() { + m_bEnteringStdMode = true; + comphelper::ScopeGuard g([this] { m_bEnteringStdMode = false; }); + if(m_bAddMode) LeaveAddMode(); if(m_bBlockMode)
