/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "view/SlsButtonBar.hxx" #include "SlideSorter.hxx" #include "model/SlsPageDescriptor.hxx" #include "model/SlideSorterModel.hxx" #include "view/SlsTheme.hxx" #include "view/SlideSorterView.hxx" #include "view/SlsToolTip.hxx" #include "controller/SlideSorterController.hxx" #include "controller/SlsSlotManager.hxx" #include "controller/SlsCurrentSlideManager.hxx" #include "controller/SlsPageSelector.hxx" #include "controller/SlsAnimator.hxx" #include "controller/SlsAnimationFunction.hxx" #include "app.hrc" #include "drawdoc.hxx" #include "sdmod.hxx" #include "optsitem.hxx" #include #include #include #include #include #include #include #include using ::com::sun::star::uno::Any; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::Sequence; using ::com::sun::star::beans::PropertyValue; using ::com::sun::star::presentation::XPresentation2; namespace sd { namespace slidesorter { namespace view { /** Base class for the painter of the background bar onto which the buttons are painted. It also provides some size information. */ class ButtonBar::BackgroundTheme { public: enum ButtonPosition { TOP, BOTTOM }; public: BackgroundTheme( const ::boost::shared_ptr& rpTheme, const ::std::vector& rButtons); ~BackgroundTheme() { } /** Set the preview bounding box, the maximal area in which to display buttons. A call to this method triggers a call to Layout(). */ void SetPreviewBoundingBox (const Rectangle& rPreviewBoundingBox); Button::IconSize GetIconSize (void) const; BitmapEx CreateBackground () const; Point GetBackgroundLocation (void); Rectangle GetButtonArea (void); void SetButtonPosition( ButtonPosition ePosition ) { mePosition = ePosition; } /// Compute the positions & sizes. void Layout (void); protected: ::boost::shared_ptr mpTheme; Rectangle maPreviewBoundingBox; Size maMinimumLargeButtonAreaSize; Size maMinimumMediumButtonAreaSize; Size maMinimumSmallButtonAreaSize; Button::IconSize meIconSize; Rectangle maButtonArea; Point maBackgroundLocation; /// This comes into effect only during Layout(), before it only caches the value. ButtonPosition mePosition; private: void UpdateMinimumIconSizes(const ::std::vector& rButtons); }; namespace { /** The source mask is essentially multiplied with the given alpha value. The result is writen to the result mask. */ void AdaptTransparency (AlphaMask& rMask, const AlphaMask& rSourceMask, const double nAlpha) { BitmapWriteAccess* pBitmap = rMask.AcquireWriteAccess(); const BitmapReadAccess* pSourceBitmap = const_cast(rSourceMask).AcquireReadAccess(); if (pBitmap!=NULL && pSourceBitmap!=NULL) { const sal_Int32 nWidth (pBitmap->Width()); const sal_Int32 nHeight (pBitmap->Height()); for (sal_Int32 nY = 0; nYGetPixel(nY, nX).GetBlueOrIndex()); const sal_uInt8 nNewValue (static_cast(nValue * (1-nAlpha))); pBitmap->SetPixel(nY, nX, 255-nNewValue); } } } } // end of anonymous namespace //===== ButtonBar::Lock ======================================================= ButtonBar::Lock::Lock (SlideSorter& rSlideSorter) : mrButtonBar(rSlideSorter.GetView().GetButtonBar()) { mrButtonBar.AcquireLock(); } ButtonBar::Lock::~Lock (void) { mrButtonBar.ReleaseLock(); } //===== ButtonBar ============================================================= ButtonBar::ButtonBar (SlideSorter& rSlideSorter) : mrSlideSorter(rSlideSorter), maPageObjectSize(0,0), maButtonBoundingBox(), maBackgroundLocation(), mpDescriptor(), mbIsExcluded(false), mpButtonUnderMouse(), mpDownButton(), maRegularButtons(), maExcludedButtons(), maNormalBackground(), mbIsMouseOverBar(false), mpBackgroundTheme(), mnLockCount(0) { HandleDataChangeEvent(); } ButtonBar::~ButtonBar (void) { } void ButtonBar::ProcessButtonDownEvent ( const model::SharedPageDescriptor& rpDescriptor, const Point aMouseModelLocation) { SetButtonUnderMouse(GetButtonAt(aMouseModelLocation)); if (mpButtonUnderMouse) mpButtonUnderMouse->SetState(Button::State_Down); mpDownButton = mpButtonUnderMouse; mrSlideSorter.GetView().RequestRepaint(rpDescriptor); } void ButtonBar::ProcessButtonUpEvent ( const model::SharedPageDescriptor& rpDescriptor, const Point aMouseModelLocation) { SetButtonUnderMouse(GetButtonAt(aMouseModelLocation)); if (mpButtonUnderMouse) { mpButtonUnderMouse->SetState(Button::State_Hover); if (mpButtonUnderMouse == mpDownButton) { // This is done only when the buttons are sufficiently visible. if (mpDescriptor->GetVisualState().GetButtonAlpha()<0.7) { mpButtonUnderMouse->ProcessClick(mpDescriptor); mbIsExcluded = mpDescriptor->HasState(model::PageDescriptor::ST_Excluded); ProcessMouseMotionEvent (rpDescriptor, aMouseModelLocation, false); } } } mpDownButton.reset(); mrSlideSorter.GetView().RequestRepaint(rpDescriptor); } void ButtonBar::ProcessMouseMotionEvent ( const model::SharedPageDescriptor& rpDescriptor, const Point aMouseModelLocation, const bool bIsMouseButtonDown) { model::SharedPageDescriptor pOldDescriptor (mpDescriptor); bool bPageHasChanged (false); bool bButtonHasChanged (false); bool bButtonStateHasChanged (false); // Update the page object for which to manage the buttons. bPageHasChanged = SetPage(rpDescriptor); mbIsMouseOverBar = IsMouseOverBar(aMouseModelLocation); // Update button under mouse. if (rpDescriptor) { bButtonHasChanged = SetButtonUnderMouse(GetButtonAt(aMouseModelLocation)); if (mpButtonUnderMouse) { // When the mouse button is down, mark the button under the // mouse only as pressed when it is the same button the mouse // button was pressed over, and where the button release would // lead to a click action. if (bIsMouseButtonDown) { if (mpButtonUnderMouse==mpDownButton) bButtonStateHasChanged = mpButtonUnderMouse->SetState(Button::State_Down); } else bButtonStateHasChanged = mpButtonUnderMouse->SetState(Button::State_Hover); } } // Show a quick help text when the mouse is over a button. if (bButtonHasChanged) { SharedSdWindow pWindow (mrSlideSorter.GetContentWindow()); if (pWindow) { if (mpButtonUnderMouse) mrSlideSorter.GetView().GetToolTip().ShowHelpText(mpButtonUnderMouse->GetHelpText()); else mrSlideSorter.GetView().GetToolTip().ShowDefaultHelpText(); } } if (bPageHasChanged || bButtonHasChanged || bButtonStateHasChanged) { if (pOldDescriptor) mrSlideSorter.GetView().RequestRepaint(pOldDescriptor); if (mpDescriptor && pOldDescriptor!=mpDescriptor) mrSlideSorter.GetView().RequestRepaint(mpDescriptor); } } void ButtonBar::UpdateButtonPosition( const model::SharedPageDescriptor& rpDescriptor, const Point& rMousePosition) { if (rpDescriptor && mpBackgroundTheme) { Rectangle aRectangle( rpDescriptor->GetBoundingBox() ); aRectangle.Bottom() -= aRectangle.GetHeight() / 2; if (aRectangle.IsInside(rMousePosition)) mpBackgroundTheme->SetButtonPosition(ButtonBar::BackgroundTheme::BOTTOM); else mpBackgroundTheme->SetButtonPosition(ButtonBar::BackgroundTheme::TOP); // Relayout, to propagate the newest location of the buttons LayoutButtons(); } } void ButtonBar::ResetPage (void) { SetPage(model::SharedPageDescriptor()); } bool ButtonBar::SetPage (const model::SharedPageDescriptor& rpDescriptor) { if (mpDescriptor != rpDescriptor) { mpDescriptor = rpDescriptor; if (mpDescriptor) mbIsExcluded = mpDescriptor->HasState(model::PageDescriptor::ST_Excluded); else mbIsExcluded = false; SetButtonUnderMouse(); mpDownButton.reset(); return true; } else return false; } SharedButton ButtonBar::GetButtonAt (const Point aModelLocation) { if (IsMouseOverBar(aModelLocation)) { const Point aLocalLocation (aModelLocation - mpDescriptor->GetBoundingBox().TopLeft()); ::std::vector& rButtons ( mbIsExcluded ? maExcludedButtons : maRegularButtons); for (sal_uInt32 nIndex=0; nIndexGetBoundingBox().IsInside(aLocalLocation)) { if (rButtons[sal_uInt32(nIndex)]->IsEnabled()) return rButtons[sal_uInt32(nIndex)]; else return SharedButton(); } } } return SharedButton(); } bool ButtonBar::IsMouseOverBar (void) const { return mbIsMouseOverBar; } bool ButtonBar::SetButtonUnderMouse (const SharedButton& rButton) { if (mpButtonUnderMouse != rButton) { if (mpButtonUnderMouse) mpButtonUnderMouse->SetState(Button::State_Normal); mpButtonUnderMouse = rButton; return true; } else return false; } void ButtonBar::Paint ( OutputDevice& rDevice, const model::SharedPageDescriptor& rpDescriptor) { if ( ! rpDescriptor) return; const double nButtonBarAlpha (rpDescriptor->GetVisualState().GetButtonBarAlpha()); if (nButtonBarAlpha >= 1) return; LayoutButtons(rpDescriptor->GetBoundingBox().GetSize()); const Point aOffset (rpDescriptor->GetBoundingBox().TopLeft()); // Paint the background. PaintButtonBackground(rDevice, rpDescriptor, aOffset); // Paint the buttons. const ::std::vector& rButtons ( rpDescriptor->HasState(model::PageDescriptor::ST_Excluded) ? maExcludedButtons : maRegularButtons); const double nButtonAlpha (rpDescriptor->GetVisualState().GetButtonAlpha()); for (sal_uInt32 nIndex=0; nIndexPaint( rDevice, aOffset, nButtonAlpha, mrSlideSorter.GetTheme()); } bool ButtonBar::IsMouseOverButton (void) const { return mpButtonUnderMouse; } void ButtonBar::PaintButtonBackground ( OutputDevice& rDevice, const model::SharedPageDescriptor& rpDescriptor, const Point aOffset) { if (maNormalBackground.IsEmpty()) { if (mpBackgroundTheme) maNormalBackground = mpBackgroundTheme->CreateBackground(); } if (!maNormalBackground.IsEmpty()) { AlphaMask aMask (maNormalBackground.GetSizePixel()); AdaptTransparency( aMask, maNormalBackground.GetAlpha(), rpDescriptor->GetVisualState().GetButtonBarAlpha()); rDevice.DrawBitmapEx(maBackgroundLocation+aOffset, BitmapEx(maNormalBackground.GetBitmap(), aMask)); } } bool ButtonBar::IsMouseOverBar (const Point aModelLocation) const { if ( ! mpDescriptor || ! mpDescriptor->GetBoundingBox().IsInside(aModelLocation)) return false; if ( ! maButtonBoundingBox.IsInside(aModelLocation - mpDescriptor->GetBoundingBox().TopLeft())) return false; return true; } void ButtonBar::LayoutButtons (const Size aPageObjectSize) { if (maPageObjectSize != aPageObjectSize) { maPageObjectSize = aPageObjectSize; if (mpBackgroundTheme) { mpBackgroundTheme->SetPreviewBoundingBox( mrSlideSorter.GetView().GetLayouter().GetPageObjectLayouter()->GetBoundingBox( Point(0,0), PageObjectLayouter::Preview, PageObjectLayouter::ModelCoordinateSystem)); LayoutButtons(); } // Release the background bitmaps so that on the next paint // they are created anew in the right size. maNormalBackground.SetEmpty(); } } bool ButtonBar::LayoutButtons (void) { const sal_Int32 nGap (mrSlideSorter.GetTheme()->GetIntegerValue(Theme::Integer_ButtonGap)); const sal_Int32 nBorder (mrSlideSorter.GetTheme()->GetIntegerValue(Theme::Integer_ButtonBorder)); const Button::IconSize eIconSize (mpBackgroundTheme->GetIconSize()); // Tell buttons which size they are. for (sal_uInt32 nIndex=0; nIndexSetIconSize(eIconSize); for (sal_uInt32 nIndex=0; nIndexSetIconSize(eIconSize); // Determine maximal height and total width of the buttons. // Start with the buttons used for the excluded state. sal_Int32 nMaximumHeight (0); sal_Int32 nExcludedTotalWidth ((maExcludedButtons.size()-1) * nGap + 2*nBorder); for (sal_uInt32 nIndex=0; nIndexGetSize()); if (aSize.Height() > nMaximumHeight) nMaximumHeight = aSize.Height(); nExcludedTotalWidth += aSize.Width(); } // Do the same for the regular buttons. sal_Int32 nRegularTotalWidth ((maRegularButtons.size()-1) * nGap + 2*nBorder); for (sal_uInt32 nIndex=0; nIndexGetSize()); if (aSize.Height() > nMaximumHeight) nMaximumHeight = aSize.Height(); nRegularTotalWidth += aSize.Width(); } nMaximumHeight += 2*nBorder; // Set up the bounding box of the button bar. mpBackgroundTheme->Layout(); maButtonBoundingBox = mpBackgroundTheme->GetButtonArea(); maBackgroundLocation = mpBackgroundTheme->GetBackgroundLocation(); if (mrSlideSorter.GetTheme()->GetIntegerValue(Theme::Integer_ButtonPaintType) == 1) { // Center the buttons. maButtonBoundingBox.Left() += (maButtonBoundingBox.GetWidth() - nRegularTotalWidth)/2; maButtonBoundingBox.Right() = maButtonBoundingBox.Left() + nRegularTotalWidth - 1; } // Place the buttons. Rectangle aBox (maButtonBoundingBox); aBox.Right() -= nBorder; for (sal_Int32 nIndex=maRegularButtons.size()-1; nIndex>=0; --nIndex) { maRegularButtons[nIndex]->Place(aBox); aBox.Right() = maRegularButtons[nIndex]->GetBoundingBox().Left() - nGap; } // For slides excluded from the show there is only one icon placed // exactly like the second of the regular icons. if (maRegularButtons.size()>=2 && maExcludedButtons.size()>=1) { aBox = maRegularButtons[1]->GetBoundingBox(); maExcludedButtons[0]->Place(aBox); } // We return true only when there is no inactive button. for (sal_uInt32 nIndex=0; nIndexIsActive()) return false; for (sal_uInt32 nIndex=0; nIndexIsActive()) return false; return true; } void ButtonBar::RequestFadeIn ( const model::SharedPageDescriptor& rpDescriptor, const bool bAnimate) { if ( ! rpDescriptor) return; if (mnLockCount > 0) return; const double nMinAlpha (0); if ( ! bAnimate) { rpDescriptor->GetVisualState().SetButtonAlpha(nMinAlpha); rpDescriptor->GetVisualState().SetButtonBarAlpha(nMinAlpha); } else StartFadeAnimation(rpDescriptor, nMinAlpha, true); } void ButtonBar::RequestFadeOut ( const model::SharedPageDescriptor& rpDescriptor, const bool bAnimate) { if ( ! rpDescriptor) return; if (mnLockCount > 0) return; const double nMaxAlpha (1); if ( ! bAnimate) { rpDescriptor->GetVisualState().SetButtonAlpha(nMaxAlpha); rpDescriptor->GetVisualState().SetButtonBarAlpha(nMaxAlpha); } else StartFadeAnimation(rpDescriptor, nMaxAlpha, false); } bool ButtonBar::IsVisible (const model::SharedPageDescriptor& rpDescriptor) { const double nMaxAlpha (1); return rpDescriptor && rpDescriptor->GetVisualState().GetButtonBarAlpha() < nMaxAlpha; } void ButtonBar::HandleDataChangeEvent (void) { maExcludedButtons.clear(); maExcludedButtons.push_back(::boost::shared_ptr