diff options
Diffstat (limited to 'sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx')
-rwxr-xr-x | sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx new file mode 100755 index 000000000000..f1b4cb6c39be --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx @@ -0,0 +1,662 @@ +/************************************************************************* + * + * 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/SlsSelectionManager.hxx" + +#include "SlideSorter.hxx" +#include "SlsSelectionCommand.hxx" +#include "controller/SlideSorterController.hxx" +#include "controller/SlsAnimator.hxx" +#include "controller/SlsCurrentSlideManager.hxx" +#include "controller/SlsFocusManager.hxx" +#include "controller/SlsProperties.hxx" +#include "controller/SlsScrollBarManager.hxx" +#include "controller/SlsSlotManager.hxx" +#include "model/SlideSorterModel.hxx" +#include "model/SlsPageEnumerationProvider.hxx" +#include "model/SlsPageDescriptor.hxx" +#include "view/SlideSorterView.hxx" +#include "drawdoc.hxx" +#include "Window.hxx" +#include <svx/svxids.hrc> +#include <com/sun/star/drawing/XMasterPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> + +#include "res_bmp.hrc" +#include "sdresid.hxx" +#include "strings.hrc" +#include "app.hrc" +#include "glob.hrc" + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::drawing; +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 { + + +namespace { + class VerticalVisibleAreaScroller + { + public: + VerticalVisibleAreaScroller (SlideSorter& rSlideSorter, + const double nStart, const double nEnd); + void operator() (const double nValue); + private: + SlideSorter& mrSlideSorter; + double mnStart; + double mnEnd; + }; + class HorizontalVisibleAreaScroller + { + public: + HorizontalVisibleAreaScroller (SlideSorter& rSlideSorter, + const double nStart, const double nEnd); + void operator() (const double nValue); + private: + SlideSorter& mrSlideSorter; + double mnStart; + double mnEnd; + }; +} + + + + +SelectionManager::SelectionManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mrController(rSlideSorter.GetController()), + maSelectionBeforeSwitch(), + mbIsMakeSelectionVisiblePending(true) +{ +} + + + + +SelectionManager::~SelectionManager (void) +{ +} + + + + +void SelectionManager::DeleteSelectedPages (void) +{ + SlideSorterController::ModelChangeLock aLock (mrController); + + // Hide focus. + bool bIsFocusShowing = mrController.GetFocusManager().IsFocusShowing(); + if (bIsFocusShowing) + mrController.GetFocusManager().ToggleFocus(); + + // Store pointers to all selected page descriptors. This is necessary + // because the pages get deselected when the first one is deleted. + model::PageEnumeration aPageEnumeration ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + ::std::vector<SdPage*> aSelectedPages; + while (aPageEnumeration.HasMoreElements()) + aSelectedPages.push_back (aPageEnumeration.GetNextElement()->GetPage()); + + // The actual deletion of the selected pages is done in one of two + // helper functions. They are specialized for normal respectively for + // master pages. + mrSlideSorter.GetView().BegUndo (SdResId(STR_UNDO_DELETEPAGES)); + if (mrSlideSorter.GetModel().GetEditMode() == EM_PAGE) + DeleteSelectedNormalPages(aSelectedPages); + else + DeleteSelectedMasterPages(aSelectedPages); + mrSlideSorter.GetView().EndUndo (); + + mrController.HandleModelChange(); + aLock.Release(); + + // Show focus and move it to next valid location. + if (bIsFocusShowing) + mrController.GetFocusManager().ToggleFocus (); + mrController.GetFocusManager().MoveFocus (FocusManager::FMD_NONE); +} + + + + +void SelectionManager::DeleteSelectedNormalPages (const ::std::vector<SdPage*>& rSelectedPages) +{ + // Prepare the deletion via the UNO API. + OSL_ASSERT(mrSlideSorter.GetModel().GetEditMode() == EM_PAGE); + + try + { + Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier( mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW ); + Reference<drawing::XDrawPages> xPages( xDrawPagesSupplier->getDrawPages(), UNO_QUERY_THROW ); + + // Iterate over all pages that where seleted when this method was called + // and delete the draw page the notes page. The iteration is done in + // reverse order so that when one slide is not deleted (to avoid an + // empty document) the remaining slide is the first one. + ::std::vector<SdPage*>::const_reverse_iterator aI; + for (aI=rSelectedPages.rbegin(); aI!=rSelectedPages.rend(); aI++) + { + // Do not delete the last slide in the document. + if (xPages->getCount() <= 1) + break; + + USHORT nPage = ((*aI)->GetPageNum()-1) / 2; + + Reference< XDrawPage > xPage( xPages->getByIndex( nPage ), UNO_QUERY_THROW ); + xPages->remove(xPage); + } + } + catch( Exception& ) + { + DBG_ERROR("SelectionManager::DeleteSelectedNormalPages(), exception caught!"); + } +} + + + + +void SelectionManager::DeleteSelectedMasterPages (const ::std::vector<SdPage*>& rSelectedPages) +{ + // Prepare the deletion via the UNO API. + OSL_ASSERT(mrSlideSorter.GetModel().GetEditMode() == EM_MASTERPAGE); + + try + { + Reference<drawing::XMasterPagesSupplier> xDrawPagesSupplier( mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW ); + Reference<drawing::XDrawPages> xPages( xDrawPagesSupplier->getMasterPages(), UNO_QUERY_THROW ); + + // Iterate over all pages that where seleted when this method was called + // and delete the draw page the notes page. The iteration is done in + // reverse order so that when one slide is not deleted (to avoid an + // empty document) the remaining slide is the first one. + ::std::vector<SdPage*>::const_reverse_iterator aI; + for (aI=rSelectedPages.rbegin(); aI!=rSelectedPages.rend(); aI++) + { + // Do not delete the last slide in the document. + if (xPages->getCount() <= 1) + break; + + USHORT nPage = ((*aI)->GetPageNum()-1) / 2; + + Reference< XDrawPage > xPage( xPages->getByIndex( nPage ), UNO_QUERY_THROW ); + xPages->remove(xPage); + } + } + catch( Exception& ) + { + DBG_ERROR("SelectionManager::DeleteSelectedMasterPages(), exception caught!"); + } +} + + + + +bool SelectionManager::MoveSelectedPages (const sal_Int32 nTargetPageIndex) +{ + bool bMoved (false); + PageSelector& rSelector (mrController.GetPageSelector()); + + mrSlideSorter.GetView().LockRedraw (TRUE); + SlideSorterController::ModelChangeLock aLock (mrController); + + // Transfer selection of the slide sorter to the document. + mrSlideSorter.GetModel().SynchronizeDocumentSelection (); + + // Detect how many pages lie between document start and insertion + // position. + sal_Int32 nPageCountBeforeTarget (0); + ::boost::shared_ptr<PageSelector::PageSelection> pSelection (rSelector.GetPageSelection()); + PageSelector::PageSelection::const_iterator iSelectedPage (pSelection->begin()); + PageSelector::PageSelection::const_iterator iSelectionEnd (pSelection->end()); + for ( ; iSelectedPage!=iSelectionEnd; ++iSelectedPage) + { + if (*iSelectedPage==NULL) + continue; + if (((*iSelectedPage)->GetPageNum()-1)/2 <= nTargetPageIndex) + ++nPageCountBeforeTarget; + else + break; + } + + // Prepare to select the moved pages. + SelectionCommand* pCommand = new SelectionCommand( + rSelector,mrController.GetCurrentSlideManager(),mrSlideSorter.GetModel()); + sal_Int32 nSelectedPageCount (rSelector.GetSelectedPageCount()); + for (sal_Int32 nOffset=0; nOffset<nSelectedPageCount; ++nOffset) + pCommand->AddSlide(sal::static_int_cast<USHORT>( + nTargetPageIndex + nOffset - nPageCountBeforeTarget + 1)); + + // At the moment we can not move master pages. + if (nTargetPageIndex>=0) + { + if (mrSlideSorter.GetModel().GetEditMode() == EM_PAGE) + bMoved = mrSlideSorter.GetModel().GetDocument()->MovePages( + sal::static_int_cast<sal_uInt16>(nTargetPageIndex)); + } + if (bMoved) + mrController.GetSlotManager()->ExecuteCommandAsynchronously( + ::std::auto_ptr<controller::Command>(pCommand)); + + mrSlideSorter.GetView().LockRedraw (FALSE); + + return bMoved; +} + + + + +void SelectionManager::SelectionHasChanged (const bool bMakeSelectionVisible) +{ + if (bMakeSelectionVisible) + mbIsMakeSelectionVisiblePending = true; + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != NULL) + { + pViewShell->Invalidate (SID_EXPAND_PAGE); + pViewShell->Invalidate (SID_SUMMARY_PAGE); + pViewShell->Invalidate(SID_SHOW_SLIDE); + pViewShell->Invalidate(SID_HIDE_SLIDE); + pViewShell->Invalidate(SID_DELETE_PAGE); + pViewShell->Invalidate(SID_DELETE_MASTER_PAGE); + pViewShell->Invalidate(SID_ASSIGN_LAYOUT); + + // StatusBar + pViewShell->Invalidate (SID_STATUS_PAGE); + pViewShell->Invalidate (SID_STATUS_LAYOUT); + + OSL_ASSERT(mrController.GetCurrentSlideManager()); + SharedPageDescriptor pDescriptor(mrController.GetCurrentSlideManager()->GetCurrentSlide()); + if (pDescriptor.get() != NULL) + pViewShell->UpdatePreview(pDescriptor->GetPage()); + + // Tell the slection change listeners that the selection has changed. + ::std::vector<Link>::iterator iListener (maSelectionChangeListeners.begin()); + ::std::vector<Link>::iterator iEnd (maSelectionChangeListeners.end()); + for (; iListener!=iEnd; ++iListener) + { + iListener->Call(NULL); + } + + // Reset the insertion position: until set again it is calculated from + // the current selection. + mnInsertionPosition = -1; + } +} + + + + +bool SelectionManager::IsMakeSelectionVisiblePending (void) const +{ + return mbIsMakeSelectionVisiblePending; +} + + + + +/** We have to distinguish three cases: 1) the selection is empty, 2) the + selection fits completely into the visible area, 3) it does not. + 1) The visible area is not modified. + 2) When the selection fits completely into the visible area we have to + decide how to align it. It is done by scrolling it there and thus when + we scoll up the (towards the end of the document) the bottom of the + selection is aligned with the bottom of the window. When we scroll + down (towards the beginning of the document) the top of the selection is + aligned with the top of the window. + 3) We have to decide what part of the selection to make visible. Here + we use the eSelectionHint and concentrate on either the first, the last, + or the most recently selected page. We then again apply the algorithm + of a). + +*/ +Size SelectionManager::MakeSelectionVisible (const SelectionHint eSelectionHint) +{ + ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow(); + if (pWindow == NULL) + return Size(0,0); + + mbIsMakeSelectionVisiblePending = false; + + // Determine the descriptors of the first and last selected page and the + // bounding box that encloses their page objects. + model::SharedPageDescriptor pFirst; + model::SharedPageDescriptor pLast; + Rectangle aSelectionBox; + model::PageEnumeration aSelectedPages ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + + if (pFirst.get() == NULL) + pFirst = pDescriptor; + pLast = pDescriptor; + + aSelectionBox.Union (mrSlideSorter.GetView().GetPageBoundingBox ( + pDescriptor, + view::SlideSorterView::CS_MODEL, + view::SlideSorterView::BBT_INFO)); + } + + if (pFirst != NULL) + { + // Determine scroll direction and the position in model coordinates + // that will be aligned with the top/left or bottom/right window + // border. + if (DoesSelectionExceedVisibleArea(aSelectionBox)) + { + // We can show only a part of the selection. + aSelectionBox = ResolveLargeSelection(pFirst,pLast, eSelectionHint); + } + + return MakeRectangleVisible(aSelectionBox); + } + + return Size(0,0); +} + + + + +Size SelectionManager::MakeRectangleVisible (const Rectangle& rBox) +{ + ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow(); + if (pWindow == NULL) + return Size(0,0); + + Rectangle aVisibleArea (pWindow->PixelToLogic( + Rectangle( + Point(0,0), + pWindow->GetOutputSizePixel()))); + + if (mrSlideSorter.GetView().GetOrientation() == SlideSorterView::VERTICAL) + { + // Scroll the visible area to make aSelectionBox visible. + sal_Int32 nNewTop (aVisibleArea.Top()); + if (mrSlideSorter.GetController().GetProperties()->IsCenterSelection()) + { + nNewTop = rBox.Top() - (aVisibleArea.GetHeight() - rBox.GetHeight()) / 2; + } + else + { + if (rBox.Top() < aVisibleArea.Top()) + nNewTop = rBox.Top(); + else if (rBox.Bottom() > aVisibleArea.Bottom()) + nNewTop = rBox.Bottom() - aVisibleArea.GetHeight(); + // otherwise we do not modify the visible area. + } + + // Make some corrections of the new visible area. + Rectangle aModelArea (mrSlideSorter.GetView().GetModelArea()); + if (nNewTop + aVisibleArea.GetHeight() > aModelArea.Bottom()) + nNewTop = aModelArea.GetHeight() - aVisibleArea.GetHeight(); + if (nNewTop < aModelArea.Top()) + nNewTop = aModelArea.Top(); + + // Scroll. + if (nNewTop != aVisibleArea.Top()) + { + mrController.GetAnimator()->AddAnimation( + VerticalVisibleAreaScroller(mrSlideSorter, aVisibleArea.Top(), nNewTop), + mrSlideSorter.GetController().GetProperties()->IsSmoothSelectionScrolling() ? + 1000 : 0 + ); + } + + return Size(0,aVisibleArea.Top() - nNewTop); + } + else + { + // Scroll the visible area to make aSelectionBox visible. + sal_Int32 nNewLeft (aVisibleArea.Left()); + if (mrSlideSorter.GetController().GetProperties()->IsCenterSelection()) + { + nNewLeft = rBox.Left() - (aVisibleArea.GetWidth() - rBox.GetWidth()) / 2; + } + else + { + if (rBox.Left() < aVisibleArea.Left()) + nNewLeft = rBox.Left(); + else if (rBox.Right() > aVisibleArea.Right()) + nNewLeft = rBox.Right() - aVisibleArea.GetWidth(); + // otherwise we do not modify the visible area. + } + + // Make some corrections of the new visible area. + Rectangle aModelArea (mrSlideSorter.GetView().GetModelArea()); + if (nNewLeft + aVisibleArea.GetWidth() > aModelArea.Right()) + nNewLeft = aModelArea.GetWidth() - aVisibleArea.GetWidth(); + if (nNewLeft < aModelArea.Left()) + nNewLeft = aModelArea.Left(); + + // Scroll. + if (nNewLeft != aVisibleArea.Left()) + { + mrController.GetAnimator()->AddAnimation( + HorizontalVisibleAreaScroller(mrSlideSorter, aVisibleArea.Left(), nNewLeft), + mrSlideSorter.GetController().GetProperties()->IsSmoothSelectionScrolling() ? + 1000 : 0 + ); + } + + return Size(aVisibleArea.Left() - nNewLeft, 0); + } +} + + + + +void SelectionManager::AddSelectionChangeListener (const Link& rListener) +{ + if (::std::find ( + maSelectionChangeListeners.begin(), + maSelectionChangeListeners.end(), + rListener) == maSelectionChangeListeners.end()) + { + maSelectionChangeListeners.push_back (rListener); + } +} + + + + +void SelectionManager::RemoveSelectionChangeListener(const Link&rListener) +{ + maSelectionChangeListeners.erase ( + ::std::find ( + maSelectionChangeListeners.begin(), + maSelectionChangeListeners.end(), + rListener)); +} + + + + +bool SelectionManager::DoesSelectionExceedVisibleArea (const Rectangle& rSelectionBox) const +{ + ::sd::Window* pWindow = mrSlideSorter.GetActiveWindow(); + if (pWindow == NULL) + return true; + + Rectangle aVisibleArea (pWindow->PixelToLogic( + Rectangle( + Point(0,0), + pWindow->GetOutputSizePixel()))); + if (mrSlideSorter.GetView().GetOrientation() == SlideSorterView::VERTICAL) + return rSelectionBox.GetHeight() > aVisibleArea.GetHeight(); + else + return rSelectionBox.GetWidth() > aVisibleArea.GetWidth(); +} + + + + +Rectangle SelectionManager::ResolveLargeSelection ( + const SharedPageDescriptor& rpFirst, + const SharedPageDescriptor& rpLast, + const SelectionHint eSelectionHint) +{ + OSL_ASSERT(rpFirst.get()!=NULL); + OSL_ASSERT(rpLast.get()!=NULL); + + // The mose recently selected page is assumed to lie in the range + // between first and last selected page. Therefore the bounding box is + // not modified. + model::SharedPageDescriptor pRecent ( + mrController.GetPageSelector().GetMostRecentlySelectedPage()); + + // Get the bounding box of the page object on which to concentrate. + model::SharedPageDescriptor pRepresentative; + switch (eSelectionHint) + { + case SH_FIRST: + pRepresentative = rpFirst; + break; + + case SH_LAST: + pRepresentative = rpLast; + break; + + case SH_RECENT: + default: + if (pRecent.get() == NULL) + pRepresentative = rpFirst; + else + pRepresentative = pRecent; + break; + } + OSL_ASSERT(pRepresentative.get() != NULL); + + return mrSlideSorter.GetView().GetPageBoundingBox ( + pRepresentative, + view::SlideSorterView::CS_MODEL, + view::SlideSorterView::BBT_INFO); +} + + + + +sal_Int32 SelectionManager::GetInsertionPosition (void) const +{ + sal_Int32 nInsertionPosition (mnInsertionPosition); + if (nInsertionPosition < 0) + { + model::PageEnumeration aSelectedPages + (model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + // Initialize (for the case of an empty selection) with the position + // at the end of the document. + nInsertionPosition = mrSlideSorter.GetModel().GetPageCount(); + while (aSelectedPages.HasMoreElements()) + { + const sal_Int32 nPosition (aSelectedPages.GetNextElement()->GetPage()->GetPageNum()); + // Convert *2+1 index to straight index (n-1)/2 after the page + // (+1). + nInsertionPosition = (nPosition-1)/2 + 1; + } + + } + return nInsertionPosition; +} + + + + +void SelectionManager::SetInsertionPosition (const sal_Int32 nInsertionPosition) +{ + if (nInsertionPosition < 0) + mnInsertionPosition = -1; + else if (nInsertionPosition > mrSlideSorter.GetModel().GetPageCount()) + { + // Assert but then ignore invalid values. + OSL_ASSERT(nInsertionPosition<=mrSlideSorter.GetModel().GetPageCount()); + return; + } + else + mnInsertionPosition = nInsertionPosition; +} + + + + +//===== VerticalVisibleAreaScroller =========================================== + +namespace { + +VerticalVisibleAreaScroller::VerticalVisibleAreaScroller ( + SlideSorter& rSlideSorter, + const double nStart, + const double nEnd) + : mrSlideSorter(rSlideSorter), + mnStart(nStart), + mnEnd(nEnd) +{ +} + + + +void VerticalVisibleAreaScroller::operator() (const double nValue) +{ + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + mrSlideSorter.GetController().GetScrollBarManager().SetTop( + int(mnStart * (1.0 - nValue) + mnEnd * nValue)); +} + + + + +HorizontalVisibleAreaScroller::HorizontalVisibleAreaScroller ( + SlideSorter& rSlideSorter, + const double nStart, + const double nEnd) + : mrSlideSorter(rSlideSorter), + mnStart(nStart), + mnEnd(nEnd) +{ +} + + + + +void HorizontalVisibleAreaScroller::operator() (const double nValue) +{ + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + mrSlideSorter.GetController().GetScrollBarManager().SetLeft( + int(mnStart * (1.0 - nValue) + mnEnd * nValue)); +} + +} // end of anonymous namespace + +} } } // end of namespace ::sd::slidesorter |