diff options
Diffstat (limited to 'sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx')
-rw-r--r-- | sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx | 1485 |
1 files changed, 1485 insertions, 0 deletions
diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx new file mode 100644 index 000000000..c710a4c1b --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx @@ -0,0 +1,1485 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> + +#include <controller/SlsSelectionFunction.hxx> + +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include "SlsDragAndDropContext.hxx" +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsFocusManager.hxx> +#include <controller/SlsScrollBarManager.hxx> +#include <controller/SlsClipboard.hxx> +#include <controller/SlsCurrentSlideManager.hxx> +#include <controller/SlsInsertionIndicatorHandler.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <controller/SlsProperties.hxx> +#include <controller/SlsVisibleAreaManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <model/SlsPageEnumerationProvider.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <framework/FrameworkHelper.hxx> +#include <osl/diagnose.h> +#include <Window.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdxfer.hxx> +#include <ViewShell.hxx> +#include <FrameView.hxx> +#include <app.hrc> +#include <o3tl/deleter.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/ptrstyle.hxx> +#include <optional> +#include <sdmod.hxx> + +namespace { +const sal_uInt32 SINGLE_CLICK (0x00000001); +const sal_uInt32 DOUBLE_CLICK (0x00000002); +const sal_uInt32 LEFT_BUTTON (0x00000010); +const sal_uInt32 RIGHT_BUTTON (0x00000020); +const sal_uInt32 MIDDLE_BUTTON (0x00000040); +const sal_uInt32 BUTTON_DOWN (0x00000100); +const sal_uInt32 BUTTON_UP (0x00000200); +const sal_uInt32 MOUSE_MOTION (0x00000400); +const sal_uInt32 MOUSE_DRAG (0x00000800); +// The rest leaves the lower 16 bit untouched so that it can be used with +// key codes. +const sal_uInt32 OVER_SELECTED_PAGE (0x00010000); +const sal_uInt32 OVER_UNSELECTED_PAGE (0x00020000); +const sal_uInt32 SHIFT_MODIFIER (0x00200000); +const sal_uInt32 CONTROL_MODIFIER (0x00400000); + +// Some absent events are defined so they can be expressed explicitly. +const sal_uInt32 NO_MODIFIER (0x00000000); +const sal_uInt32 NOT_OVER_PAGE (0x00000000); + +// Masks +const sal_uInt32 MODIFIER_MASK (SHIFT_MODIFIER | CONTROL_MODIFIER); + +} // end of anonymous namespace + +// Define some macros to make the following switch statement more readable. +#define ANY_MODIFIER(code) \ + code|NO_MODIFIER: \ + case code|SHIFT_MODIFIER: \ + case code|CONTROL_MODIFIER + +namespace sd::slidesorter::controller { + +//===== SelectionFunction::EventDescriptor ==================================== + +class SelectionFunction::EventDescriptor +{ +public: + Point maMousePosition; + Point maMouseModelPosition; + model::SharedPageDescriptor mpHitDescriptor; + SdrPage* mpHitPage; + sal_uInt32 mnEventCode; + InsertionIndicatorHandler::Mode meDragMode; + bool mbIsLeaving; + + EventDescriptor ( + sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter const & rSlideSorter); + EventDescriptor ( + sal_uInt32 nEventType, + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction, + SlideSorter const & rSlideSorter); + +private: + /** Compute a numerical code that describes a mouse event and that can + be used for fast look up of the appropriate reaction. + */ + sal_uInt32 EncodeMouseEvent (const MouseEvent& rEvent) const; + + /** Compute a numerical code that describes the current state like + whether the selection rectangle is visible or whether the page under + the mouse or the one that has the focus is selected. + */ + sal_uInt32 EncodeState() const; +}; + +//===== SelectionFunction::ModeHandler ======================================== + +class SelectionFunction::ModeHandler +{ +public: + ModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const bool bIsMouseOverIndicatorAllowed); + virtual ~ModeHandler() COVERITY_NOEXCEPT_FALSE; + + virtual Mode GetMode() const = 0; + virtual void Abort() = 0; + virtual void ProcessEvent (EventDescriptor& rDescriptor); + + /** Set the selection to exactly the specified page and also set it as + the current page. + */ + void SetCurrentPage (const model::SharedPageDescriptor& rpDescriptor); + + /// Deselect all pages. + void DeselectAllPages(); + void SelectOnePage (const model::SharedPageDescriptor& rpDescriptor); + + /** When the view on which this selection function is working is the + main view then the view is switched to the regular editing view. + */ + void SwitchView (const model::SharedPageDescriptor& rpDescriptor); + + void StartDrag ( + const Point& rMousePosition); + + bool IsMouseOverIndicatorAllowed() const { return mbIsMouseOverIndicatorAllowed;} + +protected: + SlideSorter& mrSlideSorter; + SelectionFunction& mrSelectionFunction; + + virtual bool ProcessButtonDownEvent (EventDescriptor& rDescriptor); + virtual bool ProcessButtonUpEvent (EventDescriptor& rDescriptor); + virtual bool ProcessMotionEvent (EventDescriptor& rDescriptor); + virtual bool ProcessDragEvent (EventDescriptor& rDescriptor); + virtual bool HandleUnprocessedEvent (EventDescriptor& rDescriptor); + + void ReprocessEvent (EventDescriptor& rDescriptor); + +private: + const bool mbIsMouseOverIndicatorAllowed; +}; + +namespace { + +/** This is the default handler for processing events. It activates the + multi selection or drag-and-drop when the right conditions are met. +*/ +class NormalModeHandler : public SelectionFunction::ModeHandler +{ +public: + NormalModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction); + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + + void ResetButtonDownLocation(); + +protected: + virtual bool ProcessButtonDownEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + ::std::optional<Point> maButtonDownLocation; + + /** Select all pages between and including the selection anchor and the + specified page. + */ + void RangeSelect (const model::SharedPageDescriptor& rpDescriptor); +}; + +/** Handle events during a multi selection, which typically is started by + pressing the left mouse button when not over a page. +*/ +class MultiSelectionModeHandler : public SelectionFunction::ModeHandler +{ +public: + /** Start a rectangle selection at the given position. + */ + MultiSelectionModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMouseModelPosition, + const sal_uInt32 nEventCode); + + virtual ~MultiSelectionModeHandler() override; + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + virtual void ProcessEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + + enum SelectionMode { SM_Normal, SM_Add, SM_Toggle }; + + void SetSelectionMode (const SelectionMode eSelectionMode); + void SetSelectionModeFromModifier (const sal_uInt32 nEventCode); + +protected: + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool HandleUnprocessedEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + SelectionMode meSelectionMode; + Point maSecondCorner; + PointerStyle maSavedPointer; + bool mbAutoScrollInstalled; + sal_Int32 mnAnchorIndex; + sal_Int32 mnSecondIndex; + + void UpdateModelPosition (const Point& rMouseModelPosition); + void UpdateSelection(); + + /** Update the rectangle selection so that the given position becomes + the new second point of the selection rectangle. + */ + void UpdatePosition ( + const Point& rMousePosition, + const bool bAllowAutoScroll); + + void UpdateSelectionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bIsInSelection) const; +}; + +/** Handle events during drag-and-drop. +*/ +class DragAndDropModeHandler : public SelectionFunction::ModeHandler +{ +public: + DragAndDropModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMousePosition, + vcl::Window* pWindow); + virtual ~DragAndDropModeHandler() override; + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + +protected: + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + std::unique_ptr<DragAndDropContext, o3tl::default_delete<DragAndDropContext>> mpDragAndDropContext; +}; + +} + +//===== SelectionFunction ===================================================== + + +SelectionFunction::SelectionFunction ( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) + : FuPoor ( + rSlideSorter.GetViewShell(), + rSlideSorter.GetContentWindow(), + &rSlideSorter.GetView(), + rSlideSorter.GetModel().GetDocument(), + rRequest), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mnShiftKeySelectionAnchor(-1), + mpModeHandler(std::make_shared<NormalModeHandler>(rSlideSorter, *this)) +{ +} + +SelectionFunction::~SelectionFunction() +{ + mpModeHandler.reset(); +} + +rtl::Reference<FuPoor> SelectionFunction::Create( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) +{ + rtl::Reference<FuPoor> xFunc( new SelectionFunction( rSlideSorter, rRequest ) ); + return xFunc; +} + +bool SelectionFunction::MouseButtonDown (const MouseEvent& rEvent) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + aMDPos = rEvent.GetPosPixel(); + + // mpWindow->CaptureMouse(); + + ProcessMouseEvent(BUTTON_DOWN, rEvent); + + return true; +} + +bool SelectionFunction::MouseMove (const MouseEvent& rEvent) +{ + ProcessMouseEvent(MOUSE_MOTION, rEvent); + return true; +} + +bool SelectionFunction::MouseButtonUp (const MouseEvent& rEvent) +{ + mrController.GetScrollBarManager().StopAutoScroll (); + + ProcessMouseEvent(BUTTON_UP, rEvent); + + return true; +} + +void SelectionFunction::NotifyDragFinished() +{ + SwitchToNormalMode(); +} + +bool SelectionFunction::KeyInput (const KeyEvent& rEvent) +{ + view::SlideSorterView::DrawLock aDrawLock (mrSlideSorter); + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + PageSelector::UpdateLock aLock (mrSlideSorter); + FocusManager& rFocusManager (mrController.GetFocusManager()); + bool bResult = false; + + const vcl::KeyCode& rCode (rEvent.GetKeyCode()); + switch (rCode.GetCode()) + { + case KEY_RETURN: + { + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (rFocusManager.HasFocus() && pDescriptor && pViewShell!=nullptr) + { + // The Return key triggers different functions depending on + // whether the slide sorter is the main view or displayed in + // the right pane. + if (pViewShell->IsMainViewShell()) + { + mpModeHandler->SetCurrentPage(pDescriptor); + mpModeHandler->SwitchView(pDescriptor); + } + else if (pViewShell->GetDispatcher() != nullptr) + { + pViewShell->GetDispatcher()->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + bResult = true; + } + break; + } + + case KEY_TAB: + if ( ! rFocusManager.IsFocusShowing()) + { + rFocusManager.ShowFocus(); + bResult = true; + } + break; + + case KEY_ESCAPE: + // When there is an active multiselection or drag-and-drop + // operation then stop that. + mpModeHandler->Abort(); + SwitchToNormalMode(); + bResult = true; + break; + + case KEY_SPACE: + { + // Toggle the selection state. + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + if (pDescriptor && rCode.IsMod1()) + { + if (pDescriptor->HasState(model::PageDescriptor::ST_Selected)) + mrController.GetPageSelector().DeselectPage(pDescriptor, false); + else + mrController.GetPageSelector().SelectPage(pDescriptor); + } + bResult = true; + } + break; + + // Move the focus indicator left. + case KEY_LEFT: + MoveFocus(FocusManager::FocusMoveDirection::Left, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator right. + case KEY_RIGHT: + MoveFocus(FocusManager::FocusMoveDirection::Right, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator up. + case KEY_UP: + MoveFocus(FocusManager::FocusMoveDirection::Up, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator down. + case KEY_DOWN: + MoveFocus(FocusManager::FocusMoveDirection::Down, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Go to previous page. No wrap around. + case KEY_PAGEUP: + GotoNextPage(-1); + bResult = true; + break; + + // Go to next page. No wrap around... + case KEY_PAGEDOWN: + GotoNextPage(+1); + bResult = true; + break; + + case KEY_HOME: + GotoPage(0); + bResult = true; + break; + + case KEY_END: + GotoPage(mrSlideSorter.GetModel().GetPageCount()-1); + bResult = true; + break; + + case KEY_DELETE: + case KEY_BACKSPACE: + { + if (mrSlideSorter.GetProperties()->IsUIReadOnly()) + break; + + mrController.GetSelectionManager()->DeleteSelectedPages(rCode.GetCode()==KEY_DELETE); + + mnShiftKeySelectionAnchor = -1; + bResult = true; + } + break; + + case KEY_F10: + if (rCode.IsShift()) + { + mpModeHandler->SelectOnePage( + mrSlideSorter.GetController().GetFocusManager().GetFocusedPageDescriptor()); + } + break; + + default: + break; + } + + if ( ! bResult) + bResult = FuPoor::KeyInput(rEvent); + + return bResult; +} + +void SelectionFunction::MoveFocus ( + const FocusManager::FocusMoveDirection eDirection, + const bool bIsShiftDown, + const bool bIsControlDown) +{ + // Remember the anchor of shift key multi selection. + if (bIsShiftDown) + { + if (mnShiftKeySelectionAnchor<0) + { + model::SharedPageDescriptor pFocusedDescriptor ( + mrController.GetFocusManager().GetFocusedPageDescriptor()); + mnShiftKeySelectionAnchor = pFocusedDescriptor->GetPageIndex(); + } + } + else if ( ! bIsControlDown) + ResetShiftKeySelectionAnchor(); + + mrController.GetFocusManager().MoveFocus(eDirection); + + PageSelector& rSelector (mrController.GetPageSelector()); + model::SharedPageDescriptor pFocusedDescriptor ( + mrController.GetFocusManager().GetFocusedPageDescriptor()); + if (bIsShiftDown) + { + // When shift is pressed then select all pages in the range between + // the currently and the previously focused pages, including them. + if (pFocusedDescriptor) + { + sal_Int32 nPageRangeEnd (pFocusedDescriptor->GetPageIndex()); + model::PageEnumeration aPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration( + mrSlideSorter.GetModel())); + while (aPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + if (pDescriptor) + { + const sal_Int32 nPageIndex(pDescriptor->GetPageIndex()); + if ((nPageIndex>=mnShiftKeySelectionAnchor && nPageIndex<=nPageRangeEnd) + || (nPageIndex<=mnShiftKeySelectionAnchor && nPageIndex>=nPageRangeEnd)) + { + rSelector.SelectPage(pDescriptor); + } + else + { + rSelector.DeselectPage(pDescriptor); + } + } + } + } + } + else if (bIsControlDown) + { + // When control is pressed then do not alter the selection or the + // current page, just move the focus. + } + else + { + // Without shift just select the focused page. + mpModeHandler->SelectOnePage(pFocusedDescriptor); + } +} + +void SelectionFunction::DoCut() +{ + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoCut(); + } +} + +void SelectionFunction::DoCopy() +{ + mrController.GetClipboard().DoCopy(); +} + +void SelectionFunction::DoPaste() +{ + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoPaste(); + } +} + +bool SelectionFunction::cancel() +{ + mrController.GetFocusManager().ToggleFocus(); + return true; +} + +void SelectionFunction::GotoNextPage (int nOffset) +{ + model::SharedPageDescriptor pDescriptor + = mrController.GetCurrentSlideManager()->GetCurrentSlide(); + if (pDescriptor) + { + SdPage* pPage = pDescriptor->GetPage(); + OSL_ASSERT(pPage!=nullptr); + sal_Int32 nIndex = (pPage->GetPageNum()-1) / 2; + GotoPage(nIndex + nOffset); + } + ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::GotoPage (int nIndex) +{ + sal_uInt16 nPageCount = static_cast<sal_uInt16>(mrSlideSorter.GetModel().GetPageCount()); + + if (nIndex >= nPageCount) + nIndex = nPageCount - 1; + if (nIndex < 0) + nIndex = 0; + + mrController.GetFocusManager().SetFocusedPage(nIndex); + model::SharedPageDescriptor pNextPageDescriptor ( + mrSlideSorter.GetModel().GetPageDescriptor (nIndex)); + if (pNextPageDescriptor) + mpModeHandler->SetCurrentPage(pNextPageDescriptor); + else + { + OSL_ASSERT(pNextPageDescriptor); + } + ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::ProcessMouseEvent (sal_uInt32 nEventType, const MouseEvent& rEvent) +{ + // #95491# remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + + EventDescriptor aEventDescriptor (nEventType, rEvent, mrSlideSorter); + ProcessEvent(aEventDescriptor); +} + +void SelectionFunction::MouseDragged ( + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction) +{ + EventDescriptor aEventDescriptor (MOUSE_DRAG, rEvent, nDragAction, mrSlideSorter); + ProcessEvent(aEventDescriptor); +} + +void SelectionFunction::ProcessEvent (EventDescriptor& rDescriptor) +{ + // The call to ProcessEvent may switch to another mode handler. + // Prevent the untimely destruction of the called handler by acquiring a + // temporary reference here. + std::shared_ptr<ModeHandler> pModeHandler (mpModeHandler); + pModeHandler->ProcessEvent(rDescriptor); +} + +static bool Match ( + const sal_uInt32 nEventCode, + const sal_uInt32 nPositivePattern) +{ + return (nEventCode & nPositivePattern)==nPositivePattern; +} + +void SelectionFunction::SwitchToNormalMode() +{ + if (mpModeHandler->GetMode() != NormalMode) + SwitchMode(std::make_shared<NormalModeHandler>(mrSlideSorter, *this)); +} + +void SelectionFunction::SwitchToDragAndDropMode (const Point& rMousePosition) +{ + if (mpModeHandler->GetMode() == DragAndDropMode) + return; + + SwitchMode(std::make_shared<DragAndDropModeHandler>(mrSlideSorter, *this, rMousePosition, mpWindow)); +} + +void SelectionFunction::SwitchToMultiSelectionMode ( + const Point& rMousePosition, + const sal_uInt32 nEventCode) +{ + if (mpModeHandler->GetMode() != MultiSelectionMode) + SwitchMode(std::make_shared<MultiSelectionModeHandler>(mrSlideSorter, *this, rMousePosition, nEventCode)); +} + +void SelectionFunction::SwitchMode (const std::shared_ptr<ModeHandler>& rpHandler) +{ + // Not all modes allow mouse over indicator. + if (mpModeHandler->IsMouseOverIndicatorAllowed() != rpHandler->IsMouseOverIndicatorAllowed()) + { + if ( ! rpHandler->IsMouseOverIndicatorAllowed()) + { + mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor()); + } + else + mrSlideSorter.GetView().UpdatePageUnderMouse(); + } + + mpModeHandler = rpHandler; +} + +void SelectionFunction::ResetShiftKeySelectionAnchor() +{ + mnShiftKeySelectionAnchor = -1; +} + +void SelectionFunction::ResetMouseAnchor() +{ + if (mpModeHandler && mpModeHandler->GetMode() == NormalMode) + { + std::shared_ptr<NormalModeHandler> pHandler ( + std::dynamic_pointer_cast<NormalModeHandler>(mpModeHandler)); + if (pHandler) + pHandler->ResetButtonDownLocation(); + } +} + +//===== EventDescriptor ======================================================= + +SelectionFunction::EventDescriptor::EventDescriptor ( + const sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter const & rSlideSorter) + : maMousePosition(rEvent.GetPosPixel()), + mpHitPage(), + mnEventCode(nEventType), + meDragMode(InsertionIndicatorHandler::MoveMode), + mbIsLeaving(false) +{ + maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition); + mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition); + if (mpHitDescriptor) + { + mpHitPage = mpHitDescriptor->GetPage(); + } + + mnEventCode |= EncodeMouseEvent(rEvent); + mnEventCode |= EncodeState(); + + // Detect the mouse leaving the window. When not button is pressed then + // we can call IsLeaveWindow at the event. Otherwise we have to make an + // explicit test. + mbIsLeaving = rEvent.IsLeaveWindow() + || ! ::tools::Rectangle(Point(0,0), + rSlideSorter.GetContentWindow()->GetOutputSizePixel()).Contains(maMousePosition); +} + +SelectionFunction::EventDescriptor::EventDescriptor ( + const sal_uInt32 nEventType, + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction, + SlideSorter const & rSlideSorter) + : maMousePosition(rEvent.maPosPixel), + mpHitPage(), + mnEventCode(nEventType), + meDragMode(InsertionIndicatorHandler::GetModeFromDndAction(nDragAction)), + mbIsLeaving(false) +{ + maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition); + mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition); + if (mpHitDescriptor) + { + mpHitPage = mpHitDescriptor->GetPage(); + } + + mnEventCode |= EncodeState(); + + // Detect the mouse leaving the window. When not button is pressed then + // we can call IsLeaveWindow at the event. Otherwise we have to make an + // explicit test. + mbIsLeaving = rEvent.mbLeaving + || ! ::tools::Rectangle(Point(0,0), + rSlideSorter.GetContentWindow()->GetOutputSizePixel()).Contains(maMousePosition); +} + +sal_uInt32 SelectionFunction::EventDescriptor::EncodeMouseEvent ( + const MouseEvent& rEvent) const +{ + // Initialize with the type of mouse event. + sal_uInt32 nEventCode (mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION)); + + // Detect the affected button. + switch (rEvent.GetButtons()) + { + case MOUSE_LEFT: nEventCode |= LEFT_BUTTON; break; + case MOUSE_RIGHT: nEventCode |= RIGHT_BUTTON; break; + case MOUSE_MIDDLE: nEventCode |= MIDDLE_BUTTON; break; + } + + // Detect the number of clicks. + switch (rEvent.GetClicks()) + { + case 1: nEventCode |= SINGLE_CLICK; break; + case 2: nEventCode |= DOUBLE_CLICK; break; + } + + // Detect pressed modifier keys. + if (rEvent.IsShift()) + nEventCode |= SHIFT_MODIFIER; + if (rEvent.IsMod1()) + nEventCode |= CONTROL_MODIFIER; + + return nEventCode; +} + +sal_uInt32 SelectionFunction::EventDescriptor::EncodeState() const +{ + sal_uInt32 nEventCode (0); + + // Detect whether the event has happened over a page object. + if (mpHitPage!=nullptr && mpHitDescriptor) + { + if (mpHitDescriptor->HasState(model::PageDescriptor::ST_Selected)) + nEventCode |= OVER_SELECTED_PAGE; + else + nEventCode |= OVER_UNSELECTED_PAGE; + } + + return nEventCode; +} + +//===== SelectionFunction::ModeHandler ======================================== + +SelectionFunction::ModeHandler::ModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const bool bIsMouseOverIndicatorAllowed) + : mrSlideSorter(rSlideSorter), + mrSelectionFunction(rSelectionFunction), + mbIsMouseOverIndicatorAllowed(bIsMouseOverIndicatorAllowed) +{ +} + +SelectionFunction::ModeHandler::~ModeHandler() COVERITY_NOEXCEPT_FALSE +{ +} + +void SelectionFunction::ModeHandler::ReprocessEvent (EventDescriptor& rDescriptor) +{ + mrSelectionFunction.ProcessEvent(rDescriptor); +} + +void SelectionFunction::ModeHandler::ProcessEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + PageSelector::UpdateLock aUpdateLock (mrSlideSorter); + + bool bIsProcessed (false); + switch (rDescriptor.mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION | MOUSE_DRAG)) + { + case BUTTON_DOWN: + bIsProcessed = ProcessButtonDownEvent(rDescriptor); + break; + + case BUTTON_UP: + bIsProcessed = ProcessButtonUpEvent(rDescriptor); + break; + + case MOUSE_MOTION: + bIsProcessed = ProcessMotionEvent(rDescriptor); + break; + + case MOUSE_DRAG: + bIsProcessed = ProcessDragEvent(rDescriptor); + break; + } + + if ( ! bIsProcessed) + HandleUnprocessedEvent(rDescriptor); +} + +bool SelectionFunction::ModeHandler::ProcessButtonDownEvent (EventDescriptor&) +{ + return false; +} + +bool SelectionFunction::ModeHandler::ProcessButtonUpEvent (EventDescriptor&) +{ + mrSelectionFunction.SwitchToNormalMode(); + return false; +} + +bool SelectionFunction::ModeHandler::ProcessMotionEvent (EventDescriptor& rDescriptor) +{ + if (mbIsMouseOverIndicatorAllowed) + mrSlideSorter.GetView().UpdatePageUnderMouse(rDescriptor.maMousePosition); + + if (rDescriptor.mbIsLeaving) + { + mrSelectionFunction.SwitchToNormalMode(); + mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor()); + + return true; + } + else + return false; +} + +bool SelectionFunction::ModeHandler::ProcessDragEvent (EventDescriptor&) +{ + return false; +} + +bool SelectionFunction::ModeHandler::HandleUnprocessedEvent (EventDescriptor&) +{ + return false; +} + +void SelectionFunction::ModeHandler::SetCurrentPage ( + const model::SharedPageDescriptor& rpDescriptor) +{ + SelectOnePage(rpDescriptor); + mrSlideSorter.GetController().GetCurrentSlideManager()->SwitchCurrentSlide(rpDescriptor); +} + +void SelectionFunction::ModeHandler::DeselectAllPages() +{ + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + mrSelectionFunction.ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::ModeHandler::SelectOnePage ( + const model::SharedPageDescriptor& rpDescriptor) +{ + DeselectAllPages(); + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); +} + +void SelectionFunction::ModeHandler::SwitchView (const model::SharedPageDescriptor& rpDescriptor) +{ + // Switch to the draw view. This is done only when the current + // view is the main view. + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell==nullptr || !pViewShell->IsMainViewShell()) + return; + + if (rpDescriptor && rpDescriptor->GetPage()!=nullptr) + { + mrSlideSorter.GetModel().GetDocument()->SetSelected(rpDescriptor->GetPage(), true); + pViewShell->GetFrameView()->SetSelectedPage( + (rpDescriptor->GetPage()->GetPageNum()-1)/2); + } + if (mrSlideSorter.GetViewShellBase() != nullptr) + framework::FrameworkHelper::Instance(*mrSlideSorter.GetViewShellBase())->RequestView( + framework::FrameworkHelper::msImpressViewURL, + framework::FrameworkHelper::msCenterPaneURL); +} + +void SelectionFunction::ModeHandler::StartDrag ( + const Point& rMousePosition) +{ + // Do not start a drag-and-drop operation when one is already active. + // (when dragging pages from one document into another, pressing a + // modifier key can trigger a MouseMotion event in the originating + // window (focus still in there). Together with the mouse button pressed + // (drag-and-drop is active) this triggers the start of drag-and-drop.) + if (SD_MOD()->pTransferDrag != nullptr) + return; + + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrSelectionFunction.SwitchToDragAndDropMode(rMousePosition); + } +} + +//===== NormalModeHandler ===================================================== + +NormalModeHandler::NormalModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction) + : ModeHandler(rSlideSorter, rSelectionFunction, true) +{ +} + +SelectionFunction::Mode NormalModeHandler::GetMode() const +{ + return SelectionFunction::NormalMode; +} + +void NormalModeHandler::Abort() +{ +} + +bool NormalModeHandler::ProcessButtonDownEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // Remember the location where the left button is pressed. With + // that we can filter away motion events that are caused by key + // presses. We also can tune the minimal motion distance that + // triggers a drag-and-drop operation. + if ((rDescriptor.mnEventCode & BUTTON_DOWN) != 0) + maButtonDownLocation = rDescriptor.maMousePosition; + + switch (rDescriptor.mnEventCode) + { + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + break; + + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_SELECTED_PAGE: + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_UNSELECTED_PAGE: + // A double click always shows the selected slide in the center + // pane in an edit view. + SetCurrentPage(rDescriptor.mpHitDescriptor); + SwitchView(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | SHIFT_MODIFIER: + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | SHIFT_MODIFIER: + // Range selection with the shift modifier. + RangeSelect(rDescriptor.mpHitDescriptor); + break; + + // Right button for context menu. + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + // Single right click and shift+F10 select as preparation to + // show the context menu. Change the selection only when the + // page under the mouse is not selected. In this case the + // selection is set to this single page. Otherwise the + // selection is not modified. + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + // Do not change the selection. Just adjust the insertion indicator. + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE: + // Remember the current selection so that when a multi selection + // is started, we can restore the previous selection. + mrSlideSorter.GetModel().SaveCurrentSelection(); + DeselectAllPages(); + break; + + case ANY_MODIFIER(BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE): + // Remember the current selection so that when a multi selection + // is started, we can restore the previous selection. + mrSlideSorter.GetModel().SaveCurrentSelection(); + DeselectAllPages(); + break; + + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | NOT_OVER_PAGE: + { + // Insert a new slide: + // First of all we need to set the insertion indicator which sets the + // position where the new slide will be inserted. + std::shared_ptr<InsertionIndicatorHandler> pInsertionIndicatorHandler + = mrSlideSorter.GetController().GetInsertionIndicatorHandler(); + + pInsertionIndicatorHandler->Start(false); + pInsertionIndicatorHandler->UpdatePosition( + rDescriptor.maMousePosition, + InsertionIndicatorHandler::MoveMode); + + mrSlideSorter.GetController().GetSelectionManager()->SetInsertionPosition( + pInsertionIndicatorHandler->GetInsertionPageIndex()); + + mrSlideSorter.GetViewShell()->GetDispatcher()->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + pInsertionIndicatorHandler->End(Animator::AM_Immediate); + + break; + } + + default: + return false; + } + return true; +} + +bool NormalModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + bool bIsProcessed (true); + switch (rDescriptor.mnEventCode) + { + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + // Multi selection with the control modifier. + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | CONTROL_MODIFIER: + mrSlideSorter.GetController().GetPageSelector().DeselectPage( + rDescriptor.mpHitDescriptor); + break; + + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | CONTROL_MODIFIER: + mrSlideSorter.GetController().GetPageSelector().SelectPage( + rDescriptor.mpHitDescriptor); + mrSlideSorter.GetView().SetPageUnderMouse(rDescriptor.mpHitDescriptor); + break; + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE: + break; + + default: + bIsProcessed = false; + break; + } + mrSelectionFunction.SwitchToNormalMode(); + return bIsProcessed; +} + +bool NormalModeHandler::ProcessMotionEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (ModeHandler::ProcessMotionEvent(rDescriptor)) + return true; + + bool bIsProcessed (true); + switch (rDescriptor.mnEventCode) + { + // A mouse motion without visible substitution starts that. + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE): + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE): + { + if (maButtonDownLocation) + { + const sal_Int32 nDistance(std::max( + std::abs(maButtonDownLocation->X() - rDescriptor.maMousePosition.X()), + std::abs(maButtonDownLocation->Y() - rDescriptor.maMousePosition.Y()))); + if (nDistance > 3) + StartDrag(rDescriptor.maMousePosition); + } + break; + } + + // A mouse motion not over a page starts a rectangle selection. + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE): + mrSelectionFunction.SwitchToMultiSelectionMode( + rDescriptor.maMouseModelPosition, + rDescriptor.mnEventCode); + break; + + default: + bIsProcessed = false; + break; + } + return bIsProcessed; +} + +bool NormalModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) +{ + mrSelectionFunction.SwitchToDragAndDropMode(rDescriptor.maMousePosition); + ReprocessEvent(rDescriptor); + return true; +} + +void NormalModeHandler::RangeSelect (const model::SharedPageDescriptor& rpDescriptor) +{ + PageSelector::UpdateLock aLock (mrSlideSorter); + PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + + model::SharedPageDescriptor pAnchor (rSelector.GetSelectionAnchor()); + DeselectAllPages(); + + if (!pAnchor) + return; + + // Select all pages between the anchor and the given one, including + // the two. + const sal_uInt16 nAnchorIndex ((pAnchor->GetPage()->GetPageNum()-1) / 2); + const sal_uInt16 nOtherIndex ((rpDescriptor->GetPage()->GetPageNum()-1) / 2); + + // Iterate over all pages in the range. Start with the anchor + // page. This way the PageSelector will recognize it again as + // anchor (the first selected page after a DeselectAllPages() + // becomes the anchor.) + const sal_uInt16 nStep ((nAnchorIndex < nOtherIndex) ? +1 : -1); + sal_uInt16 nIndex (nAnchorIndex); + while (true) + { + rSelector.SelectPage(nIndex); + if (nIndex == nOtherIndex) + break; + nIndex = nIndex + nStep; + } +} + +void NormalModeHandler::ResetButtonDownLocation() +{ + maButtonDownLocation = ::std::optional<Point>(); +} + +//===== MultiSelectionModeHandler ============================================= + +MultiSelectionModeHandler::MultiSelectionModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMouseModelPosition, + const sal_uInt32 nEventCode) + : ModeHandler(rSlideSorter, rSelectionFunction, false), + meSelectionMode(SM_Normal), + maSecondCorner(rMouseModelPosition), + maSavedPointer(mrSlideSorter.GetContentWindow()->GetPointer()), + mbAutoScrollInstalled(false), + mnAnchorIndex(-1), + mnSecondIndex(-1) +{ + + mrSlideSorter.GetContentWindow()->SetPointer(PointerStyle::Text); + SetSelectionModeFromModifier(nEventCode); +} + +MultiSelectionModeHandler::~MultiSelectionModeHandler() +{ + if (mbAutoScrollInstalled) + { + //a call to this handler's MultiSelectionModeHandler::UpdatePosition + //may be still waiting to be called back + mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor(); + } + mrSlideSorter.GetContentWindow()->SetPointer(maSavedPointer); +} + +SelectionFunction::Mode MultiSelectionModeHandler::GetMode() const +{ + return SelectionFunction::MultiSelectionMode; +} + +void MultiSelectionModeHandler::Abort() +{ + mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection()); +} + +void MultiSelectionModeHandler::ProcessEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // During a multi selection we do not want sudden jumps of the + // visible area caused by moving newly selected pages into view. + // Therefore disable that temporarily. The disabled object is + // released at the end of the event processing, after the focus and + // current slide have been updated. + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + + ModeHandler::ProcessEvent(rDescriptor); +} + +bool MultiSelectionModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (mbAutoScrollInstalled) + { + //a call to this handler's MultiSelectionModeHandler::UpdatePosition + //may be still waiting to be called back + mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor(); + mbAutoScrollInstalled = false; + } + + if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK)) + { + mrSelectionFunction.SwitchToNormalMode(); + return true; + } + else + return false; +} + +bool MultiSelectionModeHandler::ProcessMotionEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // The selection rectangle is visible. Handle events accordingly. + if (Match(rDescriptor.mnEventCode, MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK)) + { + SetSelectionModeFromModifier(rDescriptor.mnEventCode); + UpdatePosition(rDescriptor.maMousePosition, true); + return true; + } + else + return false; +} + +bool MultiSelectionModeHandler::HandleUnprocessedEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if ( ! ModeHandler::HandleUnprocessedEvent(rDescriptor)) + { + // If the event has not been processed then stop multi selection. + mrSelectionFunction.SwitchToNormalMode(); + ReprocessEvent(rDescriptor); + } + return true; +} + +void MultiSelectionModeHandler::UpdatePosition ( + const Point& rMousePosition, + const bool bAllowAutoScroll) +{ + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + + // Convert window coordinates into model coordinates (we need the + // window coordinates for auto-scrolling because that remains + // constant while scrolling.) + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + const Point aMouseModelPosition (pWindow->PixelToLogic(rMousePosition)); + + bool bDoAutoScroll = bAllowAutoScroll && mrSlideSorter.GetController().GetScrollBarManager().AutoScroll( + rMousePosition, + [this, &rMousePosition] () { return this->UpdatePosition(rMousePosition, false); }); + + if (!bDoAutoScroll) + UpdateModelPosition(aMouseModelPosition); + + mbAutoScrollInstalled |= bDoAutoScroll; +} + +void MultiSelectionModeHandler::SetSelectionModeFromModifier ( + const sal_uInt32 nEventCode) +{ + switch (nEventCode & MODIFIER_MASK) + { + case NO_MODIFIER: + SetSelectionMode(SM_Normal); + break; + + case SHIFT_MODIFIER: + SetSelectionMode(SM_Add); + break; + + case CONTROL_MODIFIER: + SetSelectionMode(SM_Toggle); + break; + } +} + +void MultiSelectionModeHandler::SetSelectionMode (const SelectionMode eSelectionMode) +{ + if (meSelectionMode != eSelectionMode) + { + meSelectionMode = eSelectionMode; + UpdateSelection(); + } +} + +void MultiSelectionModeHandler::UpdateSelectionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bIsInSelection) const +{ + // Determine whether the page was selected before the rectangle + // selection was started. + const bool bWasSelected (rpDescriptor->HasState(model::PageDescriptor::ST_WasSelected)); + + // Combine the two selection states depending on the selection mode. + bool bSelect (false); + switch(meSelectionMode) + { + case SM_Normal: + bSelect = bIsInSelection; + break; + + case SM_Add: + bSelect = bIsInSelection || bWasSelected; + break; + + case SM_Toggle: + if (bIsInSelection) + bSelect = !bWasSelected; + else + bSelect = bWasSelected; + break; + } + + // Set the new selection state. + if (bSelect) + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); + else + mrSlideSorter.GetController().GetPageSelector().DeselectPage(rpDescriptor); +} + +void MultiSelectionModeHandler::UpdateModelPosition (const Point& rMouseModelPosition) +{ + maSecondCorner = rMouseModelPosition; + UpdateSelection(); +} + +void MultiSelectionModeHandler::UpdateSelection() +{ + view::SlideSorterView::DrawLock aLock (mrSlideSorter); + + model::SlideSorterModel& rModel (mrSlideSorter.GetModel()); + const sal_Int32 nPageCount (rModel.GetPageCount()); + + const sal_Int32 nIndexUnderMouse ( + mrSlideSorter.GetView().GetLayouter().GetIndexAtPoint ( + maSecondCorner, + false, + false)); + if (nIndexUnderMouse < 0 || nIndexUnderMouse >= nPageCount) + return; + + if (mnAnchorIndex < 0) + mnAnchorIndex = nIndexUnderMouse; + mnSecondIndex = nIndexUnderMouse; + + Range aRange (mnAnchorIndex, mnSecondIndex); + aRange.Justify(); + + for (sal_Int32 nIndex=0; nIndex<nPageCount; ++nIndex) + { + UpdateSelectionState(rModel.GetPageDescriptor(nIndex), aRange.Contains(nIndex)); + } +} + +//===== DragAndDropModeHandler ================================================ + +DragAndDropModeHandler::DragAndDropModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMousePosition, + vcl::Window* pWindow) + : ModeHandler(rSlideSorter, rSelectionFunction, false) +{ + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + if (pDragTransferable==nullptr && mrSlideSorter.GetViewShell() != nullptr) + { + SlideSorterViewShell* pSlideSorterViewShell + = dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()); + if (pSlideSorterViewShell != nullptr) + pSlideSorterViewShell->StartDrag(rMousePosition, pWindow); + pDragTransferable = SD_MOD()->pTransferDrag; + } + + mpDragAndDropContext.reset(new DragAndDropContext(mrSlideSorter)); + mrSlideSorter.GetController().GetInsertionIndicatorHandler()->Start( + pDragTransferable != nullptr + && pDragTransferable->GetView()==&mrSlideSorter.GetView()); +} + +DragAndDropModeHandler::~DragAndDropModeHandler() +{ + if (mpDragAndDropContext) + { + // Disconnect the substitution handler from this selection function. + mpDragAndDropContext->SetTargetSlideSorter(); + mpDragAndDropContext.reset(); + } + mrSlideSorter.GetController().GetInsertionIndicatorHandler()->End(Animator::AM_Animated); +} + +SelectionFunction::Mode DragAndDropModeHandler::GetMode() const +{ + return SelectionFunction::DragAndDropMode; +} + +void DragAndDropModeHandler::Abort() +{ + mrSlideSorter.GetController().GetClipboard().Abort(); + if (mpDragAndDropContext) + mpDragAndDropContext->Dispose(); + // mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection()); +} + +bool DragAndDropModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON)) + { + // The following Process() call may lead to the destruction + // of rDescriptor.mpHitDescriptor so release our reference to it. + rDescriptor.mpHitDescriptor.reset(); + mrSelectionFunction.SwitchToNormalMode(); + return true; + } + else + return false; +} + +bool DragAndDropModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) +{ + OSL_ASSERT(mpDragAndDropContext); + + if (rDescriptor.mbIsLeaving) + { + mrSelectionFunction.SwitchToNormalMode(); + } + else if (mpDragAndDropContext) + { + mpDragAndDropContext->UpdatePosition( + rDescriptor.maMousePosition, + rDescriptor.meDragMode, true); + } + + return true; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |