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