cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsInsertionIndicatorOverlay.cxx
blob3e5139d32fb94c21a5c0337b06e7848ea03acf2f
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 <view/SlsInsertionIndicatorOverlay.hxx>
22 #include <SlideSorter.hxx>
23 #include <view/SlideSorterView.hxx>
24 #include <view/SlsLayouter.hxx>
25 #include <view/SlsPageObjectLayouter.hxx>
26 #include <view/SlsTheme.hxx>
27 #include "SlsFramePainter.hxx"
28 #include "SlsLayeredDevice.hxx"
29 #include <DrawDocShell.hxx>
30 #include <drawdoc.hxx>
31 #include <Window.hxx>
33 #include <o3tl/safeint.hxx>
34 #include <rtl/math.hxx>
35 #include <vcl/virdev.hxx>
36 #include <basegfx/range/b2drectangle.hxx>
37 #include <basegfx/polygon/b2dpolygon.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
40 namespace {
42 const double gnPreviewOffsetScale = 1.0 / 8.0;
44 ::tools::Rectangle GrowRectangle (const ::tools::Rectangle& rBox, const sal_Int32 nOffset)
46 return ::tools::Rectangle (
47 rBox.Left() - nOffset,
48 rBox.Top() - nOffset,
49 rBox.Right() + nOffset,
50 rBox.Bottom() + nOffset);
53 sal_Int32 RoundToInt (const double nValue) { return sal_Int32(::rtl::math::round(nValue)); }
55 } // end of anonymous namespace
57 namespace sd::slidesorter::view {
59 //===== InsertionIndicatorOverlay ===========================================
61 const sal_Int32 gnShadowBorder = 3;
62 const sal_Int32 gnLayerIndex = 2;
64 InsertionIndicatorOverlay::InsertionIndicatorOverlay (SlideSorter& rSlideSorter)
65 : mrSlideSorter(rSlideSorter),
66 mbIsVisible(false),
67 mpShadowPainter(
68 new FramePainter(mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_RawInsertShadow)))
72 InsertionIndicatorOverlay::~InsertionIndicatorOverlay()
74 // cid#1491947 silence Uncaught exception
75 suppress_fun_call_w_exception(Hide());
78 void InsertionIndicatorOverlay::Create (const SdTransferable* pTransferable)
80 if (pTransferable == nullptr)
81 return;
83 std::shared_ptr<controller::TransferableData> pData (
84 controller::TransferableData::GetFromTransferable(pTransferable));
85 if ( ! pData)
86 return;
87 sal_Int32 nSelectionCount (0);
88 if (pTransferable->HasPageBookmarks())
89 nSelectionCount = pTransferable->GetPageBookmarks().size();
90 else
92 DrawDocShell* pDataDocShell = dynamic_cast<DrawDocShell*>(pTransferable->GetDocShell().get());
93 if (pDataDocShell != nullptr)
95 SdDrawDocument* pDataDocument = pDataDocShell->GetDoc();
96 if (pDataDocument != nullptr)
97 nSelectionCount = pDataDocument->GetSdPageCount(PageKind::Standard);
100 Create(pData->GetRepresentatives(), nSelectionCount);
103 void InsertionIndicatorOverlay::Create (
104 const ::std::vector<controller::TransferableData::Representative>& rRepresentatives,
105 const sal_Int32 nSelectionCount)
107 view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter());
108 const std::shared_ptr<view::PageObjectLayouter>& pPageObjectLayouter (
109 rLayouter.GetPageObjectLayouter());
110 std::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme());
111 const Size aOriginalPreviewSize (pPageObjectLayouter->GetPreviewSize());
113 const double nPreviewScale (0.5);
114 const Size aPreviewSize (
115 RoundToInt(aOriginalPreviewSize.Width()*nPreviewScale),
116 RoundToInt(aOriginalPreviewSize.Height()*nPreviewScale));
117 const sal_Int32 nOffset (
118 RoundToInt(std::min(aPreviewSize.Width(),aPreviewSize.Height()) * gnPreviewOffsetScale));
120 // Determine size and offset depending on the number of previews.
121 sal_Int32 nCount (rRepresentatives.size());
122 if (nCount > 0)
123 --nCount;
124 Size aIconSize(
125 aPreviewSize.Width() + 2 * gnShadowBorder + nCount*nOffset,
126 aPreviewSize.Height() + 2 * gnShadowBorder + nCount*nOffset);
128 // Create virtual devices for bitmap and mask whose bitmaps later be
129 // combined to form the BitmapEx of the icon.
130 ScopedVclPtrInstance<VirtualDevice> pContent(
131 *mrSlideSorter.GetContentWindow()->GetOutDev(), DeviceFormat::WITH_ALPHA);
132 pContent->SetOutputSizePixel(aIconSize);
134 pContent->SetFillColor();
135 pContent->SetLineColor(pTheme->GetColor(Theme::Color_PreviewBorder));
136 const Point aOffset = PaintRepresentatives(*pContent, aPreviewSize, nOffset, rRepresentatives);
138 PaintPageCount(*pContent, nSelectionCount, aPreviewSize, aOffset);
140 maIcon = pContent->GetBitmapEx(Point(0,0), aIconSize);
141 maIcon.Scale(aIconSize);
144 Point InsertionIndicatorOverlay::PaintRepresentatives (
145 OutputDevice& rContent,
146 const Size& rPreviewSize,
147 const sal_Int32 nOffset,
148 const ::std::vector<controller::TransferableData::Representative>& rRepresentatives) const
150 const Point aOffset (0,rRepresentatives.size()==1 ? -nOffset : 0);
152 // Paint the pages.
153 Point aPageOffset (0,0);
154 double nTransparency (0);
155 const BitmapEx aExclusionOverlay (mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_HideSlideOverlay));
156 for (sal_Int32 nIndex=2; nIndex>=0; --nIndex)
158 if (rRepresentatives.size() <= o3tl::make_unsigned(nIndex))
159 continue;
160 switch(nIndex)
162 case 0 :
163 aPageOffset = Point(0, nOffset);
164 nTransparency = 0.85;
165 break;
166 case 1:
167 aPageOffset = Point(nOffset, 0);
168 nTransparency = 0.75;
169 break;
170 case 2:
171 aPageOffset = Point(2*nOffset, 2*nOffset);
172 nTransparency = 0.65;
173 break;
175 aPageOffset += aOffset;
176 aPageOffset.AdjustX(gnShadowBorder );
177 aPageOffset.AdjustY(gnShadowBorder );
179 // Paint the preview.
180 BitmapEx aPreview (rRepresentatives[nIndex].maBitmap);
181 aPreview.Scale(rPreviewSize, BmpScaleFlag::BestQuality);
182 rContent.DrawBitmapEx(aPageOffset, aPreview);
184 // When the page is marked as excluded from the slide show then
185 // paint an overlay that visualizes this.
186 if (rRepresentatives[nIndex].mbIsExcluded)
188 const vcl::Region aSavedClipRegion (rContent.GetClipRegion());
189 rContent.IntersectClipRegion(::tools::Rectangle(aPageOffset, rPreviewSize));
190 // Paint bitmap tiled over the preview to mark it as excluded.
191 const sal_Int32 nIconWidth (aExclusionOverlay.GetSizePixel().Width());
192 const sal_Int32 nIconHeight (aExclusionOverlay.GetSizePixel().Height());
193 if (nIconWidth>0 && nIconHeight>0)
195 for (::tools::Long nX=0; nX<rPreviewSize.Width(); nX+=nIconWidth)
196 for (::tools::Long nY=0; nY<rPreviewSize.Height(); nY+=nIconHeight)
197 rContent.DrawBitmapEx(Point(nX,nY)+aPageOffset, aExclusionOverlay);
199 rContent.SetClipRegion(aSavedClipRegion);
202 // Tone down the bitmap. The further back the darker it becomes.
203 ::tools::Rectangle aBox (
204 aPageOffset.X(),
205 aPageOffset.Y(),
206 aPageOffset.X()+rPreviewSize.Width()-1,
207 aPageOffset.Y()+rPreviewSize.Height()-1);
208 rContent.SetFillColor(COL_BLACK);
209 rContent.SetLineColor();
210 rContent.DrawTransparent(
211 basegfx::B2DHomMatrix(),
212 ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
213 ::basegfx::B2DRectangle(aBox.Left(), aBox.Top(), aBox.Right()+1, aBox.Bottom()+1),
215 0)),
216 nTransparency);
218 // Draw border around preview.
219 ::tools::Rectangle aBorderBox (GrowRectangle(aBox, 1));
220 rContent.SetLineColor(COL_GRAY);
221 rContent.SetFillColor();
222 rContent.DrawRect(aBorderBox);
224 // Draw shadow around preview.
225 mpShadowPainter->PaintFrame(rContent, aBorderBox);
228 return aPageOffset;
231 void InsertionIndicatorOverlay::PaintPageCount (
232 OutputDevice& rDevice,
233 const sal_Int32 nSelectionCount,
234 const Size& rPreviewSize,
235 const Point& rFirstPageOffset) const
237 // Paint the number of slides.
238 std::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme());
239 std::shared_ptr<vcl::Font> pFont(Theme::GetFont(Theme::Font_PageCount, rDevice));
240 if (!pFont)
241 return;
243 OUString sNumber (OUString::number(nSelectionCount));
245 // Determine the size of the (painted) text and create a bounding
246 // box that centers the text on the first preview.
247 rDevice.SetFont(*pFont);
248 ::tools::Rectangle aTextBox;
249 rDevice.GetTextBoundRect(aTextBox, sNumber);
250 Point aTextOffset (aTextBox.TopLeft());
251 Size aTextSize (aTextBox.GetSize());
252 // Place text inside the first page preview.
253 Point aTextLocation(rFirstPageOffset);
254 // Center the text.
255 aTextLocation += Point(
256 (rPreviewSize.Width()-aTextBox.GetWidth())/2,
257 (rPreviewSize.Height()-aTextBox.GetHeight())/2);
258 aTextBox = ::tools::Rectangle(aTextLocation, aTextSize);
260 // Paint background, border and text.
261 static const sal_Int32 nBorder = 5;
262 rDevice.SetFillColor(pTheme->GetColor(Theme::Color_Selection));
263 rDevice.SetLineColor(pTheme->GetColor(Theme::Color_Selection));
264 rDevice.DrawRect(GrowRectangle(aTextBox, nBorder));
266 rDevice.SetFillColor();
267 rDevice.SetLineColor(pTheme->GetColor(Theme::Color_PageCountFontColor));
268 rDevice.DrawRect(GrowRectangle(aTextBox, nBorder-1));
270 rDevice.SetTextColor(pTheme->GetColor(Theme::Color_PageCountFontColor));
271 rDevice.DrawText(aTextBox.TopLeft()-aTextOffset, sNumber);
274 void InsertionIndicatorOverlay::SetLocation (const Point& rLocation)
276 const Point aTopLeft (
277 rLocation - Point(
278 maIcon.GetSizePixel().Width()/2,
279 maIcon.GetSizePixel().Height()/2));
280 if (maLocation != aTopLeft)
282 const ::tools::Rectangle aOldBoundingBox (GetBoundingBox());
284 maLocation = aTopLeft;
286 if (mpLayerInvalidator && IsVisible())
288 mpLayerInvalidator->Invalidate(aOldBoundingBox);
289 mpLayerInvalidator->Invalidate(GetBoundingBox());
294 void InsertionIndicatorOverlay::Paint (
295 OutputDevice& rDevice,
296 const ::tools::Rectangle&)
298 if ( ! IsVisible())
299 return;
301 rDevice.DrawImage(maLocation, Image(maIcon));
304 void InsertionIndicatorOverlay::SetLayerInvalidator (const SharedILayerInvalidator& rpInvalidator)
306 mpLayerInvalidator = rpInvalidator;
308 if (mbIsVisible && mpLayerInvalidator)
309 mpLayerInvalidator->Invalidate(GetBoundingBox());
312 void InsertionIndicatorOverlay::Show()
314 if ( mbIsVisible)
315 return;
317 mbIsVisible = true;
319 std::shared_ptr<LayeredDevice> pLayeredDevice (
320 mrSlideSorter.GetView().GetLayeredDevice());
321 if (pLayeredDevice)
323 pLayeredDevice->RegisterPainter(shared_from_this(), gnLayerIndex);
324 if (mpLayerInvalidator)
325 mpLayerInvalidator->Invalidate(GetBoundingBox());
329 void InsertionIndicatorOverlay::Hide()
331 if (!mbIsVisible)
332 return;
334 mbIsVisible = false;
336 std::shared_ptr<LayeredDevice> pLayeredDevice (
337 mrSlideSorter.GetView().GetLayeredDevice());
338 if (pLayeredDevice)
340 if (mpLayerInvalidator)
341 mpLayerInvalidator->Invalidate(GetBoundingBox());
342 pLayeredDevice->RemovePainter(shared_from_this(), gnLayerIndex);
346 ::tools::Rectangle InsertionIndicatorOverlay::GetBoundingBox() const
348 return ::tools::Rectangle(maLocation, maIcon.GetSizePixel());
351 Size InsertionIndicatorOverlay::GetSize() const
353 return Size(
354 maIcon.GetSizePixel().Width() + 10,
355 maIcon.GetSizePixel().Height() + 10);
358 } // end of namespace ::sd::slidesorter::view
360 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */