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 <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>
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>
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
,
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
),
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)
83 std::shared_ptr
<controller::TransferableData
> pData (
84 controller::TransferableData::GetFromTransferable(pTransferable
));
87 sal_Int32
nSelectionCount (0);
88 if (pTransferable
->HasPageBookmarks())
89 nSelectionCount
= pTransferable
->GetPageBookmarks().size();
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());
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);
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
))
163 aPageOffset
= Point(0, nOffset
);
164 nTransparency
= 0.85;
167 aPageOffset
= Point(nOffset
, 0);
168 nTransparency
= 0.75;
171 aPageOffset
= Point(2*nOffset
, 2*nOffset
);
172 nTransparency
= 0.65;
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 (
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),
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
);
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
));
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
);
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 (
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
&)
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()
319 std::shared_ptr
<LayeredDevice
> pLayeredDevice (
320 mrSlideSorter
.GetView().GetLayeredDevice());
323 pLayeredDevice
->RegisterPainter(shared_from_this(), gnLayerIndex
);
324 if (mpLayerInvalidator
)
325 mpLayerInvalidator
->Invalidate(GetBoundingBox());
329 void InsertionIndicatorOverlay::Hide()
336 std::shared_ptr
<LayeredDevice
> pLayeredDevice (
337 mrSlideSorter
.GetView().GetLayeredDevice());
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
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: */