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