bump product version to 6.4.0.3
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsPageObjectPainter.cxx
blob21d54e55082912b364b616803ecc8a81f42666c7
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/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 <Window.hxx>
31 #include <sdpage.hxx>
32 #include <vcl/virdev.hxx>
33 #include <CustomAnimationEffect.hxx>
34 #include <memory>
36 using namespace ::drawinglayer::primitive2d;
38 namespace sd { namespace slidesorter { namespace view {
40 //===== PageObjectPainter =====================================================
42 PageObjectPainter::PageObjectPainter (
43 const SlideSorter& rSlideSorter)
44 : mrLayouter(rSlideSorter.GetView().GetLayouter()),
45 mpCache(rSlideSorter.GetView().GetPreviewCache()),
46 mpTheme(rSlideSorter.GetTheme()),
47 mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber, *rSlideSorter.GetContentWindow())),
48 mpShadowPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_RawShadow))),
49 mpFocusBorderPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_FocusBorder)))
51 // Replace the color (not the alpha values) in the focus border with a
52 // color derived from the current selection color.
53 Color aColor (mpTheme->GetColor(Theme::Color_Selection));
54 sal_uInt16 nHue, nSat, nBri;
55 aColor.RGBtoHSB(nHue, nSat, nBri);
56 aColor = Color::HSBtoRGB(nHue, 28, 65);
57 mpFocusBorderPainter->AdaptColor(aColor);
60 PageObjectPainter::~PageObjectPainter()
64 void PageObjectPainter::PaintPageObject (
65 OutputDevice& rDevice,
66 const model::SharedPageDescriptor& rpDescriptor)
68 if (!UpdatePageObjectLayouter())
69 return;
71 PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get();
72 // Turn off antialiasing to avoid the bitmaps from being
73 // shifted by fractions of a pixel and thus show blurry edges.
74 const AntialiasingFlags nSavedAntialiasingMode (rDevice.GetAntialiasing());
75 rDevice.SetAntialiasing(nSavedAntialiasingMode & ~AntialiasingFlags::EnableB2dDraw);
77 PaintBackground(pPageObjectLayouter, rDevice, rpDescriptor);
78 PaintPreview(pPageObjectLayouter, rDevice, rpDescriptor);
79 PaintPageNumber(pPageObjectLayouter, rDevice, rpDescriptor);
80 PaintTransitionEffect(pPageObjectLayouter, rDevice, rpDescriptor);
81 if (rpDescriptor->GetPage()->hasAnimationNode())
82 PaintCustomAnimationEffect(pPageObjectLayouter, rDevice, rpDescriptor);
83 rDevice.SetAntialiasing(nSavedAntialiasingMode);
86 bool PageObjectPainter::UpdatePageObjectLayouter()
88 // The page object layouter is quite volatile. It may have been replaced
89 // since the last call. Update it now.
90 PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get();
91 if ( ! pPageObjectLayouter)
93 OSL_FAIL("no page object layouter");
94 return false;
97 return true;
100 void PageObjectPainter::SetTheme (const std::shared_ptr<view::Theme>& rpTheme)
102 mpTheme = rpTheme;
105 void PageObjectPainter::PaintBackground (
106 PageObjectLayouter *pPageObjectLayouter,
107 OutputDevice& rDevice,
108 const model::SharedPageDescriptor& rpDescriptor) const
110 PaintBackgroundDetail(pPageObjectLayouter, rDevice, rpDescriptor);
112 // Fill the interior of the preview area with the default background
113 // color of the page.
114 SdPage* pPage = rpDescriptor->GetPage();
115 if (pPage != nullptr)
117 rDevice.SetFillColor(pPage->GetPageBackgroundColor(nullptr));
118 rDevice.SetLineColor(pPage->GetPageBackgroundColor(nullptr));
119 const ::tools::Rectangle aPreviewBox (pPageObjectLayouter->GetBoundingBox(
120 rpDescriptor,
121 PageObjectLayouter::Part::Preview,
122 PageObjectLayouter::ModelCoordinateSystem));
123 rDevice.DrawRect(aPreviewBox);
127 void PageObjectPainter::PaintPreview (
128 PageObjectLayouter *pPageObjectLayouter,
129 OutputDevice& rDevice,
130 const model::SharedPageDescriptor& rpDescriptor) const
132 const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox(
133 rpDescriptor,
134 PageObjectLayouter::Part::Preview,
135 PageObjectLayouter::ModelCoordinateSystem));
137 if (mpCache == nullptr)
138 return;
140 const SdrPage* pPage = rpDescriptor->GetPage();
141 mpCache->SetPreciousFlag(pPage, true);
143 const BitmapEx aPreview (GetPreviewBitmap(rpDescriptor, &rDevice));
144 if ( ! aPreview.IsEmpty())
146 if (aPreview.GetSizePixel() != aBox.GetSize())
147 rDevice.DrawBitmapEx(aBox.TopLeft(), aBox.GetSize(), aPreview);
148 else
149 rDevice.DrawBitmapEx(aBox.TopLeft(), aPreview);
153 BitmapEx PageObjectPainter::CreateMarkedPreview (
154 const Size& rSize,
155 const BitmapEx& rPreview,
156 const BitmapEx& rOverlay,
157 const OutputDevice* pReferenceDevice)
159 ScopedVclPtr<VirtualDevice> pDevice;
160 if (pReferenceDevice != nullptr)
161 pDevice.disposeAndReset(VclPtr<VirtualDevice>::Create(*pReferenceDevice));
162 else
163 pDevice.disposeAndReset(VclPtr<VirtualDevice>::Create());
164 pDevice->SetOutputSizePixel(rSize);
166 pDevice->DrawBitmapEx(Point(0,0), rSize, rPreview);
168 // Paint bitmap tiled over the preview to mark it as excluded.
169 const sal_Int32 nIconWidth (rOverlay.GetSizePixel().Width());
170 const sal_Int32 nIconHeight (rOverlay.GetSizePixel().Height());
171 if (nIconWidth>0 && nIconHeight>0)
173 for (long nX=0; nX<rSize.Width(); nX+=nIconWidth)
174 for (long nY=0; nY<rSize.Height(); nY+=nIconHeight)
175 pDevice->DrawBitmapEx(Point(nX,nY), rOverlay);
177 return pDevice->GetBitmapEx(Point(0,0), rSize);
180 BitmapEx PageObjectPainter::GetPreviewBitmap (
181 const model::SharedPageDescriptor& rpDescriptor,
182 const OutputDevice* pReferenceDevice) const
184 const SdrPage* pPage = rpDescriptor->GetPage();
185 const bool bIsExcluded (rpDescriptor->HasState(model::PageDescriptor::ST_Excluded));
187 if (bIsExcluded)
189 PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get();
191 BitmapEx aMarkedPreview (mpCache->GetMarkedPreviewBitmap(pPage));
192 const ::tools::Rectangle aPreviewBox (pPageObjectLayouter->GetBoundingBox(
193 rpDescriptor,
194 PageObjectLayouter::Part::Preview,
195 PageObjectLayouter::ModelCoordinateSystem));
196 if (aMarkedPreview.IsEmpty() || aMarkedPreview.GetSizePixel()!=aPreviewBox.GetSize())
198 aMarkedPreview = CreateMarkedPreview(
199 aPreviewBox.GetSize(),
200 mpCache->GetPreviewBitmap(pPage,true),
201 mpTheme->GetIcon(Theme::Icon_HideSlideOverlay),
202 pReferenceDevice);
203 mpCache->SetMarkedPreviewBitmap(pPage, aMarkedPreview);
205 return aMarkedPreview;
207 else
209 return mpCache->GetPreviewBitmap(pPage,false);
213 void PageObjectPainter::PaintPageNumber (
214 PageObjectLayouter *pPageObjectLayouter,
215 OutputDevice& rDevice,
216 const model::SharedPageDescriptor& rpDescriptor) const
218 const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox(
219 rpDescriptor,
220 PageObjectLayouter::Part::PageNumber,
221 PageObjectLayouter::ModelCoordinateSystem));
223 // Determine the color of the page number.
224 Color aPageNumberColor (mpTheme->GetColor(Theme::Color_PageNumberDefault));
225 if (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ||
226 rpDescriptor->HasState(model::PageDescriptor::ST_Selected))
228 // Page number is painted on background for hover or selection or
229 // both. Each of these background colors has a predefined luminance
230 // which is compatible with the PageNumberHover color.
231 aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberHover);
233 else
235 const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background));
236 const sal_Int32 nBackgroundLuminance (aBackgroundColor.GetLuminance());
237 // When the background color is black then this is interpreted as
238 // high contrast mode and the font color is set to white.
239 if (nBackgroundLuminance == 0)
240 aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberHighContrast);
241 else
243 // Compare luminance of default page number color and background
244 // color. When the two are similar then use a darker
245 // (preferred) or brighter font color.
246 const sal_Int32 nFontLuminance (aPageNumberColor.GetLuminance());
247 if (abs(nBackgroundLuminance - nFontLuminance) < 60)
249 if (nBackgroundLuminance > nFontLuminance-30)
250 aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberBrightBackground);
251 else
252 aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberDarkBackground);
257 // Paint the page number.
258 OSL_ASSERT(rpDescriptor->GetPage()!=nullptr);
259 const sal_Int32 nPageNumber ((rpDescriptor->GetPage()->GetPageNum() - 1) / 2 + 1);
260 const OUString sPageNumber(OUString::number(nPageNumber));
261 rDevice.SetFont(*mpPageNumberFont);
262 rDevice.SetTextColor(aPageNumberColor);
263 rDevice.DrawText(aBox, sPageNumber, DrawTextFlags::Right | DrawTextFlags::VCenter);
266 void PageObjectPainter::PaintTransitionEffect (
267 PageObjectLayouter *pPageObjectLayouter,
268 OutputDevice& rDevice,
269 const model::SharedPageDescriptor& rpDescriptor)
271 const SdPage* pPage = rpDescriptor->GetPage();
272 if (pPage!=nullptr && pPage->getTransitionType() > 0)
274 const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox(
275 rpDescriptor,
276 PageObjectLayouter::Part::TransitionEffectIndicator,
277 PageObjectLayouter::ModelCoordinateSystem));
279 rDevice.DrawBitmapEx(
280 aBox.TopCenter(),
281 pPageObjectLayouter->GetTransitionEffectIcon().GetBitmapEx());
285 void PageObjectPainter::PaintCustomAnimationEffect (
286 PageObjectLayouter *pPageObjectLayouter,
287 OutputDevice& rDevice,
288 const model::SharedPageDescriptor& rpDescriptor)
290 SdPage* pPage = rpDescriptor->GetPage();
291 std::shared_ptr< MainSequence > aMainSequence = pPage->getMainSequence();
292 EffectSequence::iterator aIter = aMainSequence->getBegin();
293 EffectSequence::iterator aEnd = aMainSequence->getEnd();
294 if ( aIter != aEnd )
296 const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox(
297 rpDescriptor,
298 PageObjectLayouter::Part::CustomAnimationEffectIndicator,
299 PageObjectLayouter::ModelCoordinateSystem));
300 rDevice.DrawBitmapEx(
301 aBox.TopCenter(),
302 pPageObjectLayouter->GetCustomAnimationEffectIcon().GetBitmapEx());
306 void PageObjectPainter::PaintBackgroundDetail (
307 PageObjectLayouter *pPageObjectLayouter,
308 OutputDevice& rDevice,
309 const model::SharedPageDescriptor& rpDescriptor) const
311 enum State { None = 0x00, Selected = 0x01, MouseOver = 0x02, Focused = 0x04 };
312 const int eState =
313 (rpDescriptor->HasState(model::PageDescriptor::ST_Selected) ? Selected : None)
314 | (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ? MouseOver : None)
315 | (rpDescriptor->HasState(model::PageDescriptor::ST_Focused) ? Focused : None);
317 bool bHasFocusBorder;
318 Theme::GradientColorType eColorType;
320 switch (eState)
322 case MouseOver | Selected | Focused:
323 eColorType = Theme::Gradient_MouseOverSelectedAndFocusedPage;
324 bHasFocusBorder = true;
325 break;
327 case MouseOver | Selected:
328 eColorType = Theme::Gradient_MouseOverSelected;
329 bHasFocusBorder = false;
330 break;
332 case MouseOver:
333 eColorType = Theme::Gradient_MouseOverPage;
334 bHasFocusBorder = false;
335 break;
337 case MouseOver | Focused:
338 eColorType = Theme::Gradient_MouseOverPage;
339 bHasFocusBorder = true;
340 break;
342 case Selected | Focused:
343 eColorType = Theme::Gradient_SelectedAndFocusedPage;
344 bHasFocusBorder = true;
345 break;
347 case Selected:
348 eColorType = Theme::Gradient_SelectedPage;
349 bHasFocusBorder = false;
350 break;
352 case Focused:
353 eColorType = Theme::Gradient_FocusedPage;
354 bHasFocusBorder = true;
355 break;
357 case None:
358 default:
359 eColorType = Theme::Gradient_NormalPage;
360 bHasFocusBorder = false;
361 break;
364 const ::tools::Rectangle aFocusSize (pPageObjectLayouter->GetBoundingBox(
365 rpDescriptor,
366 PageObjectLayouter::Part::FocusIndicator,
367 PageObjectLayouter::ModelCoordinateSystem));
369 const ::tools::Rectangle aPageObjectBox (pPageObjectLayouter->GetBoundingBox(
370 rpDescriptor,
371 PageObjectLayouter::Part::PageObject,
372 PageObjectLayouter::ModelCoordinateSystem));
374 // Fill the background with the background color of the slide sorter.
375 const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background));
376 rDevice.SetFillColor(aBackgroundColor);
377 rDevice.SetLineColor(aBackgroundColor);
378 rDevice.DrawRect(aFocusSize);
380 // Paint the slide area with a linear gradient that starts some pixels
381 // below the top and ends some pixels above the bottom.
382 const Color aTopColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Fill1));
383 const Color aBottomColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Fill2));
384 if (aTopColor != aBottomColor)
386 const sal_Int32 nHeight (aPageObjectBox.GetHeight());
387 const sal_Int32 nDefaultConstantSize(nHeight/4);
388 const sal_Int32 nMinimalGradientSize(40);
389 const sal_Int32 nY1 (
390 ::std::max<sal_Int32>(
392 ::std::min<sal_Int32>(
393 nDefaultConstantSize,
394 (nHeight - nMinimalGradientSize)/2)));
395 const sal_Int32 nY2 (nHeight-nY1);
396 const sal_Int32 nTop (aPageObjectBox.Top());
397 for (sal_Int32 nY=0; nY<nHeight; ++nY)
399 if (nY<=nY1)
400 rDevice.SetLineColor(aTopColor);
401 else if (nY>=nY2)
402 rDevice.SetLineColor(aBottomColor);
403 else
405 Color aColor (aTopColor);
406 aColor.Merge(aBottomColor, 255 * (nY2-nY) / (nY2-nY1));
407 rDevice.SetLineColor(aColor);
409 rDevice.DrawLine(
410 Point(aPageObjectBox.Left(), nY+nTop),
411 Point(aPageObjectBox.Right(), nY+nTop));
414 else
416 rDevice.SetFillColor(aTopColor);
417 rDevice.DrawRect(aPageObjectBox);
420 // Paint the simple border and, for some backgrounds, the focus border.
421 if (bHasFocusBorder)
422 mpFocusBorderPainter->PaintFrame(rDevice, aPageObjectBox);
423 else
424 PaintBorder(rDevice, eColorType, aPageObjectBox);
426 // Get bounding box of the preview around which a shadow is painted.
427 // Compensate for the border around the preview.
428 const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox(
429 rpDescriptor,
430 PageObjectLayouter::Part::Preview,
431 PageObjectLayouter::ModelCoordinateSystem));
432 ::tools::Rectangle aFrameBox (aBox.Left()-1,aBox.Top()-1,aBox.Right()+1,aBox.Bottom()+1);
433 mpShadowPainter->PaintFrame(rDevice, aFrameBox);
436 void PageObjectPainter::PaintBorder (
437 OutputDevice& rDevice,
438 const Theme::GradientColorType eColorType,
439 const ::tools::Rectangle& rBox) const
441 rDevice.SetFillColor();
442 const sal_Int32 nBorderWidth (1);
443 for (int nIndex=0; nIndex<nBorderWidth; ++nIndex)
445 const int nDelta (nIndex);
446 rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Border2));
447 rDevice.DrawLine(
448 Point(rBox.Left()-nDelta, rBox.Top()-nDelta),
449 Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta));
450 rDevice.DrawLine(
451 Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta),
452 Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta));
453 rDevice.DrawLine(
454 Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta),
455 Point(rBox.Right()+nDelta, rBox.Top()-nDelta));
457 rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Border1));
458 rDevice.DrawLine(
459 Point(rBox.Left()-nDelta, rBox.Top()-nDelta),
460 Point(rBox.Right()+nDelta, rBox.Top()-nDelta));
464 } } } // end of namespace sd::slidesorter::view
466 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */