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 <model/SlsPageEnumeration.hxx>
24 #include <view/SlideSorterView.hxx>
25 #include <view/SlsLayouter.hxx>
26 #include <view/SlsPageObjectLayouter.hxx>
27 #include <view/SlsTheme.hxx>
28 #include <cache/SlsPageCache.hxx>
29 #include "SlsFramePainter.hxx"
30 #include "SlsLayeredDevice.hxx"
31 #include <DrawDocShell.hxx>
32 #include <drawdoc.hxx>
37 #include <vcl/virdev.hxx>
38 #include <basegfx/range/b2drectangle.hxx>
39 #include <basegfx/utils/canvastools.hxx>
40 #include <basegfx/polygon/b2dpolygon.hxx>
41 #include <basegfx/polygon/b2dpolygontools.hxx>
45 static const double gnPreviewOffsetScale
= 1.0 / 8.0;
47 ::tools::Rectangle
GrowRectangle (const ::tools::Rectangle
& rBox
, const sal_Int32 nOffset
)
49 return ::tools::Rectangle (
50 rBox
.Left() - nOffset
,
52 rBox
.Right() + nOffset
,
53 rBox
.Bottom() + nOffset
);
56 sal_Int32
RoundToInt (const double nValue
) { return sal_Int32(::rtl::math::round(nValue
)); }
58 } // end of anonymous namespace
60 namespace sd
{ namespace slidesorter
{ namespace view
{
62 //===== InsertionIndicatorOverlay ===========================================
64 const static sal_Int32 gnShadowBorder
= 3;
65 const static sal_Int32 gnLayerIndex
= 2;
67 InsertionIndicatorOverlay::InsertionIndicatorOverlay (SlideSorter
& rSlideSorter
)
68 : mrSlideSorter(rSlideSorter
),
74 new FramePainter(mrSlideSorter
.GetTheme()->GetIcon(Theme::Icon_RawInsertShadow
)))
78 InsertionIndicatorOverlay::~InsertionIndicatorOverlay() COVERITY_NOEXCEPT_FALSE
83 void InsertionIndicatorOverlay::Create (const SdTransferable
* pTransferable
)
85 if (pTransferable
== nullptr)
88 std::shared_ptr
<controller::TransferableData
> pData (
89 controller::TransferableData::GetFromTransferable(pTransferable
));
92 sal_Int32
nSelectionCount (0);
93 if (pTransferable
->HasPageBookmarks())
94 nSelectionCount
= pTransferable
->GetPageBookmarks().size();
97 DrawDocShell
* pDataDocShell
= dynamic_cast<DrawDocShell
*>(pTransferable
->GetDocShell().get());
98 if (pDataDocShell
!= nullptr)
100 SdDrawDocument
* pDataDocument
= pDataDocShell
->GetDoc();
101 if (pDataDocument
!= nullptr)
102 nSelectionCount
= pDataDocument
->GetSdPageCount(PageKind::Standard
);
105 Create(pData
->GetRepresentatives(), nSelectionCount
);
108 void InsertionIndicatorOverlay::Create (
109 const ::std::vector
<controller::TransferableData::Representative
>& rRepresentatives
,
110 const sal_Int32 nSelectionCount
)
112 view::Layouter
& rLayouter (mrSlideSorter
.GetView().GetLayouter());
113 const std::shared_ptr
<view::PageObjectLayouter
>& pPageObjectLayouter (
114 rLayouter
.GetPageObjectLayouter());
115 std::shared_ptr
<view::Theme
> pTheme (mrSlideSorter
.GetTheme());
116 const Size
aOriginalPreviewSize (pPageObjectLayouter
->GetPreviewSize());
118 const double nPreviewScale (0.5);
119 const Size
aPreviewSize (
120 RoundToInt(aOriginalPreviewSize
.Width()*nPreviewScale
),
121 RoundToInt(aOriginalPreviewSize
.Height()*nPreviewScale
));
122 const sal_Int32
nOffset (
123 RoundToInt(std::min(aPreviewSize
.Width(),aPreviewSize
.Height()) * gnPreviewOffsetScale
));
125 // Determine size and offset depending on the number of previews.
126 sal_Int32
nCount (rRepresentatives
.size());
130 aPreviewSize
.Width() + 2 * gnShadowBorder
+ nCount
*nOffset
,
131 aPreviewSize
.Height() + 2 * gnShadowBorder
+ nCount
*nOffset
);
133 // Create virtual devices for bitmap and mask whose bitmaps later be
134 // combined to form the BitmapEx of the icon.
135 ScopedVclPtrInstance
<VirtualDevice
> pContent(
136 *mrSlideSorter
.GetContentWindow(), DeviceFormat::DEFAULT
, DeviceFormat::DEFAULT
);
137 pContent
->SetOutputSizePixel(aIconSize
);
139 pContent
->SetFillColor();
140 pContent
->SetLineColor(pTheme
->GetColor(Theme::Color_PreviewBorder
));
141 const Point aOffset
= PaintRepresentatives(*pContent
, aPreviewSize
, nOffset
, rRepresentatives
);
143 PaintPageCount(*pContent
, nSelectionCount
, aPreviewSize
, aOffset
);
145 maIcon
= pContent
->GetBitmapEx(Point(0,0), aIconSize
);
146 maIcon
.Scale(aIconSize
);
149 Point
InsertionIndicatorOverlay::PaintRepresentatives (
150 OutputDevice
& rContent
,
151 const Size
& rPreviewSize
,
152 const sal_Int32 nOffset
,
153 const ::std::vector
<controller::TransferableData::Representative
>& rRepresentatives
) const
155 const Point
aOffset (0,rRepresentatives
.size()==1 ? -nOffset
: 0);
158 Point
aPageOffset (0,0);
159 double nTransparency (0);
160 const BitmapEx
aExclusionOverlay (mrSlideSorter
.GetTheme()->GetIcon(Theme::Icon_HideSlideOverlay
));
161 for (sal_Int32 nIndex
=2; nIndex
>=0; --nIndex
)
163 if (rRepresentatives
.size() <= sal_uInt32(nIndex
))
168 aPageOffset
= Point(0, nOffset
);
169 nTransparency
= 0.85;
172 aPageOffset
= Point(nOffset
, 0);
173 nTransparency
= 0.75;
176 aPageOffset
= Point(2*nOffset
, 2*nOffset
);
177 nTransparency
= 0.65;
180 aPageOffset
+= aOffset
;
181 aPageOffset
.AdjustX(gnShadowBorder
);
182 aPageOffset
.AdjustY(gnShadowBorder
);
184 // Paint the preview.
185 BitmapEx
aPreview (rRepresentatives
[nIndex
].maBitmap
);
186 aPreview
.Scale(rPreviewSize
, BmpScaleFlag::BestQuality
);
187 rContent
.DrawBitmapEx(aPageOffset
, aPreview
);
189 // When the page is marked as excluded from the slide show then
190 // paint an overlay that visualizes this.
191 if (rRepresentatives
[nIndex
].mbIsExcluded
)
193 const vcl::Region
aSavedClipRegion (rContent
.GetClipRegion());
194 rContent
.IntersectClipRegion(::tools::Rectangle(aPageOffset
, rPreviewSize
));
195 // Paint bitmap tiled over the preview to mark it as excluded.
196 const sal_Int32
nIconWidth (aExclusionOverlay
.GetSizePixel().Width());
197 const sal_Int32
nIconHeight (aExclusionOverlay
.GetSizePixel().Height());
198 if (nIconWidth
>0 && nIconHeight
>0)
200 for (long nX
=0; nX
<rPreviewSize
.Width(); nX
+=nIconWidth
)
201 for (long nY
=0; nY
<rPreviewSize
.Height(); nY
+=nIconHeight
)
202 rContent
.DrawBitmapEx(Point(nX
,nY
)+aPageOffset
, aExclusionOverlay
);
204 rContent
.SetClipRegion(aSavedClipRegion
);
207 // Tone down the bitmap. The further back the darker it becomes.
208 ::tools::Rectangle
aBox (
211 aPageOffset
.X()+rPreviewSize
.Width()-1,
212 aPageOffset
.Y()+rPreviewSize
.Height()-1);
213 rContent
.SetFillColor(COL_BLACK
);
214 rContent
.SetLineColor();
215 rContent
.DrawTransparent(
216 basegfx::B2DHomMatrix(),
217 ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
218 ::basegfx::B2DRectangle(aBox
.Left(), aBox
.Top(), aBox
.Right()+1, aBox
.Bottom()+1),
223 // Draw border around preview.
224 ::tools::Rectangle
aBorderBox (GrowRectangle(aBox
, 1));
225 rContent
.SetLineColor(COL_GRAY
);
226 rContent
.SetFillColor();
227 rContent
.DrawRect(aBorderBox
);
229 // Draw shadow around preview.
230 mpShadowPainter
->PaintFrame(rContent
, aBorderBox
);
236 void InsertionIndicatorOverlay::PaintPageCount (
237 OutputDevice
& rDevice
,
238 const sal_Int32 nSelectionCount
,
239 const Size
& rPreviewSize
,
240 const Point
& rFirstPageOffset
) const
242 // Paint the number of slides.
243 std::shared_ptr
<view::Theme
> pTheme (mrSlideSorter
.GetTheme());
244 std::shared_ptr
<vcl::Font
> pFont(Theme::GetFont(Theme::Font_PageCount
, rDevice
));
248 OUString
sNumber (OUString::number(nSelectionCount
));
250 // Determine the size of the (painted) text and create a bounding
251 // box that centers the text on the first preview.
252 rDevice
.SetFont(*pFont
);
253 ::tools::Rectangle aTextBox
;
254 rDevice
.GetTextBoundRect(aTextBox
, sNumber
);
255 Point
aTextOffset (aTextBox
.TopLeft());
256 Size
aTextSize (aTextBox
.GetSize());
257 // Place text inside the first page preview.
258 Point
aTextLocation(rFirstPageOffset
);
260 aTextLocation
+= Point(
261 (rPreviewSize
.Width()-aTextBox
.GetWidth())/2,
262 (rPreviewSize
.Height()-aTextBox
.GetHeight())/2);
263 aTextBox
= ::tools::Rectangle(aTextLocation
, aTextSize
);
265 // Paint background, border and text.
266 static const sal_Int32 nBorder
= 5;
267 rDevice
.SetFillColor(pTheme
->GetColor(Theme::Color_Selection
));
268 rDevice
.SetLineColor(pTheme
->GetColor(Theme::Color_Selection
));
269 rDevice
.DrawRect(GrowRectangle(aTextBox
, nBorder
));
271 rDevice
.SetFillColor();
272 rDevice
.SetLineColor(pTheme
->GetColor(Theme::Color_PageCountFontColor
));
273 rDevice
.DrawRect(GrowRectangle(aTextBox
, nBorder
-1));
275 rDevice
.SetTextColor(pTheme
->GetColor(Theme::Color_PageCountFontColor
));
276 rDevice
.DrawText(aTextBox
.TopLeft()-aTextOffset
, sNumber
);
279 void InsertionIndicatorOverlay::SetLocation (const Point
& rLocation
)
281 const Point
aTopLeft (
283 maIcon
.GetSizePixel().Width()/2,
284 maIcon
.GetSizePixel().Height()/2));
285 if (maLocation
!= aTopLeft
)
287 const ::tools::Rectangle
aOldBoundingBox (GetBoundingBox());
289 maLocation
= aTopLeft
;
291 if (mpLayerInvalidator
&& IsVisible())
293 mpLayerInvalidator
->Invalidate(aOldBoundingBox
);
294 mpLayerInvalidator
->Invalidate(GetBoundingBox());
299 void InsertionIndicatorOverlay::Paint (
300 OutputDevice
& rDevice
,
301 const ::tools::Rectangle
&)
306 rDevice
.DrawImage(maLocation
, Image(maIcon
));
309 void InsertionIndicatorOverlay::SetLayerInvalidator (const SharedILayerInvalidator
& rpInvalidator
)
311 mpLayerInvalidator
= rpInvalidator
;
313 if (mbIsVisible
&& mpLayerInvalidator
)
314 mpLayerInvalidator
->Invalidate(GetBoundingBox());
317 void InsertionIndicatorOverlay::Show()
324 std::shared_ptr
<LayeredDevice
> pLayeredDevice (
325 mrSlideSorter
.GetView().GetLayeredDevice());
328 pLayeredDevice
->RegisterPainter(shared_from_this(), gnLayerIndex
);
329 if (mpLayerInvalidator
)
330 mpLayerInvalidator
->Invalidate(GetBoundingBox());
334 void InsertionIndicatorOverlay::Hide()
341 std::shared_ptr
<LayeredDevice
> pLayeredDevice (
342 mrSlideSorter
.GetView().GetLayeredDevice());
345 if (mpLayerInvalidator
)
346 mpLayerInvalidator
->Invalidate(GetBoundingBox());
347 pLayeredDevice
->RemovePainter(shared_from_this(), gnLayerIndex
);
351 ::tools::Rectangle
InsertionIndicatorOverlay::GetBoundingBox() const
353 return ::tools::Rectangle(maLocation
, maIcon
.GetSizePixel());
356 Size
InsertionIndicatorOverlay::GetSize() const
359 maIcon
.GetSizePixel().Width() + 10,
360 maIcon
.GetSizePixel().Height() + 10);
363 } } } // end of namespace ::sd::slidesorter::view
365 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */