1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "DocumentHelper.hxx"
22 #include <drawdoc.hxx>
23 #include <DrawDocShell.hxx>
26 #include <unmovss.hxx>
27 #include <strings.hrc>
28 #include <sdresid.hxx>
29 #include <undoback.hxx>
30 #include <ViewShell.hxx>
31 #include <ViewShellBase.hxx>
32 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
33 #include <com/sun/star/drawing/XDrawPages.hpp>
34 #include <com/sun/star/container/XIndexAccess.hpp>
35 #include <stlpool.hxx>
36 #include <svx/xfillit0.hxx>
37 #include <svx/svdundo.hxx>
38 #include <tools/diagnose_ex.h>
39 #include <xmloff/autolayout.hxx>
40 #include <sal/log.hxx>
42 using namespace ::com::sun::star
;
44 namespace sd
{ namespace sidebar
{
46 SdPage
* DocumentHelper::CopyMasterPageToLocalDocument (
47 SdDrawDocument
& rTargetDocument
,
50 SdPage
* pNewMasterPage
= nullptr;
54 if (pMasterPage
== nullptr)
57 // Check the presence of the source document.
58 SdDrawDocument
& rSourceDocument(static_cast< SdDrawDocument
& >(pMasterPage
->getSdrModelFromSdrPage()));
60 // When the given master page already belongs to the target document
61 // then there is nothing more to do.
62 if (&rSourceDocument
== &rTargetDocument
)
64 pNewMasterPage
= pMasterPage
;
68 // Test if the master pages of both the slide and its notes page are
69 // present. This is not the case when we are called during the
70 // creation of the slide master page because then the notes master
72 sal_uInt16 nSourceMasterPageCount
= rSourceDocument
.GetMasterPageCount();
73 if (nSourceMasterPageCount
%2 == 0)
74 // There should be 1 handout page + n slide masters + n notes
75 // masters = 2*n+1. An even value indicates that a new slide
76 // master but not yet the notes master has been inserted.
78 sal_uInt16 nIndex
= pMasterPage
->GetPageNum();
79 if (nSourceMasterPageCount
<= nIndex
+1)
81 // Get the slide master page.
82 if (pMasterPage
!= static_cast<SdPage
*>(
83 rSourceDocument
.GetMasterPage(nIndex
)))
85 // Get the notes master page.
86 SdPage
* pNotesMasterPage
= static_cast<SdPage
*>(
87 rSourceDocument
.GetMasterPage(nIndex
+1));
88 if (pNotesMasterPage
== nullptr)
91 // Check if a master page with the same name as that of the given
92 // master page already exists.
93 bool bPageExists (false);
94 sal_uInt16
nMasterPageCount(rTargetDocument
.GetMasterSdPageCount(PageKind::Standard
));
95 for (sal_uInt16 nMaster
=0; nMaster
<nMasterPageCount
; nMaster
++)
97 SdPage
* pCandidate
= rTargetDocument
.GetMasterSdPage (nMaster
, PageKind::Standard
);
98 if (pCandidate
->GetName() == pMasterPage
->GetName())
101 pNewMasterPage
= pCandidate
;
108 // Create a new slide (and its notes page.)
109 uno::Reference
<drawing::XDrawPagesSupplier
> xSlideSupplier (
110 rTargetDocument
.getUnoModel(), uno::UNO_QUERY
);
111 if ( ! xSlideSupplier
.is())
113 uno::Reference
<drawing::XDrawPages
> xSlides (
114 xSlideSupplier
->getDrawPages(), uno::UNO_QUERY
);
117 xSlides
->insertNewByIndex (xSlides
->getCount());
120 SdPage
* pSlide
= rTargetDocument
.GetSdPage(
121 rTargetDocument
.GetSdPageCount(PageKind::Standard
)-1,
123 if (pSlide
== nullptr)
125 pSlide
->SetAutoLayout(AUTOLAYOUT_TITLE
, true);
127 // Create a copy of the master page and the associated notes
128 // master page and insert them into our document.
129 pNewMasterPage
= AddMasterPage(rTargetDocument
, pMasterPage
);
130 if (pNewMasterPage
==nullptr)
132 SdPage
* pNewNotesMasterPage
133 = AddMasterPage(rTargetDocument
, pNotesMasterPage
);
134 if (pNewNotesMasterPage
==nullptr)
137 // Make the connection from the new slide to the master page
138 // (and do the same for the notes page.)
139 rTargetDocument
.SetMasterPage (
140 rTargetDocument
.GetSdPageCount(PageKind::Standard
)-1,
141 pNewMasterPage
->GetName(),
143 false, // Connect the new master page with the new slide but
144 // do not modify other (master) pages.
149 // We are not interested in any automatisms for our modified internal
151 rTargetDocument
.SetChanged(false);
153 return pNewMasterPage
;
156 SdPage
* DocumentHelper::GetSlideForMasterPage (SdPage
const * pMasterPage
)
158 SdPage
* pCandidate
= nullptr;
160 SdDrawDocument
* pDocument
= nullptr;
161 if (pMasterPage
!= nullptr)
162 pDocument
= dynamic_cast< SdDrawDocument
* >(&pMasterPage
->getSdrModelFromSdrPage());
164 // Iterate over all pages and check if it references the given master
166 if (pDocument
!=nullptr && pDocument
->GetSdPageCount(PageKind::Standard
) > 0)
168 // In most cases a new slide has just been inserted so start with
170 sal_uInt16
nPageIndex (pDocument
->GetSdPageCount(PageKind::Standard
)-1);
174 pCandidate
= pDocument
->GetSdPage(
177 if (pCandidate
!= nullptr)
179 if (static_cast<SdPage
*>(&pCandidate
->TRG_GetMasterPage())
193 // If no page was found, that referenced the given master page, reset
194 // the pointer that is returned.
196 pCandidate
= nullptr;
202 SdPage
* DocumentHelper::AddMasterPage (
203 SdDrawDocument
& rTargetDocument
,
204 SdPage
const * pMasterPage
)
206 SdPage
* pClonedMasterPage
= nullptr;
208 if (pMasterPage
!=nullptr)
212 // Duplicate the master page.
213 pClonedMasterPage
= static_cast<SdPage
*>(pMasterPage
->CloneSdrPage(rTargetDocument
));
215 // Copy the necessary styles.
216 SdDrawDocument
& rSourceDocument(static_cast< SdDrawDocument
& >(pMasterPage
->getSdrModelFromSdrPage()));
217 ProvideStyles(rSourceDocument
, rTargetDocument
, pClonedMasterPage
);
219 // Copy the precious flag.
220 pClonedMasterPage
->SetPrecious(pMasterPage
->IsPrecious());
222 // Now that the styles are available we can insert the cloned
224 rTargetDocument
.InsertMasterPage (pClonedMasterPage
);
226 catch(const uno::Exception
&)
228 DBG_UNHANDLED_EXCEPTION("sd");
229 pClonedMasterPage
= nullptr;
231 catch(const ::std::exception
& e
)
233 pClonedMasterPage
= nullptr;
234 SAL_WARN("sd", "caught general exception " << e
.what());
238 pClonedMasterPage
= nullptr;
239 SAL_WARN("sd", "caught general exception");
243 return pClonedMasterPage
;
246 void DocumentHelper::ProvideStyles (
247 SdDrawDocument
const & rSourceDocument
,
248 SdDrawDocument
& rTargetDocument
,
249 SdPage
const * pPage
)
251 // Get the layout name of the given page.
252 OUString
sLayoutName (pPage
->GetLayoutName());
253 sal_Int32 nIndex
= sLayoutName
.indexOf(SD_LT_SEPARATOR
);
255 sLayoutName
= sLayoutName
.copy(0, nIndex
);
257 // Copy the style sheet from source to target document.
258 SdStyleSheetPool
* pSourceStyleSheetPool
=
259 static_cast<SdStyleSheetPool
*>(rSourceDocument
.GetStyleSheetPool());
260 SdStyleSheetPool
* pTargetStyleSheetPool
=
261 static_cast<SdStyleSheetPool
*>(rTargetDocument
.GetStyleSheetPool());
262 StyleSheetCopyResultVector aCreatedStyles
;
263 pTargetStyleSheetPool
->CopyLayoutSheets (
265 *pSourceStyleSheetPool
,
268 // Add an undo action for the copied style sheets.
269 if( !aCreatedStyles
.empty() )
271 SfxUndoManager
* pUndoManager
= rTargetDocument
.GetDocSh()->GetUndoManager();
272 if (pUndoManager
!= nullptr)
274 pUndoManager
->AddUndoAction (
275 std::make_unique
<SdMoveStyleSheetsUndoAction
>(
283 void DocumentHelper::AssignMasterPageToPageList (
284 SdDrawDocument
& rTargetDocument
,
286 const std::shared_ptr
<std::vector
<SdPage
*> >& rpPageList
)
288 if (pMasterPage
== nullptr || !pMasterPage
->IsMasterPage())
291 // Make the layout name by stripping ouf the layout postfix from the
292 // layout name of the given master page.
293 OUString
sFullLayoutName(pMasterPage
->GetLayoutName());
294 OUString
sBaseLayoutName (sFullLayoutName
);
295 sal_Int32 nIndex
= sBaseLayoutName
.indexOf(SD_LT_SEPARATOR
);
297 sBaseLayoutName
= sBaseLayoutName
.copy(0, nIndex
);
299 if (rpPageList
->empty())
302 // Create a second list that contains only the valid pointers to
303 // pages for which an assignment is necessary.
304 ::std::vector
<SdPage
*> aCleanedList
;
305 for (const auto& rpPage
: *rpPageList
)
307 OSL_ASSERT(rpPage
!=nullptr && &rpPage
->getSdrModelFromSdrPage() == &rTargetDocument
);
308 if (rpPage
!= nullptr && rpPage
->GetLayoutName() != sFullLayoutName
)
310 aCleanedList
.push_back(rpPage
);
313 if (aCleanedList
.empty() )
316 SfxUndoManager
* pUndoMgr
= rTargetDocument
.GetDocSh()->GetUndoManager();
318 pUndoMgr
->EnterListAction(SdResId(STR_UNDO_SET_PRESLAYOUT
), OUString(), 0, rTargetDocument
.GetDocSh()->GetViewShell()->GetViewShellBase().GetViewShellId());
320 SdPage
* pMasterPageInDocument
= ProvideMasterPage(rTargetDocument
,pMasterPage
,rpPageList
);
321 if (pMasterPageInDocument
== nullptr)
324 // Assign the master pages to the given list of pages.
325 for (const auto& rpPage
: aCleanedList
)
327 AssignMasterPageToPage (
328 pMasterPageInDocument
,
334 pUndoMgr
->LeaveListAction();
337 SdPage
* DocumentHelper::AddMasterPage (
338 SdDrawDocument
& rTargetDocument
,
339 SdPage
const * pMasterPage
,
340 sal_uInt16 nInsertionIndex
)
342 SdPage
* pClonedMasterPage
= nullptr;
344 if (pMasterPage
!=nullptr)
346 // Duplicate the master page.
347 pClonedMasterPage
= static_cast<SdPage
*>(pMasterPage
->CloneSdrPage(rTargetDocument
));
349 // Copy the precious flag.
350 pClonedMasterPage
->SetPrecious(pMasterPage
->IsPrecious());
352 // Copy the necessary styles.
353 SdDrawDocument
& rSourceDocument(static_cast< SdDrawDocument
& >(pMasterPage
->getSdrModelFromSdrPage()));
354 ProvideStyles(rSourceDocument
, rTargetDocument
, pClonedMasterPage
);
356 // Now that the styles are available we can insert the cloned
358 rTargetDocument
.InsertMasterPage (pClonedMasterPage
, nInsertionIndex
);
360 // Adapt the size of the new master page to that of the pages in
362 Size
aNewSize (rTargetDocument
.GetSdPage(0, pMasterPage
->GetPageKind())->GetSize());
363 ::tools::Rectangle
aBorders (
364 pClonedMasterPage
->GetLeftBorder(),
365 pClonedMasterPage
->GetUpperBorder(),
366 pClonedMasterPage
->GetRightBorder(),
367 pClonedMasterPage
->GetLowerBorder());
368 pClonedMasterPage
->ScaleObjects(aNewSize
, aBorders
, true);
369 pClonedMasterPage
->SetSize(aNewSize
);
370 pClonedMasterPage
->CreateTitleAndLayout(true);
373 return pClonedMasterPage
;
376 /** In here we have to handle three cases:
377 1. pPage is a normal slide. We can use SetMasterPage to assign the
379 2. pPage is a master page that is used by at least one slide. We can
380 assign the master page to these slides.
381 3. pPage is a master page that is currently not used by any slide.
382 We can delete that page and add copies of the given master pages
385 For points 2 and 3 where one master page A is assigned to another B we have
386 to keep in mind that the master page that page A has already been
387 inserted into the target document.
389 void DocumentHelper::AssignMasterPageToPage (
390 SdPage
const * pMasterPage
,
391 const OUString
& rsBaseLayoutName
,
394 // Leave early when the parameters are invalid.
395 if (pPage
== nullptr || pMasterPage
== nullptr)
398 SdDrawDocument
& rDocument(dynamic_cast< SdDrawDocument
& >(pPage
->getSdrModelFromSdrPage()));
400 if ( ! pPage
->IsMasterPage())
402 // 1. Remove the background object (so that, if it exists, does
403 // not override the new master page) and assign the master page to
404 // the regular slide.
405 rDocument
.GetDocSh()->GetUndoManager()->AddUndoAction(
406 std::make_unique
<SdBackgroundObjUndoAction
>(
407 rDocument
, *pPage
, pPage
->getSdrPageProperties().GetItemSet()),
409 pPage
->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE
));
411 rDocument
.SetMasterPage (
412 (pPage
->GetPageNum()-1)/2,
420 // Find first slide that uses the master page.
421 SdPage
* pSlide
= nullptr;
422 sal_uInt16 nPageCount
= rDocument
.GetSdPageCount(PageKind::Standard
);
423 for (sal_uInt16 nPage
=0; nPage
<nPageCount
&&pSlide
==nullptr; nPage
++)
425 SdrPage
* pCandidate
= rDocument
.GetSdPage(nPage
,PageKind::Standard
);
426 if (pCandidate
!= nullptr
427 && pCandidate
->TRG_HasMasterPage()
428 && &(pCandidate
->TRG_GetMasterPage()) == pPage
)
430 pSlide
= static_cast<SdPage
*>(pCandidate
);
434 if (pSlide
!= nullptr)
436 // 2. Assign the given master pages to the first slide that was
437 // found above that uses the master page.
438 rDocument
.SetMasterPage (
439 (pSlide
->GetPageNum()-1)/2,
447 // 3. Replace the master page A by a copy of the given master
449 rDocument
.RemoveUnnecessaryMasterPages (
455 SdPage
* DocumentHelper::ProvideMasterPage (
456 SdDrawDocument
& rTargetDocument
,
458 const std::shared_ptr
<std::vector
<SdPage
*> >& rpPageList
)
460 // Make sure that both the master page and its notes master exist
461 // in the source document. If one is missing then return without
462 // making any changes.
463 if (pMasterPage
== nullptr)
465 // The caller should make sure that the master page is valid.
466 OSL_ASSERT(pMasterPage
!= nullptr);
469 SdDrawDocument
& rSourceDocument(static_cast< SdDrawDocument
& >(pMasterPage
->getSdrModelFromSdrPage()));
470 SdPage
* pNotesMasterPage
= static_cast<SdPage
*>(
471 rSourceDocument
.GetMasterPage(pMasterPage
->GetPageNum()+1));
472 if (pNotesMasterPage
== nullptr)
474 // The model is not in a valid state. Maybe a new master page
475 // is being (not finished yet) created? Return without making
480 SdPage
* pMasterPageInDocument
= nullptr;
481 // Search for a master page with the same name as the given one in
482 // the target document.
483 const OUString
sMasterPageLayoutName (pMasterPage
->GetLayoutName());
484 for (sal_uInt16 nIndex
=0,nCount
=rTargetDocument
.GetMasterPageCount(); nIndex
<nCount
; ++nIndex
)
486 SdPage
* pCandidate
= static_cast<SdPage
*>(rTargetDocument
.GetMasterPage(nIndex
));
487 if (pCandidate
&& sMasterPageLayoutName
== pCandidate
->GetLayoutName())
489 // The requested master page does already exist in the
490 // target document, return it.
495 // The given master page does not already belong to the target
496 // document so we have to create copies and insert them into the
499 // Determine the position where the new master pages are inserted.
500 // By default they are inserted at the end. When we assign to a
501 // master page then insert after the last of the (selected) pages.
502 sal_uInt16 nInsertionIndex
= rTargetDocument
.GetMasterPageCount();
503 if (rpPageList
->front()->IsMasterPage())
505 nInsertionIndex
= rpPageList
->back()->GetPageNum();
508 // Clone the master page.
509 if (&pMasterPage
->getSdrModelFromSdrPage() != &rTargetDocument
)
511 pMasterPageInDocument
= AddMasterPage (rTargetDocument
, pMasterPage
, nInsertionIndex
);
512 if( rTargetDocument
.IsUndoEnabled() )
513 rTargetDocument
.AddUndo(
514 rTargetDocument
.GetSdrUndoFactory().CreateUndoNewPage(*pMasterPageInDocument
));
517 pMasterPageInDocument
= pMasterPage
;
519 // Clone the notes master.
520 if (&pNotesMasterPage
->getSdrModelFromSdrPage() != &rTargetDocument
)
522 SdPage
* pClonedNotesMasterPage
523 = AddMasterPage (rTargetDocument
, pNotesMasterPage
, nInsertionIndex
+1);
524 if( rTargetDocument
.IsUndoEnabled() )
525 rTargetDocument
.AddUndo(
526 rTargetDocument
.GetSdrUndoFactory().CreateUndoNewPage(*pClonedNotesMasterPage
));
529 return pMasterPageInDocument
;
532 } } // end of namespace sd::sidebar
534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */