LibreOffice Module sd (master) 1
SlideSorterModel.cxx
Go to the documentation of this file.
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 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21
22#include <SlideSorter.hxx>
23#include <sal/log.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>
34
35#include <vcl/uitest/logger.hxx>
37
38#include <ViewShellBase.hxx>
39#include <DrawDocShell.hxx>
40#include <drawdoc.hxx>
41#include <sdpage.hxx>
42#include <FrameView.hxx>
43
44#include <o3tl/safeint.hxx>
46
47using namespace ::com::sun::star;
48using namespace ::com::sun::star::uno;
49
50namespace sd::slidesorter::model {
51
52namespace {
53 bool PrintModel (const SlideSorterModel& rModel)
54 {
55 for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndex<nCount; ++nIndex)
56 {
57 SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
58 if (pDescriptor)
59 {
61 "sd.sls",
62 nIndex << " " << pDescriptor->GetPageIndex() << " "
63 << pDescriptor->GetVisualState().mnPageId << " "
64 << FromCoreIndex(pDescriptor->GetPage()->GetPageNum())
65 << " " << pDescriptor->GetPage());
66 }
67 else
68 {
69 SAL_INFO("sd.sls", nIndex);
70 }
71 }
72
73 return true;
74 }
75 bool CheckModel (const SlideSorterModel& rModel)
76 {
77 for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndex<nCount; ++nIndex)
78 {
79 SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
80 if ( ! pDescriptor)
81 {
82 PrintModel(rModel);
83 assert(pDescriptor);
84 return false;
85 }
86 if (nIndex != pDescriptor->GetPageIndex())
87 {
88 PrintModel(rModel);
89 assert(nIndex == pDescriptor->GetPageIndex());
90 return false;
91 }
92 if (nIndex != pDescriptor->GetVisualState().mnPageId)
93 {
94 PrintModel(rModel);
95 assert(nIndex == pDescriptor->GetVisualState().mnPageId);
96 return false;
97 }
98 }
99
100 return true;
101 }
102}
103
104namespace {
105
106void collectUIInformation(const OUString& num, const OUString& rAction)
107{
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";
114
115 UITestLogger::getInstance().logEvent(aDescription);
116}
117
118}
119
121 : mrSlideSorter(rSlideSorter),
122 meEditMode(EditMode::Page),
123 maPageDescriptors(0)
124{
125}
126
128{
130}
131
133{
135}
136
138{
139 if (mrSlideSorter.GetViewShellBase() != nullptr)
141 else
142 return nullptr;
143}
144
146{
147 bool bEditModeChanged = false;
148 if (meEditMode != eEditMode)
149 {
150 meEditMode = eEditMode;
152 bEditModeChanged = true;
153 }
154 return bEditModeChanged;
155}
156
158{
159 return maPageDescriptors.size();
160}
161
163 const sal_Int32 nPageIndex,
164 const bool bCreate) const
165{
166 ::osl::MutexGuard aGuard (maMutex);
167
168 SharedPageDescriptor pDescriptor;
169
170 if (nPageIndex>=0 && nPageIndex<GetPageCount())
171 {
172 pDescriptor = maPageDescriptors[nPageIndex];
173 if (pDescriptor == nullptr && bCreate && mxSlides.is())
174 {
175 SdPage* pPage = GetPage(nPageIndex);
176 pDescriptor = std::make_shared<PageDescriptor>(
177 Reference<drawing::XDrawPage>(mxSlides->getByIndex(nPageIndex),UNO_QUERY),
178 pPage,
179 nPageIndex);
180 maPageDescriptors[nPageIndex] = pDescriptor;
181 }
182 }
183
184 return pDescriptor;
185}
186
187sal_Int32 SlideSorterModel::GetIndex (const Reference<drawing::XDrawPage>& rxSlide) const
188{
189 ::osl::MutexGuard aGuard (maMutex);
190
191 // First try to guess the right index.
192 Reference<beans::XPropertySet> xSet (rxSlide, UNO_QUERY);
193 if (xSet.is())
194 {
195 try
196 {
197 const Any aNumber (xSet->getPropertyValue("Number"));
198 sal_Int16 nNumber (-1);
199 aNumber >>= nNumber;
200 nNumber -= 1;
201 SharedPageDescriptor pDescriptor (GetPageDescriptor(nNumber, false));
202 if (pDescriptor
203 && pDescriptor->GetXDrawPage() == rxSlide)
204 {
205 return nNumber;
206 }
207 }
208 catch (uno::Exception&)
209 {
211 }
212 }
213
214 // Guess was wrong, iterate over all slides and search for the right
215 // one.
216 const sal_Int32 nCount (maPageDescriptors.size());
217 for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
218 {
219 SharedPageDescriptor pDescriptor (maPageDescriptors[nIndex]);
220
221 // Make sure that the descriptor exists. Without it the given slide
222 // can not be found.
223 if (!pDescriptor)
224 {
225 // Call GetPageDescriptor() to create the missing descriptor.
226 pDescriptor = GetPageDescriptor(nIndex);
227 }
228
229 if (pDescriptor->GetXDrawPage() == rxSlide)
230 return nIndex;
231 }
232
233 return -1;
234}
235
236sal_Int32 SlideSorterModel::GetIndex (const SdrPage* pPage) const
237{
238 if (pPage == nullptr)
239 return -1;
240
241 ::osl::MutexGuard aGuard (maMutex);
242
243 // First try to guess the right index.
244 sal_Int16 nNumber ((pPage->GetPageNum()-1)/2);
245 SharedPageDescriptor pDescriptor (GetPageDescriptor(nNumber, false));
246 if (pDescriptor
247 && pDescriptor->GetPage() == pPage)
248 {
249 return nNumber;
250 }
251
252 // Guess was wrong, iterate over all slides and search for the right
253 // one.
254 const sal_Int32 nCount (maPageDescriptors.size());
255 for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
256 {
257 pDescriptor = maPageDescriptors[nIndex];
258
259 // Make sure that the descriptor exists. Without it the given slide
260 // can not be found.
261 if (!pDescriptor)
262 {
263 // Call GetPageDescriptor() to create the missing descriptor.
264 pDescriptor = GetPageDescriptor(nIndex);
265 }
266
267 if (pDescriptor->GetPage() == pPage)
268 return nIndex;
269 }
270
271 return -1;
272}
273
274sal_uInt16 SlideSorterModel::GetCoreIndex (const sal_Int32 nIndex) const
275{
277 if (pDescriptor)
278 return pDescriptor->GetPage()->GetPageNum();
279 else
280 return mxSlides->getCount()*2+1;
281}
282
291{
292 ::osl::MutexGuard aGuard (maMutex);
293
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))
298 {
299 for (sal_Int32 nIndex=0,nCount=maPageDescriptors.size(); nIndex<nCount; ++nIndex)
300 {
303 != GetPage(nIndex))
304 {
305 SAL_INFO("sd.sls", "page " << nIndex << " differs");
306 bIsUpToDate = false;
307 break;
308 }
309 }
310 }
311 else
312 {
313 bIsUpToDate = false;
314 }
315
316 if ( ! bIsUpToDate)
317 {
318 SynchronizeDocumentSelection(); // Try to make the current selection persistent.
320 AdaptSize();
323 }
324 CheckModel(*this);
325}
326
328{
329 ::std::vector<SharedPageDescriptor> aDescriptors;
330
331 {
332 ::osl::MutexGuard aGuard (maMutex);
333 aDescriptors.swap(maPageDescriptors);
334 }
335
336 for (auto& rxDescriptor : aDescriptors)
337 {
338 if (rxDescriptor != nullptr)
339 {
340 if (rxDescriptor.use_count() > 1)
341 {
342 SAL_INFO(
343 "sd.sls",
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.
348 }
349 rxDescriptor.reset();
350 }
351 }
352}
353
355{
356 ::osl::MutexGuard aGuard (maMutex);
357
359 while (aAllPages.HasMoreElements())
360 {
361 SharedPageDescriptor pDescriptor (aAllPages.GetNextElement());
362 const bool bIsSelected (pDescriptor->HasState(PageDescriptor::ST_Selected));
363 pDescriptor->GetPage()->SetSelected(bIsSelected);
364 }
365}
366
368{
369 ::osl::MutexGuard aGuard (maMutex);
370
372 while (aAllPages.HasMoreElements())
373 {
374 SharedPageDescriptor pDescriptor (aAllPages.GetNextElement());
375 const bool bIsSelected (pDescriptor->GetPage()->IsSelected());
376 pDescriptor->SetState(PageDescriptor::ST_Selected, bIsSelected);
377 }
378}
379
381 const Reference<container::XIndexAccess>& rxSlides)
382{
383 ::osl::MutexGuard aGuard (maMutex);
384
385 // Make the current selection persistent and then release the
386 // current set of pages.
388 mxSlides = nullptr;
390
391 // Reset the current page to cause everybody to release references to it.
392 mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(-1);
393
394 // Set the new set of pages.
395 mxSlides = rxSlides;
396 AdaptSize();
399
400 model::PageEnumeration aSelectedPages (
402 if (aSelectedPages.HasMoreElements())
403 {
404 SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement());
405 mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
406 pDescriptor->GetPage());
407 }
408
409 ViewShell* pViewShell = mrSlideSorter.GetViewShell();
410 if (pViewShell != nullptr)
411 {
412 SdPage* pPage = pViewShell->getCurrentPage();
413 if (pPage != nullptr)
414 mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
415 pPage);
416 else
417 {
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());
424 else
425 {
426 // No frame view. As a last resort use the first slide as
427 // current slide.
428 mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
429 sal_Int32(0));
430 }
431 }
432 }
433
434 mrSlideSorter.GetController().GetSlotManager()->NotifyEditModeChange();
435}
436
437Reference<container::XIndexAccess> SlideSorterModel::GetDocumentSlides() const
438{
439 ::osl::MutexGuard aGuard (maMutex);
440 return mxSlides;
441}
442
444{
445 ::osl::MutexGuard aGuard (maMutex);
446
447 Reference<container::XIndexAccess> xPages;
448
449 // Get the list of pages according to the edit mode.
450 Reference<frame::XController> xController (mrSlideSorter.GetXController());
451 if (xController.is())
452 {
453 switch (meEditMode)
454 {
456 {
457 Reference<drawing::XMasterPagesSupplier> xSupplier (
458 xController->getModel(), UNO_QUERY);
459 if (xSupplier.is())
460 {
461 xPages = xSupplier->getMasterPages();
462 }
463 }
464 break;
465
466 case EditMode::Page:
467 {
468 Reference<drawing::XDrawPagesSupplier> xSupplier (
469 xController->getModel(), UNO_QUERY);
470 if (xSupplier.is())
471 {
472 xPages = xSupplier->getDrawPages();
473 }
474 }
475 break;
476
477 default:
478 // We should never get here.
479 assert(false);
480 break;
481 }
482 }
483
485}
486
488{
489 if (mxSlides.is())
490 maPageDescriptors.resize(mxSlides->getCount());
491 else
492 maPageDescriptors.resize(0);
493}
494
496{
497 if (mrSlideSorter.GetViewShellBase() != nullptr
500 else
501 return true;
502}
503
505{
507 while (aPages.HasMoreElements())
508 {
509 SharedPageDescriptor pDescriptor (aPages.GetNextElement());
510 pDescriptor->SetState(
512 pDescriptor->HasState(PageDescriptor::ST_Selected));
513 }
514}
515
517{
518 vcl::Region aRepaintRegion;
520 while (aPages.HasMoreElements())
521 {
522 SharedPageDescriptor pDescriptor (aPages.GetNextElement());
523 if (pDescriptor->SetState(
525 pDescriptor->HasState(PageDescriptor::ST_WasSelected)))
526 {
527 aRepaintRegion.Union(pDescriptor->GetBoundingBox());
528 }
529 }
530 return aRepaintRegion;
531}
532
534{
535 ::osl::MutexGuard aGuard (maMutex);
536
537 SdPage* pPage = const_cast<SdPage*>(dynamic_cast<const SdPage*>(pSdrPage));
538 if (pPage == nullptr)
539 return false;
540
541 // We are only interested in pages that are currently served by this
542 // model.
543 if (pPage->GetPageKind() != PageKind::Standard)
544 return false;
546 return false;
547
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())
553 {
554 InsertSlide(pPage, bSelected);
555 }
556 CheckModel(*this);
557
558 return true;
559}
560
561void SlideSorterModel::InsertSlide(SdPage* pPage, bool bMarkSelected)
562{
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))
567 return;
568
569 // Check that the pages in the document before and after the given page
570 // are present in this model.
571 if (nIndex>0)
573 return;
574 if (nIndex < static_cast<sal_Int32>(maPageDescriptors.size()) -1)
576 return;
577
578 auto iter = maPageDescriptors.begin() + nIndex;
579
580 // Insert the given page at index nIndex
581 iter = maPageDescriptors.insert(
582 iter,
583 std::make_shared<PageDescriptor>(
584 Reference<drawing::XDrawPage>(mxSlides->getByIndex(nIndex),UNO_QUERY),
585 pPage,
586 nIndex));
587
588 if (bMarkSelected)
589 (*iter)->SetState(PageDescriptor::ST_Selected, true);
590
591 // Update page indices.
593}
594
596{
597 sal_Int32 nIndex(0);
598
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())
603 {
604 nIndex = GetIndex(pPage);
605 }
606 else
607 {
608 // if not inserted, search for page
609 for(; nIndex < static_cast<sal_Int32>(maPageDescriptors.size()); nIndex++)
610 {
611 if(maPageDescriptors[nIndex]->GetPage() == pPage)
612 {
613 break;
614 }
615 }
616 }
617
618 bool bMarkedSelected(false);
619
621 {
623 if (maPageDescriptors[nIndex]->GetPage() != pPage)
624 return false;
625
626 auto iter = maPageDescriptors.begin() + nIndex;
627 bMarkedSelected = (*iter)->HasState(PageDescriptor::ST_Selected);
628 maPageDescriptors.erase(iter);
630
631 collectUIInformation(OUString::number(nIndex + 1), "Delete_Slide_or_Page");
632 }
633 return bMarkedSelected;
634}
635
636void SlideSorterModel::UpdateIndices (const sal_Int32 nFirstIndex)
637{
638 for (sal_Int32 nDescriptorIndex=0,nCount=maPageDescriptors.size();
639 nDescriptorIndex<nCount;
640 ++nDescriptorIndex)
641 {
642 SharedPageDescriptor& rpDescriptor (maPageDescriptors[nDescriptorIndex]);
643 if (rpDescriptor)
644 {
645 if (nDescriptorIndex < nFirstIndex)
646 {
647 if (rpDescriptor->GetPageIndex()!=nDescriptorIndex)
648 {
649 assert(rpDescriptor->GetPageIndex()==nDescriptorIndex);
650 }
651 }
652 else
653 {
654 rpDescriptor->SetPageIndex(nDescriptorIndex);
655 }
656 }
657 }
658}
659
660SdPage* SlideSorterModel::GetPage (const sal_Int32 nSdIndex) const
661{
662 SdDrawDocument* pModel = const_cast<SlideSorterModel*>(this)->GetDocument();
663 if (pModel != nullptr)
664 {
666 return pModel->GetSdPage (static_cast<sal_uInt16>(nSdIndex), PageKind::Standard);
667 else
668 return pModel->GetMasterSdPage (static_cast<sal_uInt16>(nSdIndex), PageKind::Standard);
669 }
670 else
671 return nullptr;
672}
673
674} // end of namespace ::sd::slidesorter::model
675
676/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Page
SlideSorter & mrSlideSorter
SdPage * GetSdPage(sal_uInt16 nPgNum, PageKind ePgKind) const
Definition: drawdoc2.cxx:207
SdPage * GetMasterSdPage(sal_uInt16 nPgNum, PageKind ePgKind)
Definition: drawdoc2.cxx:217
sal_uInt16 GetSdPageCount(PageKind ePgKind) const
Definition: drawdoc2.cxx:212
PageKind GetPageKind() const
Definition: sdpage.hxx:205
bool IsInserted() const
sal_uInt16 GetPageNum() const
bool IsMasterPage() const
bool IsReadOnly() const
static UITestLogger & getInstance()
void logEvent(const EventDescription &rDescription)
View for MDIFrame.
Definition: FrameView.hxx:36
sal_uInt16 GetSelectedPage() const
Definition: FrameView.hxx:96
DrawDocShell * GetDocShell() const
SdDrawDocument * GetDocument() const
Base class of the stacked shell hierarchy.
Definition: ViewShell.hxx:92
virtual SdPage * getCurrentPage() const =0
FrameView * GetFrameView()
Definition: ViewShell.hxx:221
Show previews for all the slides in a document and allow the user to insert or delete slides and modi...
Definition: SlideSorter.hxx:62
css::uno::Reference< css::frame::XController > GetXController() const
Return the XController object of the main view.
SD_DLLPUBLIC controller::SlideSorterController & GetController() const
ViewShell * GetViewShell() const
Return the view shell that was given at construction.
ViewShellBase * GetViewShellBase() const
Return the ViewShellBase object.
void CountSelectedPages()
Call this method after the model has changed to set the number of selected pages.
std::shared_ptr< SlotManager > const & GetSlotManager() const
std::shared_ptr< CurrentSlideManager > const & GetCurrentSlideManager() const
void SetDocumentSlides(const css::uno::Reference< css::container::XIndexAccess > &rxSlides)
Provide the set of pages to be displayed in the slide sorter.
static PageEnumeration CreateAllPagesEnumeration(const SlideSorterModel &rModel)
The returned enumeration of slides iterates over all slides of the given model.
static PageEnumeration CreateSelectedPagesEnumeration(const SlideSorterModel &rModel)
The returned enumeration of slides iterates over the currently selected slides of the given model.
Public class of page enumerations that delegates its calls to an implementation object that can filte...
virtual SharedPageDescriptor GetNextElement() override
Return the next element of the enumeration.
virtual bool HasMoreElements() const override
Return <TRUE> when the enumeration has more elements, i.e.
The model of the slide sorter gives access to the slides that are to be displayed in the slide sorter...
void SynchronizeModelSelection()
Set the selection of the called model to exactly that of the document.
bool SetEditMode(EditMode eEditMode)
Set a new edit mode and return whether the edit mode really has been changed.
bool NotifyPageEvent(const SdrPage *pPage)
Typically called from controller::Listener this method handles the insertion and deletion of single p...
SlideSorterModel(SlideSorter &rSlideSorter)
void UpdatePageList()
This method is called when the edit mode has changed.
SdDrawDocument * GetDocument()
This method is present to let the view create a ShowView for displaying slides.
sal_Int32 GetPageCount() const
Return the number of slides in the document regardless of whether they are visible or not or whether ...
css::uno::Reference< css::container::XIndexAccess > GetDocumentSlides() const
Return the set of pages that is currently displayed by the slide sorter.
mutable ::std::vector< SharedPageDescriptor > maPageDescriptors
css::uno::Reference< css::container::XIndexAccess > mxSlides
SharedPageDescriptor GetPageDescriptor(const sal_Int32 nPageIndex, const bool bCreate=true) const
Return a page descriptor for the page with the specified index.
void SetDocumentSlides(const css::uno::Reference< css::container::XIndexAccess > &rxSlides)
Set the XIndexAccess from which the called SlideSorterModel takes its pages.
void SaveCurrentSelection()
The current selection is saved by copying the ST_Selected state into ST_WasSelected for slides.
vcl::Region RestoreSelection()
The current selection is restored from the ST_WasSelected state from the slides.
void ClearDescriptorList()
Delete all descriptors that currently are in the container.
sal_Int32 GetIndex(const css::uno::Reference< css::drawing::XDrawPage > &rxSlide) const
Return a page descriptor for the given XDrawPage.
void SynchronizeDocumentSelection()
Set the selection of the document to exactly that of the called model.
void UpdateIndices(const sal_Int32 nFirstIndex)
void Resync()
Call this method after the document has changed its structure.
SdPage * GetPage(const sal_Int32 nCoreIndex) const
void AdaptSize()
Resize the descriptor container according to current values of page kind and edit mode.
void InsertSlide(SdPage *pPage, bool bMarkSelected)
sal_uInt16 GetCoreIndex(const sal_Int32 nIndex) const
Return an index for accessing an SdrModel that corresponds to the given SlideSorterModel index.
void Union(const tools::Rectangle &rRegion)
int nCount
#define DBG_UNHANDLED_EXCEPTION(...)
sal_Int32 nIndex
#define SAL_INFO(area, stream)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
sal_Int32 FromCoreIndex(const sal_uInt16 nCoreIndex)
std::shared_ptr< PageDescriptor > SharedPageDescriptor
EditMode
Definition: pres.hxx:53
std::map< OUString, OUString > aParameters
Reference< XController > xController