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