bump product version to 6.3.0.0.beta1
[LibreOffice.git] / sd / source / ui / sidebar / MasterPagesSelector.cxx
blob78c85363ca21841b562900d7aa3571781e412232
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 <sal/config.h>
22 #include <utility>
24 #include "MasterPagesSelector.hxx"
26 #include "MasterPageContainer.hxx"
27 #include "DocumentHelper.hxx"
28 #include <pres.hxx>
29 #include <drawdoc.hxx>
30 #include <DrawDocShell.hxx>
31 #include <sdpage.hxx>
32 #include <glob.hxx>
33 #include <app.hrc>
35 #include <DrawViewShell.hxx>
36 #include <DrawController.hxx>
37 #include <SlideSorterViewShell.hxx>
38 #include "PreviewValueSet.hxx"
39 #include <ViewShellBase.hxx>
40 #include <sfx2/objface.hxx>
41 #include <drawview.hxx>
42 #include <vcl/commandevent.hxx>
43 #include <vcl/image.hxx>
44 #include <vcl/floatwin.hxx>
45 #include <svl/languageoptions.hxx>
46 #include <sfx2/app.hxx>
47 #include <sfx2/dispatch.hxx>
48 #include <sfx2/viewfrm.hxx>
49 #include <svl/itemset.hxx>
50 #include <svl/eitem.hxx>
51 #include <svx/dlgutil.hxx>
52 #include <svx/svdpagv.hxx>
53 #include <svx/svxids.hrc>
54 #include <FrameView.hxx>
55 #include <stlpool.hxx>
56 #include <unmovss.hxx>
57 #include <sfx2/request.hxx>
58 #include <svl/itempool.hxx>
59 #include <sfx2/sidebar/Theme.hxx>
60 #include <memory>
62 using namespace ::com::sun::star::text;
64 namespace sd { namespace sidebar {
66 /** menu entry that is executed as default action when the left mouse button is
67 clicked over a master page.
69 static const char gsDefaultClickAction[] = "applyselect";
71 MasterPagesSelector::MasterPagesSelector (
72 vcl::Window* pParent,
73 SdDrawDocument& rDocument,
74 ViewShellBase& rBase,
75 const std::shared_ptr<MasterPageContainer>& rpContainer,
76 const css::uno::Reference<css::ui::XSidebar>& rxSidebar)
77 : PreviewValueSet(pParent),
78 maMutex(),
79 mpContainer(rpContainer),
80 mrDocument(rDocument),
81 mrBase(rBase),
82 maCurrentItemList(),
83 maTokenToValueSetIndex(),
84 maLockedMasterPages(),
85 mxSidebar(rxSidebar)
87 PreviewValueSet::SetSelectHdl (
88 LINK(this, MasterPagesSelector, ClickHandler));
89 PreviewValueSet::SetRightMouseClickHandler (
90 LINK(this, MasterPagesSelector, RightClickHandler));
91 PreviewValueSet::SetStyle(PreviewValueSet::GetStyle() | WB_NO_DIRECTSELECT);
93 if ( GetDPIScaleFactor() > 1 )
94 mpContainer->SetPreviewSize(MasterPageContainer::LARGE);
96 PreviewValueSet::SetPreviewSize(mpContainer->GetPreviewSizePixel());
97 PreviewValueSet::Show();
99 SetBackground(sfx2::sidebar::Theme::GetWallpaper(sfx2::sidebar::Theme::Paint_PanelBackground));
100 SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Paint_PanelBackground));
102 Link<MasterPageContainerChangeEvent&,void> aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener));
103 mpContainer->AddChangeListener(aChangeListener);
106 MasterPagesSelector::~MasterPagesSelector()
108 disposeOnce();
111 void MasterPagesSelector::dispose()
113 Clear();
114 UpdateLocks(ItemList());
116 Link<MasterPageContainerChangeEvent&,void> aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener));
117 mpContainer->RemoveChangeListener(aChangeListener);
118 mpContainer.reset();
119 PreviewValueSet::dispose();
122 void MasterPagesSelector::LateInit()
126 sal_Int32 MasterPagesSelector::GetPreferredHeight (sal_Int32 nWidth)
128 const ::osl::MutexGuard aGuard (maMutex);
130 return PreviewValueSet::GetPreferredHeight (nWidth);
133 void MasterPagesSelector::UpdateLocks (const ItemList& rItemList)
135 ItemList aNewLockList;
137 // In here we first lock the master pages in the given list and then
138 // release the locks acquired in a previous call to this method. When
139 // this were done the other way round the lock count of some master
140 // pages might drop temporarily to 0 and would lead to unnecessary
141 // deletion and re-creation of MasterPageDescriptor objects.
143 // Lock the master pages in the given list.
144 for (const auto& rItem : rItemList)
146 mpContainer->AcquireToken(rItem);
147 aNewLockList.push_back(rItem);
150 // Release the previously locked master pages.
151 for (const auto& rPage : maLockedMasterPages)
152 mpContainer->ReleaseToken(rPage);
154 maLockedMasterPages.swap(aNewLockList);
157 void MasterPagesSelector::Fill()
159 ::std::unique_ptr<ItemList> pItemList (new ItemList);
161 Fill(*pItemList);
163 UpdateLocks(*pItemList);
164 UpdateItemList(std::move(pItemList));
167 OUString MasterPagesSelector::GetContextMenuUIFile() const
169 return OUString("modules/simpress/ui/mastermenu.ui");
172 IMPL_LINK_NOARG(MasterPagesSelector, ClickHandler, ValueSet*, void)
174 // We use the framework to assign the clicked-on master page because we
175 // so use the same mechanism as the context menu does (where we do not
176 // have the option to call the assignment method directly.)
177 ExecuteCommand(gsDefaultClickAction);
180 IMPL_LINK(MasterPagesSelector, RightClickHandler, const MouseEvent&, rEvent, void)
182 // Here we only prepare the display of the context menu: the item under
183 // the mouse is selected. The actual display of the context menu is
184 // done in ContextMenuCallback which is called indirectly through
185 // PreviewValueSet::Command().
186 PreviewValueSet::GrabFocus ();
187 PreviewValueSet::ReleaseMouse();
188 SfxViewFrame* pViewFrame = mrBase.GetViewFrame();
189 if (pViewFrame != nullptr)
191 SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher();
192 if (pDispatcher != nullptr)
194 sal_uInt16 nIndex = PreviewValueSet::GetItemId (rEvent.GetPosPixel());
195 if (nIndex > 0)
196 PreviewValueSet::SelectItem (nIndex);
201 void MasterPagesSelector::Command (const CommandEvent& rEvent)
203 switch (rEvent.GetCommand())
205 case CommandEventId::ContextMenu:
207 // Use the currently selected item and show the popup menu in its
208 // center.
209 const sal_uInt16 nIndex = PreviewValueSet::GetSelectedItemId();
210 if (nIndex > 0)
212 // The position of the upper left corner of the context menu is
213 // taken either from the mouse position (when the command was sent
214 // as reaction to a right click) or in the center of the selected
215 // item (when the command was sent as reaction to Shift+F10.)
216 Point aPosition (rEvent.GetMousePosPixel());
217 if ( ! rEvent.IsMouseEvent())
219 ::tools::Rectangle aBBox (PreviewValueSet::GetItemRect(nIndex));
220 aPosition = aBBox.Center();
223 // Setup the menu.
224 VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), GetContextMenuUIFile(), "");
225 VclPtr<PopupMenu> pMenu(aBuilder.get_menu("menu"));
226 FloatingWindow* pMenuWindow = dynamic_cast<FloatingWindow*>(pMenu->GetWindow());
227 if (pMenuWindow != nullptr)
228 pMenuWindow->SetPopupModeFlags(
229 pMenuWindow->GetPopupModeFlags() | FloatWinPopupFlags::NoMouseUpClose);
230 pMenu->SetSelectHdl(LINK(this, MasterPagesSelector, OnMenuItemSelected));
232 ProcessPopupMenu(*pMenu);
234 // Show the menu.
235 pMenu->Execute(this, ::tools::Rectangle(aPosition,Size(1,1)), PopupMenuFlags::ExecuteDown);
238 break;
239 default: break;
243 void MasterPagesSelector::ProcessPopupMenu (Menu& rMenu)
245 // Disable some entries.
246 if (mpContainer->GetPreviewSize() == MasterPageContainer::SMALL)
247 rMenu.EnableItem(rMenu.GetItemId("small"), false);
248 else
249 rMenu.EnableItem(rMenu.GetItemId("large"), false);
252 IMPL_LINK(MasterPagesSelector, OnMenuItemSelected, Menu*, pMenu, bool)
254 if (pMenu == nullptr)
256 OSL_ENSURE(pMenu!=nullptr, "MasterPagesSelector::OnMenuItemSelected: illegal menu!");
257 return false;
260 pMenu->Deactivate();
261 ExecuteCommand(pMenu->GetCurItemIdent());
262 return false;
265 void MasterPagesSelector::ExecuteCommand(const OString &rIdent)
267 if (rIdent == "applyall")
269 mrBase.SetBusyState (true);
270 AssignMasterPageToAllSlides (GetSelectedMasterPage());
271 mrBase.SetBusyState (false);
273 else if (rIdent == "applyselect")
275 mrBase.SetBusyState (true);
276 AssignMasterPageToSelectedSlides (GetSelectedMasterPage());
277 mrBase.SetBusyState (false);
279 else if (rIdent == "large")
281 mrBase.SetBusyState (true);
282 mpContainer->SetPreviewSize(MasterPageContainer::LARGE);
283 mrBase.SetBusyState (false);
284 if (mxSidebar.is())
285 mxSidebar->requestLayout();
287 else if (rIdent == "small")
289 mrBase.SetBusyState (true);
290 mpContainer->SetPreviewSize(MasterPageContainer::SMALL);
291 mrBase.SetBusyState (false);
292 if (mxSidebar.is())
293 mxSidebar->requestLayout();
295 else if (rIdent == "edit")
297 using namespace ::com::sun::star;
298 uno::Reference<drawing::XDrawPage> xSelectedMaster;
299 SdPage* pMasterPage = GetSelectedMasterPage();
300 assert(pMasterPage); //rhbz#902884
301 if (pMasterPage)
302 xSelectedMaster.set(pMasterPage->getUnoPage(), uno::UNO_QUERY);
303 SfxViewFrame* pViewFrame = mrBase.GetViewFrame();
304 if (pViewFrame != nullptr && xSelectedMaster.is())
306 SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher();
307 if (pDispatcher != nullptr)
309 sal_uInt16 nIndex = PreviewValueSet::GetSelectedItemId();
310 pDispatcher->Execute(SID_MASTERPAGE, SfxCallMode::SYNCHRON);
311 PreviewValueSet::SelectItem (nIndex);
312 mrBase.GetDrawController().setCurrentPage(xSelectedMaster);
318 IMPL_LINK(MasterPagesSelector, ContainerChangeListener, MasterPageContainerChangeEvent&, rEvent, void)
320 NotifyContainerChangeEvent(rEvent);
323 SdPage* MasterPagesSelector::GetSelectedMasterPage()
325 const ::osl::MutexGuard aGuard (maMutex);
327 SdPage* pMasterPage = nullptr;
328 sal_uInt16 nIndex = PreviewValueSet::GetSelectedItemId();
329 UserData* pData = GetUserData(nIndex);
330 if (pData != nullptr)
332 pMasterPage = mpContainer->GetPageObjectForToken(pData->second, true);
334 return pMasterPage;
337 /** Assemble a list of all slides of the document and pass it to
338 AssignMasterPageToPageList().
340 void MasterPagesSelector::AssignMasterPageToAllSlides (SdPage* pMasterPage)
342 if (pMasterPage == nullptr)
343 return;
345 sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard);
346 if (nPageCount == 0)
347 return;
349 // Get a list of all pages. As a little optimization we only
350 // include pages that do not already have the given master page
351 // assigned.
352 OUString sFullLayoutName(pMasterPage->GetLayoutName());
353 ::sd::slidesorter::SharedPageSelection pPageList (
354 new ::sd::slidesorter::SlideSorterViewShell::PageSelection);
355 for (sal_uInt16 nPageIndex=0; nPageIndex<nPageCount; nPageIndex++)
357 SdPage* pPage = mrDocument.GetSdPage (nPageIndex, PageKind::Standard);
358 if (pPage != nullptr && pPage->GetLayoutName() != sFullLayoutName)
360 pPageList->push_back (pPage);
364 AssignMasterPageToPageList(pMasterPage, pPageList);
367 /** Assemble a list of the currently selected slides (selected in a visible
368 slide sorter) and pass it to AssignMasterPageToPageList().
370 void MasterPagesSelector::AssignMasterPageToSelectedSlides (
371 SdPage* pMasterPage)
373 using namespace ::sd::slidesorter;
374 using namespace ::sd::slidesorter::controller;
376 if (pMasterPage == nullptr)
377 return;
379 // Find a visible slide sorter.
380 SlideSorterViewShell* pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase);
381 if (pSlideSorter == nullptr)
382 return;
384 // Get a list of selected pages.
385 SharedPageSelection pPageSelection = pSlideSorter->GetPageSelection();
386 if (pPageSelection->empty())
387 return;
389 AssignMasterPageToPageList(pMasterPage, pPageSelection);
391 // Restore the previous selection.
392 pSlideSorter->SetPageSelection(pPageSelection);
395 void MasterPagesSelector::AssignMasterPageToPageList (
396 SdPage* pMasterPage,
397 const std::shared_ptr<std::vector<SdPage*>>& rPageList)
399 DocumentHelper::AssignMasterPageToPageList(mrDocument, pMasterPage, rPageList);
402 void MasterPagesSelector::NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent)
404 const ::osl::MutexGuard aGuard (maMutex);
406 switch (rEvent.meEventType)
408 case MasterPageContainerChangeEvent::EventType::SIZE_CHANGED:
409 PreviewValueSet::SetPreviewSize(mpContainer->GetPreviewSizePixel());
410 UpdateAllPreviews();
411 break;
413 case MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED:
415 int nIndex (GetIndexForToken(rEvent.maChildToken));
416 if (nIndex >= 0)
418 PreviewValueSet::SetItemImage (
419 static_cast<sal_uInt16>(nIndex),
420 mpContainer->GetPreviewForToken(rEvent.maChildToken));
421 PreviewValueSet::Invalidate(PreviewValueSet::GetItemRect(static_cast<sal_uInt16>(nIndex)));
424 break;
426 case MasterPageContainerChangeEvent::EventType::DATA_CHANGED:
428 InvalidateItem(rEvent.maChildToken);
429 Fill();
431 break;
433 case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED:
435 int nIndex (GetIndexForToken(rEvent.maChildToken));
436 SetItem(nIndex, MasterPageContainer::NIL_TOKEN);
437 break;
440 default:
441 break;
445 MasterPagesSelector::UserData* MasterPagesSelector::GetUserData (int nIndex) const
447 const ::osl::MutexGuard aGuard (maMutex);
449 if (nIndex>0 && static_cast<unsigned int>(nIndex)<=PreviewValueSet::GetItemCount())
450 return static_cast<UserData*>(PreviewValueSet::GetItemData(static_cast<sal_uInt16>(nIndex)));
451 else
452 return nullptr;
455 void MasterPagesSelector::SetUserData (int nIndex, std::unique_ptr<UserData> pData)
457 const ::osl::MutexGuard aGuard (maMutex);
459 delete GetUserData(nIndex);
460 PreviewValueSet::SetItemData(static_cast<sal_uInt16>(nIndex), pData.release());
463 void MasterPagesSelector::SetItem (
464 sal_uInt16 nIndex,
465 MasterPageContainer::Token aToken)
467 const ::osl::MutexGuard aGuard (maMutex);
469 RemoveTokenToIndexEntry(nIndex,aToken);
471 if (nIndex <= 0)
472 return;
474 if (aToken != MasterPageContainer::NIL_TOKEN)
476 Image aPreview (mpContainer->GetPreviewForToken(aToken));
477 MasterPageContainer::PreviewState eState (mpContainer->GetPreviewState(aToken));
479 if (aPreview.GetSizePixel().Width()>0)
481 if (PreviewValueSet::GetItemPos(nIndex) != VALUESET_ITEM_NOTFOUND)
483 PreviewValueSet::SetItemImage(nIndex,aPreview);
484 PreviewValueSet::SetItemText(nIndex, mpContainer->GetPageNameForToken(aToken));
486 else
488 PreviewValueSet::InsertItem (
489 nIndex,
490 aPreview,
491 mpContainer->GetPageNameForToken(aToken),
492 nIndex);
494 SetUserData(nIndex, std::make_unique<UserData>(nIndex,aToken));
496 AddTokenToIndexEntry(nIndex,aToken);
499 if (eState == MasterPageContainer::PS_CREATABLE)
500 mpContainer->RequestPreview(aToken);
502 else
504 PreviewValueSet::RemoveItem(nIndex);
509 void MasterPagesSelector::AddTokenToIndexEntry (
510 sal_uInt16 nIndex,
511 MasterPageContainer::Token aToken)
513 const ::osl::MutexGuard aGuard (maMutex);
515 maTokenToValueSetIndex[aToken] = nIndex;
518 void MasterPagesSelector::RemoveTokenToIndexEntry (
519 sal_uInt16 nIndex,
520 MasterPageContainer::Token aNewToken)
522 const ::osl::MutexGuard aGuard (maMutex);
524 UserData* pData = GetUserData(nIndex);
525 if (pData != nullptr)
527 // Get the token that the index pointed to previously.
528 MasterPageContainer::Token aOldToken (pData->second);
530 if (aNewToken != aOldToken
531 && nIndex == GetIndexForToken(aOldToken))
533 maTokenToValueSetIndex[aOldToken] = 0;
538 void MasterPagesSelector::InvalidatePreview (const SdPage* pPage)
540 const ::osl::MutexGuard aGuard (maMutex);
542 for (size_t nIndex=1; nIndex<=PreviewValueSet::GetItemCount(); nIndex++)
544 UserData* pData = GetUserData(nIndex);
545 if (pData != nullptr)
547 MasterPageContainer::Token aToken (pData->second);
548 if (pPage == mpContainer->GetPageObjectForToken(aToken,false))
550 mpContainer->InvalidatePreview(aToken);
551 mpContainer->RequestPreview(aToken);
552 break;
558 void MasterPagesSelector::UpdateAllPreviews()
560 const ::osl::MutexGuard aGuard (maMutex);
562 for (size_t nIndex=1; nIndex<=PreviewValueSet::GetItemCount(); nIndex++)
564 UserData* pData = GetUserData(nIndex);
565 if (pData != nullptr)
567 MasterPageContainer::Token aToken (pData->second);
568 PreviewValueSet::SetItemImage(
569 nIndex,
570 mpContainer->GetPreviewForToken(aToken));
571 if (mpContainer->GetPreviewState(aToken) == MasterPageContainer::PS_CREATABLE)
572 mpContainer->RequestPreview(aToken);
575 PreviewValueSet::Rearrange();
578 void MasterPagesSelector::ClearPageSet()
580 const ::osl::MutexGuard aGuard (maMutex);
582 for (size_t nIndex=1; nIndex<=PreviewValueSet::GetItemCount(); nIndex++)
584 UserData* pData = GetUserData(nIndex);
585 delete pData;
587 PreviewValueSet::Clear();
590 void MasterPagesSelector::SetHelpId( const OString& aId )
592 const ::osl::MutexGuard aGuard (maMutex);
594 PreviewValueSet::SetHelpId( aId );
597 sal_Int32 MasterPagesSelector::GetIndexForToken (MasterPageContainer::Token aToken) const
599 const ::osl::MutexGuard aGuard (maMutex);
601 TokenToValueSetIndex::const_iterator iIndex (maTokenToValueSetIndex.find(aToken));
602 if (iIndex != maTokenToValueSetIndex.end())
603 return iIndex->second;
604 else
605 return -1;
608 void MasterPagesSelector::Clear()
610 const ::osl::MutexGuard aGuard (maMutex);
612 ClearPageSet();
615 void MasterPagesSelector::InvalidateItem (MasterPageContainer::Token aToken)
617 const ::osl::MutexGuard aGuard (maMutex);
619 auto iItem = std::find(maCurrentItemList.begin(), maCurrentItemList.end(), aToken);
620 if (iItem != maCurrentItemList.end())
621 *iItem = MasterPageContainer::NIL_TOKEN;
624 void MasterPagesSelector::UpdateItemList (::std::unique_ptr<ItemList> && pNewItemList)
626 const ::osl::MutexGuard aGuard (maMutex);
628 ItemList::const_iterator iNewItem (pNewItemList->begin());
629 ItemList::const_iterator iCurrentItem (maCurrentItemList.begin());
630 ItemList::const_iterator iNewEnd (pNewItemList->end());
631 ItemList::const_iterator iCurrentEnd (maCurrentItemList.end());
632 sal_uInt16 nIndex (1);
634 // Update existing items.
635 for ( ; iNewItem!=iNewEnd && iCurrentItem!=iCurrentEnd; ++iNewItem, ++iCurrentItem,++nIndex)
637 if (*iNewItem != *iCurrentItem)
639 SetItem(nIndex,*iNewItem);
643 // Append new items.
644 for ( ; iNewItem!=iNewEnd; ++iNewItem,++nIndex)
646 SetItem(nIndex,*iNewItem);
649 // Remove trailing items.
650 for ( ; iCurrentItem!=iCurrentEnd; ++iCurrentItem,++nIndex)
652 SetItem(nIndex,MasterPageContainer::NIL_TOKEN);
655 maCurrentItemList.swap(*pNewItemList);
657 PreviewValueSet::Rearrange();
658 if (mxSidebar.is())
659 mxSidebar->requestLayout();
662 css::ui::LayoutSize MasterPagesSelector::GetHeightForWidth (const sal_Int32 nWidth)
664 const sal_Int32 nHeight (GetPreferredHeight(nWidth));
665 return css::ui::LayoutSize(nHeight,nHeight,nHeight);
668 } } // end of namespace sd::sidebar
670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */