/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sdext.hxx"

#include "PresenterToolBar.hxx"

#include "PresenterBitmapContainer.hxx"
#include "PresenterCanvasHelper.hxx"
#include "PresenterGeometryHelper.hxx"
#include "PresenterPaintManager.hxx"
#include "PresenterPaneBase.hxx"
#include "PresenterPaneFactory.hxx"
#include "PresenterTimer.hxx"
#include "PresenterWindowManager.hxx"

#include <cppuhelper/compbase2.hxx>
#include <com/sun/star/awt/FontDescriptor.hpp>
#include <com/sun/star/awt/PosSize.hpp>
#include <com/sun/star/awt/XWindowPeer.hpp>
#include <com/sun/star/deployment/XPackageInformationProvider.hpp>
#include <com/sun/star/drawing/framework/XControllerManager.hpp>
#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
#include <com/sun/star/drawing/framework/XPane.hpp>
#include <com/sun/star/geometry/AffineMatrix2D.hpp>
#include <com/sun/star/lang/XServiceName.hpp>
#include <com/sun/star/rendering/CompositeOperation.hpp>
#include <com/sun/star/rendering/RenderState.hpp>
#include <com/sun/star/rendering/TextDirection.hpp>
#include <com/sun/star/rendering/ViewState.hpp>
#include <com/sun/star/rendering/XSpriteCanvas.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/util/Color.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <rtl/ustrbuf.hxx>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <map>

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

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

namespace sdext { namespace presenter {

static const sal_Int32 gnGapSize (20);
static const sal_Int32 gnMinimalSeparatorSize (20);
static const sal_Int32 gnSeparatorInset (0);

namespace {

    class Text
    {
    public:
        Text (void);
        Text (const Text& rText);
        Text (
            const OUString& rsText,
            const PresenterTheme::SharedFontDescriptor& rpFont);

        void SetText (const OUString& rsText);
        OUString GetText (void) const;
        PresenterTheme::SharedFontDescriptor GetFont (void) const;

        void Paint (
            const Reference<rendering::XCanvas>& rxCanvas,
            const rendering::ViewState& rViewState,
            const awt::Rectangle& rBoundingBox,
            const awt::Point& rOffset);

        geometry::RealRectangle2D GetBoundingBox (
            const Reference<rendering::XCanvas>& rxCanvas);

    private:
        OUString msText;
        PresenterTheme::SharedFontDescriptor mpFont;
    };

    class ElementMode
        : private ::boost::noncopyable
    {
    public:
        ElementMode (void);
        
        SharedBitmapDescriptor mpIcon;
        OUString msAction;
        Text maText;
        
        void ReadElementMode (
            const Reference<beans::XPropertySet>& rxProperties,
            const ::rtl::OUString& rsModeName,
            ::boost::shared_ptr<ElementMode>& rpDefaultMode,
            ::sdext::presenter::PresenterToolBar::Context& rContext);
    };
    typedef ::boost::shared_ptr<ElementMode> SharedElementMode;

}  // end of anonymous namespace


class PresenterToolBar::Context
    : private ::boost::noncopyable
{
public:
    Reference<drawing::XPresenterHelper> mxPresenterHelper;
    css::uno::Reference<css::rendering::XCanvas> mxCanvas;
};

    


//===== PresenterToolBar::Element =============================================

namespace {
    typedef cppu::WeakComponentImplHelper2< 
        css::document::XEventListener,
        css::frame::XStatusListener
        > ElementInterfaceBase;

    class Element
        : private ::cppu::BaseMutex,
          private ::boost::noncopyable,
          public ElementInterfaceBase
    {
    public:
        Element (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual ~Element (void);

        virtual void SAL_CALL disposing (void);

        virtual void SetModes (
            const SharedElementMode& rpNormalMode,
            const SharedElementMode& rpMouseOverMode,
            const SharedElementMode& rpSelectedMode,
            const SharedElementMode& rpDisabledMode);
        virtual void CurrentSlideHasChanged (void);
        virtual void SetLocation (const awt::Point& rLocation);
        virtual void SetSize (const geometry::RealSize2D& rSize);
        virtual void Paint (
            const Reference<rendering::XCanvas>& rxCanvas,
            const rendering::ViewState& rViewState) = 0;
        awt::Size GetBoundingSize (
            const Reference<rendering::XCanvas>& rxCanvas);
        awt::Rectangle GetBoundingBox (void) const;
        virtual bool SetState (const bool bIsOver, const bool bIsPressed);
        virtual void Invalidate (const bool bSynchronous = true);
        virtual bool IsOutside (const awt::Rectangle& rBox);
        virtual bool IsFilling (void) const;
        void UpdateState (void);

        OUString GetAction (void) const;

        // lang::XEventListener

        virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent)
            throw(css::uno::RuntimeException);
    
        // document::XEventListener

        virtual void SAL_CALL notifyEvent (const css::document::EventObject& rEvent)
            throw(css::uno::RuntimeException);

        // frame::XStatusListener

        virtual void SAL_CALL statusChanged (const css::frame::FeatureStateEvent& rEvent)
            throw(css::uno::RuntimeException);

    protected:
        ::rtl::Reference<PresenterToolBar> mpToolBar;
        awt::Point maLocation;
        awt::Size maSize;
        SharedElementMode mpNormal;
        SharedElementMode mpMouseOver;
        SharedElementMode mpSelected;
        SharedElementMode mpDisabled;
        SharedElementMode mpMode;
        bool mbIsOver;
        bool mbIsPressed;
        bool mbIsSelected;
    
        virtual awt::Size CreateBoundingSize (
            const Reference<rendering::XCanvas>& rxCanvas) = 0;

        bool IsEnabled (void) const;
        void SetEnabledState (const bool bIsEnabled);

    private:
        bool mbIsEnabled;
    };

} // end of anonymous namespace


class PresenterToolBar::ElementContainerPart
    : public ::std::vector<rtl::Reference<Element> >
{
};




//===== Button ================================================================

namespace {

    class Button : public Element
    {
    public:
        static ::rtl::Reference<Element> Create (
            const ::rtl::Reference<PresenterToolBar>& rpToolBar);

        virtual ~Button (void);
        virtual void SAL_CALL disposing (void);

        virtual void Paint (
            const Reference<rendering::XCanvas>& rxCanvas,
            const rendering::ViewState& rViewState);

        // lang::XEventListener

        virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent)
            throw(css::uno::RuntimeException);
    
    protected:
        virtual awt::Size CreateBoundingSize (
            const Reference<rendering::XCanvas>& rxCanvas);
    
    private:
        bool mbIsListenerRegistered;

        Button (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        void Initialize (void);
        void PaintIcon (
            const Reference<rendering::XCanvas>& rxCanvas,
            const sal_Int32 nTextHeight,
            const rendering::ViewState& rViewState);
        PresenterBitmapDescriptor::Mode GetMode (void) const;
    };




//===== Label =================================================================

    class Label : public Element
    {
    public:
        Label (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        
        void SetText (const OUString& rsText);
        virtual void Paint (
            const Reference<rendering::XCanvas>& rxCanvas,
            const rendering::ViewState& rViewState);
        virtual bool SetState (const bool bIsOver, const bool bIsPressed);

    protected:
        virtual awt::Size CreateBoundingSize (
            const Reference<rendering::XCanvas>& rxCanvas);
    };


// Some specialized controls.


    class ProgressLabel : public Label
    {
    public:
        ProgressLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual void CurrentSlideHasChanged (void);
    };

    class TimeFormatter
    {
    public:
        TimeFormatter (void);
        OUString FormatTime (const oslDateTime& rTime);
    private:
        bool mbIs24HourFormat;
        bool mbIsAmPmFormat;
        bool mbIsShowSeconds;
    };

    class TimeLabel : public Label
    {
    public:
        void ConnectToTimer (void);
        virtual void TimeHasChanged (const oslDateTime& rCurrentTime) = 0;
    protected:
        TimeLabel(const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        using Element::disposing;
        virtual void SAL_CALL disposing (void);
    private:
        class Listener : public PresenterClockTimer::Listener
        {
        public:
            Listener (const ::rtl::Reference<TimeLabel>& rxLabel)
                : mxLabel(rxLabel) {}
            virtual ~Listener (void) {}
            virtual void TimeHasChanged (const oslDateTime& rCurrentTime)
            { if (mxLabel.is()) mxLabel->TimeHasChanged(rCurrentTime); }
        private:
            ::rtl::Reference<TimeLabel> mxLabel;
        };
        ::boost::shared_ptr<PresenterClockTimer::Listener> mpListener;
    };

    class CurrentTimeLabel : public TimeLabel
    {
    public:
        static ::rtl::Reference<Element> Create (
            const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual void SetModes (
            const SharedElementMode& rpNormalMode,
            const SharedElementMode& rpMouseOverMode,
            const SharedElementMode& rpSelectedMode,
            const SharedElementMode& rpDisabledMode);
    private:
        TimeFormatter maTimeFormatter;
        CurrentTimeLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual ~CurrentTimeLabel (void);
        virtual void TimeHasChanged (const oslDateTime& rCurrentTime);
    };

    class PresentationTimeLabel : public TimeLabel
    {
    public:
        static ::rtl::Reference<Element> Create (
            const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual void SetModes (
            const SharedElementMode& rpNormalMode,
            const SharedElementMode& rpMouseOverMode,
            const SharedElementMode& rpSelectedMode,
            const SharedElementMode& rpDisabledMode);
    private:
        TimeFormatter maTimeFormatter;
        TimeValue maStartTimeValue;
        PresentationTimeLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual ~PresentationTimeLabel (void);
        virtual void TimeHasChanged (const oslDateTime& rCurrentTime);
    };

    class VerticalSeparator : public Element
    {
    public:
        explicit VerticalSeparator (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual void Paint (
            const Reference<rendering::XCanvas>& rxCanvas,
            const rendering::ViewState& rViewState);
        virtual bool IsFilling (void) const;

    protected:
        virtual awt::Size CreateBoundingSize (
            const Reference<rendering::XCanvas>& rxCanvas);
    };

    class HorizontalSeparator : public Element
    {
    public:
        explicit HorizontalSeparator (const ::rtl::Reference<PresenterToolBar>& rpToolBar);
        virtual void Paint (
            const Reference<rendering::XCanvas>& rxCanvas,
            const rendering::ViewState& rViewState);
        virtual bool IsFilling (void) const;

    protected:
        virtual awt::Size CreateBoundingSize (
            const Reference<rendering::XCanvas>& rxCanvas);
    };
} // end of anonymous namespace



//===== PresenterToolBar ======================================================

PresenterToolBar::PresenterToolBar (
    const Reference<XComponentContext>& rxContext,
    const css::uno::Reference<css::awt::XWindow>& rxWindow,
    const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
    const ::rtl::Reference<PresenterController>& rpPresenterController,
    const Anchor eAnchor)
    : PresenterToolBarInterfaceBase(m_aMutex),
      mxComponentContext(rxContext),
      maElementContainer(),
      mpCurrentContainerPart(),
      mxWindow(rxWindow),
      mxCanvas(rxCanvas),
      mxSlideShowController(),
      mxCurrentSlide(),
      mpPresenterController(rpPresenterController),
      mbIsLayoutPending(false),
      meAnchor(eAnchor),
      maBoundingBox(),
      maMinimalSize()
{
}




void PresenterToolBar::Initialize (
    const ::rtl::OUString& rsConfigurationPath)
{
    try
    {
        CreateControls(rsConfigurationPath);

        if (mxWindow.is())
        {
            mxWindow->addWindowListener(this);
            mxWindow->addPaintListener(this);
            mxWindow->addMouseListener(this);
            mxWindow->addMouseMotionListener(this);

            Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
            if (xPeer.is())
                xPeer->setBackground(util::Color(0xff000000));
            
            mxWindow->setVisible(sal_True);
        }

        mxSlideShowController = mpPresenterController->GetSlideShowController();
        UpdateSlideNumber();
        mbIsLayoutPending = true;
    }
    catch (RuntimeException&)
    {
        mpCurrentContainerPart.reset();
        maElementContainer.clear();
        throw;
    }
}




PresenterToolBar::~PresenterToolBar (void)
{
}




void SAL_CALL PresenterToolBar::disposing (void)
{
    if (mxWindow.is())
    {
        mxWindow->removeWindowListener(this);
        mxWindow->removePaintListener(this);
        mxWindow->removeMouseListener(this);
        mxWindow->removeMouseMotionListener(this);
        mxWindow = NULL;
    }

    // Dispose tool bar elements.
    ElementContainer::iterator iPart (maElementContainer.begin());
    ElementContainer::const_iterator iEnd (maElementContainer.end());
    for ( ; iPart!=iEnd; ++iPart)
    {
        OSL_ASSERT(iPart->get()!=NULL);
        ElementContainerPart::iterator iElement ((*iPart)->begin());
        ElementContainerPart::const_iterator iPartEnd ((*iPart)->end());
        for ( ; iElement!=iPartEnd; ++iElement)
        {
            if (iElement->get() != NULL)
            {
                ::rtl::Reference<Element> pElement (*iElement);
                Reference<lang::XComponent> xComponent (
                    static_cast<XWeak*>(pElement.get()), UNO_QUERY);
                if (xComponent.is())
                    xComponent->dispose();
            }
        }
    }

    mpCurrentContainerPart.reset();
    maElementContainer.clear();
}




void PresenterToolBar::InvalidateArea (
    const awt::Rectangle& rRepaintBox,
    const bool bSynchronous)
{
    mpPresenterController->GetPaintManager()->Invalidate(
        mxWindow,
        rRepaintBox,
        bSynchronous);
}




sal_Int32 PresenterToolBar::GetCurrentSlideIndex (void)
{
    if (mxSlideShowController.is())
        return mxSlideShowController->getCurrentSlideIndex();
    else
        return -1;
}




sal_Int32 PresenterToolBar::GetSlideCount (void)
{
    if (mxSlideShowController.is())
        return mxSlideShowController->getSlideCount();
    else
        return 0;
}




void PresenterToolBar::RequestLayout (void)
{
    mbIsLayoutPending = true;

    mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
}




geometry::RealSize2D PresenterToolBar::GetSize (void)
{
    if (mbIsLayoutPending)
        Layout(mxCanvas);
    return geometry::RealSize2D(
        maBoundingBox.X2 - maBoundingBox.X1,
        maBoundingBox.Y2 - maBoundingBox.Y1);
}




geometry::RealSize2D PresenterToolBar::GetMinimalSize (void)
{
    if (mbIsLayoutPending)
        Layout(mxCanvas);
    return maMinimalSize;
}




::rtl::Reference<PresenterController> PresenterToolBar::GetPresenterController (void) const
{
    return mpPresenterController;
}




Reference<awt::XWindow> PresenterToolBar::GetWindow (void) const
{
    return mxWindow;
}




Reference<XComponentContext> PresenterToolBar::GetComponentContext (void) const
{
    return mxComponentContext;
}




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

void SAL_CALL PresenterToolBar::disposing (const lang::EventObject& rEventObject)
    throw (RuntimeException)
{
    if (rEventObject.Source == mxWindow)
        mxWindow = NULL;
}




//----- XWindowListener -------------------------------------------------------
    
void SAL_CALL PresenterToolBar::windowResized (const awt::WindowEvent& rEvent)
    throw (RuntimeException)
{
    (void)rEvent;
    mbIsLayoutPending = true;
}




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




void SAL_CALL PresenterToolBar::windowShown (const lang::EventObject& rEvent)
    throw (RuntimeException)
{
    (void)rEvent;
    mbIsLayoutPending = true;
}




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




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

void SAL_CALL PresenterToolBar::windowPaint (const css::awt::PaintEvent& rEvent)
    throw (RuntimeException)
{
    if ( ! mxCanvas.is())
        return;

    if ( ! mbIsPresenterViewActive)
        return;

    const rendering::ViewState aViewState (
        geometry::AffineMatrix2D(1,0,0, 0,1,0),
        PresenterGeometryHelper::CreatePolygon(rEvent.UpdateRect, mxCanvas->getDevice()));

    if (mbIsLayoutPending)
        Layout(mxCanvas);
    
    Paint(rEvent.UpdateRect, aViewState);

    // Make the back buffer visible.
    Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
    if (xSpriteCanvas.is())
        xSpriteCanvas->updateScreen(sal_False);
}




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

void SAL_CALL PresenterToolBar::mousePressed (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    CheckMouseOver(rEvent, true, true);
}




void SAL_CALL PresenterToolBar::mouseReleased (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    CheckMouseOver(rEvent, true);
}




void SAL_CALL PresenterToolBar::mouseEntered (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    CheckMouseOver(rEvent, true);
}




void SAL_CALL PresenterToolBar::mouseExited (const css::awt::MouseEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    CheckMouseOver(rEvent, false);
}




//----- XMouseMotionListener --------------------------------------------------
    
void SAL_CALL PresenterToolBar::mouseMoved (const css::awt::MouseEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    ThrowIfDisposed();

    CheckMouseOver(rEvent, true);
}




void SAL_CALL PresenterToolBar::mouseDragged (const css::awt::MouseEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    ThrowIfDisposed();
    (void)rEvent;
}




//----- XDrawView -------------------------------------------------------------

void SAL_CALL PresenterToolBar::setCurrentPage (const Reference<drawing::XDrawPage>& rxSlide)
    throw (RuntimeException)
{
    if (rxSlide != mxCurrentSlide)
    {
        mxCurrentSlide = rxSlide;
        UpdateSlideNumber();
    }
}




Reference<drawing::XDrawPage> SAL_CALL PresenterToolBar::getCurrentPage (void)
    throw (RuntimeException)
{
    return mxCurrentSlide;
}




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

void PresenterToolBar::CreateControls (
    const ::rtl::OUString& rsConfigurationPath)
{
    if ( ! mxWindow.is())
        return;
    
    // Expand the macro in the bitmap file names.
    PresenterConfigurationAccess aConfiguration (
        mxComponentContext,
        OUString::createFromAscii("/org.openoffice.Office.PresenterScreen/"),
        PresenterConfigurationAccess::READ_ONLY);

    mpCurrentContainerPart.reset(new ElementContainerPart());
    maElementContainer.clear();
    maElementContainer.push_back(mpCurrentContainerPart);

    Reference<container::XHierarchicalNameAccess> xToolBarNode (
        aConfiguration.GetConfigurationNode(rsConfigurationPath),
        UNO_QUERY);
    if (xToolBarNode.is())
    {
        Reference<container::XNameAccess> xEntries (
            PresenterConfigurationAccess::GetConfigurationNode(xToolBarNode, A2S("Entries")),
            UNO_QUERY);
        Context aContext;
        aContext.mxPresenterHelper = mpPresenterController->GetPresenterHelper();
        aContext.mxCanvas = mxCanvas;
        if (xEntries.is()
            && aContext.mxPresenterHelper.is()
            && aContext.mxCanvas.is())
        {
            PresenterConfigurationAccess::ForAll(
                xEntries,
                ::boost::bind(&PresenterToolBar::ProcessEntry, this, _2, ::boost::ref(aContext)));
        }
    }
}




void PresenterToolBar::ProcessEntry (
    const Reference<beans::XPropertySet>& rxProperties,
    Context& rContext)
{
    if ( ! rxProperties.is())
        return;

    // Type has to be present.
    OUString sType;
    if ( ! (PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Type")) >>= sType))
        return;

    OUString sName;
    PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Name")) >>= sName;

    // Read mode specific values.
    SharedElementMode pNormalMode (new ElementMode());
    SharedElementMode pMouseOverMode (new ElementMode());
    SharedElementMode pSelectedMode (new ElementMode());
    SharedElementMode pDisabledMode (new ElementMode());
    pNormalMode->ReadElementMode(rxProperties, A2S("Normal"), pNormalMode, rContext);
    pMouseOverMode->ReadElementMode(rxProperties, A2S("MouseOver"), pNormalMode, rContext);
    pSelectedMode->ReadElementMode(rxProperties, A2S("Selected"), pNormalMode, rContext);
    pDisabledMode->ReadElementMode(rxProperties, A2S("Disabled"), pNormalMode, rContext);

    // Create new element.
    ::rtl::Reference<Element> pElement;
    if (sType.equalsAscii("Button"))
        pElement = Button::Create(this);
    else if (sType.equalsAscii("CurrentTimeLabel"))
        pElement = CurrentTimeLabel::Create(this);
    else if (sType.equalsAscii("PresentationTimeLabel"))
        pElement = PresentationTimeLabel::Create(this);
    else if (sType.equalsAscii("VerticalSeparator"))
        pElement = ::rtl::Reference<Element>(new VerticalSeparator(this));
    else if (sType.equalsAscii("HorizontalSeparator"))
        pElement = ::rtl::Reference<Element>(new HorizontalSeparator(this));
    else if (sType.equalsAscii("Label"))
        pElement = ::rtl::Reference<Element>(new Label(this));
    else if (sType.equalsAscii("ChangeOrientation"))
    {
        mpCurrentContainerPart.reset(new ElementContainerPart());
        maElementContainer.push_back(mpCurrentContainerPart);
        return;
    }
    if (pElement.is())
    {
        pElement->SetModes( pNormalMode, pMouseOverMode, pSelectedMode, pDisabledMode);
        pElement->UpdateState();
        if (mpCurrentContainerPart.get() != NULL)
            mpCurrentContainerPart->push_back(pElement);
    }
}




void PresenterToolBar::Layout (
    const Reference<rendering::XCanvas>& rxCanvas)
{
    if (maElementContainer.size() == 0)
        return;

    mbIsLayoutPending = false;

    const awt::Rectangle aWindowBox (mxWindow->getPosSize());
    ElementContainer::iterator iPart;
    ElementContainer::iterator iEnd (maElementContainer.end());
    ::std::vector<geometry::RealSize2D> aPartSizes (maElementContainer.size());
    geometry::RealSize2D aTotalSize (0,0);
	bool bIsHorizontal (true);
    sal_Int32 nIndex;
    double nTotalHorizontalGap (0);
    sal_Int32 nGapCount (0);
    for (iPart=maElementContainer.begin(),nIndex=0; iPart!=iEnd; ++iPart,++nIndex)
    {
        geometry::RealSize2D aSize (CalculatePartSize(rxCanvas, *iPart, bIsHorizontal));
        
        // Remember the size of each part for later.
        aPartSizes[nIndex] = aSize;

        // Add gaps between elements.
        if ((*iPart)->size()>1 && bIsHorizontal)
        {
            nTotalHorizontalGap += ((*iPart)->size() - 1) * gnGapSize;
            nGapCount += (*iPart)->size()-1;
        }
        
        // Orientation changes for each part.
        bIsHorizontal = !bIsHorizontal;
        // Width is accumulated.
        aTotalSize.Width += aSize.Width;
        // Height is the maximum height of all parts.
        aTotalSize.Height = ::std::max(aTotalSize.Height, aSize.Height);
    }
    // Add gaps between parts.
    if (maElementContainer.size() > 1)
    {
        nTotalHorizontalGap += (maElementContainer.size() - 1) * gnGapSize;
        nGapCount += maElementContainer.size()-1;
    }

    // Calculate the minimal size so that the window size of the tool bar
    // can be adapted accordingly.
    maMinimalSize = aTotalSize;
    maMinimalSize.Width += nTotalHorizontalGap;

    // Calculate the gaps between elements.
    double nGapWidth (0);
    if (nGapCount > 0)
    {
        if (aTotalSize.Width + nTotalHorizontalGap > aWindowBox.Width)
            nTotalHorizontalGap = aWindowBox.Width - aTotalSize.Width;
        nGapWidth = nTotalHorizontalGap / nGapCount;
    }

    // Determine the location of the left edge.
    double nX (0);
    switch (meAnchor)
    {
        case Left : nX = 0; break;
        case Center: nX = (aWindowBox.Width - aTotalSize.Width - nTotalHorizontalGap) / 2; break;
        case Right: nX = aWindowBox.Width - aTotalSize.Width - nTotalHorizontalGap; break;
    }

    // Place the parts.
    double nY ((aWindowBox.Height - aTotalSize.Height) / 2);
    bIsHorizontal = true;
    
    maBoundingBox.X1 = nX;
    maBoundingBox.Y1 = nY;
    maBoundingBox.X2 = nX + aTotalSize.Width + nTotalHorizontalGap;
    maBoundingBox.Y2 = nY + aTotalSize.Height;
    
    for (iPart=maElementContainer.begin(), nIndex=0; iPart!=iEnd; ++iPart,++nIndex)
    {
        geometry::RealRectangle2D aBoundingBox(
            nX, nY,
            nX+aPartSizes[nIndex].Width, nY+aTotalSize.Height);

        // Add space for gaps between elements.
        if ((*iPart)->size() > 1)
            if (bIsHorizontal)
                aBoundingBox.X2 += ((*iPart)->size()-1) * nGapWidth;

        LayoutPart(rxCanvas, *iPart, aBoundingBox, aPartSizes[nIndex], bIsHorizontal);
        bIsHorizontal = !bIsHorizontal;
        nX += aBoundingBox.X2 - aBoundingBox.X1 + nGapWidth;
    }

    // The whole window has to be repainted.
    mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
}




geometry::RealSize2D PresenterToolBar::CalculatePartSize (
    const Reference<rendering::XCanvas>& rxCanvas,
    const SharedElementContainerPart& rpPart,
    const bool bIsHorizontal)
{
    geometry::RealSize2D aTotalSize (0,0);

    if (mxWindow.is())
    {
        const awt::Rectangle aWindowBox (mxWindow->getPosSize());

        // Calculate the summed width of all elements.
        ElementContainerPart::const_iterator iElement;
        for (iElement=rpPart->begin(); iElement!=rpPart->end(); ++iElement)
        {
            if (iElement->get() == NULL)
                continue;

            const awt::Size aBSize ((*iElement)->GetBoundingSize(rxCanvas));
            if (bIsHorizontal)
            {
                aTotalSize.Width += aBSize.Width;
                if (aBSize.Height > aTotalSize.Height)
                    aTotalSize.Height = aBSize.Height;
            }
            else
            {
                aTotalSize.Height += aBSize.Height;
                if (aBSize.Width > aTotalSize.Width)
                    aTotalSize.Width = aBSize.Width;
            }
        }
    }
    return aTotalSize;
}




void PresenterToolBar::LayoutPart (
    const Reference<rendering::XCanvas>& rxCanvas,
    const SharedElementContainerPart& rpPart,
    const geometry::RealRectangle2D& rBoundingBox,
    const geometry::RealSize2D& rPartSize,
    const bool bIsHorizontal)
{
    double nGap (0);
    if (rpPart->size() > 1)
    {
        if (bIsHorizontal)
            nGap = (rBoundingBox.X2 - rBoundingBox.X1 - rPartSize.Width) / (rpPart->size()-1);
        else
            nGap = (rBoundingBox.Y2 - rBoundingBox.Y1 - rPartSize.Height) / (rpPart->size()-1);
    }

    // Place the elements.
    double nX (rBoundingBox.X1);
    double nY (rBoundingBox.Y1);

    ElementContainerPart::const_iterator iElement;
    ElementContainerPart::const_iterator iEnd (rpPart->end());
    for (iElement=rpPart->begin(); iElement!=iEnd; ++iElement)
    {
        if (iElement->get() == NULL)
            continue;

        const awt::Size aElementSize ((*iElement)->GetBoundingSize(rxCanvas));
        if (bIsHorizontal)
        {
            if ((*iElement)->IsFilling())
            {
                nY = rBoundingBox.Y1;
                (*iElement)->SetSize(geometry::RealSize2D(aElementSize.Width, rBoundingBox.Y2 - rBoundingBox.Y1));
            }
            else
                nY = rBoundingBox.Y1 + (rBoundingBox.Y2-rBoundingBox.Y1 - aElementSize.Height) / 2;
            (*iElement)->SetLocation(awt::Point(sal_Int32(0.5 + nX), sal_Int32(0.5 + nY)));
            nX += aElementSize.Width + nGap;
        }
        else
        {
            if ((*iElement)->IsFilling())
            {
                nX = rBoundingBox.X1;
                (*iElement)->SetSize(geometry::RealSize2D(rBoundingBox.X2 - rBoundingBox.X1, aElementSize.Height));
            }
            else
                nX = rBoundingBox.X1 + (rBoundingBox.X2-rBoundingBox.X1 - aElementSize.Width) / 2;
            (*iElement)->SetLocation(awt::Point(sal_Int32(0.5 + nX), sal_Int32(0.5 + nY)));
            nY += aElementSize.Height + nGap;
        }
    }
}




void PresenterToolBar::Paint (
    const awt::Rectangle& rUpdateBox,
    const rendering::ViewState& rViewState)
{
    OSL_ASSERT(mxCanvas.is());

    ElementContainer::iterator iPart;
    ElementContainer::const_iterator iEnd (maElementContainer.end());
    for (iPart=maElementContainer.begin(); iPart!=iEnd; ++iPart)
    {
        ElementContainerPart::iterator iElement;
        ElementContainerPart::const_iterator iPartEnd ((*iPart)->end());
        for (iElement=(*iPart)->begin(); iElement!=iPartEnd; ++iElement)
        {
            if (iElement->get() != NULL)
            {
                if ( ! (*iElement)->IsOutside(rUpdateBox))
                    (*iElement)->Paint(mxCanvas, rViewState);
            }
        }
    }
}




void PresenterToolBar::UpdateSlideNumber (void)
{
	if( mxSlideShowController.is() )
	{
        ElementContainer::iterator iPart;
        ElementContainer::const_iterator iEnd (maElementContainer.end());
        for (iPart=maElementContainer.begin(); iPart!=iEnd; ++iPart)
        {
            ElementContainerPart::iterator iElement;
            ElementContainerPart::const_iterator iPartEnd ((*iPart)->end());
            for (iElement=(*iPart)->begin(); iElement!=iPartEnd; ++iElement)
            {
                if (iElement->get() != NULL)
                    (*iElement)->CurrentSlideHasChanged();
            }
        }
	}
}




void PresenterToolBar::CheckMouseOver (
    const css::awt::MouseEvent& rEvent,
    const bool bOverWindow,
    const bool bMouseDown)
{
    ElementContainer::iterator iPart;
    ElementContainer::const_iterator iEnd (maElementContainer.end());
    for (iPart=maElementContainer.begin(); iPart!=iEnd; ++iPart)
    {
        ElementContainerPart::iterator iElement;
        ElementContainerPart::const_iterator iPartEnd ((*iPart)->end());
        for (iElement=(*iPart)->begin(); iElement!=iPartEnd; ++iElement)
        {
            if (iElement->get() == NULL)
                continue;
        
            awt::Rectangle aBox ((*iElement)->GetBoundingBox());
            const bool bIsOver = bOverWindow
                && aBox.X <= rEvent.X
                && aBox.Width+aBox.X-1 >= rEvent.X
                && aBox.Y <= rEvent.Y
                && aBox.Height+aBox.Y-1 >= rEvent.Y;
            (*iElement)->SetState(
                bIsOver,
                bIsOver && rEvent.Buttons!=0 && bMouseDown && rEvent.ClickCount>0);
        }
    }
}




void PresenterToolBar::ThrowIfDisposed (void) const
    throw (::com::sun::star::lang::DisposedException)
{
	if (rBHelper.bDisposed || rBHelper.bInDispose)
	{
        throw lang::DisposedException (
            OUString(RTL_CONSTASCII_USTRINGPARAM(
                "PresenterToolBar has already been disposed")),
            const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
    }
}




//===== PresenterToolBarView ==================================================

PresenterToolBarView::PresenterToolBarView (
    const Reference<XComponentContext>& rxContext,
    const Reference<XResourceId>& rxViewId,
    const Reference<frame::XController>& rxController,
    const ::rtl::Reference<PresenterController>& rpPresenterController)
    : PresenterToolBarViewInterfaceBase(m_aMutex),
      mxPane(),
      mxViewId(rxViewId),
      mxWindow(),
      mxCanvas(),
      mpPresenterController(rpPresenterController),
      mxSlideShowController(rpPresenterController->GetSlideShowController()),
      mpToolBar()
{
    try
    {
        Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
        Reference<XConfigurationController> xCC(xCM->getConfigurationController(),UNO_QUERY_THROW);
        mxPane = Reference<XPane>(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);

        mxWindow = mxPane->getWindow();
        mxCanvas = mxPane->getCanvas();

        mpToolBar = new PresenterToolBar(
            rxContext,
            mxWindow,
            mxCanvas,
            rpPresenterController,
            PresenterToolBar::Center);
        mpToolBar->Initialize(A2S("PresenterScreenSettings/ToolBars/ToolBar"));

        if (mxWindow.is())
        {
            mxWindow->addPaintListener(this);

            Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
            if (xPeer.is())
                xPeer->setBackground(util::Color(0xff000000));
            
            mxWindow->setVisible(sal_True);
        }
    }
    catch (RuntimeException&)
    {
        mxViewId = NULL;
        throw;
    }
}




PresenterToolBarView::~PresenterToolBarView (void)
{
}




void SAL_CALL PresenterToolBarView::disposing (void)
{
    Reference<lang::XComponent> xComponent (static_cast<XWeak*>(mpToolBar.get()), UNO_QUERY);
    mpToolBar = NULL;
    if (xComponent.is())
        xComponent->dispose();
    
    if (mxWindow.is())
    {
        mxWindow->removePaintListener(this);
        mxWindow = NULL;
    }
    mxCanvas = NULL;
    mxViewId = NULL;
    mxPane = NULL;
    mpPresenterController = NULL;
    mxSlideShowController = NULL;

}




::rtl::Reference<PresenterToolBar> PresenterToolBarView::GetPresenterToolBar (void) const
{
    return mpToolBar;
}




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

void SAL_CALL PresenterToolBarView::windowPaint (const css::awt::PaintEvent& rEvent)
    throw (RuntimeException)
{
    awt::Rectangle aWindowBox (mxWindow->getPosSize());
    mpPresenterController->GetCanvasHelper()->Paint(
        mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
        mxCanvas,
        rEvent.UpdateRect,
        awt::Rectangle(0,0,aWindowBox.Width, aWindowBox.Height),
        awt::Rectangle());
}




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

void SAL_CALL PresenterToolBarView::disposing (const lang::EventObject& rEventObject)
    throw (RuntimeException)
{
    if (rEventObject.Source == mxWindow)
        mxWindow = NULL;
}




//----- XResourceId -----------------------------------------------------------

Reference<XResourceId> SAL_CALL PresenterToolBarView::getResourceId (void)
    throw (RuntimeException)
{
    return mxViewId;
}




sal_Bool SAL_CALL PresenterToolBarView::isAnchorOnly (void)
    throw (RuntimeException)
{
    return false;
}




//----- XDrawView -------------------------------------------------------------

void SAL_CALL PresenterToolBarView::setCurrentPage (const Reference<drawing::XDrawPage>& rxSlide)
    throw (RuntimeException)
{
    Reference<drawing::XDrawView> xToolBar (static_cast<XWeak*>(mpToolBar.get()), UNO_QUERY);
    if (xToolBar.is())
        xToolBar->setCurrentPage(rxSlide);
}




Reference<drawing::XDrawPage> SAL_CALL PresenterToolBarView::getCurrentPage (void)
    throw (RuntimeException)
{
    return NULL;
}




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

void PresenterToolBarView::ThrowIfDisposed (void) const
    throw (::com::sun::star::lang::DisposedException)
{
	if (rBHelper.bDisposed || rBHelper.bInDispose)
	{
        throw lang::DisposedException (
            OUString(RTL_CONSTASCII_USTRINGPARAM(
                "PresenterToolBarView has already been disposed")),
            const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
    }
}




//===== PresenterToolBar::Element =============================================

namespace {

Element::Element (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : ElementInterfaceBase(m_aMutex),
      mpToolBar(rpToolBar),
      maLocation(),
      maSize(),
      mpNormal(),
      mpMouseOver(),
      mpSelected(),
      mpDisabled(),
      mpMode(),
      mbIsOver(false),
      mbIsPressed(false),
      mbIsSelected(false),
      mbIsEnabled(true)
{
    if (mpToolBar.get() != NULL)
    {
        OSL_ASSERT(mpToolBar->GetPresenterController().is());
        OSL_ASSERT(mpToolBar->GetPresenterController()->GetWindowManager().is());
    }
}




Element::~Element (void)
{
}




void Element::SetModes (
    const SharedElementMode& rpNormalMode,
    const SharedElementMode& rpMouseOverMode,
    const SharedElementMode& rpSelectedMode,
    const SharedElementMode& rpDisabledMode)
{
    mpNormal = rpNormalMode;
    mpMouseOver = rpMouseOverMode;
    mpSelected = rpSelectedMode;
    mpDisabled = rpDisabledMode;
    mpMode = rpNormalMode;
}




void Element::disposing (void)
{
}




awt::Size Element::GetBoundingSize (
    const Reference<rendering::XCanvas>& rxCanvas)
{
    maSize = CreateBoundingSize(rxCanvas);
    return maSize;
}




awt::Rectangle Element::GetBoundingBox (void) const
{
    return awt::Rectangle(maLocation.X,maLocation.Y, maSize.Width, maSize.Height);
}




void Element::CurrentSlideHasChanged (void)
{
    UpdateState();
}




void Element::SetLocation (const awt::Point& rLocation)
{
    maLocation = rLocation;
}



void Element::SetSize (const geometry::RealSize2D& rSize)
{
    maSize = awt::Size(sal_Int32(0.5+rSize.Width), sal_Int32(0.5+rSize.Height));
}




bool Element::SetState (
    const bool bIsOver,
    const bool bIsPressed)
{
    bool bModified (mbIsOver != bIsOver || mbIsPressed != bIsPressed);
    bool bClicked (mbIsPressed && bIsOver && ! bIsPressed);
    
    mbIsOver = bIsOver;
    mbIsPressed = bIsPressed;

    // When the element is disabled then ignore mouse over or selection.
    // When the element is selected then ignore mouse over.
    if ( ! mbIsEnabled)
        mpMode = mpDisabled;
    else if (mbIsSelected)
        mpMode = mpSelected;
    else if (mbIsOver)
        mpMode = mpMouseOver;
    else
        mpMode = mpNormal;

    if (bClicked && mbIsEnabled)
    {
        if (mpMode.get() != NULL)
        {
            do
            {
                if (mpMode->msAction.getLength() <= 0)
                    break;
                
                if (mpToolBar.get() == NULL)
                    break;

                if (mpToolBar->GetPresenterController().get() == NULL)
                    break;

                mpToolBar->GetPresenterController()->DispatchUnoCommand(mpMode->msAction);
                mpToolBar->RequestLayout();
            }
            while (false);
        }

    }
    else if (bModified)
    {
        Invalidate();
    }

    return bModified;
}




void Element::Invalidate (const bool bSynchronous)
{
    OSL_ASSERT(mpToolBar.is());
    mpToolBar->InvalidateArea(GetBoundingBox(), bSynchronous);
}




bool Element::IsOutside (const awt::Rectangle& rBox)
{
    if (rBox.X >= maLocation.X+maSize.Width)
        return true;
    else if (rBox.Y >= maLocation.Y+maSize.Height)
        return true;
    else if (maLocation.X >= rBox.X+rBox.Width)
        return true;
    else if (maLocation.Y >= rBox.Y+rBox.Height)
        return true;
    else
        return false;
}



bool Element::IsEnabled (void) const
{
    return mbIsEnabled;
}




void Element::SetEnabledState (const bool bIsEnabled)
{
    mbIsEnabled = bIsEnabled;
}




bool Element::IsFilling (void) const
{
    return false;
}




void Element::UpdateState (void)
{
    OSL_ASSERT(mpToolBar.get() != NULL);
    OSL_ASSERT(mpToolBar->GetPresenterController().get() != NULL);

    if (mpMode.get() == NULL)
        return;

    util::URL aURL (mpToolBar->GetPresenterController()->CreateURLFromString(mpMode->msAction));
    Reference<frame::XDispatch> xDispatch (mpToolBar->GetPresenterController()->GetDispatch(aURL));
    if (xDispatch.is())
    {
        xDispatch->addStatusListener(this, aURL);
        xDispatch->removeStatusListener(this, aURL);
    }
}




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

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



//----- document::XEventListener ----------------------------------------------

void SAL_CALL Element::notifyEvent (const css::document::EventObject& rEvent)
    throw(css::uno::RuntimeException)
{
    (void)rEvent;
    UpdateState();
}




//----- frame::XStatusListener ------------------------------------------------

void SAL_CALL Element::statusChanged (const css::frame::FeatureStateEvent& rEvent)
    throw(css::uno::RuntimeException)
{
    bool bIsSelected (mbIsSelected);
    bool bIsEnabled (rEvent.IsEnabled);
    rEvent.State >>= bIsSelected;
    
    if (bIsSelected != mbIsSelected || bIsEnabled != mbIsEnabled)
    {
        mbIsEnabled = bIsEnabled;
        mbIsSelected = bIsSelected;
        SetState(mbIsOver, mbIsPressed);
        mpToolBar->RequestLayout();
    }
}

} // end of anonymous namespace




//===== ElementMode ===========================================================

namespace {

ElementMode::ElementMode (void)
    : mpIcon(),
      msAction(),
      maText()
{
}




void ElementMode::ReadElementMode (
    const Reference<beans::XPropertySet>& rxElementProperties,
    const OUString& rsModeName,
    ::boost::shared_ptr<ElementMode>& rpDefaultMode,
    ::sdext::presenter::PresenterToolBar::Context& rContext)
{
    try
    {
    Reference<container::XHierarchicalNameAccess> xNode (
        PresenterConfigurationAccess::GetProperty(rxElementProperties, rsModeName),
        UNO_QUERY);
    Reference<beans::XPropertySet> xProperties (
        PresenterConfigurationAccess::GetNodeProperties(xNode, OUString()));
    if ( ! xProperties.is() && rpDefaultMode.get()!=NULL)
    {
        // The mode is not specified.  Use the given, possibly empty,
        // default mode instead.
        mpIcon = rpDefaultMode->mpIcon;
        msAction = rpDefaultMode->msAction;
        maText = rpDefaultMode->maText;
    }

    // Read action.
    if ( ! (PresenterConfigurationAccess::GetProperty(xProperties, A2S("Action")) >>= msAction))
        if (rpDefaultMode.get()!=NULL)
            msAction = rpDefaultMode->msAction;

    // Read text and font
    OUString sText (rpDefaultMode.get()!=NULL ? rpDefaultMode->maText.GetText() : OUString());
    PresenterConfigurationAccess::GetProperty(xProperties, A2S("Text")) >>= sText;
    Reference<container::XHierarchicalNameAccess> xFontNode (
        PresenterConfigurationAccess::GetProperty(xProperties, A2S("Font")), UNO_QUERY);
    PresenterTheme::SharedFontDescriptor pFont (PresenterTheme::ReadFont(
        xFontNode,
        A2S(""),
        rpDefaultMode.get()!=NULL
            ? rpDefaultMode->maText.GetFont()
            : PresenterTheme::SharedFontDescriptor()));
    maText = Text(sText,pFont);

    // Read bitmaps to display as icons.
    Reference<container::XHierarchicalNameAccess> xIconNode (
        PresenterConfigurationAccess::GetProperty(xProperties, A2S("Icon")), UNO_QUERY);
    mpIcon = PresenterBitmapContainer::LoadBitmap(
        xIconNode,
        A2S(""),
        rContext.mxPresenterHelper,
        rContext.mxCanvas,
        rpDefaultMode.get()!=NULL ? rpDefaultMode->mpIcon : SharedBitmapDescriptor());
    }
    catch(Exception&)
    {
        OSL_ASSERT(false);
    }
}

} // end of anonymous namespace




//===== Button ================================================================

namespace {

::rtl::Reference<Element> Button::Create (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
{
    ::rtl::Reference<Button> pElement (new Button(rpToolBar));
    pElement->Initialize();
    return ::rtl::Reference<Element>(pElement.get());
}




Button::Button (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : Element(rpToolBar),
      mbIsListenerRegistered(false)
{
    OSL_ASSERT(mpToolBar.get() != NULL);
    OSL_ASSERT(mpToolBar->GetPresenterController().is());
    OSL_ASSERT(mpToolBar->GetPresenterController()->GetWindowManager().is());
}




Button::~Button (void)
{
}




void Button::Initialize (void)
{
    mpToolBar->GetPresenterController()->GetWindowManager()->AddLayoutListener(this);
    mbIsListenerRegistered = true;
}




void Button::disposing (void)
{
    OSL_ASSERT(mpToolBar.get() != NULL);
    if (mpToolBar.get() != NULL
        && mbIsListenerRegistered)
    {
        OSL_ASSERT(mpToolBar->GetPresenterController().is());
        OSL_ASSERT(mpToolBar->GetPresenterController()->GetWindowManager().is());

        mbIsListenerRegistered = false;
        mpToolBar->GetPresenterController()->GetWindowManager()->RemoveLayoutListener(this);
    }
    Element::disposing();
}




void Button::Paint (
    const Reference<rendering::XCanvas>& rxCanvas,
    const rendering::ViewState& rViewState)
{
    OSL_ASSERT(rxCanvas.is());

    if (mpMode.get() == NULL)
        return;
    
    if (mpMode->mpIcon.get() == NULL)
        return;

    geometry::RealRectangle2D aTextBBox (mpMode->maText.GetBoundingBox(rxCanvas));
    sal_Int32 nTextHeight (sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.Y2 - aTextBBox.Y1));
    
    PaintIcon(rxCanvas, nTextHeight, rViewState);
    awt::Point aOffset(0,0);
    if ( ! IsEnabled())
        if (mpMode->mpIcon.get() != NULL)
        {
            Reference<rendering::XBitmap> xBitmap (mpMode->mpIcon->GetNormalBitmap());
            if (xBitmap.is())
                aOffset.Y = xBitmap->getSize().Height;
        }
    mpMode->maText.Paint(rxCanvas, rViewState, GetBoundingBox(), aOffset);
}




awt::Size Button::CreateBoundingSize (
    const Reference<rendering::XCanvas>& rxCanvas)
{
    if (mpMode.get() == NULL)
        return awt::Size();

    geometry::RealRectangle2D aTextBBox (mpMode->maText.GetBoundingBox(rxCanvas));
    const sal_Int32 nGap (5);
    sal_Int32 nTextHeight (sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.Y2 - aTextBBox.Y1));
    sal_Int32 nTextWidth (sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.X2 - aTextBBox.X1));
    Reference<rendering::XBitmap> xBitmap;
    if (mpMode->mpIcon.get() != NULL)
        xBitmap = mpMode->mpIcon->GetNormalBitmap();
    if (xBitmap.is())
    {
        geometry::IntegerSize2D aSize (xBitmap->getSize());
        return awt::Size(
            ::std::max(aSize.Width, sal_Int32(0.5 + aTextBBox.X2 - aTextBBox.X1)),
            aSize.Height+ nGap + nTextHeight);
    }
    else
        return awt::Size(nTextWidth,nTextHeight);
}




void Button::PaintIcon (
    const Reference<rendering::XCanvas>& rxCanvas,
    const sal_Int32 nTextHeight,
    const rendering::ViewState& rViewState)
{
    if (mpMode.get() == NULL)
        return;
    
    Reference<rendering::XBitmap> xBitmap (mpMode->mpIcon->GetBitmap(GetMode()));
    if (xBitmap.is())
    {
        const sal_Int32 nX (maLocation.X
            + (maSize.Width-xBitmap->getSize().Width) / 2);
        const sal_Int32 nY (maLocation.Y
            + (maSize.Height - nTextHeight - xBitmap->getSize().Height) / 2);
        const rendering::RenderState aRenderState(
            geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
            NULL,
            Sequence<double>(4),
            rendering::CompositeOperation::OVER);
        rxCanvas->drawBitmap(xBitmap, rViewState, aRenderState);
    }
}




PresenterBitmapDescriptor::Mode Button::GetMode (void) const
{
    if ( ! IsEnabled())
        return PresenterBitmapDescriptor::Disabled;
    else if (mbIsPressed)
        return PresenterBitmapDescriptor::ButtonDown;
    else if (mbIsOver)
        return PresenterBitmapDescriptor::MouseOver;
    else
        return PresenterBitmapDescriptor::Normal;
}




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

void SAL_CALL Button::disposing (const css::lang::EventObject& rEvent)
    throw(css::uno::RuntimeException)
{
    (void)rEvent;
    mbIsListenerRegistered = false;
    Element::disposing(rEvent);
}
    
} // end of anonymous namespace




//===== PresenterToolBar::Label ===============================================

namespace {

Label::Label (const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : Element(rpToolBar)
{
}




awt::Size Label::CreateBoundingSize (
    const Reference<rendering::XCanvas>& rxCanvas)
{
    if (mpMode.get() == NULL)
        return awt::Size(0,0);
    
    geometry::RealRectangle2D aTextBBox (mpMode->maText.GetBoundingBox(rxCanvas));
    return awt::Size(
        sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.X2 - aTextBBox.X1),
        sal::static_int_cast<sal_Int32>(0.5 + aTextBBox.Y2 - aTextBBox.Y1));
}
 




void Label::SetText (const OUString& rsText)
{
    OSL_ASSERT(mpToolBar.get() != NULL);
    if (mpMode.get() == NULL)
        return;

    const bool bRequestLayout (mpMode->maText.GetText().getLength() != rsText.getLength());

    mpMode->maText.SetText(rsText);
    // Just use the character count for determing whether a layout is
    // necessary.  This is an optimization to avoid layouts every time a new
    // time value is set on some labels.
    if (bRequestLayout)
        mpToolBar->RequestLayout();
    else
        Invalidate(false);
}




void Label::Paint (
    const Reference<rendering::XCanvas>& rxCanvas,
    const rendering::ViewState& rViewState)
{
    OSL_ASSERT(rxCanvas.is());
    if (mpMode.get() == NULL)
        return;
    
    mpMode->maText.Paint(rxCanvas, rViewState, GetBoundingBox(), awt::Point(0,0));
}




bool Label::SetState (const bool bIsOver, const bool bIsPressed)
{
    // For labels there is no mouse over effect.
    (void)bIsOver;
    (void)bIsPressed;
    return Element::SetState(false, false);
}

} // end of anonymous namespace




//===== Text ==================================================================

namespace {

Text::Text (void)
    : msText(),
      mpFont()
{
}




Text::Text (const Text& rText)
    : msText(rText.msText),
      mpFont(rText.mpFont)
{
}




Text::Text (
    const OUString& rsText,
    const PresenterTheme::SharedFontDescriptor& rpFont)
    : msText(rsText),
      mpFont(rpFont)
{
}




void Text::SetText (const OUString& rsText)
{
    msText = rsText;
}




OUString Text::GetText (void) const
{
    return msText;
}




PresenterTheme::SharedFontDescriptor Text::GetFont (void) const
{
    return mpFont;
}




void Text::Paint (
    const Reference<rendering::XCanvas>& rxCanvas,
    const rendering::ViewState& rViewState,
    const awt::Rectangle& rBoundingBox,
    const awt::Point& rOffset)
{
    (void)rOffset;
    OSL_ASSERT(rxCanvas.is());

    if (msText.getLength() <= 0)
        return;
    if (mpFont.get() == NULL)
        return;

    if ( ! mpFont->mxFont.is())
        mpFont->PrepareFont(rxCanvas);
    if ( ! mpFont->mxFont.is())
        return;

    rendering::StringContext aContext (msText, 0, msText.getLength());

    Reference<rendering::XTextLayout> xLayout (
        mpFont->mxFont->createTextLayout(
            aContext,
            rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
            0));

    geometry::RealRectangle2D aBox (xLayout->queryTextBounds());
    const double nTextWidth = aBox.X2 - aBox.X1;
    const double nY = rBoundingBox.Y + rBoundingBox.Height - aBox.Y2;
    const double nX = rBoundingBox.X + (rBoundingBox.Width - nTextWidth)/2;

    rendering::RenderState aRenderState(
        geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
        NULL,
        Sequence<double>(4),
        rendering::CompositeOperation::SOURCE);
    PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
    
    rxCanvas->drawText(
        aContext,
        mpFont->mxFont,
        rViewState,
        aRenderState,
        rendering::TextDirection::WEAK_LEFT_TO_RIGHT);
}




geometry::RealRectangle2D Text::GetBoundingBox (const Reference<rendering::XCanvas>& rxCanvas)
{
    if (mpFont.get() != NULL && msText.getLength() > 0)
    {
        if ( ! mpFont->mxFont.is())
            mpFont->PrepareFont(rxCanvas);
        if (mpFont->mxFont.is())
        {
            rendering::StringContext aContext (msText, 0, msText.getLength());
            Reference<rendering::XTextLayout> xLayout (
                mpFont->mxFont->createTextLayout(
                    aContext,
                    rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
                    0));
            return xLayout->queryTextBounds();
        }
    }
    return geometry::RealRectangle2D(0,0,0,0);
}




//===== ProgressLabel =========================================================

ProgressLabel::ProgressLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : Label(rpToolBar)
{
    SetText(A2S("-/-"));
}




void ProgressLabel::CurrentSlideHasChanged (void)
{
    Label::CurrentSlideHasChanged();
    OSL_ASSERT(mpToolBar.is());
    try
    {
        const sal_Int32 nCurrentSlideIndex (mpToolBar->GetCurrentSlideIndex() + 1);
        const sal_Int32 nSlideCount (mpToolBar->GetSlideCount());
        if (nCurrentSlideIndex >= 0 && nSlideCount > 0)
            SetText(
                OUString::valueOf(nCurrentSlideIndex)
                    + OUString::createFromAscii(" / ")
                        + OUString::valueOf(nSlideCount));
        else
            SetText(A2S(""));
        Invalidate();
    }
    catch (RuntimeException&)
    {
    }
}




//===== TimeFormatter =========================================================

TimeFormatter::TimeFormatter (void)
    : mbIs24HourFormat(true),
      mbIsAmPmFormat(false),
      mbIsShowSeconds(true)
{
}




OUString TimeFormatter::FormatTime (const oslDateTime& rTime)
{
    ::rtl::OUStringBuffer sText;

    const sal_Int32 nHours (sal::static_int_cast<sal_Int32>(rTime.Hours));
    const sal_Int32 nMinutes (sal::static_int_cast<sal_Int32>(rTime.Minutes));
    const sal_Int32 nSeconds(sal::static_int_cast<sal_Int32>(rTime.Seconds));
    
    // Hours
    if (mbIs24HourFormat)
        sText.append(OUString::valueOf(nHours));
    else
        sText.append(OUString::valueOf(
            sal::static_int_cast<sal_Int32>(nHours>12 ? nHours-12 : nHours)));

    sText.append(A2S(":"));

    // Minutes
    const OUString sMinutes (OUString::valueOf(nMinutes));
    if (sMinutes.getLength() == 1)
        sText.append(A2S("0"));
    sText.append(sMinutes);

    // Seconds
    if (mbIsShowSeconds)
    {
        sText.append(A2S(":"));
        const OUString sSeconds (OUString::valueOf(nSeconds));
        if (sSeconds.getLength() == 1)
            sText.append(A2S("0"));
        sText.append(sSeconds);
    }

    if (mbIsAmPmFormat)
    {
        if (rTime.Hours < 12)
            sText.append(A2S("am"));
        else
            sText.append(A2S("pm"));
    }
    return sText.makeStringAndClear();
}




//===== TimeLabel =============================================================

TimeLabel::TimeLabel (const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : Label(rpToolBar),
      mpListener()
{
}




void SAL_CALL TimeLabel::disposing (void)
{
    PresenterClockTimer::Instance(mpToolBar->GetComponentContext())->RemoveListener(mpListener);
    mpListener.reset();
}




void TimeLabel::ConnectToTimer (void)
{
    mpListener.reset(new Listener(this));
    PresenterClockTimer::Instance(mpToolBar->GetComponentContext())->AddListener(mpListener);
}




//===== CurrentTimeLabel ======================================================

::rtl::Reference<Element> CurrentTimeLabel::Create (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
{
    ::rtl::Reference<TimeLabel> pElement(new CurrentTimeLabel(rpToolBar));
    pElement->ConnectToTimer();
    return ::rtl::Reference<Element>(pElement.get());
}




CurrentTimeLabel::~CurrentTimeLabel (void)
{
}




CurrentTimeLabel::CurrentTimeLabel (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : TimeLabel(rpToolBar),
      maTimeFormatter()
{
}




void CurrentTimeLabel::TimeHasChanged (const oslDateTime& rCurrentTime)
{
    SetText(maTimeFormatter.FormatTime(rCurrentTime));
    Invalidate(false);
}




void CurrentTimeLabel::SetModes (
    const SharedElementMode& rpNormalMode,
    const SharedElementMode& rpMouseOverMode,
    const SharedElementMode& rpSelectedMode,
    const SharedElementMode& rpDisabledMode)
{
    TimeLabel::SetModes(rpNormalMode, rpMouseOverMode, rpSelectedMode, rpDisabledMode);
    SetText(maTimeFormatter.FormatTime(PresenterClockTimer::GetCurrentTime()));
}




//===== PresentationTimeLabel =================================================

::rtl::Reference<Element> PresentationTimeLabel::Create (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
{
    ::rtl::Reference<TimeLabel> pElement(new PresentationTimeLabel(rpToolBar));
    pElement->ConnectToTimer();
    return ::rtl::Reference<Element>(pElement.get());
}




PresentationTimeLabel::~PresentationTimeLabel (void)
{
}




PresentationTimeLabel::PresentationTimeLabel (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : TimeLabel(rpToolBar),
      maTimeFormatter(),
      maStartTimeValue()
{
    maStartTimeValue.Seconds = 0;
    maStartTimeValue.Nanosec = 0;
}




void PresentationTimeLabel::TimeHasChanged (const oslDateTime& rCurrentTime)
{
    TimeValue aCurrentTimeValue;
    if (osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rCurrentTime), &aCurrentTimeValue))
    {
        if (maStartTimeValue.Seconds==0 && maStartTimeValue.Nanosec==0)
        {
            // This method is called for the first time.  Initialize the
            // start time.  The start time is rounded to nearest second to
            // keep the time updates synchronized with the current time label.
            maStartTimeValue = aCurrentTimeValue;
            if (maStartTimeValue.Nanosec >= 500000000)
                maStartTimeValue.Seconds += 1;
            maStartTimeValue.Nanosec = 0;
        }
        
        TimeValue aElapsedTimeValue;
        aElapsedTimeValue.Seconds = aCurrentTimeValue.Seconds - maStartTimeValue.Seconds;
        aElapsedTimeValue.Nanosec = aCurrentTimeValue.Nanosec - maStartTimeValue.Nanosec;

        oslDateTime aElapsedDateTime;
        if (osl_getDateTimeFromTimeValue(&aElapsedTimeValue, &aElapsedDateTime))
        {
            SetText(maTimeFormatter.FormatTime(aElapsedDateTime));
            Invalidate(false);
        }
    }
}



void PresentationTimeLabel::SetModes (
    const SharedElementMode& rpNormalMode,
    const SharedElementMode& rpMouseOverMode,
    const SharedElementMode& rpSelectedMode,
    const SharedElementMode& rpDisabledMode)
{
    TimeLabel::SetModes(rpNormalMode, rpMouseOverMode, rpSelectedMode, rpDisabledMode);

    oslDateTime aStartDateTime;
    if (osl_getDateTimeFromTimeValue(&maStartTimeValue, &aStartDateTime))
    {
        SetText(maTimeFormatter.FormatTime(aStartDateTime));
    }
}




//===== VerticalSeparator =====================================================

VerticalSeparator::VerticalSeparator (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : Element(rpToolBar)
{
}




void VerticalSeparator::Paint (
    const Reference<rendering::XCanvas>& rxCanvas,
    const rendering::ViewState& rViewState)
{
    OSL_ASSERT(rxCanvas.is());

    awt::Rectangle aBBox (GetBoundingBox());

    rendering::RenderState aRenderState(
        geometry::AffineMatrix2D(1,0,0, 0,1,0),
        NULL,
        Sequence<double>(4),
        rendering::CompositeOperation::OVER);
    if (mpMode.get() != NULL)
    {
        PresenterTheme::SharedFontDescriptor pFont (mpMode->maText.GetFont());
        if (pFont.get() != NULL)
            PresenterCanvasHelper::SetDeviceColor(aRenderState, pFont->mnColor);
    }

    if (aBBox.Height >= gnMinimalSeparatorSize + 2*gnSeparatorInset)
    {
        aBBox.Height -= 2*gnSeparatorInset;
        aBBox.Y += gnSeparatorInset;
    }
    rxCanvas->fillPolyPolygon(
        PresenterGeometryHelper::CreatePolygon(aBBox, rxCanvas->getDevice()),
        rViewState,
        aRenderState);
}




awt::Size VerticalSeparator::CreateBoundingSize (
    const Reference<rendering::XCanvas>& rxCanvas)
{
    (void)rxCanvas;
    return awt::Size(1,20);
}




bool VerticalSeparator::IsFilling (void) const
{
    return true;
}




//===== HorizontalSeparator ===================================================

HorizontalSeparator::HorizontalSeparator (
    const ::rtl::Reference<PresenterToolBar>& rpToolBar)
    : Element(rpToolBar)
{
}




void HorizontalSeparator::Paint (
    const Reference<rendering::XCanvas>& rxCanvas,
    const rendering::ViewState& rViewState)
{
    OSL_ASSERT(rxCanvas.is());

    awt::Rectangle aBBox (GetBoundingBox());

    rendering::RenderState aRenderState(
        geometry::AffineMatrix2D(1,0,0, 0,1,0),
        NULL,
        Sequence<double>(4),
        rendering::CompositeOperation::OVER);
    if (mpMode.get() != NULL)
    {
        PresenterTheme::SharedFontDescriptor pFont (mpMode->maText.GetFont());
        if (pFont.get() != NULL)
            PresenterCanvasHelper::SetDeviceColor(aRenderState, pFont->mnColor);
    }

    if (aBBox.Width >= gnMinimalSeparatorSize+2*gnSeparatorInset)
    {
        aBBox.Width -= 2*gnSeparatorInset;
        aBBox.X += gnSeparatorInset;
    }
    rxCanvas->fillPolyPolygon(
        PresenterGeometryHelper::CreatePolygon(aBBox, rxCanvas->getDevice()),
        rViewState,
        aRenderState);
}




awt::Size HorizontalSeparator::CreateBoundingSize (
    const Reference<rendering::XCanvas>& rxCanvas)
{
    (void)rxCanvas;
    return awt::Size(20,1);
}




bool HorizontalSeparator::IsFilling (void) const
{
    return true;
}




} // end of anonymous namespace


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