bump product version to 6.3.0.0.beta1
[LibreOffice.git] / sd / source / ui / slidesorter / view / SlsPageObjectPainter.cxx
blob395181bbce4cbe4ebf38c2650c46a5173336c5cd
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 <controller/SlsProperties.hxx>
31 #include <Window.hxx>
32 #include <sdpage.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/vclenum.hxx>
35 #include <vcl/virdev.hxx>
36 #include <CustomAnimationEffect.hxx>
37 #include <memory>
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())
72 return;
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");
97 return false;
100 return true;
103 void PageObjectPainter::SetTheme (const std::shared_ptr<view::Theme>& rpTheme)
105 mpTheme = 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(
123 rpDescriptor,
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(
136 rpDescriptor,
137 PageObjectLayouter::Part::Preview,
138 PageObjectLayouter::ModelCoordinateSystem));
140 if (mpCache == nullptr)
141 return;
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);
151 else
152 rDevice.DrawBitmapEx(aBox.TopLeft(), aPreview);
156 BitmapEx PageObjectPainter::CreateMarkedPreview (
157 const Size& rSize,
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));
165 else
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));
190 if (bIsExcluded)
192 PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get();
194 BitmapEx aMarkedPreview (mpCache->GetMarkedPreviewBitmap(pPage));
195 const ::tools::Rectangle aPreviewBox (pPageObjectLayouter->GetBoundingBox(
196 rpDescriptor,
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),
205 pReferenceDevice);
206 mpCache->SetMarkedPreviewBitmap(pPage, aMarkedPreview);
208 return aMarkedPreview;
210 else
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(
222 rpDescriptor,
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);
236 else
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);
244 else
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);
254 else
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(
278 rpDescriptor,
279 PageObjectLayouter::Part::TransitionEffectIndicator,
280 PageObjectLayouter::ModelCoordinateSystem));
282 rDevice.DrawBitmapEx(
283 aBox.TopCenter(),
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();
297 if ( aIter != aEnd )
299 const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox(
300 rpDescriptor,
301 PageObjectLayouter::Part::CustomAnimationEffectIndicator,
302 PageObjectLayouter::ModelCoordinateSystem));
303 rDevice.DrawBitmapEx(
304 aBox.TopCenter(),
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 };
315 const int eState =
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;
323 switch (eState)
325 case MouseOver | Selected | Focused:
326 eColorType = Theme::Gradient_MouseOverSelectedAndFocusedPage;
327 bHasFocusBorder = true;
328 break;
330 case MouseOver | Selected:
331 eColorType = Theme::Gradient_MouseOverSelected;
332 bHasFocusBorder = false;
333 break;
335 case MouseOver:
336 eColorType = Theme::Gradient_MouseOverPage;
337 bHasFocusBorder = false;
338 break;
340 case MouseOver | Focused:
341 eColorType = Theme::Gradient_MouseOverPage;
342 bHasFocusBorder = true;
343 break;
345 case Selected | Focused:
346 eColorType = Theme::Gradient_SelectedAndFocusedPage;
347 bHasFocusBorder = true;
348 break;
350 case Selected:
351 eColorType = Theme::Gradient_SelectedPage;
352 bHasFocusBorder = false;
353 break;
355 case Focused:
356 eColorType = Theme::Gradient_FocusedPage;
357 bHasFocusBorder = true;
358 break;
360 case None:
361 default:
362 eColorType = Theme::Gradient_NormalPage;
363 bHasFocusBorder = false;
364 break;
367 const ::tools::Rectangle aFocusSize (pPageObjectLayouter->GetBoundingBox(
368 rpDescriptor,
369 PageObjectLayouter::Part::FocusIndicator,
370 PageObjectLayouter::ModelCoordinateSystem));
372 const ::tools::Rectangle aPageObjectBox (pPageObjectLayouter->GetBoundingBox(
373 rpDescriptor,
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)
402 if (nY<=nY1)
403 rDevice.SetLineColor(aTopColor);
404 else if (nY>=nY2)
405 rDevice.SetLineColor(aBottomColor);
406 else
408 Color aColor (aTopColor);
409 aColor.Merge(aBottomColor, 255 * (nY2-nY) / (nY2-nY1));
410 rDevice.SetLineColor(aColor);
412 rDevice.DrawLine(
413 Point(aPageObjectBox.Left(), nY+nTop),
414 Point(aPageObjectBox.Right(), nY+nTop));
417 else
419 rDevice.SetFillColor(aTopColor);
420 rDevice.DrawRect(aPageObjectBox);
423 // Paint the simple border and, for some backgrounds, the focus border.
424 if (bHasFocusBorder)
425 mpFocusBorderPainter->PaintFrame(rDevice, aPageObjectBox);
426 else
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(
432 rpDescriptor,
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));
450 rDevice.DrawLine(
451 Point(rBox.Left()-nDelta, rBox.Top()-nDelta),
452 Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta));
453 rDevice.DrawLine(
454 Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta),
455 Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta));
456 rDevice.DrawLine(
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));
461 rDevice.DrawLine(
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: */