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>

Reply via email to