1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: SlsClipboard.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "precompiled_sd.hxx"
33 #include "controller/SlsClipboard.hxx"
35 #include "SlideSorterViewShell.hxx"
36 #include "SlideSorter.hxx"
37 #include "model/SlideSorterModel.hxx"
38 #include "model/SlsPageDescriptor.hxx"
39 #include "model/SlsPageEnumerationProvider.hxx"
40 #include "view/SlideSorterView.hxx"
41 #include "view/SlsViewOverlay.hxx"
42 #include "view/SlsPageObject.hxx"
43 #include "controller/SlideSorterController.hxx"
44 #include "controller/SlsPageSelector.hxx"
45 #include "controller/SlsSelectionFunction.hxx"
46 #include "controller/SlsCurrentSlideManager.hxx"
47 #include "controller/SlsScrollBarManager.hxx"
48 #include "controller/SlsFocusManager.hxx"
49 #include "controller/SlsSelectionManager.hxx"
50 #include "SlsTransferable.hxx"
52 #include "ViewShellBase.hxx"
53 #include "DrawViewShell.hxx"
56 #include "fuslhide.hxx"
58 #include "fucushow.hxx"
59 #include "fusldlg.hxx"
60 #include "fuexpand.hxx"
61 #include "fusumry.hxx"
64 #include "strings.hrc"
65 #include "sdresid.hxx"
69 #include "ins_paste.hxx"
70 #include "drawdoc.hxx"
71 #include "DrawDocShell.hxx"
74 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
75 #include <sfx2/request.hxx>
76 #include <sfx2/viewfrm.hxx>
77 #include <sfx2/bindings.hxx>
78 #include <sfx2/docfile.hxx>
79 #include <svx/svxids.hrc>
80 #include <vcl/msgbox.hxx>
81 #include <tools/urlobj.hxx>
82 #include <rtl/ustring.hxx>
83 #include <vos/mutex.hxx>
84 #include <vcl/svapp.hxx>
86 namespace sd
{ namespace slidesorter
{ namespace controller
{
89 Clipboard::Clipboard (SlideSorter
& rSlideSorter
)
90 : ViewClipboard(rSlideSorter
.GetView()),
91 mrSlideSorter(rSlideSorter
),
92 mrController(mrSlideSorter
.GetController()),
95 mbUpdateSelectionPending(false)
102 Clipboard::~Clipboard (void)
109 /** With the current implementation the forwarded calls to the current
110 function will come back eventually to call the local Do(Cut|Copy|Paste)
111 methods. A shortcut is possible but would be an unclean hack.
113 void Clipboard::HandleSlotCall (SfxRequest
& rRequest
)
115 ViewShell
* pViewShell
= mrSlideSorter
.GetViewShell();
116 FunctionReference xFunc
;
117 if (pViewShell
!= NULL
)
118 xFunc
= pViewShell
->GetCurrentFunction();
119 switch (rRequest
.GetSlot())
122 if (mrSlideSorter
.GetModel().GetEditMode() != EM_MASTERPAGE
)
133 if (mrSlideSorter
.GetModel().GetEditMode() != EM_MASTERPAGE
)
144 // Prevent redraws while inserting pages from the clipboard
145 // because the intermediate inconsistent state might lead to
147 if (mrSlideSorter
.GetModel().GetEditMode() != EM_MASTERPAGE
)
149 mrSlideSorter
.GetView().LockRedraw (TRUE
);
154 mrController
.GetSelectionManager()->MakeSelectionVisible();
155 mrSlideSorter
.GetView().LockRedraw(FALSE
);
170 void Clipboard::DoCut (::Window
* pWindow
)
172 if (mrSlideSorter
.GetModel().GetPageCount() > 1)
182 void Clipboard::DoDelete (::Window
* )
184 if (mrSlideSorter
.GetModel().GetPageCount() > 1)
186 mrController
.GetSelectionManager()->DeleteSelectedPages();
193 void Clipboard::DoCopy (::Window
* pWindow
)
195 CreateSlideTransferable( pWindow
, FALSE
);
201 void Clipboard::DoPaste (::Window
* pWindow
)
203 SdTransferable
* pClipTransferable
= SD_MOD()->pTransferClip
;
205 if (pClipTransferable
!=NULL
&& pClipTransferable
->IsPageTransferable())
207 sal_Int32 nInsertPosition
= GetInsertionPosition(pWindow
);
209 if (nInsertPosition
>= 0)
211 // Paste the pages from the clipboard.
212 sal_Int32 nInsertPageCount
= PasteTransferable(nInsertPosition
);
213 // Select the pasted pages and make the first of them the
215 mrSlideSorter
.GetView().GetWindow()->GrabFocus();
216 SelectPageRange(nInsertPosition
, nInsertPageCount
);
224 sal_Int32
Clipboard::GetInsertionPosition (::Window
* pWindow
)
226 sal_Int32 nInsertPosition
= -1;
228 // Determine the insertion position:
229 // a) When the insertion indicator is visible, then at that position.
230 // b) When the focus indicator is visible, then before or after the
231 // focused page, depending on user input to a dialog.
232 // c) When there is a selection but no focus, then after the
234 // d) After the last page when there is no selection and no focus.
236 view::InsertionIndicatorOverlay
& rInsertionIndicatorOverlay (
237 mrSlideSorter
.GetView().GetOverlay().GetInsertionIndicatorOverlay());
238 if (rInsertionIndicatorOverlay
.isVisible())
240 nInsertPosition
= rInsertionIndicatorOverlay
.GetInsertionPageIndex();
242 else if (mrController
.GetFocusManager().IsFocusShowing())
244 SdInsertPasteDlg
aDialog (pWindow
);
245 if (aDialog
.Execute() == RET_OK
)
247 nInsertPosition
= mrController
.GetFocusManager().GetFocusedPageIndex();
248 if ( ! aDialog
.IsInsertBefore())
254 nInsertPosition
= mrController
.GetSelectionManager()->GetInsertionPosition();
257 return nInsertPosition
;
263 sal_Int32
Clipboard::PasteTransferable (sal_Int32 nInsertPosition
)
265 SdTransferable
* pClipTransferable
= SD_MOD()->pTransferClip
;
266 bool bMergeMasterPages
= !pClipTransferable
->HasSourceDoc (
267 mrSlideSorter
.GetModel().GetDocument());
268 USHORT
nInsertIndex ((USHORT
)(nInsertPosition
* 2 + 1));
269 sal_Int32
nInsertPageCount (0);
270 if (pClipTransferable
->HasPageBookmarks())
272 const List
& rBookmarkList
= pClipTransferable
->GetPageBookmarks();
273 const ::vos::OGuard
aGuard (Application::GetSolarMutex());
275 nInsertPageCount
= (USHORT
) rBookmarkList
.Count();
276 mrSlideSorter
.GetModel().GetDocument()->InsertBookmarkAsPage(
277 const_cast<List
*>(&rBookmarkList
),
283 pClipTransferable
->GetPageDocShell(),
290 SfxObjectShell
* pShell
= pClipTransferable
->GetDocShell();
291 DrawDocShell
* pDataDocSh
= (DrawDocShell
*)pShell
;
292 SdDrawDocument
* pDataDoc
= pDataDocSh
->GetDoc();
295 && pDataDoc
->GetSdPageCount(PK_STANDARD
))
297 const ::vos::OGuard
aGuard (Application::GetSolarMutex());
299 bMergeMasterPages
= (pDataDoc
!= mrSlideSorter
.GetModel().GetDocument());
300 nInsertPageCount
= pDataDoc
->GetSdPageCount( PK_STANDARD
);
301 mrSlideSorter
.GetModel().GetDocument()->InsertBookmarkAsPage(
314 mrController
.HandleModelChange();
315 return nInsertPageCount
;
321 void Clipboard::SelectPageRange (sal_Int32 nFirstIndex
, sal_Int32 nPageCount
)
323 // Select the newly inserted pages. That are the nInsertPageCount pages
324 // after the nInsertIndex position.
325 PageSelector
& rSelector (mrController
.GetPageSelector());
326 rSelector
.DeselectAllPages();
327 for (USHORT i
=0; i
<nPageCount
; i
++)
329 model::SharedPageDescriptor
pDescriptor (
330 mrSlideSorter
.GetModel().GetPageDescriptor(nFirstIndex
+ i
));
331 if (pDescriptor
.get() != NULL
)
333 rSelector
.SelectPage(pDescriptor
);
334 // The first page of the new selection is made the current page.
337 mrController
.GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor
);
338 mrController
.GetFocusManager().SetFocusedPage(pDescriptor
);
347 void Clipboard::CreateSlideTransferable (
353 // Insert all selected pages into a bookmark list and remember them in
354 // maPagesToRemove for possible later removal.
355 model::PageEnumeration aSelectedPages
356 (model::PageEnumerationProvider::CreateSelectedPagesEnumeration(
357 mrSlideSorter
.GetModel()));
358 while (aSelectedPages
.HasMoreElements())
360 model::SharedPageDescriptor
pDescriptor (aSelectedPages
.GetNextElement());
361 aBookmarkList
.Insert (
362 new String(pDescriptor
->GetPage()->GetName()),
364 maPagesToRemove
.push_back (pDescriptor
->GetPage());
367 if (aBookmarkList
.Count() > 0)
369 mrSlideSorter
.GetView().BrkAction();
370 SdDrawDocument
* pDocument
= mrSlideSorter
.GetModel().GetDocument();
371 SdTransferable
* pTransferable
= new Transferable (
375 dynamic_cast<SlideSorterViewShell
*>(mrSlideSorter
.GetViewShell()));
378 SD_MOD()->pTransferDrag
= pTransferable
;
380 SD_MOD()->pTransferClip
= pTransferable
;
382 pDocument
->CreatingDataObj (pTransferable
);
383 pTransferable
->SetWorkDocument( dynamic_cast<SdDrawDocument
*>(pDocument
->AllocModel()) );
384 pDocument
->CreatingDataObj (NULL
);
385 TransferableObjectDescriptor aObjDesc
;
386 pTransferable
->GetWorkDocument()->GetDocSh()
387 ->FillTransferableObjectDescriptor (aObjDesc
);
389 if (pDocument
->GetDocSh() != NULL
)
390 aObjDesc
.maDisplayName
= pDocument
->GetDocSh()
391 ->GetMedium()->GetURLObject().GetURLNoPass();
393 ::Window
* pActionWindow
= pWindow
;
394 if (pActionWindow
== NULL
)
396 ViewShell
* pViewShell
= mrSlideSorter
.GetViewShell();
397 if (pViewShell
!= NULL
)
398 pActionWindow
= pViewShell
->GetActiveWindow();
401 pTransferable
->SetStartPos (pActionWindow
->PixelToLogic(
402 pActionWindow
->GetPointerPosPixel()));
403 pTransferable
->SetObjectDescriptor (aObjDesc
);
404 pTransferable
->SetPageBookmarks (aBookmarkList
, !bDrag
);
406 for (void* p
=aBookmarkList
.First(); p
!=NULL
; p
=aBookmarkList
.Next())
407 delete static_cast<String
*>(p
);
411 pTransferable
->SetView (&mrSlideSorter
.GetView());
412 sal_Int8
nDragSourceActions (DND_ACTION_COPY
);
413 // The move action is available only when not all pages would be
414 // moved. Otherwise an empty document would remain. Crash.
415 sal_Int32 nRemainingPages
= mrSlideSorter
.GetModel().GetPageCount() - aBookmarkList
.Count();
416 if (nRemainingPages
> 0)
417 nDragSourceActions
|= DND_ACTION_MOVE
;
418 pTransferable
->StartDrag (pActionWindow
, nDragSourceActions
);
421 pTransferable
->CopyToClipboard (pActionWindow
);
428 void Clipboard::StartDrag (
432 maPagesToRemove
.clear();
433 maPagesToSelect
.clear();
434 mbUpdateSelectionPending
= false;
435 CreateSlideTransferable (pWindow
, TRUE
);
441 void Clipboard::DragFinished (sal_Int8 nDropAction
)
443 // Hide the substitution display and insertion indicator.
444 mrSlideSorter
.GetView().GetOverlay().GetSubstitutionOverlay().setVisible(false);
445 mrSlideSorter
.GetView().GetOverlay().GetInsertionIndicatorOverlay().setVisible(false);
447 SdTransferable
* pDragTransferable
= SD_MOD()->pTransferDrag
;
449 if (pDragTransferable
!= NULL
)
450 pDragTransferable
->SetView (NULL
);
452 PageSelector
& rSelector (mrController
.GetPageSelector());
453 if ((nDropAction
& DND_ACTION_MOVE
) != 0
454 && ! maPagesToRemove
.empty())
456 // Remove the pages that have been moved to another place (possibly
457 // in the same document.)
458 rSelector
.DeselectAllPages();
459 PageList::iterator aDraggedPage
;
460 for (aDraggedPage
=maPagesToRemove
.begin();
461 aDraggedPage
!=maPagesToRemove
.end();
464 rSelector
.SelectPage (*aDraggedPage
);
466 mrController
.GetSelectionManager()->DeleteSelectedPages ();
475 void Clipboard::SelectPages (void)
477 PageSelector
& rSelector (mrController
.GetPageSelector());
479 // Select the dropped pages.
480 PageList::iterator iPage
;
481 rSelector
.DeselectAllPages();
482 for (iPage
=maPagesToSelect
.begin(); iPage
!=maPagesToSelect
.end(); ++iPage
)
484 rSelector
.SelectPage(*iPage
);
491 sal_Int8
Clipboard::AcceptDrop (
492 const AcceptDropEvent
& rEvent
,
493 DropTargetHelper
& rTargetHelper
,
494 ::sd::Window
* pTargetWindow
,
498 sal_Int8 nResult
= DND_ACTION_NONE
;
500 switch (IsDropAccepted())
505 nResult
= rEvent
.mnAction
;
507 // Use the copy action when the drop action is the default, i.e. not
508 // explicitly set to move or link, and when the source and
509 // target models are not the same.
510 const SdTransferable
* pDragTransferable
= SD_MOD()->pTransferDrag
;
511 if (pDragTransferable
!= NULL
512 && pDragTransferable
->IsPageTransferable()
513 && ((rEvent
.maDragEvent
.DropAction
514 & ::com::sun::star::datatransfer::dnd::DNDConstants::ACTION_DEFAULT
) != 0)
515 && (mrSlideSorter
.GetModel().GetDocument()->GetDocSh()
516 != pDragTransferable
->GetPageDocShell()))
518 nResult
= DND_ACTION_COPY
;
521 // Show the insertion marker and the substitution for a drop.
522 Point aPosition
= pTargetWindow
->PixelToLogic (rEvent
.maPosPixel
);
523 view::ViewOverlay
& rOverlay (mrSlideSorter
.GetView().GetOverlay());
524 rOverlay
.GetInsertionIndicatorOverlay().SetPosition (aPosition
);
525 rOverlay
.GetInsertionIndicatorOverlay().setVisible(true);
526 rOverlay
.GetSubstitutionOverlay().SetPosition (aPosition
);
528 // Scroll the window when the mouse reaches the window border.
529 mrController
.GetScrollBarManager().AutoScroll (rEvent
.maPosPixel
);
534 nResult
= ExecuteOrAcceptShapeDrop(
553 sal_Int8
Clipboard::ExecuteDrop (
554 const ExecuteDropEvent
& rEvent
,
555 DropTargetHelper
& rTargetHelper
,
556 ::sd::Window
* pTargetWindow
,
560 sal_Int8 nResult
= DND_ACTION_NONE
;
562 switch (IsDropAccepted())
566 const SdTransferable
* pDragTransferable
= SD_MOD()->pTransferDrag
;
567 const Point
aEventModelPosition (
568 pTargetWindow
->PixelToLogic (rEvent
.maPosPixel
));
569 long int nXOffset
= labs (pDragTransferable
->GetStartPos().X()
570 - aEventModelPosition
.X());
571 long int nYOffset
= labs (pDragTransferable
->GetStartPos().Y()
572 - aEventModelPosition
.Y());
573 const bool bContinue
=
574 ( pDragTransferable
->GetView() != &mrSlideSorter
.GetView() )
575 || ( nXOffset
>= 2 && nYOffset
>= 2 );
577 // Get insertion position and then turn off the insertion indicator.
578 view::ViewOverlay
& rOverlay (mrSlideSorter
.GetView().GetOverlay());
579 rOverlay
.GetInsertionIndicatorOverlay().SetPosition(
580 aEventModelPosition
);
581 USHORT nIndex
= DetermineInsertPosition (*pDragTransferable
);
582 OSL_TRACE ("Clipboard::AcceptDrop() called for index %d",
584 rOverlay
.GetInsertionIndicatorOverlay().setVisible(false);
588 SlideSorterController::ModelChangeLock
aModelChangeLock (mrController
);
590 if (pDragTransferable
->GetView() == &mrSlideSorter
.GetView()
591 && rEvent
.mnAction
== DND_ACTION_MOVE
)
593 // We are asked to move pages inside one view. For this we
594 // call MoveSelectedPages() which is faster than going the
597 // Remember to select the moved pages afterwards.
598 maPagesToRemove
.swap(maPagesToSelect
);
599 maPagesToRemove
.clear();
601 USHORT nSdrModelIndex
;
602 if (nIndex
!= SDRPAGE_NOTFOUND
)
603 nSdrModelIndex
= nIndex
/ 2 - 1;
605 nSdrModelIndex
= SDRPAGE_NOTFOUND
;
606 mrController
.GetSelectionManager()->MoveSelectedPages(nSdrModelIndex
);
607 mbUpdateSelectionPending
= true;
608 nResult
= DND_ACTION_NONE
;
612 // Handle a general drop operation.
613 HandlePageDrop (*pDragTransferable
);
614 nResult
= rEvent
.mnAction
;
621 nResult
= ExecuteOrAcceptShapeDrop(
640 USHORT
Clipboard::DetermineInsertPosition (const SdTransferable
& )
642 USHORT nInsertPosition
= SDRPAGE_NOTFOUND
;
644 // Tell the model to move the dragged pages behind the one with the
645 // index nInsertionIndex which first has to be transformed into an index
646 // understandable by the document.
647 view::InsertionIndicatorOverlay
& rOverlay (
648 mrSlideSorter
.GetView().GetOverlay().GetInsertionIndicatorOverlay());
649 sal_Int32
nInsertionIndex (rOverlay
.GetInsertionPageIndex());
651 // The index returned by the overlay starts with 1 for the first slide.
652 // This is now converted that to an SdModel index that also starts with 1.
653 if (nInsertionIndex
>= 0)
654 nInsertPosition
= (USHORT
)nInsertionIndex
* 2 + 1;
656 return nInsertPosition
;
662 USHORT
Clipboard::InsertSlides (
663 const SdTransferable
& rTransferable
,
664 USHORT nInsertPosition
)
666 USHORT nInsertedPageCount
= ViewClipboard::InsertSlides (
670 // Remember the inserted pages so that they can be selected when the
671 // operation is finished.
672 int nDocumentIndex
= nInsertPosition
/ 2 - 1;
673 for (USHORT i
=1; i
<=nInsertedPageCount
; i
++)
675 model::SharedPageDescriptor
pDescriptor (
676 mrSlideSorter
.GetModel().GetPageDescriptor(nDocumentIndex
+ i
));
677 if (pDescriptor
.get() != NULL
)
678 maPagesToSelect
.push_back (pDescriptor
->GetPage());
681 mbUpdateSelectionPending
|= (nInsertedPageCount
>0);
683 return nInsertedPageCount
;
689 Clipboard::DropType
Clipboard::IsDropAccepted (void) const
691 DropType
eResult (DT_NONE
);
693 const SdTransferable
* pDragTransferable
= SD_MOD()->pTransferDrag
;
694 if (pDragTransferable
!= NULL
)
696 if (pDragTransferable
->IsPageTransferable())
698 if (mrSlideSorter
.GetModel().GetEditMode() != EM_MASTERPAGE
)
713 sal_Int8
Clipboard::ExecuteOrAcceptShapeDrop (
714 DropCommand eCommand
,
715 const Point
& rPosition
,
716 const void* pDropEvent
,
717 DropTargetHelper
& rTargetHelper
,
718 ::sd::Window
* pTargetWindow
,
722 sal_Int8 nResult
= 0;
724 // The dropping of a shape is accepted or executed only when there is
725 // DrawViewShell available to which we can forward this call. This has
726 // technical reasons: The actual code to accept or execute a shape drop
727 // is implemented in the ViewShell class and uses the page view of the
728 // main edit view. This is not possible without a DrawViewShell.
729 ::boost::shared_ptr
<DrawViewShell
> pDrawViewShell
;
730 if (mrSlideSorter
.GetViewShell() != NULL
)
731 pDrawViewShell
= ::boost::dynamic_pointer_cast
<DrawViewShell
>(
732 mrSlideSorter
.GetViewShell()->GetViewShellBase().GetMainViewShell());
733 if (pDrawViewShell
.get() != NULL
734 && (pDrawViewShell
->GetShellType() == ViewShell::ST_IMPRESS
735 || pDrawViewShell
->GetShellType() == ViewShell::ST_DRAW
))
737 // The drop is only accepted or executed when it takes place over a
738 // page object. Therefore we replace a missing page number by the
739 // number of the page under the mouse.
740 if (nPage
== SDRPAGE_NOTFOUND
)
742 model::SharedPageDescriptor
pDescriptor (
743 mrSlideSorter
.GetModel().GetPageDescriptor(
744 mrSlideSorter
.GetView().GetPageIndexAtPoint(rPosition
)));
745 if (pDescriptor
.get() != NULL
&& pDescriptor
->GetPage()!=NULL
)
746 nPage
= (pDescriptor
->GetPage()->GetPageNum() - 1) / 2;
749 // Now comes the code that is different for the Execute and Accept:
750 // We simply forward the call to the AcceptDrop() or ExecuteDrop()
751 // methods of the DrawViewShell in the center pane.
752 if (nPage
!= SDRPAGE_NOTFOUND
)
756 nResult
= pDrawViewShell
->AcceptDrop(
757 *reinterpret_cast<const AcceptDropEvent
*>(pDropEvent
),
765 nResult
= pDrawViewShell
->ExecuteDrop(
766 *reinterpret_cast<const ExecuteDropEvent
*>(pDropEvent
),
780 } } } // end of namespace ::sd::slidesorter::controller