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