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