1 #/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <SlideSorterViewShell.hxx>
21 #include <ViewShellImplementation.hxx>
22 
23 #include <SlideSorter.hxx>
24 #include <controller/SlideSorterController.hxx>
25 #include <controller/SlsClipboard.hxx>
26 #include <controller/SlsScrollBarManager.hxx>
27 #include <controller/SlsPageSelector.hxx>
28 #include <controller/SlsSlotManager.hxx>
29 #include <controller/SlsCurrentSlideManager.hxx>
30 #include <controller/SlsSelectionManager.hxx>
31 #include <view/SlideSorterView.hxx>
32 #include <view/SlsLayouter.hxx>
33 #include <model/SlideSorterModel.hxx>
34 #include <model/SlsPageDescriptor.hxx>
35 #include <framework/FrameworkHelper.hxx>
36 #include <ViewShellBase.hxx>
37 #include <drawdoc.hxx>
38 #include <sdpage.hxx>
39 #include <app.hrc>
40 #include <AccessibleSlideSorterView.hxx>
41 #include <DrawDocShell.hxx>
42 #include <DrawViewShell.hxx>
43 #include <FrameView.hxx>
44 #include <SdUnoSlideView.hxx>
45 #include <ViewShellManager.hxx>
46 #include <Window.hxx>
47 #include <drawview.hxx>
48 #include <sfx2/msg.hxx>
49 #include <sfx2/objface.hxx>
50 #include <sfx2/viewfrm.hxx>
51 #include <sfx2/bindings.hxx>
52 #include <sfx2/request.hxx>
53 #include <sfx2/sidebar/SidebarChildWindow.hxx>
54 #include <sfx2/devtools/DevelopmentToolChildWindow.hxx>
55 #include <svx/svxids.hrc>
56 #include <vcl/EnumContext.hxx>
57 #include <svx/sidebar/ContextChangeEventMultiplexer.hxx>
58 #include <tools/diagnose_ex.h>
59 #include <sfx2/sidebar/SidebarController.hxx>
60 
61 using namespace ::sd::slidesorter;
62 #define ShellClass_SlideSorterViewShell
63 #include <sdslots.hxx>
64 
65 using namespace ::com::sun::star;
66 using namespace ::com::sun::star::uno;
67 using namespace ::com::sun::star::drawing::framework;
68 
69 using ::sd::framework::FrameworkHelper;
70 using ::vcl::EnumContext;
71 using namespace sfx2::sidebar;
72 
73 namespace sd::slidesorter {
74 
75 namespace {
76 
77 bool inChartContext(sd::View* pView)
78 {
79     if (!pView)
80         return false;
81 
82     SfxViewShell* pViewShell = pView->GetSfxViewShell();
83     SidebarController* pSidebar = SidebarController::GetSidebarControllerForView(pViewShell);
84     if (pSidebar)
85         return pSidebar->hasChartContextCurrently();
86 
87     return false;
88 }
89 
90 } // anonymous namespace
91 
92 
93 SFX_IMPL_INTERFACE(SlideSorterViewShell, SfxShell)
94 
95 void SlideSorterViewShell::InitInterface_Impl()
96 {
97     GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
98     GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId());
99 }
100 
101 
102 std::shared_ptr<SlideSorterViewShell> SlideSorterViewShell::Create (
103     SfxViewFrame* pFrame,
104     ViewShellBase& rViewShellBase,
105     vcl::Window* pParentWindow,
106     FrameView* pFrameViewArgument)
107 {
108     std::shared_ptr<SlideSorterViewShell> pViewShell;
109     try
110     {
111         pViewShell.reset(
112             new SlideSorterViewShell(pFrame,rViewShellBase,pParentWindow,pFrameViewArgument));
113         pViewShell->Initialize();
114         if (pViewShell->mpSlideSorter == nullptr)
115             pViewShell.reset();
116     }
117     catch(Exception&)
118     {
119         pViewShell.reset();
120     }
121     return pViewShell;
122 }
123 
124 SlideSorterViewShell::SlideSorterViewShell (
125     SfxViewFrame* /*pFrame*/,
126     ViewShellBase& rViewShellBase,
127     vcl::Window* pParentWindow,
128     FrameView* pFrameViewArgument)
129     : ViewShell (pParentWindow, rViewShellBase),
130       mpSlideSorter(),
131       mbIsArrangeGUIElementsPending(true)
132 {
133     GetContentWindow()->set_id("slidesorter");
134     meShellType = ST_SLIDE_SORTER;
135 
136     if (pFrameViewArgument != nullptr)
137         mpFrameView = pFrameViewArgument;
138     else
139         mpFrameView = new FrameView(GetDoc());
140     GetFrameView()->Connect();
141 
142     SetName ("SlideSorterViewShell");
143 
144     pParentWindow->SetStyle(pParentWindow->GetStyle() | WB_DIALOGCONTROL);
145 }
146 
147 SlideSorterViewShell::~SlideSorterViewShell()
148 {
149     DisposeFunctions();
150 
151     try
152     {
153         ::sd::Window* pWindow = GetActiveWindow();
154         if (pWindow!=nullptr)
155         {
156             css::uno::Reference<css::lang::XComponent> xComponent (
157                     pWindow->GetAccessible(false),
158                     css::uno::UNO_QUERY);
159             if (xComponent.is())
160                 xComponent->dispose();
161         }
162     }
163     catch( css::uno::Exception& )
164     {
165         TOOLS_WARN_EXCEPTION( "sd", "sd::SlideSorterViewShell::~SlideSorterViewShell()" );
166     }
167     GetFrameView()->Disconnect();
168 }
169 
170 void SlideSorterViewShell::Initialize()
171 {
172     mpSlideSorter = SlideSorter::CreateSlideSorter(
173         *this,
174         mpContentWindow,
175         mpHorizontalScrollBar,
176         mpVerticalScrollBar,
177         mpScrollBarBox);
178     mpView = &mpSlideSorter->GetView();
179 
180     doShow();
181 
182     SetPool( &GetDoc()->GetPool() );
183     SetUndoManager( GetDoc()->GetDocSh()->GetUndoManager() );
184 
185     // For accessibility we have to shortly hide the content window.
186     // This triggers the construction of a new accessibility object for
187     // the new view shell.  (One is created earlier while the constructor
188     // of the base class is executed.  At that time the correct
189     // accessibility object can not be constructed.)
190     sd::Window *pWindow (mpSlideSorter->GetContentWindow().get());
191     if (pWindow)
192     {
193         pWindow->Hide();
194         pWindow->Show();
195     }
196 }
197 
198 void SlideSorterViewShell::Init (bool bIsMainViewShell)
199 {
200     ViewShell::Init(bIsMainViewShell);
201 
202     // since the updatePageList will show focus, the window.show() must be called ahead. This show is deferred from Init()
203     ::sd::Window* pActiveWindow = GetActiveWindow();
204     if (pActiveWindow)
205         pActiveWindow->Show();
206     mpSlideSorter->GetModel().UpdatePageList();
207 
208     if (mpContentWindow)
209         mpContentWindow->SetViewShell(this);
210 }
211 
212 SlideSorterViewShell* SlideSorterViewShell::GetSlideSorter (ViewShellBase& rBase)
213 {
214     SlideSorterViewShell* pViewShell = nullptr;
215 
216     // Test the center and left pane for showing a slide sorter.
217     OUString aPaneURLs[] = {
218         FrameworkHelper::msCenterPaneURL,
219         FrameworkHelper::msFullScreenPaneURL,
220         FrameworkHelper::msLeftImpressPaneURL,
221         FrameworkHelper::msLeftDrawPaneURL,
222         OUString()};
223 
224     try
225     {
226         std::shared_ptr<FrameworkHelper> pFrameworkHelper (FrameworkHelper::Instance(rBase));
227         if (pFrameworkHelper->IsValid())
228             for (int i=0; pViewShell==nullptr && !aPaneURLs[i].isEmpty(); ++i)
229             {
230                 pViewShell = dynamic_cast<SlideSorterViewShell*>(
231                     pFrameworkHelper->GetViewShell(aPaneURLs[i]).get());
232             }
233     }
234     catch (RuntimeException&)
235     {}
236 
237     return pViewShell;
238 }
239 
240 Reference<drawing::XDrawSubController> SlideSorterViewShell::CreateSubController()
241 {
242     Reference<drawing::XDrawSubController> xSubController;
243 
244     if (IsMainViewShell())
245     {
246         // Create uno controller for the main view shell.
247         xSubController.set( new SdUnoSlideView( *mpSlideSorter));
248     }
249 
250     return xSubController;
251 }
252 
253 /** If there is a valid controller then create a new instance of
254     <type>AccessibleSlideSorterView</type>.  Otherwise delegate this call
255     to the base class to return a default object (probably an empty
256     reference).
257 */
258 css::uno::Reference<css::accessibility::XAccessible>
259     SlideSorterViewShell::CreateAccessibleDocumentView (::sd::Window* pWindow)
260 {
261     // When the view is not set then the initialization is not yet complete
262     // and we can not yet provide an accessibility object.
263     if (mpView == nullptr || mpSlideSorter == nullptr)
264         return nullptr;
265 
266     assert(mpSlideSorter);
267 
268     rtl::Reference<::accessibility::AccessibleSlideSorterView> pAccessibleView =
269     new ::accessibility::AccessibleSlideSorterView(
270         *mpSlideSorter,
271         pWindow);
272 
273     pAccessibleView->Init();
274 
275     return pAccessibleView;
276 }
277 
278 void SlideSorterViewShell::SwitchViewFireFocus(const css::uno::Reference< css::accessibility::XAccessible >& xAcc )
279 {
280     if (xAcc)
281     {
282         ::accessibility::AccessibleSlideSorterView* pBase = static_cast< ::accessibility::AccessibleSlideSorterView* >(xAcc.get());
283         if (pBase)
284         {
285             pBase->SwitchViewActivated();
286         }
287     }
288 }
289 
290 SlideSorter& SlideSorterViewShell::GetSlideSorter() const
291 {
292     assert(mpSlideSorter);
293     return *mpSlideSorter;
294 }
295 
296 bool SlideSorterViewShell::RelocateToParentWindow (vcl::Window* pParentWindow)
297 {
298     assert(mpSlideSorter);
299     if ( ! mpSlideSorter)
300         return false;
301 
302     mpSlideSorter->RelocateToWindow(pParentWindow);
303     ReadFrameViewData(mpFrameView);
304 
305     return true;
306 }
307 
308 SfxUndoManager* SlideSorterViewShell::ImpGetUndoManager() const
309 {
310     SfxShell* pObjectBar = GetViewShellBase().GetViewShellManager()->GetTopShell();
311     if (pObjectBar != nullptr)
312     {
313         // When it exists then return the undo manager of the currently
314         // active object bar.  The object bar is missing when the
315         // SlideSorterViewShell is not the main view shell.
316         return pObjectBar->GetUndoManager();
317     }
318     else
319     {
320         // Return the undo manager of this  shell when there is no object or
321         // tool bar.
322         return const_cast<SlideSorterViewShell*>(this)->GetUndoManager();
323     }
324 }
325 
326 SdPage* SlideSorterViewShell::getCurrentPage() const
327 {
328     // since SlideSorterViewShell::GetActualPage() currently also
329     // returns master pages, which is a wrong behaviour for GetActualPage(),
330     // we can just use that for now
331     return const_cast<SlideSorterViewShell*>(this)->GetActualPage();
332 }
333 
334 SdPage* SlideSorterViewShell::GetActualPage()
335 {
336     SdPage* pCurrentPage = nullptr;
337 
338     // 1. Try to get the current page from the view shell in the center pane
339     // (if we are that not ourself).
340     if ( ! IsMainViewShell())
341     {
342         std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell();
343         if (pMainViewShell != nullptr)
344             pCurrentPage = pMainViewShell->GetActualPage();
345     }
346 
347     if (pCurrentPage == nullptr)
348     {
349         model::SharedPageDescriptor pDescriptor (
350             mpSlideSorter->GetController().GetCurrentSlideManager()->GetCurrentSlide());
351         if (pDescriptor)
352             pCurrentPage = pDescriptor->GetPage();
353     }
354 
355     return pCurrentPage;
356 }
357 
358 void SlideSorterViewShell::GetMenuState ( SfxItemSet& rSet)
359 {
360     ViewShell::GetMenuState(rSet);
361     assert(mpSlideSorter);
362     mpSlideSorter->GetController().GetSlotManager()->GetMenuState(rSet);
363 }
364 
365 void SlideSorterViewShell::GetClipboardState ( SfxItemSet& rSet)
366 {
367     ViewShell::GetMenuState(rSet);
368     assert(mpSlideSorter);
369     mpSlideSorter->GetController().GetSlotManager()->GetClipboardState(rSet);
370 }
371 
372 void SlideSorterViewShell::ExecCtrl (SfxRequest& rRequest)
373 {
374     assert(mpSlideSorter);
375     mpSlideSorter->GetController().ExecCtrl(rRequest);
376 }
377 
378 void SlideSorterViewShell::GetCtrlState (SfxItemSet& rSet)
379 {
380     assert(mpSlideSorter);
381     mpSlideSorter->GetController().GetCtrlState(rSet);
382 }
383 
384 void SlideSorterViewShell::FuSupport (SfxRequest& rRequest)
385 {
386     assert(mpSlideSorter);
387     mpSlideSorter->GetController().FuSupport(rRequest);
388 }
389 
390 /** We have to handle those slot calls here that need to have access to
391     private or protected members and methods of this class.
392 */
393 void SlideSorterViewShell::FuTemporary (SfxRequest& rRequest)
394 {
395     assert(mpSlideSorter);
396     switch (rRequest.GetSlot())
397     {
398         case SID_MODIFYPAGE:
399         {
400             SdPage* pCurrentPage = GetActualPage();
401             if (pCurrentPage != nullptr)
402                 mpImpl->ProcessModifyPageSlot (
403                     rRequest,
404                     pCurrentPage,
405                     PageKind::Standard);
406             Cancel();
407             rRequest.Done ();
408         }
409         break;
410 
411         default:
412             mpSlideSorter->GetController().FuTemporary(rRequest);
413             break;
414     }
415 }
416 
417 void SlideSorterViewShell::GetStatusBarState (SfxItemSet& rSet)
418 {
419     assert(mpSlideSorter);
420     mpSlideSorter->GetController().GetStatusBarState(rSet);
421 }
422 
423 void SlideSorterViewShell::FuPermanent (SfxRequest& rRequest)
424 {
425     assert(mpSlideSorter);
426     mpSlideSorter->GetController().FuPermanent(rRequest);
427 }
428 
429 void SlideSorterViewShell::GetAttrState (SfxItemSet& rSet)
430 {
431     assert(mpSlideSorter);
432     mpSlideSorter->GetController().GetAttrState(rSet);
433 }
434 
435 void SlideSorterViewShell::ExecStatusBar (SfxRequest& )
436 {
437 }
438 
439 void SlideSorterViewShell::Paint (
440     const ::tools::Rectangle& rBBox,
441     ::sd::Window* pWindow)
442 {
443     SetActiveWindow (pWindow);
444     assert(mpSlideSorter);
445     if (mpSlideSorter)
446         mpSlideSorter->GetController().Paint(rBBox,pWindow);
447 }
448 
449 void SlideSorterViewShell::ArrangeGUIElements()
450 {
451     if (IsActive())
452     {
453         assert(mpSlideSorter);
454         mpSlideSorter->ArrangeGUIElements(maViewPos, maViewSize);
455         mbIsArrangeGUIElementsPending = false;
456     }
457     else
458         mbIsArrangeGUIElementsPending = true;
459 }
460 
461 void SlideSorterViewShell::Activate (bool bIsMDIActivate)
462 {
463     if(inChartContext(GetView()))
464     {
465         // Avoid context changes for chart during activation / deactivation.
466         const bool bIsContextBroadcasterEnabled (SfxShell::SetContextBroadcasterEnabled(false));
467 
468         ViewShell::Activate(bIsMDIActivate);
469 
470         SfxShell::SetContextBroadcasterEnabled(bIsContextBroadcasterEnabled);
471         return;
472     }
473 
474     ViewShell::Activate(bIsMDIActivate);
475     if (mbIsArrangeGUIElementsPending)
476         ArrangeGUIElements();
477 
478     // Determine and broadcast the context that belongs to the main view shell.
479     EnumContext::Context eContext = EnumContext::Context::Unknown;
480     std::shared_ptr<ViewShell> pMainViewShell (GetViewShellBase().GetMainViewShell());
481     ViewShell::ShellType eMainViewShellType (
482         pMainViewShell
483             ? pMainViewShell->GetShellType()
484             : ViewShell::ST_NONE);
485     switch (eMainViewShellType)
486     {
487         case ViewShell::ST_IMPRESS:
488         case ViewShell::ST_SLIDE_SORTER:
489         case ViewShell::ST_NOTES:
490         case ViewShell::ST_DRAW:
491             eContext = EnumContext::Context::DrawPage;
492             if( nullptr != dynamic_cast< const DrawViewShell *>( pMainViewShell.get() ))
493             {
494                 DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get());
495                 if (pDrawViewShell != nullptr)
496                     eContext = EnumContext::GetContextEnum(pDrawViewShell->GetSidebarContextName());
497             }
498             break;
499 
500         default:
501             break;
502     }
503     ContextChangeEventMultiplexer::NotifyContextChange(
504         &GetViewShellBase(),
505         eContext);
506 }
507 
508 void SlideSorterViewShell::Deactivate (bool /*bIsMDIActivate*/)
509 {
510     // Save Settings - Specifically SlidesPerRow to retrieve it later
511     WriteFrameViewData();
512 }
513 
514 void SlideSorterViewShell::Command (
515     const CommandEvent& rEvent,
516     ::sd::Window* pWindow)
517 {
518     assert(mpSlideSorter);
519     if ( ! mpSlideSorter->GetController().Command (rEvent, pWindow))
520         ViewShell::Command (rEvent, pWindow);
521 }
522 
523 void SlideSorterViewShell::ReadFrameViewData (FrameView* pFrameView)
524 {
525     assert(mpSlideSorter);
526     if (pFrameView != nullptr)
527     {
528         view::SlideSorterView& rView (mpSlideSorter->GetView());
529 
530         sal_uInt16 nSlidesPerRow (pFrameView->GetSlidesPerRow());
531         if (nSlidesPerRow > 0
532             && rView.GetOrientation() == view::Layouter::GRID
533             && IsMainViewShell())
534         {
535             rView.GetLayouter().SetColumnCount(nSlidesPerRow,nSlidesPerRow);
536         }
537         if (IsMainViewShell())
538             mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
539                 mpFrameView->GetSelectedPage());
540         mpSlideSorter->GetController().Rearrange(true);
541 
542         // DrawMode for 'main' window
543         if (GetActiveWindow()->GetOutDev()->GetDrawMode() != pFrameView->GetDrawMode() )
544             GetActiveWindow()->GetOutDev()->SetDrawMode( pFrameView->GetDrawMode() );
545     }
546 
547     // When this slide sorter is not displayed in the main window then we do
548     // not share the same frame view and have to find other ways to acquire
549     // certain values.
550     if ( ! IsMainViewShell())
551     {
552         std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell();
553         if (pMainViewShell != nullptr)
554             mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(
555                 pMainViewShell->getCurrentPage());
556     }
557 }
558 
559 void SlideSorterViewShell::WriteFrameViewData()
560 {
561     assert(mpSlideSorter);
562     if (mpFrameView == nullptr)
563         return;
564 
565     view::SlideSorterView& rView (mpSlideSorter->GetView());
566     mpFrameView->SetSlidesPerRow(static_cast<sal_uInt16>(rView.GetLayouter().GetColumnCount()));
567 
568     // DrawMode for 'main' window
569     if( mpFrameView->GetDrawMode() != GetActiveWindow()->GetOutDev()->GetDrawMode() )
570         mpFrameView->SetDrawMode( GetActiveWindow()->GetOutDev()->GetDrawMode() );
571 
572     SdPage* pActualPage = GetActualPage();
573     if (pActualPage != nullptr)
574     {
575         if (IsMainViewShell())
576             mpFrameView->SetSelectedPage((pActualPage->GetPageNum()- 1) / 2);
577         // else
578         // The slide sorter is not expected to switch the current page
579         // other than by double clicks.  That is handled separately.
580     }
581     else
582     {
583         // We have no current page to set but at least we can make sure
584         // that the index of the frame view has a legal value.
585         if (mpFrameView->GetSelectedPage() >= mpSlideSorter->GetModel().GetPageCount())
586             mpFrameView->SetSelectedPage(static_cast<sal_uInt16>(mpSlideSorter->GetModel().GetPageCount())-1);
587     }
588 }
589 
590 void SlideSorterViewShell::SetZoom (::tools::Long )
591 {
592     // Ignored.
593     // The zoom scale is adapted internally to fit a number of columns in
594     // the window.
595 }
596 
597 void SlideSorterViewShell::SetZoomRect (const ::tools::Rectangle& rZoomRect)
598 {
599     assert(mpSlideSorter);
600     Size aPageSize (mpSlideSorter->GetView().GetLayouter().GetPageObjectSize());
601 
602     ::tools::Rectangle aRect(rZoomRect);
603 
604     if (aRect.GetWidth()  < aPageSize.Width())
605     {
606         ::tools::Long nWidthDiff  = (aPageSize.Width() - aRect.GetWidth()) / 2;
607 
608         aRect.AdjustLeft( -nWidthDiff );
609         aRect.AdjustRight(nWidthDiff );
610 
611         if (aRect.Left() < 0)
612         {
613             aRect.SetPos(Point(0, aRect.Top()));
614         }
615     }
616 
617     if (aRect.GetHeight()  < aPageSize.Height())
618     {
619         ::tools::Long nHeightDiff  = (aPageSize.Height() - aRect.GetHeight()) / 2;
620 
621         aRect.AdjustTop( -nHeightDiff );
622         aRect.AdjustBottom(nHeightDiff );
623 
624         if (aRect.Top() < 0)
625         {
626             aRect.SetPos(Point(aRect.Left(), 0));
627         }
628     }
629 
630     ViewShell::SetZoomRect(aRect);
631 
632     GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM );
633     GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER );
634 }
635 
636 void SlideSorterViewShell::UpdateScrollBars()
637 {
638     // Do not call the overwritten method of the base class: We do all the
639     // scroll bar setup by ourselves.
640     mpSlideSorter->GetController().GetScrollBarManager().UpdateScrollBars(true);
641 }
642 
643 void SlideSorterViewShell::StartDrag (
644     const Point& rDragPt,
645     vcl::Window* pWindow )
646 {
647     assert(mpSlideSorter);
648     mpSlideSorter->GetController().GetClipboard().StartDrag (
649         rDragPt,
650         pWindow);
651 }
652 
653 sal_Int8 SlideSorterViewShell::AcceptDrop (
654     const AcceptDropEvent& rEvt,
655     DropTargetHelper& rTargetHelper,
656     ::sd::Window* pTargetWindow,
657     sal_uInt16 nPage,
658     SdrLayerID nLayer)
659 {
660     assert(mpSlideSorter);
661     return mpSlideSorter->GetController().GetClipboard().AcceptDrop (
662         rEvt,
663         rTargetHelper,
664         pTargetWindow,
665         nPage,
666         nLayer);
667 }
668 
669 sal_Int8 SlideSorterViewShell::ExecuteDrop (
670     const ExecuteDropEvent& rEvt,
671     DropTargetHelper& rTargetHelper,
672     ::sd::Window* pTargetWindow,
673     sal_uInt16 nPage,
674     SdrLayerID nLayer)
675 {
676     assert(mpSlideSorter);
677     return mpSlideSorter->GetController().GetClipboard().ExecuteDrop (
678         rEvt,
679         rTargetHelper,
680         pTargetWindow,
681         nPage,
682         nLayer);
683 }
684 
685 std::shared_ptr<SlideSorterViewShell::PageSelection>
686     SlideSorterViewShell::GetPageSelection() const
687 {
688     assert(mpSlideSorter);
689     return mpSlideSorter->GetController().GetPageSelector().GetPageSelection();
690 }
691 
692 void SlideSorterViewShell::SetPageSelection (
693     const std::shared_ptr<PageSelection>& rSelection)
694 {
695     assert(mpSlideSorter);
696     mpSlideSorter->GetController().GetPageSelector().SetPageSelection(rSelection, true);
697 }
698 
699 void SlideSorterViewShell::AddSelectionChangeListener (
700     const Link<LinkParamNone*,void>& rCallback)
701 {
702     assert(mpSlideSorter);
703     mpSlideSorter->GetController().GetSelectionManager()->AddSelectionChangeListener(rCallback);
704 }
705 
706 void SlideSorterViewShell::RemoveSelectionChangeListener (
707     const Link<LinkParamNone*,void>& rCallback)
708 {
709     assert(mpSlideSorter);
710     mpSlideSorter->GetController().GetSelectionManager()->RemoveSelectionChangeListener(rCallback);
711 }
712 
713 void SlideSorterViewShell::MainViewEndEditAndUnmarkAll()
714 {
715     std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell();
716     DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get());
717     SdrView* pView = pDrawViewShell ? pDrawViewShell->GetDrawView() : nullptr;
718     if (pView)
719     {
720         pView->SdrEndTextEdit();
721         pView->UnmarkAll();
722     }
723 }
724 
725 std::pair<sal_uInt16, sal_uInt16> SlideSorterViewShell::SyncPageSelectionToDocument(const std::shared_ptr<SlideSorterViewShell::PageSelection> &rpSelection)
726 {
727     sal_uInt16 firstSelectedPageNo = SAL_MAX_UINT16;
728     sal_uInt16 lastSelectedPageNo = 0;
729 
730     GetDoc()->UnselectAllPages();
731     for (auto& rpPage : *rpSelection)
732     {
733         // Check page number
734         sal_uInt16 pageNo = rpPage->GetPageNum();
735         if (pageNo > lastSelectedPageNo)
736             lastSelectedPageNo = pageNo;
737         if (pageNo < firstSelectedPageNo)
738             firstSelectedPageNo = pageNo;
739         GetDoc()->SetSelected(rpPage, true);
740     }
741 
742     return std::make_pair(firstSelectedPageNo, lastSelectedPageNo);
743 }
744 
745 void SlideSorterViewShell::ExecMovePageFirst (SfxRequest& /*rReq*/)
746 {
747     MainViewEndEditAndUnmarkAll();
748 
749     std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection());
750 
751     // SdDrawDocument MovePages is based on SdPage IsSelected, so
752     // transfer the SlideSorter selection to SdPages
753     SyncPageSelectionToDocument(xSelection);
754 
755     // Moves selected pages after page -1
756     GetDoc()->MovePages( sal_uInt16(-1) );
757 
758     PostMoveSlidesActions(xSelection);
759 }
760 
761 void SlideSorterViewShell::GetStateMovePageFirst (SfxItemSet& rSet)
762 {
763     if ( ! IsMainViewShell())
764     {
765         std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell();
766         DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get());
767         if (pDrawViewShell != nullptr && pDrawViewShell->GetPageKind() == PageKind::Handout)
768         {
769             rSet.DisableItem( SID_MOVE_PAGE_FIRST );
770             rSet.DisableItem( SID_MOVE_PAGE_UP );
771             return;
772         }
773     }
774 
775     std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection());
776 
777     // SdDrawDocument MovePages is based on SdPage IsSelected, so
778     // transfer the SlideSorter selection to SdPages
779     sal_uInt16 firstSelectedPageNo = SyncPageSelectionToDocument(xSelection).first;
780     // Now compute human page number from internal page number
781     firstSelectedPageNo = (firstSelectedPageNo - 1) / 2;
782 
783     if (firstSelectedPageNo == 0)
784     {
785         rSet.DisableItem( SID_MOVE_PAGE_FIRST );
786         rSet.DisableItem( SID_MOVE_PAGE_UP );
787     }
788 }
789 
790 void SlideSorterViewShell::ExecMovePageUp (SfxRequest& /*rReq*/)
791 {
792     MainViewEndEditAndUnmarkAll();
793 
794     std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection());
795 
796     // SdDrawDocument MovePages is based on SdPage IsSelected, so
797     // transfer the SlideSorter selection to SdPages
798     sal_uInt16 firstSelectedPageNo = SyncPageSelectionToDocument(xSelection).first;
799 
800     // In case no slide is selected
801     if (firstSelectedPageNo == SAL_MAX_UINT16)
802         return;
803 
804     // Now compute human page number from internal page number
805     firstSelectedPageNo = (firstSelectedPageNo - 1) / 2;
806 
807     if (firstSelectedPageNo == 0)
808         return;
809 
810     // Move pages before firstSelectedPageNo - 1 (so after firstSelectedPageNo - 2),
811     // remembering that -1 means at first, which is good.
812     GetDoc()->MovePages( firstSelectedPageNo - 2 );
813 
814     PostMoveSlidesActions(xSelection);
815 }
816 
817 void SlideSorterViewShell::GetStateMovePageUp (SfxItemSet& rSet)
818 {
819     GetStateMovePageFirst(rSet);
820 }
821 
822 void SlideSorterViewShell::ExecMovePageDown (SfxRequest& /*rReq*/)
823 {
824     MainViewEndEditAndUnmarkAll();
825 
826     std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection());
827 
828     // SdDrawDocument MovePages is based on SdPage IsSelected, so
829     // transfer the SlideSorter selection to SdPages
830     sal_uInt16 lastSelectedPageNo = SyncPageSelectionToDocument(xSelection).second;
831 
832     // Get page number of the last page
833     sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard);
834 
835     // Now compute human page number from internal page number
836     lastSelectedPageNo = (lastSelectedPageNo - 1) / 2;
837     if (lastSelectedPageNo == nNoOfPages - 1)
838         return;
839 
840     // Move to position after lastSelectedPageNo
841     GetDoc()->MovePages( lastSelectedPageNo + 1 );
842 
843     PostMoveSlidesActions(xSelection);
844 }
845 
846 void SlideSorterViewShell::GetStateMovePageDown (SfxItemSet& rSet)
847 {
848     GetStateMovePageLast( rSet );
849 }
850 
851 void SlideSorterViewShell::ExecMovePageLast (SfxRequest& /*rReq*/)
852 {
853     MainViewEndEditAndUnmarkAll();
854 
855     std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection());
856 
857     // SdDrawDocument MovePages is based on SdPage IsSelected, so
858     // transfer the SlideSorter selection to SdPages
859     SyncPageSelectionToDocument(xSelection);
860 
861     // Get page number of the last page
862     sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard);
863 
864     // Move to position after last page No (=Number of pages - 1)
865     GetDoc()->MovePages( nNoOfPages - 1 );
866 
867     PostMoveSlidesActions(xSelection);
868 }
869 
870 void SlideSorterViewShell::GetStateMovePageLast (SfxItemSet& rSet)
871 {
872     std::shared_ptr<ViewShell> pMainViewShell = GetViewShellBase().GetMainViewShell();
873     DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(pMainViewShell.get());
874     if (pDrawViewShell != nullptr && pDrawViewShell->GetPageKind() == PageKind::Handout)
875     {
876         rSet.DisableItem( SID_MOVE_PAGE_LAST );
877         rSet.DisableItem( SID_MOVE_PAGE_DOWN );
878         return;
879     }
880 
881     std::shared_ptr<SlideSorterViewShell::PageSelection> xSelection(GetPageSelection());
882 
883     // SdDrawDocument MovePages is based on SdPage IsSelected, so
884     // transfer the SlideSorter selection to SdPages
885     sal_uInt16 lastSelectedPageNo = SyncPageSelectionToDocument(xSelection).second;
886 
887     // Get page number of the last page
888     sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard);
889 
890     // Now compute human page number from internal page number
891     lastSelectedPageNo = (lastSelectedPageNo - 1) / 2;
892     if (lastSelectedPageNo == nNoOfPages - 1)
893     {
894         rSet.DisableItem( SID_MOVE_PAGE_LAST );
895         rSet.DisableItem( SID_MOVE_PAGE_DOWN );
896     }
897 }
898 
899 void SlideSorterViewShell::PostMoveSlidesActions(const std::shared_ptr<SlideSorterViewShell::PageSelection> &rpSelection)
900 {
901     sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard);
902     for (sal_uInt16 nPage = 0; nPage < nNoOfPages; nPage++)
903     {
904         SdPage* pPage = GetDoc()->GetSdPage(nPage, PageKind::Standard);
905         GetDoc()->SetSelected(pPage, false);
906     }
907 
908     mpSlideSorter->GetController().GetPageSelector().DeselectAllPages();
909     for (const auto& rpPage : *rpSelection)
910     {
911         mpSlideSorter->GetController().GetPageSelector().SelectPage(rpPage);
912     }
913 
914     // Refresh toolbar icons
915     SfxBindings& rBindings = GetViewFrame()->GetBindings();
916     rBindings.Invalidate(SID_MOVE_PAGE_FIRST);
917     rBindings.Invalidate(SID_MOVE_PAGE_UP);
918     rBindings.Invalidate(SID_MOVE_PAGE_DOWN);
919     rBindings.Invalidate(SID_MOVE_PAGE_LAST);
920 
921 }
922 
923 } // end of namespace ::sd::slidesorter
924 
925 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
926