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