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