1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <view.hxx>
11 #include <swmodule.hxx>
12 #include <wrtsh.hxx>
13 #include <poolfmt.hxx>
14 #include <docsh.hxx>
15 
16 #include <titlepage.hxx>
17 #include <fmtpdsc.hxx>
18 #include <pagedesc.hxx>
19 
20 namespace
21 {
lcl_GetPageDesc(SwWrtShell & rSh,sal_uInt16 & rPageNo,std::unique_ptr<const SwFormatPageDesc> * ppPageFormatDesc)22     bool lcl_GetPageDesc(SwWrtShell& rSh, sal_uInt16 &rPageNo, std::unique_ptr<const SwFormatPageDesc>* ppPageFormatDesc)
23     {
24         bool bRet = false;
25         SfxItemSet aSet(rSh.GetAttrPool(), svl::Items<RES_PAGEDESC, RES_PAGEDESC>{});
26         if (rSh.GetCurAttr(aSet))
27         {
28             const SfxPoolItem* pItem(nullptr);
29             if (SfxItemState::SET == aSet.GetItemState( RES_PAGEDESC, true, &pItem ) && pItem)
30             {
31                 ::std::optional<sal_uInt16> oNumOffset = static_cast<const SwFormatPageDesc *>(pItem)->GetNumOffset();
32                 if (oNumOffset)
33                     rPageNo = *oNumOffset;
34                 if (ppPageFormatDesc)
35                     ppPageFormatDesc->reset(static_cast<const SwFormatPageDesc *>(pItem->Clone()));
36                 bRet = true;
37             }
38         }
39         return bRet;
40     }
41 
lcl_ChangePage(SwWrtShell & rSh,sal_uInt16 nNewNumber,const SwPageDesc * pNewDesc)42     void lcl_ChangePage(SwWrtShell& rSh, sal_uInt16 nNewNumber, const SwPageDesc *pNewDesc)
43     {
44         const size_t nCurIdx = rSh.GetCurPageDesc();
45         const SwPageDesc &rCurrentDesc = rSh.GetPageDesc(nCurIdx);
46 
47         std::unique_ptr<const SwFormatPageDesc> pPageFormatDesc;
48         sal_uInt16 nDontCare;
49         lcl_GetPageDesc(rSh, nDontCare, &pPageFormatDesc);
50 
51         // If we want a new number then set it, otherwise reuse the existing one
52         sal_uInt16 nPgNo = 0;
53         if (nNewNumber)
54         {
55             // -1: Allow special case to prevent inheriting re-numbering from the existing page.
56             nPgNo = nNewNumber == SAL_MAX_UINT16 ? 0 : nNewNumber;
57         }
58         else if (pPageFormatDesc)
59         {
60             ::std::optional<sal_uInt16> oNumOffset = pPageFormatDesc->GetNumOffset();
61             if (oNumOffset)
62                 nPgNo = *oNumOffset;
63         }
64 
65         // If we want a new descriptor then set it, otherwise reuse the existing one
66         if (pNewDesc || nPgNo)
67         {
68             SwFormatPageDesc aPageFormatDesc(pNewDesc ? pNewDesc : &rCurrentDesc);
69             if (nPgNo) aPageFormatDesc.SetNumOffset(nPgNo);
70             rSh.SetAttrItem(aPageFormatDesc);
71         }
72     }
73 
lcl_PushCursor(SwWrtShell & rSh)74     void lcl_PushCursor(SwWrtShell& rSh)
75     {
76         rSh.LockView(true);
77         rSh.StartAllAction();
78         rSh.SwCursorShell::Push();
79     }
80 
lcl_PopCursor(SwWrtShell & rSh)81     void lcl_PopCursor(SwWrtShell& rSh)
82     {
83         rSh.SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent);
84         rSh.EndAllAction();
85         rSh.LockView(false);
86     }
87 
lcl_GetCurrentPage(const SwWrtShell & rSh)88     sal_uInt16 lcl_GetCurrentPage(const SwWrtShell& rSh)
89     {
90         OUString sDummy;
91         sal_uInt16 nPhyNum=1, nVirtNum=1;
92         rSh.GetPageNumber(0, true, nPhyNum, nVirtNum, sDummy);
93         return nPhyNum;
94     }
95 
lcl_GotoPage(SwWrtShell & rSh,const sal_uInt16 nStartingPage,sal_uInt16 nOffset=0)96 bool lcl_GotoPage(SwWrtShell& rSh, const sal_uInt16 nStartingPage, sal_uInt16 nOffset = 0)
97 {
98     rSh.GotoPage(nStartingPage, /*bRecord=*/false);
99 
100     sal_uInt16 nCurrentPage = lcl_GetCurrentPage(rSh);
101     // return false if at document end (unless that was the requested destination)
102     if (nCurrentPage == rSh.GetPageCnt())
103         return nCurrentPage == nStartingPage + nOffset;
104 
105     if (nCurrentPage != nStartingPage)
106     {
107         assert(nStartingPage != 1 && "Physical page 1 couldn't be found/moved to?");
108         // Probably there is an auto-inserted blank page to handle odd/even, which Goto doesn't understand.
109         rSh.GotoPage(nStartingPage + 1, /*bRecord=*/false);
110 
111         nCurrentPage = lcl_GetCurrentPage(rSh);
112         assert(nCurrentPage == nStartingPage + 1 && "Impossible, since unknown goes to last page");
113         if (nCurrentPage != nStartingPage + 1)
114             return false;
115     }
116     // Now that we have the correct starting point, move to the correct offset.
117     while (nOffset--)
118         rSh.SttNxtPg();
119     return true;
120 }
121 } // namespace
122 
123 /*
124  * Only include the Index page in the list if the page count implies one
125  * to reduce confusing things
126  */
FillList()127 void SwTitlePageDlg::FillList()
128 {
129     sal_uInt16 nTitlePages = m_xPageCountNF->get_value();
130     m_xPagePropertiesLB->clear();
131     if (mpTitleDesc)
132         m_xPagePropertiesLB->append_text(mpTitleDesc->GetName());
133     if (nTitlePages > 1 && mpIndexDesc)
134         m_xPagePropertiesLB->append_text(mpIndexDesc->GetName());
135     if (mpNormalDesc)
136         m_xPagePropertiesLB->append_text(mpNormalDesc->GetName());
137     m_xPagePropertiesLB->set_active(0);
138 }
139 
GetInsertPosition() const140 sal_uInt16 SwTitlePageDlg::GetInsertPosition() const
141 {
142     sal_uInt16 nPage = 1;
143     if (m_xPageStartNF->get_sensitive())
144         nPage = m_xPageStartNF->get_value();
145     return nPage;
146 }
147 
SwTitlePageDlg(weld::Window * pParent)148 SwTitlePageDlg::SwTitlePageDlg(weld::Window *pParent)
149     : SfxDialogController(pParent, "modules/swriter/ui/titlepage.ui", "DLG_TITLEPAGE")
150     , mrSh(*::GetActiveView()->GetWrtShellPtr())
151     , m_xUseExistingPagesRB(m_xBuilder->weld_radio_button("RB_USE_EXISTING_PAGES"))
152     , m_xPageCountNF(m_xBuilder->weld_spin_button("NF_PAGE_COUNT"))
153     , m_xDocumentStartRB(m_xBuilder->weld_radio_button("RB_DOCUMENT_START"))
154     , m_xPageStartRB(m_xBuilder->weld_radio_button("RB_PAGE_START"))
155     , m_xPageStartNF(m_xBuilder->weld_spin_button("NF_PAGE_START"))
156     , m_xRestartNumberingCB(m_xBuilder->weld_check_button("CB_RESTART_NUMBERING"))
157     , m_xRestartNumberingNF(m_xBuilder->weld_spin_button("NF_RESTART_NUMBERING"))
158     , m_xSetPageNumberCB(m_xBuilder->weld_check_button("CB_SET_PAGE_NUMBER"))
159     , m_xSetPageNumberNF(m_xBuilder->weld_spin_button("NF_SET_PAGE_NUMBER"))
160     , m_xPagePropertiesLB(m_xBuilder->weld_combo_box("LB_PAGE_PROPERTIES"))
161     , m_xPagePropertiesPB(m_xBuilder->weld_button("PB_PAGE_PROPERTIES"))
162     , m_xOkPB(m_xBuilder->weld_button("ok"))
163 {
164     m_xOkPB->connect_clicked(LINK(this, SwTitlePageDlg, OKHdl));
165     m_xRestartNumberingCB->connect_toggled(LINK(this, SwTitlePageDlg, RestartNumberingHdl));
166     m_xSetPageNumberCB->connect_toggled(LINK(this, SwTitlePageDlg, SetPageNumberHdl));
167     m_xPageStartNF->set_max(mrSh.GetPageCnt() + 1);
168 
169     sal_uInt16 nSetPage = 1;
170     sal_uInt16 nResetPage = 1;
171     sal_uInt16 nTitlePages = 1;
172     lcl_PushCursor(mrSh);
173 
174     SwView& rView = mrSh.GetView();
175     rView.InvalidateRulerPos();
176 
177     bool bMaybeResetNumbering = false;
178 
179     mpTitleDesc = mrSh.GetPageDescFromPool(RES_POOLPAGE_FIRST);
180     mpIndexDesc = mrSh.GetPageDescFromPool(RES_POOLPAGE_REGISTER);
181     mpNormalDesc = mrSh.GetPageDescFromPool(RES_POOLPAGE_STANDARD);
182 
183     mrSh.StartOfSection();
184     if (lcl_GetPageDesc(mrSh, nSetPage, &mpPageFormatDesc))
185     {
186         if (mpPageFormatDesc->GetPageDesc() == mpTitleDesc)
187         {
188             while (mrSh.SttNxtPg())
189             {
190                 const size_t nCurIdx = mrSh.GetCurPageDesc();
191                 const SwPageDesc& rPageDesc = mrSh.GetPageDesc(nCurIdx);
192 
193                 if (mpIndexDesc != &rPageDesc)
194                 {
195                     mpNormalDesc = &rPageDesc;
196                     bMaybeResetNumbering = lcl_GetPageDesc(mrSh, nResetPage, nullptr);
197                     break;
198                 }
199                 ++nTitlePages;
200             }
201         }
202     }
203     lcl_PopCursor(mrSh);
204 
205     m_xUseExistingPagesRB->set_active(true);
206     m_xPageCountNF->set_value(nTitlePages);
207     m_xPageCountNF->connect_value_changed(LINK(this, SwTitlePageDlg, ValueChangeHdl));
208 
209     m_xDocumentStartRB->set_active(true);
210     m_xPageStartNF->set_sensitive(false);
211     m_xPageStartNF->set_value(lcl_GetCurrentPage(mrSh));
212     Link<weld::Toggleable&,void> aStartPageHdl = LINK(this, SwTitlePageDlg, StartPageHdl);
213     m_xDocumentStartRB->connect_toggled(aStartPageHdl);
214     m_xPageStartRB->connect_toggled(aStartPageHdl);
215 
216     m_xRestartNumberingNF->set_value(nResetPage);
217     if (bMaybeResetNumbering && nResetPage > 0)
218     {
219         m_xRestartNumberingCB->set_active(true);
220     }
221     m_xRestartNumberingNF->set_sensitive(m_xRestartNumberingCB->get_active());
222 
223     m_xSetPageNumberNF->set_value(nSetPage);
224     if (nSetPage > 1)
225         m_xSetPageNumberCB->set_active(true);
226     m_xSetPageNumberNF->set_sensitive(m_xSetPageNumberCB->get_active());
227 
228     FillList();
229     m_xPagePropertiesPB->connect_clicked(LINK(this, SwTitlePageDlg, EditHdl));
230 }
231 
IMPL_LINK_NOARG(SwTitlePageDlg,ValueChangeHdl,weld::SpinButton &,void)232 IMPL_LINK_NOARG(SwTitlePageDlg, ValueChangeHdl, weld::SpinButton&, void)
233 {
234     if (m_xPageCountNF->get_value() == 1 || m_xPageCountNF->get_value() == 2)
235         FillList();
236 }
237 
IMPL_LINK_NOARG(SwTitlePageDlg,RestartNumberingHdl,weld::Toggleable &,void)238 IMPL_LINK_NOARG(SwTitlePageDlg, RestartNumberingHdl, weld::Toggleable&, void)
239 {
240     m_xRestartNumberingNF->set_sensitive(m_xRestartNumberingCB->get_active());
241 }
242 
IMPL_LINK_NOARG(SwTitlePageDlg,SetPageNumberHdl,weld::Toggleable &,void)243 IMPL_LINK_NOARG(SwTitlePageDlg, SetPageNumberHdl, weld::Toggleable&, void)
244 {
245     m_xSetPageNumberNF->set_sensitive(m_xSetPageNumberCB->get_active());
246 }
247 
IMPL_LINK_NOARG(SwTitlePageDlg,StartPageHdl,weld::Toggleable &,void)248 IMPL_LINK_NOARG(SwTitlePageDlg, StartPageHdl, weld::Toggleable&, void)
249 {
250     m_xPageStartNF->set_sensitive(m_xPageStartRB->get_active());
251 }
252 
~SwTitlePageDlg()253 SwTitlePageDlg::~SwTitlePageDlg()
254 {
255 }
256 
IMPL_LINK_NOARG(SwTitlePageDlg,EditHdl,weld::Button &,void)257 IMPL_LINK_NOARG(SwTitlePageDlg, EditHdl, weld::Button&, void)
258 {
259     SwView& rView = mrSh.GetView();
260     rView.GetDocShell()->FormatPage(m_xPagePropertiesLB->get_active_text(), "page", mrSh);
261     rView.InvalidateRulerPos();
262 }
263 
IMPL_LINK_NOARG(SwTitlePageDlg,OKHdl,weld::Button &,void)264 IMPL_LINK_NOARG(SwTitlePageDlg, OKHdl, weld::Button&, void)
265 {
266     lcl_PushCursor(mrSh);
267 
268     mrSh.StartUndo();
269 
270     SwFormatPageDesc aTitleDesc(mpTitleDesc);
271 
272     if (m_xSetPageNumberCB->get_active())
273         aTitleDesc.SetNumOffset(m_xSetPageNumberNF->get_value());
274     else if (mpPageFormatDesc)
275         aTitleDesc.SetNumOffset(mpPageFormatDesc->GetNumOffset());
276 
277     sal_uInt16 nNumTitlePages = m_xPageCountNF->get_value();
278     if (!m_xUseExistingPagesRB->get_active())
279     {
280         // Assuming that a failure to GotoPage means the end of the document,
281         // insert new pages after the last page.
282         if (!lcl_GotoPage(mrSh, GetInsertPosition()))
283         {
284             mrSh.EndPg();
285             // Add one more page as a content page to follow the new title pages.
286             mrSh.InsertPageBreak();
287         }
288         for (sal_uInt16 nI = 0; nI < nNumTitlePages; ++nI)
289             mrSh.InsertPageBreak();
290         // In order to be able to access these new pages, the layout needs to be recalculated first.
291         mrSh.CalcLayout();
292     }
293 
294     if (lcl_GotoPage(mrSh, GetInsertPosition()))
295     {
296         mrSh.SetAttrItem(aTitleDesc);
297         for (sal_uInt16 nI = 1; nI < nNumTitlePages; ++nI)
298         {
299             if (mrSh.SttNxtPg())
300                 lcl_ChangePage(mrSh, SAL_MAX_UINT16, mpIndexDesc);
301         }
302     }
303 
304     if ((m_xRestartNumberingCB->get_active() || nNumTitlePages > 1)
305         && lcl_GotoPage(mrSh, GetInsertPosition(), nNumTitlePages))
306     {
307         sal_uInt16 nPgNo
308             = m_xRestartNumberingCB->get_active() ? m_xRestartNumberingNF->get_value() : 0;
309         const SwPageDesc* pNewDesc = nNumTitlePages > 1 ? mpNormalDesc : nullptr;
310         lcl_ChangePage(mrSh, nPgNo, pNewDesc);
311     }
312 
313     mrSh.EndUndo();
314     lcl_PopCursor(mrSh);
315     if (!m_xUseExistingPagesRB->get_active())
316         lcl_GotoPage(mrSh, GetInsertPosition());
317     m_xDialog->response(RET_OK);
318 }
319 
320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
321