cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / slidesorter / controller / SlsClipboard.cxx
blob2c0b81bde2b22a205c5da292ea83def0dd0388fe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <cassert>
24 #include <controller/SlsClipboard.hxx>
26 #include <SlideSorterViewShell.hxx>
27 #include <SlideSorter.hxx>
28 #include <model/SlideSorterModel.hxx>
29 #include <model/SlsPageDescriptor.hxx>
30 #include <model/SlsPageEnumerationProvider.hxx>
31 #include <utility>
32 #include <view/SlideSorterView.hxx>
33 #include <controller/SlideSorterController.hxx>
34 #include <controller/SlsInsertionIndicatorHandler.hxx>
35 #include <controller/SlsPageSelector.hxx>
36 #include <controller/SlsSelectionFunction.hxx>
37 #include <controller/SlsCurrentSlideManager.hxx>
38 #include <controller/SlsFocusManager.hxx>
39 #include <controller/SlsSelectionManager.hxx>
40 #include <controller/SlsTransferableData.hxx>
41 #include <controller/SlsSelectionObserver.hxx>
42 #include <controller/SlsVisibleAreaManager.hxx>
43 #include <cache/SlsPageCache.hxx>
45 #include <ViewShellBase.hxx>
46 #include <DrawViewShell.hxx>
47 #include <Window.hxx>
48 #include <fupoor.hxx>
49 #include <strings.hrc>
50 #include <sdresid.hxx>
51 #include <sdxfer.hxx>
52 #include <sdmod.hxx>
53 #include <ins_paste.hxx>
54 #include <drawdoc.hxx>
55 #include <DrawDocShell.hxx>
56 #include <sdpage.hxx>
57 #include <sdtreelb.hxx>
59 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
60 #include <sfx2/request.hxx>
61 #include <sfx2/viewfrm.hxx>
62 #include <sfx2/bindings.hxx>
63 #include <sfx2/docfile.hxx>
64 #include <svx/svxids.hrc>
65 #include <tools/urlobj.hxx>
66 #include <rtl/ustring.hxx>
67 #include <vcl/svapp.hxx>
69 namespace sd::slidesorter::controller {
71 namespace {
72 /** Temporarily deactivate slide tracking of the VisibleAreaManager.
73 This is used as a workaround to avoid unwanted repositioning of
74 the visible area when the selection of slides is copied to the
75 clipboard (cloning of slides leads to model change notifications
76 for the original model.)
78 class TemporarySlideTrackingDeactivator
80 public:
81 explicit TemporarySlideTrackingDeactivator (SlideSorterController& rController)
82 : mrController(rController),
83 mbIsCurrentSlideTrackingActive (
84 mrController.GetVisibleAreaManager().IsCurrentSlideTrackingActive())
86 if (mbIsCurrentSlideTrackingActive)
87 mrController.GetVisibleAreaManager().DeactivateCurrentSlideTracking();
89 ~TemporarySlideTrackingDeactivator()
91 if (mbIsCurrentSlideTrackingActive)
92 mrController.GetVisibleAreaManager().ActivateCurrentSlideTracking();
95 private:
96 SlideSorterController& mrController;
97 const bool mbIsCurrentSlideTrackingActive;
99 } // end of anonymous namespace
101 class Clipboard::UndoContext
103 public:
104 UndoContext (
105 SdDrawDocument* pDocument,
106 std::shared_ptr<ViewShell> pMainViewShell)
107 : mpDocument(pDocument),
108 mpMainViewShell(std::move(pMainViewShell))
110 if (mpDocument!=nullptr && mpDocument->IsUndoEnabled())
112 if (mpMainViewShell && mpMainViewShell->GetShellType() == ViewShell::ST_DRAW)
113 mpDocument->BegUndo(SdResId(STRING_DRAG_AND_DROP_PAGES));
114 else
115 mpDocument->BegUndo(SdResId(STRING_DRAG_AND_DROP_SLIDES));
119 ~UndoContext()
121 if (mpDocument!=nullptr && mpDocument->IsUndoEnabled())
122 mpDocument->EndUndo();
123 if (mpMainViewShell && mpMainViewShell->GetViewFrame()!=nullptr)
125 SfxBindings& rBindings = mpMainViewShell->GetViewFrame()->GetBindings();
126 rBindings.Invalidate(SID_UNDO);
127 rBindings.Invalidate(SID_REDO);
130 private:
131 SdDrawDocument* mpDocument;
132 std::shared_ptr<ViewShell> mpMainViewShell;
135 Clipboard::Clipboard (SlideSorter& rSlideSorter)
136 : ViewClipboard(rSlideSorter.GetView()),
137 mrSlideSorter(rSlideSorter),
138 mrController(mrSlideSorter.GetController()),
139 mnDragFinishedUserEventId(nullptr)
143 Clipboard::~Clipboard()
145 if (mnDragFinishedUserEventId != nullptr)
146 Application::RemoveUserEvent(mnDragFinishedUserEventId);
149 /** With the current implementation the forwarded calls to the current
150 function will come back eventually to call the local Do(Cut|Copy|Paste)
151 methods. A shortcut is possible but would be an unclean hack.
153 void Clipboard::HandleSlotCall (SfxRequest& rRequest)
155 ViewShell* pViewShell = mrSlideSorter.GetViewShell();
156 rtl::Reference<FuPoor> xFunc;
157 if (pViewShell != nullptr)
158 xFunc = pViewShell->GetCurrentFunction();
159 switch (rRequest.GetSlot())
161 case SID_CUT:
162 if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage)
164 if(xFunc.is())
165 xFunc->DoCut();
166 else
167 DoCut();
169 rRequest.Done();
170 break;
172 case SID_COPY:
173 if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage)
175 if(xFunc.is())
176 xFunc->DoCopy();
177 else
178 DoCopy();
180 rRequest.Done();
181 break;
183 case SID_PASTE:
184 // Prevent redraws while inserting pages from the clipboard
185 // because the intermediate inconsistent state might lead to
186 // a crash.
187 if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage)
189 view::SlideSorterView::DrawLock aLock (mrSlideSorter);
190 SelectionObserver::Context aContext (mrSlideSorter);
191 if(xFunc.is())
192 xFunc->DoPaste();
193 else
194 DoPaste();
196 rRequest.Done();
197 break;
199 case SID_DELETE:
200 DoDelete();
201 rRequest.Done();
202 break;
206 void Clipboard::DoCut ()
208 if (mrSlideSorter.GetModel().GetPageCount() > 1)
210 DoCopy();
211 DoDelete();
215 void Clipboard::DoDelete()
217 if (mrSlideSorter.GetModel().GetPageCount() > 1)
219 mrController.GetSelectionManager()->DeleteSelectedPages();
223 void Clipboard::DoCopy ()
225 CreateSlideTransferable( nullptr, false );
228 void Clipboard::DoPaste ()
230 SdTransferable* pClipTransferable = SdModule::get()->pTransferClip;
232 if (pClipTransferable==nullptr || !pClipTransferable->IsPageTransferable())
233 return;
235 sal_Int32 nInsertPosition = GetInsertionPosition();
237 if (nInsertPosition >= 0)
239 // Paste the pages from the clipboard.
240 sal_Int32 nInsertPageCount = PasteTransferable(nInsertPosition);
241 // Select the pasted pages and make the first of them the
242 // current page.
243 mrSlideSorter.GetContentWindow()->GrabFocus();
244 SelectPageRange(nInsertPosition, nInsertPageCount);
248 sal_Int32 Clipboard::GetInsertionPosition ()
250 sal_Int32 nInsertPosition = -1;
252 // Determine the insertion position:
253 // a) When the insertion indicator is visible, then at that position.
254 // b) When the focus indicator is visible, then before or after the
255 // focused page, depending on user input to a dialog.
256 // c) When there is a selection but no focus, then after the
257 // selection.
258 // d) After the last page when there is no selection and no focus.
260 std::shared_ptr<controller::InsertionIndicatorHandler> pInsertionIndicatorHandler (
261 mrController.GetInsertionIndicatorHandler());
262 if (pInsertionIndicatorHandler->IsActive())
264 // Use the insertion index of an active insertion indicator.
265 nInsertPosition = pInsertionIndicatorHandler->GetInsertionPageIndex();
267 else if (mrController.GetSelectionManager()->GetInsertionPosition() >= 0)
269 // Use the insertion index of an insertion indicator that has been
270 // deactivated a short while ago.
271 nInsertPosition = mrController.GetSelectionManager()->GetInsertionPosition();
273 else if (mrController.GetFocusManager().IsFocusShowing())
275 // Use the focus to determine the insertion position.
276 vcl::Window* pWin = mrSlideSorter.GetContentWindow();
277 SdInsertPasteDlg aDialog(pWin ? pWin->GetFrameWeld() : nullptr);
278 if (aDialog.run() == RET_OK)
280 nInsertPosition = mrController.GetFocusManager().GetFocusedPageIndex();
281 if (!aDialog.IsInsertBefore())
282 nInsertPosition ++;
286 return nInsertPosition;
289 sal_Int32 Clipboard::PasteTransferable (sal_Int32 nInsertPosition)
291 SdTransferable* pClipTransferable = SdModule::get()->pTransferClip;
292 model::SlideSorterModel& rModel (mrSlideSorter.GetModel());
293 bool bMergeMasterPages = !pClipTransferable->HasSourceDoc (rModel.GetDocument());
294 sal_uInt16 nInsertIndex (rModel.GetCoreIndex(nInsertPosition));
295 sal_Int32 nInsertPageCount (0);
296 if (pClipTransferable->HasPageBookmarks())
298 const std::vector<OUString> &rBookmarkList = pClipTransferable->GetPageBookmarks();
299 const SolarMutexGuard aGuard;
301 nInsertPageCount = static_cast<sal_uInt16>(rBookmarkList.size());
302 rModel.GetDocument()->InsertBookmarkAsPage(
303 rBookmarkList,
304 nullptr,
305 false,
306 false,
307 nInsertIndex,
308 false,
309 pClipTransferable->GetPageDocShell(),
310 true,
311 bMergeMasterPages,
312 false);
314 else
316 SfxObjectShell* pShell = pClipTransferable->GetDocShell().get();
317 DrawDocShell* pDataDocSh = static_cast<DrawDocShell*>(pShell);
318 SdDrawDocument* pDataDoc = pDataDocSh->GetDoc();
320 if (pDataDoc!=nullptr
321 && pDataDoc->GetSdPageCount(PageKind::Standard))
323 const SolarMutexGuard aGuard;
325 bMergeMasterPages = (pDataDoc != rModel.GetDocument());
326 nInsertPageCount = pDataDoc->GetSdPageCount( PageKind::Standard );
327 rModel.GetDocument()->InsertBookmarkAsPage(
328 std::vector<OUString>(),
329 nullptr,
330 false,
331 false,
332 nInsertIndex,
333 false,
334 pDataDocSh,
335 true,
336 bMergeMasterPages,
337 false);
340 mrController.HandleModelChange();
341 return nInsertPageCount;
344 void Clipboard::SelectPageRange (sal_Int32 nFirstIndex, sal_Int32 nPageCount)
346 // Select the newly inserted pages. That are the nInsertPageCount pages
347 // after the nInsertIndex position.
348 PageSelector& rSelector (mrController.GetPageSelector());
349 rSelector.DeselectAllPages();
350 for (sal_Int32 i=0; i<nPageCount; i++)
352 model::SharedPageDescriptor pDescriptor (
353 mrSlideSorter.GetModel().GetPageDescriptor(nFirstIndex + i));
354 if (pDescriptor)
356 rSelector.SelectPage(pDescriptor);
357 // The first page of the new selection is made the current page.
358 if (i == 0)
360 mrController.GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor);
366 void Clipboard::CreateSlideTransferable (
367 vcl::Window* pWindow,
368 bool bDrag)
370 std::vector<OUString> aBookmarkList;
372 // Insert all selected pages into a bookmark list and remember them in
373 // maPagesToRemove for possible later removal.
374 model::PageEnumeration aSelectedPages
375 (model::PageEnumerationProvider::CreateSelectedPagesEnumeration(
376 mrSlideSorter.GetModel()));
377 SdDrawDocument* const pDocument = mrSlideSorter.GetModel().GetDocument();
378 DrawDocShell* const pDataDocSh = pDocument->GetDocSh();
380 sal_Int32 nUniqueID = 0;
381 while (aSelectedPages.HasMoreElements())
383 model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement());
385 //ensure that the slides have unique names
386 const OUString sOrigName = pDescriptor->GetPage()->GetName();
387 if ( pDataDocSh && !pDataDocSh->IsPageNameUnique( sOrigName ) )
389 OUString sUniqueName;
390 bool bUnique = false;
391 while ( !bUnique )
393 sUniqueName = sOrigName + "_clipboard" + OUString::number(nUniqueID++);
394 bUnique = pDataDocSh->IsNewPageNameValid( sUniqueName );
395 if ( bUnique )
396 pDescriptor->GetPage()->SetName(sUniqueName);
400 aBookmarkList.push_back(pDescriptor->GetPage()->GetName());
401 maPagesToRemove.push_back (pDescriptor->GetPage());
404 // Create a small set of representatives of the selection for which
405 // previews are included into the transferable so that an insertion
406 // indicator can be rendered.
407 aSelectedPages.Rewind();
408 ::std::vector<TransferableData::Representative> aRepresentatives;
409 aRepresentatives.reserve(3);
410 std::shared_ptr<cache::PageCache> pPreviewCache (
411 mrSlideSorter.GetView().GetPreviewCache());
412 while (aSelectedPages.HasMoreElements())
414 model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement());
415 if ( ! pDescriptor || pDescriptor->GetPage()==nullptr)
416 continue;
417 BitmapEx aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false));
418 aRepresentatives.emplace_back(
419 aPreview,
420 pDescriptor->HasState(model::PageDescriptor::ST_Excluded));
421 if (aRepresentatives.size() >= 3)
422 break;
425 if (aBookmarkList.empty())
426 return;
428 mrSlideSorter.GetView().BrkAction();
429 rtl::Reference<SdTransferable> pTransferable = TransferableData::CreateTransferable (
430 pDocument,
431 dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()),
432 std::move(aRepresentatives));
434 if (bDrag)
435 SdModule::get()->pTransferDrag = pTransferable.get();
436 else
437 SdModule::get()->pTransferClip = pTransferable.get();
439 pDocument->CreatingDataObj (pTransferable.get());
440 pTransferable->SetWorkDocument(pDocument->AllocSdDrawDocument());
441 std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor);
442 pTransferable->GetWorkDocument()->GetDocSh()
443 ->FillTransferableObjectDescriptor (*pObjDesc);
445 if (pDataDocSh != nullptr)
446 pObjDesc->maDisplayName = pDataDocSh->GetMedium()->GetURLObject().GetURLNoPass();
448 vcl::Window* pActionWindow = pWindow;
449 if (pActionWindow == nullptr)
451 ViewShell* pViewShell = mrSlideSorter.GetViewShell();
452 if (pViewShell != nullptr)
453 pActionWindow = pViewShell->GetActiveWindow();
456 assert(pActionWindow);
458 pTransferable->SetStartPos (pActionWindow->PixelToLogic(
459 pActionWindow->GetPointerPosPixel()));
460 pTransferable->SetObjectDescriptor (std::move(pObjDesc));
463 TemporarySlideTrackingDeactivator aDeactivator (mrController);
464 pTransferable->SetPageBookmarks (std::move(aBookmarkList), !bDrag);
467 if (bDrag)
469 pTransferable->SetView (&mrSlideSorter.GetView());
470 pTransferable->StartDrag (pActionWindow, DND_ACTION_COPY | DND_ACTION_MOVE);
472 else
473 pTransferable->CopyToClipboard (pActionWindow);
475 pDocument->CreatingDataObj(nullptr);
478 std::shared_ptr<SdTransferable::UserData> Clipboard::CreateTransferableUserData (SdTransferable* pTransferable)
482 SdPageObjsTLV::SdPageObjsTransferable* pTreeListBoxTransferable
483 = dynamic_cast<SdPageObjsTLV::SdPageObjsTransferable*>(pTransferable);
484 if (pTreeListBoxTransferable == nullptr)
485 break;
487 // Find view shell for the document of the transferable.
488 ::sd::ViewShell* pViewShell
489 = SdPageObjsTLV::GetViewShellForDocShell(pTreeListBoxTransferable->GetDocShell());
490 if (pViewShell == nullptr)
491 break;
493 // Find slide sorter for the document of the transferable.
494 SlideSorterViewShell* pSlideSorterViewShell
495 = SlideSorterViewShell::GetSlideSorter(pViewShell->GetViewShellBase());
496 if (pSlideSorterViewShell == nullptr)
497 break;
498 SlideSorter& rSlideSorter (pSlideSorterViewShell->GetSlideSorter());
500 // Get bookmark from transferable.
501 TransferableDataHelper aDataHelper (pTransferable);
502 INetBookmark aINetBookmark;
503 if ( ! aDataHelper.GetINetBookmark(SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark))
504 break;
505 const OUString sURL (aINetBookmark.GetURL());
506 const sal_Int32 nIndex (sURL.indexOf('#'));
507 if (nIndex == -1)
508 break;
509 OUString sBookmark (sURL.copy(nIndex+1));
511 // Make sure that the bookmark points to a page.
512 SdDrawDocument* pTransferableDocument = rSlideSorter.GetModel().GetDocument();
513 if (pTransferableDocument == nullptr)
514 break;
515 bool bIsMasterPage = false;
516 const sal_uInt16 nPageIndex (pTransferableDocument->GetPageByName(sBookmark, bIsMasterPage));
517 if (nPageIndex == SDRPAGE_NOTFOUND)
518 break;
520 // Create preview.
521 ::std::vector<TransferableData::Representative> aRepresentatives;
522 aRepresentatives.reserve(1);
523 std::shared_ptr<cache::PageCache> pPreviewCache (
524 rSlideSorter.GetView().GetPreviewCache());
525 model::SharedPageDescriptor pDescriptor (rSlideSorter.GetModel().GetPageDescriptor((nPageIndex-1)/2));
526 if ( ! pDescriptor || pDescriptor->GetPage()==nullptr)
527 break;
528 BitmapEx aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false));
529 aRepresentatives.emplace_back(
530 aPreview,
531 pDescriptor->HasState(model::PageDescriptor::ST_Excluded));
533 // Remember the page in maPagesToRemove so that it can be removed
534 // when drag and drop action is "move".
535 Clipboard& rOtherClipboard (pSlideSorterViewShell->GetSlideSorter().GetController().GetClipboard());
536 rOtherClipboard.maPagesToRemove.clear();
537 rOtherClipboard.maPagesToRemove.push_back(pDescriptor->GetPage());
539 // Create the new transferable.
540 std::shared_ptr<SdTransferable::UserData> pNewTransferable =
541 std::make_shared<TransferableData>(
542 pSlideSorterViewShell,
543 std::move(aRepresentatives));
544 pTransferable->SetWorkDocument(pTreeListBoxTransferable->GetSourceDoc()->AllocSdDrawDocument());
545 // pTransferable->SetView(&mrSlideSorter.GetView());
547 // Set page bookmark list.
548 std::vector<OUString> aPageBookmarks { sBookmark };
549 pTransferable->SetPageBookmarks(std::move(aPageBookmarks), false);
551 // Replace the view referenced by the transferable with the
552 // corresponding slide sorter view.
553 pTransferable->SetView(&pSlideSorterViewShell->GetSlideSorter().GetView());
555 return pNewTransferable;
557 while (false);
559 return std::shared_ptr<SdTransferable::UserData>();
562 void Clipboard::StartDrag (
563 const Point& rPosition,
564 vcl::Window* pWindow)
566 maPagesToRemove.clear();
567 CreateSlideTransferable(pWindow, true);
569 mrController.GetInsertionIndicatorHandler()->UpdatePosition(
570 rPosition,
571 InsertionIndicatorHandler::UnknownMode);
574 void Clipboard::DragFinished (sal_Int8 nDropAction)
576 if (mnDragFinishedUserEventId == nullptr)
578 mnDragFinishedUserEventId = Application::PostUserEvent(
579 LINK(this, Clipboard, ProcessDragFinished),
580 reinterpret_cast<void*>(nDropAction));
584 IMPL_LINK(Clipboard, ProcessDragFinished, void*, pUserData, void)
586 const sal_Int8 nDropAction (static_cast<sal_Int8>(reinterpret_cast<sal_IntPtr>(pUserData)));
588 mnDragFinishedUserEventId = nullptr;
590 // Hide the substitution display and insertion indicator.
591 ::rtl::Reference<SelectionFunction> pFunction (mrController.GetCurrentSelectionFunction());
592 if (pFunction.is())
593 pFunction->NotifyDragFinished();
595 PageSelector& rSelector (mrController.GetPageSelector());
596 if ((nDropAction & DND_ACTION_MOVE) != 0
597 && ! maPagesToRemove.empty())
599 // Remove the pages that have been moved to another place (possibly
600 // in the same document.)
601 rSelector.DeselectAllPages();
602 for (const auto& rpDraggedPage : maPagesToRemove)
604 rSelector.SelectPage(rpDraggedPage);
606 mrController.GetSelectionManager()->DeleteSelectedPages();
608 mxUndoContext.reset();
609 mxSelectionObserverContext.reset();
612 sal_Int8 Clipboard::AcceptDrop (
613 const AcceptDropEvent& rEvent,
614 DropTargetHelper& rTargetHelper,
615 ::sd::Window* pTargetWindow,
616 sal_uInt16 nPage,
617 SdrLayerID nLayer)
619 sal_Int8 nAction (DND_ACTION_NONE);
621 const Clipboard::DropType eDropType (IsDropAccepted());
623 switch (eDropType)
625 case DT_PAGE:
626 case DT_PAGE_FROM_NAVIGATOR:
628 // Accept a drop.
629 nAction = rEvent.mnAction;
631 // Use the copy action when the drop action is the default, i.e. not
632 // explicitly set to move or link, and when the source and
633 // target models are not the same.
634 SdTransferable* pDragTransferable = SdModule::get()->pTransferDrag;
635 if (pDragTransferable != nullptr
636 && pDragTransferable->IsPageTransferable()
637 && ((rEvent.maDragEvent.DropAction
638 & css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT) != 0)
639 && (mrSlideSorter.GetModel().GetDocument()->GetDocSh()
640 != pDragTransferable->GetPageDocShell()))
642 nAction = DND_ACTION_COPY;
644 else if (IsInsertionTrivial(pDragTransferable, nAction))
646 nAction = DND_ACTION_NONE;
649 // Show the insertion marker and the substitution for a drop.
650 SelectionFunction* pSelectionFunction = dynamic_cast<SelectionFunction*>(
651 mrSlideSorter.GetViewShell()->GetCurrentFunction().get());
652 if (pSelectionFunction != nullptr)
653 pSelectionFunction->MouseDragged(rEvent, nAction);
655 // Scroll the window when the mouse reaches the window border.
656 // mrController.GetScrollBarManager().AutoScroll (rEvent.maPosPixel);
658 break;
660 case DT_SHAPE:
661 nAction = ExecuteOrAcceptShapeDrop(
662 DC_ACCEPT,
663 rEvent.maPosPixel,
664 &rEvent,
665 rTargetHelper,
666 pTargetWindow,
667 nPage,
668 nLayer);
669 break;
671 default:
672 case DT_NONE:
673 nAction = DND_ACTION_NONE;
674 break;
677 return nAction;
680 sal_Int8 Clipboard::ExecuteDrop (
681 const ExecuteDropEvent& rEvent,
682 DropTargetHelper& rTargetHelper,
683 ::sd::Window* pTargetWindow,
684 sal_uInt16 nPage,
685 SdrLayerID nLayer)
687 sal_Int8 nResult = DND_ACTION_NONE;
688 mxUndoContext.reset();
689 const Clipboard::DropType eDropType (IsDropAccepted());
691 switch (eDropType)
693 case DT_PAGE:
694 case DT_PAGE_FROM_NAVIGATOR:
696 SdTransferable* pDragTransferable = SdModule::get()->pTransferDrag;
697 const Point aEventModelPosition (
698 pTargetWindow->PixelToLogic (rEvent.maPosPixel));
699 const sal_Int32 nXOffset (std::abs (pDragTransferable->GetStartPos().X()
700 - aEventModelPosition.X()));
701 const sal_Int32 nYOffset (std::abs (pDragTransferable->GetStartPos().Y()
702 - aEventModelPosition.Y()));
703 bool bContinue =
704 ( pDragTransferable->GetView() != &mrSlideSorter.GetView() )
705 || ( nXOffset >= 2 && nYOffset >= 2 );
707 std::shared_ptr<InsertionIndicatorHandler> pInsertionIndicatorHandler(
708 mrController.GetInsertionIndicatorHandler());
709 // Get insertion position and then turn off the insertion indicator.
710 pInsertionIndicatorHandler->UpdatePosition(aEventModelPosition, rEvent.mnAction);
711 // sal_uInt16 nIndex = DetermineInsertPosition(*pDragTransferable);
713 // Do not process the insertion when it is trivial,
714 // i.e. would insert pages at their original place.
715 if (IsInsertionTrivial(pDragTransferable, rEvent.mnAction))
716 bContinue = false;
718 // Tell the insertion indicator handler to hide before the model
719 // is modified. Doing it later may result in page objects whose
720 // animation state is not properly reset because they are then
721 // in another run then before the model change.
722 pInsertionIndicatorHandler->End(Animator::AM_Immediate);
724 if (bContinue)
726 SlideSorterController::ModelChangeLock aModelChangeLock (mrController);
728 // Handle a general drop operation.
729 mxUndoContext.reset(new UndoContext (
730 mrSlideSorter.GetModel().GetDocument(),
731 mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell()));
732 mxSelectionObserverContext.reset(new SelectionObserver::Context(mrSlideSorter));
734 if (rEvent.mnAction == DND_ACTION_MOVE)
736 SdDrawDocument* pDoc = mrSlideSorter.GetModel().GetDocument();
737 const bool bDoesMakePageObjectsNamesUnique = pDoc->DoesMakePageObjectsNamesUnique();
738 pDoc->DoMakePageObjectsNamesUnique(false);
739 HandlePageDrop(*pDragTransferable);
740 pDoc->DoMakePageObjectsNamesUnique(bDoesMakePageObjectsNamesUnique);
742 else
743 HandlePageDrop(*pDragTransferable);
745 nResult = rEvent.mnAction;
747 // We leave the undo context alive for when moving or
748 // copying inside one view then the actions in
749 // NotifyDragFinished should be covered as well as
750 // well as the ones above.
753 // When the pages originated in another slide sorter then
754 // only that is notified automatically about the drag
755 // operation being finished. Because the target slide sorter
756 // has be notified, too, add a callback for that.
757 std::shared_ptr<TransferableData> pSlideSorterTransferable (
758 TransferableData::GetFromTransferable(pDragTransferable));
759 assert(pSlideSorterTransferable);
760 if (pSlideSorterTransferable
761 && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell())
763 DragFinished(nResult);
766 // Notify the receiving selection function that drag-and-drop is
767 // finished and the substitution handler can be released.
768 ::rtl::Reference<SelectionFunction> pFunction (
769 mrController.GetCurrentSelectionFunction());
770 if (pFunction.is())
771 pFunction->NotifyDragFinished();
773 break;
775 case DT_SHAPE:
776 nResult = ExecuteOrAcceptShapeDrop(
777 DC_EXECUTE,
778 rEvent.maPosPixel,
779 &rEvent,
780 rTargetHelper,
781 pTargetWindow,
782 nPage,
783 nLayer);
784 break;
786 default:
787 case DT_NONE:
788 break;
791 return nResult;
794 bool Clipboard::IsInsertionTrivial (
795 SdTransferable const * pTransferable,
796 const sal_Int8 nDndAction) const
798 std::shared_ptr<TransferableData> pSlideSorterTransferable (
799 TransferableData::GetFromTransferable(pTransferable));
800 if (pSlideSorterTransferable
801 && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell())
802 return false;
803 return mrController.GetInsertionIndicatorHandler()->IsInsertionTrivial(nDndAction);
806 void Clipboard::Abort()
808 if (mxSelectionObserverContext)
810 mxSelectionObserverContext->Abort();
811 mxSelectionObserverContext.reset();
815 sal_uInt16 Clipboard::DetermineInsertPosition ()
817 // Tell the model to move the dragged pages behind the one with the
818 // index nInsertionIndex which first has to be transformed into an index
819 // understandable by the document.
820 const sal_Int32 nInsertionIndex (
821 mrController.GetInsertionIndicatorHandler()->GetInsertionPageIndex());
823 // Convert to insertion index to that of an SdModel.
824 if (nInsertionIndex >= 0)
825 return mrSlideSorter.GetModel().GetCoreIndex(nInsertionIndex);
826 else
827 return 0;
830 Clipboard::DropType Clipboard::IsDropAccepted() const
832 const SdTransferable* pDragTransferable = SdModule::get()->pTransferDrag;
833 if (pDragTransferable == nullptr)
834 return DT_NONE;
836 if (pDragTransferable->IsPageTransferable())
838 if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage)
839 return DT_PAGE;
840 else
841 return DT_NONE;
844 const SdPageObjsTLV::SdPageObjsTransferable* pPageObjsTransferable
845 = dynamic_cast<const SdPageObjsTLV::SdPageObjsTransferable*>(pDragTransferable);
846 if (pPageObjsTransferable != nullptr)
847 return DT_PAGE_FROM_NAVIGATOR;
849 return DT_SHAPE;
852 sal_Int8 Clipboard::ExecuteOrAcceptShapeDrop (
853 DropCommand eCommand,
854 const Point& rPosition,
855 const void* pDropEvent,
856 DropTargetHelper& rTargetHelper,
857 ::sd::Window* pTargetWindow,
858 sal_uInt16 nPage,
859 SdrLayerID nLayer)
861 sal_Int8 nResult = 0;
863 // The dropping of a shape is accepted or executed only when there is
864 // DrawViewShell available to which we can forward this call. This has
865 // technical reasons: The actual code to accept or execute a shape drop
866 // is implemented in the ViewShell class and uses the page view of the
867 // main edit view. This is not possible without a DrawViewShell.
868 std::shared_ptr<DrawViewShell> pDrawViewShell;
869 if (mrSlideSorter.GetViewShell() != nullptr)
870 pDrawViewShell = std::dynamic_pointer_cast<DrawViewShell>(
871 mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell());
872 if (pDrawViewShell != nullptr
873 && (pDrawViewShell->GetShellType() == ViewShell::ST_IMPRESS
874 || pDrawViewShell->GetShellType() == ViewShell::ST_DRAW))
876 // The drop is only accepted or executed when it takes place over a
877 // page object. Therefore we replace a missing page number by the
878 // number of the page under the mouse.
879 if (nPage == SDRPAGE_NOTFOUND)
881 model::SharedPageDescriptor pDescriptor (
882 mrSlideSorter.GetModel().GetPageDescriptor(
883 mrSlideSorter.GetView().GetPageIndexAtPoint(rPosition)));
884 if (pDescriptor)
885 nPage = pDescriptor->GetPageIndex();
888 // Now comes the code that is different for the Execute and Accept:
889 // We simply forward the call to the AcceptDrop() or ExecuteDrop()
890 // methods of the DrawViewShell in the center pane.
891 if (nPage != SDRPAGE_NOTFOUND)
892 switch (eCommand)
894 case DC_ACCEPT:
895 nResult = pDrawViewShell->AcceptDrop(
896 *static_cast<const AcceptDropEvent*>(pDropEvent),
897 rTargetHelper,
898 pTargetWindow,
899 nPage,
900 nLayer);
901 break;
903 case DC_EXECUTE:
904 nResult = pDrawViewShell->ExecuteDrop(
905 *static_cast<const ExecuteDropEvent*>(pDropEvent),
906 rTargetHelper,
907 pTargetWindow,
908 nPage,
909 nLayer);
910 break;
914 return nResult;
917 } // end of namespace ::sd::slidesorter::controller
919 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */