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>

Reply via email to