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