tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sd / source / ui / slidesorter / model / SlideSorterModel.cxx
blob4dcfefb30971fb1431c3717381e6179c2fe2e18e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
41 #include <sdpage.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 {
52 namespace {
53 bool PrintModel (const SlideSorterModel& rModel)
55 for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndex<nCount; ++nIndex)
57 SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
58 if (pDescriptor)
60 SAL_INFO(
61 "sd.sls",
62 nIndex << " " << pDescriptor->GetPageIndex() << " "
63 << pDescriptor->GetVisualState().mnPageId << " "
64 << FromCoreIndex(pDescriptor->GetPage()->GetPageNum())
65 << " " << pDescriptor->GetPage());
67 else
69 SAL_INFO("sd.sls", nIndex);
73 return true;
75 bool CheckModel (const SlideSorterModel& rModel)
77 for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndex<nCount; ++nIndex)
79 SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex));
80 if ( ! pDescriptor)
82 PrintModel(rModel);
83 assert(pDescriptor);
84 return false;
86 if (nIndex != pDescriptor->GetPageIndex())
88 PrintModel(rModel);
89 assert(nIndex == pDescriptor->GetPageIndex());
90 return false;
92 if (nIndex != pDescriptor->GetVisualState().mnPageId)
94 PrintModel(rModel);
95 assert(nIndex == pDescriptor->GetVisualState().mnPageId);
96 return false;
100 return true;
104 namespace {
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),
123 maPageDescriptors(0)
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();
141 else
142 return nullptr;
145 bool SlideSorterModel::SetEditMode (EditMode eEditMode)
147 bool bEditModeChanged = false;
148 if (meEditMode != eEditMode)
150 meEditMode = eEditMode;
151 UpdatePageList();
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),
178 pPage,
179 nPageIndex);
180 maPageDescriptors[nPageIndex] = pDescriptor;
184 return 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);
193 if (xSet.is())
197 const Any aNumber (xSet->getPropertyValue(u"Number"_ustr));
198 sal_Int16 nNumber (-1);
199 aNumber >>= nNumber;
200 nNumber -= 1;
201 SharedPageDescriptor pDescriptor (GetPageDescriptor(nNumber, false));
202 if (pDescriptor
203 && pDescriptor->GetXDrawPage() == rxSlide)
205 return nNumber;
208 catch (uno::Exception&)
210 DBG_UNHANDLED_EXCEPTION("sd");
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)
219 SharedPageDescriptor pDescriptor (maPageDescriptors[nIndex]);
221 // Make sure that the descriptor exists. Without it the given slide
222 // can not be found.
223 if (!pDescriptor)
225 // Call GetPageDescriptor() to create the missing descriptor.
226 pDescriptor = GetPageDescriptor(nIndex);
229 if (pDescriptor->GetXDrawPage() == rxSlide)
230 return nIndex;
233 return -1;
236 sal_Int32 SlideSorterModel::GetIndex (const SdrPage* pPage) const
238 if (pPage == nullptr)
239 return -1;
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));
246 if (pDescriptor
247 && pDescriptor->GetPage() == pPage)
249 return nNumber;
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)
257 pDescriptor = maPageDescriptors[nIndex];
259 // Make sure that the descriptor exists. Without it the given slide
260 // can not be found.
261 if (!pDescriptor)
263 // Call GetPageDescriptor() to create the missing descriptor.
264 pDescriptor = GetPageDescriptor(nIndex);
267 if (pDescriptor->GetPage() == pPage)
268 return nIndex;
271 return -1;
274 sal_uInt16 SlideSorterModel::GetCoreIndex (const sal_Int32 nIndex) const
276 SharedPageDescriptor pDescriptor (GetPageDescriptor(nIndex));
277 if (pDescriptor)
278 return pDescriptor->GetPage()->GetPageNum();
279 else
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()
303 != GetPage(nIndex))
305 SAL_INFO("sd.sls", "page " << nIndex << " differs");
306 bIsUpToDate = false;
307 break;
311 else
313 bIsUpToDate = false;
316 if ( ! bIsUpToDate)
318 SynchronizeDocumentSelection(); // Try to make the current selection persistent.
319 ClearDescriptorList ();
320 AdaptSize();
321 SynchronizeModelSelection();
322 mrSlideSorter.GetController().GetPageSelector().CountSelectedPages();
324 CheckModel(*this);
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)
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.
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();
388 mxSlides = nullptr;
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.
395 mxSlides = rxSlides;
396 AdaptSize();
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(
415 pPage);
416 else
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
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));
434 mrSlideSorter.GetController().GetSlotManager()->NotifyEditModeChange();
437 Reference<container::XIndexAccess> SlideSorterModel::GetDocumentSlides() const
439 ::osl::MutexGuard aGuard (maMutex);
440 return mxSlides;
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())
453 switch (meEditMode)
455 case EditMode::MasterPage:
457 Reference<drawing::XMasterPagesSupplier> xSupplier (
458 xController->getModel(), UNO_QUERY);
459 if (xSupplier.is())
461 xPages = xSupplier->getMasterPages();
464 break;
466 case EditMode::Page:
468 Reference<drawing::XDrawPagesSupplier> xSupplier (
469 xController->getModel(), UNO_QUERY);
470 if (xSupplier.is())
472 xPages = xSupplier->getDrawPages();
475 break;
477 default:
478 // We should never get here.
479 assert(false);
480 break;
484 mrSlideSorter.GetController().SetDocumentSlides(xPages);
487 void SlideSorterModel::AdaptSize()
489 if (mxSlides.is())
490 maPageDescriptors.resize(mxSlides->getCount());
491 else
492 maPageDescriptors.resize(0);
495 bool SlideSorterModel::IsReadOnly() const
497 if (mrSlideSorter.GetViewShellBase() != nullptr
498 && mrSlideSorter.GetViewShellBase()->GetDocShell())
499 return mrSlideSorter.GetViewShellBase()->GetDocShell()->IsReadOnly();
500 else
501 return true;
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)
539 return false;
541 // We are only interested in pages that are currently served by this
542 // model.
543 if (pPage->GetPageKind() != PageKind::Standard)
544 return false;
545 if (pPage->IsMasterPage() != (meEditMode==EditMode::MasterPage))
546 return false;
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);
556 CheckModel(*this);
558 return true;
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))
567 return;
569 // Check that the pages in the document before and after the given page
570 // are present in this model.
571 if (nIndex>0)
572 if (GetPage(nIndex-1) != GetPageDescriptor(nIndex-1)->GetPage())
573 return;
574 if (nIndex < static_cast<sal_Int32>(maPageDescriptors.size()) -1)
575 if (GetPage(nIndex+1) != GetPageDescriptor(nIndex)->GetPage())
576 return;
578 auto iter = maPageDescriptors.begin() + nIndex;
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));
588 if (bMarkSelected)
589 (*iter)->SetState(PageDescriptor::ST_Selected, true);
591 // Update page indices.
592 UpdateIndices(nIndex+1);
595 bool SlideSorterModel::DeleteSlide (const SdPage* pPage)
597 sal_Int32 nIndex(0);
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);
606 else
608 // if not inserted, search for page
609 for(; nIndex < static_cast<sal_Int32>(maPageDescriptors.size()); nIndex++)
611 if(maPageDescriptors[nIndex]->GetPage() == pPage)
613 break;
618 bool bMarkedSelected(false);
620 if(nIndex >= 0 && o3tl::make_unsigned(nIndex) < maPageDescriptors.size())
622 if (maPageDescriptors[nIndex])
623 if (maPageDescriptors[nIndex]->GetPage() != pPage)
624 return false;
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;
640 ++nDescriptorIndex)
642 SharedPageDescriptor& rpDescriptor (maPageDescriptors[nDescriptorIndex]);
643 if (rpDescriptor)
645 if (nDescriptorIndex < nFirstIndex)
647 if (rpDescriptor->GetPageIndex()!=nDescriptorIndex)
649 assert(rpDescriptor->GetPageIndex()==nDescriptorIndex);
652 else
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);
667 else
668 return pModel->GetMasterSdPage (static_cast<sal_uInt16>(nSdIndex), PageKind::Standard);
670 else
671 return nullptr;
674 } // end of namespace ::sd::slidesorter::model
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */