/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include "precompiled_sd.hxx" #include "view/SlsPageObjectPainter.hxx" #include "model/SlsPageDescriptor.hxx" #include "view/SlideSorterView.hxx" #include "view/SlsPageObjectLayouter.hxx" #include "view/SlsLayouter.hxx" #include "view/SlsTheme.hxx" #include "view/SlsButtonBar.hxx" #include "SlsFramePainter.hxx" #include "cache/SlsPageCache.hxx" #include "controller/SlsProperties.hxx" #include "Window.hxx" #include "sdpage.hxx" #include "sdresid.hxx" #include #include #include #include using namespace ::drawinglayer::primitive2d; namespace sd { namespace slidesorter { namespace view { namespace { sal_uInt8 Blend ( const sal_uInt8 nValue1, const sal_uInt8 nValue2, const double nWeight) { const double nValue (nValue1*(1-nWeight) + nValue2 * nWeight); if (nValue < 0) return 0; else if (nValue > 255) return 255; else return (sal_uInt8)nValue; } sal_uInt8 ClampColorChannel (const double nValue) { if (nValue <= 0) return 0; else if (nValue >= 255) return 255; else return sal_uInt8(nValue); } sal_uInt8 CalculateColorChannel( const double nColor1, const double nColor2, const double nAlpha1, const double nAlpha2, const double nAlpha0) { if (nAlpha0 == 0) return 0; const double nColor0 ((nAlpha1*nColor1 + nAlpha1*nAlpha2*nColor1 + nAlpha2*nColor2) / nAlpha0); return ClampColorChannel(255 * nColor0); } } // end of anonymous namespace //===== PageObjectPainter ===================================================== PageObjectPainter::PageObjectPainter ( const SlideSorter& rSlideSorter) : mrLayouter(rSlideSorter.GetView().GetLayouter()), mpPageObjectLayouter(), mpCache(rSlideSorter.GetView().GetPreviewCache()), mpProperties(rSlideSorter.GetProperties()), mpTheme(rSlideSorter.GetTheme()), mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber, *rSlideSorter.GetContentWindow())), mpShadowPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_RawShadow))), mpFocusBorderPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_FocusBorder))), maNormalBackground(), maSelectionBackground(), maFocusedSelectionBackground(), maMouseOverBackground(), maMouseOverFocusedBackground(), msUnhideString(mpTheme->GetString(Theme::String_Unhide)), mrButtonBar(rSlideSorter.GetView().GetButtonBar()), maSize() { // Replace the color (not the alpha values) in the focus border with a // color derived from the current selection color. Color aColor (mpTheme->GetColor(Theme::Color_Selection)); sal_uInt16 nHue, nSat, nBri; aColor.RGBtoHSB(nHue, nSat, nBri); aColor = Color::HSBtoRGB(nHue, 28, 65); mpFocusBorderPainter->AdaptColor(aColor, true); } PageObjectPainter::~PageObjectPainter (void) { } void PageObjectPainter::PaintPageObject ( OutputDevice& rDevice, const model::SharedPageDescriptor& rpDescriptor) { if (UpdatePageObjectLayouter()) { // Turn off antialiasing to avoid the bitmaps from being // shifted by fractions of a pixel and thus show blurry edges. const sal_uInt16 nSavedAntialiasingMode (rDevice.GetAntialiasing()); rDevice.SetAntialiasing(nSavedAntialiasingMode & ~ANTIALIASING_ENABLE_B2DDRAW); PaintBackground(rDevice, rpDescriptor); PaintPreview(rDevice, rpDescriptor); PaintPageNumber(rDevice, rpDescriptor); PaintTransitionEffect(rDevice, rpDescriptor); mrButtonBar.Paint(rDevice, rpDescriptor); rDevice.SetAntialiasing(nSavedAntialiasingMode); } } bool PageObjectPainter::UpdatePageObjectLayouter (void) { // The page object layouter is quite volatile. It may have been replaced // since the last call. Update it now. mpPageObjectLayouter = mrLayouter.GetPageObjectLayouter(); if ( ! mpPageObjectLayouter) { OSL_ASSERT(mpPageObjectLayouter); return false; } else return true; } void PageObjectPainter::NotifyResize (const bool bForce) { if (bForce || ! mpPageObjectLayouter) InvalidateBitmaps(); else if (UpdatePageObjectLayouter()) { const Size aSize (mpPageObjectLayouter->GetSize( PageObjectLayouter::FocusIndicator, PageObjectLayouter::WindowCoordinateSystem)); if ( maSize!=aSize) { maSize = aSize; InvalidateBitmaps(); } } } void PageObjectPainter::InvalidateBitmaps (void) { maNormalBackground.SetEmpty(); maSelectionBackground.SetEmpty(); maFocusedSelectionBackground.SetEmpty(); maFocusedBackground.SetEmpty(); maMouseOverBackground.SetEmpty(); maMouseOverFocusedBackground.SetEmpty(); maMouseOverSelectedAndFocusedBackground.SetEmpty(); } void PageObjectPainter::SetTheme (const ::boost::shared_ptr& rpTheme) { mpTheme = rpTheme; NotifyResize(true); } void PageObjectPainter::PaintBackground ( OutputDevice& rDevice, const model::SharedPageDescriptor& rpDescriptor) { const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox( rpDescriptor, PageObjectLayouter::FocusIndicator, PageObjectLayouter::ModelCoordinateSystem)); const Bitmap& rBackground (GetBackgroundForState(rpDescriptor, rDevice)); rDevice.DrawBitmap(aBox.TopLeft(), rBackground); // Fill the interior of the preview area with the default background // color of the page. SdPage* pPage = rpDescriptor->GetPage(); if (pPage != NULL) { rDevice.SetFillColor(pPage->GetPageBackgroundColor(NULL)); rDevice.SetLineColor(pPage->GetPageBackgroundColor(NULL)); const Rectangle aPreviewBox (mpPageObjectLayouter->GetBoundingBox( rpDescriptor, PageObjectLayouter::Preview, PageObjectLayouter::ModelCoordinateSystem)); rDevice.DrawRect(aPreviewBox); } } void PageObjectPainter::PaintPreview ( OutputDevice& rDevice, const model::SharedPageDescriptor& rpDescriptor) const { const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox( rpDescriptor, PageObjectLayouter::Preview, PageObjectLayouter::ModelCoordinateSystem)); if( bool(mpCache)) { const SdrPage* pPage = rpDescriptor->GetPage(); mpCache->SetPreciousFlag(pPage, true); const Bitmap aPreview (GetPreviewBitmap(rpDescriptor, &rDevice)); if ( ! aPreview.IsEmpty()) { if (aPreview.GetSizePixel() != aBox.GetSize()) rDevice.DrawBitmap(aBox.TopLeft(), aBox.GetSize(), aPreview); else rDevice.DrawBitmap(aBox.TopLeft(), aPreview); } } } Bitmap PageObjectPainter::CreateMarkedPreview ( const Size& rSize, const Bitmap& rPreview, const BitmapEx& rOverlay, const OutputDevice* pReferenceDevice) const { ::boost::scoped_ptr pDevice; if (pReferenceDevice != NULL) pDevice.reset(new VirtualDevice(*pReferenceDevice)); else pDevice.reset(new VirtualDevice()); pDevice->SetOutputSizePixel(rSize); pDevice->DrawBitmap(Point(0,0), rSize, rPreview); // Paint bitmap tiled over the preview to mark it as excluded. const sal_Int32 nIconWidth (rOverlay.GetSizePixel().Width()); const sal_Int32 nIconHeight (rOverlay.GetSizePixel().Height()); if (nIconWidth>0 && nIconHeight>0) { for (sal_Int32 nX=0; nXDrawBitmapEx(Point(nX,nY), rOverlay); } return pDevice->GetBitmap(Point(0,0), rSize); } Bitmap PageObjectPainter::GetPreviewBitmap ( const model::SharedPageDescriptor& rpDescriptor, const OutputDevice* pReferenceDevice) const { const SdrPage* pPage = rpDescriptor->GetPage(); const bool bIsExcluded (rpDescriptor->HasState(model::PageDescriptor::ST_Excluded)); if (bIsExcluded) { Bitmap aMarkedPreview (mpCache->GetMarkedPreviewBitmap(pPage,false)); const Rectangle aPreviewBox (mpPageObjectLayouter->GetBoundingBox( rpDescriptor, PageObjectLayouter::Preview, PageObjectLayouter::ModelCoordinateSystem)); if (aMarkedPreview.IsEmpty() || aMarkedPreview.GetSizePixel()!=aPreviewBox.GetSize()) { aMarkedPreview = CreateMarkedPreview( aPreviewBox.GetSize(), mpCache->GetPreviewBitmap(pPage,true), mpTheme->GetIcon(Theme::Icon_HideSlideOverlay), pReferenceDevice); mpCache->SetMarkedPreviewBitmap(pPage, aMarkedPreview); } return aMarkedPreview; } else { return mpCache->GetPreviewBitmap(pPage,false); } } void PageObjectPainter::PaintPageNumber ( OutputDevice& rDevice, const model::SharedPageDescriptor& rpDescriptor) const { const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox( rpDescriptor, PageObjectLayouter::PageNumber, PageObjectLayouter::ModelCoordinateSystem)); // Determine the color of the page number. Color aPageNumberColor (mpTheme->GetColor(Theme::Color_PageNumberDefault)); if (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) || rpDescriptor->HasState(model::PageDescriptor::ST_Selected)) { // Page number is painted on background for hover or selection or // both. Each of these background colors has a predefined luminance // which is compatible with the PageNumberHover color. aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberHover)); } else { const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background)); const sal_Int32 nBackgroundLuminance (aBackgroundColor.GetLuminance()); // When the background color is black then this is interpreted as // high contrast mode and the font color is set to white. if (nBackgroundLuminance == 0) aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberHighContrast)); else { // Compare luminance of default page number color and background // color. When the two are similar then use a darker // (preferred) or brighter font color. const sal_Int32 nFontLuminance (aPageNumberColor.GetLuminance()); if (abs(nBackgroundLuminance - nFontLuminance) < 60) { if (nBackgroundLuminance > nFontLuminance-30) aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberBrightBackground)); else aPageNumberColor = Color(mpTheme->GetColor(Theme::Color_PageNumberDarkBackground)); } } } // Paint the page number. OSL_ASSERT(rpDescriptor->GetPage()!=NULL); const sal_Int32 nPageNumber ((rpDescriptor->GetPage()->GetPageNum() - 1) / 2 + 1); const String sPageNumber (String::CreateFromInt32(nPageNumber)); rDevice.SetFont(*mpPageNumberFont); rDevice.SetTextColor(aPageNumberColor); rDevice.DrawText(aBox, sPageNumber, TEXT_DRAW_RIGHT | TEXT_DRAW_VCENTER); } void PageObjectPainter::PaintTransitionEffect ( OutputDevice& rDevice, const model::SharedPageDescriptor& rpDescriptor) const { const SdPage* pPage = rpDescriptor->GetPage(); if (pPage!=NULL && pPage->getTransitionType() > 0) { const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox( rpDescriptor, PageObjectLayouter::TransitionEffectIndicator, PageObjectLayouter::ModelCoordinateSystem)); rDevice.DrawBitmapEx( aBox.TopLeft(), mpPageObjectLayouter->GetTransitionEffectIcon().GetBitmapEx()); } } Bitmap& PageObjectPainter::GetBackgroundForState ( const model::SharedPageDescriptor& rpDescriptor, const OutputDevice& rReferenceDevice) { enum State { None = 0x00, Selected = 0x01, MouseOver = 0x02, Focused = 0x04 }; const int eState = (rpDescriptor->HasState(model::PageDescriptor::ST_Selected) ? Selected : None) | (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ? MouseOver : None) | (rpDescriptor->HasState(model::PageDescriptor::ST_Focused) ? Focused : None); switch (eState) { case MouseOver | Selected | Focused: return GetBackground( maMouseOverSelectedAndFocusedBackground, Theme::Gradient_MouseOverSelectedAndFocusedPage, rReferenceDevice, true); case MouseOver | Selected: case MouseOver: return GetBackground( maMouseOverBackground, Theme::Gradient_MouseOverPage, rReferenceDevice, false); case MouseOver | Focused: return GetBackground( maMouseOverFocusedBackground, Theme::Gradient_MouseOverPage, rReferenceDevice, true); case Selected | Focused: return GetBackground( maFocusedSelectionBackground, Theme::Gradient_SelectedAndFocusedPage, rReferenceDevice, true); case Selected: return GetBackground( maSelectionBackground, Theme::Gradient_SelectedPage, rReferenceDevice, false); case Focused: return GetBackground( maFocusedBackground, Theme::Gradient_FocusedPage, rReferenceDevice, true); case None: default: return GetBackground( maNormalBackground, Theme::Gradient_NormalPage, rReferenceDevice, false); } } Bitmap& PageObjectPainter::GetBackground( Bitmap& rBackground, Theme::GradientColorType eType, const OutputDevice& rReferenceDevice, const bool bHasFocusBorder) { if (rBackground.IsEmpty()) rBackground = CreateBackgroundBitmap(rReferenceDevice, eType, bHasFocusBorder); return rBackground; } Bitmap PageObjectPainter::CreateBackgroundBitmap( const OutputDevice& rReferenceDevice, const Theme::GradientColorType eColorType, const bool bHasFocusBorder) const { const Size aSize (mpPageObjectLayouter->GetSize( PageObjectLayouter::FocusIndicator, PageObjectLayouter::WindowCoordinateSystem)); const Rectangle aPageObjectBox (mpPageObjectLayouter->GetBoundingBox( Point(0,0), PageObjectLayouter::PageObject, PageObjectLayouter::ModelCoordinateSystem)); VirtualDevice aBitmapDevice (rReferenceDevice); aBitmapDevice.SetOutputSizePixel(aSize); // Fill the background with the background color of the slide sorter. const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background)); aBitmapDevice.SetFillColor(aBackgroundColor); aBitmapDevice.SetLineColor(aBackgroundColor); aBitmapDevice.DrawRect(Rectangle(Point(0,0), aSize)); // Paint the slide area with a linear gradient that starts some pixels // below the top and ends some pixels above the bottom. const Color aTopColor(mpTheme->GetGradientColor(eColorType, Theme::Fill1)); const Color aBottomColor(mpTheme->GetGradientColor(eColorType, Theme::Fill2)); if (aTopColor != aBottomColor) { const sal_Int32 nHeight (aPageObjectBox.GetHeight()); const sal_Int32 nDefaultConstantSize(nHeight/4); const sal_Int32 nMinimalGradientSize(40); const sal_Int32 nY1 ( ::std::max( 0, ::std::min( nDefaultConstantSize, (nHeight - nMinimalGradientSize)/2))); const sal_Int32 nY2 (nHeight-nY1); const sal_Int32 nTop (aPageObjectBox.Top()); for (sal_Int32 nY=0; nY=nY2) aBitmapDevice.SetLineColor(aBottomColor); else { Color aColor (aTopColor); aColor.Merge(aBottomColor, 255 * (nY2-nY) / (nY2-nY1)); aBitmapDevice.SetLineColor(aColor); } aBitmapDevice.DrawLine( Point(aPageObjectBox.Left(), nY+nTop), Point(aPageObjectBox.Right(), nY+nTop)); } } else { aBitmapDevice.SetFillColor(aTopColor); aBitmapDevice.DrawRect(aPageObjectBox); } // Paint the simple border and, for some backgrounds, the focus border. if (bHasFocusBorder) mpFocusBorderPainter->PaintFrame(aBitmapDevice, aPageObjectBox); else PaintBorder(aBitmapDevice, eColorType, aPageObjectBox); // Get bounding box of the preview around which a shadow is painted. // Compensate for the border around the preview. const Rectangle aBox (mpPageObjectLayouter->GetBoundingBox( Point(0,0), PageObjectLayouter::Preview, PageObjectLayouter::ModelCoordinateSystem)); Rectangle aFrameBox (aBox.Left()-1,aBox.Top()-1,aBox.Right()+1,aBox.Bottom()+1); mpShadowPainter->PaintFrame(aBitmapDevice, aFrameBox); return aBitmapDevice.GetBitmap (Point(0,0),aSize); } void PageObjectPainter::PaintBorder ( OutputDevice& rDevice, const Theme::GradientColorType eColorType, const Rectangle& rBox) const { rDevice.SetFillColor(); const sal_Int32 nBorderWidth (1); for (int nIndex=0; nIndexGetGradientColor(eColorType, Theme::Border2)); rDevice.DrawLine( Point(rBox.Left()-nDelta, rBox.Top()-nDelta), Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta)); rDevice.DrawLine( Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta), Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta)); rDevice.DrawLine( Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta), Point(rBox.Right()+nDelta, rBox.Top()-nDelta)); rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::Border1)); rDevice.DrawLine( Point(rBox.Left()-nDelta, rBox.Top()-nDelta), Point(rBox.Right()+nDelta, rBox.Top()-nDelta)); } } } } } // end of namespace sd::slidesorter::view