/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

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

#include "PresenterController.hxx"

#include "PresenterAccessibility.hxx"
#include "PresenterAnimator.hxx"
#include "PresenterCanvasHelper.hxx"
#include "PresenterCurrentSlideObserver.hxx"
#include "PresenterFrameworkObserver.hxx"
#include "PresenterHelper.hxx"
#include "PresenterNotesView.hxx"
#include "PresenterPaintManager.hxx"
#include "PresenterPaneAnimator.hxx"
#include "PresenterPaneBase.hxx"
#include "PresenterPaneContainer.hxx"
#include "PresenterPaneBorderPainter.hxx"
#include "PresenterTheme.hxx"
#include "PresenterViewFactory.hxx"
#include "PresenterWindowManager.hxx"

#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/awt/Key.hpp>
#include <com/sun/star/awt/KeyModifier.hpp>
#include <com/sun/star/awt/MouseButton.hpp>
#include <com/sun/star/awt/XWindowPeer.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/drawing/XDrawView.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/drawing/framework/ResourceActivationMode.hpp>
#include <com/sun/star/drawing/framework/ResourceId.hpp>
#include <com/sun/star/drawing/framework/XControllerManager.hpp>
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/presentation/XPresentation.hpp>
#include <com/sun/star/presentation/XPresentationSupplier.hpp>
#include <com/sun/star/rendering/CompositeOperation.hpp>
#include <com/sun/star/rendering/TextDirection.hpp>

#include <rtl/ustrbuf.hxx>
#include <boost/bind.hpp>

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

namespace {
    const sal_Int32 ResourceActivationEventType = 0;
    const sal_Int32 ResourceDeactivationEventType = 1;
    const sal_Int32 ConfigurationUpdateEndEventType = 2;
}


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


namespace sdext { namespace presenter {

PresenterController::InstanceContainer PresenterController::maInstances;

::rtl::Reference<PresenterController> PresenterController::Instance (
    const css::uno::Reference<css::frame::XFrame>& rxFrame)
{
    InstanceContainer::const_iterator iInstance (maInstances.find(rxFrame));
    if (iInstance != maInstances.end())
        return iInstance->second;
    else
        return ::rtl::Reference<PresenterController>();
}




PresenterController::PresenterController (
    const Reference<XComponentContext>& rxContext,
    const Reference<frame::XController>& rxController,
    const Reference<presentation::XSlideShowController>& rxSlideShowController,
    const rtl::Reference<PresenterPaneContainer>& rpPaneContainer,
    const Reference<XResourceId>& rxMainPaneId)
    : PresenterControllerInterfaceBase(m_aMutex),
      mxComponentContext(rxContext),
      mxController(rxController),
      mxConfigurationController(),
      mxSlideShowController(rxSlideShowController),
      mxMainPaneId(rxMainPaneId),
      mpPaneContainer(rpPaneContainer),
      mnCurrentSlideIndex(-1),
      mxCurrentSlide(),
      mxNextSlide(),
      mpWindowManager(new PresenterWindowManager(rxContext,mpPaneContainer,this)),
      mpCurrentPaneAnimation(),
      mnWindowBackgroundColor(0x00ffffff),
      mpTheme(),
      mxMainWindow(),
      mpPaneBorderPainter(),
      mpAnimator(new PresenterAnimator()),
      mpCanvasHelper(new PresenterCanvasHelper()),
      mxPresenterHelper(),
      mpPaintManager(),
      mnPendingSlideNumber(-1),
      mxUrlTransformer(),
      mpAccessibleObject(),
      mbIsAccessibilityActive(false)
{
    OSL_ASSERT(mxController.is());

    if ( ! mxSlideShowController.is())
        throw new lang::IllegalArgumentException(
            A2S("missing slide show controller"),
            static_cast<XWeak*>(this),
            2);

    new PresenterCurrentSlideObserver(this,rxSlideShowController);

    // Listen for configuration changes.
    Reference<XControllerManager> xCM (mxController, UNO_QUERY_THROW);
    mxConfigurationController = xCM->getConfigurationController();
    if (mxConfigurationController.is())
    {
        mxConfigurationController->addConfigurationChangeListener(
            this,
            A2S("ResourceActivation"),
            Any(ResourceActivationEventType));
        mxConfigurationController->addConfigurationChangeListener(
            this,
            A2S("ResourceDeactivation"),
            Any(ResourceDeactivationEventType));
        mxConfigurationController->addConfigurationChangeListener(
            this,
            A2S("ConfigurationUpdateEnd"),
            Any(ConfigurationUpdateEndEventType));
    }

    // Listen for the frame being activated.
    Reference<frame::XFrame> xFrame (mxController->getFrame());
    if (xFrame.is())
        xFrame->addFrameActionListener(this);

    // Create the border painter.
    mpPaneBorderPainter = new PresenterPaneBorderPainter(rxContext);
    mpWindowManager->SetPaneBorderPainter(mpPaneBorderPainter);

    // Create an object that is able to load the bitmaps in a format that is
    // supported by the canvas.
    Reference<lang::XMultiComponentFactory> xFactory (
        rxContext->getServiceManager(), UNO_QUERY);
    if ( ! xFactory.is())
        return;
    mxPresenterHelper = Reference<drawing::XPresenterHelper>(
        xFactory->createInstanceWithContext(
            A2S("com.sun.star.drawing.PresenterHelper"),
            rxContext),
        UNO_QUERY_THROW);

    if (mxSlideShowController.is())
    {
        mxSlideShowController->activate();
        Reference<beans::XPropertySet> xProperties (mxSlideShowController, UNO_QUERY);
        if (xProperties.is())
        {
            Reference<awt::XWindow> xWindow (
                xProperties->getPropertyValue(A2S("ParentWindow")), UNO_QUERY);
            if (xWindow.is())
                xWindow->addKeyListener(this);
        }
    }

    UpdateCurrentSlide(0);

    maInstances[mxController->getFrame()] = this;

    // Create a URLTransformer.
    if (xFactory.is())
    {
        mxUrlTransformer = Reference<util::XURLTransformer>(
            xFactory->createInstanceWithContext(
                A2S("com.sun.star.util.URLTransformer"),
                mxComponentContext),
            UNO_QUERY);
    }
}




PresenterController::~PresenterController (void)
{
}




void PresenterController::disposing (void)
{
    maInstances.erase(mxController->getFrame());
 
    if (mxMainWindow.is())
    {
        mxMainWindow->removeKeyListener(this);
        mxMainWindow->removeFocusListener(this);
        mxMainWindow->removeMouseListener(this);
        mxMainWindow->removeMouseMotionListener(this);
        mxMainWindow = NULL;
    }
    if (mxConfigurationController.is())
        mxConfigurationController->removeConfigurationChangeListener(this);

    Reference<XComponent> xWindowManagerComponent (
        static_cast<XWeak*>(mpWindowManager.get()), UNO_QUERY);
    mpWindowManager = NULL;
    if (xWindowManagerComponent.is())
        xWindowManagerComponent->dispose();
    
    if (mxController.is())
    {
        Reference<frame::XFrame> xFrame (mxController->getFrame());
        if (xFrame.is())
            xFrame->removeFrameActionListener(this);
        mxController = NULL;
    }

    mxComponentContext = NULL;
    mxConfigurationController = NULL;
    mxSlideShowController = NULL;
    mxMainPaneId = NULL;
    mpPaneContainer = NULL;
    mnCurrentSlideIndex = -1;
    mxCurrentSlide = NULL;
    mxNextSlide = NULL;
    mpCurrentPaneAnimation.reset();
    mpTheme.reset();
    {
        Reference<lang::XComponent> xComponent (
            static_cast<XWeak*>(mpPaneBorderPainter.get()), UNO_QUERY);
        mpPaneBorderPainter = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }
    mpAnimator.reset();
    mpCanvasHelper.reset();
    {
        Reference<lang::XComponent> xComponent (mxPresenterHelper, UNO_QUERY);
        mxPresenterHelper = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }
    mpPaintManager.reset();
    mnPendingSlideNumber = -1;
    {
        Reference<lang::XComponent> xComponent (mxUrlTransformer, UNO_QUERY);
        mxUrlTransformer = NULL;
        if (xComponent.is())
            xComponent->dispose();
    }
}




void PresenterController::UpdateCurrentSlide (const sal_Int32 nOffset)
{
    GetSlides(nOffset);
    UpdatePaneTitles();
    UpdateViews();
    
    // Update the accessibility object.
    if (IsAccessibilityActive())
    {
        sal_Int32 nSlideCount (0);
        Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
        if (xIndexAccess.is())
            nSlideCount = xIndexAccess->getCount();
        mpAccessibleObject->NotifyCurrentSlideChange(mnCurrentSlideIndex, nSlideCount);
    }
}




void PresenterController::GetSlides (const sal_Int32 nOffset)
{
    if ( ! mxSlideShowController.is())
        return;

    // Get the current slide from the slide show controller.
    mxCurrentSlide = NULL;
    Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
    sal_Int32 nSlideIndex = -1;
    try
    {
        nSlideIndex = mxSlideShowController->getCurrentSlideIndex() + nOffset;
        if (mxSlideShowController->isPaused())
            nSlideIndex = -1;

        if (xIndexAccess.is() && nSlideIndex>=0)
        {
            if (nSlideIndex < xIndexAccess->getCount())
            {
                mnCurrentSlideIndex = nSlideIndex;
                mxCurrentSlide = Reference<drawing::XDrawPage>(
                    xIndexAccess->getByIndex(nSlideIndex), UNO_QUERY);
            }
        }
    }
    catch (RuntimeException&)
    {
    }

    // Get the next slide.
    mxNextSlide = NULL;
    try
    {
        const sal_Int32 nNextSlideIndex (mxSlideShowController->getNextSlideIndex()+nOffset);
        if (nNextSlideIndex >= 0)
        {
            if (xIndexAccess.is())
            {
                if (nNextSlideIndex < xIndexAccess->getCount())
                    mxNextSlide = Reference<drawing::XDrawPage>(
                        xIndexAccess->getByIndex(nNextSlideIndex), UNO_QUERY);
            }
        }
    }
    catch (RuntimeException&)
    {
    }
}




void PresenterController::UpdatePaneTitles (void)
{
    if ( ! mxSlideShowController.is())
        return;

    // Get placeholders and their values.
    const OUString sCurrentSlideNumberPlaceholder (A2S("CURRENT_SLIDE_NUMBER"));
    const OUString sCurrentSlideNamePlaceholder (A2S("CURRENT_SLIDE_NAME"));
    const OUString sSlideCountPlaceholder (A2S("SLIDE_COUNT"));

    // Get string for slide count.
    OUString sSlideCount (A2S("---"));
    Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
    if (xIndexAccess.is())
        sSlideCount = OUString::valueOf(xIndexAccess->getCount());

    // Get string for current slide index.
    OUString sCurrentSlideNumber (OUString::valueOf(mnCurrentSlideIndex + 1));

    // Get name of the current slide.
    OUString sCurrentSlideName;
    Reference<container::XNamed> xNamedSlide (mxCurrentSlide, UNO_QUERY);
    if (xNamedSlide.is())
        sCurrentSlideName = xNamedSlide->getName();
    Reference<beans::XPropertySet> xSlideProperties (mxCurrentSlide, UNO_QUERY);
    if (xSlideProperties.is())
    {
        try
        {
            OUString sName;
            if (xSlideProperties->getPropertyValue(A2S("LinkDisplayName")) >>= sName)
            {
                // Find out whether the name of the current slide has been
                // automatically created or has been set by the user.
                if (sName != sCurrentSlideName)
                    sCurrentSlideName = sName;
            }
        }
        catch (beans::UnknownPropertyException&)
        {
        }
    }

    // Replace the placeholders with their current values.
    PresenterPaneContainer::PaneList::const_iterator iPane;
    for (iPane=mpPaneContainer->maPanes.begin(); iPane!=mpPaneContainer->maPanes.end(); ++iPane)
    {
        OSL_ASSERT((*iPane).get() != NULL);
        
        OUString sTemplate (IsAccessibilityActive()
            ? (*iPane)->msAccessibleTitleTemplate
            : (*iPane)->msTitleTemplate);
        if (sTemplate.getLength() <= 0)
            continue;

        OUStringBuffer sResult;
        sResult.ensureCapacity(sTemplate.getLength());

        sal_Int32 nIndex (0);
        while (true)
        {
            sal_Int32 nStartIndex = sTemplate.indexOf('%', nIndex);
            if (nStartIndex < 0)
            {
                // Add the remaining part of the string.
                sResult.append(sTemplate.copy(nIndex, sTemplate.getLength()-nIndex));
                break;
            }
            else
            {
                // Add the part preceding the next %.
                sResult.append(sTemplate.copy(nIndex, nStartIndex-nIndex));

                // Get the placeholder
                ++nIndex;
                ++nStartIndex;
                const sal_Int32 nEndIndex (sTemplate.indexOf('%', nStartIndex+1));
                const OUString sPlaceholder (sTemplate.copy(nStartIndex, nEndIndex-nStartIndex));
                nIndex = nEndIndex+1;

                // Replace the placeholder with its current value.
                if (sPlaceholder == sCurrentSlideNumberPlaceholder)
                    sResult.append(sCurrentSlideNumber);
                else if (sPlaceholder == sCurrentSlideNamePlaceholder)
                    sResult.append(sCurrentSlideName);
                else if (sPlaceholder == sSlideCountPlaceholder)
                    sResult.append(sSlideCount);
            }
        }

        (*iPane)->msTitle = sResult.makeStringAndClear();
        if ((*iPane)->mxPane.is())
            (*iPane)->mxPane->SetTitle((*iPane)->msTitle);
    }
}




void PresenterController::UpdateViews (void)
{
    // Tell all views about the slides they should display.
    PresenterPaneContainer::PaneList::const_iterator iPane;
    for (iPane=mpPaneContainer->maPanes.begin(); iPane!=mpPaneContainer->maPanes.end(); ++iPane)
    {
        Reference<drawing::XDrawView> xDrawView ((*iPane)->mxView, UNO_QUERY);
        if (xDrawView.is())
            xDrawView->setCurrentPage(mxCurrentSlide);
    }
}




SharedBitmapDescriptor
    PresenterController::GetViewBackground (const ::rtl::OUString& rsViewURL) const
{
    if (mpTheme.get() != NULL)
    {
        const OUString sStyleName (mpTheme->GetStyleName(rsViewURL));
        return mpTheme->GetBitmap(sStyleName, A2S("Background"));
    }
    return SharedBitmapDescriptor();
}




PresenterTheme::SharedFontDescriptor
    PresenterController::GetViewFont (const ::rtl::OUString& rsViewURL) const
{
    if (mpTheme.get() != NULL)
    {
        const OUString sStyleName (mpTheme->GetStyleName(rsViewURL));
        return mpTheme->GetFont(sStyleName);
    }
    return PresenterTheme::SharedFontDescriptor();
}




::boost::shared_ptr<PresenterTheme> PresenterController::GetTheme (void) const
{
    return mpTheme;
}




::rtl::Reference<PresenterWindowManager> PresenterController::GetWindowManager (void) const
{
    return mpWindowManager;
}




Reference<presentation::XSlideShowController>
    PresenterController::GetSlideShowController(void) const
{
    return mxSlideShowController;
}




rtl::Reference<PresenterPaneContainer> PresenterController::GetPaneContainer (void) const
{
    return mpPaneContainer;
}




::rtl::Reference<PresenterPaneBorderPainter> PresenterController::GetPaneBorderPainter (void) const
{
    return mpPaneBorderPainter;
}




::boost::shared_ptr<PresenterAnimator> PresenterController::GetAnimator (void) const
{
    return mpAnimator;
}




::boost::shared_ptr<PresenterCanvasHelper> PresenterController::GetCanvasHelper (void) const
{
    return mpCanvasHelper;
}




Reference<drawing::XPresenterHelper> PresenterController::GetPresenterHelper (void) const
{
    return mxPresenterHelper;
}




::boost::shared_ptr<PresenterPaintManager> PresenterController::GetPaintManager (void) const
{
    return mpPaintManager;
}




void PresenterController::HideSlideSorter (void)
{
    if (mpCurrentPaneAnimation.get() != NULL)
    {
        mpCurrentPaneAnimation->HidePane();
        mpCurrentPaneAnimation.reset();
    }
}




void PresenterController::ShowView (const OUString& rsViewURL)
{
    PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
        mpPaneContainer->FindViewURL(rsViewURL));
    if (pDescriptor.get() != NULL)
    {
        pDescriptor->mbIsActive = true;
        mxConfigurationController->requestResourceActivation(
            pDescriptor->mxPaneId,
            ResourceActivationMode_ADD);
        mxConfigurationController->requestResourceActivation(
            ResourceId::createWithAnchor(
                mxComponentContext,
                rsViewURL,
                pDescriptor->mxPaneId),
            ResourceActivationMode_REPLACE);
    }
}




void PresenterController::HideView (const OUString& rsViewURL)
{
    PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
        mpPaneContainer->FindViewURL(rsViewURL));
    if (pDescriptor.get() != NULL)
    {
        mxConfigurationController->requestResourceDeactivation(
            ResourceId::createWithAnchor(
                mxComponentContext,
                rsViewURL,
                pDescriptor->mxPaneId));
    }
}




bool PresenterController::IsViewVisible (const OUString& rsViewURL) const
{
   PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
        mpPaneContainer->FindViewURL(rsViewURL));
    if (pDescriptor.get() != NULL)
    {
        return mxConfigurationController->getResource(
            ResourceId::createWithAnchor(
                mxComponentContext,
                rsViewURL,
                pDescriptor->mxPaneId)).is();
    }
    return false;
}




void PresenterController::DispatchUnoCommand (const OUString& rsCommand) const
{
    if ( ! mxUrlTransformer.is())
        return;

    util::URL aURL;
    aURL.Complete = rsCommand;
    mxUrlTransformer->parseStrict(aURL); 

    Reference<frame::XDispatch> xDispatch (GetDispatch(aURL));
    if ( ! xDispatch.is())
        return;

    xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>());
}




Reference<css::frame::XDispatch> PresenterController::GetDispatch (const util::URL& rURL) const
{
    if ( ! mxController.is())
        return NULL;
    
    Reference<frame::XDispatchProvider> xDispatchProvider (mxController->getFrame(), UNO_QUERY);
    if ( ! xDispatchProvider.is())
        return NULL;

    return xDispatchProvider->queryDispatch(
        rURL,
        OUString(),
        frame::FrameSearchFlag::SELF);
}




util::URL PresenterController::CreateURLFromString (const ::rtl::OUString& rsURL) const
{
    util::URL aURL;

    if (mxUrlTransformer.is())
    {
        aURL.Complete = rsURL;
        mxUrlTransformer->parseStrict(aURL);
    }

    return aURL;
}




Reference<drawing::framework::XConfigurationController>
    PresenterController::GetConfigurationController (void) const
{
    return mxConfigurationController;
}




Reference<drawing::XDrawPage> PresenterController::GetCurrentSlide (void) const
{
    return mxCurrentSlide;
}




::rtl::Reference<PresenterAccessible> PresenterController::GetAccessible (void) const
{
    return mpAccessibleObject;
}




void PresenterController::SetAccessibilityActiveState (const bool bIsActive)
{
    if ( mbIsAccessibilityActive != bIsActive)
    {
        mbIsAccessibilityActive = bIsActive;
        UpdatePaneTitles();
    }
}




bool PresenterController::IsAccessibilityActive (void) const
{
    return mbIsAccessibilityActive;
}




void PresenterController::HandleMouseClick (const awt::MouseEvent& rEvent)
{
    if (mxSlideShowController.is())
    {
        switch (rEvent.Buttons)
        {
            case awt::MouseButton::LEFT:
                if (rEvent.Modifiers == awt::KeyModifier::MOD2)
                    mxSlideShowController->gotoNextSlide();
                else
                    mxSlideShowController->gotoNextEffect();
                break;

            case awt::MouseButton::RIGHT:
                mxSlideShowController->gotoPreviousSlide();
                break;

            default:
                // Other or multiple buttons.
                break;
        }
    }
}




void PresenterController::RequestViews (
    const bool bIsSlideSorterActive,
    const bool bIsNotesViewActive,
    const bool bIsHelpViewActive)
{
    PresenterPaneContainer::PaneList::const_iterator iPane;
    PresenterPaneContainer::PaneList::const_iterator iEnd (mpPaneContainer->maPanes.end());
    for (iPane=mpPaneContainer->maPanes.begin(); iPane!=iEnd; ++iPane)
    {
        bool bActivate (true);
        const OUString sViewURL ((*iPane)->msViewURL);
        if (sViewURL == PresenterViewFactory::msNotesViewURL)
        {
            bActivate = bIsNotesViewActive && !bIsSlideSorterActive && !bIsHelpViewActive;
        }
        else if (sViewURL == PresenterViewFactory::msSlideSorterURL)
        {
            bActivate = bIsSlideSorterActive;
        }
        else if (sViewURL == PresenterViewFactory::msCurrentSlidePreviewViewURL
            || sViewURL == PresenterViewFactory::msNextSlidePreviewViewURL)
        {
            bActivate = !bIsSlideSorterActive && ! bIsHelpViewActive;
        }
        else if (sViewURL == PresenterViewFactory::msToolBarViewURL)
        {
            bActivate = true;
        }
        else if (sViewURL == PresenterViewFactory::msHelpViewURL)
        {
            bActivate = bIsHelpViewActive;
        }

        if (bActivate)
            ShowView(sViewURL);
        else
            HideView(sViewURL);
    }
}




//----- XConfigurationChangeListener ------------------------------------------

void SAL_CALL PresenterController::notifyConfigurationChange (
    const ConfigurationChangeEvent& rEvent)
    throw (RuntimeException)
{
    ThrowIfDisposed();

    sal_Int32 nType (0);
    if ( ! (rEvent.UserData >>= nType))
        return;
    
    switch (nType)
    {
        case ResourceActivationEventType:
            if (rEvent.ResourceId->compareTo(mxMainPaneId) == 0)
            {
                InitializeMainPane(Reference<XPane>(rEvent.ResourceObject,UNO_QUERY));
            }
            else if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_DIRECT))
            {
                // A pane bound to the main pane has been created and is
                // stored in the pane container.
                Reference<XPane> xPane (rEvent.ResourceObject,UNO_QUERY);
                if (xPane.is())
                {
                    PresenterPaneContainer::SharedPaneDescriptor pDescriptor (
                        mpPaneContainer->FindPaneId(xPane->getResourceId()));
                                
                    // When there is a call out anchor location set then tell the
                    // window about it.
                    if (pDescriptor->mbHasCalloutAnchor)
                        pDescriptor->mxPane->SetCalloutAnchor(
                            pDescriptor->maCalloutAnchorLocation);
                }
            }
            else if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_INDIRECT))
            {
                // A view bound to one of the panes has been created and is
                // stored in the pane container along with its pane.
                Reference<XView> xView (rEvent.ResourceObject,UNO_QUERY);
                if (xView.is())
                {
                    SharedBitmapDescriptor pViewBackground(
                        GetViewBackground(xView->getResourceId()->getResourceURL()));
                    mpPaneContainer->StoreView(xView, pViewBackground);
                    UpdateViews();
                    mpWindowManager->NotifyViewCreation(xView);
                }
            }
            break;

        case ResourceDeactivationEventType:
            if (rEvent.ResourceId->isBoundTo(mxMainPaneId,AnchorBindingMode_INDIRECT))
            {
                // If this is a view then remove it from the pane container.
                Reference<XView> xView (rEvent.ResourceObject,UNO_QUERY);
                if (xView.is())
                {
                    PresenterPaneContainer::SharedPaneDescriptor pDescriptor(
                        mpPaneContainer->RemoveView(xView));

                    // A possibly opaque view has been removed.  Update()
                    // updates the clip polygon.
                    mpWindowManager->Update();
                    // Request the repainting of the area previously
                    // occupied by the view.
                    if (pDescriptor.get() != NULL)
                        GetPaintManager()->Invalidate(pDescriptor->mxBorderWindow);
                }
            }
            break;

        case ConfigurationUpdateEndEventType:
            if (IsAccessibilityActive())
            {
                mpAccessibleObject->UpdateAccessibilityHierarchy();
                UpdateCurrentSlide(0);
            }
            break;
    }
}




//----- XEventListener --------------------------------------------------------

void SAL_CALL PresenterController::disposing (
    const lang::EventObject& rEvent)
    throw (RuntimeException)
{
    if (rEvent.Source == mxController)
        mxController = NULL;
    else if (rEvent.Source == mxConfigurationController)
        mxConfigurationController = NULL;
    else if (rEvent.Source == mxSlideShowController)
        mxSlideShowController = NULL;
    else if (rEvent.Source == mxMainWindow)
        mxMainWindow = NULL;
}




//----- XFrameActionListener --------------------------------------------------

void SAL_CALL PresenterController::frameAction (
    const frame::FrameActionEvent& rEvent)
    throw (RuntimeException)
{
    if (rEvent.Action == frame::FrameAction_FRAME_ACTIVATED)
    {
        if (mxSlideShowController.is())
            mxSlideShowController->activate();
    }
}




//----- XKeyListener ----------------------------------------------------------

void SAL_CALL PresenterController::keyPressed (const awt::KeyEvent& rEvent)
    throw (RuntimeException)
{
    // Tell all views about the unhandled key event.
    PresenterPaneContainer::PaneList::const_iterator iPane;
    for (iPane=mpPaneContainer->maPanes.begin(); iPane!=mpPaneContainer->maPanes.end(); ++iPane)
    {
        if ( ! (*iPane)->mbIsActive)
            continue;

        Reference<awt::XKeyListener> xKeyListener ((*iPane)->mxView, UNO_QUERY);
        if (xKeyListener.is())
            xKeyListener->keyPressed(rEvent);
    }
}




void SAL_CALL PresenterController::keyReleased (const awt::KeyEvent& rEvent)
    throw (RuntimeException)
{
    if (rEvent.Source != mxMainWindow)
        return;

    switch (rEvent.KeyCode)
    {
        case awt::Key::ESCAPE:
        case awt::Key::SUBTRACT:
        {
			if( mxController.is() )
			{
				Reference< XPresentationSupplier > xPS( mxController->getModel(), UNO_QUERY );
				if( xPS.is() )
				{
					Reference< XPresentation > xP( xPS->getPresentation() );
					if( xP.is() )
						xP->end();
				}
			}
        }
        break;

        case awt::Key::PAGEDOWN:
            if (mxSlideShowController.is())
            {
                if (rEvent.Modifiers == awt::KeyModifier::MOD2)
                    mxSlideShowController->gotoNextSlide();
                else
                    mxSlideShowController->gotoNextEffect();
            }
            break;

        case awt::Key::RIGHT:
        case awt::Key::SPACE:
        case awt::Key::DOWN:
        case awt::Key::N:
            if (mxSlideShowController.is())
            {
                mxSlideShowController->gotoNextEffect();
            }
            break;

        case awt::Key::LEFT:
        case awt::Key::PAGEUP:
            if (mxSlideShowController.is())
            {
                if (rEvent.Modifiers == awt::KeyModifier::MOD2)
                    mxSlideShowController->gotoPreviousSlide();
                else
                    mxSlideShowController->gotoPreviousEffect();
            }
            break;

        case awt::Key::UP:
        case awt::Key::P:
        case awt::Key::BACKSPACE:
            if (mxSlideShowController.is())
            {
                mxSlideShowController->gotoPreviousEffect();
            }
            break;

        case awt::Key::HOME:
            if (mxSlideShowController.is())
            {
                mxSlideShowController->gotoFirstSlide();
            }
            break;

        case awt::Key::END:
            if (mxSlideShowController.is())
            {
                mxSlideShowController->gotoLastSlide();
            }
            break;

        case awt::Key::W:
        case awt::Key::COMMA:
            if (mxSlideShowController.is())
            {
                if (mxSlideShowController->isPaused())
                    mxSlideShowController->resume();
                else
                    mxSlideShowController->blankScreen(0x00ffffff);
            }
            break;

        case awt::Key::B:
        case awt::Key::POINT:
            if (mxSlideShowController.is())
            {
                if (mxSlideShowController->isPaused())
                    mxSlideShowController->resume();
                else
                    mxSlideShowController->blankScreen(0x00000000);
            }
            break;

        case awt::Key::NUM0:
        case awt::Key::NUM1:
        case awt::Key::NUM2:
        case awt::Key::NUM3:
        case awt::Key::NUM4:
        case awt::Key::NUM5:
        case awt::Key::NUM6:
        case awt::Key::NUM7:
        case awt::Key::NUM8:
        case awt::Key::NUM9:
            HandleNumericKeyPress(rEvent.KeyCode-awt::Key::NUM0, rEvent.Modifiers);
            break;

        case awt::Key::RETURN:
            if (mnPendingSlideNumber > 0)
            {
                if (mxSlideShowController.is())
                    mxSlideShowController->gotoSlideIndex(mnPendingSlideNumber - 1);
                mnPendingSlideNumber = -1;
            }
            else
            {
                if (mxSlideShowController.is())
                    mxSlideShowController->gotoNextEffect();
            }

            break;

        case awt::Key::F1:
            // Toggle the help view.
            if (mpWindowManager.get() != NULL)
            {
                if (mpWindowManager->GetViewMode() != PresenterWindowManager::VM_Help)
                    mpWindowManager->SetViewMode(PresenterWindowManager::VM_Help);
                else
                    mpWindowManager->SetHelpViewState(false);
            }

            break;

        default:
            // Tell all views about the unhandled key event.
            PresenterPaneContainer::PaneList::const_iterator iPane;
            for (iPane=mpPaneContainer->maPanes.begin(); iPane!=mpPaneContainer->maPanes.end(); ++iPane)
            {
                if ( ! (*iPane)->mbIsActive)
                    continue;

                Reference<awt::XKeyListener> xKeyListener ((*iPane)->mxView, UNO_QUERY);
                if (xKeyListener.is())
                    xKeyListener->keyReleased(rEvent);
            }
            break;
    }
}




void PresenterController::HandleNumericKeyPress (
    const sal_Int32 nKey,
    const sal_Int32 nModifiers)
{
    switch (nModifiers)
    {
        case 0:
            if (mnPendingSlideNumber == -1)
                mnPendingSlideNumber = 0;
            UpdatePendingSlideNumber(mnPendingSlideNumber * 10 + nKey);
            break;

        case awt::KeyModifier::MOD1:
            // Ctrl-1, Ctrl-2, and Ctrl-3 are used to switch between views
            // (slide view, notes view, normal)
            mnPendingSlideNumber = -1;
            if (mpWindowManager.get() == NULL)
                return;
            switch(nKey)
            {
                case 1:
                    mpWindowManager->SetViewMode(PresenterWindowManager::VM_Standard);
                    break;
                case 2:
                    mpWindowManager->SetViewMode(PresenterWindowManager::VM_Notes);
                    break;
                case 3:
                    mpWindowManager->SetViewMode(PresenterWindowManager::VM_SlideOverview);
                    break;
                default:
                    // Ignore unsupported key.
                    break;
            }

        default:
            // Ignore unsupported modifiers.
            break;
    }
}




//----- XFocusListener --------------------------------------------------------

void SAL_CALL PresenterController::focusGained (const css::awt::FocusEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    (void)rEvent;
}




void SAL_CALL PresenterController::focusLost (const css::awt::FocusEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    (void)rEvent;
}




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

void SAL_CALL PresenterController::mousePressed (const css::awt::MouseEvent& rEvent)
    throw (css::uno::RuntimeException)
{
    (void)rEvent;
    if (mxMainWindow.is())
        mxMainWindow->setFocus();
}




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



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



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




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




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




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

void PresenterController::InitializeMainPane (const Reference<XPane>& rxPane)
{
    if ( ! rxPane.is())
        return;

    mpAccessibleObject = new PresenterAccessible(
        mxComponentContext,
        this,
        rxPane);

    LoadTheme(rxPane);

    // Main pane has been created and is now observed by the window
    // manager.
    mpWindowManager->SetParentPane(rxPane);
    mpWindowManager->SetTheme(mpTheme);

    if (mpPaneBorderPainter.get() != NULL)
        mpPaneBorderPainter->SetTheme(mpTheme);

    // Add key listener
    mxMainWindow = rxPane->getWindow();
    if (mxMainWindow.is())
    {
        mxMainWindow->addKeyListener(this);
        mxMainWindow->addFocusListener(this);
        mxMainWindow->addMouseListener(this);
        mxMainWindow->addMouseMotionListener(this);
    }
    Reference<XPane2> xPane2 (rxPane, UNO_QUERY);
    if (xPane2.is())
        xPane2->setVisible(sal_True);

    mpPaintManager.reset(new PresenterPaintManager(mxMainWindow, mxPresenterHelper, mpPaneContainer));

    mxCanvas = Reference<rendering::XSpriteCanvas>(rxPane->getCanvas(), UNO_QUERY);

    if (mxSlideShowController.is())
        mxSlideShowController->activate();

    UpdateCurrentSlide(0);
}




void PresenterController::LoadTheme (const Reference<XPane>& rxPane)
{
    // Create (load) the current theme.
    if (rxPane.is())
        mpTheme.reset(new PresenterTheme(mxComponentContext, OUString(), rxPane->getCanvas()));
}




double PresenterController::GetSlideAspectRatio (void) const
{
    double nSlideAspectRatio (28.0/21.0);
    
    try
    {
        if (mxController.is())
        {
            Reference<drawing::XDrawPagesSupplier> xSlideSupplier (
                mxController->getModel(), UNO_QUERY_THROW);
            Reference<drawing::XDrawPages> xSlides (xSlideSupplier->getDrawPages());
            if (xSlides.is() && xSlides->getCount()>0)
            {
                Reference<beans::XPropertySet> xProperties(xSlides->getByIndex(0),UNO_QUERY_THROW);
                sal_Int32 nWidth (28000);
                sal_Int32 nHeight (21000);
                if ((xProperties->getPropertyValue(OUString::createFromAscii("Width")) >>= nWidth)
                    && (xProperties->getPropertyValue(OUString::createFromAscii("Height")) >>= nHeight)
                    && nHeight > 0)
                {
                    nSlideAspectRatio = double(nWidth) / double(nHeight);
                }
            }
        }
    }
    catch (RuntimeException&)
    {
        OSL_ASSERT(false);
    }

    return nSlideAspectRatio;
}




void PresenterController::UpdatePendingSlideNumber (const sal_Int32 nPendingSlideNumber)
{
    mnPendingSlideNumber = nPendingSlideNumber;

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

    if ( ! mxMainWindow.is())
        return;

    PresenterTheme::SharedFontDescriptor pFont (
        mpTheme->GetFont(A2S("PendingSlideNumberFont")));
    if (pFont.get() == NULL)
        return;

    pFont->PrepareFont(Reference<rendering::XCanvas>(mxCanvas, UNO_QUERY));
    if ( ! pFont->mxFont.is())
        return;

    const OUString sText (OUString::valueOf(mnPendingSlideNumber));
    rendering::StringContext aContext (sText, 0, sText.getLength());
    Reference<rendering::XTextLayout> xLayout (
        pFont->mxFont->createTextLayout(
            aContext,
            rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
            0));
}




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


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

