/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: PresenterScrollBar.cxx,v $
 *
 * $Revision: 1.4 $
 *
 * 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
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include "PresenterScrollBar.hxx"
#include "PresenterBitmapContainer.hxx"
#include "PresenterComponent.hxx"
#include "PresenterGeometryHelper.hxx"
#include "PresenterPaintManager.hxx"
#include "PresenterUIPainter.hxx"
#include <com/sun/star/awt/PosSize.hpp>
#include <com/sun/star/awt/WindowAttribute.hpp>
#include <com/sun/star/awt/XWindowPeer.hpp>
#include <com/sun/star/awt/XToolkit.hpp>
#include <com/sun/star/rendering/CompositeOperation.hpp>
#include <com/sun/star/rendering/TexturingMode.hpp>
#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
#include <boost/weak_ptr.hpp>
#include <cmath>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using ::rtl::OUString;

#define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString)))

const static double gnScrollBarGap (10);

namespace sdext { namespace presenter {


//===== PresenterScrollBar ====================================================

boost::weak_ptr<PresenterBitmapContainer> PresenterScrollBar::mpSharedBitmaps;

PresenterScrollBar::PresenterScrollBar (
    const Reference<XComponentContext>& rxComponentContext,
    const Reference<awt::XWindow>& rxParentWindow,
    const ::boost::shared_ptr<PresenterPaintManager>& rpPaintManager,
    const ::boost::function<void(double)>& rThumbMotionListener)
    : PresenterScrollBarInterfaceBase(m_aMutex),
      mxComponentContext(rxComponentContext),
      mxParentWindow(rxParentWindow),
      mxWindow(),
      mxCanvas(),
      mxPresenterHelper(),
      mpPaintManager(rpPaintManager),
      mnThumbPosition(0),
      mnTotalSize(0),
      mnThumbSize(0),
      maDragAnchor(-1,-1),
      maThumbMotionListener(rThumbMotionListener),
      meButtonDownArea(None),
      meMouseMoveArea(None),
      mbIsNotificationActive(false),
      mpBitmaps(),
      mpPrevButtonDescriptor(),
      mpNextButtonDescriptor(),
      mpPagerStartDescriptor(),
      mpPagerCenterDescriptor(),
      mpPagerEndDescriptor(),
      mpThumbStartDescriptor(),
      mpThumbCenterDescriptor(),
      mpThumbEndDescriptor()
{
    try
    {
        Reference<lang::XMultiComponentFactory> xFactory (rxComponentContext->getServiceManager());
        if ( ! xFactory.is())
            throw RuntimeException();

        mxPresenterHelper = Reference<drawing::XPresenterHelper>(
            xFactory->createInstanceWithContext(
                OUString::createFromAscii("com.sun.star.comp.Draw.PresenterHelper"),
                rxComponentContext),
            UNO_QUERY_THROW);

        if (mxPresenterHelper.is())
            mxWindow = mxPresenterHelper->createWindow(rxParentWindow,
                sal_False,
                sal_False,
                sal_False,
                sal_False);

        // Make the background transparent.  The slide show paints its own background.
        Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY_THROW);
        if (xPeer.is())
        {
            xPeer->setBackground(0xff000000);
        }

        mxWindow->setVisible(sal_True);
        mxWindow->addWindowListener(this);
        mxWindow->addPaintListener(this);
        mxWindow->addMouseListener(this);
        mxWindow->addMouseMotionListener(this);
    }
    catch (RuntimeException&)
    {
    }
}




PresenterScrollBar::~PresenterScrollBar (void)
{
}




void SAL_CALL PresenterScrollBar::disposing (void)
{
    if (mxWindow.is())
    {
        mxWindow->removeWindowListener(this);
        mxWindow->removePaintListener(this);
        mxWindow->removeMouseListener(this);
        mxWindow->removeMouseMotionListener(this);
        
        Reference<lang::XComponent> xComponent (mxWindow, UNO_QUERY);
        mxWindow = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }

    mpBitmaps.reset();
}




void PresenterScrollBar::SetVisible (const bool bIsVisible)
{
    if (mxWindow.is())
        mxWindow->setVisible(bIsVisible);
}




void PresenterScrollBar::SetPosSize (const css::geometry::RealRectangle2D& rBox)
{
    if (mxWindow.is())
    {
        mxWindow->setPosSize(
            sal_Int32(floor(rBox.X1)),
            sal_Int32(ceil(rBox.Y1)),
            sal_Int32(ceil(rBox.X2-rBox.X1)),
            sal_Int32(floor(rBox.Y2-rBox.Y1)),
            awt::PosSize::POSSIZE);
        UpdateBorders();
    }
}




void PresenterScrollBar::SetThumbPosition (
    double nPosition,
    const bool bAsynchronousUpdate)
{
    SetThumbPosition(nPosition, bAsynchronousUpdate, true, true);
}




void PresenterScrollBar::SetThumbPosition (
    double nPosition,
    const bool bAsynchronousUpdate,
    const bool bValidate,
    const bool bNotify)
{
    if (bValidate)
        nPosition = ValidateThumbPosition(nPosition);

    if (nPosition != mnThumbPosition && ! mbIsNotificationActive)
    {
        mnThumbPosition = nPosition;

        UpdateBorders();
        Repaint(GetRectangle(Total), bAsynchronousUpdate);
        if (bNotify)
            NotifyThumbPositionChange();
    }
}




void PresenterScrollBar::SetTotalSize (const double nTotalSize)
{
    if (mnTotalSize != nTotalSize)
    {
        mnTotalSize = nTotalSize + 1;
        UpdateBorders();
        Repaint(GetRectangle(Total), false);
    }
}




void PresenterScrollBar::SetThumbSize (const double nThumbSize)
{
    OSL_ASSERT(nThumbSize>=0);
    if (mnThumbSize != nThumbSize)
    {
        mnThumbSize = nThumbSize;
        UpdateBorders();
        Repaint(GetRectangle(Total), false);
    }
}




void PresenterScrollBar::SetCanvas (const Reference<css::rendering::XCanvas>& rxCanvas)
{
    if (mxCanvas != rxCanvas)
    {
        mxCanvas = rxCanvas;
        if (mxCanvas.is())
        {            
            if (mpBitmaps.get()==NULL)
            {
                if (mpSharedBitmaps.expired())
                {
                    try
                    {
                        mpBitmaps.reset(new PresenterBitmapContainer(
                            OUString::createFromAscii("PresenterScreenSettings/ScrollBar/Bitmaps"),
                            ::boost::shared_ptr<PresenterBitmapContainer>(),
                            mxComponentContext,
                            mxCanvas,
                            PresenterComponent::GetBasePath(mxComponentContext)));
                        mpSharedBitmaps = mpBitmaps;
                    }
                    catch(Exception&)
                    {
                        OSL_ASSERT(false);
                    }
                }
                else
                    mpBitmaps = ::boost::shared_ptr<PresenterBitmapContainer>(mpSharedBitmaps);
                UpdateBitmaps();
                UpdateBorders();
            }

            Repaint(GetRectangle(Total), false);
        }
    }
}




void PresenterScrollBar::CheckValues (void)
{
    mnThumbPosition = ValidateThumbPosition(mnThumbPosition);
}




double PresenterScrollBar::ValidateThumbPosition (double nPosition)
{
    if (nPosition + mnThumbSize > mnTotalSize)
        nPosition = mnTotalSize - mnThumbSize;
    if (nPosition < 0)
        nPosition = 0;
    return nPosition;
}




void PresenterScrollBar::Paint (
    const awt::Rectangle& rUpdateBox,
    const bool bNoClip)
{
    if ( ! mxCanvas.is() || ! mxWindow.is())
    {
        OSL_ASSERT(mxCanvas.is());
        OSL_ASSERT(mxWindow.is());
        return;
    }

    if ( ! bNoClip)
    {
        if (PresenterGeometryHelper::AreRectanglesDisjoint (rUpdateBox, mxWindow->getPosSize()))
            return;
    }

    PaintComposite(rUpdateBox, PagerUp,
        mpPagerStartDescriptor, mpPagerCenterDescriptor, SharedBitmapDescriptor());
    PaintComposite(rUpdateBox, PagerDown,
        SharedBitmapDescriptor(), mpPagerCenterDescriptor, mpPagerEndDescriptor);
    PaintComposite(rUpdateBox, Thumb,
        mpThumbStartDescriptor, mpThumbCenterDescriptor, mpThumbEndDescriptor);
    PaintBitmap(rUpdateBox, PrevButton, mpPrevButtonDescriptor);
    PaintBitmap(rUpdateBox, NextButton, mpNextButtonDescriptor);

    Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
    if (xSpriteCanvas.is())
        xSpriteCanvas->updateScreen(sal_False);
}






//----- XWindowListener -------------------------------------------------------
    
void SAL_CALL PresenterScrollBar::windowResized (const css::awt::WindowEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    (void)rEvent;
}




   
void SAL_CALL PresenterScrollBar::windowMoved (const css::awt::WindowEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    (void)rEvent;
}




void SAL_CALL PresenterScrollBar::windowShown (const css::lang::EventObject& rEvent)
    throw (css::uno::RuntimeException)
{
    (void)rEvent;
}




void SAL_CALL PresenterScrollBar::windowHidden (const css::lang::EventObject& rEvent)
    throw (css::uno::RuntimeException)
{
    (void)rEvent;
}




//----- XPaintListener --------------------------------------------------------

void SAL_CALL PresenterScrollBar::windowPaint (const css::awt::PaintEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    if (mxWindow.is())
    {
        awt::Rectangle aRepaintBox (rEvent.UpdateRect);
        const awt::Rectangle aWindowBox (mxWindow->getPosSize());
        aRepaintBox.X += aWindowBox.X;
        aRepaintBox.Y += aWindowBox.Y;
        Paint(aRepaintBox);

        Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
        if (xSpriteCanvas.is())
            xSpriteCanvas->updateScreen(sal_False);
    }
}



    
//----- XMouseListener --------------------------------------------------------

void SAL_CALL PresenterScrollBar::mousePressed (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    maDragAnchor.X = rEvent.X;
    maDragAnchor.Y = rEvent.Y;
    meButtonDownArea = GetArea(rEvent.X, rEvent.Y);
}




void SAL_CALL PresenterScrollBar::mouseReleased (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    (void)rEvent;
    if (mxPresenterHelper.is())
        mxPresenterHelper->releaseMouse(mxWindow);

    if (meButtonDownArea != None && meButtonDownArea == GetArea(rEvent.X, rEvent.Y))
    {
        switch (meButtonDownArea)
        {
            case PrevButton:
                SetThumbPosition(mnThumbPosition - mnTotalSize / 100.0, true, true, true);
                break;

            case NextButton:
                SetThumbPosition(mnThumbPosition + mnTotalSize / 100.0, true, true, true);
                break;

            case PagerUp:
                SetThumbPosition(mnThumbPosition - mnTotalSize / 20.0, true, true, true);
                break;

            case PagerDown:
                SetThumbPosition(mnThumbPosition + mnTotalSize / 20.0, true, true, true);
                break;

            default:
                break;
        }
    }
}




void SAL_CALL PresenterScrollBar::mouseEntered (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    (void)rEvent;
}




void SAL_CALL PresenterScrollBar::mouseExited (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    (void)rEvent;
    if (meMouseMoveArea != None)
    {
        const Area eOldMouseMoveArea (meMouseMoveArea);
        meMouseMoveArea = None;
        Repaint(GetRectangle(eOldMouseMoveArea), true);
    }
    meButtonDownArea = None;
    meMouseMoveArea = None;
}





//----- XMouseMotionListener --------------------------------------------------
    
void SAL_CALL PresenterScrollBar::mouseMoved (const css::awt::MouseEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    const Area eArea (GetArea(rEvent.X, rEvent.Y));
    if (eArea != meMouseMoveArea)
    {
        const Area eOldMouseMoveArea (meMouseMoveArea);
        meMouseMoveArea = eArea;
        if (eOldMouseMoveArea != None)
            Repaint(GetRectangle(eOldMouseMoveArea), meMouseMoveArea==None);
        if (meMouseMoveArea != None)
            Repaint(GetRectangle(meMouseMoveArea), true);
    }
}




void SAL_CALL PresenterScrollBar::mouseDragged (const css::awt::MouseEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    if (meButtonDownArea != Thumb)
        return;
    if (mxPresenterHelper.is())
        mxPresenterHelper->captureMouse(mxWindow);

    const double nDragDistance (GetDragDistance(rEvent.X,rEvent.Y));
    UpdateDragAnchor(nDragDistance);
    if (nDragDistance != 0)
    {
        SetThumbPosition(mnThumbPosition + nDragDistance, true, true, true);
    }
}




//----- lang::XEventListener --------------------------------------------------

void SAL_CALL PresenterScrollBar::disposing (const css::lang::EventObject& rEvent)
    throw (css::uno::RuntimeException)
{
    if (rEvent.Source == mxWindow)
        mxWindow = NULL;
}




//-----------------------------------------------------------------------------

geometry::RealRectangle2D PresenterScrollBar::GetRectangle (const Area eArea) const
{
    OSL_ASSERT(eArea>=0 && eArea<__AreaCount__);

    return maBox[eArea];
}




void PresenterScrollBar::Repaint (
    const geometry::RealRectangle2D aBox,
    const bool bAsynchronousUpdate)
{
    if (mpPaintManager.get() != NULL)
        mpPaintManager->Invalidate(
            mxWindow,
            PresenterGeometryHelper::ConvertRectangle(aBox),
            bAsynchronousUpdate);
}




void PresenterScrollBar::PaintBitmap(
    const css::awt::Rectangle& rUpdateBox,
    const Area eArea,
    const SharedBitmapDescriptor& rpBitmaps)
{
    const geometry::RealRectangle2D aLocalBox (GetRectangle(eArea));
    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
    geometry::RealRectangle2D aBox (aLocalBox);
    aBox.X1 += aWindowBox.X;
    aBox.Y1 += aWindowBox.Y;
    aBox.X2 += aWindowBox.X;
    aBox.Y2 += aWindowBox.Y;

    Reference<rendering::XBitmap> xBitmap (GetBitmap(eArea,rpBitmaps));
    
    if (xBitmap.is())
    {
        Reference<rendering::XPolyPolygon2D> xClipPolygon (
            PresenterGeometryHelper::CreatePolygon(
                PresenterGeometryHelper::Intersection(rUpdateBox,
                    PresenterGeometryHelper::ConvertRectangle(aBox)),
                mxCanvas->getDevice()));
        
        const rendering::ViewState aViewState (
            geometry::AffineMatrix2D(1,0,0, 0,1,0),
            xClipPolygon);
        
        const geometry::IntegerSize2D aBitmapSize (xBitmap->getSize());
        rendering::RenderState aRenderState (
            geometry::AffineMatrix2D(
                1,0,aBox.X1 + (aBox.X2-aBox.X1 - aBitmapSize.Width)/2,
                0,1,aBox.Y1 + (aBox.Y2-aBox.Y1 - aBitmapSize.Height)/2),
            NULL,
            Sequence<double>(3),
            rendering::CompositeOperation::SOURCE);

        mxCanvas->drawBitmap(
            xBitmap,
            aViewState,
            aRenderState);
    }
}




void PresenterScrollBar::NotifyThumbPositionChange (void)
{
    if ( ! mbIsNotificationActive)
    {
        mbIsNotificationActive = true;

        try
        {
            maThumbMotionListener(mnThumbPosition);
        }
        catch (Exception&)
        {
        }
        
        mbIsNotificationActive = false;
    }
}




PresenterScrollBar::Area PresenterScrollBar::GetArea (const double nX, const double nY) const
{
    const geometry::RealPoint2D aPoint(nX, nY);
    
    if (PresenterGeometryHelper::IsInside(GetRectangle(Pager), aPoint))
    {
        if (PresenterGeometryHelper::IsInside(GetRectangle(Thumb), aPoint))
            return Thumb;
        else if (PresenterGeometryHelper::IsInside(GetRectangle(PagerUp), aPoint))
            return PagerUp;
        else if (PresenterGeometryHelper::IsInside(GetRectangle(PagerDown), aPoint))
            return PagerDown;
    }
    else if (PresenterGeometryHelper::IsInside(GetRectangle(PrevButton), aPoint))
        return PrevButton;
    else if (PresenterGeometryHelper::IsInside(GetRectangle(NextButton), aPoint))
        return NextButton;

    return None;
}




void PresenterScrollBar::UpdateWidthOrHeight (
    sal_Int32& rSize,
    const SharedBitmapDescriptor& rpDescriptor)
{
    if (rpDescriptor.get() != NULL)
    {
        Reference<rendering::XBitmap> xBitmap (rpDescriptor->GetNormalBitmap());
        if (xBitmap.is())
        {
            const geometry::IntegerSize2D aBitmapSize (xBitmap->getSize());
            const sal_Int32 nBitmapSize = (sal_Int32)GetMinor(aBitmapSize.Width, aBitmapSize.Height);
            if (nBitmapSize > rSize)
                rSize = nBitmapSize;
        }
    }
}




css::uno::Reference<css::rendering::XBitmap> PresenterScrollBar::GetBitmap (
    const Area eArea,
    const SharedBitmapDescriptor& rpBitmaps) const
{
    if (rpBitmaps.get() == NULL)
        return NULL;
    else
        return rpBitmaps->GetBitmap(GetBitmapMode(eArea));
}




PresenterBitmapContainer::BitmapDescriptor::Mode PresenterScrollBar::GetBitmapMode (
    const Area eArea) const
{
    if (IsDisabled(eArea))
        return PresenterBitmapContainer::BitmapDescriptor::Disabled;
    else if (eArea == meMouseMoveArea)
        return PresenterBitmapContainer::BitmapDescriptor::MouseOver;
    else
        return PresenterBitmapContainer::BitmapDescriptor::Normal;
}




bool PresenterScrollBar::IsDisabled (const Area eArea) const
{
    OSL_ASSERT(eArea>=0 && eArea<__AreaCount__);

    return ! maEnabledState[eArea];
}




//===== PresenterVerticalScrollBar ============================================

PresenterVerticalScrollBar::PresenterVerticalScrollBar (
    const Reference<XComponentContext>& rxComponentContext,
    const Reference<awt::XWindow>& rxParentWindow,
    const ::boost::shared_ptr<PresenterPaintManager>& rpPaintManager,
    const ::boost::function<void(double)>& rThumbMotionListener)
    : PresenterScrollBar(rxComponentContext, rxParentWindow, rpPaintManager, rThumbMotionListener),
      mnScrollBarWidth(0)
{
}




PresenterVerticalScrollBar::~PresenterVerticalScrollBar (void)
{
}




double PresenterVerticalScrollBar::GetDragDistance (const sal_Int32 nX, const sal_Int32 nY) const
{
    (void)nX;
    const double nDistance (nY - maDragAnchor.Y);
    if (nDistance == 0)
        return 0;
    else
    {
        const awt::Rectangle aWindowBox (mxWindow->getPosSize());
        const double nBarWidth (aWindowBox.Width);
        const double nPagerHeight (aWindowBox.Height - 2*nBarWidth);
        const double nDragDistance (mnTotalSize / nPagerHeight * nDistance);
        if (nDragDistance + mnThumbPosition < 0)
            return -mnThumbPosition;
        else if (mnThumbPosition + nDragDistance > mnTotalSize-mnThumbSize)
            return mnTotalSize-mnThumbSize-mnThumbPosition;
        else
            return nDragDistance;
    }
}




void PresenterVerticalScrollBar::UpdateDragAnchor (const double nDragDistance)
{
    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
    const double nBarWidth (aWindowBox.Width);
    const double nPagerHeight (aWindowBox.Height - 2*nBarWidth);
    maDragAnchor.Y += nDragDistance * nPagerHeight /  mnTotalSize;
}




sal_Int32 PresenterVerticalScrollBar::GetSize (void) const
{
    return mnScrollBarWidth;
}




geometry::RealPoint2D PresenterVerticalScrollBar::GetPoint (
    const double nMajor, const double nMinor) const
{
    return geometry::RealPoint2D(nMinor, nMajor);
}




double PresenterVerticalScrollBar::GetMajor (const double nX, const double nY) const
{
    (void)nX;
    return nY;
}




double PresenterVerticalScrollBar::GetMinor (const double nX, const double nY) const
{
    (void)nY;
    return nX;
}




void PresenterVerticalScrollBar::UpdateBorders (void)
{
    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
    double nBottom = aWindowBox.Height;
    
    if (mpNextButtonDescriptor.get() != NULL)
    {
        Reference<rendering::XBitmap> xBitmap (mpNextButtonDescriptor->GetNormalBitmap());
        if (xBitmap.is())
        {
            geometry::IntegerSize2D aSize (xBitmap->getSize());
            maBox[NextButton] = geometry::RealRectangle2D(
                0, nBottom - aSize.Height, aWindowBox.Width, nBottom);
            nBottom -= aSize.Height + gnScrollBarGap;
        }
    }
    if (mpPrevButtonDescriptor.get() != NULL)
    {
        Reference<rendering::XBitmap> xBitmap (mpPrevButtonDescriptor->GetNormalBitmap());
        if (xBitmap.is())
        {
            geometry::IntegerSize2D aSize (xBitmap->getSize());
            maBox[PrevButton] = geometry::RealRectangle2D(
                0, nBottom - aSize.Height, aWindowBox.Width, nBottom);
            nBottom -= aSize.Height + gnScrollBarGap;
        }
    }
    const double nPagerHeight (nBottom);
    maBox[Pager] = geometry::RealRectangle2D(
        0,0, aWindowBox.Width, nBottom);
    if (mnTotalSize < 1)
    {
        maBox[Thumb] = maBox[Pager];
        
        // Set up the enabled/disabled states.
        maEnabledState[PrevButton] = false;
        maEnabledState[PagerUp] = false;
        maEnabledState[NextButton] = false;
        maEnabledState[PagerDown] = false;
        maEnabledState[Thumb] = false;
    }
    else
    {
        const double nThumbSize = ::std::min(mnThumbSize,mnTotalSize);
        const double nThumbPosition = ::std::min(::std::max(0.0,mnThumbPosition), mnTotalSize - nThumbSize);
        maBox[Thumb] = geometry::RealRectangle2D(
            0, nThumbPosition / mnTotalSize * nPagerHeight,
            aWindowBox.Width, 
                (nThumbPosition+nThumbSize) / mnTotalSize * nPagerHeight);

        // Set up the enabled/disabled states.
        maEnabledState[PrevButton] = nThumbPosition>0;
        maEnabledState[PagerUp] = nThumbPosition>0;
        maEnabledState[NextButton] = nThumbPosition+nThumbSize < mnTotalSize;
        maEnabledState[PagerDown] = nThumbPosition+nThumbSize < mnTotalSize;
        maEnabledState[Thumb] = nThumbSize < mnTotalSize;
    }
    maBox[PagerUp] = geometry::RealRectangle2D(
        maBox[Pager].X1, maBox[Pager].Y1, maBox[Pager].X2, maBox[Thumb].Y1-1);
    maBox[PagerDown] = geometry::RealRectangle2D(
        maBox[Pager].X1, maBox[Thumb].Y2+1, maBox[Pager].X2, maBox[Pager].Y2);
    maBox[Total] = PresenterGeometryHelper::Union(
        PresenterGeometryHelper::Union(maBox[PrevButton], maBox[NextButton]),
        maBox[Pager]);
}




void PresenterVerticalScrollBar::UpdateBitmaps (void)
{
    if (mpBitmaps.get() != NULL)
    {
        mpPrevButtonDescriptor = mpBitmaps->GetBitmap(A2S("Up"));
        mpNextButtonDescriptor = mpBitmaps->GetBitmap(A2S("Down"));
        mpPagerStartDescriptor = mpBitmaps->GetBitmap(A2S("PagerTop"));
        mpPagerCenterDescriptor = mpBitmaps->GetBitmap(A2S("PagerVertical"));
        mpPagerEndDescriptor = mpBitmaps->GetBitmap(A2S("PagerBottom"));
        mpThumbStartDescriptor = mpBitmaps->GetBitmap(A2S("ThumbTop"));
        mpThumbCenterDescriptor = mpBitmaps->GetBitmap(A2S("ThumbVertical"));
        mpThumbEndDescriptor = mpBitmaps->GetBitmap(A2S("ThumbBottom"));

        mnScrollBarWidth = 0;
        UpdateWidthOrHeight(mnScrollBarWidth, mpPrevButtonDescriptor);
        UpdateWidthOrHeight(mnScrollBarWidth, mpNextButtonDescriptor);
        UpdateWidthOrHeight(mnScrollBarWidth, mpPagerStartDescriptor);
        UpdateWidthOrHeight(mnScrollBarWidth, mpPagerCenterDescriptor);
        UpdateWidthOrHeight(mnScrollBarWidth, mpPagerEndDescriptor);
        UpdateWidthOrHeight(mnScrollBarWidth, mpThumbStartDescriptor);
        UpdateWidthOrHeight(mnScrollBarWidth, mpThumbCenterDescriptor);
        UpdateWidthOrHeight(mnScrollBarWidth, mpThumbEndDescriptor);
        if (mnScrollBarWidth == 0)
            mnScrollBarWidth = 20;
    }
}




void PresenterVerticalScrollBar::PaintComposite(
    const css::awt::Rectangle& rUpdateBox,
    const Area eArea,
    const SharedBitmapDescriptor& rpStartBitmaps,
    const SharedBitmapDescriptor& rpCenterBitmaps,
    const SharedBitmapDescriptor& rpEndBitmaps)
{
    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
    geometry::RealRectangle2D aBox (GetRectangle(eArea));
    aBox.X1 += aWindowBox.X;
    aBox.Y1 += aWindowBox.Y;
    aBox.X2 += aWindowBox.X;
    aBox.Y2 += aWindowBox.Y;

    // Get bitmaps and sizes.

    PresenterUIPainter::PaintVerticalBitmapComposite(
        mxCanvas,
        rUpdateBox,
        (eArea == Thumb
            ? PresenterGeometryHelper::ConvertRectangleWithConstantSize(aBox)
            : PresenterGeometryHelper::ConvertRectangle(aBox)),
        GetBitmap(eArea, rpStartBitmaps),
        GetBitmap(eArea, rpCenterBitmaps),
        GetBitmap(eArea, rpEndBitmaps));
}




//===== PresenterHorizontalScrollBar ============================================

PresenterHorizontalScrollBar::PresenterHorizontalScrollBar (
    const Reference<XComponentContext>& rxComponentContext,
    const Reference<awt::XWindow>& rxParentWindow,
    const ::boost::shared_ptr<PresenterPaintManager>& rpPaintManager,
    const ::boost::function<void(double)>& rThumbMotionListener)
    : PresenterScrollBar(rxComponentContext, rxParentWindow, rpPaintManager, rThumbMotionListener),
      mnScrollBarHeight(0)
{
}




PresenterHorizontalScrollBar::~PresenterHorizontalScrollBar (void)
{
}




double PresenterHorizontalScrollBar::GetDragDistance (const sal_Int32 nX, const sal_Int32 nY) const
{
    (void)nY;
    const double nDistance (nX - maDragAnchor.X);
    if (nDistance == 0)
        return 0;
    else
    {
        const awt::Rectangle aWindowBox (mxWindow->getPosSize());
        const double nBarHeight (aWindowBox.Height);
        const double nPagerWidth (aWindowBox.Width - 2*nBarHeight);
        const double nDragDistance (mnTotalSize / nPagerWidth * nDistance);
        if (nDragDistance + mnThumbPosition < 0)
            return -mnThumbPosition;
        else if (mnThumbPosition + nDragDistance > mnTotalSize-mnThumbSize)
            return mnTotalSize-mnThumbSize-mnThumbPosition;
        else
            return nDragDistance;
    }
}




void PresenterHorizontalScrollBar::UpdateDragAnchor (const double nDragDistance)
{
    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
        const double nBarHeight (aWindowBox.Height);
        const double nPagerWidth (aWindowBox.Width - 2*nBarHeight);
    maDragAnchor.X += nDragDistance * nPagerWidth /  mnTotalSize;
}




sal_Int32 PresenterHorizontalScrollBar::GetSize (void) const
{
    return mnScrollBarHeight;
}





geometry::RealPoint2D PresenterHorizontalScrollBar::GetPoint (
    const double nMajor, const double nMinor) const
{
    return geometry::RealPoint2D(nMajor, nMinor);
}




double PresenterHorizontalScrollBar::GetMajor (const double nX, const double nY) const
{
    (void)nY;
    return nX;
}




double PresenterHorizontalScrollBar::GetMinor (const double nX, const double nY) const
{
    (void)nX;
    return nY;
}




void PresenterHorizontalScrollBar::UpdateBorders (void)
{
    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
    double nRight = aWindowBox.Width;
    const double nGap (2);
    
    if (mpNextButtonDescriptor.get() != NULL)
    {
        Reference<rendering::XBitmap> xBitmap (mpNextButtonDescriptor->GetNormalBitmap());
        if (xBitmap.is())
        {
            geometry::IntegerSize2D aSize (xBitmap->getSize());
            maBox[NextButton] = geometry::RealRectangle2D(
                nRight - aSize.Width,0, nRight, aWindowBox.Height);
            nRight -= aSize.Width + nGap;
        }
    }
    if (mpPrevButtonDescriptor.get() != NULL)
    {
        Reference<rendering::XBitmap> xBitmap (mpPrevButtonDescriptor->GetNormalBitmap());
        if (xBitmap.is())
        {
            geometry::IntegerSize2D aSize (xBitmap->getSize());
            maBox[PrevButton] = geometry::RealRectangle2D(
                nRight - aSize.Width,0, nRight, aWindowBox.Height);
            nRight -= aSize.Width + nGap;
        }
    }
    
    const double nPagerWidth (nRight);
    maBox[Pager] = geometry::RealRectangle2D(
        0,0, nRight, aWindowBox.Height);
    if (mnTotalSize == 0)
    {
        maBox[Thumb] = maBox[Pager];
        
        // Set up the enabled/disabled states.
        maEnabledState[PrevButton] = false;
        maEnabledState[PagerUp] = false;
        maEnabledState[NextButton] = false;
        maEnabledState[PagerDown] = false;
        maEnabledState[Thumb] = false;
    }
    else
    {
        const double nThumbSize = ::std::min(mnThumbSize,mnTotalSize);
        const double nThumbPosition = ::std::min(::std::max(0.0,mnThumbPosition), mnTotalSize - nThumbSize);
        maBox[Thumb] = geometry::RealRectangle2D(
            (nThumbPosition) / mnTotalSize * nPagerWidth, 0,
            (nThumbPosition+nThumbSize) / mnTotalSize * nPagerWidth, aWindowBox.Height);

        // Set up the enabled/disabled states.
        maEnabledState[PrevButton] = nThumbPosition>0;
        maEnabledState[PagerUp] = nThumbPosition>0;
        maEnabledState[NextButton] = nThumbPosition+nThumbSize < mnTotalSize;
        maEnabledState[PagerDown] = nThumbPosition+nThumbSize < mnTotalSize;
        maEnabledState[Thumb] = nThumbSize < mnTotalSize;
    }
    maBox[PagerUp] = geometry::RealRectangle2D(
        maBox[Pager].X1, maBox[Pager].Y1, maBox[Thumb].X1-1, maBox[Pager].Y2);
    maBox[PagerDown] = geometry::RealRectangle2D(
        maBox[Thumb].X2+1, maBox[Pager].Y1, maBox[Pager].X2, maBox[Pager].Y2);
    maBox[Total] = PresenterGeometryHelper::Union(
        PresenterGeometryHelper::Union(maBox[PrevButton], maBox[NextButton]),
        maBox[Pager]);
}




void PresenterHorizontalScrollBar::UpdateBitmaps (void)
{
    if (mpBitmaps.get() != NULL)
    {
        mpPrevButtonDescriptor = mpBitmaps->GetBitmap(A2S("Left"));
        mpNextButtonDescriptor = mpBitmaps->GetBitmap(A2S("Right"));
        mpPagerStartDescriptor = mpBitmaps->GetBitmap(A2S("PagerLeft"));
        mpPagerCenterDescriptor = mpBitmaps->GetBitmap(A2S("PagerHorizontal"));
        mpPagerEndDescriptor = mpBitmaps->GetBitmap(A2S("PagerRight"));
        mpThumbStartDescriptor = mpBitmaps->GetBitmap(A2S("ThumbLeft"));
        mpThumbCenterDescriptor = mpBitmaps->GetBitmap(A2S("ThumbHorizontal"));
        mpThumbEndDescriptor = mpBitmaps->GetBitmap(A2S("ThumbRight"));

        mnScrollBarHeight = 0;
        UpdateWidthOrHeight(mnScrollBarHeight, mpPrevButtonDescriptor);
        UpdateWidthOrHeight(mnScrollBarHeight, mpNextButtonDescriptor);
        UpdateWidthOrHeight(mnScrollBarHeight, mpPagerStartDescriptor);
        UpdateWidthOrHeight(mnScrollBarHeight, mpPagerCenterDescriptor);
        UpdateWidthOrHeight(mnScrollBarHeight, mpPagerEndDescriptor);
        UpdateWidthOrHeight(mnScrollBarHeight, mpThumbStartDescriptor);
        UpdateWidthOrHeight(mnScrollBarHeight, mpThumbCenterDescriptor);
        UpdateWidthOrHeight(mnScrollBarHeight, mpThumbEndDescriptor);
        if (mnScrollBarHeight == 0)
            mnScrollBarHeight = 20;
    }
}



void PresenterHorizontalScrollBar::PaintComposite(
    const css::awt::Rectangle& rUpdateBox,
    const Area eArea,
    const SharedBitmapDescriptor& rpStartBitmaps,
    const SharedBitmapDescriptor& rpCenterBitmaps,
    const SharedBitmapDescriptor& rpEndBitmaps)
{
    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
    geometry::RealRectangle2D aBox (GetRectangle(eArea));
    aBox.X1 += aWindowBox.X;
    aBox.Y1 += aWindowBox.Y;
    aBox.X2 += aWindowBox.X;
    aBox.Y2 += aWindowBox.Y;

    PresenterUIPainter::PaintHorizontalBitmapComposite(
        mxCanvas,
        rUpdateBox,
        PresenterGeometryHelper::ConvertRectangle(aBox),
        GetBitmap(eArea, rpStartBitmaps),
        GetBitmap(eArea, rpCenterBitmaps),
        GetBitmap(eArea, rpEndBitmaps));
}




} } // end of namespace ::sdext::presenter
