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

#include "precompiled_sd.hxx"

#include "controller/SlideSorterController.hxx"

#include "SlideSorter.hxx"
#include "controller/SlsPageSelector.hxx"
#include "controller/SlsSelectionFunction.hxx"
#include "controller/SlsProperties.hxx"
#include "controller/SlsCurrentSlideManager.hxx"
#include "SlsListener.hxx"
#include "controller/SlsFocusManager.hxx"
#include "SlsSelectionCommand.hxx"
#include "controller/SlsAnimator.hxx"
#include "controller/SlsClipboard.hxx"
#include "controller/SlsScrollBarManager.hxx"
#include "controller/SlsPageObjectFactory.hxx"
#include "controller/SlsSelectionManager.hxx"
#include "controller/SlsSlotManager.hxx"
#include "model/SlideSorterModel.hxx"
#include "model/SlsPageEnumerationProvider.hxx"
#include "model/SlsPageDescriptor.hxx"
#include "view/SlideSorterView.hxx"
#include "view/SlsLayouter.hxx"
#include "view/SlsViewOverlay.hxx"
#include "view/SlsFontProvider.hxx"
#include "cache/SlsPageCache.hxx"
#include "cache/SlsPageCacheManager.hxx"

#include "drawdoc.hxx"
#include "DrawViewShell.hxx"
#include "TextLogger.hxx"
#include "ViewShellBase.hxx"
#include "Window.hxx"
#include "FrameView.hxx"
#include "DrawDocShell.hxx"
#include "sdpage.hxx"
#include "res_bmp.hrc"
#include "sdresid.hxx"
#include "strings.hrc"
#include "app.hrc"
#include "glob.hrc"
#include "sdmod.hxx"
#include "sdxfer.hxx"
#include "FrameView.hxx"
#include "ViewShellHint.hxx"
#include "AccessibleSlideSorterView.hxx"
#include "AccessibleSlideSorterObject.hxx"

#include <vcl/window.hxx>
#include <svx/svdopage.hxx>
#include <svx/svxids.hrc>
#include <svx/ruler.hxx>
#include <svx/zoomitem.hxx>
#include <svtools/tabbar.hxx>
#include <sfx2/request.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/dispatch.hxx>
#include <tools/link.hxx>
#include <vcl/svapp.hxx>

#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#include <com/sun/star/drawing/XDrawPages.hpp>
#include <com/sun/star/accessibility/AccessibleEventId.hpp>


using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::sd::slidesorter::model;
using namespace ::sd::slidesorter::view;
using namespace ::sd::slidesorter::controller;

namespace sd { namespace slidesorter { namespace controller {


SlideSorterController::SlideSorterController (SlideSorter& rSlideSorter)
    : mrSlideSorter(rSlideSorter),
      mrModel(mrSlideSorter.GetModel()),
      mrView(mrSlideSorter.GetView()),
      mpPageSelector(),
      mpFocusManager(),
      mpSlotManager(),
      mpClipboard(),
      mpScrollBarManager(),
      mpCurrentSlideManager(),
      mpSelectionManager(),
      mpAnimator(new Animator(rSlideSorter)),
      mpListener(),
      mnModelChangeLockCount(0),
      mbPreModelChangeDone(false),
      mbPostModelChangePending(false),
      maSelectionBeforeSwitch(),
      mnCurrentPageBeforeSwitch(0),
      mpEditModeChangeMasterPage(NULL),
      maTotalWindowArea(),
      mnPaintEntranceCount(0),
      mbIsContextMenuOpen(false),
      mpProperties(new Properties())
{
    ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow();
    OSL_ASSERT(pWindow!=NULL);
    if (pWindow != NULL)
    {
        // The whole background is painted by the view and controls.
        ::Window* pParentWindow = pWindow->GetParent();
        OSL_ASSERT(pParentWindow!=NULL);
        pParentWindow->SetBackground (Wallpaper());

        // Connect the view with the window that has been created by our base
        // class.
        pWindow->SetBackground (Wallpaper());
        mrView.AddWindowToPaintView(pWindow);
        mrView.SetActualWin(pWindow);
        pWindow->SetCenterAllowed (false);
        pWindow->SetViewSize (mrView.GetModelArea().GetSize());
        pWindow->EnableRTL(FALSE);

        // Reinitialize colors in Properties with window specific values.
        mpProperties->SetBackgroundColor(
            pWindow->GetSettings().GetStyleSettings().GetWindowColor());
        mpProperties->SetTextColor(
            pWindow->GetSettings().GetStyleSettings().GetWindowTextColor());
        mpProperties->SetSelectionColor(
            pWindow->GetSettings().GetStyleSettings().GetMenuHighlightColor());
        mpProperties->SetHighlightColor(
            pWindow->GetSettings().GetStyleSettings().GetMenuHighlightColor());
    }
}




void SlideSorterController::Init (void)
{
    mrView.HandleModelChange();
    
    mpCurrentSlideManager.reset(new CurrentSlideManager(mrSlideSorter));
    mpPageSelector.reset(new PageSelector(mrSlideSorter));
    mpFocusManager.reset(new FocusManager(mrSlideSorter));
    mpSlotManager.reset(new SlotManager(mrSlideSorter));
    mpClipboard.reset(new Clipboard(mrSlideSorter));
    mpScrollBarManager.reset(new ScrollBarManager(mrSlideSorter));
    mpSelectionManager.reset(new SelectionManager(mrSlideSorter));

    mpScrollBarManager->LateInitialization();

    // Create the selection function.
    SfxRequest aRequest (
        SID_OBJECT_SELECT,
        0,
        mrModel.GetDocument()->GetItemPool());
    mrSlideSorter.SetCurrentFunction(CreateSelectionFunction(aRequest));

    mpListener = new Listener(mrSlideSorter);

    mpPageSelector->UpdateAllPages();
    GetSelectionManager()->SelectionHasChanged();
}




SlideSorterController::~SlideSorterController (void)
{
    try
    {
        uno::Reference<lang::XComponent> xComponent (
            static_cast<XWeak*>(mpListener.get()), uno::UNO_QUERY);
        if (xComponent.is())
            xComponent->dispose();
    }
    catch( uno::Exception& e )
    {
        (void)e;
        DBG_ERROR( "sd::SlideSorterController::~SlideSorterController(), exception caught!" );
    }

    // dispose should have been called by now so that nothing is to be done
    // to shut down cleanly.
}




model::SharedPageDescriptor SlideSorterController::GetPageAt (
    const Point& aPixelPosition)
{
    sal_Int32 nHitPageIndex (mrView.GetPageIndexAtPoint(aPixelPosition));
    model::SharedPageDescriptor pDescriptorAtPoint;
    if (nHitPageIndex >= 0)
        pDescriptorAtPoint = mrModel.GetPageDescriptor(nHitPageIndex);

    return pDescriptorAtPoint;
}

PageSelector& SlideSorterController::GetPageSelector (void)
{
    OSL_ASSERT(mpPageSelector.get()!=NULL);
    return *mpPageSelector.get();
}




FocusManager& SlideSorterController::GetFocusManager (void)
{
    OSL_ASSERT(mpFocusManager.get()!=NULL);
    return *mpFocusManager.get();
}




Clipboard& SlideSorterController::GetClipboard (void)
{
    OSL_ASSERT(mpClipboard.get()!=NULL);
    return *mpClipboard.get();
}




ScrollBarManager& SlideSorterController::GetScrollBarManager (void)
{
    OSL_ASSERT(mpScrollBarManager.get()!=NULL);
    return *mpScrollBarManager.get();
}




::boost::shared_ptr<CurrentSlideManager> SlideSorterController::GetCurrentSlideManager (void) const
{
    OSL_ASSERT(mpCurrentSlideManager.get()!=NULL);
    return mpCurrentSlideManager;
}




::boost::shared_ptr<SlotManager> SlideSorterController::GetSlotManager (void) const
{
    OSL_ASSERT(mpSlotManager.get()!=NULL);
    return mpSlotManager;
}




::boost::shared_ptr<SelectionManager> SlideSorterController::GetSelectionManager (void) const
{
    OSL_ASSERT(mpSelectionManager.get()!=NULL);
    return mpSelectionManager;
}




void SlideSorterController::PrePaint()
{
    // forward VCLs PrePaint window event to DrawingLayer
    mrView.PrePaint();
}




void SlideSorterController::Paint (
    const Rectangle& rBBox,
    ::Window* pWindow)
{
    //    if (mnPaintEntranceCount == 0)
    {
        ++mnPaintEntranceCount;

        try
        {
            if (GetSelectionManager()->IsMakeSelectionVisiblePending())
                GetSelectionManager()->MakeSelectionVisible();

            mrView.SetApplicationDocumentColor(GetProperties()->GetBackgroundColor());
            mrView.CompleteRedraw(pWindow, Region(rBBox), 0);
        }
        catch (const Exception&)
        {
            // Ignore all exceptions.
        }

        --mnPaintEntranceCount;
    }
}




void SlideSorterController::FuTemporary (SfxRequest& rRequest)
{
    mpSlotManager->FuTemporary (rRequest);
}




void SlideSorterController::FuPermanent (SfxRequest &rRequest)
{
    mpSlotManager->FuPermanent (rRequest);
}




void SlideSorterController::FuSupport (SfxRequest &rRequest)
{
    mpSlotManager->FuSupport (rRequest);
}




bool SlideSorterController::Command (
    const CommandEvent& rEvent,
    ::sd::Window* pWindow)
{
    bool bEventHasBeenHandled = false;

    ViewShell* pViewShell = mrSlideSorter.GetViewShell();
    
    switch (rEvent.GetCommand())
    {
        case COMMAND_CONTEXTMENU:
        {
            SdPage* pPage = NULL;
            USHORT nPopupId;

            model::PageEnumeration aSelectedPages (
                PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel));
            if (aSelectedPages.HasMoreElements())
                pPage = aSelectedPages.GetNextElement()->GetPage();

            // Choose the popup menu depending on a) the type of the main
            // view shell, b) the edit mode, and c) on whether the selection
            // is empty or not.
            ViewShell::ShellType eMainViewShellType (ViewShell::ST_NONE);
            ::boost::shared_ptr<ViewShell> pMainViewShell (
                pViewShell->GetViewShellBase().GetMainViewShell());
            if (pMainViewShell.get() != NULL)
                eMainViewShellType = pMainViewShell->GetShellType();
            switch (eMainViewShellType)
            {
                case ViewShell::ST_DRAW:
                    if (pPage != NULL)
                        nPopupId = RID_SLIDE_SORTER_DRAW_SEL_POPUP;
                    else
                        nPopupId = RID_SLIDE_SORTER_DRAW_NOSEL_POPUP;
                    break;

                default:
                    if (mrModel.GetEditMode() == EM_PAGE)
                        if (pPage != NULL)
                            nPopupId = RID_SLIDE_SORTER_IMPRESS_SEL_POPUP;
                        else
                            nPopupId = RID_SLIDE_SORTER_IMPRESS_NOSEL_POPUP;
                    else
                        if (pPage != NULL)
                            nPopupId = RID_SLIDE_SORTER_MASTER_SEL_POPUP;
                        else
                            nPopupId = RID_SLIDE_SORTER_MASTER_NOSEL_POPUP;
            }

            if (pPage == NULL)
            {
                // When there is no selection, then we show the insertion
                // indicator so that the user knows where a page insertion
                // would take place.
                mrView.GetOverlay().GetInsertionIndicatorOverlay().SetPosition(
                    pWindow->PixelToLogic(rEvent.GetMousePosPixel()));
                mrView.GetOverlay().GetInsertionIndicatorOverlay().setVisible(true);
            }

            pWindow->ReleaseMouse();
            if (rEvent.IsMouseEvent())
            {
                mbIsContextMenuOpen = true;
                if (pViewShell != NULL)
                {
                    SfxDispatcher* pDispatcher = pViewShell->GetDispatcher();
                    if (pDispatcher != NULL)
                        pDispatcher->ExecutePopup(SdResId(nPopupId));
                }
            }
            else
            {
                // The event is not a mouse event.  Use the center of the
                // focused page as top left position of the context menu.
                if (pPage != NULL)
                {
                    model::SharedPageDescriptor pDescriptor (
                        GetFocusManager().GetFocusedPageDescriptor());
                    if (pDescriptor.get() != NULL)
                    {
                        Rectangle aBBox (mrView.GetPageBoundingBox (
                            pDescriptor,
                            view::SlideSorterView::CS_SCREEN,
                            view::SlideSorterView::BBT_SHAPE));
                        Point aPosition (aBBox.Center());
                        mbIsContextMenuOpen = true;
                        if (pViewShell != NULL)
                            pViewShell->GetViewFrame()->GetDispatcher()->ExecutePopup(
                                SdResId(nPopupId),
                                pWindow,
                                &aPosition);
                    }
                }
            }
            mbIsContextMenuOpen = false;
            if (pPage == NULL)
            {
                // Remember the position of the insertion indicator before
                // it is hidden, so that a pending slide insertion slot call
                // finds the right place to insert a new slide.
                GetSelectionManager()->SetInsertionPosition(
                    mrView.GetOverlay().GetInsertionIndicatorOverlay().GetInsertionPageIndex());
                mrView.GetOverlay().GetInsertionIndicatorOverlay().setVisible(false);
            }
            bEventHasBeenHandled = true;
        }
        break;

        case COMMAND_WHEEL:
        {
            // We ignore zooming with control+mouse wheel.
            const CommandWheelData* pData = rEvent.GetWheelData();
            if (pData!=NULL && pData->IsMod1())
                bEventHasBeenHandled = true;
        }
        break;
    }

    return bEventHasBeenHandled;
}




void SlideSorterController::LockModelChange (void)
{
    mnModelChangeLockCount += 1;
}




void SlideSorterController::UnlockModelChange (void)
{
    mnModelChangeLockCount -= 1;
    if (mnModelChangeLockCount==0 && mbPostModelChangePending)
        PostModelChange();
}




void SlideSorterController::PreModelChange (void)
{
    // Prevent PreModelChange to execute more than once per model lock.
    if (mbPostModelChangePending)
        return;
    mbPreModelChangeDone = true;
    
    if (mrSlideSorter.GetViewShell() != NULL)
        mrSlideSorter.GetViewShell()->Broadcast(
            ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START));

    mpPageSelector->PrepareModelChange();
    GetCurrentSlideManager()->PrepareModelChange();

    ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow();
    if (pWindow != NULL)
        mrView.PreModelChange();

    mbPostModelChangePending = true;
}




void SlideSorterController::PostModelChange (void)
{
    mbPostModelChangePending = false;
    mrModel.Resync();

    ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow();
    if (pWindow != NULL)
    {
        GetCurrentSlideManager()->HandleModelChange();

        mrView.PostModelChange ();

        pWindow->SetViewOrigin (Point (0,0));
        pWindow->SetViewSize (mrView.GetModelArea().GetSize());

        // The visibility of the scroll bars may have to be changed.  Then
        // the size of the view has to change, too.  Let Rearrange() handle
        // that.
        Rearrange();
    }

    mpPageSelector->HandleModelChange ();

    if (mrSlideSorter.GetViewShell() != NULL)
        mrSlideSorter.GetViewShell()->Broadcast(
            ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END));
}




void SlideSorterController::HandleModelChange (void)
{
    // Ignore this call when the document is not in a valid state, i.e. has
    // not the same number of regular and notes pages.
    bool bIsDocumentValid = (mrModel.GetDocument()->GetPageCount() % 2 == 1);

    if (bIsDocumentValid)
    {
        ModelChangeLock aLock (*this);
        PreModelChange();
    }
}




IMPL_LINK(SlideSorterController, WindowEventHandler, VclWindowEvent*, pEvent)
{
    if (pEvent != NULL)
    {
        ::Window* pWindow = pEvent->GetWindow();
        ::sd::Window* pActiveWindow = mrSlideSorter.GetActiveWindow();
        switch (pEvent->GetId())
        {
            case VCLEVENT_WINDOW_ACTIVATE:
            case VCLEVENT_WINDOW_SHOW:
                if (pActiveWindow != NULL && pWindow == pActiveWindow->GetParent())
                    mrView.RequestRepaint();
                break;

            case VCLEVENT_WINDOW_GETFOCUS:
                if (pActiveWindow != NULL && pWindow == pActiveWindow)
                    GetFocusManager().ShowFocus(false);
                break;

            case VCLEVENT_WINDOW_LOSEFOCUS:
                if (pActiveWindow != NULL && pWindow == pActiveWindow)
                    GetFocusManager().HideFocus();
                break;

            case VCLEVENT_APPLICATION_DATACHANGED:
            {
                // Invalidate the preview cache.
                cache::PageCacheManager::Instance()->InvalidateAllCaches();

                // Update the draw mode.
                ULONG nDrawMode (Application::GetSettings().GetStyleSettings().GetHighContrastMode()
                    ? ViewShell::OUTPUT_DRAWMODE_CONTRAST
                    : ViewShell::OUTPUT_DRAWMODE_COLOR);
                if (mrSlideSorter.GetViewShell() != NULL)
                    mrSlideSorter.GetViewShell()->GetFrameView()->SetDrawMode(nDrawMode);
                if (pActiveWindow != NULL)
                    pActiveWindow->SetDrawMode(nDrawMode);
                mrView.HandleDrawModeChange();
                
                // When the system font has changed a layout has to be done.
                mrView.Resize();
                FontProvider::Instance().Invalidate();
            }
            break;

            default:
                break;
        }
    }

    return TRUE;
}




void SlideSorterController::GetCtrlState (SfxItemSet& rSet)
{
    if (rSet.GetItemState(SID_RELOAD) != SFX_ITEM_UNKNOWN)
    {
        // "Letzte Version" vom SFx en/disablen lassen
        SfxViewFrame* pSlideViewFrame = SfxViewFrame::Current();
        DBG_ASSERT(pSlideViewFrame!=NULL,
            "SlideSorterController::GetCtrlState: ViewFrame not found");
        if (pSlideViewFrame)
        {
            pSlideViewFrame->GetSlotState (SID_RELOAD, NULL, &rSet);
        }
        else		// MI sagt: kein MDIFrame --> disablen
        {
            rSet.DisableItem(SID_RELOAD);
        }
    }

    // Output quality.
    if (rSet.GetItemState(SID_OUTPUT_QUALITY_COLOR)==SFX_ITEM_AVAILABLE
        ||rSet.GetItemState(SID_OUTPUT_QUALITY_GRAYSCALE)==SFX_ITEM_AVAILABLE
        ||rSet.GetItemState(SID_OUTPUT_QUALITY_BLACKWHITE)==SFX_ITEM_AVAILABLE
        ||rSet.GetItemState(SID_OUTPUT_QUALITY_CONTRAST)==SFX_ITEM_AVAILABLE)
    {
        ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow();
        if (pWindow != NULL)
        {
            ULONG nMode = pWindow->GetDrawMode();
            UINT16 nQuality = 0;

            switch (nMode)
            {
                case ViewShell::OUTPUT_DRAWMODE_COLOR:
                    nQuality = 0;
                    break;
                case ViewShell::OUTPUT_DRAWMODE_GRAYSCALE:
                    nQuality = 1;
                    break;
                case ViewShell::OUTPUT_DRAWMODE_BLACKWHITE:
                    nQuality = 2;
                    break;
                case ViewShell::OUTPUT_DRAWMODE_CONTRAST:
                    nQuality = 3;
                    break;
            }

            rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_COLOR,
                    (BOOL)(nQuality==0)));
            rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_GRAYSCALE,
                    (BOOL)(nQuality==1)));
            rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_BLACKWHITE,
                    (BOOL)(nQuality==2)));
            rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_CONTRAST,
                    (BOOL)(nQuality==3)));
        }
    }

    if (rSet.GetItemState(SID_MAIL_SCROLLBODY_PAGEDOWN) == SFX_ITEM_AVAILABLE)
    {
        rSet.Put (SfxBoolItem( SID_MAIL_SCROLLBODY_PAGEDOWN, TRUE));
    }
}




void SlideSorterController::GetStatusBarState (SfxItemSet& rSet)
{
    mpSlotManager->GetStatusBarState (rSet);
}




void SlideSorterController::ExecCtrl (SfxRequest& rRequest)
{
    mpSlotManager->ExecCtrl (rRequest);
}




void SlideSorterController::GetAttrState (SfxItemSet& rSet)
{
    mpSlotManager->GetAttrState (rSet);
}




void SlideSorterController::ExecStatusBar (SfxRequest& )
{
}




void SlideSorterController::UpdateAllPages (void)
{
    // Do a redraw.
    mrView.InvalidateAllWin();
}




Rectangle SlideSorterController::Resize (const Rectangle& rAvailableSpace)
{
    Rectangle aContentArea (rAvailableSpace);

    if (maTotalWindowArea != rAvailableSpace)
    {
        maTotalWindowArea = rAvailableSpace;
        aContentArea = Rearrange(true);
    }

    return aContentArea;
}




Rectangle  SlideSorterController::Rearrange (bool bForce)
{
    Rectangle aNewContentArea (maTotalWindowArea);

    ::boost::shared_ptr<sd::Window> pWindow = mrSlideSorter.GetContentWindow();
    if (pWindow.get() != NULL)
    {
        // Place the scroll bars.
        aNewContentArea = GetScrollBarManager().PlaceScrollBars(maTotalWindowArea);

        bool bSizeHasChanged (false);
        // Only when bForce is not true we have to test for a size change in
        // order to determine whether the window and the view have to be resized.
        if ( ! bForce)
        {
            Rectangle aCurrentContentArea (
                pWindow->GetPosPixel(),
                pWindow->GetOutputSizePixel());
            bSizeHasChanged = (aNewContentArea != aCurrentContentArea);
        }
        if (bForce || bSizeHasChanged)
        {
            // The browser window gets the remaining space.
            pWindow->SetPosSizePixel (aNewContentArea.TopLeft(), aNewContentArea.GetSize());
            mrView.Resize();
        }

        // Adapt the scroll bars to the new zoom factor of the browser
        // window and the arrangement of the page objects.
        GetScrollBarManager().UpdateScrollBars(false, !bForce);
    }

    return aNewContentArea;
}




FunctionReference SlideSorterController::CreateSelectionFunction (SfxRequest& rRequest)
{
    FunctionReference xFunc( SelectionFunction::Create(mrSlideSorter, rRequest) );
    return xFunc;
}




void SlideSorterController::PrepareEditModeChange (void)
{
    //  Before we throw away the page descriptors we prepare for selecting
    //  descriptors in the other mode and for restoring the current
    //  selection when switching back to the current mode.
    if (mrModel.GetEditMode() == EM_PAGE)
    {
        maSelectionBeforeSwitch.clear();

        // Search for the first selected page and determine the master page
        // used by its page object.  It will be selected after the switch.
        // In the same loop the current selection is stored.
        PageEnumeration aSelectedPages (
            PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel));
        while (aSelectedPages.HasMoreElements())
        {
            SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement());
            SdPage* pPage = pDescriptor->GetPage();
            // Remember the master page of the first selected descriptor.
            if (pPage!=NULL && mpEditModeChangeMasterPage==NULL)
                mpEditModeChangeMasterPage = &static_cast<SdPage&>(
                    pPage->TRG_GetMasterPage());

            maSelectionBeforeSwitch.push_back(pPage);
        }

        // Remember the current page.
        if (mrSlideSorter.GetViewShell() != NULL)
            mnCurrentPageBeforeSwitch = (mrSlideSorter.GetViewShell()->GetViewShellBase()
            .GetMainViewShell()->GetActualPage()->GetPageNum()-1)/2;
    }
}




bool SlideSorterController::ChangeEditMode (EditMode eEditMode)
{
    bool bResult (false);
    if (mrModel.GetEditMode() != eEditMode)
    {
        ModelChangeLock aLock (*this);
        PreModelChange();
        // Do the actual edit mode switching.
        bResult = mrModel.SetEditMode(eEditMode);
        if (bResult)
            HandleModelChange();
    }
    return bResult;
}




void SlideSorterController::FinishEditModeChange (void)
{
    if (mrModel.GetEditMode() == EM_MASTERPAGE)
    {
        // Search for the master page that was determined in
        // PrepareEditModeChange() and make it the current page.
        PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(mrModel));
        while (aAllPages.HasMoreElements())
        {
            SharedPageDescriptor pDescriptor (aAllPages.GetNextElement());
            if (pDescriptor->GetPage() == mpEditModeChangeMasterPage)
            {
                GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor);
                break;
            }
        }
    }
    else
    {
        SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(mnCurrentPageBeforeSwitch));
        GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor);

        // Restore the selection.
        ::std::vector<SdPage*>::iterator iPage;
        for (iPage=maSelectionBeforeSwitch.begin();
             iPage!=maSelectionBeforeSwitch.end();
             ++iPage)
        {
            mpPageSelector->SelectPage(*iPage);
        }
        maSelectionBeforeSwitch.clear( );
    }
    mpEditModeChangeMasterPage = NULL;
}




void SlideSorterController::PageNameHasChanged (int nPageIndex, const String& rsOldName)
{
    // Request a repaint for the page object whose name has changed.
    model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex));
    if (pDescriptor.get() != NULL)
        mrView.RequestRepaint(pDescriptor);

    // Get a pointer to the corresponding accessible object and notify
    // that of the name change.
    ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow();
    if (pWindow == NULL)
        return;

    ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >
        xAccessible (pWindow->GetAccessible(FALSE));
    if ( ! xAccessible.is())
        return;

    // Now comes a small hack.  We assume that the accessible object is
    // an instantiation of AccessibleSlideSorterView and cast it to that
    // class.  The cleaner alternative to this cast would be a new member
    // in which we would store the last AccessibleSlideSorterView object
    // created by SlideSorterViewShell::CreateAccessibleDocumentView().
    // But then there is no guaranty that the accessible object obtained
    // from the window really is that instance last created by
    // CreateAccessibleDocumentView().
    // However, the dynamic cast together with the check of the result
    // being NULL should be safe enough.
    ::accessibility::AccessibleSlideSorterView* pAccessibleView
            = dynamic_cast< ::accessibility::AccessibleSlideSorterView*>(xAccessible.get());
    if (pAccessibleView == NULL)
        return;

    ::accessibility::AccessibleSlideSorterObject* pChild
            = pAccessibleView->GetAccessibleChildImplementation(nPageIndex);
    if (pChild == NULL || pChild->GetPage() == NULL)
        return;

    ::rtl::OUString sOldName (rsOldName);
    ::rtl::OUString sNewName (pChild->GetPage()->GetName());
    pChild->FireAccessibleEvent(
        ::com::sun::star::accessibility::AccessibleEventId::NAME_CHANGED,
        makeAny(sOldName),
        makeAny(sNewName));
}




bool SlideSorterController::IsContextMenuOpen (void) const
{
    return mbIsContextMenuOpen;
}




::boost::shared_ptr<Properties> SlideSorterController::GetProperties (void) const
{
    return mpProperties;
}




void SlideSorterController::SetDocumentSlides (const Reference<container::XIndexAccess>& rxSlides)
{
    if (mrModel.GetDocumentSlides() != rxSlides)
    {
        ModelChangeLock aLock (*this);
        PreModelChange();
        
        mrModel.SetDocumentSlides(rxSlides);
        mrView.Layout();
    }
}




::boost::shared_ptr<Animator> SlideSorterController::GetAnimator (void) const
{
    return mpAnimator;
}




//===== SlideSorterController::ModelChangeLock ================================

SlideSorterController::ModelChangeLock::ModelChangeLock (
    SlideSorterController& rController)
    : mpController(&rController)
{
    mpController->LockModelChange();
}




SlideSorterController::ModelChangeLock::~ModelChangeLock (void)
{
    Release();
}




void SlideSorterController::ModelChangeLock::Release (void)
{
    if (mpController != NULL)
    {
        mpController->UnlockModelChange();
        mpController = NULL;
    }
}

} } } // end of namespace ::sd::slidesorter

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
