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 <model/SlideSorterModel.hxx>
22 #include <SlideSorter.hxx>
23 #include <sal/log.hxx>
24 #include <model/SlsPageDescriptor.hxx>
25 #include <model/SlsPageEnumerationProvider.hxx>
26 #include <controller/SlideSorterController.hxx>
27 #include <controller/SlsPageSelector.hxx>
28 #include <controller/SlsCurrentSlideManager.hxx>
29 #include <controller/SlsSlotManager.hxx>
30 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
31 #include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/frame/XController.hpp>
35 #include <vcl/uitest/logger.hxx>
36 #include <vcl/uitest/eventdescription.hxx>
38 #include <ViewShellBase.hxx>
39 #include <DrawDocShell.hxx>
40 #include <drawdoc.hxx>
42 #include <FrameView.hxx>
44 #include <o3tl/safeint.hxx>
45 #include <comphelper/diagnose_ex.hxx>
47 using namespace ::com::sun::star
;
48 using namespace ::com::sun::star::uno
;
50 namespace sd::slidesorter::model
{
53 bool PrintModel (const SlideSorterModel
& rModel
)
55 for (sal_Int32 nIndex
=0,nCount
=rModel
.GetPageCount(); nIndex
<nCount
; ++nIndex
)
57 SharedPageDescriptor
pDescriptor (rModel
.GetPageDescriptor(nIndex
));
62 nIndex
<< " " << pDescriptor
->GetPageIndex() << " "
63 << pDescriptor
->GetVisualState().mnPageId
<< " "
64 << FromCoreIndex(pDescriptor
->GetPage()->GetPageNum())
65 << " " << pDescriptor
->GetPage());
69 SAL_INFO("sd.sls", nIndex
);
75 bool CheckModel (const SlideSorterModel
& rModel
)
77 for (sal_Int32 nIndex
=0,nCount
=rModel
.GetPageCount(); nIndex
<nCount
; ++nIndex
)
79 SharedPageDescriptor
pDescriptor (rModel
.GetPageDescriptor(nIndex
));
86 if (nIndex
!= pDescriptor
->GetPageIndex())
89 assert(nIndex
== pDescriptor
->GetPageIndex());
92 if (nIndex
!= pDescriptor
->GetVisualState().mnPageId
)
95 assert(nIndex
== pDescriptor
->GetVisualState().mnPageId
);
106 void collectUIInformation(const OUString
& num
, const OUString
& rAction
)
108 EventDescription aDescription
;
109 aDescription
.aID
= "impress_win_or_draw_win";
110 aDescription
.aParameters
= {{"POS", num
}};
111 aDescription
.aAction
= rAction
;
112 aDescription
.aKeyWord
= "ImpressWindowUIObject";
113 aDescription
.aParent
= "MainWindow";
115 UITestLogger::getInstance().logEvent(aDescription
);
120 SlideSorterModel::SlideSorterModel (SlideSorter
& rSlideSorter
)
121 : mrSlideSorter(rSlideSorter
),
122 meEditMode(EditMode::Page
),
127 SlideSorterModel::~SlideSorterModel()
129 ClearDescriptorList ();
132 void SlideSorterModel::Dispose()
134 ClearDescriptorList ();
137 SdDrawDocument
* SlideSorterModel::GetDocument()
139 if (mrSlideSorter
.GetViewShellBase() != nullptr)
140 return mrSlideSorter
.GetViewShellBase()->GetDocument();
145 bool SlideSorterModel::SetEditMode (EditMode eEditMode
)
147 bool bEditModeChanged
= false;
148 if (meEditMode
!= eEditMode
)
150 meEditMode
= eEditMode
;
152 bEditModeChanged
= true;
154 return bEditModeChanged
;
157 sal_Int32
SlideSorterModel::GetPageCount() const
159 return maPageDescriptors
.size();
162 SharedPageDescriptor
SlideSorterModel::GetPageDescriptor (
163 const sal_Int32 nPageIndex
,
164 const bool bCreate
) const
166 ::osl::MutexGuard
aGuard (maMutex
);
168 SharedPageDescriptor pDescriptor
;
170 if (nPageIndex
>=0 && nPageIndex
<GetPageCount())
172 pDescriptor
= maPageDescriptors
[nPageIndex
];
173 if (pDescriptor
== nullptr && bCreate
&& mxSlides
.is())
175 SdPage
* pPage
= GetPage(nPageIndex
);
176 pDescriptor
= std::make_shared
<PageDescriptor
>(
177 Reference
<drawing::XDrawPage
>(mxSlides
->getByIndex(nPageIndex
),UNO_QUERY
),
180 maPageDescriptors
[nPageIndex
] = pDescriptor
;
187 sal_Int32
SlideSorterModel::GetIndex (const Reference
<drawing::XDrawPage
>& rxSlide
) const
189 ::osl::MutexGuard
aGuard (maMutex
);
191 // First try to guess the right index.
192 Reference
<beans::XPropertySet
> xSet (rxSlide
, UNO_QUERY
);
197 const Any
aNumber (xSet
->getPropertyValue(u
"Number"_ustr
));
198 sal_Int16
nNumber (-1);
201 SharedPageDescriptor
pDescriptor (GetPageDescriptor(nNumber
, false));
203 && pDescriptor
->GetXDrawPage() == rxSlide
)
208 catch (uno::Exception
&)
210 DBG_UNHANDLED_EXCEPTION("sd");
214 // Guess was wrong, iterate over all slides and search for the right
216 const sal_Int32
nCount (maPageDescriptors
.size());
217 for (sal_Int32 nIndex
=0; nIndex
<nCount
; ++nIndex
)
219 SharedPageDescriptor
pDescriptor (maPageDescriptors
[nIndex
]);
221 // Make sure that the descriptor exists. Without it the given slide
225 // Call GetPageDescriptor() to create the missing descriptor.
226 pDescriptor
= GetPageDescriptor(nIndex
);
229 if (pDescriptor
->GetXDrawPage() == rxSlide
)
236 sal_Int32
SlideSorterModel::GetIndex (const SdrPage
* pPage
) const
238 if (pPage
== nullptr)
241 ::osl::MutexGuard
aGuard (maMutex
);
243 // First try to guess the right index.
244 sal_Int16
nNumber ((pPage
->GetPageNum()-1)/2);
245 SharedPageDescriptor
pDescriptor (GetPageDescriptor(nNumber
, false));
247 && pDescriptor
->GetPage() == pPage
)
252 // Guess was wrong, iterate over all slides and search for the right
254 const sal_Int32
nCount (maPageDescriptors
.size());
255 for (sal_Int32 nIndex
=0; nIndex
<nCount
; ++nIndex
)
257 pDescriptor
= maPageDescriptors
[nIndex
];
259 // Make sure that the descriptor exists. Without it the given slide
263 // Call GetPageDescriptor() to create the missing descriptor.
264 pDescriptor
= GetPageDescriptor(nIndex
);
267 if (pDescriptor
->GetPage() == pPage
)
274 sal_uInt16
SlideSorterModel::GetCoreIndex (const sal_Int32 nIndex
) const
276 SharedPageDescriptor
pDescriptor (GetPageDescriptor(nIndex
));
278 return pDescriptor
->GetPage()->GetPageNum();
280 return mxSlides
->getCount()*2+1;
283 /** For now this method uses a trivial algorithm: throw away all descriptors
284 and create them anew (on demand). The main problem that we are facing
285 when designing a better algorithm is that we can not compare pointers to
286 pages stored in the PageDescriptor objects and those obtained from the
287 document: pages may have been deleted and others may have been created
288 at the exact same memory locations.
290 void SlideSorterModel::Resync()
292 ::osl::MutexGuard
aGuard (maMutex
);
294 // Check if document and this model really differ.
295 bool bIsUpToDate (true);
296 SdDrawDocument
* pDocument
= GetDocument();
297 if (pDocument
!=nullptr && maPageDescriptors
.size()==pDocument
->GetSdPageCount(PageKind::Standard
))
299 for (sal_Int32 nIndex
=0,nCount
=maPageDescriptors
.size(); nIndex
<nCount
; ++nIndex
)
301 if (maPageDescriptors
[nIndex
]
302 && maPageDescriptors
[nIndex
]->GetPage()
305 SAL_INFO("sd.sls", "page " << nIndex
<< " differs");
318 SynchronizeDocumentSelection(); // Try to make the current selection persistent.
319 ClearDescriptorList ();
321 SynchronizeModelSelection();
322 mrSlideSorter
.GetController().GetPageSelector().CountSelectedPages();
327 void SlideSorterModel::ClearDescriptorList()
329 ::std::vector
<SharedPageDescriptor
> aDescriptors
;
332 ::osl::MutexGuard
aGuard (maMutex
);
333 aDescriptors
.swap(maPageDescriptors
);
336 for (auto& rxDescriptor
: aDescriptors
)
338 if (rxDescriptor
!= nullptr)
340 if (rxDescriptor
.use_count() > 1)
344 "trying to delete page descriptor that is still used with"
345 " count " << rxDescriptor
.use_count());
346 // No assertion here because that can hang the office when
347 // opening a dialog from here.
349 rxDescriptor
.reset();
354 void SlideSorterModel::SynchronizeDocumentSelection()
356 ::osl::MutexGuard
aGuard (maMutex
);
358 PageEnumeration
aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this));
359 while (aAllPages
.HasMoreElements())
361 SharedPageDescriptor
pDescriptor (aAllPages
.GetNextElement());
362 const bool bIsSelected (pDescriptor
->HasState(PageDescriptor::ST_Selected
));
363 pDescriptor
->GetPage()->SetSelected(bIsSelected
);
367 void SlideSorterModel::SynchronizeModelSelection()
369 ::osl::MutexGuard
aGuard (maMutex
);
371 PageEnumeration
aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this));
372 while (aAllPages
.HasMoreElements())
374 SharedPageDescriptor
pDescriptor (aAllPages
.GetNextElement());
375 const bool bIsSelected (pDescriptor
->GetPage()->IsSelected());
376 pDescriptor
->SetState(PageDescriptor::ST_Selected
, bIsSelected
);
380 void SlideSorterModel::SetDocumentSlides (
381 const Reference
<container::XIndexAccess
>& rxSlides
)
383 ::osl::MutexGuard
aGuard (maMutex
);
385 // Make the current selection persistent and then release the
386 // current set of pages.
387 SynchronizeDocumentSelection();
389 ClearDescriptorList ();
391 // Reset the current page to cause everybody to release references to it.
392 mrSlideSorter
.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(-1);
394 // Set the new set of pages.
397 SynchronizeModelSelection();
398 mrSlideSorter
.GetController().GetPageSelector().CountSelectedPages();
400 model::PageEnumeration
aSelectedPages (
401 model::PageEnumerationProvider::CreateSelectedPagesEnumeration(*this));
402 if (aSelectedPages
.HasMoreElements())
404 SharedPageDescriptor
pDescriptor (aSelectedPages
.GetNextElement());
405 mrSlideSorter
.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
406 pDescriptor
->GetPage());
409 ViewShell
* pViewShell
= mrSlideSorter
.GetViewShell();
410 if (pViewShell
!= nullptr)
412 SdPage
* pPage
= pViewShell
->getCurrentPage();
413 if (pPage
!= nullptr)
414 mrSlideSorter
.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
418 // No current page. This can only be when the slide sorter is
419 // the main view shell. Get current slide form frame view.
420 const FrameView
* pFrameView
= pViewShell
->GetFrameView();
421 if (pFrameView
!= nullptr)
422 mrSlideSorter
.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
423 pFrameView
->GetSelectedPage());
426 // No frame view. As a last resort use the first slide as
428 mrSlideSorter
.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
434 mrSlideSorter
.GetController().GetSlotManager()->NotifyEditModeChange();
437 Reference
<container::XIndexAccess
> SlideSorterModel::GetDocumentSlides() const
439 ::osl::MutexGuard
aGuard (maMutex
);
443 void SlideSorterModel::UpdatePageList()
445 ::osl::MutexGuard
aGuard (maMutex
);
447 Reference
<container::XIndexAccess
> xPages
;
449 // Get the list of pages according to the edit mode.
450 Reference
<frame::XController
> xController (mrSlideSorter
.GetXController());
451 if (xController
.is())
455 case EditMode::MasterPage
:
457 Reference
<drawing::XMasterPagesSupplier
> xSupplier (
458 xController
->getModel(), UNO_QUERY
);
461 xPages
= xSupplier
->getMasterPages();
468 Reference
<drawing::XDrawPagesSupplier
> xSupplier (
469 xController
->getModel(), UNO_QUERY
);
472 xPages
= xSupplier
->getDrawPages();
478 // We should never get here.
484 mrSlideSorter
.GetController().SetDocumentSlides(xPages
);
487 void SlideSorterModel::AdaptSize()
490 maPageDescriptors
.resize(mxSlides
->getCount());
492 maPageDescriptors
.resize(0);
495 bool SlideSorterModel::IsReadOnly() const
497 if (mrSlideSorter
.GetViewShellBase() != nullptr
498 && mrSlideSorter
.GetViewShellBase()->GetDocShell())
499 return mrSlideSorter
.GetViewShellBase()->GetDocShell()->IsReadOnly();
504 void SlideSorterModel::SaveCurrentSelection()
506 PageEnumeration
aPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this));
507 while (aPages
.HasMoreElements())
509 SharedPageDescriptor
pDescriptor (aPages
.GetNextElement());
510 pDescriptor
->SetState(
511 PageDescriptor::ST_WasSelected
,
512 pDescriptor
->HasState(PageDescriptor::ST_Selected
));
516 vcl::Region
SlideSorterModel::RestoreSelection()
518 vcl::Region aRepaintRegion
;
519 PageEnumeration
aPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this));
520 while (aPages
.HasMoreElements())
522 SharedPageDescriptor
pDescriptor (aPages
.GetNextElement());
523 if (pDescriptor
->SetState(
524 PageDescriptor::ST_Selected
,
525 pDescriptor
->HasState(PageDescriptor::ST_WasSelected
)))
527 aRepaintRegion
.Union(pDescriptor
->GetBoundingBox());
530 return aRepaintRegion
;
533 bool SlideSorterModel::NotifyPageEvent (const SdrPage
* pSdrPage
)
535 ::osl::MutexGuard
aGuard (maMutex
);
537 SdPage
* pPage
= const_cast<SdPage
*>(dynamic_cast<const SdPage
*>(pSdrPage
));
538 if (pPage
== nullptr)
541 // We are only interested in pages that are currently served by this
543 if (pPage
->GetPageKind() != PageKind::Standard
)
545 if (pPage
->IsMasterPage() != (meEditMode
==EditMode::MasterPage
))
548 //NotifyPageEvent is called for add, remove, *and* change position so for
549 //the change position case we must ensure we don't end up with the slide
550 //duplicated in our list
551 bool bSelected
= DeleteSlide(pPage
);
552 if (pPage
->IsInserted())
554 InsertSlide(pPage
, bSelected
);
561 void SlideSorterModel::InsertSlide(SdPage
* pPage
, bool bMarkSelected
)
563 // Find the index at which to insert the given page.
564 sal_uInt16
nCoreIndex (pPage
->GetPageNum());
565 sal_Int32
nIndex (FromCoreIndex(nCoreIndex
));
566 if (pPage
!= GetPage(nIndex
))
569 // Check that the pages in the document before and after the given page
570 // are present in this model.
572 if (GetPage(nIndex
-1) != GetPageDescriptor(nIndex
-1)->GetPage())
574 if (nIndex
< static_cast<sal_Int32
>(maPageDescriptors
.size()) -1)
575 if (GetPage(nIndex
+1) != GetPageDescriptor(nIndex
)->GetPage())
578 auto iter
= maPageDescriptors
.begin() + nIndex
;
580 // Insert the given page at index nIndex
581 iter
= maPageDescriptors
.insert(
583 std::make_shared
<PageDescriptor
>(
584 Reference
<drawing::XDrawPage
>(mxSlides
->getByIndex(nIndex
),UNO_QUERY
),
589 (*iter
)->SetState(PageDescriptor::ST_Selected
, true);
591 // Update page indices.
592 UpdateIndices(nIndex
+1);
595 bool SlideSorterModel::DeleteSlide (const SdPage
* pPage
)
599 // Caution, GetIndex() may be negative since it uses GetPageNumber()-1
600 // for calculation, so do this only when page is inserted, else the
601 // GetPageNumber() will be zero and thus GetIndex() == -1
602 if(pPage
->IsInserted())
604 nIndex
= GetIndex(pPage
);
608 // if not inserted, search for page
609 for(; nIndex
< static_cast<sal_Int32
>(maPageDescriptors
.size()); nIndex
++)
611 if(maPageDescriptors
[nIndex
]->GetPage() == pPage
)
618 bool bMarkedSelected(false);
620 if(nIndex
>= 0 && o3tl::make_unsigned(nIndex
) < maPageDescriptors
.size())
622 if (maPageDescriptors
[nIndex
])
623 if (maPageDescriptors
[nIndex
]->GetPage() != pPage
)
626 auto iter
= maPageDescriptors
.begin() + nIndex
;
627 bMarkedSelected
= (*iter
)->HasState(PageDescriptor::ST_Selected
);
628 maPageDescriptors
.erase(iter
);
629 UpdateIndices(nIndex
);
631 collectUIInformation(OUString::number(nIndex
+ 1), u
"Delete_Slide_or_Page"_ustr
);
633 return bMarkedSelected
;
636 void SlideSorterModel::UpdateIndices (const sal_Int32 nFirstIndex
)
638 for (sal_Int32 nDescriptorIndex
=0,nCount
=maPageDescriptors
.size();
639 nDescriptorIndex
<nCount
;
642 SharedPageDescriptor
& rpDescriptor (maPageDescriptors
[nDescriptorIndex
]);
645 if (nDescriptorIndex
< nFirstIndex
)
647 if (rpDescriptor
->GetPageIndex()!=nDescriptorIndex
)
649 assert(rpDescriptor
->GetPageIndex()==nDescriptorIndex
);
654 rpDescriptor
->SetPageIndex(nDescriptorIndex
);
660 SdPage
* SlideSorterModel::GetPage (const sal_Int32 nSdIndex
) const
662 SdDrawDocument
* pModel
= const_cast<SlideSorterModel
*>(this)->GetDocument();
663 if (pModel
!= nullptr)
665 if (meEditMode
== EditMode::Page
)
666 return pModel
->GetSdPage (static_cast<sal_uInt16
>(nSdIndex
), PageKind::Standard
);
668 return pModel
->GetMasterSdPage (static_cast<sal_uInt16
>(nSdIndex
), PageKind::Standard
);
674 } // end of namespace ::sd::slidesorter::model
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */