1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 #include "precompiled_sd.hxx"
25
26 #include "controller/SlsClipboard.hxx"
27
28 #include "SlideSorterViewShell.hxx"
29 #include "SlideSorter.hxx"
30 #include "model/SlideSorterModel.hxx"
31 #include "model/SlsPageDescriptor.hxx"
32 #include "model/SlsPageEnumerationProvider.hxx"
33 #include "view/SlideSorterView.hxx"
34 #include "view/SlsTheme.hxx"
35 #include "controller/SlideSorterController.hxx"
36 #include "controller/SlsInsertionIndicatorHandler.hxx"
37 #include "controller/SlsPageSelector.hxx"
38 #include "controller/SlsSelectionFunction.hxx"
39 #include "controller/SlsCurrentSlideManager.hxx"
40 #include "controller/SlsScrollBarManager.hxx"
41 #include "controller/SlsFocusManager.hxx"
42 #include "controller/SlsSelectionManager.hxx"
43 #include "controller/SlsTransferableData.hxx"
44 #include "controller/SlsSelectionObserver.hxx"
45 #include "controller/SlsVisibleAreaManager.hxx"
46 #include "cache/SlsPageCache.hxx"
47
48 #include "ViewShellBase.hxx"
49 #include "View.hxx"
50 #include "DrawViewShell.hxx"
51 #include "Window.hxx"
52 #include "fupoor.hxx"
53 #include "fuslhide.hxx"
54 #include "fuzoom.hxx"
55 #include "fucushow.hxx"
56 #include "fusldlg.hxx"
57 #include "fuexpand.hxx"
58 #include "fusumry.hxx"
59 #include "app.hrc"
60 #include "glob.hrc"
61 #include "strings.hrc"
62 #include "sdresid.hxx"
63 #include "sdxfer.hxx"
64 #include "sdmod.hxx"
65 #include "sddll.hxx"
66 #include "ins_paste.hxx"
67 #include "drawdoc.hxx"
68 #include "DrawDocShell.hxx"
69 #include "sdpage.hxx"
70 #include "sdtreelb.hxx"
71
72 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
73 #include <sfx2/request.hxx>
74 #include <sfx2/viewfrm.hxx>
75 #include <sfx2/bindings.hxx>
76 #include <sfx2/docfile.hxx>
77 #include <svx/svxids.hrc>
78 #include <svx/svdstr.hrc>
79 #include <vcl/msgbox.hxx>
80 #include <tools/urlobj.hxx>
81 #include <rtl/ustring.hxx>
82 #include <vos/mutex.hxx>
83 #include <vcl/svapp.hxx>
84 #include <boost/bind.hpp>
85
86
87 namespace sd { namespace slidesorter { namespace controller {
88
89
90 namespace {
91 /** Temporarily deactivate slide tracking of the VisibleAreaManager.
92 This is used as a workaround to avoid unwanted repositioning of
93 the visible area when the selection of slides is copied to the
94 clipboard (cloning of slides leads to model change notifications
95 for the original model.)
96 */
97 class TemporarySlideTrackingDeactivator
98 {
99 public:
TemporarySlideTrackingDeactivator(SlideSorterController & rController)100 TemporarySlideTrackingDeactivator (SlideSorterController& rController)
101 : mrController(rController),
102 mbIsCurrentSlideTrackingActive (
103 mrController.GetVisibleAreaManager().IsCurrentSlideTrackingActive())
104 {
105 if (mbIsCurrentSlideTrackingActive)
106 mrController.GetVisibleAreaManager().DeactivateCurrentSlideTracking();
107 }
~TemporarySlideTrackingDeactivator(void)108 ~TemporarySlideTrackingDeactivator (void)
109 {
110 if (mbIsCurrentSlideTrackingActive)
111 mrController.GetVisibleAreaManager().ActivateCurrentSlideTracking();
112 }
113
114 private:
115 SlideSorterController& mrController;
116 const bool mbIsCurrentSlideTrackingActive;
117 };
118 } // end of anonymous namespace
119
120
121 class Clipboard::UndoContext
122 {
123 public:
UndoContext(SdDrawDocument * pDocument,const::boost::shared_ptr<ViewShell> & rpMainViewShell,const::boost::shared_ptr<view::Theme> & rpTheme)124 UndoContext (
125 SdDrawDocument* pDocument,
126 const ::boost::shared_ptr<ViewShell>& rpMainViewShell,
127 const ::boost::shared_ptr<view::Theme>& rpTheme)
128 : mpDocument(pDocument),
129 mpMainViewShell(rpMainViewShell)
130 {
131 if (mpDocument!=NULL && mpDocument->IsUndoEnabled())
132 {
133 if (mpMainViewShell && mpMainViewShell->GetShellType() == ViewShell::ST_DRAW)
134 mpDocument->BegUndo(rpTheme->GetString(view::Theme::String_DragAndDropPages));
135 else
136 mpDocument->BegUndo(rpTheme->GetString(view::Theme::String_DragAndDropSlides));
137 }
138 }
139
~UndoContext(void)140 ~UndoContext (void)
141 {
142 if (mpDocument!=NULL && mpDocument->IsUndoEnabled())
143 mpDocument->EndUndo();
144 if (mpMainViewShell && mpMainViewShell->GetViewFrame()!=NULL)
145 {
146 SfxBindings& rBindings = mpMainViewShell->GetViewFrame()->GetBindings();
147 rBindings.Invalidate(SID_UNDO);
148 rBindings.Invalidate(SID_REDO);
149 }
150 }
151 private:
152 SdDrawDocument* mpDocument;
153 ::boost::shared_ptr<ViewShell> mpMainViewShell;
154 };
155
156
157
158
Clipboard(SlideSorter & rSlideSorter)159 Clipboard::Clipboard (SlideSorter& rSlideSorter)
160 : ViewClipboard(rSlideSorter.GetView()),
161 mrSlideSorter(rSlideSorter),
162 mrController(mrSlideSorter.GetController()),
163 maPagesToRemove(),
164 maPagesToSelect(),
165 mbUpdateSelectionPending(false),
166 mpUndoContext(),
167 mpSelectionObserverContext(),
168 mnDragFinishedUserEventId(0)
169 {
170 }
171
172
173
174
~Clipboard(void)175 Clipboard::~Clipboard (void)
176 {
177 if (mnDragFinishedUserEventId != 0)
178 Application::RemoveUserEvent(mnDragFinishedUserEventId);
179 }
180
181
182
183
184 /** With the current implementation the forwarded calls to the current
185 function will come back eventually to call the local Do(Cut|Copy|Paste)
186 methods. A shortcut is possible but would be an unclean hack.
187 */
HandleSlotCall(SfxRequest & rRequest)188 void Clipboard::HandleSlotCall (SfxRequest& rRequest)
189 {
190 ViewShell* pViewShell = mrSlideSorter.GetViewShell();
191 FunctionReference xFunc;
192 if (pViewShell != NULL)
193 xFunc = pViewShell->GetCurrentFunction();
194 switch (rRequest.GetSlot())
195 {
196 case SID_CUT:
197 if (mrSlideSorter.GetModel().GetEditMode() != EM_MASTERPAGE)
198 {
199 if(xFunc.is())
200 xFunc->DoCut();
201 else
202 DoCut();
203 }
204 rRequest.Done();
205 break;
206
207 case SID_COPY:
208 if (mrSlideSorter.GetModel().GetEditMode() != EM_MASTERPAGE)
209 {
210 if(xFunc.is())
211 xFunc->DoCopy();
212 else
213 DoCopy();
214 }
215 rRequest.Done();
216 break;
217
218 case SID_PASTE:
219 // Prevent redraws while inserting pages from the clipboard
220 // because the intermediate inconsistent state might lead to
221 // a crash.
222 if (mrSlideSorter.GetModel().GetEditMode() != EM_MASTERPAGE)
223 {
224 view::SlideSorterView::DrawLock aLock (mrSlideSorter);
225 SelectionObserver::Context aContext (mrSlideSorter);
226 if(xFunc.is())
227 xFunc->DoPaste();
228 else
229 DoPaste();
230 }
231 rRequest.Done();
232 break;
233
234 case SID_DELETE:
235 DoDelete();
236 rRequest.Done();
237 break;
238 }
239 }
240
241
242
243
DoCut(::Window * pWindow)244 void Clipboard::DoCut (::Window* pWindow)
245 {
246 if (mrSlideSorter.GetModel().GetPageCount() > 1)
247 {
248 DoCopy(pWindow);
249 DoDelete(pWindow);
250 }
251 }
252
253
254
255
DoDelete(::Window *)256 void Clipboard::DoDelete (::Window* )
257 {
258 if (mrSlideSorter.GetModel().GetPageCount() > 1)
259 {
260 mrController.GetSelectionManager()->DeleteSelectedPages();
261 }
262 }
263
264
265
266
DoCopy(::Window * pWindow)267 void Clipboard::DoCopy (::Window* pWindow )
268 {
269 CreateSlideTransferable( pWindow, sal_False );
270 }
271
272
273
274
DoPaste(::Window * pWindow)275 void Clipboard::DoPaste (::Window* pWindow)
276 {
277 SdTransferable* pClipTransferable = SD_MOD()->pTransferClip;
278
279 if (pClipTransferable!=NULL && pClipTransferable->IsPageTransferable())
280 {
281 sal_Int32 nInsertPosition = GetInsertionPosition(pWindow);
282
283 if (nInsertPosition >= 0)
284 {
285 // Paste the pages from the clipboard.
286 sal_Int32 nInsertPageCount = PasteTransferable(nInsertPosition);
287 // Select the pasted pages and make the first of them the
288 // current page.
289 mrSlideSorter.GetContentWindow()->GrabFocus();
290 SelectPageRange(nInsertPosition, nInsertPageCount);
291 }
292 }
293 }
294
295
296
297
GetInsertionPosition(::Window * pWindow)298 sal_Int32 Clipboard::GetInsertionPosition (::Window* pWindow)
299 {
300 sal_Int32 nInsertPosition = -1;
301
302 // Determine the insertion position:
303 // a) When the insertion indicator is visible, then at that position.
304 // b) When the focus indicator is visible, then before or after the
305 // focused page, depending on user input to a dialog.
306 // c) When there is a selection but no focus, then after the
307 // selection.
308 // d) After the last page when there is no selection and no focus.
309
310 ::boost::shared_ptr<controller::InsertionIndicatorHandler> pInsertionIndicatorHandler (
311 mrController.GetInsertionIndicatorHandler());
312 if (pInsertionIndicatorHandler->IsActive())
313 {
314 // Use the insertion index of an active insertion indicator.
315 nInsertPosition = pInsertionIndicatorHandler->GetInsertionPageIndex();
316 }
317 else if (mrController.GetSelectionManager()->GetInsertionPosition() >= 0)
318 {
319 // Use the insertion index of an insertion indicator that has been
320 // deactivated a short while ago.
321 nInsertPosition = mrController.GetSelectionManager()->GetInsertionPosition();
322 }
323 else if (mrController.GetFocusManager().IsFocusShowing())
324 {
325 // Use the focus to determine the insertion position.
326 SdInsertPasteDlg aDialog (pWindow);
327 if (aDialog.Execute() == RET_OK)
328 {
329 nInsertPosition = mrController.GetFocusManager().GetFocusedPageIndex();
330 if ( ! aDialog.IsInsertBefore())
331 nInsertPosition ++;
332 }
333 }
334
335 return nInsertPosition;
336 }
337
338
339
340
PasteTransferable(sal_Int32 nInsertPosition)341 sal_Int32 Clipboard::PasteTransferable (sal_Int32 nInsertPosition)
342 {
343 SdTransferable* pClipTransferable = SD_MOD()->pTransferClip;
344 model::SlideSorterModel& rModel (mrSlideSorter.GetModel());
345 bool bMergeMasterPages = !pClipTransferable->HasSourceDoc (rModel.GetDocument());
346 sal_uInt16 nInsertIndex (rModel.GetCoreIndex(nInsertPosition));
347 sal_Int32 nInsertPageCount (0);
348 if (pClipTransferable->HasPageBookmarks())
349 {
350 const List& rBookmarkList = pClipTransferable->GetPageBookmarks();
351 const ::vos::OGuard aGuard (Application::GetSolarMutex());
352
353 nInsertPageCount = (sal_uInt16) rBookmarkList.Count();
354 rModel.GetDocument()->InsertBookmarkAsPage(
355 const_cast<List*>(&rBookmarkList),
356 NULL,
357 sal_False,
358 sal_False,
359 nInsertIndex,
360 sal_False,
361 pClipTransferable->GetPageDocShell(),
362 sal_True,
363 bMergeMasterPages,
364 sal_False);
365 }
366 else
367 {
368 SfxObjectShell* pShell = pClipTransferable->GetDocShell();
369 DrawDocShell* pDataDocSh = (DrawDocShell*)pShell;
370 SdDrawDocument* pDataDoc = pDataDocSh->GetDoc();
371
372 if (pDataDoc!=NULL
373 && pDataDoc->GetSdPageCount(PK_STANDARD))
374 {
375 const ::vos::OGuard aGuard (Application::GetSolarMutex());
376
377 bMergeMasterPages = (pDataDoc != rModel.GetDocument());
378 nInsertPageCount = pDataDoc->GetSdPageCount( PK_STANDARD );
379 rModel.GetDocument()->InsertBookmarkAsPage(
380 NULL,
381 NULL,
382 sal_False,
383 sal_False,
384 nInsertIndex,
385 sal_False,
386 pDataDocSh,
387 sal_True,
388 bMergeMasterPages,
389 sal_False);
390 }
391 }
392 mrController.HandleModelChange();
393 return nInsertPageCount;
394 }
395
396
397
398
SelectPageRange(sal_Int32 nFirstIndex,sal_Int32 nPageCount)399 void Clipboard::SelectPageRange (sal_Int32 nFirstIndex, sal_Int32 nPageCount)
400 {
401 // Select the newly inserted pages. That are the nInsertPageCount pages
402 // after the nInsertIndex position.
403 PageSelector& rSelector (mrController.GetPageSelector());
404 rSelector.DeselectAllPages();
405 for (sal_uInt16 i=0; i<nPageCount; i++)
406 {
407 model::SharedPageDescriptor pDescriptor (
408 mrSlideSorter.GetModel().GetPageDescriptor(nFirstIndex + i));
409 if (pDescriptor.get() != NULL)
410 {
411 rSelector.SelectPage(pDescriptor);
412 // The first page of the new selection is made the current page.
413 if (i == 0)
414 {
415 mrController.GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor);
416 }
417 }
418 }
419 }
420
421
422
423
CreateSlideTransferable(::Window * pWindow,bool bDrag)424 void Clipboard::CreateSlideTransferable (
425 ::Window* pWindow,
426 bool bDrag)
427 {
428 List aBookmarkList;
429
430 // Insert all selected pages into a bookmark list and remember them in
431 // maPagesToRemove for possible later removal.
432 model::PageEnumeration aSelectedPages
433 (model::PageEnumerationProvider::CreateSelectedPagesEnumeration(
434 mrSlideSorter.GetModel()));
435 while (aSelectedPages.HasMoreElements())
436 {
437 model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement());
438 aBookmarkList.Insert (
439 new String(pDescriptor->GetPage()->GetName()),
440 LIST_APPEND);
441 maPagesToRemove.push_back (pDescriptor->GetPage());
442 }
443
444 // Create a small set of representatives of the selection for which
445 // previews are included into the transferable so that an insertion
446 // indicator can be rendered.
447 aSelectedPages.Rewind();
448 ::std::vector<TransferableData::Representative> aRepresentatives;
449 aRepresentatives.reserve(3);
450 ::boost::shared_ptr<cache::PageCache> pPreviewCache (
451 mrSlideSorter.GetView().GetPreviewCache());
452 while (aSelectedPages.HasMoreElements())
453 {
454 model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement());
455 if ( ! pDescriptor || pDescriptor->GetPage()==NULL)
456 continue;
457 Bitmap aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false));
458 aRepresentatives.push_back(TransferableData::Representative(
459 aPreview,
460 pDescriptor->HasState(model::PageDescriptor::ST_Excluded)));
461 if (aRepresentatives.size() >= 3)
462 break;
463 }
464
465 if (aBookmarkList.Count() > 0)
466 {
467 mrSlideSorter.GetView().BrkAction();
468 SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument();
469 SdTransferable* pTransferable = TransferableData::CreateTransferable (
470 pDocument,
471 NULL,
472 sal_False,
473 dynamic_cast<SlideSorterViewShell*>(mrSlideSorter.GetViewShell()),
474 aRepresentatives);
475
476 if (bDrag)
477 SD_MOD()->pTransferDrag = pTransferable;
478 else
479 SD_MOD()->pTransferClip = pTransferable;
480
481 pDocument->CreatingDataObj (pTransferable);
482 pTransferable->SetWorkDocument( dynamic_cast<SdDrawDocument*>(pDocument->AllocModel()) );
483 pDocument->CreatingDataObj (NULL);
484 TransferableObjectDescriptor aObjDesc;
485 pTransferable->GetWorkDocument()->GetDocSh()
486 ->FillTransferableObjectDescriptor (aObjDesc);
487
488 if (pDocument->GetDocSh() != NULL)
489 aObjDesc.maDisplayName = pDocument->GetDocSh()
490 ->GetMedium()->GetURLObject().GetURLNoPass();
491
492 ::Window* pActionWindow = pWindow;
493 if (pActionWindow == NULL)
494 {
495 ViewShell* pViewShell = mrSlideSorter.GetViewShell();
496 if (pViewShell != NULL)
497 pActionWindow = pViewShell->GetActiveWindow();
498 }
499
500 pTransferable->SetStartPos (pActionWindow->PixelToLogic(
501 pActionWindow->GetPointerPosPixel()));
502 pTransferable->SetObjectDescriptor (aObjDesc);
503
504 {
505 TemporarySlideTrackingDeactivator aDeactivator (mrController);
506 pTransferable->SetPageBookmarks (aBookmarkList, !bDrag);
507 }
508
509 for (void* p=aBookmarkList.First(); p!=NULL; p=aBookmarkList.Next())
510 delete static_cast<String*>(p);
511
512 if (bDrag)
513 {
514 pTransferable->SetView (&mrSlideSorter.GetView());
515 sal_Int8 nDragSourceActions (DND_ACTION_COPY);
516 // The move action is available only when not all pages would be
517 // moved. Otherwise an empty document would remain. Crash.
518 sal_Int32 nRemainingPages = mrSlideSorter.GetModel().GetPageCount() - aBookmarkList.Count();
519 if (nRemainingPages > 0)
520 nDragSourceActions |= DND_ACTION_MOVE;
521 pTransferable->StartDrag (pActionWindow, nDragSourceActions);
522 }
523 else
524 pTransferable->CopyToClipboard (pActionWindow);
525 }
526 }
527
528
529
530
CreateTransferableUserData(SdTransferable * pTransferable)531 ::boost::shared_ptr<SdTransferable::UserData> Clipboard::CreateTransferableUserData (SdTransferable* pTransferable)
532 {
533 do
534 {
535 SdPageObjsTLB::SdPageObjsTransferable* pTreeListBoxTransferable
536 = dynamic_cast<SdPageObjsTLB::SdPageObjsTransferable*>(pTransferable);
537 if (pTreeListBoxTransferable == NULL)
538 break;
539
540 // Find view shell for the document of the transferable.
541 ::sd::ViewShell* pViewShell
542 = SdPageObjsTLB::GetViewShellForDocShell(pTreeListBoxTransferable->GetDocShell());
543 if (pViewShell == NULL)
544 break;
545
546 // Find slide sorter for the document of the transferable.
547 SlideSorterViewShell* pSlideSorterViewShell
548 = SlideSorterViewShell::GetSlideSorter(pViewShell->GetViewShellBase());
549 if (pSlideSorterViewShell == NULL)
550 break;
551 SlideSorter& rSlideSorter (pSlideSorterViewShell->GetSlideSorter());
552
553 // Get bookmark from transferable.
554 TransferableDataHelper aDataHelper (pTransferable);
555 INetBookmark aINetBookmark;
556 if ( ! aDataHelper.GetINetBookmark(SOT_FORMATSTR_ID_NETSCAPE_BOOKMARK, aINetBookmark))
557 break;
558 const rtl::OUString sURL (aINetBookmark.GetURL());
559 const sal_Int32 nIndex (sURL.indexOf((sal_Unicode)'#'));
560 if (nIndex == -1)
561 break;
562 String sBookmark (sURL.copy(nIndex+1));
563
564 // Make sure that the bookmark points to a page.
565 SdDrawDocument* pTransferableDocument = rSlideSorter.GetModel().GetDocument();
566 if (pTransferableDocument == NULL)
567 break;
568 sal_Bool bIsMasterPage = sal_False;
569 const sal_uInt16 nPageIndex (pTransferableDocument->GetPageByName(sBookmark, bIsMasterPage));
570 if (nPageIndex == SDRPAGE_NOTFOUND)
571 break;
572
573 // Create preview.
574 ::std::vector<TransferableData::Representative> aRepresentatives;
575 aRepresentatives.reserve(1);
576 ::boost::shared_ptr<cache::PageCache> pPreviewCache (
577 rSlideSorter.GetView().GetPreviewCache());
578 model::SharedPageDescriptor pDescriptor (rSlideSorter.GetModel().GetPageDescriptor((nPageIndex-1)/2));
579 if ( ! pDescriptor || pDescriptor->GetPage()==NULL)
580 break;
581 Bitmap aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false));
582 aRepresentatives.push_back(TransferableData::Representative(
583 aPreview,
584 pDescriptor->HasState(model::PageDescriptor::ST_Excluded)));
585
586 // Remember the page in maPagesToRemove so that it can be removed
587 // when drag and drop action is "move".
588 Clipboard& rOtherClipboard (pSlideSorterViewShell->GetSlideSorter().GetController().GetClipboard());
589 rOtherClipboard.maPagesToRemove.clear();
590 rOtherClipboard.maPagesToRemove.push_back(pDescriptor->GetPage());
591
592 // Create the new transferable.
593 ::boost::shared_ptr<SdTransferable::UserData> pNewTransferable (
594 new TransferableData(
595 pSlideSorterViewShell,
596 aRepresentatives));
597 pTransferable->SetWorkDocument( dynamic_cast<SdDrawDocument*>(
598 pTreeListBoxTransferable->GetSourceDoc()->AllocModel()));
599 // pTransferable->SetView(&mrSlideSorter.GetView());
600
601 // Set page bookmark list.
602 List aPageBookmarks;
603 aPageBookmarks.Insert(new String(sBookmark));
604 pTransferable->SetPageBookmarks(aPageBookmarks, false);
605
606 // Replace the view referenced by the transferable with the
607 // corresponding slide sorter view.
608 pTransferable->SetView(&pSlideSorterViewShell->GetSlideSorter().GetView());
609
610 return pNewTransferable;
611 }
612 while (false);
613
614 return ::boost::shared_ptr<SdTransferable::UserData>();
615 }
616
617
618
619
StartDrag(const Point & rPosition,::Window * pWindow)620 void Clipboard::StartDrag (
621 const Point& rPosition,
622 ::Window* pWindow)
623 {
624 maPagesToRemove.clear();
625 maPagesToSelect.clear();
626 mbUpdateSelectionPending = false;
627 CreateSlideTransferable(pWindow, sal_True);
628
629 mrController.GetInsertionIndicatorHandler()->UpdatePosition(
630 rPosition,
631 InsertionIndicatorHandler::UnknownMode);
632 }
633
634
635
636
DragFinished(sal_Int8 nDropAction)637 void Clipboard::DragFinished (sal_Int8 nDropAction)
638 {
639 // SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag;
640
641 if (mnDragFinishedUserEventId == 0)
642 {
643 if ( ! Application::PostUserEvent(
644 mnDragFinishedUserEventId,
645 LINK(this, Clipboard, ProcessDragFinished),
646 reinterpret_cast<void*>(nDropAction)))
647 {
648 mnDragFinishedUserEventId = 0;
649 }
650 }
651 }
652
653
654
655
IMPL_LINK(Clipboard,ProcessDragFinished,void *,pUserData)656 IMPL_LINK(Clipboard, ProcessDragFinished, void*, pUserData)
657 {
658 const sal_Int8 nDropAction (static_cast<sal_Int8>(reinterpret_cast<sal_IntPtr>(pUserData)));
659
660 mnDragFinishedUserEventId = 0;
661
662 // Hide the substitution display and insertion indicator.
663 ::rtl::Reference<SelectionFunction> pFunction (mrController.GetCurrentSelectionFunction());
664 if (pFunction.is())
665 pFunction->NotifyDragFinished();
666
667 PageSelector& rSelector (mrController.GetPageSelector());
668 if ((nDropAction & DND_ACTION_MOVE) != 0
669 && ! maPagesToRemove.empty())
670 {
671 // Remove the pages that have been moved to another place (possibly
672 // in the same document.)
673 rSelector.DeselectAllPages();
674 PageList::iterator aDraggedPage;
675 for (aDraggedPage=maPagesToRemove.begin();
676 aDraggedPage!=maPagesToRemove.end();
677 aDraggedPage++)
678 {
679 rSelector.SelectPage(*aDraggedPage);
680 }
681 mrController.GetSelectionManager()->DeleteSelectedPages();
682 }
683 mpUndoContext.reset();
684 mpSelectionObserverContext.reset();
685
686 return 1;
687 }
688
689
690
691
SelectPages(void)692 void Clipboard::SelectPages (void)
693 {
694 PageSelector& rSelector (mrController.GetPageSelector());
695
696 // Select the dropped pages.
697 PageList::iterator iPage;
698 rSelector.DeselectAllPages();
699 for (iPage=maPagesToSelect.begin(); iPage!=maPagesToSelect.end(); ++iPage)
700 {
701 rSelector.SelectPage(*iPage);
702 }
703 }
704
705
706
707
AcceptDrop(const AcceptDropEvent & rEvent,DropTargetHelper & rTargetHelper,::sd::Window * pTargetWindow,sal_uInt16 nPage,sal_uInt16 nLayer)708 sal_Int8 Clipboard::AcceptDrop (
709 const AcceptDropEvent& rEvent,
710 DropTargetHelper& rTargetHelper,
711 ::sd::Window* pTargetWindow,
712 sal_uInt16 nPage,
713 sal_uInt16 nLayer)
714 {
715 sal_Int8 nAction (DND_ACTION_NONE);
716
717 const Clipboard::DropType eDropType (IsDropAccepted(rTargetHelper));
718
719 switch (eDropType)
720 {
721 case DT_PAGE:
722 case DT_PAGE_FROM_NAVIGATOR:
723 {
724 // Accept a drop.
725 nAction = rEvent.mnAction;
726
727 // Use the copy action when the drop action is the default, i.e. not
728 // explicitly set to move or link, and when the source and
729 // target models are not the same.
730 SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag;
731 if (pDragTransferable != NULL
732 && pDragTransferable->IsPageTransferable()
733 && ((rEvent.maDragEvent.DropAction
734 & ::com::sun::star::datatransfer::dnd::DNDConstants::ACTION_DEFAULT) != 0)
735 && (mrSlideSorter.GetModel().GetDocument()->GetDocSh()
736 != pDragTransferable->GetPageDocShell()))
737 {
738 nAction = DND_ACTION_COPY;
739 }
740 else if (IsInsertionTrivial(pDragTransferable, nAction))
741 {
742 nAction = DND_ACTION_NONE;
743 }
744
745 // Show the insertion marker and the substitution for a drop.
746 SelectionFunction* pSelectionFunction = dynamic_cast<SelectionFunction*>(
747 mrSlideSorter.GetViewShell()->GetCurrentFunction().get());
748 if (pSelectionFunction != NULL)
749 pSelectionFunction->MouseDragged(rEvent, nAction);
750
751 // Scroll the window when the mouse reaches the window border.
752 // mrController.GetScrollBarManager().AutoScroll (rEvent.maPosPixel);
753 }
754 break;
755
756 case DT_SHAPE:
757 nAction = ExecuteOrAcceptShapeDrop(
758 DC_ACCEPT,
759 rEvent.maPosPixel,
760 &rEvent,
761 rTargetHelper,
762 pTargetWindow,
763 nPage,
764 nLayer);
765 break;
766
767 default:
768 case DT_NONE:
769 nAction = DND_ACTION_NONE;
770 break;
771 }
772
773 return nAction;
774 }
775
776
777
778
ExecuteDrop(const ExecuteDropEvent & rEvent,DropTargetHelper & rTargetHelper,::sd::Window * pTargetWindow,sal_uInt16 nPage,sal_uInt16 nLayer)779 sal_Int8 Clipboard::ExecuteDrop (
780 const ExecuteDropEvent& rEvent,
781 DropTargetHelper& rTargetHelper,
782 ::sd::Window* pTargetWindow,
783 sal_uInt16 nPage,
784 sal_uInt16 nLayer)
785 {
786 sal_Int8 nResult = DND_ACTION_NONE;
787 mpUndoContext.reset();
788 const Clipboard::DropType eDropType (IsDropAccepted(rTargetHelper));
789
790 switch (eDropType)
791 {
792 case DT_PAGE:
793 case DT_PAGE_FROM_NAVIGATOR:
794 {
795 SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag;
796 const Point aEventModelPosition (
797 pTargetWindow->PixelToLogic (rEvent.maPosPixel));
798 const sal_Int32 nXOffset (labs (pDragTransferable->GetStartPos().X()
799 - aEventModelPosition.X()));
800 const sal_Int32 nYOffset (labs (pDragTransferable->GetStartPos().Y()
801 - aEventModelPosition.Y()));
802 bool bContinue =
803 ( pDragTransferable->GetView() != &mrSlideSorter.GetView() )
804 || ( nXOffset >= 2 && nYOffset >= 2 );
805
806 ::boost::shared_ptr<InsertionIndicatorHandler> pInsertionIndicatorHandler(
807 mrController.GetInsertionIndicatorHandler());
808 // Get insertion position and then turn off the insertion indicator.
809 pInsertionIndicatorHandler->UpdatePosition(aEventModelPosition, rEvent.mnAction);
810 // sal_uInt16 nIndex = DetermineInsertPosition(*pDragTransferable);
811
812 // Do not process the insertion when it is trivial,
813 // i.e. would insert pages at their original place.
814 if (IsInsertionTrivial(pDragTransferable, rEvent.mnAction))
815 bContinue = false;
816
817 // Tell the insertion indicator handler to hide before the model
818 // is modified. Doing it later may result in page objects whose
819 // animation state is not properly reset because they are then
820 // in another run then before the model change.
821 pInsertionIndicatorHandler->End(Animator::AM_Immediate);
822
823 if (bContinue)
824 {
825 SlideSorterController::ModelChangeLock aModelChangeLock (mrController);
826
827 // Handle a general drop operation.
828 mpUndoContext.reset(new UndoContext (
829 mrSlideSorter.GetModel().GetDocument(),
830 mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell(),
831 mrSlideSorter.GetTheme()));
832 mpSelectionObserverContext.reset(new SelectionObserver::Context(mrSlideSorter));
833
834 HandlePageDrop(*pDragTransferable);
835 nResult = rEvent.mnAction;
836
837 // We leave the undo context alive for when moving or
838 // copying inside one view then the actions in
839 // NotifyDragFinished should be covered as well as
840 // well as the ones above.
841 }
842
843 // When the pages originated in another slide sorter then
844 // only that is notified automatically about the drag
845 // operation being finished. Because the target slide sorter
846 // has be notified, too, add a callback for that.
847 ::boost::shared_ptr<TransferableData> pSlideSorterTransferable (
848 TransferableData::GetFromTransferable(pDragTransferable));
849 BOOST_ASSERT(pSlideSorterTransferable);
850 if (pSlideSorterTransferable
851 && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell())
852 {
853 DragFinished(nResult);
854 }
855
856 // Notify the receiving selection function that drag-and-drop is
857 // finished and the substitution handler can be released.
858 ::rtl::Reference<SelectionFunction> pFunction (
859 mrController.GetCurrentSelectionFunction());
860 if (pFunction.is())
861 pFunction->NotifyDragFinished();
862 }
863 break;
864
865 case DT_SHAPE:
866 nResult = ExecuteOrAcceptShapeDrop(
867 DC_EXECUTE,
868 rEvent.maPosPixel,
869 &rEvent,
870 rTargetHelper,
871 pTargetWindow,
872 nPage,
873 nLayer);
874 break;
875
876 default:
877 case DT_NONE:
878 break;
879 }
880
881 return nResult;
882 }
883
884
885
886
IsInsertionTrivial(SdTransferable * pTransferable,const sal_Int8 nDndAction) const887 bool Clipboard::IsInsertionTrivial (
888 SdTransferable* pTransferable,
889 const sal_Int8 nDndAction) const
890 {
891 ::boost::shared_ptr<TransferableData> pSlideSorterTransferable (
892 TransferableData::GetFromTransferable(pTransferable));
893 if (pSlideSorterTransferable
894 && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell())
895 return false;
896 return mrController.GetInsertionIndicatorHandler()->IsInsertionTrivial(nDndAction);
897 }
898
899
900
901
Abort(void)902 void Clipboard::Abort (void)
903 {
904 if (mpSelectionObserverContext)
905 {
906 mpSelectionObserverContext->Abort();
907 mpSelectionObserverContext.reset();
908 }
909 }
910
911
912
913
DetermineInsertPosition(const SdTransferable &)914 sal_uInt16 Clipboard::DetermineInsertPosition (const SdTransferable& )
915 {
916 // Tell the model to move the dragged pages behind the one with the
917 // index nInsertionIndex which first has to be transformed into an index
918 // understandable by the document.
919 const sal_Int32 nInsertionIndex (
920 mrController.GetInsertionIndicatorHandler()->GetInsertionPageIndex());
921
922 // Convert to insertion index to that of an SdModel.
923 if (nInsertionIndex >= 0)
924 return mrSlideSorter.GetModel().GetCoreIndex(nInsertionIndex);
925 else
926 return 0;
927 }
928
929
930
931
InsertSlides(const SdTransferable & rTransferable,sal_uInt16 nInsertPosition)932 sal_uInt16 Clipboard::InsertSlides (
933 const SdTransferable& rTransferable,
934 sal_uInt16 nInsertPosition)
935 {
936 sal_uInt16 nInsertedPageCount = ViewClipboard::InsertSlides (
937 rTransferable,
938 nInsertPosition);
939
940 // Remember the inserted pages so that they can be selected when the
941 // operation is finished.
942 maPagesToSelect.clear();
943 SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument();
944 if (pDocument != NULL)
945 for (sal_Int32 i=0; i<=nInsertedPageCount; i+=2)
946 maPagesToSelect.push_back(
947 dynamic_cast<SdPage*>(pDocument->GetPage(nInsertPosition+i)));
948
949 mbUpdateSelectionPending |= (nInsertedPageCount>0);
950
951 return nInsertedPageCount;
952 }
953
954
955
956
IsDropAccepted(DropTargetHelper &) const957 Clipboard::DropType Clipboard::IsDropAccepted (DropTargetHelper&) const
958 {
959 const SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag;
960 if (pDragTransferable == NULL)
961 return DT_NONE;
962
963 if (pDragTransferable->IsPageTransferable())
964 {
965 if (mrSlideSorter.GetModel().GetEditMode() != EM_MASTERPAGE)
966 return DT_PAGE;
967 else
968 return DT_NONE;
969 }
970
971 const SdPageObjsTLB::SdPageObjsTransferable* pPageObjsTransferable
972 = dynamic_cast<const SdPageObjsTLB::SdPageObjsTransferable*>(pDragTransferable);
973 if (pPageObjsTransferable != NULL)
974 return DT_PAGE_FROM_NAVIGATOR;
975
976 return DT_SHAPE;
977 }
978
979
980
981
ExecuteOrAcceptShapeDrop(DropCommand eCommand,const Point & rPosition,const void * pDropEvent,DropTargetHelper & rTargetHelper,::sd::Window * pTargetWindow,sal_uInt16 nPage,sal_uInt16 nLayer)982 sal_Int8 Clipboard::ExecuteOrAcceptShapeDrop (
983 DropCommand eCommand,
984 const Point& rPosition,
985 const void* pDropEvent,
986 DropTargetHelper& rTargetHelper,
987 ::sd::Window* pTargetWindow,
988 sal_uInt16 nPage,
989 sal_uInt16 nLayer)
990 {
991 sal_Int8 nResult = 0;
992
993 // The dropping of a shape is accepted or executed only when there is
994 // DrawViewShell available to which we can forward this call. This has
995 // technical reasons: The actual code to accept or execute a shape drop
996 // is implemented in the ViewShell class and uses the page view of the
997 // main edit view. This is not possible without a DrawViewShell.
998 ::boost::shared_ptr<DrawViewShell> pDrawViewShell;
999 if (mrSlideSorter.GetViewShell() != NULL)
1000 pDrawViewShell = ::boost::dynamic_pointer_cast<DrawViewShell>(
1001 mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell());
1002 if (pDrawViewShell.get() != NULL
1003 && (pDrawViewShell->GetShellType() == ViewShell::ST_IMPRESS
1004 || pDrawViewShell->GetShellType() == ViewShell::ST_DRAW))
1005 {
1006 // The drop is only accepted or executed when it takes place over a
1007 // page object. Therefore we replace a missing page number by the
1008 // number of the page under the mouse.
1009 if (nPage == SDRPAGE_NOTFOUND)
1010 {
1011 model::SharedPageDescriptor pDescriptor (
1012 mrSlideSorter.GetModel().GetPageDescriptor(
1013 mrSlideSorter.GetView().GetPageIndexAtPoint(rPosition)));
1014 if (pDescriptor)
1015 nPage = pDescriptor->GetPageIndex();
1016 }
1017
1018 // Now comes the code that is different for the Execute and Accept:
1019 // We simply forward the call to the AcceptDrop() or ExecuteDrop()
1020 // methods of the DrawViewShell in the center pane.
1021 if (nPage != SDRPAGE_NOTFOUND)
1022 switch (eCommand)
1023 {
1024 case DC_ACCEPT:
1025 nResult = pDrawViewShell->AcceptDrop(
1026 *reinterpret_cast<const AcceptDropEvent*>(pDropEvent),
1027 rTargetHelper,
1028 pTargetWindow,
1029 nPage,
1030 nLayer);
1031 break;
1032
1033 case DC_EXECUTE:
1034 nResult = pDrawViewShell->ExecuteDrop(
1035 *reinterpret_cast<const ExecuteDropEvent*>(pDropEvent),
1036 rTargetHelper,
1037 pTargetWindow,
1038 nPage,
1039 nLayer);
1040 break;
1041 }
1042 }
1043
1044 return nResult;
1045 }
1046
1047
1048
1049 } } } // end of namespace ::sd::slidesorter::controller
1050
1051