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 <sal/config.h>
24 #include "MasterPagesSelector.hxx"
26 #include "MasterPageContainer.hxx"
27 #include "DocumentHelper.hxx"
29 #include <drawdoc.hxx>
30 #include <DrawDocShell.hxx>
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>
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 (
73 SdDrawDocument
& rDocument
,
75 const std::shared_ptr
<MasterPageContainer
>& rpContainer
,
76 const css::uno::Reference
<css::ui::XSidebar
>& rxSidebar
)
77 : PreviewValueSet(pParent
),
79 mpContainer(rpContainer
),
80 mrDocument(rDocument
),
83 maTokenToValueSetIndex(),
84 maLockedMasterPages(),
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()
111 void MasterPagesSelector::dispose()
114 UpdateLocks(ItemList());
116 Link
<MasterPageContainerChangeEvent
&,void> aChangeListener (LINK(this,MasterPagesSelector
,ContainerChangeListener
));
117 mpContainer
->RemoveChangeListener(aChangeListener
);
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
);
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());
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
209 const sal_uInt16 nIndex
= PreviewValueSet::GetSelectedItemId();
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();
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
);
235 pMenu
->Execute(this, ::tools::Rectangle(aPosition
,Size(1,1)), PopupMenuFlags::ExecuteDown
);
243 void MasterPagesSelector::ProcessPopupMenu (Menu
& rMenu
)
245 // Disable some entries.
246 if (mpContainer
->GetPreviewSize() == MasterPageContainer::SMALL
)
247 rMenu
.EnableItem(rMenu
.GetItemId("small"), false);
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!");
261 ExecuteCommand(pMenu
->GetCurItemIdent());
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);
285 mxSidebar
->requestLayout();
287 else if (rIdent
== "small")
289 mrBase
.SetBusyState (true);
290 mpContainer
->SetPreviewSize(MasterPageContainer::SMALL
);
291 mrBase
.SetBusyState (false);
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
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);
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)
345 sal_uInt16 nPageCount
= mrDocument
.GetSdPageCount(PageKind::Standard
);
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
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 (
373 using namespace ::sd::slidesorter
;
374 using namespace ::sd::slidesorter::controller
;
376 if (pMasterPage
== nullptr)
379 // Find a visible slide sorter.
380 SlideSorterViewShell
* pSlideSorter
= SlideSorterViewShell::GetSlideSorter(mrBase
);
381 if (pSlideSorter
== nullptr)
384 // Get a list of selected pages.
385 SharedPageSelection pPageSelection
= pSlideSorter
->GetPageSelection();
386 if (pPageSelection
->empty())
389 AssignMasterPageToPageList(pMasterPage
, pPageSelection
);
391 // Restore the previous selection.
392 pSlideSorter
->SetPageSelection(pPageSelection
);
395 void MasterPagesSelector::AssignMasterPageToPageList (
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());
413 case MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED
:
415 int nIndex (GetIndexForToken(rEvent
.maChildToken
));
418 PreviewValueSet::SetItemImage (
419 static_cast<sal_uInt16
>(nIndex
),
420 mpContainer
->GetPreviewForToken(rEvent
.maChildToken
));
421 PreviewValueSet::Invalidate(PreviewValueSet::GetItemRect(static_cast<sal_uInt16
>(nIndex
)));
426 case MasterPageContainerChangeEvent::EventType::DATA_CHANGED
:
428 InvalidateItem(rEvent
.maChildToken
);
433 case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED
:
435 int nIndex (GetIndexForToken(rEvent
.maChildToken
));
436 SetItem(nIndex
, MasterPageContainer::NIL_TOKEN
);
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
)));
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 (
465 MasterPageContainer::Token aToken
)
467 const ::osl::MutexGuard
aGuard (maMutex
);
469 RemoveTokenToIndexEntry(nIndex
,aToken
);
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
));
488 PreviewValueSet::InsertItem (
491 mpContainer
->GetPageNameForToken(aToken
),
494 SetUserData(nIndex
, std::make_unique
<UserData
>(nIndex
,aToken
));
496 AddTokenToIndexEntry(nIndex
,aToken
);
499 if (eState
== MasterPageContainer::PS_CREATABLE
)
500 mpContainer
->RequestPreview(aToken
);
504 PreviewValueSet::RemoveItem(nIndex
);
509 void MasterPagesSelector::AddTokenToIndexEntry (
511 MasterPageContainer::Token aToken
)
513 const ::osl::MutexGuard
aGuard (maMutex
);
515 maTokenToValueSetIndex
[aToken
] = nIndex
;
518 void MasterPagesSelector::RemoveTokenToIndexEntry (
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
);
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(
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
);
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
;
608 void MasterPagesSelector::Clear()
610 const ::osl::MutexGuard
aGuard (maMutex
);
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
);
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();
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: */