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>
33 #include <DrawController.hxx>
34 #include <SlideSorterViewShell.hxx>
35 #include <vcl/vclptr.hxx>
36 #include <ViewShellBase.hxx>
37 #include <o3tl/safeint.hxx>
38 #include <vcl/image.hxx>
39 #include <sfx2/dispatch.hxx>
40 #include <sfx2/viewfrm.hxx>
41 #include <sfx2/sidebar/Theme.hxx>
43 #include <vcl/virdev.hxx>
45 namespace sd::sidebar
{
47 /** menu entry that is executed as default action when the left mouse button is
48 clicked over a master page.
50 constexpr OUStringLiteral gsDefaultClickAction
= u
"applyselect";
52 MasterPagesSelector::MasterPagesSelector(weld::Widget
* pParent
, SdDrawDocument
& rDocument
,
54 std::shared_ptr
<MasterPageContainer
> pContainer
,
55 css::uno::Reference
<css::ui::XSidebar
> xSidebar
,
56 const OUString
& rUIFileName
, const OUString
& rIconViewId
)
57 : PanelLayout(pParent
, u
"MasterPagePanel"_ustr
, rUIFileName
)
58 , mpContainer(std::move(pContainer
))
59 , mxPreviewIconView(m_xBuilder
->weld_icon_view(rIconViewId
))
60 , mrDocument(rDocument
)
62 , mxSidebar(std::move(xSidebar
))
63 , maIconViewId(rIconViewId
)
65 mxPreviewIconView
->connect_item_activated(LINK(this, MasterPagesSelector
, MasterPageSelected
));
66 mxPreviewIconView
->connect_mouse_press(LINK(this, MasterPagesSelector
, MousePressHdl
));
67 mxPreviewIconView
->connect_query_tooltip(LINK(this, MasterPagesSelector
, QueryTooltipHdl
));
69 Link
<MasterPageContainerChangeEvent
&,void> aChangeListener (LINK(this,MasterPagesSelector
,ContainerChangeListener
));
70 mpContainer
->AddChangeListener(aChangeListener
);
73 MasterPagesSelector::~MasterPagesSelector()
76 UpdateLocks(ItemList());
78 Link
<MasterPageContainerChangeEvent
&,void> aChangeListener (LINK(this,MasterPagesSelector
,ContainerChangeListener
));
79 mpContainer
->RemoveChangeListener(aChangeListener
);
81 mxPreviewIconView
.reset();
84 void MasterPagesSelector::LateInit()
88 void MasterPagesSelector::UpdateLocks (const ItemList
& rItemList
)
90 ItemList aNewLockList
;
92 // In here we first lock the master pages in the given list and then
93 // release the locks acquired in a previous call to this method. When
94 // this were done the other way round the lock count of some master
95 // pages might drop temporarily to 0 and would lead to unnecessary
96 // deletion and re-creation of MasterPageDescriptor objects.
98 // Lock the master pages in the given list.
99 for (const auto& rItem
: rItemList
)
101 mpContainer
->AcquireToken(rItem
);
102 aNewLockList
.push_back(rItem
);
105 // Release the previously locked master pages.
106 for (const auto& rPage
: maLockedMasterPages
)
107 mpContainer
->ReleaseToken(rPage
);
109 maLockedMasterPages
.swap(aNewLockList
);
112 void MasterPagesSelector::Fill()
114 ::std::unique_ptr
<ItemList
> pItemList (new ItemList
);
118 UpdateLocks(*pItemList
);
119 UpdateItemList(std::move(pItemList
));
122 OUString
MasterPagesSelector::GetContextMenuUIFile() const
124 return u
"modules/simpress/ui/mastermenu.ui"_ustr
;
127 IMPL_LINK_NOARG(MasterPagesSelector
, MasterPageSelected
, weld::IconView
&, bool)
129 ExecuteCommand(gsDefaultClickAction
);
133 IMPL_LINK(MasterPagesSelector
, MousePressHdl
, const MouseEvent
&, rMEvet
, bool)
135 if (!rMEvet
.IsRight())
138 const Point
& pPos
= rMEvet
.GetPosPixel();
139 for (int i
= 0; i
< mxPreviewIconView
->n_children(); i
++)
141 const ::tools::Rectangle aRect
= mxPreviewIconView
->get_rect(i
);
142 if (aRect
.Contains(pPos
))
144 mxPreviewIconView
->select(i
);
145 ShowContextMenu(pPos
);
152 IMPL_LINK(MasterPagesSelector
, QueryTooltipHdl
, const weld::TreeIter
&, iter
, OUString
)
154 const OUString sId
= mxPreviewIconView
->get_id(iter
);
156 return mpContainer
->GetPageNameForToken(sId
.toUInt32());
161 void MasterPagesSelector::ShowContextMenu(const Point
& pPos
)
164 ::tools::Rectangle
aRect(pPos
, Size(1, 1));
165 std::unique_ptr
<weld::Builder
> xBuilder(
166 Application::CreateBuilder(mxPreviewIconView
.get(), GetContextMenuUIFile()));
167 std::unique_ptr
<weld::Menu
> xMenu(xBuilder
->weld_menu(u
"menu"_ustr
));
168 ProcessPopupMenu(*xMenu
);
170 ExecuteCommand(xMenu
->popup_at_rect(mxPreviewIconView
.get(), aRect
));
173 void MasterPagesSelector::ProcessPopupMenu(weld::Menu
& rMenu
)
175 // Disable some entries.
176 if (mpContainer
->GetPreviewSize() == MasterPageContainer::SMALL
)
177 rMenu
.set_sensitive(u
"small"_ustr
, false);
179 rMenu
.set_sensitive(u
"large"_ustr
, false);
182 void MasterPagesSelector::ExecuteCommand(const OUString
&rIdent
)
184 if (rIdent
== "applyall")
186 mrBase
.SetBusyState (true);
187 AssignMasterPageToAllSlides (GetSelectedMasterPage());
188 mrBase
.SetBusyState (false);
190 else if (rIdent
== "applyselect")
192 mrBase
.SetBusyState (true);
193 AssignMasterPageToSelectedSlides (GetSelectedMasterPage());
194 mrBase
.SetBusyState (false);
196 else if (rIdent
== "large")
198 mrBase
.SetBusyState (true);
199 mpContainer
->SetPreviewSize(MasterPageContainer::LARGE
);
200 mrBase
.SetBusyState (false);
202 mxSidebar
->requestLayout();
204 else if (rIdent
== "small")
206 mrBase
.SetBusyState (true);
207 mpContainer
->SetPreviewSize(MasterPageContainer::SMALL
);
208 mrBase
.SetBusyState (false);
210 mxSidebar
->requestLayout();
212 else if (rIdent
== "edit")
214 using namespace ::com::sun::star
;
215 uno::Reference
<drawing::XDrawPage
> xSelectedMaster
;
216 SdPage
* pMasterPage
= GetSelectedMasterPage();
217 assert(pMasterPage
); //rhbz#902884
219 xSelectedMaster
.set(pMasterPage
->getUnoPage(), uno::UNO_QUERY
);
220 SfxViewFrame
& rViewFrame
= mrBase
.GetViewFrame();
221 if (xSelectedMaster
.is())
223 SfxDispatcher
* pDispatcher
= rViewFrame
.GetDispatcher();
224 if (pDispatcher
!= nullptr)
226 pDispatcher
->Execute(SID_MASTERPAGE
, SfxCallMode::SYNCHRON
);
227 mrBase
.GetDrawController()->setCurrentPage(xSelectedMaster
);
231 mxPreviewIconView
->unselect_all();
234 IMPL_LINK(MasterPagesSelector
, ContainerChangeListener
, MasterPageContainerChangeEvent
&, rEvent
, void)
236 NotifyContainerChangeEvent(rEvent
);
239 SdPage
* MasterPagesSelector::GetSelectedMasterPage()
241 const ::osl::MutexGuard
aGuard (maMutex
);
243 SdPage
* pMasterPage
= nullptr;
244 OUString sSelectedId
= mxPreviewIconView
->get_selected_id();
246 if (!sSelectedId
.isEmpty())
248 const MasterPageContainer::Token aToken
249 = static_cast<MasterPageContainer::Token
>(sSelectedId
.toInt32());
250 pMasterPage
= mpContainer
->GetPageObjectForToken(aToken
, true);
255 /** Assemble a list of all slides of the document and pass it to
256 AssignMasterPageToPageList().
258 void MasterPagesSelector::AssignMasterPageToAllSlides (SdPage
* pMasterPage
)
260 if (pMasterPage
== nullptr)
263 sal_uInt16 nPageCount
= mrDocument
.GetSdPageCount(PageKind::Standard
);
267 // Get a list of all pages. As a little optimization we only
268 // include pages that do not already have the given master page
270 OUString
sFullLayoutName(pMasterPage
->GetLayoutName());
271 ::sd::slidesorter::SharedPageSelection pPageList
=
272 std::make_shared
<::sd::slidesorter::SlideSorterViewShell::PageSelection
>();
273 for (sal_uInt16 nPageIndex
=0; nPageIndex
<nPageCount
; nPageIndex
++)
275 SdPage
* pPage
= mrDocument
.GetSdPage (nPageIndex
, PageKind::Standard
);
276 if (pPage
!= nullptr && pPage
->GetLayoutName() != sFullLayoutName
)
278 pPageList
->push_back (pPage
);
282 AssignMasterPageToPageList(pMasterPage
, pPageList
);
285 /** Assemble a list of the currently selected slides (selected in a visible
286 slide sorter) and pass it to AssignMasterPageToPageList().
288 void MasterPagesSelector::AssignMasterPageToSelectedSlides (
291 using namespace ::sd::slidesorter
;
292 using namespace ::sd::slidesorter::controller
;
294 if (pMasterPage
== nullptr)
297 // Find a visible slide sorter.
298 SlideSorterViewShell
* pSlideSorter
= SlideSorterViewShell::GetSlideSorter(mrBase
);
299 if (pSlideSorter
== nullptr)
302 // Get a list of selected pages.
303 SharedPageSelection pPageSelection
= pSlideSorter
->GetPageSelection();
304 if (pPageSelection
->empty())
307 AssignMasterPageToPageList(pMasterPage
, pPageSelection
);
309 // Restore the previous selection.
310 pSlideSorter
->SetPageSelection(pPageSelection
);
313 void MasterPagesSelector::AssignMasterPageToPageList (
315 const std::shared_ptr
<std::vector
<SdPage
*>>& rPageList
)
317 DocumentHelper::AssignMasterPageToPageList(mrDocument
, pMasterPage
, rPageList
);
320 void MasterPagesSelector::NotifyContainerChangeEvent (const MasterPageContainerChangeEvent
& rEvent
)
322 const ::osl::MutexGuard
aGuard (maMutex
);
324 switch (rEvent
.meEventType
)
326 case MasterPageContainerChangeEvent::EventType::SIZE_CHANGED
:
330 case MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED
:
332 int nIndex (GetIndexForToken(rEvent
.maChildToken
));
335 Image
aPreview(mpContainer
->GetPreviewForToken(rEvent
.maChildToken
));
336 if (aPreview
.GetSizePixel().Width() > 0)
338 VclPtr
<VirtualDevice
> aDev
= GetVirtualDevice(aPreview
);
339 mxPreviewIconView
->set_image(nIndex
, aDev
);
345 case MasterPageContainerChangeEvent::EventType::DATA_CHANGED
:
347 InvalidateItem(rEvent
.maChildToken
);
352 case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED
:
354 int nIndex (GetIndexForToken(rEvent
.maChildToken
));
355 SetItem(nIndex
, MasterPageContainer::NIL_TOKEN
);
364 std::unique_ptr
<MasterPagesSelector::UserData
> MasterPagesSelector::GetUserData(int nIndex
) const
366 const ::osl::MutexGuard
aGuard(maMutex
);
368 if (nIndex
>= 0 && nIndex
< mxPreviewIconView
->n_children())
370 const MasterPageContainer::Token aToken
371 = static_cast<MasterPageContainer::Token
>(mxPreviewIconView
->get_id(nIndex
).toInt32());
372 return std::make_unique
<UserData
>(std::make_pair(nIndex
, aToken
));
378 void MasterPagesSelector::SetItem (
380 MasterPageContainer::Token aToken
)
382 const ::osl::MutexGuard
aGuard (maMutex
);
384 RemoveTokenToIndexEntry(nIndex
, aToken
);
386 mxPreviewIconView
->freeze();
388 if (aToken
!= MasterPageContainer::NIL_TOKEN
)
390 Image
aPreview (mpContainer
->GetPreviewForToken(aToken
));
391 MasterPageContainer::PreviewState
eState (mpContainer
->GetPreviewState(aToken
));
393 if (aPreview
.GetSizePixel().Width() > 0)
395 VclPtr
<VirtualDevice
> aVDev
= GetVirtualDevice(aPreview
);
396 if (!mxPreviewIconView
->get_id(nIndex
).isEmpty())
398 mxPreviewIconView
->set_image(nIndex
, aVDev
);
399 mxPreviewIconView
->set_id(nIndex
, OUString::number(aToken
));
403 OUString sId
= OUString::number(aToken
);
404 mxPreviewIconView
->insert(nIndex
, nullptr, &sId
, aVDev
, nullptr);
407 AddTokenToIndexEntry(nIndex
, aToken
);
410 if (eState
== MasterPageContainer::PS_CREATABLE
)
411 mpContainer
->RequestPreview(aToken
);
415 mxPreviewIconView
->remove(nIndex
);
417 mxPreviewIconView
->thaw();
420 void MasterPagesSelector::AddTokenToIndexEntry (
422 MasterPageContainer::Token aToken
)
424 const ::osl::MutexGuard
aGuard (maMutex
);
426 maTokenToValueSetIndex
[aToken
] = nIndex
;
429 void MasterPagesSelector::RemoveTokenToIndexEntry (
431 MasterPageContainer::Token aNewToken
)
433 const ::osl::MutexGuard
aGuard (maMutex
);
435 std::unique_ptr
<UserData
> pData
= GetUserData(nIndex
);
436 if (pData
!= nullptr)
438 // Get the token that the index pointed to previously.
439 MasterPageContainer::Token
aOldToken (pData
->second
);
441 if (aNewToken
!= aOldToken
442 && nIndex
== GetIndexForToken(aOldToken
))
444 maTokenToValueSetIndex
[aOldToken
] = -1;
449 void MasterPagesSelector::InvalidatePreview (const SdPage
* pPage
)
451 const ::osl::MutexGuard
aGuard (maMutex
);
453 for (int nIndex
= 0; nIndex
< mxPreviewIconView
->n_children(); nIndex
++)
455 std::unique_ptr
<UserData
> pData
= GetUserData(nIndex
);
456 if (pData
!= nullptr)
458 MasterPageContainer::Token
aToken (pData
->second
);
459 if (pPage
== mpContainer
->GetPageObjectForToken(aToken
,false))
461 mpContainer
->InvalidatePreview(aToken
);
462 mpContainer
->RequestPreview(aToken
);
469 VclPtr
<VirtualDevice
> MasterPagesSelector::GetVirtualDevice(const Image
& rImage
)
471 BitmapEx aPreviewBitmap
= rImage
.GetBitmapEx();
472 VclPtr
<VirtualDevice
> pVDev
= VclPtr
<VirtualDevice
>::Create();
473 const Point
aNull(0, 0);
474 if (pVDev
->GetDPIScaleFactor() > 1)
475 aPreviewBitmap
.Scale(pVDev
->GetDPIScaleFactor(), pVDev
->GetDPIScaleFactor());
476 const Size
aSize(aPreviewBitmap
.GetSizePixel());
477 pVDev
->SetOutputSizePixel(aSize
);
478 pVDev
->DrawBitmapEx(aNull
, aPreviewBitmap
);
483 void MasterPagesSelector::UpdateAllPreviews()
485 const ::osl::MutexGuard
aGuard (maMutex
);
487 mxPreviewIconView
->freeze();
488 for (int aIndex
= 0; aIndex
< mxPreviewIconView
->n_children(); aIndex
++)
490 const MasterPageContainer::Token aToken
491 = static_cast<MasterPageContainer::Token
>(mxPreviewIconView
->get_id(aIndex
).toInt32());
493 Image
aPreview(mpContainer
->GetPreviewForToken(aToken
));
494 if (aPreview
.GetSizePixel().Width() > 0)
496 VclPtr
<VirtualDevice
> pVDev
= GetVirtualDevice(aPreview
);
497 mxPreviewIconView
->set_image(aIndex
, pVDev
);
499 else if (mpContainer
->GetPreviewState(aToken
) == MasterPageContainer::PS_CREATABLE
)
501 mpContainer
->RequestPreview(aToken
);
504 mxPreviewIconView
->thaw();
507 void MasterPagesSelector::ClearPageSet()
509 const ::osl::MutexGuard
aGuard (maMutex
);
510 mxPreviewIconView
->clear();
513 void MasterPagesSelector::SetHelpId( const OUString
& aId
)
515 const ::osl::MutexGuard
aGuard (maMutex
);
516 mxPreviewIconView
->set_help_id(aId
);
519 sal_Int32
MasterPagesSelector::GetIndexForToken (MasterPageContainer::Token aToken
) const
521 const ::osl::MutexGuard
aGuard (maMutex
);
523 TokenToValueSetIndex::const_iterator
iIndex (maTokenToValueSetIndex
.find(aToken
));
524 if (iIndex
!= maTokenToValueSetIndex
.end())
525 return iIndex
->second
;
530 void MasterPagesSelector::Clear()
532 const ::osl::MutexGuard
aGuard (maMutex
);
537 void MasterPagesSelector::InvalidateItem (MasterPageContainer::Token aToken
)
539 const ::osl::MutexGuard
aGuard (maMutex
);
541 auto iItem
= std::find(maCurrentItemList
.begin(), maCurrentItemList
.end(), aToken
);
542 if (iItem
!= maCurrentItemList
.end())
543 *iItem
= MasterPageContainer::NIL_TOKEN
;
546 void MasterPagesSelector::UpdateItemList (::std::unique_ptr
<ItemList
> && pNewItemList
)
548 const ::osl::MutexGuard
aGuard (maMutex
);
550 ItemList::const_iterator
iNewItem (pNewItemList
->begin());
551 ItemList::const_iterator
iCurrentItem (maCurrentItemList
.begin());
552 ItemList::const_iterator
iNewEnd (pNewItemList
->end());
553 ItemList::const_iterator
iCurrentEnd (maCurrentItemList
.end());
554 sal_uInt16
nIndex(0);
556 // Update existing items.
557 for ( ; iNewItem
!=iNewEnd
&& iCurrentItem
!=iCurrentEnd
; ++iNewItem
, ++iCurrentItem
,++nIndex
)
559 if (*iNewItem
!= *iCurrentItem
)
561 SetItem(nIndex
,*iNewItem
);
566 for ( ; iNewItem
!=iNewEnd
; ++iNewItem
,++nIndex
)
568 SetItem(nIndex
,*iNewItem
);
571 // Remove trailing items.
572 for ( ; iCurrentItem
!=iCurrentEnd
; ++iCurrentItem
,++nIndex
)
574 SetItem(nIndex
,MasterPageContainer::NIL_TOKEN
);
577 maCurrentItemList
.swap(*pNewItemList
);
580 mxSidebar
->requestLayout();
583 css::ui::LayoutSize
MasterPagesSelector::GetHeightForWidth(const sal_Int32 nWidth
)
585 if (maIconViewId
== "masterpageall_icons")
586 return css::ui::LayoutSize(-1, -1, -1);
588 const sal_uInt32 nItemWidth
= mxPreviewIconView
->get_item_width();
589 sal_Int32 nMinimumHeight
= mxPreviewIconView
->get_preferred_size().getHeight();
590 const sal_Int32 itemsInRows
= floor(nWidth
/ nItemWidth
);
591 sal_Int32 totalItems
= mxPreviewIconView
->n_children();
593 nMinimumHeight
= ((totalItems
/ itemsInRows
) + 1) * nMinimumHeight
;
594 return css::ui::LayoutSize(nMinimumHeight
, nMinimumHeight
, nMinimumHeight
);
597 } // end of namespace sd::sidebar
599 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */