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