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 <memory>
21 #include <sal/config.h>
22 
23 #include <controller/SlsSelectionFunction.hxx>
24 
25 #include <SlideSorter.hxx>
26 #include <SlideSorterViewShell.hxx>
27 #include "SlsDragAndDropContext.hxx"
28 #include <controller/SlideSorterController.hxx>
29 #include <controller/SlsPageSelector.hxx>
30 #include <controller/SlsFocusManager.hxx>
31 #include <controller/SlsScrollBarManager.hxx>
32 #include <controller/SlsClipboard.hxx>
33 #include <controller/SlsCurrentSlideManager.hxx>
34 #include <controller/SlsInsertionIndicatorHandler.hxx>
35 #include <controller/SlsSelectionManager.hxx>
36 #include <controller/SlsProperties.hxx>
37 #include <controller/SlsVisibleAreaManager.hxx>
38 #include <model/SlideSorterModel.hxx>
39 #include <model/SlsPageDescriptor.hxx>
40 #include <model/SlsPageEnumerationProvider.hxx>
41 #include <view/SlideSorterView.hxx>
42 #include <view/SlsLayouter.hxx>
43 #include <framework/FrameworkHelper.hxx>
44 #include <Window.hxx>
45 #include <sdpage.hxx>
46 #include <drawdoc.hxx>
47 #include <sdxfer.hxx>
48 #include <ViewShell.hxx>
49 #include <FrameView.hxx>
50 #include <app.hrc>
51 #include <o3tl/deleter.hxx>
52 #include <sfx2/dispatch.hxx>
53 #include <vcl/ptrstyle.hxx>
54 #include <boost/optional.hpp>
55 #include <sdmod.hxx>
56 
57 namespace {
58 static const sal_uInt32 SINGLE_CLICK             (0x00000001);
59 static const sal_uInt32 DOUBLE_CLICK             (0x00000002);
60 static const sal_uInt32 LEFT_BUTTON              (0x00000010);
61 static const sal_uInt32 RIGHT_BUTTON             (0x00000020);
62 static const sal_uInt32 MIDDLE_BUTTON            (0x00000040);
63 static const sal_uInt32 BUTTON_DOWN              (0x00000100);
64 static const sal_uInt32 BUTTON_UP                (0x00000200);
65 static const sal_uInt32 MOUSE_MOTION             (0x00000400);
66 static const sal_uInt32 MOUSE_DRAG               (0x00000800);
67 // The rest leaves the lower 16 bit untouched so that it can be used with
68 // key codes.
69 static const sal_uInt32 OVER_SELECTED_PAGE       (0x00010000);
70 static const sal_uInt32 OVER_UNSELECTED_PAGE     (0x00020000);
71 static const sal_uInt32 SHIFT_MODIFIER           (0x00200000);
72 static const sal_uInt32 CONTROL_MODIFIER         (0x00400000);
73 
74 // Some absent events are defined so they can be expressed explicitly.
75 static const sal_uInt32 NO_MODIFIER              (0x00000000);
76 static const sal_uInt32 NOT_OVER_PAGE            (0x00000000);
77 
78 // Masks
79 static const sal_uInt32 MODIFIER_MASK            (SHIFT_MODIFIER | CONTROL_MODIFIER);
80 
81 } // end of anonymous namespace
82 
83 // Define some macros to make the following switch statement more readable.
84 #define ANY_MODIFIER(code)                  \
85          code|NO_MODIFIER:                  \
86     case code|SHIFT_MODIFIER:               \
87     case code|CONTROL_MODIFIER
88 
89 namespace sd { namespace slidesorter { namespace controller {
90 
91 //===== SelectionFunction::EventDescriptor ====================================
92 
93 class SelectionFunction::EventDescriptor
94 {
95 public:
96     Point const maMousePosition;
97     Point maMouseModelPosition;
98     model::SharedPageDescriptor mpHitDescriptor;
99     SdrPage* mpHitPage;
100     sal_uInt32 mnEventCode;
101     InsertionIndicatorHandler::Mode const meDragMode;
102     bool mbIsLeaving;
103 
104     EventDescriptor (
105         sal_uInt32 nEventType,
106         const MouseEvent& rEvent,
107         SlideSorter const & rSlideSorter);
108     EventDescriptor (
109         sal_uInt32 nEventType,
110         const AcceptDropEvent& rEvent,
111         const sal_Int8 nDragAction,
112         SlideSorter const & rSlideSorter);
113 
114 private:
115     /** Compute a numerical code that describes a mouse event and that can
116         be used for fast look up of the appropriate reaction.
117     */
118     sal_uInt32 EncodeMouseEvent (const MouseEvent& rEvent) const;
119 
120     /** Compute a numerical code that describes the current state like
121         whether the selection rectangle is visible or whether the page under
122         the mouse or the one that has the focus is selected.
123     */
124     sal_uInt32 EncodeState() const;
125 };
126 
127 //===== SelectionFunction::ModeHandler ========================================
128 
129 class SelectionFunction::ModeHandler
130 {
131 public:
132     ModeHandler (
133         SlideSorter& rSlideSorter,
134         SelectionFunction& rSelectionFunction,
135         const bool bIsMouseOverIndicatorAllowed);
136     virtual ~ModeHandler() COVERITY_NOEXCEPT_FALSE;
137 
138     virtual Mode GetMode() const = 0;
139     virtual void Abort() = 0;
140     virtual void ProcessEvent (EventDescriptor& rDescriptor);
141 
142     /** Set the selection to exactly the specified page and also set it as
143         the current page.
144     */
145     void SetCurrentPage (const model::SharedPageDescriptor& rpDescriptor);
146 
147     /// Deselect all pages.
148     void DeselectAllPages();
149     void SelectOnePage (const model::SharedPageDescriptor& rpDescriptor);
150 
151         /** When the view on which this selection function is working is the
152         main view then the view is switched to the regular editing view.
153     */
154     void SwitchView (const model::SharedPageDescriptor& rpDescriptor);
155 
156     void StartDrag (
157         const Point& rMousePosition);
158 
IsMouseOverIndicatorAllowed() const159     bool IsMouseOverIndicatorAllowed() const { return mbIsMouseOverIndicatorAllowed;}
160 
161 protected:
162     SlideSorter& mrSlideSorter;
163     SelectionFunction& mrSelectionFunction;
164 
165     virtual bool ProcessButtonDownEvent (EventDescriptor& rDescriptor);
166     virtual bool ProcessButtonUpEvent (EventDescriptor& rDescriptor);
167     virtual bool ProcessMotionEvent (EventDescriptor& rDescriptor);
168     virtual bool ProcessDragEvent (EventDescriptor& rDescriptor);
169     virtual bool HandleUnprocessedEvent (EventDescriptor& rDescriptor);
170 
171     void ReprocessEvent (EventDescriptor& rDescriptor);
172 
173 private:
174     const bool mbIsMouseOverIndicatorAllowed;
175 };
176 
177 /** This is the default handler for processing events.  It activates the
178     multi selection or drag-and-drop when the right conditions are met.
179 */
180 class NormalModeHandler : public SelectionFunction::ModeHandler
181 {
182 public:
183     NormalModeHandler (
184         SlideSorter& rSlideSorter,
185         SelectionFunction& rSelectionFunction);
186 
187     virtual SelectionFunction::Mode GetMode() const override;
188     virtual void Abort() override;
189 
190     void ResetButtonDownLocation();
191 
192 protected:
193     virtual bool ProcessButtonDownEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
194     virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
195     virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
196     virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
197 
198 private:
199     ::boost::optional<Point> maButtonDownLocation;
200 
201     /** Select all pages between and including the selection anchor and the
202         specified page.
203     */
204     void RangeSelect (const model::SharedPageDescriptor& rpDescriptor);
205 };
206 
207 /** Handle events during a multi selection, which typically is started by
208     pressing the left mouse button when not over a page.
209 */
210 class MultiSelectionModeHandler : public SelectionFunction::ModeHandler
211 {
212 public:
213     /** Start a rectangle selection at the given position.
214     */
215     MultiSelectionModeHandler (
216         SlideSorter& rSlideSorter,
217         SelectionFunction& rSelectionFunction,
218         const Point& rMouseModelPosition,
219         const sal_uInt32 nEventCode);
220 
221     virtual ~MultiSelectionModeHandler() override;
222 
223     virtual SelectionFunction::Mode GetMode() const override;
224     virtual void Abort() override;
225     virtual void ProcessEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
226 
227     enum SelectionMode { SM_Normal, SM_Add, SM_Toggle };
228 
229     void SetSelectionMode (const SelectionMode eSelectionMode);
230     void SetSelectionModeFromModifier (const sal_uInt32 nEventCode);
231 
232 protected:
233     virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
234     virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
235     virtual bool HandleUnprocessedEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
236 
237 private:
238     SelectionMode meSelectionMode;
239     Point maSecondCorner;
240     PointerStyle const maSavedPointer;
241     bool mbAutoScrollInstalled;
242     sal_Int32 mnAnchorIndex;
243     sal_Int32 mnSecondIndex;
244 
245     void UpdateModelPosition (const Point& rMouseModelPosition);
246     void UpdateSelection();
247 
248     /** Update the rectangle selection so that the given position becomes
249         the new second point of the selection rectangle.
250     */
251     void UpdatePosition (
252         const Point& rMousePosition,
253         const bool bAllowAutoScroll);
254 
255     void UpdateSelectionState (
256         const model::SharedPageDescriptor& rpDescriptor,
257         const bool bIsInSelection) const;
258 };
259 
260 /** Handle events during drag-and-drop.
261 */
262 class DragAndDropModeHandler : public SelectionFunction::ModeHandler
263 {
264 public:
265     DragAndDropModeHandler (
266         SlideSorter& rSlideSorter,
267         SelectionFunction& rSelectionFunction,
268         const Point& rMousePosition,
269         vcl::Window* pWindow);
270     virtual ~DragAndDropModeHandler() override;
271 
272     virtual SelectionFunction::Mode GetMode() const override;
273     virtual void Abort() override;
274 
275 protected:
276     virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
277     virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override;
278 
279 private:
280     std::unique_ptr<DragAndDropContext, o3tl::default_delete<DragAndDropContext>> mpDragAndDropContext;
281 };
282 
283 //===== SelectionFunction =====================================================
284 
285 
SelectionFunction(SlideSorter & rSlideSorter,SfxRequest & rRequest)286 SelectionFunction::SelectionFunction (
287     SlideSorter& rSlideSorter,
288     SfxRequest& rRequest)
289     : FuPoor (
290         rSlideSorter.GetViewShell(),
291         rSlideSorter.GetContentWindow(),
292         &rSlideSorter.GetView(),
293         rSlideSorter.GetModel().GetDocument(),
294         rRequest),
295       mrSlideSorter(rSlideSorter),
296       mrController(mrSlideSorter.GetController()),
297       mnShiftKeySelectionAnchor(-1),
298       mpModeHandler(new NormalModeHandler(rSlideSorter, *this))
299 {
300 }
301 
~SelectionFunction()302 SelectionFunction::~SelectionFunction()
303 {
304     mpModeHandler.reset();
305 }
306 
Create(SlideSorter & rSlideSorter,SfxRequest & rRequest)307 rtl::Reference<FuPoor> SelectionFunction::Create(
308     SlideSorter& rSlideSorter,
309     SfxRequest& rRequest)
310 {
311     rtl::Reference<FuPoor> xFunc( new SelectionFunction( rSlideSorter, rRequest ) );
312     return xFunc;
313 }
314 
MouseButtonDown(const MouseEvent & rEvent)315 bool SelectionFunction::MouseButtonDown (const MouseEvent& rEvent)
316 {
317     // remember button state for creation of own MouseEvents
318     SetMouseButtonCode (rEvent.GetButtons());
319     aMDPos = rEvent.GetPosPixel();
320 
321     //  mpWindow->CaptureMouse();
322 
323     ProcessMouseEvent(BUTTON_DOWN, rEvent);
324 
325     return true;
326 }
327 
MouseMove(const MouseEvent & rEvent)328 bool SelectionFunction::MouseMove (const MouseEvent& rEvent)
329 {
330     ProcessMouseEvent(MOUSE_MOTION, rEvent);
331     return true;
332 }
333 
MouseButtonUp(const MouseEvent & rEvent)334 bool SelectionFunction::MouseButtonUp (const MouseEvent& rEvent)
335 {
336     mrController.GetScrollBarManager().StopAutoScroll ();
337 
338     ProcessMouseEvent(BUTTON_UP, rEvent);
339 
340     return true;
341 }
342 
NotifyDragFinished()343 void SelectionFunction::NotifyDragFinished()
344 {
345     SwitchToNormalMode();
346 }
347 
KeyInput(const KeyEvent & rEvent)348 bool SelectionFunction::KeyInput (const KeyEvent& rEvent)
349 {
350     view::SlideSorterView::DrawLock aDrawLock (mrSlideSorter);
351     PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter);
352     PageSelector::UpdateLock aLock (mrSlideSorter);
353     FocusManager& rFocusManager (mrController.GetFocusManager());
354     bool bResult = false;
355 
356     const vcl::KeyCode& rCode (rEvent.GetKeyCode());
357     switch (rCode.GetCode())
358     {
359         case KEY_RETURN:
360         {
361             model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor());
362             ViewShell* pViewShell = mrSlideSorter.GetViewShell();
363             if (rFocusManager.HasFocus() && pDescriptor && pViewShell!=nullptr)
364             {
365                 // The Return key triggers different functions depending on
366                 // whether the slide sorter is the main view or displayed in
367                 // the right pane.
368                 if (pViewShell->IsMainViewShell())
369                 {
370                     mpModeHandler->SetCurrentPage(pDescriptor);
371                     mpModeHandler->SwitchView(pDescriptor);
372                 }
373                 else if (pViewShell->GetDispatcher() != nullptr)
374                 {
375                     pViewShell->GetDispatcher()->Execute(
376                         SID_INSERTPAGE,
377                         SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
378                 }
379                 bResult = true;
380             }
381             break;
382         }
383 
384         case KEY_TAB:
385             if ( ! rFocusManager.IsFocusShowing())
386             {
387                 rFocusManager.ShowFocus();
388                 bResult = true;
389             }
390             break;
391 
392         case KEY_ESCAPE:
393             // When there is an active multiselection or drag-and-drop
394             // operation then stop that.
395             mpModeHandler->Abort();
396             SwitchToNormalMode();
397             bResult = true;
398             break;
399 
400         case KEY_SPACE:
401         {
402             // Toggle the selection state.
403             model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor());
404             if (pDescriptor && rCode.IsMod1())
405             {
406                 if (pDescriptor->HasState(model::PageDescriptor::ST_Selected))
407                     mrController.GetPageSelector().DeselectPage(pDescriptor, false);
408                 else
409                     mrController.GetPageSelector().SelectPage(pDescriptor);
410             }
411             bResult = true;
412         }
413         break;
414 
415         // Move the focus indicator left.
416         case KEY_LEFT:
417             MoveFocus(FocusManager::FocusMoveDirection::Left, rCode.IsShift(), rCode.IsMod1());
418             bResult = true;
419             break;
420 
421         // Move the focus indicator right.
422         case KEY_RIGHT:
423             MoveFocus(FocusManager::FocusMoveDirection::Right, rCode.IsShift(), rCode.IsMod1());
424             bResult = true;
425             break;
426 
427         // Move the focus indicator up.
428         case KEY_UP:
429             MoveFocus(FocusManager::FocusMoveDirection::Up, rCode.IsShift(), rCode.IsMod1());
430             bResult = true;
431             break;
432 
433         // Move the focus indicator down.
434         case KEY_DOWN:
435             MoveFocus(FocusManager::FocusMoveDirection::Down, rCode.IsShift(), rCode.IsMod1());
436             bResult = true;
437             break;
438 
439         // Go to previous page.  No wrap around.
440         case KEY_PAGEUP:
441             GotoNextPage(-1);
442             bResult = true;
443             break;
444 
445         // Go to next page.  No wrap around...
446         case KEY_PAGEDOWN:
447             GotoNextPage(+1);
448             bResult = true;
449             break;
450 
451         case KEY_HOME:
452             GotoPage(0);
453             bResult = true;
454             break;
455 
456         case KEY_END:
457             GotoPage(mrSlideSorter.GetModel().GetPageCount()-1);
458             bResult = true;
459             break;
460 
461         case KEY_DELETE:
462         case KEY_BACKSPACE:
463         {
464             if (mrSlideSorter.GetProperties()->IsUIReadOnly())
465                 break;
466 
467             mrController.GetSelectionManager()->DeleteSelectedPages(rCode.GetCode()==KEY_DELETE);
468 
469             mnShiftKeySelectionAnchor = -1;
470             bResult = true;
471         }
472         break;
473 
474         case KEY_F10:
475             if (rCode.IsShift())
476             {
477                 mpModeHandler->SelectOnePage(
478                     mrSlideSorter.GetController().GetFocusManager().GetFocusedPageDescriptor());
479             }
480             break;
481 
482         default:
483             break;
484     }
485 
486     if ( ! bResult)
487         bResult = FuPoor::KeyInput(rEvent);
488 
489     return bResult;
490 }
491 
MoveFocus(const FocusManager::FocusMoveDirection eDirection,const bool bIsShiftDown,const bool bIsControlDown)492 void SelectionFunction::MoveFocus (
493     const FocusManager::FocusMoveDirection eDirection,
494     const bool bIsShiftDown,
495     const bool bIsControlDown)
496 {
497     // Remember the anchor of shift key multi selection.
498     if (bIsShiftDown)
499     {
500         if (mnShiftKeySelectionAnchor<0)
501         {
502             model::SharedPageDescriptor pFocusedDescriptor (
503                 mrController.GetFocusManager().GetFocusedPageDescriptor());
504             mnShiftKeySelectionAnchor = pFocusedDescriptor->GetPageIndex();
505         }
506     }
507     else if ( ! bIsControlDown)
508         ResetShiftKeySelectionAnchor();
509 
510     mrController.GetFocusManager().MoveFocus(eDirection);
511 
512     PageSelector& rSelector (mrController.GetPageSelector());
513     model::SharedPageDescriptor pFocusedDescriptor (
514         mrController.GetFocusManager().GetFocusedPageDescriptor());
515     if (bIsShiftDown)
516     {
517         // When shift is pressed then select all pages in the range between
518         // the currently and the previously focused pages, including them.
519         if (pFocusedDescriptor)
520         {
521             sal_Int32 nPageRangeEnd (pFocusedDescriptor->GetPageIndex());
522             model::PageEnumeration aPages (
523                 model::PageEnumerationProvider::CreateAllPagesEnumeration(
524                     mrSlideSorter.GetModel()));
525             while (aPages.HasMoreElements())
526             {
527                 model::SharedPageDescriptor pDescriptor (aPages.GetNextElement());
528                 if (pDescriptor)
529                 {
530                     const sal_Int32 nPageIndex(pDescriptor->GetPageIndex());
531                     if ((nPageIndex>=mnShiftKeySelectionAnchor && nPageIndex<=nPageRangeEnd)
532                         || (nPageIndex<=mnShiftKeySelectionAnchor && nPageIndex>=nPageRangeEnd))
533                     {
534                         rSelector.SelectPage(pDescriptor);
535                     }
536                     else
537                     {
538                         rSelector.DeselectPage(pDescriptor);
539                     }
540                 }
541             }
542         }
543     }
544     else if (bIsControlDown)
545     {
546         // When control is pressed then do not alter the selection or the
547         // current page, just move the focus.
548     }
549     else
550     {
551         // Without shift just select the focused page.
552         mpModeHandler->SelectOnePage(pFocusedDescriptor);
553     }
554 }
555 
DoCut()556 void SelectionFunction::DoCut()
557 {
558     if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly())
559     {
560         mrController.GetClipboard().DoCut();
561     }
562 }
563 
DoCopy()564 void SelectionFunction::DoCopy()
565 {
566     mrController.GetClipboard().DoCopy();
567 }
568 
DoPaste()569 void SelectionFunction::DoPaste()
570 {
571     if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly())
572     {
573         mrController.GetClipboard().DoPaste();
574     }
575 }
576 
cancel()577 bool SelectionFunction::cancel()
578 {
579     mrController.GetFocusManager().ToggleFocus();
580     return true;
581 }
582 
GotoNextPage(int nOffset)583 void SelectionFunction::GotoNextPage (int nOffset)
584 {
585     model::SharedPageDescriptor pDescriptor
586         = mrController.GetCurrentSlideManager()->GetCurrentSlide();
587     if (pDescriptor.get() != nullptr)
588     {
589         SdPage* pPage = pDescriptor->GetPage();
590         OSL_ASSERT(pPage!=nullptr);
591         sal_Int32 nIndex = (pPage->GetPageNum()-1) / 2;
592         GotoPage(nIndex + nOffset);
593     }
594     ResetShiftKeySelectionAnchor();
595 }
596 
GotoPage(int nIndex)597 void SelectionFunction::GotoPage (int nIndex)
598 {
599     sal_uInt16 nPageCount = static_cast<sal_uInt16>(mrSlideSorter.GetModel().GetPageCount());
600 
601     if (nIndex >= nPageCount)
602         nIndex = nPageCount - 1;
603     if (nIndex < 0)
604         nIndex = 0;
605 
606     mrController.GetFocusManager().SetFocusedPage(nIndex);
607     model::SharedPageDescriptor pNextPageDescriptor (
608         mrSlideSorter.GetModel().GetPageDescriptor (nIndex));
609     if (pNextPageDescriptor.get() != nullptr)
610         mpModeHandler->SetCurrentPage(pNextPageDescriptor);
611     else
612     {
613         OSL_ASSERT(pNextPageDescriptor.get() != nullptr);
614     }
615     ResetShiftKeySelectionAnchor();
616 }
617 
ProcessMouseEvent(sal_uInt32 nEventType,const MouseEvent & rEvent)618 void SelectionFunction::ProcessMouseEvent (sal_uInt32 nEventType, const MouseEvent& rEvent)
619 {
620     // #95491# remember button state for creation of own MouseEvents
621     SetMouseButtonCode (rEvent.GetButtons());
622 
623     EventDescriptor aEventDescriptor (nEventType, rEvent, mrSlideSorter);
624     ProcessEvent(aEventDescriptor);
625 }
626 
MouseDragged(const AcceptDropEvent & rEvent,const sal_Int8 nDragAction)627 void SelectionFunction::MouseDragged (
628     const AcceptDropEvent& rEvent,
629     const sal_Int8 nDragAction)
630 {
631     EventDescriptor aEventDescriptor (MOUSE_DRAG, rEvent, nDragAction, mrSlideSorter);
632     ProcessEvent(aEventDescriptor);
633 }
634 
ProcessEvent(EventDescriptor & rDescriptor)635 void SelectionFunction::ProcessEvent (EventDescriptor& rDescriptor)
636 {
637     // The call to ProcessEvent may switch to another mode handler.
638     // Prevent the untimely destruction of the called handler  by acquiring a
639     // temporary reference here.
640     std::shared_ptr<ModeHandler> pModeHandler (mpModeHandler);
641     pModeHandler->ProcessEvent(rDescriptor);
642 }
643 
Match(const sal_uInt32 nEventCode,const sal_uInt32 nPositivePattern)644 static bool Match (
645     const sal_uInt32 nEventCode,
646     const sal_uInt32 nPositivePattern)
647 {
648     return (nEventCode & nPositivePattern)==nPositivePattern;
649 }
650 
SwitchToNormalMode()651 void SelectionFunction::SwitchToNormalMode()
652 {
653     if (mpModeHandler->GetMode() != NormalMode)
654         SwitchMode(std::shared_ptr<ModeHandler>(
655             new NormalModeHandler(mrSlideSorter, *this)));
656 }
657 
SwitchToDragAndDropMode(const Point & rMousePosition)658 void SelectionFunction::SwitchToDragAndDropMode (const Point& rMousePosition)
659 {
660     if (mpModeHandler->GetMode() == DragAndDropMode)
661         return;
662 
663     SwitchMode(std::shared_ptr<ModeHandler>(
664         new DragAndDropModeHandler(mrSlideSorter, *this, rMousePosition, mpWindow)));
665 }
666 
SwitchToMultiSelectionMode(const Point & rMousePosition,const sal_uInt32 nEventCode)667 void SelectionFunction::SwitchToMultiSelectionMode (
668     const Point& rMousePosition,
669     const sal_uInt32 nEventCode)
670 {
671     if (mpModeHandler->GetMode() != MultiSelectionMode)
672         SwitchMode(std::shared_ptr<ModeHandler>(
673             new MultiSelectionModeHandler(mrSlideSorter, *this, rMousePosition, nEventCode)));
674 }
675 
SwitchMode(const std::shared_ptr<ModeHandler> & rpHandler)676 void SelectionFunction::SwitchMode (const std::shared_ptr<ModeHandler>& rpHandler)
677 {
678     // Not all modes allow mouse over indicator.
679     if (mpModeHandler->IsMouseOverIndicatorAllowed() != rpHandler->IsMouseOverIndicatorAllowed())
680     {
681         if ( ! rpHandler->IsMouseOverIndicatorAllowed())
682         {
683             mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor());
684         }
685         else
686             mrSlideSorter.GetView().UpdatePageUnderMouse();
687     }
688 
689     mpModeHandler = rpHandler;
690 }
691 
ResetShiftKeySelectionAnchor()692 void SelectionFunction::ResetShiftKeySelectionAnchor()
693 {
694     mnShiftKeySelectionAnchor = -1;
695 }
696 
ResetMouseAnchor()697 void SelectionFunction::ResetMouseAnchor()
698 {
699     if (mpModeHandler && mpModeHandler->GetMode() == NormalMode)
700     {
701         std::shared_ptr<NormalModeHandler> pHandler (
702             std::dynamic_pointer_cast<NormalModeHandler>(mpModeHandler));
703         if (pHandler)
704             pHandler->ResetButtonDownLocation();
705     }
706 }
707 
708 //===== EventDescriptor =======================================================
709 
EventDescriptor(const sal_uInt32 nEventType,const MouseEvent & rEvent,SlideSorter const & rSlideSorter)710 SelectionFunction::EventDescriptor::EventDescriptor (
711     const sal_uInt32 nEventType,
712     const MouseEvent& rEvent,
713     SlideSorter const & rSlideSorter)
714     : maMousePosition(rEvent.GetPosPixel()),
715       maMouseModelPosition(),
716       mpHitDescriptor(),
717       mpHitPage(),
718       mnEventCode(nEventType),
719       meDragMode(InsertionIndicatorHandler::MoveMode),
720       mbIsLeaving(false)
721 {
722     maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition);
723     mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition);
724     if (mpHitDescriptor)
725     {
726         mpHitPage = mpHitDescriptor->GetPage();
727     }
728 
729     mnEventCode |= EncodeMouseEvent(rEvent);
730     mnEventCode |= EncodeState();
731 
732     // Detect the mouse leaving the window.  When not button is pressed then
733     // we can call IsLeaveWindow at the event.  Otherwise we have to make an
734     // explicit test.
735     mbIsLeaving = rEvent.IsLeaveWindow()
736         || ! ::tools::Rectangle(Point(0,0),
737              rSlideSorter.GetContentWindow()->GetOutputSizePixel()).IsInside(maMousePosition);
738 }
739 
EventDescriptor(const sal_uInt32 nEventType,const AcceptDropEvent & rEvent,const sal_Int8 nDragAction,SlideSorter const & rSlideSorter)740 SelectionFunction::EventDescriptor::EventDescriptor (
741     const sal_uInt32 nEventType,
742     const AcceptDropEvent& rEvent,
743     const sal_Int8 nDragAction,
744     SlideSorter const & rSlideSorter)
745     : maMousePosition(rEvent.maPosPixel),
746       maMouseModelPosition(),
747       mpHitDescriptor(),
748       mpHitPage(),
749       mnEventCode(nEventType),
750       meDragMode(InsertionIndicatorHandler::GetModeFromDndAction(nDragAction)),
751       mbIsLeaving(false)
752 {
753     maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition);
754     mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition);
755     if (mpHitDescriptor)
756     {
757         mpHitPage = mpHitDescriptor->GetPage();
758     }
759 
760     mnEventCode |= EncodeState();
761 
762     // Detect the mouse leaving the window.  When not button is pressed then
763     // we can call IsLeaveWindow at the event.  Otherwise we have to make an
764     // explicit test.
765     mbIsLeaving = rEvent.mbLeaving
766         || ! ::tools::Rectangle(Point(0,0),
767              rSlideSorter.GetContentWindow()->GetOutputSizePixel()).IsInside(maMousePosition);
768 }
769 
EncodeMouseEvent(const MouseEvent & rEvent) const770 sal_uInt32 SelectionFunction::EventDescriptor::EncodeMouseEvent (
771     const MouseEvent& rEvent) const
772 {
773     // Initialize with the type of mouse event.
774     sal_uInt32 nEventCode (mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION));
775 
776     // Detect the affected button.
777     switch (rEvent.GetButtons())
778     {
779         case MOUSE_LEFT:   nEventCode |= LEFT_BUTTON; break;
780         case MOUSE_RIGHT:  nEventCode |= RIGHT_BUTTON; break;
781         case MOUSE_MIDDLE: nEventCode |= MIDDLE_BUTTON; break;
782     }
783 
784     // Detect the number of clicks.
785     switch (rEvent.GetClicks())
786     {
787         case 1: nEventCode |= SINGLE_CLICK; break;
788         case 2: nEventCode |= DOUBLE_CLICK; break;
789     }
790 
791     // Detect pressed modifier keys.
792     if (rEvent.IsShift())
793         nEventCode |= SHIFT_MODIFIER;
794     if (rEvent.IsMod1())
795         nEventCode |= CONTROL_MODIFIER;
796 
797     return nEventCode;
798 }
799 
EncodeState() const800 sal_uInt32 SelectionFunction::EventDescriptor::EncodeState() const
801 {
802     sal_uInt32 nEventCode (0);
803 
804     // Detect whether the event has happened over a page object.
805     if (mpHitPage!=nullptr && mpHitDescriptor)
806     {
807         if (mpHitDescriptor->HasState(model::PageDescriptor::ST_Selected))
808             nEventCode |= OVER_SELECTED_PAGE;
809         else
810             nEventCode |= OVER_UNSELECTED_PAGE;
811     }
812 
813     return nEventCode;
814 }
815 
816 //===== SelectionFunction::ModeHandler ========================================
817 
ModeHandler(SlideSorter & rSlideSorter,SelectionFunction & rSelectionFunction,const bool bIsMouseOverIndicatorAllowed)818 SelectionFunction::ModeHandler::ModeHandler (
819     SlideSorter& rSlideSorter,
820     SelectionFunction& rSelectionFunction,
821     const bool bIsMouseOverIndicatorAllowed)
822     : mrSlideSorter(rSlideSorter),
823       mrSelectionFunction(rSelectionFunction),
824       mbIsMouseOverIndicatorAllowed(bIsMouseOverIndicatorAllowed)
825 {
826 }
827 
~ModeHandler()828 SelectionFunction::ModeHandler::~ModeHandler() COVERITY_NOEXCEPT_FALSE
829 {
830 }
831 
ReprocessEvent(EventDescriptor & rDescriptor)832 void SelectionFunction::ModeHandler::ReprocessEvent (EventDescriptor& rDescriptor)
833 {
834     mrSelectionFunction.ProcessEvent(rDescriptor);
835 }
836 
ProcessEvent(SelectionFunction::EventDescriptor & rDescriptor)837 void SelectionFunction::ModeHandler::ProcessEvent (
838     SelectionFunction::EventDescriptor& rDescriptor)
839 {
840     PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter);
841     PageSelector::UpdateLock aUpdateLock (mrSlideSorter);
842 
843     bool bIsProcessed (false);
844     switch (rDescriptor.mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION | MOUSE_DRAG))
845     {
846         case BUTTON_DOWN:
847             bIsProcessed = ProcessButtonDownEvent(rDescriptor);
848             break;
849 
850         case BUTTON_UP:
851             bIsProcessed = ProcessButtonUpEvent(rDescriptor);
852             break;
853 
854         case MOUSE_MOTION:
855             bIsProcessed = ProcessMotionEvent(rDescriptor);
856             break;
857 
858         case MOUSE_DRAG:
859             bIsProcessed = ProcessDragEvent(rDescriptor);
860             break;
861     }
862 
863     if ( ! bIsProcessed)
864         HandleUnprocessedEvent(rDescriptor);
865 }
866 
ProcessButtonDownEvent(EventDescriptor &)867 bool SelectionFunction::ModeHandler::ProcessButtonDownEvent (EventDescriptor&)
868 {
869     return false;
870 }
871 
ProcessButtonUpEvent(EventDescriptor &)872 bool SelectionFunction::ModeHandler::ProcessButtonUpEvent (EventDescriptor&)
873 {
874     mrSelectionFunction.SwitchToNormalMode();
875     return false;
876 }
877 
ProcessMotionEvent(EventDescriptor & rDescriptor)878 bool SelectionFunction::ModeHandler::ProcessMotionEvent (EventDescriptor& rDescriptor)
879 {
880     if (mbIsMouseOverIndicatorAllowed)
881         mrSlideSorter.GetView().UpdatePageUnderMouse(rDescriptor.maMousePosition);
882 
883     if (rDescriptor.mbIsLeaving)
884     {
885         mrSelectionFunction.SwitchToNormalMode();
886         mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor());
887 
888         return true;
889     }
890     else
891         return false;
892 }
893 
ProcessDragEvent(EventDescriptor &)894 bool SelectionFunction::ModeHandler::ProcessDragEvent (EventDescriptor&)
895 {
896     return false;
897 }
898 
HandleUnprocessedEvent(EventDescriptor &)899 bool SelectionFunction::ModeHandler::HandleUnprocessedEvent (EventDescriptor&)
900 {
901     return false;
902 }
903 
SetCurrentPage(const model::SharedPageDescriptor & rpDescriptor)904 void SelectionFunction::ModeHandler::SetCurrentPage (
905     const model::SharedPageDescriptor& rpDescriptor)
906 {
907     SelectOnePage(rpDescriptor);
908     mrSlideSorter.GetController().GetCurrentSlideManager()->SwitchCurrentSlide(rpDescriptor);
909 }
910 
DeselectAllPages()911 void SelectionFunction::ModeHandler::DeselectAllPages()
912 {
913     mrSlideSorter.GetController().GetPageSelector().DeselectAllPages();
914     mrSelectionFunction.ResetShiftKeySelectionAnchor();
915 }
916 
SelectOnePage(const model::SharedPageDescriptor & rpDescriptor)917 void SelectionFunction::ModeHandler::SelectOnePage (
918     const model::SharedPageDescriptor& rpDescriptor)
919 {
920     DeselectAllPages();
921     mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor);
922 }
923 
SwitchView(const model::SharedPageDescriptor & rpDescriptor)924 void SelectionFunction::ModeHandler::SwitchView (const model::SharedPageDescriptor& rpDescriptor)
925 {
926     // Switch to the draw view.  This is done only when the current
927     // view is the main view.
928     ViewShell* pViewShell = mrSlideSorter.GetViewShell();
929     if (pViewShell==nullptr || !pViewShell->IsMainViewShell())
930         return;
931 
932     if (rpDescriptor.get()!=nullptr && rpDescriptor->GetPage()!=nullptr)
933     {
934         mrSlideSorter.GetModel().GetDocument()->SetSelected(rpDescriptor->GetPage(), true);
935         pViewShell->GetFrameView()->SetSelectedPage(
936             (rpDescriptor->GetPage()->GetPageNum()-1)/2);
937     }
938     if (mrSlideSorter.GetViewShellBase() != nullptr)
939         framework::FrameworkHelper::Instance(*mrSlideSorter.GetViewShellBase())->RequestView(
940             framework::FrameworkHelper::msImpressViewURL,
941             framework::FrameworkHelper::msCenterPaneURL);
942 }
943 
StartDrag(const Point & rMousePosition)944 void SelectionFunction::ModeHandler::StartDrag (
945     const Point& rMousePosition)
946 {
947     // Do not start a drag-and-drop operation when one is already active.
948     // (when dragging pages from one document into another, pressing a
949     // modifier key can trigger a MouseMotion event in the originating
950     // window (focus still in there).  Together with the mouse button pressed
951     // (drag-and-drop is active) this triggers the start of drag-and-drop.)
952     if (SD_MOD()->pTransferDrag != nullptr)
953         return;
954 
955     if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly())
956     {
957         mrSelectionFunction.SwitchToDragAndDropMode(rMousePosition);
958     }
959 }
960 
961 //===== NormalModeHandler =====================================================
962 
NormalModeHandler(SlideSorter & rSlideSorter,SelectionFunction & rSelectionFunction)963 NormalModeHandler::NormalModeHandler (
964     SlideSorter& rSlideSorter,
965     SelectionFunction& rSelectionFunction)
966     : ModeHandler(rSlideSorter, rSelectionFunction, true),
967       maButtonDownLocation()
968 {
969 }
970 
GetMode() const971 SelectionFunction::Mode NormalModeHandler::GetMode() const
972 {
973     return SelectionFunction::NormalMode;
974 }
975 
Abort()976 void NormalModeHandler::Abort()
977 {
978 }
979 
ProcessButtonDownEvent(SelectionFunction::EventDescriptor & rDescriptor)980 bool NormalModeHandler::ProcessButtonDownEvent (
981     SelectionFunction::EventDescriptor& rDescriptor)
982 {
983     // Remember the location where the left button is pressed.  With
984     // that we can filter away motion events that are caused by key
985     // presses.  We also can tune the minimal motion distance that
986     // triggers a drag-and-drop operation.
987     if ((rDescriptor.mnEventCode & BUTTON_DOWN) != 0)
988         maButtonDownLocation = rDescriptor.maMousePosition;
989 
990     switch (rDescriptor.mnEventCode)
991     {
992         case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE:
993             SetCurrentPage(rDescriptor.mpHitDescriptor);
994             break;
995 
996         case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE:
997             break;
998 
999         case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_SELECTED_PAGE:
1000         case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_UNSELECTED_PAGE:
1001             // A double click always shows the selected slide in the center
1002             // pane in an edit view.
1003             SetCurrentPage(rDescriptor.mpHitDescriptor);
1004             SwitchView(rDescriptor.mpHitDescriptor);
1005             break;
1006 
1007         case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | SHIFT_MODIFIER:
1008         case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | SHIFT_MODIFIER:
1009             // Range selection with the shift modifier.
1010             RangeSelect(rDescriptor.mpHitDescriptor);
1011             break;
1012 
1013             // Right button for context menu.
1014         case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE:
1015             // Single right click and shift+F10 select as preparation to
1016             // show the context menu.  Change the selection only when the
1017             // page under the mouse is not selected.  In this case the
1018             // selection is set to this single page.  Otherwise the
1019             // selection is not modified.
1020             SetCurrentPage(rDescriptor.mpHitDescriptor);
1021             break;
1022 
1023         case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE:
1024             // Do not change the selection.  Just adjust the insertion indicator.
1025             break;
1026 
1027         case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE:
1028             // Remember the current selection so that when a multi selection
1029             // is started, we can restore the previous selection.
1030             mrSlideSorter.GetModel().SaveCurrentSelection();
1031             DeselectAllPages();
1032             break;
1033 
1034         case ANY_MODIFIER(BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE):
1035             // Remember the current selection so that when a multi selection
1036             // is started, we can restore the previous selection.
1037             mrSlideSorter.GetModel().SaveCurrentSelection();
1038             DeselectAllPages();
1039             break;
1040 
1041         case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | NOT_OVER_PAGE:
1042         {
1043             // Insert a new slide:
1044             // First of all we need to set the insertion indicator which sets the
1045             // position where the new slide will be inserted.
1046             std::shared_ptr<InsertionIndicatorHandler> pInsertionIndicatorHandler
1047                 = mrSlideSorter.GetController().GetInsertionIndicatorHandler();
1048 
1049             pInsertionIndicatorHandler->Start(false);
1050             pInsertionIndicatorHandler->UpdatePosition(
1051                     rDescriptor.maMousePosition,
1052                     InsertionIndicatorHandler::MoveMode);
1053 
1054             mrSlideSorter.GetController().GetSelectionManager()->SetInsertionPosition(
1055                 pInsertionIndicatorHandler->GetInsertionPageIndex());
1056 
1057             mrSlideSorter.GetViewShell()->GetDispatcher()->Execute(
1058                 SID_INSERTPAGE,
1059                 SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
1060 
1061             pInsertionIndicatorHandler->End(Animator::AM_Immediate);
1062 
1063             break;
1064         }
1065 
1066         default:
1067             return false;
1068     }
1069     return true;
1070 }
1071 
ProcessButtonUpEvent(SelectionFunction::EventDescriptor & rDescriptor)1072 bool NormalModeHandler::ProcessButtonUpEvent (
1073     SelectionFunction::EventDescriptor& rDescriptor)
1074 {
1075     bool bIsProcessed (true);
1076     switch (rDescriptor.mnEventCode)
1077     {
1078         case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE:
1079             SetCurrentPage(rDescriptor.mpHitDescriptor);
1080             break;
1081 
1082             // Multi selection with the control modifier.
1083         case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | CONTROL_MODIFIER:
1084             mrSlideSorter.GetController().GetPageSelector().DeselectPage(
1085                 rDescriptor.mpHitDescriptor);
1086             break;
1087 
1088         case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | CONTROL_MODIFIER:
1089             mrSlideSorter.GetController().GetPageSelector().SelectPage(
1090                 rDescriptor.mpHitDescriptor);
1091             mrSlideSorter.GetView().SetPageUnderMouse(rDescriptor.mpHitDescriptor);
1092             break;
1093         case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE:
1094             break;
1095 
1096         default:
1097             bIsProcessed = false;
1098             break;
1099     }
1100     mrSelectionFunction.SwitchToNormalMode();
1101     return bIsProcessed;
1102 }
1103 
ProcessMotionEvent(SelectionFunction::EventDescriptor & rDescriptor)1104 bool NormalModeHandler::ProcessMotionEvent (
1105     SelectionFunction::EventDescriptor& rDescriptor)
1106 {
1107     if (ModeHandler::ProcessMotionEvent(rDescriptor))
1108         return true;
1109 
1110     bool bIsProcessed (true);
1111     switch (rDescriptor.mnEventCode)
1112     {
1113         // A mouse motion without visible substitution starts that.
1114         case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE):
1115         case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE):
1116         {
1117             if (maButtonDownLocation)
1118             {
1119                 const sal_Int32 nDistance(std::max(
1120                     std::abs(maButtonDownLocation->X() - rDescriptor.maMousePosition.X()),
1121                     std::abs(maButtonDownLocation->Y() - rDescriptor.maMousePosition.Y())));
1122                 if (nDistance > 3)
1123                     StartDrag(rDescriptor.maMousePosition);
1124             }
1125             break;
1126         }
1127 
1128         // A mouse motion not over a page starts a rectangle selection.
1129         case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE):
1130             mrSelectionFunction.SwitchToMultiSelectionMode(
1131                 rDescriptor.maMouseModelPosition,
1132                 rDescriptor.mnEventCode);
1133             break;
1134 
1135         default:
1136             bIsProcessed = false;
1137             break;
1138     }
1139     return bIsProcessed;
1140 }
1141 
ProcessDragEvent(SelectionFunction::EventDescriptor & rDescriptor)1142 bool NormalModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor)
1143 {
1144     mrSelectionFunction.SwitchToDragAndDropMode(rDescriptor.maMousePosition);
1145     ReprocessEvent(rDescriptor);
1146     return true;
1147 }
1148 
RangeSelect(const model::SharedPageDescriptor & rpDescriptor)1149 void NormalModeHandler::RangeSelect (const model::SharedPageDescriptor& rpDescriptor)
1150 {
1151     PageSelector::UpdateLock aLock (mrSlideSorter);
1152     PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector());
1153 
1154     model::SharedPageDescriptor pAnchor (rSelector.GetSelectionAnchor());
1155     DeselectAllPages();
1156 
1157     if (pAnchor.get() == nullptr)
1158         return;
1159 
1160     // Select all pages between the anchor and the given one, including
1161     // the two.
1162     const sal_uInt16 nAnchorIndex ((pAnchor->GetPage()->GetPageNum()-1) / 2);
1163     const sal_uInt16 nOtherIndex ((rpDescriptor->GetPage()->GetPageNum()-1) / 2);
1164 
1165     // Iterate over all pages in the range.  Start with the anchor
1166     // page.  This way the PageSelector will recognize it again as
1167     // anchor (the first selected page after a DeselectAllPages()
1168     // becomes the anchor.)
1169     const sal_uInt16 nStep ((nAnchorIndex < nOtherIndex) ? +1 : -1);
1170     sal_uInt16 nIndex (nAnchorIndex);
1171     while (true)
1172     {
1173         rSelector.SelectPage(nIndex);
1174         if (nIndex == nOtherIndex)
1175             break;
1176         nIndex = nIndex + nStep;
1177     }
1178 }
1179 
ResetButtonDownLocation()1180 void NormalModeHandler::ResetButtonDownLocation()
1181 {
1182     maButtonDownLocation = ::boost::optional<Point>();
1183 }
1184 
1185 //===== MultiSelectionModeHandler =============================================
1186 
MultiSelectionModeHandler(SlideSorter & rSlideSorter,SelectionFunction & rSelectionFunction,const Point & rMouseModelPosition,const sal_uInt32 nEventCode)1187 MultiSelectionModeHandler::MultiSelectionModeHandler (
1188     SlideSorter& rSlideSorter,
1189     SelectionFunction& rSelectionFunction,
1190     const Point& rMouseModelPosition,
1191     const sal_uInt32 nEventCode)
1192     : ModeHandler(rSlideSorter, rSelectionFunction, false),
1193       meSelectionMode(SM_Normal),
1194       maSecondCorner(rMouseModelPosition),
1195       maSavedPointer(mrSlideSorter.GetContentWindow()->GetPointer()),
1196       mbAutoScrollInstalled(false),
1197       mnAnchorIndex(-1),
1198       mnSecondIndex(-1)
1199 {
1200 
1201     mrSlideSorter.GetContentWindow()->SetPointer(PointerStyle::Text);
1202     SetSelectionModeFromModifier(nEventCode);
1203 }
1204 
~MultiSelectionModeHandler()1205 MultiSelectionModeHandler::~MultiSelectionModeHandler()
1206 {
1207     if (mbAutoScrollInstalled)
1208     {
1209         //a call to this handler's MultiSelectionModeHandler::UpdatePosition
1210         //may be still waiting to be called back
1211         mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor();
1212     }
1213     mrSlideSorter.GetContentWindow()->SetPointer(maSavedPointer);
1214 }
1215 
GetMode() const1216 SelectionFunction::Mode MultiSelectionModeHandler::GetMode() const
1217 {
1218     return SelectionFunction::MultiSelectionMode;
1219 }
1220 
Abort()1221 void MultiSelectionModeHandler::Abort()
1222 {
1223     mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection());
1224 }
1225 
ProcessEvent(SelectionFunction::EventDescriptor & rDescriptor)1226 void MultiSelectionModeHandler::ProcessEvent (
1227     SelectionFunction::EventDescriptor& rDescriptor)
1228 {
1229     // During a multi selection we do not want sudden jumps of the
1230     // visible area caused by moving newly selected pages into view.
1231     // Therefore disable that temporarily.  The disabled object is
1232     // released at the end of the event processing, after the focus and
1233     // current slide have been updated.
1234     VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter);
1235 
1236     ModeHandler::ProcessEvent(rDescriptor);
1237 }
1238 
ProcessButtonUpEvent(SelectionFunction::EventDescriptor & rDescriptor)1239 bool MultiSelectionModeHandler::ProcessButtonUpEvent (
1240     SelectionFunction::EventDescriptor& rDescriptor)
1241 {
1242     if (mbAutoScrollInstalled)
1243     {
1244         //a call to this handler's MultiSelectionModeHandler::UpdatePosition
1245         //may be still waiting to be called back
1246         mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor();
1247         mbAutoScrollInstalled = false;
1248     }
1249 
1250     if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK))
1251     {
1252         mrSelectionFunction.SwitchToNormalMode();
1253         return true;
1254     }
1255     else
1256         return false;
1257 }
1258 
ProcessMotionEvent(SelectionFunction::EventDescriptor & rDescriptor)1259 bool MultiSelectionModeHandler::ProcessMotionEvent (
1260     SelectionFunction::EventDescriptor& rDescriptor)
1261 {
1262     // The selection rectangle is visible.  Handle events accordingly.
1263     if (Match(rDescriptor.mnEventCode, MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK))
1264     {
1265         SetSelectionModeFromModifier(rDescriptor.mnEventCode);
1266         UpdatePosition(rDescriptor.maMousePosition, true);
1267         return true;
1268     }
1269     else
1270         return false;
1271 }
1272 
HandleUnprocessedEvent(SelectionFunction::EventDescriptor & rDescriptor)1273 bool MultiSelectionModeHandler::HandleUnprocessedEvent (
1274     SelectionFunction::EventDescriptor& rDescriptor)
1275 {
1276     if ( ! ModeHandler::HandleUnprocessedEvent(rDescriptor))
1277     {
1278         // If the event has not been processed then stop multi selection.
1279         mrSelectionFunction.SwitchToNormalMode();
1280         ReprocessEvent(rDescriptor);
1281     }
1282     return true;
1283 }
1284 
UpdatePosition(const Point & rMousePosition,const bool bAllowAutoScroll)1285 void MultiSelectionModeHandler::UpdatePosition (
1286     const Point& rMousePosition,
1287     const bool bAllowAutoScroll)
1288 {
1289     VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter);
1290 
1291     // Convert window coordinates into model coordinates (we need the
1292     // window coordinates for auto-scrolling because that remains
1293     // constant while scrolling.)
1294     sd::Window *pWindow (mrSlideSorter.GetContentWindow().get());
1295     const Point aMouseModelPosition (pWindow->PixelToLogic(rMousePosition));
1296 
1297     bool bDoAutoScroll = bAllowAutoScroll && mrSlideSorter.GetController().GetScrollBarManager().AutoScroll(
1298         rMousePosition,
1299         [this, &rMousePosition] () { return this->UpdatePosition(rMousePosition, false); });
1300 
1301     if (!bDoAutoScroll)
1302         UpdateModelPosition(aMouseModelPosition);
1303 
1304     mbAutoScrollInstalled |= bDoAutoScroll;
1305 }
1306 
SetSelectionModeFromModifier(const sal_uInt32 nEventCode)1307 void MultiSelectionModeHandler::SetSelectionModeFromModifier (
1308     const sal_uInt32 nEventCode)
1309 {
1310     switch (nEventCode & MODIFIER_MASK)
1311     {
1312         case NO_MODIFIER:
1313             SetSelectionMode(SM_Normal);
1314             break;
1315 
1316         case SHIFT_MODIFIER:
1317             SetSelectionMode(SM_Add);
1318             break;
1319 
1320         case CONTROL_MODIFIER:
1321             SetSelectionMode(SM_Toggle);
1322             break;
1323     }
1324 }
1325 
SetSelectionMode(const SelectionMode eSelectionMode)1326 void MultiSelectionModeHandler::SetSelectionMode (const SelectionMode eSelectionMode)
1327 {
1328     if (meSelectionMode != eSelectionMode)
1329     {
1330         meSelectionMode = eSelectionMode;
1331         UpdateSelection();
1332     }
1333 }
1334 
UpdateSelectionState(const model::SharedPageDescriptor & rpDescriptor,const bool bIsInSelection) const1335 void MultiSelectionModeHandler::UpdateSelectionState (
1336     const model::SharedPageDescriptor& rpDescriptor,
1337     const bool bIsInSelection) const
1338 {
1339     // Determine whether the page was selected before the rectangle
1340     // selection was started.
1341     const bool bWasSelected (rpDescriptor->HasState(model::PageDescriptor::ST_WasSelected));
1342 
1343     // Combine the two selection states depending on the selection mode.
1344     bool bSelect (false);
1345     switch(meSelectionMode)
1346     {
1347         case SM_Normal:
1348             bSelect = bIsInSelection;
1349             break;
1350 
1351         case SM_Add:
1352             bSelect = bIsInSelection || bWasSelected;
1353             break;
1354 
1355         case SM_Toggle:
1356             if (bIsInSelection)
1357                 bSelect = !bWasSelected;
1358             else
1359                 bSelect = bWasSelected;
1360             break;
1361     }
1362 
1363     // Set the new selection state.
1364     if (bSelect)
1365         mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor);
1366     else
1367         mrSlideSorter.GetController().GetPageSelector().DeselectPage(rpDescriptor);
1368 }
1369 
UpdateModelPosition(const Point & rMouseModelPosition)1370 void MultiSelectionModeHandler::UpdateModelPosition (const Point& rMouseModelPosition)
1371 {
1372     maSecondCorner = rMouseModelPosition;
1373     UpdateSelection();
1374 }
1375 
UpdateSelection()1376 void MultiSelectionModeHandler::UpdateSelection()
1377 {
1378     view::SlideSorterView::DrawLock aLock (mrSlideSorter);
1379 
1380     model::SlideSorterModel& rModel (mrSlideSorter.GetModel());
1381     const sal_Int32 nPageCount (rModel.GetPageCount());
1382 
1383     const sal_Int32 nIndexUnderMouse (
1384         mrSlideSorter.GetView().GetLayouter().GetIndexAtPoint (
1385             maSecondCorner,
1386             false,
1387             false));
1388     if (!(nIndexUnderMouse>=0 && nIndexUnderMouse<nPageCount))
1389         return;
1390 
1391     if (mnAnchorIndex < 0)
1392         mnAnchorIndex = nIndexUnderMouse;
1393     mnSecondIndex = nIndexUnderMouse;
1394 
1395     Range aRange (mnAnchorIndex, mnSecondIndex);
1396     aRange.Justify();
1397 
1398     for (sal_Int32 nIndex=0; nIndex<nPageCount; ++nIndex)
1399     {
1400         UpdateSelectionState(rModel.GetPageDescriptor(nIndex), aRange.IsInside(nIndex));
1401     }
1402 }
1403 
1404 //===== DragAndDropModeHandler ================================================
1405 
DragAndDropModeHandler(SlideSorter & rSlideSorter,SelectionFunction & rSelectionFunction,const Point & rMousePosition,vcl::Window * pWindow)1406 DragAndDropModeHandler::DragAndDropModeHandler (
1407     SlideSorter& rSlideSorter,
1408     SelectionFunction& rSelectionFunction,
1409     const Point& rMousePosition,
1410     vcl::Window* pWindow)
1411     : ModeHandler(rSlideSorter, rSelectionFunction, false)
1412 {
1413     SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag;
1414     if (pDragTransferable==nullptr && mrSlideSorter.GetViewShell() != nullptr)
1415     {
1416         SlideSorterViewShell* pSlideSorterViewShell
1417             = dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell());
1418         if (pSlideSorterViewShell != nullptr)
1419             pSlideSorterViewShell->StartDrag(rMousePosition, pWindow);
1420         pDragTransferable = SD_MOD()->pTransferDrag;
1421     }
1422 
1423     mpDragAndDropContext.reset(new DragAndDropContext(mrSlideSorter));
1424     mrSlideSorter.GetController().GetInsertionIndicatorHandler()->Start(
1425         pDragTransferable != nullptr
1426             && pDragTransferable->GetView()==&mrSlideSorter.GetView());
1427 }
1428 
~DragAndDropModeHandler()1429 DragAndDropModeHandler::~DragAndDropModeHandler()
1430 {
1431     if (mpDragAndDropContext)
1432     {
1433         // Disconnect the substitution handler from this selection function.
1434         mpDragAndDropContext->SetTargetSlideSorter();
1435         mpDragAndDropContext.reset();
1436     }
1437     mrSlideSorter.GetController().GetInsertionIndicatorHandler()->End(Animator::AM_Animated);
1438 }
1439 
GetMode() const1440 SelectionFunction::Mode DragAndDropModeHandler::GetMode() const
1441 {
1442     return SelectionFunction::DragAndDropMode;
1443 }
1444 
Abort()1445 void DragAndDropModeHandler::Abort()
1446 {
1447     mrSlideSorter.GetController().GetClipboard().Abort();
1448     if (mpDragAndDropContext)
1449         mpDragAndDropContext->Dispose();
1450     //    mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection());
1451 }
1452 
ProcessButtonUpEvent(SelectionFunction::EventDescriptor & rDescriptor)1453 bool DragAndDropModeHandler::ProcessButtonUpEvent (
1454     SelectionFunction::EventDescriptor& rDescriptor)
1455 {
1456     if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON))
1457     {
1458         // The following Process() call may lead to the destruction
1459         // of rDescriptor.mpHitDescriptor so release our reference to it.
1460         rDescriptor.mpHitDescriptor.reset();
1461         mrSelectionFunction.SwitchToNormalMode();
1462         return true;
1463     }
1464     else
1465         return false;
1466 }
1467 
ProcessDragEvent(SelectionFunction::EventDescriptor & rDescriptor)1468 bool DragAndDropModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor)
1469 {
1470     OSL_ASSERT(mpDragAndDropContext);
1471 
1472     if (rDescriptor.mbIsLeaving)
1473     {
1474         mrSelectionFunction.SwitchToNormalMode();
1475     }
1476     else if (mpDragAndDropContext)
1477     {
1478         mpDragAndDropContext->UpdatePosition(
1479             rDescriptor.maMousePosition,
1480             rDescriptor.meDragMode, true);
1481     }
1482 
1483     return true;
1484 }
1485 
1486 } } } // end of namespace ::sd::slidesorter::controller
1487 
1488 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1489