xref: /AOO41X/main/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx (revision b0c15f6daf4d91e9f5836e9d8fd1bcc73ec86447)
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       maSize()
118 {
119     // Replace the color (not the alpha values) in the focus border with a
120     // color derived from the current selection color.
121     Color aColor (mpTheme->GetColor(Theme::Color_Selection));
122     sal_uInt16 nHue, nSat, nBri;
123     aColor.RGBtoHSB(nHue, nSat, nBri);
124     aColor = Color::HSBtoRGB(nHue, 28, 65);
125     mpFocusBorderPainter->AdaptColor(aColor, true);
126 }
127 
128 
129 
130 
131 PageObjectPainter::~PageObjectPainter (void)
132 {
133 }
134 
135 
136 
137 
138 void PageObjectPainter::PaintPageObject (
139     OutputDevice& rDevice,
140     const model::SharedPageDescriptor& rpDescriptor)
141 {
142     // The page object layouter is quite volatile. It may have been replaced
143     // since the last call.  Update it now.
144     mpPageObjectLayouter = mrLayouter.GetPageObjectLayouter();
145     if ( ! mpPageObjectLayouter)
146     {
147         OSL_ASSERT(mpPageObjectLayouter);
148         return;
149     }
150 
151     // Turn off antialiasing to avoid the bitmaps from being shifted by
152     // fractions of a pixel and thus show blurry edges.
153     const sal_uInt16 nSavedAntialiasingMode (rDevice.GetAntialiasing());
154     rDevice.SetAntialiasing(nSavedAntialiasingMode & ~ANTIALIASING_ENABLE_B2DDRAW);
155 
156     PaintBackground(rDevice, rpDescriptor);
157     PaintPreview(rDevice, rpDescriptor);
158     PaintPageNumber(rDevice, rpDescriptor);
159     PaintTransitionEffect(rDevice, rpDescriptor);
160     mrButtonBar.Paint(rDevice, rpDescriptor);
161 
162     rDevice.SetAntialiasing(nSavedAntialiasingMode);
163 }
164 
165 
166 
167 
168 void PageObjectPainter::NotifyResize (const bool bForce)
169 {
170     if (bForce || ! mpPageObjectLayouter)
171         InvalidateBitmaps();
172     else
173     {
174         const Size aSize (mpPageObjectLayouter->GetSize(
175                 PageObjectLayouter::FocusIndicator,
176                 PageObjectLayouter::WindowCoordinateSystem));
177         if ( maSize!=aSize)
178         {
179             maSize = aSize;
180             InvalidateBitmaps();
181         }
182     }
183 }
184 
185 
186 
187 
188 void PageObjectPainter::InvalidateBitmaps (void)
189 {
190     maNormalBackground.SetEmpty();
191     maSelectionBackground.SetEmpty();
192     maFocusedSelectionBackground.SetEmpty();
193     maFocusedBackground.SetEmpty();
194     maMouseOverBackground.SetEmpty();
195     maMouseOverFocusedBackground.SetEmpty();
196     maMouseOverSelectedAndFocusedBackground.SetEmpty();
197 }
198 
199 
200 
201 
202 void PageObjectPainter::SetTheme (const ::boost::shared_ptr<view::Theme>& rpTheme)
203 {
204     mpTheme = rpTheme;
205     NotifyResize(true);
206 }
207 
208 
209 
210 
211 void PageObjectPainter::PaintBackground (
212     OutputDevice& rDevice,
213     const model::SharedPageDescriptor& rpDescriptor)
214 {
215     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
216         rpDescriptor,
217         PageObjectLayouter::FocusIndicator,
218         PageObjectLayouter::ModelCoordinateSystem));
219 
220     const Bitmap& rBackground (GetBackgroundForState(rpDescriptor, rDevice));
221     rDevice.DrawBitmap(aBox.TopLeft(), rBackground);
222 
223     // Fill the interior of the preview area with the default background
224     // color of the page.
225     SdPage* pPage = rpDescriptor->GetPage();
226     if (pPage != NULL)
227     {
228         rDevice.SetFillColor(pPage->GetPageBackgroundColor(NULL));
229         rDevice.SetLineColor(pPage->GetPageBackgroundColor(NULL));
230         const Rectangle aPreviewBox (mpPageObjectLayouter->GetBoundingBox(
231             rpDescriptor,
232             PageObjectLayouter::Preview,
233             PageObjectLayouter::ModelCoordinateSystem));
234         rDevice.DrawRect(aPreviewBox);
235     }
236 }
237 
238 
239 
240 
241 void PageObjectPainter::PaintPreview (
242     OutputDevice& rDevice,
243     const model::SharedPageDescriptor& rpDescriptor) const
244 {
245     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
246         rpDescriptor,
247         PageObjectLayouter::Preview,
248         PageObjectLayouter::ModelCoordinateSystem));
249 
250     if (mpCache != NULL)
251     {
252         const SdrPage* pPage = rpDescriptor->GetPage();
253         mpCache->SetPreciousFlag(pPage, true);
254 
255         const Bitmap aPreview (GetPreviewBitmap(rpDescriptor, &rDevice));
256         if ( ! aPreview.IsEmpty())
257         {
258             if (aPreview.GetSizePixel() != aBox.GetSize())
259                 rDevice.DrawBitmap(aBox.TopLeft(), aBox.GetSize(), aPreview);
260             else
261                 rDevice.DrawBitmap(aBox.TopLeft(), aPreview);
262         }
263     }
264 }
265 
266 
267 
268 
269 Bitmap PageObjectPainter::CreateMarkedPreview (
270     const Size& rSize,
271     const Bitmap& rPreview,
272     const BitmapEx& rOverlay,
273     const OutputDevice* pReferenceDevice) const
274 {
275     ::boost::scoped_ptr<VirtualDevice> pDevice;
276     if (pReferenceDevice != NULL)
277         pDevice.reset(new VirtualDevice(*pReferenceDevice));
278     else
279         pDevice.reset(new VirtualDevice());
280     pDevice->SetOutputSizePixel(rSize);
281 
282     pDevice->DrawBitmap(Point(0,0), rSize, rPreview);
283 
284     // Paint bitmap tiled over the preview to mark it as excluded.
285     const sal_Int32 nIconWidth (rOverlay.GetSizePixel().Width());
286     const sal_Int32 nIconHeight (rOverlay.GetSizePixel().Height());
287     if (nIconWidth>0 && nIconHeight>0)
288     {
289         for (sal_Int32 nX=0; nX<rSize.Width(); nX+=nIconWidth)
290             for (sal_Int32 nY=0; nY<rSize.Height(); nY+=nIconHeight)
291                 pDevice->DrawBitmapEx(Point(nX,nY), rOverlay);
292     }
293     return pDevice->GetBitmap(Point(0,0), rSize);
294 }
295 
296 
297 
298 
299 Bitmap PageObjectPainter::GetPreviewBitmap (
300     const model::SharedPageDescriptor& rpDescriptor,
301     const OutputDevice* pReferenceDevice) const
302 {
303     const SdrPage* pPage = rpDescriptor->GetPage();
304     const bool bIsExcluded (rpDescriptor->HasState(model::PageDescriptor::ST_Excluded));
305 
306     if (bIsExcluded)
307     {
308         Bitmap aMarkedPreview (mpCache->GetMarkedPreviewBitmap(pPage,false));
309         const Rectangle aPreviewBox (mpPageObjectLayouter->GetBoundingBox(
310             rpDescriptor,
311             PageObjectLayouter::Preview,
312             PageObjectLayouter::ModelCoordinateSystem));
313         if (aMarkedPreview.IsEmpty() || aMarkedPreview.GetSizePixel()!=aPreviewBox.GetSize())
314         {
315             aMarkedPreview = CreateMarkedPreview(
316                 aPreviewBox.GetSize(),
317                 mpCache->GetPreviewBitmap(pPage,true),
318                 mpTheme->GetIcon(Theme::Icon_HideSlideOverlay),
319                 pReferenceDevice);
320             mpCache->SetMarkedPreviewBitmap(pPage, aMarkedPreview);
321         }
322         return aMarkedPreview;
323     }
324     else
325     {
326         return mpCache->GetPreviewBitmap(pPage,false);
327     }
328 }
329 
330 
331 
332 
333 void PageObjectPainter::PaintPageNumber (
334     OutputDevice& rDevice,
335     const model::SharedPageDescriptor& rpDescriptor) const
336 {
337     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
338         rpDescriptor,
339         PageObjectLayouter::PageNumber,
340         PageObjectLayouter::ModelCoordinateSystem));
341 
342     // Determine the color of the page number.
343     Color aPageNumberColor (mpTheme->GetColor(Theme::Color_PageNumberDefault));
344     if (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ||
345         rpDescriptor->HasState(model::PageDescriptor::ST_Selected))
346     {
347         // Page number is painted on background for hover or selection or
348         // both.  Each of these background colors has a predefined luminance
349         // which is compatible with the PageNumberHover color.
350         aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberHover));
351     }
352     else
353     {
354         const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background));
355         const sal_Int32 nBackgroundLuminance (aBackgroundColor.GetLuminance());
356         // When the background color is black then this is interpreted as
357         // high contrast mode and the font color is set to white.
358         if (nBackgroundLuminance == 0)
359             aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberHighContrast));
360         else
361         {
362             // Compare luminance of default page number color and background
363             // color.  When the two are similar then use a darker
364             // (preferred) or brighter font color.
365             const sal_Int32 nFontLuminance (aPageNumberColor.GetLuminance());
366             if (abs(nBackgroundLuminance - nFontLuminance) < 60)
367             {
368                 if (nBackgroundLuminance > nFontLuminance-30)
369                     aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberBrightBackground));
370                 else
371                     aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberDarkBackground));
372             }
373         }
374     }
375 
376     // Paint the page number.
377     OSL_ASSERT(rpDescriptor->GetPage()!=NULL);
378     const sal_Int32 nPageNumber ((rpDescriptor->GetPage()->GetPageNum() - 1) / 2 + 1);
379     const String sPageNumber (String::CreateFromInt32(nPageNumber));
380     rDevice.SetFont(*mpPageNumberFont);
381     rDevice.SetTextColor(aPageNumberColor);
382     rDevice.DrawText(aBox, sPageNumber, TEXT_DRAW_RIGHT | TEXT_DRAW_VCENTER);
383 }
384 
385 
386 
387 
388 void PageObjectPainter::PaintTransitionEffect (
389     OutputDevice& rDevice,
390     const model::SharedPageDescriptor& rpDescriptor) const
391 {
392     const SdPage* pPage = rpDescriptor->GetPage();
393     if (pPage!=NULL && pPage->getTransitionType() > 0)
394     {
395         const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
396             rpDescriptor,
397             PageObjectLayouter::TransitionEffectIndicator,
398             PageObjectLayouter::ModelCoordinateSystem));
399 
400         rDevice.DrawBitmapEx(
401             aBox.TopLeft(),
402             mpPageObjectLayouter->GetTransitionEffectIcon().GetBitmapEx());
403     }
404 }
405 
406 
407 
408 
409 Bitmap& PageObjectPainter::GetBackgroundForState (
410     const model::SharedPageDescriptor& rpDescriptor,
411     const OutputDevice& rReferenceDevice)
412 {
413     enum State { None = 0x00, Selected = 0x01, MouseOver = 0x02, Focused = 0x04 };
414     const int eState =
415           (rpDescriptor->HasState(model::PageDescriptor::ST_Selected) ? Selected : None)
416         | (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ? MouseOver : None)
417         | (rpDescriptor->HasState(model::PageDescriptor::ST_Focused) ? Focused : None);
418 
419     switch (eState)
420     {
421         case MouseOver | Selected | Focused:
422             return GetBackground(
423                 maMouseOverSelectedAndFocusedBackground,
424                 Theme::Gradient_MouseOverSelectedAndFocusedPage,
425                 rReferenceDevice,
426                 true);
427 
428         case MouseOver | Selected:
429         case MouseOver:
430             return GetBackground(
431                 maMouseOverBackground,
432                 Theme::Gradient_MouseOverPage,
433                 rReferenceDevice,
434                 false);
435 
436         case MouseOver | Focused:
437             return GetBackground(
438                 maMouseOverFocusedBackground,
439                 Theme::Gradient_MouseOverPage,
440                 rReferenceDevice,
441                 true);
442 
443         case Selected | Focused:
444             return GetBackground(
445                 maFocusedSelectionBackground,
446                 Theme::Gradient_SelectedAndFocusedPage,
447                 rReferenceDevice,
448                 true);
449 
450         case Selected:
451             return GetBackground(
452                 maSelectionBackground,
453                 Theme::Gradient_SelectedPage,
454                 rReferenceDevice,
455                 false);
456 
457         case Focused:
458             return GetBackground(
459                 maFocusedBackground,
460                 Theme::Gradient_FocusedPage,
461                 rReferenceDevice,
462                 true);
463 
464         case None:
465         default:
466             return GetBackground(
467                 maNormalBackground,
468                 Theme::Gradient_NormalPage,
469                 rReferenceDevice,
470                 false);
471     }
472 }
473 
474 
475 
476 
477 Bitmap& PageObjectPainter::GetBackground(
478     Bitmap& rBackground,
479     Theme::GradientColorType eType,
480     const OutputDevice& rReferenceDevice,
481     const bool bHasFocusBorder)
482 {
483     if (rBackground.IsEmpty())
484         rBackground = CreateBackgroundBitmap(rReferenceDevice, eType, bHasFocusBorder);
485     return rBackground;
486 }
487 
488 
489 
490 
491 Bitmap PageObjectPainter::CreateBackgroundBitmap(
492     const OutputDevice& rReferenceDevice,
493     const Theme::GradientColorType eColorType,
494     const bool bHasFocusBorder) const
495 {
496     const Size aSize (mpPageObjectLayouter->GetSize(
497         PageObjectLayouter::FocusIndicator,
498         PageObjectLayouter::WindowCoordinateSystem));
499     const Rectangle aPageObjectBox (mpPageObjectLayouter->GetBoundingBox(
500         Point(0,0),
501         PageObjectLayouter::PageObject,
502         PageObjectLayouter::ModelCoordinateSystem));
503     VirtualDevice aBitmapDevice (rReferenceDevice);
504     aBitmapDevice.SetOutputSizePixel(aSize);
505 
506     // Fill the background with the background color of the slide sorter.
507     const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background));
508     aBitmapDevice.SetFillColor(aBackgroundColor);
509     aBitmapDevice.SetLineColor(aBackgroundColor);
510     aBitmapDevice.DrawRect(Rectangle(Point(0,0), aSize));
511 
512     // Paint the slide area with a linear gradient that starts some pixels
513     // below the top and ends some pixels above the bottom.
514     const Color aTopColor(mpTheme->GetGradientColor(eColorType, Theme::Fill1));
515     const Color aBottomColor(mpTheme->GetGradientColor(eColorType, Theme::Fill2));
516     if (aTopColor != aBottomColor)
517     {
518         const sal_Int32 nHeight (aPageObjectBox.GetHeight());
519         const sal_Int32 nDefaultConstantSize(nHeight/4);
520         const sal_Int32 nMinimalGradientSize(40);
521         const sal_Int32 nY1 (
522             ::std::max<sal_Int32>(
523                 0,
524                 ::std::min<sal_Int32>(
525                     nDefaultConstantSize,
526                     (nHeight - nMinimalGradientSize)/2)));
527         const sal_Int32 nY2 (nHeight-nY1);
528         const sal_Int32 nTop (aPageObjectBox.Top());
529         for (sal_Int32 nY=0; nY<nHeight; ++nY)
530         {
531             if (nY<=nY1)
532                 aBitmapDevice.SetLineColor(aTopColor);
533             else if (nY>=nY2)
534                 aBitmapDevice.SetLineColor(aBottomColor);
535             else
536             {
537                 Color aColor (aTopColor);
538                 aColor.Merge(aBottomColor, 255 * (nY2-nY) / (nY2-nY1));
539                 aBitmapDevice.SetLineColor(aColor);
540             }
541             aBitmapDevice.DrawLine(
542                 Point(aPageObjectBox.Left(), nY+nTop),
543                 Point(aPageObjectBox.Right(), nY+nTop));
544         }
545     }
546     else
547     {
548         aBitmapDevice.SetFillColor(aTopColor);
549         aBitmapDevice.DrawRect(aPageObjectBox);
550     }
551 
552     // Paint the simple border and, for some backgrounds, the focus border.
553     if (bHasFocusBorder)
554         mpFocusBorderPainter->PaintFrame(aBitmapDevice, aPageObjectBox);
555     else
556         PaintBorder(aBitmapDevice, eColorType, aPageObjectBox);
557 
558     // Get bounding box of the preview around which a shadow is painted.
559     // Compensate for the border around the preview.
560     const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox(
561         Point(0,0),
562         PageObjectLayouter::Preview,
563         PageObjectLayouter::ModelCoordinateSystem));
564     Rectangle aFrameBox (aBox.Left()-1,aBox.Top()-1,aBox.Right()+1,aBox.Bottom()+1);
565     mpShadowPainter->PaintFrame(aBitmapDevice, aFrameBox);
566 
567     return aBitmapDevice.GetBitmap (Point(0,0),aSize);
568 }
569 
570 
571 
572 
573 void PageObjectPainter::PaintBorder (
574     OutputDevice& rDevice,
575     const Theme::GradientColorType eColorType,
576     const Rectangle& rBox) const
577 {
578     rDevice.SetFillColor();
579     const sal_Int32 nBorderWidth (1);
580     for (int nIndex=0; nIndex<nBorderWidth; ++nIndex)
581     {
582         const int nDelta (nIndex);
583         rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::Border2));
584         rDevice.DrawLine(
585             Point(rBox.Left()-nDelta, rBox.Top()-nDelta),
586             Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta));
587         rDevice.DrawLine(
588             Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta),
589             Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta));
590         rDevice.DrawLine(
591             Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta),
592             Point(rBox.Right()+nDelta, rBox.Top()-nDelta));
593 
594         rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::Border1));
595         rDevice.DrawLine(
596             Point(rBox.Left()-nDelta, rBox.Top()-nDelta),
597             Point(rBox.Right()+nDelta, rBox.Top()-nDelta));
598     }
599 }
600 
601 
602 
603 } } } // end of namespace sd::slidesorter::view
604