tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / sd / source / ui / sidebar / MasterPagesSelector.cxx
blob33d87e4d919c2aa6131f937bbe11eda916d73cbb
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 <sdpage.hxx>
31 #include <app.hrc>
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>
42 #include <memory>
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,
53 ViewShellBase& rBase,
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)
61 , mrBase(rBase)
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()
75 Clear();
76 UpdateLocks(ItemList());
78 Link<MasterPageContainerChangeEvent&,void> aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener));
79 mpContainer->RemoveChangeListener(aChangeListener);
80 mpContainer.reset();
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);
116 Fill(*pItemList);
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);
130 return true;
133 IMPL_LINK(MasterPagesSelector, MousePressHdl, const MouseEvent&, rMEvet, bool)
135 if (!rMEvet.IsRight())
136 return false;
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);
146 break;
149 return false;
152 IMPL_LINK(MasterPagesSelector, QueryTooltipHdl, const weld::TreeIter&, iter, OUString)
154 const OUString sId = mxPreviewIconView->get_id(iter);
155 if (!sId.isEmpty())
156 return mpContainer->GetPageNameForToken(sId.toUInt32());
158 return OUString();
161 void MasterPagesSelector::ShowContextMenu(const Point& pPos)
163 // Setup the menu.
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);
169 // Show the menu.
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);
178 else
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);
201 if (mxSidebar.is())
202 mxSidebar->requestLayout();
204 else if (rIdent == "small")
206 mrBase.SetBusyState (true);
207 mpContainer->SetPreviewSize(MasterPageContainer::SMALL);
208 mrBase.SetBusyState (false);
209 if (mxSidebar.is())
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
218 if (pMasterPage)
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);
252 return pMasterPage;
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)
261 return;
263 sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard);
264 if (nPageCount == 0)
265 return;
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
269 // assigned.
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 (
289 SdPage* pMasterPage)
291 using namespace ::sd::slidesorter;
292 using namespace ::sd::slidesorter::controller;
294 if (pMasterPage == nullptr)
295 return;
297 // Find a visible slide sorter.
298 SlideSorterViewShell* pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase);
299 if (pSlideSorter == nullptr)
300 return;
302 // Get a list of selected pages.
303 SharedPageSelection pPageSelection = pSlideSorter->GetPageSelection();
304 if (pPageSelection->empty())
305 return;
307 AssignMasterPageToPageList(pMasterPage, pPageSelection);
309 // Restore the previous selection.
310 pSlideSorter->SetPageSelection(pPageSelection);
313 void MasterPagesSelector::AssignMasterPageToPageList (
314 SdPage* pMasterPage,
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:
327 UpdateAllPreviews();
328 break;
330 case MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED:
332 int nIndex (GetIndexForToken(rEvent.maChildToken));
333 if (nIndex >= 0)
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);
343 break;
345 case MasterPageContainerChangeEvent::EventType::DATA_CHANGED:
347 InvalidateItem(rEvent.maChildToken);
348 Fill();
350 break;
352 case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED:
354 int nIndex (GetIndexForToken(rEvent.maChildToken));
355 SetItem(nIndex, MasterPageContainer::NIL_TOKEN);
356 break;
359 default:
360 break;
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));
374 else
375 return nullptr;
378 void MasterPagesSelector::SetItem (
379 sal_uInt16 nIndex,
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));
401 else
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);
413 else
415 mxPreviewIconView->remove(nIndex);
417 mxPreviewIconView->thaw();
420 void MasterPagesSelector::AddTokenToIndexEntry (
421 sal_uInt16 nIndex,
422 MasterPageContainer::Token aToken)
424 const ::osl::MutexGuard aGuard (maMutex);
426 maTokenToValueSetIndex[aToken] = nIndex;
429 void MasterPagesSelector::RemoveTokenToIndexEntry (
430 sal_uInt16 nIndex,
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);
463 break;
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);
480 return pVDev;
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;
526 else
527 return -1;
530 void MasterPagesSelector::Clear()
532 const ::osl::MutexGuard aGuard (maMutex);
534 ClearPageSet();
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);
565 // Append new items.
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);
579 if (mxSidebar.is())
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();
592 if (itemsInRows > 0)
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: */