sw/inc/crsrsh.hxx | 2 sw/inc/swabstdlg.hxx | 1 sw/source/core/crsr/crstrvl.cxx | 11 ++- sw/source/ui/dialog/swdlgfact.cxx | 5 + sw/source/ui/dialog/swdlgfact.hxx | 1 sw/source/ui/misc/pagenumberdlg.cxx | 14 ++++ sw/source/uibase/inc/pagenumberdlg.hxx | 2 sw/source/uibase/shells/textfld.cxx | 103 +++++++++++++++++++++++++++++++- sw/uiconfig/swriter/ui/pagenumberdlg.ui | 24 ++++++- 9 files changed, 153 insertions(+), 10 deletions(-)
New commits: commit 21fad629e72791f6cc1791fdba858bf52b9bfeff Author: Justin Luth <justin.l...@collabora.com> AuthorDate: Wed Apr 26 13:29:25 2023 -0400 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Apr 28 08:08:06 2023 +0200 tdf#86630 sw page number wizard: mirror right/left If the user puts the page numbers on the left or right side of the page, they usually want that mirrored on even and odd pages. This got rather tricky, but in the end I have enough safeguards that it seems to work logically and stablely. So I think it is ready to be submitted. Change-Id: I321e575cd9f6718579ffee99f1258bffe26581f2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151152 Reviewed-by: Justin Luth <jl...@mail.com> Tested-by: Jenkins diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 319911a33e65..5f99deff126b 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -675,7 +675,7 @@ public: bool GotoHeaderText(); ///< jump from the content to the header bool GotoFooterText(); ///< jump from the content to the footer // jump to the header/footer of the given or current PageDesc - bool SetCursorInHdFt( size_t nDescNo, bool bInHeader ); + bool SetCursorInHdFt(size_t nDescNo, bool bInHeader, bool bEven = false, bool bFirst = false); // is point of cursor in header/footer. pbInHeader return true if it is // in a headerframe otherwise in a footerframe bool IsInHeaderFooter( bool* pbInHeader = nullptr ) const; diff --git a/sw/inc/swabstdlg.hxx b/sw/inc/swabstdlg.hxx index 9749539c5643..49eefef5e4a3 100644 --- a/sw/inc/swabstdlg.hxx +++ b/sw/inc/swabstdlg.hxx @@ -238,6 +238,7 @@ protected: public: virtual int GetPageNumberPosition() const = 0; virtual int GetPageNumberAlignment() const = 0; + virtual bool GetMirrorOnEvenPages() const = 0; virtual SvxNumType GetPageNumberType() const = 0; virtual void SetPageNumberType(SvxNumType nSet) = 0; }; diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index c5c0c741f5dd..c8b1e3d8513a 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -189,7 +189,7 @@ bool SwCursorShell::GotoFooterText() return nullptr != pFrame; } -bool SwCursorShell::SetCursorInHdFt( size_t nDescNo, bool bInHeader ) +bool SwCursorShell::SetCursorInHdFt(size_t nDescNo, bool bInHeader, bool bEven, bool bFirst) { SwDoc *pMyDoc = GetDoc(); const SwPageDesc* pDesc = nullptr; @@ -216,14 +216,17 @@ bool SwCursorShell::SetCursorInHdFt( size_t nDescNo, bool bInHeader ) const SwFormatContent* pCnt = nullptr; if( bInHeader ) { - // mirrored pages? ignore for now - const SwFormatHeader& rHd = pDesc->GetMaster().GetHeader(); + const SwFormatHeader& rHd + = bEven ? bFirst ? pDesc->GetFirstLeft().GetHeader() : pDesc->GetLeft().GetHeader() + : bFirst ? pDesc->GetFirstMaster().GetHeader() : pDesc->GetMaster().GetHeader(); if( rHd.GetHeaderFormat() ) pCnt = &rHd.GetHeaderFormat()->GetContent(); } else { - const SwFormatFooter& rFt = pDesc->GetMaster().GetFooter(); + const SwFormatFooter& rFt + = bEven ? bFirst ? pDesc->GetFirstLeft().GetFooter() : pDesc->GetLeft().GetFooter() + : bFirst ? pDesc->GetFirstMaster().GetFooter() : pDesc->GetMaster().GetFooter(); if( rFt.GetFooterFormat() ) pCnt = &rFt.GetFooterFormat()->GetContent(); } diff --git a/sw/source/ui/dialog/swdlgfact.cxx b/sw/source/ui/dialog/swdlgfact.cxx index 672b953ff8e2..6ec8e23a6304 100644 --- a/sw/source/ui/dialog/swdlgfact.cxx +++ b/sw/source/ui/dialog/swdlgfact.cxx @@ -642,6 +642,11 @@ int AbstractSwPageNumberDlg_Impl::GetPageNumberAlignment() const return m_xDlg->GetPageNumberAlignment(); } +bool AbstractSwPageNumberDlg_Impl::GetMirrorOnEvenPages() const +{ + return m_xDlg->GetMirrorOnEvenPages(); +} + SvxNumType AbstractSwPageNumberDlg_Impl::GetPageNumberType() const { return m_xDlg->GetPageNumberType(); diff --git a/sw/source/ui/dialog/swdlgfact.hxx b/sw/source/ui/dialog/swdlgfact.hxx index 788aaa9c6f80..6d3f37aa6a47 100644 --- a/sw/source/ui/dialog/swdlgfact.hxx +++ b/sw/source/ui/dialog/swdlgfact.hxx @@ -166,6 +166,7 @@ public: virtual bool StartExecuteAsync(AsyncContext &rCtx) override; virtual int GetPageNumberPosition() const override; virtual int GetPageNumberAlignment() const override; + bool GetMirrorOnEvenPages() const override; SvxNumType GetPageNumberType() const override; void SetPageNumberType(SvxNumType nSet) override; }; diff --git a/sw/source/ui/misc/pagenumberdlg.cxx b/sw/source/ui/misc/pagenumberdlg.cxx index a4a329f62ca6..06facf1bff22 100644 --- a/sw/source/ui/misc/pagenumberdlg.cxx +++ b/sw/source/ui/misc/pagenumberdlg.cxx @@ -30,6 +30,7 @@ SwPageNumberDlg::SwPageNumberDlg(weld::Window* pParent) , m_xCancel(m_xBuilder->weld_button("cancel")) , m_xPageNumberPosition(m_xBuilder->weld_combo_box("positionCombo")) , m_xPageNumberAlignment(m_xBuilder->weld_combo_box("alignmentCombo")) + , m_xMirrorOnEvenPages(m_xBuilder->weld_check_button("mirrorCheckbox")) , m_xPageNumberTypeLB(new SvxPageNumberListBox(m_xBuilder->weld_combo_box("numfmtlb"))) , m_xPreviewImage(m_xBuilder->weld_image("previewImage")) , m_aPageNumberPosition(1) // bottom @@ -41,6 +42,8 @@ SwPageNumberDlg::SwPageNumberDlg(weld::Window* pParent) m_xPageNumberAlignment->connect_changed(LINK(this, SwPageNumberDlg, AlignmentSelectHdl)); m_xPageNumberPosition->set_active(m_aPageNumberPosition); m_xPageNumberAlignment->set_active(m_aPageNumberAlignment); + m_xMirrorOnEvenPages->set_sensitive(false); + m_xMirrorOnEvenPages->set_state(TRISTATE_TRUE); SvxNumOptionsTabPageHelper::GetI18nNumbering(m_xPageNumberTypeLB->get_widget(), ::std::numeric_limits<sal_uInt16>::max()); m_xPageNumberTypeLB->connect_changed(LINK(this, SwPageNumberDlg, NumberTypeSelectHdl)); @@ -59,6 +62,11 @@ IMPL_LINK_NOARG(SwPageNumberDlg, AlignmentSelectHdl, weld::ComboBox&, void) { m_aPageNumberAlignment = m_xPageNumberAlignment->get_active(); updateImage(); + + if (m_aPageNumberAlignment == 1) // centered + m_xMirrorOnEvenPages->set_sensitive(false); + else + m_xMirrorOnEvenPages->set_sensitive(true); } IMPL_LINK_NOARG(SwPageNumberDlg, NumberTypeSelectHdl, weld::ComboBox&, void) @@ -66,6 +74,12 @@ IMPL_LINK_NOARG(SwPageNumberDlg, NumberTypeSelectHdl, weld::ComboBox&, void) m_nPageNumberType = m_xPageNumberTypeLB->get_active_id(); } +bool SwPageNumberDlg::GetMirrorOnEvenPages() +{ + return m_xMirrorOnEvenPages->get_sensitive() + && m_xMirrorOnEvenPages->get_state() == TRISTATE_TRUE; +} + void SwPageNumberDlg::SetPageNumberType(SvxNumType nSet) { m_nPageNumberType = nSet; diff --git a/sw/source/uibase/inc/pagenumberdlg.hxx b/sw/source/uibase/inc/pagenumberdlg.hxx index 5e093b5a924f..b5dccd93127a 100644 --- a/sw/source/uibase/inc/pagenumberdlg.hxx +++ b/sw/source/uibase/inc/pagenumberdlg.hxx @@ -31,6 +31,7 @@ class SwPageNumberDlg final : public SfxDialogController std::unique_ptr<weld::Button> m_xCancel; std::unique_ptr<weld::ComboBox> m_xPageNumberPosition; std::unique_ptr<weld::ComboBox> m_xPageNumberAlignment; + std::unique_ptr<weld::CheckButton> m_xMirrorOnEvenPages; std::unique_ptr<SvxPageNumberListBox> m_xPageNumberTypeLB; std::unique_ptr<weld::Image> m_xPreviewImage; @@ -50,6 +51,7 @@ public: SwPageNumberDlg(weld::Window* pParent); int GetPageNumberPosition() const { return m_aPageNumberPosition; } int GetPageNumberAlignment() const { return m_aPageNumberAlignment; } + bool GetMirrorOnEvenPages(); SvxNumType GetPageNumberType() const { return m_nPageNumberType; } void SetPageNumberType(SvxNumType nSet); }; diff --git a/sw/source/uibase/shells/textfld.cxx b/sw/source/uibase/shells/textfld.cxx index 766d5e03c0a2..eb21afce0482 100644 --- a/sw/source/uibase/shells/textfld.cxx +++ b/sw/source/uibase/shells/textfld.cxx @@ -1052,6 +1052,8 @@ FIELD_INSERT: const SwPageDesc& rDesc = rSh.GetPageDesc(nPageDescIndex); const bool bHeaderAlreadyOn = rDesc.GetMaster().GetHeader().IsActive(); const bool bFooterAlreadyOn = rDesc.GetMaster().GetFooter().IsActive(); + const bool bIsSinglePage = rDesc.GetFollow() != &rDesc; + const size_t nMirrorPagesNeeded = rDesc.IsFirstShared() ? 2 : 3; SvxPageItem aPageItem(SID_ATTR_PAGE); aPageItem.SetNumType(pDlg->GetPageNumberType()); @@ -1066,10 +1068,76 @@ FIELD_INSERT: else if (!bHeader && !bFooterAlreadyOn) rSh.ChangeHeaderOrFooter(rDesc.GetName(), false, /*On=*/true, /*Warn=*/false); - if (bHeader) - rSh.GotoHeaderText(); + const bool bCreateMirror = !bIsSinglePage && pDlg->GetMirrorOnEvenPages() + && nMirrorPagesNeeded <= rSh.GetPageCnt(); + if (bCreateMirror) + { + // Use different left/right header/footer + if ((bHeader && rDesc.IsHeaderShared()) || (!bHeader && rDesc.IsFooterShared())) + { + SwPageDesc rNewDesc(rSh.GetPageDesc(nPageDescIndex)); + + if (bHeader) + rNewDesc.ChgHeaderShare(false); + else + rNewDesc.ChgFooterShare(false); + + rSh.ChgPageDesc(nPageDescIndex, rNewDesc); + } + } + + // Go to the header or footer insert position + bool bInHF = false; + bool bSkipMirror = true; + size_t nEvenPage = 0; + if (bCreateMirror) + { + // There are enough pages that there probably is a valid odd page. + // However, that is not guaranteed: perhaps the page style switched, + // or a blank page was forced, or some other complexity. + bInHF = rSh.SetCursorInHdFt(nPageDescIndex, bHeader, /*Even=*/true); + if (bInHF) + { + // Remember valid EVEN page. Mirror it if also a valid ODD or FIRST page + nEvenPage = rSh.GetVirtPageNum(); + assert (nEvenPage && "couldn't find page number. Use a bool instead"); + } + + bInHF = rSh.SetCursorInHdFt(nPageDescIndex, bHeader, /*Even=*/false); + if (bInHF && nEvenPage) + { + // Even though the cursor may be on a FIRST page, + // the user requested mirrored pages, and we have both ODD and EVEN, + // so set page numbers on these two pages, and leave FIRST alone. + bSkipMirror = false; + } + if (!bInHF) + { + // no ODD page, look for FIRST page + bInHF = rSh.SetCursorInHdFt(nPageDescIndex, bHeader, false, /*First=*/true); + if (bInHF && nEvenPage) + { + // Unlikely but valid situation: EVEN and FIRST pages, but no ODD page. + // In this case, the first header gets the specified page number + // and the even header is mirrored, with an empty odd header, + // as the user (somewhat) requested. + bSkipMirror = false; + } + } + assert((bInHF || nEvenPage) && "Impossible - why couldn't the move happen?"); + assert((bInHF || nEvenPage == rSh.GetVirtPageNum()) && "Unexpected move"); + } else - rSh.GotoFooterText(); + { + // CurrFrame is lost when mirror is created. Goto*Text crashes if no CurrFrame + assert(rSh.GetCurrFrame()); // not guaranteed, but normally assumed + + if (bHeader) + bInHF = rSh.GotoHeaderText(); + else + bInHF = rSh.GotoFooterText(); + assert(bInHF && "shouldn't have a problem going to text when no mirroring"); + } SwTextNode* pTextNode = rSh.GetCursor()->GetPoint()->GetNode().GetTextNode(); @@ -1114,6 +1182,35 @@ FIELD_INSERT: OUString(), OUString(), SVX_NUM_PAGEDESC); aMgr.InsertField(aData); + // Mirror on the even pages + if (!bSkipMirror && bCreateMirror + && rSh.SetCursorInHdFt(nPageDescIndex, bHeader, /*Even=*/true)) + { + assert(nEvenPage && "what? no even page and yet we got here?"); + pTextNode = rSh.GetCursor()->GetPoint()->GetNode().GetTextNode(); + + // Insert new line if there is already text in header/footer + if (pTextNode && !pTextNode->GetText().isEmpty()) + { + rDoc->getIDocumentContentOperations().SplitNode( + *rSh.GetCursor()->GetPoint(), false); + // Go back to start of header/footer + rSh.SetCursorInHdFt(nPageDescIndex, bHeader, /*Even=*/true); + } + + // mirror the adjustment + assert(pDlg->GetPageNumberAlignment() != 1 && "cannot have Center and bMirror"); + SvxAdjust eAdjust = SvxAdjust::Left; + if (!pDlg->GetPageNumberAlignment()) + eAdjust = SvxAdjust::Right; + SvxAdjustItem aMirrorAdjustItem(eAdjust, RES_PARATR_ADJUST); + rSh.SetAttrItem(aMirrorAdjustItem); + + // Insert page number + SwFieldMgr aEvenMgr(pShell); + aEvenMgr.InsertField(aData); + } + rSh.SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent); rSh.EndAllAction(); rSh.LockView(false); diff --git a/sw/uiconfig/swriter/ui/pagenumberdlg.ui b/sw/uiconfig/swriter/ui/pagenumberdlg.ui index 99f9f9145527..5ed72d4fad3f 100644 --- a/sw/uiconfig/swriter/ui/pagenumberdlg.ui +++ b/sw/uiconfig/swriter/ui/pagenumberdlg.ui @@ -166,6 +166,26 @@ <property name="position">3</property> </packing> </child> + <child> + <object class="GtkCheckButton" id="mirrorCheckbox"> + <property name="label" translatable="yes" context="numberingoptionspage|mirrorCheckbox">Mirror on even pages</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="mirrorCheckbox-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="numberingoptionspage|extended_tip|mirrorCheckbox">Creates separate left/right pages with mirrored page number placements</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> <child> <object class="GtkLabel" id="numfmtLabel"> <property name="visible">True</property> @@ -187,7 +207,7 @@ <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">4</property> + <property name="position">5</property> </packing> </child> <child> @@ -206,7 +226,7 @@ <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">5</property> + <property name="position">6</property> </packing> </child> </object>