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 78929e622173a4fb08949909872140c441334722 Author: Justin Luth <[email protected]> AuthorDate: Wed Apr 26 13:29:25 2023 -0400 Commit: Justin Luth <[email protected]> CommitDate: Thu May 18 17:56:12 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 <[email protected]> Tested-by: Jenkins Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151901 Tested-by: Justin Luth <[email protected]> diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index efc8aa1eec49..37e799cf2b3f 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -672,7 +672,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 ae47d54053f4..994360eca166 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 954e9ba6707c..67093bd9577d 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 e9e20bf336da..abd947a9340d 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 39c8d1fc6f54..691b6127b204 100644 --- a/sw/source/ui/dialog/swdlgfact.hxx +++ b/sw/source/ui/dialog/swdlgfact.hxx @@ -165,6 +165,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 9d2ad412840b..fa757ed2212d 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)); @@ -64,6 +67,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) @@ -71,6 +79,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 9e3adff871a7..77baf43ab498 100644 --- a/sw/source/uibase/inc/pagenumberdlg.hxx +++ b/sw/source/uibase/inc/pagenumberdlg.hxx @@ -31,6 +31,7 @@ class SwPageNumberDlg : 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; @@ -51,6 +52,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 93f19ca4e0dd..66f016df7c65 100644 --- a/sw/source/uibase/shells/textfld.cxx +++ b/sw/source/uibase/shells/textfld.cxx @@ -1049,6 +1049,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()); @@ -1063,10 +1065,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(); @@ -1111,6 +1179,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 3063e42ac4ac..49a89e1fd98d 100644 --- a/sw/uiconfig/swriter/ui/pagenumberdlg.ui +++ b/sw/uiconfig/swriter/ui/pagenumberdlg.ui @@ -152,6 +152,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> @@ -173,7 +193,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> @@ -192,7 +212,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>
