xref: /AOO41X/main/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx (revision 7871dc3ea494bf86c742e1f4dfc9c6e20f5bcb2a)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "precompiled_sd.hxx"
25 
26 #include "view/SlsPageObjectPainter.hxx"
27 
28 #include "model/SlsPageDescriptor.hxx"
29 #include "view/SlideSorterView.hxx"
30 #include "view/SlsPageObjectLayouter.hxx"
31 #include "view/SlsLayouter.hxx"
32 #include "view/SlsTheme.hxx"
33 #include "view/SlsButtonBar.hxx"
34 #include "SlsFramePainter.hxx"
35 #include "cache/SlsPageCache.hxx"
36 #include "controller/SlsProperties.hxx"
37 #include "Window.hxx"
38 #include "sdpage.hxx"
39 #include "sdresid.hxx"
40 #include <vcl/svapp.hxx>
41 #include <vcl/vclenum.hxx>
42 #include <vcl/virdev.hxx>
43 #include <boost/scoped_ptr.hpp>
44 
45 using namespace ::drawinglayer::primitive2d;
46 
47 namespace sd { namespace slidesorter { namespace view {
48 
49 namespace {
50 
51 sal_uInt8 Blend (
52     const sal_uInt8 nValue1,
53     const sal_uInt8 nValue2,
54     const double nWeight)
55 {
56     const double nValue (nValue1*(1-nWeight) + nValue2 * nWeight);
57     if (nValue < 0)
58         return 0;
59     else if (nValue > 255)
60         return 255;
61     else
62         return (sal_uInt8)nValue;
63 }
64 
65 sal_uInt8 ClampColorChannel (const double nValue)
66 {
67     if (nValue <= 0)
68         return 0;
69     else if (nValue >= 255)
70         return 255;
71     else
72         return sal_uInt8(nValue);
73 }
74 
75 sal_uInt8 CalculateColorChannel(
76     const double nColor1,
77     const double nColor2,
78     const double nAlpha1,
79     const double nAlpha2,
80     const double nAlpha0)
81 {
82     if (nAlpha0 == 0)
83         return 0;
84 
85     const double nColor0 ((nAlpha1*nColor1 + nAlpha1*nAlpha2*nColor1 + nAlpha2*nColor2) / nAlpha0);
86     return ClampColorChannel(255 * nColor0);
87 }
88 
89 } // end of anonymous namespace
90 
91 
92 
93 
94 //===== PageObjectPainter =====================================================
95 
96 PageObjectPainter::PageObjectPainter (
97     const SlideSorter& rSlideSorter)
98     : mrLayouter(rSlideSorter.GetView().GetLayouter()),
99       mpPageObjectLayouter(),
100       mpCache(rSlideSorter.GetView().GetPreviewCache()),
101       mpProperties(rSlideSorter.GetProperties()),
102       mpTheme(rSlideSorter.GetTheme()),
103       mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber, *rSlideSorter.GetContentWindow())),
104       mpShadowPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_RawShadow))),
105       mpFocusBorderPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_FocusBorder))),
106       maNormalBackground(),
107       maSelectionBackground(),
108       maFocusedSelectionBackground(),
109       maMouseOverBackground(),
110       maMouseOverFocusedBackground(),
111       msUnhideString(mpTheme->GetString(Theme::String_Unhide)),
112       mrButtonBar(rSlideSorter.GetView().GetButtonBar()),
113       maSize()
114 {
115     // Replace the color (not the alpha values) in the focus border with a
116     // color derived from the current selection color.
117     Color aColor (mpTheme->GetColor(Theme::Color_Selection));
118     sal_uInt16 nHue, nSat, nBri;
119     aColor.RGBtoHSB(nHue, nSat, nBri);
120     aColor = Color::HSBtoRGB(nHue, 28, 65);
121     mpFocusBorderPainter->AdaptColor(aColor, true);
122 }
123 
124 
125 
126 
127 PageObjectPainter::~PageObjectPainter (void)
128 {
129 }
130 
131 
132 
133 
134 void PageObjectPainter::PaintPageObject (
135     OutputDevice& rDevice,
136     const model::SharedPageDescriptor& rpDescriptor)
137 {
138     // The page object layouter is quite volatile. It may have been replaced
139     // since the last call.  Update it now.
140     mpPageObjectLayouter = mrLayouter.GetPageObjectLayouter();
141     if ( ! mpPageObjectLayouter)
142     {
143         OSL_ASSERT(mpPageObjectLayouter);
144         return;
145     }
146 
147     // Turn off antialiasing to avoid the bitmaps from being shifted by
148     // fractions of a pixel and thus show blurry edges.
149     const sal_uInt16 nSavedAntialiasingMode (rDevice.GetAntialiasing());
150     rDevice.SetAntialiasing(nSavedAntialiasingMode & ~ANTIALIASING_ENABLE_B2DDRAW);
151 
152     PaintBackground(rDevice, rpDescriptor);
153     PaintPreview(rDevice, rpDescriptor);
154     PaintPageNumber(rDevice, rpDescriptor);
155     PaintTransitionEffect(rDevice, rpDescriptor);
156     mrButtonBar.Paint(rDevice, rpDescriptor);
157 
158     rDevice.SetAntialiasing(nSavedAntialiasingMode);
159 }
160 
161 
162 
163 
164 void PageObjectPainter::NotifyResize (const bool bForce)
165 {
166     if (bForce || ! mpPageObjectLayouter)
167         InvalidateBitmaps();
168     else
169     {
170         const Size aSize (mpPageObjectLayouter->GetSize(
171                 PageObjectLayouter::FocusIndicator,
172                 PageObjectLayouter::WindowCoordinateSystem));
173         if ( maSize!=aSize)
174         {
175             maSize = aSize;
176             InvalidateBitmaps();
177         }
178     }
179 }
180 
181 
182 
183 
184 void PageObjectPainter::InvalidateBitmaps (void)
185 {
186     maNormalBackground.SetEmpty();
187     maSelectionBackground.SetEmpty();
188     maFocusedSelectionBackground.SetEmpty();
189     maFocusedBackground.SetEmpty();
190     maMouseOverBackground.SetEmpty();
191     maMouseOverFocusedBackground.SetEmpty();
192     maMouseOverSelectedAndFocusedBackground.SetEmpty();
193 }
194 
195 
196 
197 
198 void PageObjectPainter::SetTheme (const ::boost::shared_ptr<view::Theme>& rpTheme)
199 {
200     mpTheme = rpTheme;
201     NotifyResize(true);
202 }
203 
204 
205 
206 
207 void PageObjectPainter::PaintBackground (
208     OutputDevice& rDevice,
209     const model::SharedPageDescriptor& rpDescriptor)
210 {
211     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
212         rpDescriptor,
213         PageObjectLayouter::FocusIndicator,
214         PageObjectLayouter::ModelCoordinateSystem));
215 
216     const Bitmap& rBackground (GetBackgroundForState(rpDescriptor, rDevice));
217     rDevice.DrawBitmap(aBox.TopLeft(), rBackground);
218 
219     // Fill the interior of the preview area with the default background
220     // color of the page.
221     SdPage* pPage = rpDescriptor->GetPage();
222     if (pPage != NULL)
223     {
224         rDevice.SetFillColor(pPage->GetPageBackgroundColor(NULL));
225         rDevice.SetLineColor(pPage->GetPageBackgroundColor(NULL));
226         const Rectangle aPreviewBox (mpPageObjectLayouter->GetBoundingBox(
227             rpDescriptor,
228             PageObjectLayouter::Preview,
229             PageObjectLayouter::ModelCoordinateSystem));
230         rDevice.DrawRect(aPreviewBox);
231     }
232 }
233 
234 
235 
236 
237 void PageObjectPainter::PaintPreview (
238     OutputDevice& rDevice,
239     const model::SharedPageDescriptor& rpDescriptor) const
240 {
241     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
242         rpDescriptor,
243         PageObjectLayouter::Preview,
244         PageObjectLayouter::ModelCoordinateSystem));
245 
246     if (mpCache != NULL)
247     {
248         const SdrPage* pPage = rpDescriptor->GetPage();
249         mpCache->SetPreciousFlag(pPage, true);
250 
251         const Bitmap aPreview (GetPreviewBitmap(rpDescriptor, &rDevice));
252         if ( ! aPreview.IsEmpty())
253         {
254             if (aPreview.GetSizePixel() != aBox.GetSize())
255                 rDevice.DrawBitmap(aBox.TopLeft(), aBox.GetSize(), aPreview);
256             else
257                 rDevice.DrawBitmap(aBox.TopLeft(), aPreview);
258         }
259     }
260 }
261 
262 
263 
264 
265 Bitmap PageObjectPainter::CreateMarkedPreview (
266     const Size& rSize,
267     const Bitmap& rPreview,
268     const BitmapEx& rOverlay,
269     const OutputDevice* pReferenceDevice) const
270 {
271     ::boost::scoped_ptr<VirtualDevice> pDevice;
272     if (pReferenceDevice != NULL)
273         pDevice.reset(new VirtualDevice(*pReferenceDevice));
274     else
275         pDevice.reset(new VirtualDevice());
276     pDevice->SetOutputSizePixel(rSize);
277 
278     pDevice->DrawBitmap(Point(0,0), rSize, rPreview);
279 
280     // Paint bitmap tiled over the preview to mark it as excluded.
281     const sal_Int32 nIconWidth (rOverlay.GetSizePixel().Width());
282     const sal_Int32 nIconHeight (rOverlay.GetSizePixel().Height());
283     if (nIconWidth>0 && nIconHeight>0)
284     {
285         for (sal_Int32 nX=0; nX<rSize.Width(); nX+=nIconWidth)
286             for (sal_Int32 nY=0; nY<rSize.Height(); nY+=nIconHeight)
287                 pDevice->DrawBitmapEx(Point(nX,nY), rOverlay);
288     }
289     return pDevice->GetBitmap(Point(0,0), rSize);
290 }
291 
292 
293 
294 
295 Bitmap PageObjectPainter::GetPreviewBitmap (
296     const model::SharedPageDescriptor& rpDescriptor,
297     const OutputDevice* pReferenceDevice) const
298 {
299     const SdrPage* pPage = rpDescriptor->GetPage();
300     const bool bIsExcluded (rpDescriptor->HasState(model::PageDescriptor::ST_Excluded));
301 
302     if (bIsExcluded)
303     {
304         Bitmap aMarkedPreview (mpCache->GetMarkedPreviewBitmap(pPage,false));
305         const Rectangle aPreviewBox (mpPageObjectLayouter->GetBoundingBox(
306             rpDescriptor,
307             PageObjectLayouter::Preview,
308             PageObjectLayouter::ModelCoordinateSystem));
309         if (aMarkedPreview.IsEmpty() || aMarkedPreview.GetSizePixel()!=aPreviewBox.GetSize())
310         {
311             aMarkedPreview = CreateMarkedPreview(
312                 aPreviewBox.GetSize(),
313                 mpCache->GetPreviewBitmap(pPage,true),
314                 mpTheme->GetIcon(Theme::Icon_HideSlideOverlay),
315                 pReferenceDevice);
316             mpCache->SetMarkedPreviewBitmap(pPage, aMarkedPreview);
317         }
318         return aMarkedPreview;
319     }
320     else
321     {
322         return mpCache->GetPreviewBitmap(pPage,false);
323     }
324 }
325 
326 
327 
328 
329 void PageObjectPainter::PaintPageNumber (
330     OutputDevice& rDevice,
331     const model::SharedPageDescriptor& rpDescriptor) const
332 {
333     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
334         rpDescriptor,
335         PageObjectLayouter::PageNumber,
336         PageObjectLayouter::ModelCoordinateSystem));
337 
338     // Determine the color of the page number.
339     Color aPageNumberColor (mpTheme->GetColor(Theme::Color_PageNumberDefault));
340     if (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ||
341         rpDescriptor->HasState(model::PageDescriptor::ST_Selected))
342     {
343         // Page number is painted on background for hover or selection or
344         // both.  Each of these background colors has a predefined luminance
345         // which is compatible with the PageNumberHover color.
346         aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberHover));
347     }
348     else
349     {
350         const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background));
351         const sal_Int32 nBackgroundLuminance (aBackgroundColor.GetLuminance());
352         // When the background color is black then this is interpreted as
353         // high contrast mode and the font color is set to white.
354         if (nBackgroundLuminance == 0)
355             aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberHighContrast));
356         else
357         {
358             // Compare luminance of default page number color and background
359             // color.  When the two are similar then use a darker
360             // (preferred) or brighter font color.
361             const sal_Int32 nFontLuminance (aPageNumberColor.GetLuminance());
362             if (abs(nBackgroundLuminance - nFontLuminance) < 60)
363             {
364                 if (nBackgroundLuminance > nFontLuminance-30)
365                     aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberBrightBackground));
366                 else
367                     aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberDarkBackground));
368             }
369         }
370     }
371 
372     // Paint the page number.
373     OSL_ASSERT(rpDescriptor->GetPage()!=NULL);
374     const sal_Int32 nPageNumber ((rpDescriptor->GetPage()->GetPageNum() - 1) / 2 + 1);
375     const String sPageNumber (String::CreateFromInt32(nPageNumber));
376     rDevice.SetFont(*mpPageNumberFont);
377     rDevice.SetTextColor(aPageNumberColor);
378     rDevice.DrawText(aBox, sPageNumber, TEXT_DRAW_RIGHT | TEXT_DRAW_VCENTER);
379 }
380 
381 
382 
383 
384 void PageObjectPainter::PaintTransitionEffect (
385     OutputDevice& rDevice,
386     const model::SharedPageDescriptor& rpDescriptor) const
387 {
388     const SdPage* pPage = rpDescriptor->GetPage();
389     if (pPage!=NULL && pPage->getTransitionType() > 0)
390     {
391         const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
392             rpDescriptor,
393             PageObjectLayouter::TransitionEffectIndicator,
394             PageObjectLayouter::ModelCoordinateSystem));
395 
396         rDevice.DrawBitmapEx(
397             aBox.TopLeft(),
398             mpPageObjectLayouter->GetTransitionEffectIcon().GetBitmapEx());
399     }
400 }
401 
402 
403 
404 
405 Bitmap& PageObjectPainter::GetBackgroundForState (
406     const model::SharedPageDescriptor& rpDescriptor,
407     const OutputDevice& rReferenceDevice)
408 {
409     enum State { None = 0x00, Selected = 0x01, MouseOver = 0x02, Focused = 0x04 };
410     const int eState =
411           (rpDescriptor->HasState(model::PageDescriptor::ST_Selected) ? Selected : None)
412         | (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ? MouseOver : None)
413         | (rpDescriptor->HasState(model::PageDescriptor::ST_Focused) ? Focused : None);
414 
415     switch (eState)
416     {
417         case MouseOver | Selected | Focused:
418             return GetBackground(
419                 maMouseOverSelectedAndFocusedBackground,
420                 Theme::Gradient_MouseOverSelectedAndFocusedPage,
421                 rReferenceDevice,
422                 true);
423 
424         case MouseOver | Selected:
425         case MouseOver:
426             return GetBackground(
427                 maMouseOverBackground,
428                 Theme::Gradient_MouseOverPage,
429                 rReferenceDevice,
430                 false);
431 
432         case MouseOver | Focused:
433             return GetBackground(
434                 maMouseOverFocusedBackground,
435                 Theme::Gradient_MouseOverPage,
436                 rReferenceDevice,
437                 true);
438 
439         case Selected | Focused:
440             return GetBackground(
441                 maFocusedSelectionBackground,
442                 Theme::Gradient_SelectedAndFocusedPage,
443                 rReferenceDevice,
444                 true);
445 
446         case Selected:
447             return GetBackground(
448                 maSelectionBackground,
449                 Theme::Gradient_SelectedPage,
450                 rReferenceDevice,
451                 false);
452 
453         case Focused:
454             return GetBackground(
455                 maFocusedBackground,
456                 Theme::Gradient_FocusedPage,
457                 rReferenceDevice,
458                 true);
459 
460         case None:
461         default:
462             return GetBackground(
463                 maNormalBackground,
464                 Theme::Gradient_NormalPage,
465                 rReferenceDevice,
466                 false);
467     }
468 }
469 
470 
471 
472 
473 Bitmap& PageObjectPainter::GetBackground(
474     Bitmap& rBackground,
475     Theme::GradientColorType eType,
476     const OutputDevice& rReferenceDevice,
477     const bool bHasFocusBorder)
478 {
479     if (rBackground.IsEmpty())
480         rBackground = CreateBackgroundBitmap(rReferenceDevice, eType, bHasFocusBorder);
481     return rBackground;
482 }
483 
484 
485 
486 
487 Bitmap PageObjectPainter::CreateBackgroundBitmap(
488     const OutputDevice& rReferenceDevice,
489     const Theme::GradientColorType eColorType,
490     const bool bHasFocusBorder) const
491 {
492     const Size aSize (mpPageObjectLayouter->GetSize(
493         PageObjectLayouter::FocusIndicator,
494         PageObjectLayouter::WindowCoordinateSystem));
495     const Rectangle aPageObjectBox (mpPageObjectLayouter->GetBoundingBox(
496         Point(0,0),
497         PageObjectLayouter::PageObject,
498         PageObjectLayouter::ModelCoordinateSystem));
499     VirtualDevice aBitmapDevice (rReferenceDevice);
500     aBitmapDevice.SetOutputSizePixel(aSize);
501 
502     // Fill the background with the background color of the slide sorter.
503     const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background));
504     aBitmapDevice.SetFillColor(aBackgroundColor);
505     aBitmapDevice.SetLineColor(aBackgroundColor);
506     aBitmapDevice.DrawRect(Rectangle(Point(0,0), aSize));
507 
508     // Paint the slide area with a linear gradient that starts some pixels
509     // below the top and ends some pixels above the bottom.
510     const Color aTopColor(mpTheme->GetGradientColor(eColorType, Theme::Fill1));
511     const Color aBottomColor(mpTheme->GetGradientColor(eColorType, Theme::Fill2));
512     if (aTopColor != aBottomColor)
513     {
514         const sal_Int32 nHeight (aPageObjectBox.GetHeight());
515         const sal_Int32 nDefaultConstantSize(nHeight/4);
516         const sal_Int32 nMinimalGradientSize(40);
517         const sal_Int32 nY1 (
518             ::std::max<sal_Int32>(
519                 0,
520                 ::std::min<sal_Int32>(
521                     nDefaultConstantSize,
522                     (nHeight - nMinimalGradientSize)/2)));
523         const sal_Int32 nY2 (nHeight-nY1);
524         const sal_Int32 nTop (aPageObjectBox.Top());
525         for (sal_Int32 nY=0; nY<nHeight; ++nY)
526         {
527             if (nY<=nY1)
528                 aBitmapDevice.SetLineColor(aTopColor);
529             else if (nY>=nY2)
530                 aBitmapDevice.SetLineColor(aBottomColor);
531             else
532             {
533                 Color aColor (aTopColor);
534                 aColor.Merge(aBottomColor, 255 * (nY2-nY) / (nY2-nY1));
535                 aBitmapDevice.SetLineColor(aColor);
536             }
537             aBitmapDevice.DrawLine(
538                 Point(aPageObjectBox.Left(), nY+nTop),
539                 Point(aPageObjectBox.Right(), nY+nTop));
540         }
541     }
542     else
543     {
544         aBitmapDevice.SetFillColor(aTopColor);
545         aBitmapDevice.DrawRect(aPageObjectBox);
546     }
547 
548     // Paint the simple border and, for some backgrounds, the focus border.
549     if (bHasFocusBorder)
550         mpFocusBorderPainter->PaintFrame(aBitmapDevice, aPageObjectBox);
551     else
552         PaintBorder(aBitmapDevice, eColorType, aPageObjectBox);
553 
554     // Get bounding box of the preview around which a shadow is painted.
555     // Compensate for the border around the preview.
556     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
557         Point(0,0),
558         PageObjectLayouter::Preview,
559         PageObjectLayouter::ModelCoordinateSystem));
560     Rectangle aFrameBox (aBox.Left()-1,aBox.Top()-1,aBox.Right()+1,aBox.Bottom()+1);
561     mpShadowPainter->PaintFrame(aBitmapDevice, aFrameBox);
562 
563     return aBitmapDevice.GetBitmap (Point(0,0),aSize);
564 }
565 
566 
567 
568 
569 void PageObjectPainter::PaintBorder (
570     OutputDevice& rDevice,
571     const Theme::GradientColorType eColorType,
572     const Rectangle& rBox) const
573 {
574     rDevice.SetFillColor();
575     const sal_Int32 nBorderWidth (1);
576     for (int nIndex=0; nIndex<nBorderWidth; ++nIndex)
577     {
578         const int nDelta (nIndex);
579         rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::Border2));
580         rDevice.DrawLine(
581             Point(rBox.Left()-nDelta, rBox.Top()-nDelta),
582             Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta));
583         rDevice.DrawLine(
584             Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta),
585             Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta));
586         rDevice.DrawLine(
587             Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta),
588             Point(rBox.Right()+nDelta, rBox.Top()-nDelta));
589 
590         rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::Border1));
591         rDevice.DrawLine(
592             Point(rBox.Left()-nDelta, rBox.Top()-nDelta),
593             Point(rBox.Right()+nDelta, rBox.Top()-nDelta));
594     }
595 }
596 
597 
598 
599 } } } // end of namespace sd::slidesorter::view
600