diff options
Diffstat (limited to 'sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx')
-rwxr-xr-x | sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx | 1317 |
1 files changed, 1317 insertions, 0 deletions
diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx new file mode 100755 index 000000000000..c1d742ce7158 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx @@ -0,0 +1,1317 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_sd.hxx" + +#include "controller/SlsSelectionFunction.hxx" + +#include "SlideSorter.hxx" +#include "SlideSorterViewShell.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/SlsSelectionManager.hxx" +#include "controller/SlsProperties.hxx" +#include "model/SlideSorterModel.hxx" +#include "model/SlsPageDescriptor.hxx" +#include "model/SlsPageEnumerationProvider.hxx" +#include "view/SlideSorterView.hxx" +#include "view/SlsViewOverlay.hxx" +#include "view/SlsLayouter.hxx" +#include "view/SlsPageObjectViewObjectContact.hxx" +#include "framework/FrameworkHelper.hxx" +#include "ViewShellBase.hxx" +#include "DrawController.hxx" +#include <vcl/sound.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdpagv.hxx> +#include <vcl/msgbox.hxx> +#include "Window.hxx" +#include "sdpage.hxx" +#include "drawdoc.hxx" +#include "ViewShell.hxx" +#include "ViewShellBase.hxx" +#include "FrameView.hxx" +#include "app.hrc" +#include "sdresid.hxx" +#include "strings.hrc" + +namespace { +static const sal_uInt32 SINGLE_CLICK (0x00000001); +static const sal_uInt32 DOUBLE_CLICK (0x00000002); +static const sal_uInt32 LEFT_BUTTON (0x00000010); +static const sal_uInt32 RIGHT_BUTTON (0x00000020); +static const sal_uInt32 MIDDLE_BUTTON (0x00000040); +static const sal_uInt32 BUTTON_DOWN (0x00000100); +static const sal_uInt32 BUTTON_UP (0x00000200); +static const sal_uInt32 MOUSE_MOTION (0x00000400); +// The rest leaves the lower 16 bit untouched so that it can be used with +// key codes. +static const sal_uInt32 OVER_SELECTED_PAGE (0x00010000); +static const sal_uInt32 OVER_UNSELECTED_PAGE (0x00020000); +static const sal_uInt32 OVER_FADE_INDICATOR (0x00040000); +static const sal_uInt32 SHIFT_MODIFIER (0x00100000); +static const sal_uInt32 CONTROL_MODIFIER (0x00200000); +static const sal_uInt32 SUBSTITUTION_VISIBLE (0x01000000); +static const sal_uInt32 RECTANGLE_VISIBLE (0x02000000); + +static const sal_uInt32 KEY_EVENT (0x10000000); + +// Some absent events are defined so they can be expressed explicitly. +static const sal_uInt32 NO_MODIFIER (0x00000000); +static const sal_uInt32 SUBSTITUTION_NOT_VISIBLE (0x00000000); +static const sal_uInt32 NOT_OVER_PAGE (0x00000000); + + +} // end of anonymous namespace + + +namespace sd { namespace slidesorter { namespace controller { + +class SelectionFunction::SubstitutionHandler +{ +public: + SubstitutionHandler (SlideSorter& rSlideSorter); + ~SubstitutionHandler (void); + + /** Create a substitution display of the currently selected pages and + use the given position as the anchor point. + */ + void Start (const Point& rMouseModelPosition); + + /** Move the substitution display by the distance the mouse has + travelled since the last call to this method or to + CreateSubstitution(). The given point becomes the new anchor. + */ + void UpdatePosition (const Point& rMouseModelPosition); + + /** Move the substitution display of the currently selected pages. + */ + void Process (void); + + void End (void); + + bool HasBeenMoved (void) const; + +private: + SlideSorter& mrSlideSorter; + + bool mbHasBeenMoved; + + /** Determine whether there is a) a substitution and b) its insertion at + the current position of the insertion marker would alter the + document. This would be the case when the substitution has been + moved or is not consecutive. + */ + bool IsSubstitutionInsertionNonTrivial (void) const; +}; + + +class SelectionFunction::EventDescriptor +{ +public: + + Point maMousePosition; + Point maMouseModelPosition; + ::boost::weak_ptr<model::PageDescriptor> mpHitDescriptor; + SdrPage* mpHitPage; + sal_uInt32 mnEventCode; + + EventDescriptor ( + sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter& rSlideSorter); + EventDescriptor ( + const KeyEvent& rEvent, + SlideSorter& rSlideSorter); +}; + + +TYPEINIT1(SelectionFunction, FuPoor); + + +SelectionFunction::SelectionFunction ( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) + : SlideFunction (rSlideSorter, rRequest), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mbDragSelection(false), + maInsertionMarkerBox(), + mbProcessingMouseButtonDown(false), + mpSubstitutionHandler(new SubstitutionHandler(mrSlideSorter)) +{ + //af aDelayToScrollTimer.SetTimeout(50); + aDragTimer.SetTimeoutHdl( LINK( this, SelectionFunction, DragSlideHdl ) ); +} + +SelectionFunction::~SelectionFunction (void) +{ + aDragTimer.Stop(); +} + + + + +FunctionReference SelectionFunction::Create( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) +{ + FunctionReference xFunc( new SelectionFunction( rSlideSorter, rRequest ) ); + return xFunc; +} + + + + +BOOL SelectionFunction::MouseButtonDown (const MouseEvent& rEvent) +{ + // #95491# remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + mbProcessingMouseButtonDown = true; + + mpWindow->CaptureMouse(); + + ProcessMouseEvent(BUTTON_DOWN, rEvent); + + return TRUE; +} + + + + +BOOL SelectionFunction::MouseMove (const MouseEvent& rEvent) +{ + Point aMousePosition (rEvent.GetPosPixel()); + + // Determine page under mouse and show the mouse over effect. + model::SharedPageDescriptor pHitDescriptor (mrController.GetPageAt(aMousePosition)); + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + rOverlay.GetMouseOverIndicatorOverlay().SetSlideUnderMouse( + rEvent.IsLeaveWindow() ? model::SharedPageDescriptor() : pHitDescriptor); + if (pHitDescriptor.get() != NULL) + rOverlay.GetMouseOverIndicatorOverlay().setVisible(true); + else + rOverlay.GetMouseOverIndicatorOverlay().setVisible(false); + + // Allow one mouse move before the drag timer is disabled. + if (aDragTimer.IsActive()) + { + if (bFirstMouseMove) + bFirstMouseMove = FALSE; + else + aDragTimer.Stop(); + } + + Rectangle aRectangle (Point(0,0),mpWindow->GetOutputSizePixel()); + if ( ! aRectangle.IsInside(aMousePosition) + && rOverlay.GetSubstitutionOverlay().isVisible()) + { + // Mouse left the window with pressed left button. Make it a drag. + StartDrag(); + } + else + { + // Call ProcessMouseEvent() only when one of the buttons is + // pressed. This prevents calling the method on every motion. + if (rEvent.GetButtons() != 0 + && mbProcessingMouseButtonDown) + { + ProcessMouseEvent(MOUSE_MOTION, rEvent); + } + } + + return TRUE; +} + + + + +BOOL SelectionFunction::MouseButtonUp (const MouseEvent& rEvent) +{ + mrController.GetScrollBarManager().StopAutoScroll (); + + // #95491# remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + + if (aDragTimer.IsActive()) + aDragTimer.Stop(); + + ProcessMouseEvent(BUTTON_UP, rEvent); + + mbProcessingMouseButtonDown = false; + mpWindow->ReleaseMouse(); + + return TRUE; +} + + + + +BOOL SelectionFunction::KeyInput (const KeyEvent& rEvent) +{ + FocusManager& rFocusManager (mrController.GetFocusManager()); + BOOL bResult = FALSE; + + switch (rEvent.GetKeyCode().GetCode()) + { + case KEY_RETURN: + if (rFocusManager.HasFocus()) + { + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + if (pDescriptor.get() != NULL) + { + SetCurrentPage(pDescriptor); + SwitchView(pDescriptor); + } + bResult = TRUE; + } + break; + + case KEY_TAB: + if ( ! rFocusManager.IsFocusShowing()) + rFocusManager.ShowFocus(); + else + if (rEvent.GetKeyCode().IsShift()) + rFocusManager.MoveFocus (FocusManager::FMD_LEFT); + else + rFocusManager.MoveFocus (FocusManager::FMD_RIGHT); + bResult = TRUE; + break; + + case KEY_ESCAPE: + rFocusManager.SetFocusToToolBox(); + bResult = TRUE; + break; + + case KEY_SPACE: + { + // Toggle the selection state. + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + if (pDescriptor.get() != NULL) + { + // Doing a multi selection by default. Can we ask the event + // for the state of the shift key? + if (pDescriptor->IsSelected()) + mrController.GetPageSelector().DeselectPage(pDescriptor); + else + mrController.GetPageSelector().SelectPage(pDescriptor); + } + bResult = TRUE; + } + break; + + + // Move the focus indicator left. + case KEY_LEFT: + rFocusManager.MoveFocus (FocusManager::FMD_LEFT); + bResult = TRUE; + break; + + // Move the focus indicator right. + case KEY_RIGHT: + rFocusManager.MoveFocus (FocusManager::FMD_RIGHT); + bResult = TRUE; + break; + + // Move the focus indicator up. + case KEY_UP: + rFocusManager.MoveFocus (FocusManager::FMD_UP); + bResult = TRUE; + break; + + // Move the focus indicator down. + case KEY_DOWN: + rFocusManager.MoveFocus (FocusManager::FMD_DOWN); + 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_DELETE: + case KEY_BACKSPACE: + { + if (mrController.GetProperties()->IsUIReadOnly()) + break; + + int nSelectedPagesCount = 0; + + // Count the selected pages and look if there any objects on any + // of the selected pages so that we can warn the user and + // prevent an accidental deletion. + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + nSelectedPagesCount++; + aSelectedPages.GetNextElement(); + } + + if (nSelectedPagesCount > 0) + mrController.GetSelectionManager()->DeleteSelectedPages(); + + bResult = TRUE; + } + break; + + case KEY_F10: + if (rEvent.GetKeyCode().IsShift()) + ProcessKeyEvent(rEvent); + break; + + default: + break; + } + + if ( ! bResult) + bResult = SlideFunction::KeyInput (rEvent); + + return bResult; +} + + + + +void SelectionFunction::Activate() +{ + FuPoor::Activate(); +} + + + + +void SelectionFunction::Deactivate() +{ + FuPoor::Deactivate(); +} + + + +void SelectionFunction::ScrollStart (void) +{ +} + + + + +void SelectionFunction::ScrollEnd (void) +{ +} + + + + +void SelectionFunction::DoCut (void) +{ + if ( ! mrController.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoCut(); + } +} + + + + +void SelectionFunction::DoCopy (void) +{ + mrController.GetClipboard().DoCopy(); +} + + + + +void SelectionFunction::DoPaste (void) +{ + if ( ! mrController.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoPaste(); + } +} + + + + +void SelectionFunction::Paint (const Rectangle&, ::sd::Window* ) +{ +} + + + + +IMPL_LINK( SelectionFunction, DragSlideHdl, Timer*, EMPTYARG ) +{ + StartDrag(); + return 0; +} + + + + +void SelectionFunction::StartDrag (void) +{ + if (mbPageHit + && ! mrController.GetProperties()->IsUIReadOnly()) + { + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + mpSubstitutionHandler->Start(rOverlay.GetSubstitutionOverlay().GetPosition()); + mbPageHit = false; + mpWindow->ReleaseMouse(); + + if (mrSlideSorter.GetViewShell() != NULL) + { + SlideSorterViewShell* pSlideSorterViewShell + = dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()); + pSlideSorterViewShell->StartDrag ( + rOverlay.GetSubstitutionOverlay().GetPosition(), + mpWindow); + } + } +} + + + + +bool SelectionFunction::cancel (void) +{ + mrController.GetFocusManager().ToggleFocus(); + return true; +} + + + + +void SelectionFunction::SelectHitPage (const model::SharedPageDescriptor& rpDescriptor) +{ + mrController.GetPageSelector().SelectPage(rpDescriptor); +} + + + + +void SelectionFunction::DeselectHitPage (const model::SharedPageDescriptor& rpDescriptor) +{ + mrController.GetPageSelector().DeselectPage(rpDescriptor); +} + + + + +void SelectionFunction::DeselectAllPages (void) +{ + mrController.GetPageSelector().DeselectAllPages(); +} + + + + +void SelectionFunction::StartRectangleSelection (const Point& rMouseModelPosition) +{ + if (mrController.GetProperties()->IsShowSelection()) + { + mrSlideSorter.GetView().GetOverlay().GetSelectionRectangleOverlay().Start( + rMouseModelPosition); + } +} + + + + +void SelectionFunction::UpdateRectangleSelection (const Point& rMouseModelPosition) +{ + if (mrController.GetProperties()->IsShowSelection()) + { + mrSlideSorter.GetView().GetOverlay().GetSelectionRectangleOverlay().Update( + rMouseModelPosition); + } +} + + + + +void SelectionFunction::ProcessRectangleSelection (bool bToggleSelection) +{ + if ( ! mrController.GetProperties()->IsShowSelection()) + return; + + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + if (rOverlay.GetSelectionRectangleOverlay().isVisible()) + { + PageSelector& rSelector (mrController.GetPageSelector()); + + rOverlay.GetSelectionRectangleOverlay().setVisible(false); + + // Select all pages whose page object lies completly inside the drag + // rectangle. + const Rectangle& rSelectionRectangle ( + rOverlay.GetSelectionRectangleOverlay().GetSelectionRectangle()); + model::PageEnumeration aPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration( + mrSlideSorter.GetModel())); + while (aPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + Rectangle aPageBox (mrSlideSorter.GetView().GetPageBoundingBox( + pDescriptor, + view::SlideSorterView::CS_MODEL, + view::SlideSorterView::BBT_SHAPE)); + if (rSelectionRectangle.IsOver(aPageBox)) + { + // When we are extending the selection (shift key is + // pressed) then toggle the selection state of the page. + // Otherwise select it: this results in the previously + // selected pages becoming deslected. + if (bToggleSelection && pDescriptor->IsSelected()) + rSelector.DeselectPage(pDescriptor); + else + rSelector.SelectPage(pDescriptor); + } + } + } +} + + + + +void SelectionFunction::PrepareMouseMotion (const Point& ) +{ + if ( ! mrController.GetProperties()->IsUIReadOnly()) + { + bFirstMouseMove = TRUE; + aDragTimer.Start(); + } +} + + + + +void SelectionFunction::RangeSelect (const model::SharedPageDescriptor& rpDescriptor) +{ + PageSelector& rSelector (mrController.GetPageSelector()); + + model::SharedPageDescriptor pAnchor (rSelector.GetSelectionAnchor()); + DeselectAllPages(); + + if (pAnchor.get() != NULL) + { + // Select all pages between the anchor and the given one, including + // the two. + USHORT nAnchorIndex ((pAnchor->GetPage()->GetPageNum()-1) / 2); + USHORT 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.) + USHORT nStep = (nAnchorIndex < nOtherIndex) ? +1 : -1; + USHORT nIndex = nAnchorIndex; + while (true) + { + rSelector.SelectPage(nIndex); + if (nIndex == nOtherIndex) + break; + nIndex = nIndex + nStep; + } + } +} + + + + +void SelectionFunction::GotoNextPage (int nOffset) +{ + model::SharedPageDescriptor pDescriptor + = mrController.GetCurrentSlideManager()->GetCurrentSlide(); + if (pDescriptor.get() != NULL) + { + SdPage* pPage = pDescriptor->GetPage(); + OSL_ASSERT(pPage!=NULL); + sal_Int32 nIndex = (pPage->GetPageNum()-1) / 2; + nIndex += nOffset; + USHORT nPageCount = (USHORT)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.get() != NULL) + SetCurrentPage(pNextPageDescriptor); + else + { + OSL_ASSERT(pNextPageDescriptor.get() != NULL); + } + } +} + + + + +void SelectionFunction::ProcessMouseEvent (sal_uInt32 nEventType, const MouseEvent& rEvent) +{ + // #95491# remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + + // 1. Compute some frequently used values relating to the event. + ::std::auto_ptr<EventDescriptor> pEventDescriptor ( + new EventDescriptor(nEventType, rEvent, mrSlideSorter)); + + // 2. Compute a numerical code that describes the event and that is used + // for fast look-up of the associated reaction. + pEventDescriptor->mnEventCode = EncodeMouseEvent(*pEventDescriptor, rEvent); + + // 3. Process the event. + EventPreprocessing(*pEventDescriptor); + if ( ! EventProcessing(*pEventDescriptor)) + { + OSL_TRACE ("can not handle event code %x", pEventDescriptor->mnEventCode); + } + EventPostprocessing(*pEventDescriptor); + + if (nEventType == BUTTON_UP) + mbPageHit = false; +} + + + + +sal_uInt32 SelectionFunction::EncodeMouseEvent ( + const EventDescriptor& rDescriptor, + const MouseEvent& rEvent) const +{ + // Initialize with the type of mouse event. + sal_uInt32 nEventCode (rDescriptor.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 whether the event has happened over a page object. + if (rDescriptor.mpHitPage != NULL && ! rDescriptor.mpHitDescriptor.expired()) + { + model::SharedPageDescriptor pHitDescriptor (rDescriptor.mpHitDescriptor); + if (pHitDescriptor->IsSelected()) + nEventCode |= OVER_SELECTED_PAGE; + else + nEventCode |= OVER_UNSELECTED_PAGE; + } + + // Detect pressed modifier keys. + if (rEvent.IsShift()) + nEventCode |= SHIFT_MODIFIER; + if (rEvent.IsMod1()) + nEventCode |= CONTROL_MODIFIER; + + // Detect whether we are dragging pages or dragging a selection rectangle. + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + if (rOverlay.GetSubstitutionOverlay().isVisible()) + nEventCode |= SUBSTITUTION_VISIBLE; + if (rOverlay.GetSelectionRectangleOverlay().isVisible()) + nEventCode |= RECTANGLE_VISIBLE; + + return nEventCode; +} + + + + +void SelectionFunction::ProcessKeyEvent (const KeyEvent& rEvent) +{ + // 1. Compute some frequently used values relating to the event. + ::std::auto_ptr<EventDescriptor> pEventDescriptor ( + new EventDescriptor(rEvent, mrSlideSorter)); + + // 2. Encode the event. + pEventDescriptor->mnEventCode = EncodeKeyEvent(*pEventDescriptor, rEvent); + + // 3. Process the event. + EventPreprocessing(*pEventDescriptor); + if ( ! EventProcessing(*pEventDescriptor)) + OSL_TRACE ("can not handle event code %x", pEventDescriptor->mnEventCode); + EventPostprocessing(*pEventDescriptor); +} + + + + +sal_uInt32 SelectionFunction::EncodeKeyEvent ( + const EventDescriptor& rDescriptor, + const KeyEvent& rEvent) const +{ + // Initialize as key event. + sal_uInt32 nEventCode (KEY_EVENT); + + // Add the actual key code in the lower 16 bit. + nEventCode |= rEvent.GetKeyCode().GetCode(); + + // Detect pressed modifier keys. + if (rEvent.GetKeyCode().IsShift()) + nEventCode |= SHIFT_MODIFIER; + if (rEvent.GetKeyCode().IsMod1()) + nEventCode |= CONTROL_MODIFIER; + + // Detect whether the event has happened over a page object. + if (rDescriptor.mpHitPage != NULL && ! rDescriptor.mpHitDescriptor.expired()) + { + model::SharedPageDescriptor pHitDescriptor (rDescriptor.mpHitDescriptor); + if (pHitDescriptor->IsSelected()) + nEventCode |= OVER_SELECTED_PAGE; + else + nEventCode |= OVER_UNSELECTED_PAGE; + } + + // Detect whether we are dragging pages or dragging a selection rectangle. + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + if (rOverlay.GetSubstitutionOverlay().isVisible()) + nEventCode |= SUBSTITUTION_VISIBLE; + if (rOverlay.GetSelectionRectangleOverlay().isVisible()) + nEventCode |= RECTANGLE_VISIBLE; + + return nEventCode; +} + + + +void SelectionFunction::EventPreprocessing (const EventDescriptor& rDescriptor) +{ + // Some general processing that is not specific to the exact event code. + if (rDescriptor.mnEventCode & BUTTON_DOWN) + mbPageHit = (rDescriptor.mpHitPage!=NULL); + + // Set the focus to the slide under the mouse. + if (rDescriptor.mpHitPage != NULL) + mrController.GetFocusManager().FocusPage((rDescriptor.mpHitPage->GetPageNum()-1)/2); +} + + + + +bool SelectionFunction::EventProcessing (const EventDescriptor& rDescriptor) +{ + // 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 +#define ANY_PAGE(code) \ + code|NOT_OVER_PAGE: \ + case code|OVER_UNSELECTED_PAGE: \ + case code|OVER_SELECTED_PAGE +#define ANY_PAGE_AND_MODIFIER(code) \ + ANY_PAGE(code|NO_MODIFIER): \ + case ANY_PAGE(code|SHIFT_MODIFIER): \ + case ANY_PAGE(code|CONTROL_MODIFIER) + + + bool bResult (true); + bool bMakeSelectionVisible (true); + + mrController.GetPageSelector().DisableBroadcasting(); + + // 2b. With the event code determine the type of operation with which to + // react to the event. + + // Acquire a shared_ptr to the hit page descriptor. + model::SharedPageDescriptor pHitDescriptor; + if ( ! rDescriptor.mpHitDescriptor.expired()) + pHitDescriptor = model::SharedPageDescriptor(rDescriptor.mpHitDescriptor); + + switch (rDescriptor.mnEventCode) + { + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + SetCurrentPage(pHitDescriptor); + PrepareMouseMotion(mpWindow->PixelToLogic(rDescriptor.maMousePosition)); + mpSubstitutionHandler->Start(rDescriptor.maMouseModelPosition); + break; + + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + SetCurrentPage(pHitDescriptor); + mpSubstitutionHandler->End(); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + PrepareMouseMotion(mpWindow->PixelToLogic(rDescriptor.maMousePosition)); + mpSubstitutionHandler->Start(rDescriptor.maMouseModelPosition); + break; + + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_SELECTED_PAGE: + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_UNSELECTED_PAGE: + // A double click allways shows the selected slide in the center + // pane in an edit view. + SetCurrentPage(pHitDescriptor); + SwitchView(pHitDescriptor); + break; + + // Multi selection with the control modifier. + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | CONTROL_MODIFIER: + DeselectHitPage(pHitDescriptor); + PrepareMouseMotion(mpWindow->PixelToLogic(rDescriptor.maMousePosition)); + break; + + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | CONTROL_MODIFIER: + SelectHitPage(pHitDescriptor); + PrepareMouseMotion(mpWindow->PixelToLogic(rDescriptor.maMousePosition)); + + break; + + // Range selection with the shift modifier. + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | SHIFT_MODIFIER: + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | SHIFT_MODIFIER: + RangeSelect(pHitDescriptor); + break; + + // Was: Preview of the page transition effect. + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_FADE_INDICATOR: + // No action. + break; + + // Right button for context menu. + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + case KEY_EVENT | KEY_F10 | SHIFT_MODIFIER | 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. + DeselectAllPages(); + SelectHitPage(pHitDescriptor); + SetCurrentPage(pHitDescriptor); + bMakeSelectionVisible = false; + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + case KEY_EVENT | KEY_F10 | OVER_SELECTED_PAGE | SHIFT_MODIFIER: + // Do not change the selection. Just adjust the insertion indicator. + bMakeSelectionVisible = false; + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE: + // The Shift+F10 key event is here just for completeness. It should + // not be possible to really receive this (not being over a page.) + case KEY_EVENT | KEY_F10 | SHIFT_MODIFIER | NOT_OVER_PAGE: + DeselectAllPages(); + bMakeSelectionVisible = false; + break; + + // A mouse motion without visible substitution starts that. + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE): + mrController.GetScrollBarManager().AutoScroll(rDescriptor.maMousePosition); + mpSubstitutionHandler->Start(rDescriptor.maMouseModelPosition); + break; + + // Move substitution. + case ANY_PAGE_AND_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | SUBSTITUTION_VISIBLE): + if ((rDescriptor.mnEventCode & CONTROL_MODIFIER) != 0) + StartDrag(); + mrController.GetScrollBarManager().AutoScroll(rDescriptor.maMousePosition); + mpSubstitutionHandler->UpdatePosition(rDescriptor.maMouseModelPosition); + break; + + // Place substitution. + case ANY_PAGE_AND_MODIFIER(BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | SUBSTITUTION_VISIBLE): + // When the substitution has not been moved the button up event + // is taken to be part of a single click. The selected pages + // are therefore not moved (which technically would be necessary + // for unconsecutive multi selections). Instead the page under + // the mouse becomes the only selected page. + if (mpSubstitutionHandler->HasBeenMoved()) + { + // The following Process() call may lead to the desctruction + // of pHitDescriptor so release our reference to it. + pHitDescriptor.reset(); + mpSubstitutionHandler->Process(); + } + else + if (pHitDescriptor != NULL) + SetCurrentPage(pHitDescriptor); + mpSubstitutionHandler->End(); + break; + + // Rectangle selection. + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE | NO_MODIFIER: + DeselectAllPages(); + StartRectangleSelection(rDescriptor.maMouseModelPosition); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE | SHIFT_MODIFIER: + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE | CONTROL_MODIFIER: + StartRectangleSelection(rDescriptor.maMouseModelPosition); + break; + + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE): + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE): + case ANY_PAGE_AND_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | RECTANGLE_VISIBLE): + mrController.GetScrollBarManager().AutoScroll(rDescriptor.maMousePosition); + UpdateRectangleSelection(rDescriptor.maMouseModelPosition); + break; + + case ANY_PAGE(BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | RECTANGLE_VISIBLE | NO_MODIFIER): + ProcessRectangleSelection(false); + break; + + case ANY_PAGE(BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | RECTANGLE_VISIBLE | SHIFT_MODIFIER): + case ANY_PAGE(BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | RECTANGLE_VISIBLE | CONTROL_MODIFIER): + ProcessRectangleSelection(true); + break; + + default: + bResult = false; + break; + } + mrController.GetPageSelector().EnableBroadcasting(bMakeSelectionVisible); + + return bResult; +} + + + + +void SelectionFunction::EventPostprocessing (const EventDescriptor& rDescriptor) +{ + if (rDescriptor.mnEventCode & BUTTON_UP) + { + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + + mpWindow->ReleaseMouse(); + + // The overlays should not be visible anymore. Warn when one of + // them still is. An exception is th insertion indicator in the + // case that the context menu is visible. + DBG_ASSERT( + mrController.IsContextMenuOpen() + || !rOverlay.GetInsertionIndicatorOverlay().isVisible(), + "slidesorter::SelectionFunction: insertion indicator still visible"); + DBG_ASSERT( + !rOverlay.GetSubstitutionOverlay().isVisible(), + "slidesorter::SelectionFunction: substitution still visible"); + DBG_ASSERT( + !rOverlay.GetSelectionRectangleOverlay().isVisible(), + "slidesorter::SelectionFunction: selection rectangle still visible"); + + // Now turn them off. + if ( ! mrController.IsContextMenuOpen()) + rOverlay.GetInsertionIndicatorOverlay().setVisible(false); + rOverlay.GetSubstitutionOverlay().setVisible(false); + rOverlay.GetSelectionRectangleOverlay().setVisible(false); + } +} + + + + +void SelectionFunction::SetCurrentPage (const model::SharedPageDescriptor& rpDescriptor) +{ + PageSelector& rSelector (mrController.GetPageSelector()); + rSelector.DeselectAllPages (); + rSelector.SelectPage(rpDescriptor); + + mrController.GetCurrentSlideManager()->SwitchCurrentSlide(rpDescriptor); +} + + + + +void SelectionFunction::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!=NULL && pViewShell->IsMainViewShell()) + { + if (rpDescriptor.get()!=NULL && rpDescriptor->GetPage()!=NULL) + { + mrSlideSorter.GetModel().GetDocument()->SetSelected(rpDescriptor->GetPage(), TRUE); + mpViewShell->GetFrameView()->SetSelectedPage( + (rpDescriptor->GetPage()->GetPageNum()-1)/2); + } + if (mrSlideSorter.GetViewShellBase() != NULL) + framework::FrameworkHelper::Instance(*mrSlideSorter.GetViewShellBase())->RequestView( + framework::FrameworkHelper::msImpressViewURL, + framework::FrameworkHelper::msCenterPaneURL); + } +} + + + + +//===== EventDescriptor ======================================================= + +SelectionFunction::EventDescriptor::EventDescriptor ( + sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter& rSlideSorter) + : maMousePosition(), + maMouseModelPosition(), + mpHitDescriptor(), + mpHitPage(), + mnEventCode(nEventType) +{ + ::Window* pWindow = rSlideSorter.GetActiveWindow(); + + maMousePosition = rEvent.GetPosPixel(); + maMouseModelPosition = pWindow->PixelToLogic(maMousePosition); + model::SharedPageDescriptor pHitDescriptor ( + rSlideSorter.GetController().GetPageAt(maMousePosition)); + if (pHitDescriptor.get() != NULL) + { + mpHitDescriptor = pHitDescriptor; + mpHitPage = pHitDescriptor->GetPage(); + } +} + + + + + +SelectionFunction::EventDescriptor::EventDescriptor ( + const KeyEvent&, + SlideSorter& rSlideSorter) + : maMousePosition(), + maMouseModelPosition(), + mpHitDescriptor(), + mpHitPage(), + mnEventCode(0) +{ + mpHitDescriptor = rSlideSorter.GetController().GetFocusManager().GetFocusedPageDescriptor(); + model::SharedPageDescriptor pHitDescriptor ( + rSlideSorter.GetController().GetFocusManager().GetFocusedPageDescriptor()); + if (pHitDescriptor.get() != NULL) + { + mpHitPage = pHitDescriptor->GetPage(); + mpHitDescriptor = pHitDescriptor; + } +} + + + + + +//===== SubstitutionHandler =================================================== + +SelectionFunction::SubstitutionHandler::SubstitutionHandler (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mbHasBeenMoved(false) +{ +} + + + + +SelectionFunction::SubstitutionHandler::~SubstitutionHandler (void) +{ + if (mrSlideSorter.IsValid()) + { + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + rOverlay.GetSubstitutionOverlay().setVisible(false); + rOverlay.GetSubstitutionOverlay().Clear(); + } +} + + + + +void SelectionFunction::SubstitutionHandler::Start (const Point& rMouseModelPosition) +{ + // No Drag-and-Drop for master pages. + if (mrSlideSorter.GetModel().GetEditMode() != EM_PAGE) + return; + + if (mrSlideSorter.GetController().GetProperties()->IsUIReadOnly()) + return; + + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + + if ( ! rOverlay.GetSubstitutionOverlay().isVisible()) + { + // Show a new substitution for the selected page objects. + model::PageEnumeration aSelectedPages( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + rOverlay.GetSubstitutionOverlay().Create(aSelectedPages, rMouseModelPosition); + rOverlay.GetSubstitutionOverlay().setVisible(true); + mbHasBeenMoved = false; + } + else + UpdatePosition(rMouseModelPosition); +} + + + + +void SelectionFunction::SubstitutionHandler::UpdatePosition (const Point& rMouseModelPosition) +{ + if (mrSlideSorter.GetController().GetProperties()->IsUIReadOnly()) + return; + + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + + // Move the existing substitution to the new position. + rOverlay.GetSubstitutionOverlay().SetPosition(rMouseModelPosition); + + rOverlay.GetInsertionIndicatorOverlay().SetPosition(rMouseModelPosition); + rOverlay.GetInsertionIndicatorOverlay().setVisible(true); + + mbHasBeenMoved = true; +} + + + + +void SelectionFunction::SubstitutionHandler::Process (void) +{ + if (mrSlideSorter.GetController().GetProperties()->IsUIReadOnly()) + return; + + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + + if (IsSubstitutionInsertionNonTrivial()) + { + // Tell the model to move the selected pages behind the one with the + // index mnInsertionIndex which first has to transformed into an index + // understandable by the document. + sal_Int32 nInsertionIndex = rOverlay.GetInsertionIndicatorOverlay().GetInsertionPageIndex(); + if (nInsertionIndex >= 0) + { + USHORT nDocumentIndex = (USHORT)nInsertionIndex-1; + mrSlideSorter.GetController().GetSelectionManager()->MoveSelectedPages(nDocumentIndex); + } + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != NULL) + pViewShell->GetViewFrame()->GetBindings().Invalidate(SID_STATUS_PAGE); + } +} + + + + +void SelectionFunction::SubstitutionHandler::End (void) +{ + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + rOverlay.GetSubstitutionOverlay().setVisible(false); + rOverlay.GetSubstitutionOverlay().Clear(); + rOverlay.GetInsertionIndicatorOverlay().setVisible(false); +} + + + + +bool SelectionFunction::SubstitutionHandler::HasBeenMoved (void) const +{ + return mbHasBeenMoved; +} + + + + +bool SelectionFunction::SubstitutionHandler::IsSubstitutionInsertionNonTrivial (void) const +{ + bool bIsNonTrivial = false; + + do + { + view::ViewOverlay& rOverlay (mrSlideSorter.GetView().GetOverlay()); + + // Make sure that the substitution and the insertion indicator are visible. + if ( ! rOverlay.GetSubstitutionOverlay().isVisible()) + break; + if ( ! rOverlay.GetInsertionIndicatorOverlay().isVisible()) + break; + + // Iterate over all selected pages and check whether there are + // holes. While we do this we remember the indices of the first and + // last selected page as preparation for the next step. + sal_Int32 nCurrentIndex = -1; + sal_Int32 nFirstIndex = -1; + sal_Int32 nLastIndex = -1; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements() && ! bIsNonTrivial) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + SdPage* pPage = pDescriptor->GetPage(); + if (pPage != NULL) + { + // Get the page number and compare it to the last one. + sal_Int32 nPageNumber = (pPage->GetPageNum()-1)/2; + if (nCurrentIndex>=0 && nPageNumber>(nCurrentIndex+1)) + bIsNonTrivial = true; + else + nCurrentIndex = nPageNumber; + + // Remember indices of the first and last page of the selection. + if (nFirstIndex == -1) + nFirstIndex = nPageNumber; + nLastIndex = nPageNumber; + } + } + if (bIsNonTrivial) + break; + + // When we come here then the selection is consecutive. We still + // have to check that the insertion position is not directly in + // front or directly behind the selection and thus moving the + // selection there would not change the model. + sal_Int32 nInsertionIndex = rOverlay.GetInsertionIndicatorOverlay().GetInsertionPageIndex(); + if (nInsertionIndex<nFirstIndex || nInsertionIndex>(nLastIndex+1)) + bIsNonTrivial = true; + } + while (false); + + return bIsNonTrivial; +} + +} } } // end of namespace ::sd::slidesorter::controller |