1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: SlsSelectionManager.cxx,v $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 #include "precompiled_sd.hxx"
34 #include "controller/SlsSelectionManager.hxx"
36 #include "SlideSorter.hxx"
37 #include "SlsSelectionCommand.hxx"
38 #include "controller/SlideSorterController.hxx"
39 #include "controller/SlsAnimator.hxx"
40 #include "controller/SlsCurrentSlideManager.hxx"
41 #include "controller/SlsFocusManager.hxx"
42 #include "controller/SlsProperties.hxx"
43 #include "controller/SlsScrollBarManager.hxx"
44 #include "controller/SlsSlotManager.hxx"
45 #include "model/SlideSorterModel.hxx"
46 #include "model/SlsPageEnumerationProvider.hxx"
47 #include "model/SlsPageDescriptor.hxx"
48 #include "view/SlideSorterView.hxx"
49 #include "drawdoc.hxx"
51 #include <svx/svxids.hrc>
52 #include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
53 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
55 #include "res_bmp.hrc"
56 #include "sdresid.hxx"
57 #include "strings.hrc"
62 using namespace ::com::sun::star
;
63 using namespace ::com::sun::star::drawing
;
64 using namespace ::com::sun::star::uno
;
65 using namespace ::sd::slidesorter::model
;
66 using namespace ::sd::slidesorter::view
;
67 using namespace ::sd::slidesorter::controller
;
69 namespace sd
{ namespace slidesorter
{ namespace controller
{
73 class VerticalVisibleAreaScroller
76 VerticalVisibleAreaScroller (SlideSorter
& rSlideSorter
,
77 const double nStart
, const double nEnd
);
78 void operator() (const double nValue
);
80 SlideSorter
& mrSlideSorter
;
84 class HorizontalVisibleAreaScroller
87 HorizontalVisibleAreaScroller (SlideSorter
& rSlideSorter
,
88 const double nStart
, const double nEnd
);
89 void operator() (const double nValue
);
91 SlideSorter
& mrSlideSorter
;
100 SelectionManager::SelectionManager (SlideSorter
& rSlideSorter
)
101 : mrSlideSorter(rSlideSorter
),
102 mrController(rSlideSorter
.GetController()),
103 maSelectionBeforeSwitch(),
104 mbIsMakeSelectionVisiblePending(true)
111 SelectionManager::~SelectionManager (void)
118 void SelectionManager::DeleteSelectedPages (void)
120 SlideSorterController::ModelChangeLock
aLock (mrController
);
123 bool bIsFocusShowing
= mrController
.GetFocusManager().IsFocusShowing();
125 mrController
.GetFocusManager().ToggleFocus();
127 // Store pointers to all selected page descriptors. This is necessary
128 // because the pages get deselected when the first one is deleted.
129 model::PageEnumeration
aPageEnumeration (
130 PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter
.GetModel()));
131 ::std::vector
<SdPage
*> aSelectedPages
;
132 while (aPageEnumeration
.HasMoreElements())
133 aSelectedPages
.push_back (aPageEnumeration
.GetNextElement()->GetPage());
135 // The actual deletion of the selected pages is done in one of two
136 // helper functions. They are specialized for normal respectively for
138 mrSlideSorter
.GetView().BegUndo (SdResId(STR_UNDO_DELETEPAGES
));
139 if (mrSlideSorter
.GetModel().GetEditMode() == EM_PAGE
)
140 DeleteSelectedNormalPages(aSelectedPages
);
142 DeleteSelectedMasterPages(aSelectedPages
);
143 mrSlideSorter
.GetView().EndUndo ();
145 mrController
.HandleModelChange();
148 // Show focus and move it to next valid location.
150 mrController
.GetFocusManager().ToggleFocus ();
151 mrController
.GetFocusManager().MoveFocus (FocusManager::FMD_NONE
);
157 void SelectionManager::DeleteSelectedNormalPages (const ::std::vector
<SdPage
*>& rSelectedPages
)
159 // Prepare the deletion via the UNO API.
160 OSL_ASSERT(mrSlideSorter
.GetModel().GetEditMode() == EM_PAGE
);
164 Reference
<drawing::XDrawPagesSupplier
> xDrawPagesSupplier( mrSlideSorter
.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW
);
165 Reference
<drawing::XDrawPages
> xPages( xDrawPagesSupplier
->getDrawPages(), UNO_QUERY_THROW
);
167 // Iterate over all pages that where seleted when this method was called
168 // and delete the draw page the notes page. The iteration is done in
169 // reverse order so that when one slide is not deleted (to avoid an
170 // empty document) the remaining slide is the first one.
171 ::std::vector
<SdPage
*>::const_reverse_iterator aI
;
172 for (aI
=rSelectedPages
.rbegin(); aI
!=rSelectedPages
.rend(); aI
++)
174 // Do not delete the last slide in the document.
175 if (xPages
->getCount() <= 1)
178 USHORT nPage
= ((*aI
)->GetPageNum()-1) / 2;
180 Reference
< XDrawPage
> xPage( xPages
->getByIndex( nPage
), UNO_QUERY_THROW
);
181 xPages
->remove(xPage
);
186 DBG_ERROR("SelectionManager::DeleteSelectedNormalPages(), exception caught!");
193 void SelectionManager::DeleteSelectedMasterPages (const ::std::vector
<SdPage
*>& rSelectedPages
)
195 // Prepare the deletion via the UNO API.
196 OSL_ASSERT(mrSlideSorter
.GetModel().GetEditMode() == EM_MASTERPAGE
);
200 Reference
<drawing::XMasterPagesSupplier
> xDrawPagesSupplier( mrSlideSorter
.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW
);
201 Reference
<drawing::XDrawPages
> xPages( xDrawPagesSupplier
->getMasterPages(), UNO_QUERY_THROW
);
203 // Iterate over all pages that where seleted when this method was called
204 // and delete the draw page the notes page. The iteration is done in
205 // reverse order so that when one slide is not deleted (to avoid an
206 // empty document) the remaining slide is the first one.
207 ::std::vector
<SdPage
*>::const_reverse_iterator aI
;
208 for (aI
=rSelectedPages
.rbegin(); aI
!=rSelectedPages
.rend(); aI
++)
210 // Do not delete the last slide in the document.
211 if (xPages
->getCount() <= 1)
214 USHORT nPage
= ((*aI
)->GetPageNum()-1) / 2;
216 Reference
< XDrawPage
> xPage( xPages
->getByIndex( nPage
), UNO_QUERY_THROW
);
217 xPages
->remove(xPage
);
222 DBG_ERROR("SelectionManager::DeleteSelectedMasterPages(), exception caught!");
229 bool SelectionManager::MoveSelectedPages (const sal_Int32 nTargetPageIndex
)
232 PageSelector
& rSelector (mrController
.GetPageSelector());
234 mrSlideSorter
.GetView().LockRedraw (TRUE
);
235 SlideSorterController::ModelChangeLock
aLock (mrController
);
237 // Transfer selection of the slide sorter to the document.
238 mrSlideSorter
.GetModel().SynchronizeDocumentSelection ();
240 // Detect how many pages lie between document start and insertion
242 sal_Int32
nPageCountBeforeTarget (0);
243 ::boost::shared_ptr
<PageSelector::PageSelection
> pSelection (rSelector
.GetPageSelection());
244 PageSelector::PageSelection::const_iterator
iSelectedPage (pSelection
->begin());
245 PageSelector::PageSelection::const_iterator
iSelectionEnd (pSelection
->end());
246 for ( ; iSelectedPage
!=iSelectionEnd
; ++iSelectedPage
)
248 if (*iSelectedPage
==NULL
)
250 if (((*iSelectedPage
)->GetPageNum()-1)/2 <= nTargetPageIndex
)
251 ++nPageCountBeforeTarget
;
256 // Prepare to select the moved pages.
257 SelectionCommand
* pCommand
= new SelectionCommand(
258 rSelector
,mrController
.GetCurrentSlideManager(),mrSlideSorter
.GetModel());
259 sal_Int32
nSelectedPageCount (rSelector
.GetSelectedPageCount());
260 for (sal_Int32 nOffset
=0; nOffset
<nSelectedPageCount
; ++nOffset
)
261 pCommand
->AddSlide(sal::static_int_cast
<USHORT
>(
262 nTargetPageIndex
+ nOffset
- nPageCountBeforeTarget
+ 1));
264 // At the moment we can not move master pages.
265 if (nTargetPageIndex
>=0)
267 if (mrSlideSorter
.GetModel().GetEditMode() == EM_PAGE
)
268 bMoved
= mrSlideSorter
.GetModel().GetDocument()->MovePages(
269 sal::static_int_cast
<sal_uInt16
>(nTargetPageIndex
));
272 mrController
.GetSlotManager()->ExecuteCommandAsynchronously(
273 ::std::auto_ptr
<controller::Command
>(pCommand
));
275 mrSlideSorter
.GetView().LockRedraw (FALSE
);
283 void SelectionManager::SelectionHasChanged (const bool bMakeSelectionVisible
)
285 if (bMakeSelectionVisible
)
286 mbIsMakeSelectionVisiblePending
= true;
288 ViewShell
* pViewShell
= mrSlideSorter
.GetViewShell();
289 if (pViewShell
!= NULL
)
291 pViewShell
->Invalidate (SID_EXPAND_PAGE
);
292 pViewShell
->Invalidate (SID_SUMMARY_PAGE
);
293 pViewShell
->Invalidate(SID_SHOW_SLIDE
);
294 pViewShell
->Invalidate(SID_HIDE_SLIDE
);
295 pViewShell
->Invalidate(SID_DELETE_PAGE
);
296 pViewShell
->Invalidate(SID_DELETE_MASTER_PAGE
);
299 pViewShell
->Invalidate (SID_STATUS_PAGE
);
300 pViewShell
->Invalidate (SID_STATUS_LAYOUT
);
302 OSL_ASSERT(mrController
.GetCurrentSlideManager());
303 SharedPageDescriptor
pDescriptor(mrController
.GetCurrentSlideManager()->GetCurrentSlide());
304 if (pDescriptor
.get() != NULL
)
305 pViewShell
->UpdatePreview(pDescriptor
->GetPage());
307 // Tell the slection change listeners that the selection has changed.
308 ::std::vector
<Link
>::iterator
iListener (maSelectionChangeListeners
.begin());
309 ::std::vector
<Link
>::iterator
iEnd (maSelectionChangeListeners
.end());
310 for (; iListener
!=iEnd
; ++iListener
)
312 iListener
->Call(NULL
);
315 // Reset the insertion position: until set again it is calculated from
316 // the current selection.
317 mnInsertionPosition
= -1;
324 bool SelectionManager::IsMakeSelectionVisiblePending (void) const
326 return mbIsMakeSelectionVisiblePending
;
332 /** We have to distinguish three cases: 1) the selection is empty, 2) the
333 selection fits completely into the visible area, 3) it does not.
334 1) The visible area is not modified.
335 2) When the selection fits completely into the visible area we have to
336 decide how to align it. It is done by scrolling it there and thus when
337 we scoll up the (towards the end of the document) the bottom of the
338 selection is aligned with the bottom of the window. When we scroll
339 down (towards the beginning of the document) the top of the selection is
340 aligned with the top of the window.
341 3) We have to decide what part of the selection to make visible. Here
342 we use the eSelectionHint and concentrate on either the first, the last,
343 or the most recently selected page. We then again apply the algorithm
347 Size
SelectionManager::MakeSelectionVisible (const SelectionHint eSelectionHint
)
349 ::sd::Window
* pWindow
= mrSlideSorter
.GetActiveWindow();
353 mbIsMakeSelectionVisiblePending
= false;
355 // Determine the descriptors of the first and last selected page and the
356 // bounding box that encloses their page objects.
357 model::SharedPageDescriptor pFirst
;
358 model::SharedPageDescriptor pLast
;
359 Rectangle aSelectionBox
;
360 model::PageEnumeration
aSelectedPages (
361 PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter
.GetModel()));
362 while (aSelectedPages
.HasMoreElements())
364 model::SharedPageDescriptor
pDescriptor (aSelectedPages
.GetNextElement());
366 if (pFirst
.get() == NULL
)
367 pFirst
= pDescriptor
;
370 aSelectionBox
.Union (mrSlideSorter
.GetView().GetPageBoundingBox (
372 view::SlideSorterView::CS_MODEL
,
373 view::SlideSorterView::BBT_INFO
));
378 // Determine scroll direction and the position in model coordinates
379 // that will be aligned with the top/left or bottom/right window
381 if (DoesSelectionExceedVisibleArea(aSelectionBox
))
383 // We can show only a part of the selection.
384 aSelectionBox
= ResolveLargeSelection(pFirst
,pLast
, eSelectionHint
);
387 return MakeRectangleVisible(aSelectionBox
);
396 Size
SelectionManager::MakeRectangleVisible (const Rectangle
& rBox
)
398 ::sd::Window
* pWindow
= mrSlideSorter
.GetActiveWindow();
402 Rectangle
aVisibleArea (pWindow
->PixelToLogic(
405 pWindow
->GetOutputSizePixel())));
407 if (mrSlideSorter
.GetView().GetOrientation() == SlideSorterView::VERTICAL
)
409 // Scroll the visible area to make aSelectionBox visible.
410 sal_Int32
nNewTop (aVisibleArea
.Top());
411 if (mrSlideSorter
.GetController().GetProperties()->IsCenterSelection())
413 nNewTop
= rBox
.Top() - (aVisibleArea
.GetHeight() - rBox
.GetHeight()) / 2;
417 if (rBox
.Top() < aVisibleArea
.Top())
418 nNewTop
= rBox
.Top();
419 else if (rBox
.Bottom() > aVisibleArea
.Bottom())
420 nNewTop
= rBox
.Bottom() - aVisibleArea
.GetHeight();
421 // otherwise we do not modify the visible area.
424 // Make some corrections of the new visible area.
425 Rectangle
aModelArea (mrSlideSorter
.GetView().GetModelArea());
426 if (nNewTop
+ aVisibleArea
.GetHeight() > aModelArea
.Bottom())
427 nNewTop
= aModelArea
.GetHeight() - aVisibleArea
.GetHeight();
428 if (nNewTop
< aModelArea
.Top())
429 nNewTop
= aModelArea
.Top();
432 if (nNewTop
!= aVisibleArea
.Top())
434 mrController
.GetAnimator()->AddAnimation(
435 VerticalVisibleAreaScroller(mrSlideSorter
, aVisibleArea
.Top(), nNewTop
),
436 mrSlideSorter
.GetController().GetProperties()->IsSmoothSelectionScrolling() ?
441 return Size(0,aVisibleArea
.Top() - nNewTop
);
445 // Scroll the visible area to make aSelectionBox visible.
446 sal_Int32
nNewLeft (aVisibleArea
.Left());
447 if (mrSlideSorter
.GetController().GetProperties()->IsCenterSelection())
449 nNewLeft
= rBox
.Left() - (aVisibleArea
.GetWidth() - rBox
.GetWidth()) / 2;
453 if (rBox
.Left() < aVisibleArea
.Left())
454 nNewLeft
= rBox
.Left();
455 else if (rBox
.Right() > aVisibleArea
.Right())
456 nNewLeft
= rBox
.Right() - aVisibleArea
.GetWidth();
457 // otherwise we do not modify the visible area.
460 // Make some corrections of the new visible area.
461 Rectangle
aModelArea (mrSlideSorter
.GetView().GetModelArea());
462 if (nNewLeft
+ aVisibleArea
.GetWidth() > aModelArea
.Right())
463 nNewLeft
= aModelArea
.GetWidth() - aVisibleArea
.GetWidth();
464 if (nNewLeft
< aModelArea
.Left())
465 nNewLeft
= aModelArea
.Left();
468 if (nNewLeft
!= aVisibleArea
.Left())
470 mrController
.GetAnimator()->AddAnimation(
471 HorizontalVisibleAreaScroller(mrSlideSorter
, aVisibleArea
.Left(), nNewLeft
),
472 mrSlideSorter
.GetController().GetProperties()->IsSmoothSelectionScrolling() ?
477 return Size(aVisibleArea
.Left() - nNewLeft
, 0);
484 void SelectionManager::AddSelectionChangeListener (const Link
& rListener
)
487 maSelectionChangeListeners
.begin(),
488 maSelectionChangeListeners
.end(),
489 rListener
) == maSelectionChangeListeners
.end())
491 maSelectionChangeListeners
.push_back (rListener
);
498 void SelectionManager::RemoveSelectionChangeListener(const Link
&rListener
)
500 maSelectionChangeListeners
.erase (
502 maSelectionChangeListeners
.begin(),
503 maSelectionChangeListeners
.end(),
510 bool SelectionManager::DoesSelectionExceedVisibleArea (const Rectangle
& rSelectionBox
) const
512 ::sd::Window
* pWindow
= mrSlideSorter
.GetActiveWindow();
516 Rectangle
aVisibleArea (pWindow
->PixelToLogic(
519 pWindow
->GetOutputSizePixel())));
520 if (mrSlideSorter
.GetView().GetOrientation() == SlideSorterView::VERTICAL
)
521 return rSelectionBox
.GetHeight() > aVisibleArea
.GetHeight();
523 return rSelectionBox
.GetWidth() > aVisibleArea
.GetWidth();
529 Rectangle
SelectionManager::ResolveLargeSelection (
530 const SharedPageDescriptor
& rpFirst
,
531 const SharedPageDescriptor
& rpLast
,
532 const SelectionHint eSelectionHint
)
534 OSL_ASSERT(rpFirst
.get()!=NULL
);
535 OSL_ASSERT(rpLast
.get()!=NULL
);
537 // The mose recently selected page is assumed to lie in the range
538 // between first and last selected page. Therefore the bounding box is
540 model::SharedPageDescriptor
pRecent (
541 mrController
.GetPageSelector().GetMostRecentlySelectedPage());
543 // Get the bounding box of the page object on which to concentrate.
544 model::SharedPageDescriptor pRepresentative
;
545 switch (eSelectionHint
)
548 pRepresentative
= rpFirst
;
552 pRepresentative
= rpLast
;
557 if (pRecent
.get() == NULL
)
558 pRepresentative
= rpFirst
;
560 pRepresentative
= pRecent
;
563 OSL_ASSERT(pRepresentative
.get() != NULL
);
565 return mrSlideSorter
.GetView().GetPageBoundingBox (
567 view::SlideSorterView::CS_MODEL
,
568 view::SlideSorterView::BBT_INFO
);
574 sal_Int32
SelectionManager::GetInsertionPosition (void) const
576 sal_Int32
nInsertionPosition (mnInsertionPosition
);
577 if (nInsertionPosition
< 0)
579 model::PageEnumeration aSelectedPages
580 (model::PageEnumerationProvider::CreateSelectedPagesEnumeration(
581 mrSlideSorter
.GetModel()));
582 // Initialize (for the case of an empty selection) with the position
583 // at the end of the document.
584 nInsertionPosition
= mrSlideSorter
.GetModel().GetPageCount();
585 while (aSelectedPages
.HasMoreElements())
587 const sal_Int32
nPosition (aSelectedPages
.GetNextElement()->GetPage()->GetPageNum());
588 // Convert *2+1 index to straight index (n-1)/2 after the page
590 nInsertionPosition
= (nPosition
-1)/2 + 1;
594 return nInsertionPosition
;
600 void SelectionManager::SetInsertionPosition (const sal_Int32 nInsertionPosition
)
602 if (nInsertionPosition
< 0)
603 mnInsertionPosition
= -1;
604 else if (nInsertionPosition
> mrSlideSorter
.GetModel().GetPageCount())
606 // Assert but then ignore invalid values.
607 OSL_ASSERT(nInsertionPosition
<=mrSlideSorter
.GetModel().GetPageCount());
611 mnInsertionPosition
= nInsertionPosition
;
617 //===== VerticalVisibleAreaScroller ===========================================
621 VerticalVisibleAreaScroller::VerticalVisibleAreaScroller (
622 SlideSorter
& rSlideSorter
,
625 : mrSlideSorter(rSlideSorter
),
633 void VerticalVisibleAreaScroller::operator() (const double nValue
)
635 mrSlideSorter
.GetView().InvalidatePageObjectVisibilities();
636 mrSlideSorter
.GetController().GetScrollBarManager().SetTop(
637 int(mnStart
* (1.0 - nValue
) + mnEnd
* nValue
));
643 HorizontalVisibleAreaScroller::HorizontalVisibleAreaScroller (
644 SlideSorter
& rSlideSorter
,
647 : mrSlideSorter(rSlideSorter
),
656 void HorizontalVisibleAreaScroller::operator() (const double nValue
)
658 mrSlideSorter
.GetView().InvalidatePageObjectVisibilities();
659 mrSlideSorter
.GetController().GetScrollBarManager().SetLeft(
660 int(mnStart
* (1.0 - nValue
) + mnEnd
* nValue
));
663 } // end of anonymous namespace
665 } } } // end of namespace ::sd::slidesorter