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/SlsPageObjectPainter.hxx>
22 #include <model/SlsPageDescriptor.hxx>
23 #include <view/SlideSorterView.hxx>
24 #include <view/SlsPageObjectLayouter.hxx>
25 #include <view/SlsLayouter.hxx>
26 #include <view/SlsTheme.hxx>
27 #include <SlideSorter.hxx>
28 #include "SlsFramePainter.hxx"
29 #include <cache/SlsPageCache.hxx>
30 #include <controller/SlsProperties.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/vclenum.hxx>
35 #include <vcl/virdev.hxx>
36 #include <CustomAnimationEffect.hxx>
39 using namespace ::drawinglayer::primitive2d
;
41 namespace sd
{ namespace slidesorter
{ namespace view
{
43 //===== PageObjectPainter =====================================================
45 PageObjectPainter::PageObjectPainter (
46 const SlideSorter
& rSlideSorter
)
47 : mrLayouter(rSlideSorter
.GetView().GetLayouter()),
48 mpCache(rSlideSorter
.GetView().GetPreviewCache()),
49 mpTheme(rSlideSorter
.GetTheme()),
50 mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber
, *rSlideSorter
.GetContentWindow())),
51 mpShadowPainter(new FramePainter(mpTheme
->GetIcon(Theme::Icon_RawShadow
))),
52 mpFocusBorderPainter(new FramePainter(mpTheme
->GetIcon(Theme::Icon_FocusBorder
)))
54 // Replace the color (not the alpha values) in the focus border with a
55 // color derived from the current selection color.
56 Color
aColor (mpTheme
->GetColor(Theme::Color_Selection
));
57 sal_uInt16 nHue
, nSat
, nBri
;
58 aColor
.RGBtoHSB(nHue
, nSat
, nBri
);
59 aColor
= Color::HSBtoRGB(nHue
, 28, 65);
60 mpFocusBorderPainter
->AdaptColor(aColor
);
63 PageObjectPainter::~PageObjectPainter()
67 void PageObjectPainter::PaintPageObject (
68 OutputDevice
& rDevice
,
69 const model::SharedPageDescriptor
& rpDescriptor
)
71 if (!UpdatePageObjectLayouter())
74 PageObjectLayouter
*pPageObjectLayouter
= mrLayouter
.GetPageObjectLayouter().get();
75 // Turn off antialiasing to avoid the bitmaps from being
76 // shifted by fractions of a pixel and thus show blurry edges.
77 const AntialiasingFlags
nSavedAntialiasingMode (rDevice
.GetAntialiasing());
78 rDevice
.SetAntialiasing(nSavedAntialiasingMode
& ~AntialiasingFlags::EnableB2dDraw
);
80 PaintBackground(pPageObjectLayouter
, rDevice
, rpDescriptor
);
81 PaintPreview(pPageObjectLayouter
, rDevice
, rpDescriptor
);
82 PaintPageNumber(pPageObjectLayouter
, rDevice
, rpDescriptor
);
83 PaintTransitionEffect(pPageObjectLayouter
, rDevice
, rpDescriptor
);
84 if (rpDescriptor
->GetPage()->hasAnimationNode())
85 PaintCustomAnimationEffect(pPageObjectLayouter
, rDevice
, rpDescriptor
);
86 rDevice
.SetAntialiasing(nSavedAntialiasingMode
);
89 bool PageObjectPainter::UpdatePageObjectLayouter()
91 // The page object layouter is quite volatile. It may have been replaced
92 // since the last call. Update it now.
93 PageObjectLayouter
*pPageObjectLayouter
= mrLayouter
.GetPageObjectLayouter().get();
94 if ( ! pPageObjectLayouter
)
96 OSL_FAIL("no page object layouter");
103 void PageObjectPainter::SetTheme (const std::shared_ptr
<view::Theme
>& rpTheme
)
108 void PageObjectPainter::PaintBackground (
109 PageObjectLayouter
*pPageObjectLayouter
,
110 OutputDevice
& rDevice
,
111 const model::SharedPageDescriptor
& rpDescriptor
) const
113 PaintBackgroundDetail(pPageObjectLayouter
, rDevice
, rpDescriptor
);
115 // Fill the interior of the preview area with the default background
116 // color of the page.
117 SdPage
* pPage
= rpDescriptor
->GetPage();
118 if (pPage
!= nullptr)
120 rDevice
.SetFillColor(pPage
->GetPageBackgroundColor(nullptr));
121 rDevice
.SetLineColor(pPage
->GetPageBackgroundColor(nullptr));
122 const ::tools::Rectangle
aPreviewBox (pPageObjectLayouter
->GetBoundingBox(
124 PageObjectLayouter::Part::Preview
,
125 PageObjectLayouter::ModelCoordinateSystem
));
126 rDevice
.DrawRect(aPreviewBox
);
130 void PageObjectPainter::PaintPreview (
131 PageObjectLayouter
*pPageObjectLayouter
,
132 OutputDevice
& rDevice
,
133 const model::SharedPageDescriptor
& rpDescriptor
) const
135 const ::tools::Rectangle
aBox (pPageObjectLayouter
->GetBoundingBox(
137 PageObjectLayouter::Part::Preview
,
138 PageObjectLayouter::ModelCoordinateSystem
));
140 if (mpCache
== nullptr)
143 const SdrPage
* pPage
= rpDescriptor
->GetPage();
144 mpCache
->SetPreciousFlag(pPage
, true);
146 const BitmapEx
aPreview (GetPreviewBitmap(rpDescriptor
, &rDevice
));
147 if ( ! aPreview
.IsEmpty())
149 if (aPreview
.GetSizePixel() != aBox
.GetSize())
150 rDevice
.DrawBitmapEx(aBox
.TopLeft(), aBox
.GetSize(), aPreview
);
152 rDevice
.DrawBitmapEx(aBox
.TopLeft(), aPreview
);
156 BitmapEx
PageObjectPainter::CreateMarkedPreview (
158 const BitmapEx
& rPreview
,
159 const BitmapEx
& rOverlay
,
160 const OutputDevice
* pReferenceDevice
)
162 ScopedVclPtr
<VirtualDevice
> pDevice
;
163 if (pReferenceDevice
!= nullptr)
164 pDevice
.disposeAndReset(VclPtr
<VirtualDevice
>::Create(*pReferenceDevice
));
166 pDevice
.disposeAndReset(VclPtr
<VirtualDevice
>::Create());
167 pDevice
->SetOutputSizePixel(rSize
);
169 pDevice
->DrawBitmapEx(Point(0,0), rSize
, rPreview
);
171 // Paint bitmap tiled over the preview to mark it as excluded.
172 const sal_Int32
nIconWidth (rOverlay
.GetSizePixel().Width());
173 const sal_Int32
nIconHeight (rOverlay
.GetSizePixel().Height());
174 if (nIconWidth
>0 && nIconHeight
>0)
176 for (long nX
=0; nX
<rSize
.Width(); nX
+=nIconWidth
)
177 for (long nY
=0; nY
<rSize
.Height(); nY
+=nIconHeight
)
178 pDevice
->DrawBitmapEx(Point(nX
,nY
), rOverlay
);
180 return pDevice
->GetBitmapEx(Point(0,0), rSize
);
183 BitmapEx
PageObjectPainter::GetPreviewBitmap (
184 const model::SharedPageDescriptor
& rpDescriptor
,
185 const OutputDevice
* pReferenceDevice
) const
187 const SdrPage
* pPage
= rpDescriptor
->GetPage();
188 const bool bIsExcluded (rpDescriptor
->HasState(model::PageDescriptor::ST_Excluded
));
192 PageObjectLayouter
*pPageObjectLayouter
= mrLayouter
.GetPageObjectLayouter().get();
194 BitmapEx
aMarkedPreview (mpCache
->GetMarkedPreviewBitmap(pPage
));
195 const ::tools::Rectangle
aPreviewBox (pPageObjectLayouter
->GetBoundingBox(
197 PageObjectLayouter::Part::Preview
,
198 PageObjectLayouter::ModelCoordinateSystem
));
199 if (aMarkedPreview
.IsEmpty() || aMarkedPreview
.GetSizePixel()!=aPreviewBox
.GetSize())
201 aMarkedPreview
= CreateMarkedPreview(
202 aPreviewBox
.GetSize(),
203 mpCache
->GetPreviewBitmap(pPage
,true),
204 mpTheme
->GetIcon(Theme::Icon_HideSlideOverlay
),
206 mpCache
->SetMarkedPreviewBitmap(pPage
, aMarkedPreview
);
208 return aMarkedPreview
;
212 return mpCache
->GetPreviewBitmap(pPage
,false);
216 void PageObjectPainter::PaintPageNumber (
217 PageObjectLayouter
*pPageObjectLayouter
,
218 OutputDevice
& rDevice
,
219 const model::SharedPageDescriptor
& rpDescriptor
) const
221 const ::tools::Rectangle
aBox (pPageObjectLayouter
->GetBoundingBox(
223 PageObjectLayouter::Part::PageNumber
,
224 PageObjectLayouter::ModelCoordinateSystem
));
226 // Determine the color of the page number.
227 Color
aPageNumberColor (mpTheme
->GetColor(Theme::Color_PageNumberDefault
));
228 if (rpDescriptor
->HasState(model::PageDescriptor::ST_MouseOver
) ||
229 rpDescriptor
->HasState(model::PageDescriptor::ST_Selected
))
231 // Page number is painted on background for hover or selection or
232 // both. Each of these background colors has a predefined luminance
233 // which is compatible with the PageNumberHover color.
234 aPageNumberColor
= mpTheme
->GetColor(Theme::Color_PageNumberHover
);
238 const Color
aBackgroundColor (mpTheme
->GetColor(Theme::Color_Background
));
239 const sal_Int32
nBackgroundLuminance (aBackgroundColor
.GetLuminance());
240 // When the background color is black then this is interpreted as
241 // high contrast mode and the font color is set to white.
242 if (nBackgroundLuminance
== 0)
243 aPageNumberColor
= mpTheme
->GetColor(Theme::Color_PageNumberHighContrast
);
246 // Compare luminance of default page number color and background
247 // color. When the two are similar then use a darker
248 // (preferred) or brighter font color.
249 const sal_Int32
nFontLuminance (aPageNumberColor
.GetLuminance());
250 if (abs(nBackgroundLuminance
- nFontLuminance
) < 60)
252 if (nBackgroundLuminance
> nFontLuminance
-30)
253 aPageNumberColor
= mpTheme
->GetColor(Theme::Color_PageNumberBrightBackground
);
255 aPageNumberColor
= mpTheme
->GetColor(Theme::Color_PageNumberDarkBackground
);
260 // Paint the page number.
261 OSL_ASSERT(rpDescriptor
->GetPage()!=nullptr);
262 const sal_Int32
nPageNumber ((rpDescriptor
->GetPage()->GetPageNum() - 1) / 2 + 1);
263 const OUString
sPageNumber(OUString::number(nPageNumber
));
264 rDevice
.SetFont(*mpPageNumberFont
);
265 rDevice
.SetTextColor(aPageNumberColor
);
266 rDevice
.DrawText(aBox
, sPageNumber
, DrawTextFlags::Right
| DrawTextFlags::VCenter
);
269 void PageObjectPainter::PaintTransitionEffect (
270 PageObjectLayouter
*pPageObjectLayouter
,
271 OutputDevice
& rDevice
,
272 const model::SharedPageDescriptor
& rpDescriptor
)
274 const SdPage
* pPage
= rpDescriptor
->GetPage();
275 if (pPage
!=nullptr && pPage
->getTransitionType() > 0)
277 const ::tools::Rectangle
aBox (pPageObjectLayouter
->GetBoundingBox(
279 PageObjectLayouter::Part::TransitionEffectIndicator
,
280 PageObjectLayouter::ModelCoordinateSystem
));
282 rDevice
.DrawBitmapEx(
284 pPageObjectLayouter
->GetTransitionEffectIcon().GetBitmapEx());
288 void PageObjectPainter::PaintCustomAnimationEffect (
289 PageObjectLayouter
*pPageObjectLayouter
,
290 OutputDevice
& rDevice
,
291 const model::SharedPageDescriptor
& rpDescriptor
)
293 SdPage
* pPage
= rpDescriptor
->GetPage();
294 std::shared_ptr
< MainSequence
> aMainSequence
= pPage
->getMainSequence();
295 EffectSequence::iterator aIter
= aMainSequence
->getBegin();
296 EffectSequence::iterator aEnd
= aMainSequence
->getEnd();
299 const ::tools::Rectangle
aBox (pPageObjectLayouter
->GetBoundingBox(
301 PageObjectLayouter::Part::CustomAnimationEffectIndicator
,
302 PageObjectLayouter::ModelCoordinateSystem
));
303 rDevice
.DrawBitmapEx(
305 pPageObjectLayouter
->GetCustomAnimationEffectIcon().GetBitmapEx());
309 void PageObjectPainter::PaintBackgroundDetail (
310 PageObjectLayouter
*pPageObjectLayouter
,
311 OutputDevice
& rDevice
,
312 const model::SharedPageDescriptor
& rpDescriptor
) const
314 enum State
{ None
= 0x00, Selected
= 0x01, MouseOver
= 0x02, Focused
= 0x04 };
316 (rpDescriptor
->HasState(model::PageDescriptor::ST_Selected
) ? Selected
: None
)
317 | (rpDescriptor
->HasState(model::PageDescriptor::ST_MouseOver
) ? MouseOver
: None
)
318 | (rpDescriptor
->HasState(model::PageDescriptor::ST_Focused
) ? Focused
: None
);
320 bool bHasFocusBorder
;
321 Theme::GradientColorType eColorType
;
325 case MouseOver
| Selected
| Focused
:
326 eColorType
= Theme::Gradient_MouseOverSelectedAndFocusedPage
;
327 bHasFocusBorder
= true;
330 case MouseOver
| Selected
:
331 eColorType
= Theme::Gradient_MouseOverSelected
;
332 bHasFocusBorder
= false;
336 eColorType
= Theme::Gradient_MouseOverPage
;
337 bHasFocusBorder
= false;
340 case MouseOver
| Focused
:
341 eColorType
= Theme::Gradient_MouseOverPage
;
342 bHasFocusBorder
= true;
345 case Selected
| Focused
:
346 eColorType
= Theme::Gradient_SelectedAndFocusedPage
;
347 bHasFocusBorder
= true;
351 eColorType
= Theme::Gradient_SelectedPage
;
352 bHasFocusBorder
= false;
356 eColorType
= Theme::Gradient_FocusedPage
;
357 bHasFocusBorder
= true;
362 eColorType
= Theme::Gradient_NormalPage
;
363 bHasFocusBorder
= false;
367 const ::tools::Rectangle
aFocusSize (pPageObjectLayouter
->GetBoundingBox(
369 PageObjectLayouter::Part::FocusIndicator
,
370 PageObjectLayouter::ModelCoordinateSystem
));
372 const ::tools::Rectangle
aPageObjectBox (pPageObjectLayouter
->GetBoundingBox(
374 PageObjectLayouter::Part::PageObject
,
375 PageObjectLayouter::ModelCoordinateSystem
));
377 // Fill the background with the background color of the slide sorter.
378 const Color
aBackgroundColor (mpTheme
->GetColor(Theme::Color_Background
));
379 rDevice
.SetFillColor(aBackgroundColor
);
380 rDevice
.SetLineColor(aBackgroundColor
);
381 rDevice
.DrawRect(aFocusSize
);
383 // Paint the slide area with a linear gradient that starts some pixels
384 // below the top and ends some pixels above the bottom.
385 const Color
aTopColor(mpTheme
->GetGradientColor(eColorType
, Theme::GradientColorClass::Fill1
));
386 const Color
aBottomColor(mpTheme
->GetGradientColor(eColorType
, Theme::GradientColorClass::Fill2
));
387 if (aTopColor
!= aBottomColor
)
389 const sal_Int32
nHeight (aPageObjectBox
.GetHeight());
390 const sal_Int32
nDefaultConstantSize(nHeight
/4);
391 const sal_Int32
nMinimalGradientSize(40);
392 const sal_Int32
nY1 (
393 ::std::max
<sal_Int32
>(
395 ::std::min
<sal_Int32
>(
396 nDefaultConstantSize
,
397 (nHeight
- nMinimalGradientSize
)/2)));
398 const sal_Int32
nY2 (nHeight
-nY1
);
399 const sal_Int32
nTop (aPageObjectBox
.Top());
400 for (sal_Int32 nY
=0; nY
<nHeight
; ++nY
)
403 rDevice
.SetLineColor(aTopColor
);
405 rDevice
.SetLineColor(aBottomColor
);
408 Color
aColor (aTopColor
);
409 aColor
.Merge(aBottomColor
, 255 * (nY2
-nY
) / (nY2
-nY1
));
410 rDevice
.SetLineColor(aColor
);
413 Point(aPageObjectBox
.Left(), nY
+nTop
),
414 Point(aPageObjectBox
.Right(), nY
+nTop
));
419 rDevice
.SetFillColor(aTopColor
);
420 rDevice
.DrawRect(aPageObjectBox
);
423 // Paint the simple border and, for some backgrounds, the focus border.
425 mpFocusBorderPainter
->PaintFrame(rDevice
, aPageObjectBox
);
427 PaintBorder(rDevice
, eColorType
, aPageObjectBox
);
429 // Get bounding box of the preview around which a shadow is painted.
430 // Compensate for the border around the preview.
431 const ::tools::Rectangle
aBox (pPageObjectLayouter
->GetBoundingBox(
433 PageObjectLayouter::Part::Preview
,
434 PageObjectLayouter::ModelCoordinateSystem
));
435 ::tools::Rectangle
aFrameBox (aBox
.Left()-1,aBox
.Top()-1,aBox
.Right()+1,aBox
.Bottom()+1);
436 mpShadowPainter
->PaintFrame(rDevice
, aFrameBox
);
439 void PageObjectPainter::PaintBorder (
440 OutputDevice
& rDevice
,
441 const Theme::GradientColorType eColorType
,
442 const ::tools::Rectangle
& rBox
) const
444 rDevice
.SetFillColor();
445 const sal_Int32
nBorderWidth (1);
446 for (int nIndex
=0; nIndex
<nBorderWidth
; ++nIndex
)
448 const int nDelta (nIndex
);
449 rDevice
.SetLineColor(mpTheme
->GetGradientColor(eColorType
, Theme::GradientColorClass::Border2
));
451 Point(rBox
.Left()-nDelta
, rBox
.Top()-nDelta
),
452 Point(rBox
.Left()-nDelta
, rBox
.Bottom()+nDelta
));
454 Point(rBox
.Left()-nDelta
, rBox
.Bottom()+nDelta
),
455 Point(rBox
.Right()+nDelta
, rBox
.Bottom()+nDelta
));
457 Point(rBox
.Right()+nDelta
, rBox
.Bottom()+nDelta
),
458 Point(rBox
.Right()+nDelta
, rBox
.Top()-nDelta
));
460 rDevice
.SetLineColor(mpTheme
->GetGradientColor(eColorType
, Theme::GradientColorClass::Border1
));
462 Point(rBox
.Left()-nDelta
, rBox
.Top()-nDelta
),
463 Point(rBox
.Right()+nDelta
, rBox
.Top()-nDelta
));
467 } } } // end of namespace sd::slidesorter::view
469 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */