1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
21 #include <com/sun/star/datatransfer/XTransferable.hpp>
22 #include <com/sun/star/datatransfer/dnd/DropTargetDragEnterEvent.hpp>
23 #include <cppuhelper/interfacecontainer.hxx>
24 #include "clipboard.hxx"
25 #include "DropTarget.hxx"
26 #include "DragActionConversion.hxx"
27 #include "DragSource.hxx"
28 #include <rtl/ustring.h>
29 #include <premac.h>
30 #include <Carbon/Carbon.h>
31 #include <postmac.h>
32 #include <osx/salframe.h>
33 #include <osx/salframeview.h>
34 #include <cppuhelper/supportsservice.hxx>
35 
36 using namespace cppu;
37 using namespace osl;
38 using namespace com::sun::star::datatransfer;
39 using namespace com::sun::star::datatransfer::dnd;
40 using namespace com::sun::star::datatransfer::dnd::DNDConstants;
41 using namespace com::sun::star::datatransfer::clipboard;
42 using namespace com::sun::star::lang;
43 using namespace com::sun::star::uno;
44 using namespace com::sun::star;
45 using namespace comphelper;
46 
dropTarget_getImplementationName()47 static OUString dropTarget_getImplementationName()
48 {
49     return "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1";
50 }
51 
dropTarget_getSupportedServiceNames()52 static Sequence<OUString> dropTarget_getSupportedServiceNames()
53 {
54     return { OUString("com.sun.star.datatransfer.dnd.OleDropTarget") };
55 }
56 
57 namespace /* private */
58 {
59     // Cocoa's coordinate system has its origin lower-left, VCL's
60     // coordinate system upper-left hence we need to transform
61     // coordinates
62 
CocoaToVCL(NSPoint & rPoint,const NSRect & bounds)63     void CocoaToVCL(NSPoint& rPoint, const NSRect& bounds)
64     {
65         rPoint.y = bounds.size.height - rPoint.y;
66     }
67 }
68 
69 @implementation DropTargetHelper
70 
71 -(DropTargetHelper*)initWithDropTarget:(DropTarget*)pdt
72 {
73     self = [super init];
74 
75     if (self)
76     {
77         mDropTarget = pdt;
78     }
79 
80     return self;
81 }
82 
83 -(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
84 {
85     return mDropTarget->draggingEntered(sender);
86 }
87 
88 -(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
89 {
90     return mDropTarget->draggingUpdated(sender);
91 }
92 
93 -(void)draggingExited:(id <NSDraggingInfo>)sender
94 {
95     mDropTarget->draggingExited(sender);
96 }
97 
98 -(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
99 {
100     (void) sender;
101     return DropTarget::prepareForDragOperation();
102 }
103 
104 -(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
105 {
106     (void) sender;
107     return mDropTarget->performDragOperation();
108 }
109 
110 -(void)concludeDragOperation:(id <NSDraggingInfo>)sender
111 {
112     mDropTarget->concludeDragOperation(sender);
113 }
114 
115 @end
116 
DropTarget()117 DropTarget::DropTarget() :
118     WeakComponentImplHelper<XInitialization, XDropTarget, XDropTargetDragContext, XDropTargetDropContext, XServiceInfo>(m_aMutex),
119     mView(nil),
120     mpFrame(nullptr),
121     mDropTargetHelper(nil),
122     mbActive(false),
123     mDragSourceSupportedActions(DNDConstants::ACTION_NONE),
124     mSelectedDropAction(DNDConstants::ACTION_NONE),
125     mDefaultActions(DNDConstants::ACTION_COPY_OR_MOVE | DNDConstants::ACTION_LINK | DNDConstants::ACTION_DEFAULT)
126 {
127     mDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper());
128 }
129 
~DropTarget()130 DropTarget::~DropTarget()
131 {
132     if( AquaSalFrame::isAlive( mpFrame ) )
133         [static_cast<id <DraggingDestinationHandler>>(mView) unregisterDraggingDestinationHandler:mDropTargetHelper];
134     [mDropTargetHelper release];
135 }
136 
determineDropAction(sal_Int8 dropActions,id sender) const137 sal_Int8 DropTarget::determineDropAction(sal_Int8 dropActions, id sender) const
138 {
139     sal_Int8 dropAct = dropActions;
140     bool srcAndDestEqual = false;
141 
142     if ([sender draggingSource] != nil)
143     {
144         // Internal DnD
145         NSView* destView = [[sender draggingDestinationWindow] contentView];
146         srcAndDestEqual = (DragSource::g_DragSourceView == destView);
147     }
148 
149     // If ACTION_DEFAULT is set this means NSDragOperationGeneric
150     // has been set and we map this to ACTION_MOVE or ACTION_COPY
151     // depending on whether or not source and dest are equal,
152     // this hopefully satisfies all parties
153     if( (dropActions == DNDConstants::ACTION_DEFAULT)
154         || ((dropActions == mDragSourceSupportedActions)
155         && !(~mDragSourceSupportedActions & DNDConstants::ACTION_COPY_OR_MOVE ) ) )
156     {
157         dropAct = srcAndDestEqual ? DNDConstants::ACTION_MOVE :
158         DNDConstants::ACTION_COPY;
159     }
160      // if more than one drop actions have been specified
161      // set ACTION_DEFAULT in order to let the drop target
162      // decide which one to use
163     else if (dropActions != DNDConstants::ACTION_NONE &&
164             dropActions != DNDConstants::ACTION_MOVE &&
165             dropActions != DNDConstants::ACTION_COPY &&
166             dropActions != DNDConstants::ACTION_LINK)
167     {
168         if (srcAndDestEqual)
169         {
170             dropAct = dropActions;
171         }
172         else // source and destination are different
173         {
174             if (dropActions & DNDConstants::ACTION_COPY)
175                 dropAct = DNDConstants::ACTION_COPY;
176             else if (dropActions & DNDConstants::ACTION_MOVE)
177                 dropAct = DNDConstants::ACTION_MOVE;
178             else if (dropActions & DNDConstants::ACTION_LINK)
179                 dropAct = DNDConstants::ACTION_LINK;
180         }
181 
182         dropAct |= DNDConstants::ACTION_DEFAULT;
183     }
184 
185     return dropAct;
186 }
187 
draggingEntered(id sender)188 NSDragOperation DropTarget::draggingEntered(id sender)
189 {
190     // Initially when DnD will be started no modifier key can be pressed yet
191     // thus we are getting all actions that the drag source supports, we save
192     // this value because later the system masks the drag source actions if
193     // a modifier key will be pressed
194     mDragSourceSupportedActions = SystemToOfficeDragActions([sender draggingSourceOperationMask]);
195 
196     // Only if the drop target is really interested in the drag actions
197     // supported by the source
198     if (mDragSourceSupportedActions & mDefaultActions)
199     {
200         sal_Int8 currentAction = determineDropAction(mDragSourceSupportedActions, sender);
201 
202         NSRect bounds = [mView bounds];
203         NSPoint mouseLoc = [NSEvent mouseLocation];
204 
205         id wnd = [mView window];
206         NSPoint dragLocation = [mView convertPoint:[wnd convertRectFromScreen:NSMakeRect(mouseLoc.x, mouseLoc.y, 1, 1)].origin fromView:nil];
207 
208         CocoaToVCL(dragLocation, bounds);
209 
210         sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
211         sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
212 
213         NSPasteboard* dragPboard = [sender draggingPasteboard];
214         mXCurrentDragClipboard = new AquaClipboard(dragPboard, false);
215 
216         uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable.is() ?
217                                                       DragSource::g_XTransferable : mXCurrentDragClipboard->getContents();
218 
219         DropTargetDragEnterEvent dtdee(static_cast<OWeakObject*>(this),
220                                         0,
221                                         this,
222                                         currentAction,
223                                         posX,
224                                         posY,
225                                         mDragSourceSupportedActions,
226                                         xTransferable->getTransferDataFlavors());
227 
228         fire_dragEnter(dtdee);
229     }
230 
231     return OfficeToSystemDragActions(mSelectedDropAction);
232 }
233 
draggingUpdated(id sender)234 NSDragOperation DropTarget::draggingUpdated(id sender)
235 {
236     sal_Int8 currentDragSourceActions =
237     SystemToOfficeDragActions([sender draggingSourceOperationMask]);
238     NSDragOperation dragOp = NSDragOperationNone;
239 
240     if (currentDragSourceActions & mDefaultActions)
241     {
242         sal_Int8 currentAction = determineDropAction(currentDragSourceActions, sender);
243         NSRect bounds = [mView bounds];
244         NSPoint mouseLoc = [NSEvent mouseLocation];
245 
246         id wnd = [mView window];
247         NSPoint dragLocation = [mView convertPoint:[wnd convertRectFromScreen:NSMakeRect(mouseLoc.x, mouseLoc.y, 1, 1)].origin fromView:nil];
248 
249         CocoaToVCL(dragLocation, bounds);
250 
251         sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
252         sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
253 
254         DropTargetDragEvent dtde(static_cast<OWeakObject*>(this),
255                                0,
256                                this,
257                                currentAction,
258                                posX,
259                                posY,
260                                mDragSourceSupportedActions);
261 
262         fire_dragOver(dtde);
263 
264         // drag over callbacks likely have rendered something
265         [mView setNeedsDisplay: TRUE];
266 
267         dragOp = OfficeToSystemDragActions(mSelectedDropAction);
268 
269         //NSLog(@"Drag update: Source actions: %x proposed action %x selected action %x", mDragSourceSupportedActions, currentAction, mSelectedDropAction);
270     }
271 
272     if (dragOp == NSDragOperationNone)
273         [[NSCursor operationNotAllowedCursor] set];
274     else if (dragOp == NSDragOperationCopy)
275         [[NSCursor dragCopyCursor] set];
276     else
277         [[NSCursor arrowCursor] set];
278 
279     return dragOp;
280 }
281 
draggingExited(id)282 void DropTarget::draggingExited(id /*sender*/)
283 {
284     DropTargetEvent dte(static_cast<OWeakObject*>(this), 0);
285     fire_dragExit(dte);
286     mDragSourceSupportedActions = DNDConstants::ACTION_NONE;
287     mSelectedDropAction = DNDConstants::ACTION_NONE;
288     [[NSCursor arrowCursor] set];
289 }
290 
prepareForDragOperation()291 BOOL DropTarget::prepareForDragOperation()
292 {
293     return 1;
294 }
295 
performDragOperation()296 BOOL DropTarget::performDragOperation()
297 {
298     bool bSuccess = false;
299 
300     if (mSelectedDropAction != DNDConstants::ACTION_NONE)
301     {
302         uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable;
303 
304         if (!DragSource::g_XTransferable.is())
305         {
306             xTransferable = mXCurrentDragClipboard->getContents();
307         }
308 
309         NSRect bounds = [mView bounds];
310         NSPoint mouseLoc = [NSEvent mouseLocation];
311 
312         id wnd = [mView window];
313         NSPoint dragLocation = [mView convertPoint:[wnd convertRectFromScreen:NSMakeRect(mouseLoc.x, mouseLoc.y, 1, 1)].origin fromView:nil];
314 
315         CocoaToVCL(dragLocation, bounds);
316 
317         sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
318         sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
319 
320         DropTargetDropEvent dtde(static_cast<OWeakObject*>(this),
321                                0,
322                                this,
323                                mSelectedDropAction,
324                                posX,
325                                posY,
326                                mDragSourceSupportedActions,
327                                xTransferable);
328 
329         fire_drop(dtde);
330 
331         bSuccess = true;
332     }
333 
334     return bSuccess;
335 }
336 
concludeDragOperation(id)337 void DropTarget::concludeDragOperation(id /*sender*/)
338 {
339     mDragSourceSupportedActions = DNDConstants::ACTION_NONE;
340     mSelectedDropAction = DNDConstants::ACTION_NONE;
341     mXCurrentDragClipboard.clear();
342     [[NSCursor arrowCursor] set];
343 }
344 
345 // called from WeakComponentImplHelperX::dispose
346 // WeakComponentImplHelper calls disposing before it destroys
347 // itself.
disposing()348 void SAL_CALL DropTarget::disposing()
349 {
350 }
351 
initialize(const Sequence<Any> & aArguments)352 void SAL_CALL DropTarget::initialize(const Sequence< Any >& aArguments)
353 {
354     if (aArguments.getLength() < 2)
355     {
356         throw RuntimeException("DropTarget::initialize: Cannot install window event handler",
357                                static_cast<OWeakObject*>(this));
358     }
359 
360     Any pNSView = aArguments[0];
361     sal_uInt64 tmp = 0;
362     pNSView >>= tmp;
363     mView = reinterpret_cast<id>(tmp);
364     mpFrame = [static_cast<SalFrameView*>(mView) getSalFrame];
365 
366     mDropTargetHelper = [[DropTargetHelper alloc] initWithDropTarget: this];
367 
368     [static_cast<id <DraggingDestinationHandler>>(mView) registerDraggingDestinationHandler:mDropTargetHelper];
369     [mView registerForDraggedTypes: DataFlavorMapper::getAllSupportedPboardTypes()];
370 
371     id wnd = [mView window];
372     NSWindow* parentWnd = [wnd parentWindow];
373 SAL_WNODEPRECATED_DECLARATIONS_PUSH
374         // 'NSClosableWindowMask' is deprecated: first deprecated in macOS 10.12
375         // 'NSResizableWindowMask' is deprecated: first deprecated in macOS 10.12
376         // 'NSTitleWindowMask' is deprecated: first deprecated in macOS 10.12
377     unsigned int topWndStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask);
378 SAL_WNODEPRECATED_DECLARATIONS_POP
379     unsigned int wndStyles = [wnd styleMask] & topWndStyle;
380 
381     if (parentWnd == nil && (wndStyles == topWndStyle))
382     {
383         [wnd registerDraggingDestinationHandler:mDropTargetHelper];
384 SAL_WNODEPRECATED_DECLARATIONS_PUSH
385             // "'NSFilenamesPboardType' is deprecated: first deprecated in macOS 10.14 - Create
386             // multiple pasteboard items with NSPasteboardTypeFileURL or kUTTypeFileURL instead"
387         [wnd registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
388 SAL_WNODEPRECATED_DECLARATIONS_POP
389     }
390 }
391 
addDropTargetListener(const uno::Reference<XDropTargetListener> & dtl)392 void SAL_CALL DropTarget::addDropTargetListener(const uno::Reference<XDropTargetListener>& dtl)
393 {
394     rBHelper.addListener(cppu::UnoType<decltype(dtl)>::get(), dtl);
395 }
396 
removeDropTargetListener(const uno::Reference<XDropTargetListener> & dtl)397 void SAL_CALL DropTarget::removeDropTargetListener(const uno::Reference<XDropTargetListener>& dtl)
398 {
399     rBHelper.removeListener(cppu::UnoType<decltype(dtl)>::get(), dtl);
400 }
401 
isActive()402 sal_Bool SAL_CALL DropTarget::isActive(  )
403 {
404     return mbActive;
405 }
406 
setActive(sal_Bool active)407 void SAL_CALL DropTarget::setActive(sal_Bool active)
408 {
409     mbActive = active;
410 }
411 
getDefaultActions()412 sal_Int8 SAL_CALL DropTarget::getDefaultActions()
413 {
414     return mDefaultActions;
415 }
416 
setDefaultActions(sal_Int8 actions)417 void SAL_CALL DropTarget::setDefaultActions(sal_Int8 actions)
418 {
419     OSL_ENSURE( actions < 8, "No valid default actions");
420     mDefaultActions= actions;
421 }
422 
acceptDrag(sal_Int8 dragOperation)423 void SAL_CALL DropTarget::acceptDrag(sal_Int8 dragOperation)
424 {
425     mSelectedDropAction = dragOperation;
426 }
427 
rejectDrag()428 void SAL_CALL DropTarget::rejectDrag()
429 {
430     mSelectedDropAction = DNDConstants::ACTION_NONE;
431 }
432 
acceptDrop(sal_Int8 dropOperation)433 void SAL_CALL DropTarget::acceptDrop(sal_Int8 dropOperation)
434 {
435     mSelectedDropAction = dropOperation;
436 }
437 
rejectDrop()438 void SAL_CALL DropTarget::rejectDrop()
439 {
440     mSelectedDropAction = DNDConstants::ACTION_NONE;
441 }
442 
dropComplete(sal_Bool success)443 void SAL_CALL DropTarget::dropComplete(sal_Bool success)
444 {
445     // Reset the internal transferable used as shortcut in case this is
446     // an internal D&D operation
447     DragSource::g_XTransferable.clear();
448     DragSource::g_DropSuccessSet = true;
449     DragSource::g_DropSuccess = success;
450 }
451 
fire_drop(const DropTargetDropEvent & dte)452 void DropTarget::fire_drop( const DropTargetDropEvent& dte)
453 {
454     OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
455     if( pContainer)
456     {
457         OInterfaceIteratorHelper iter( *pContainer);
458         while( iter.hasMoreElements())
459         {
460             uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
461 
462             try { listener->drop( dte); }
463             catch(RuntimeException&) {}
464         }
465     }
466 }
467 
fire_dragEnter(const DropTargetDragEnterEvent & e)468 void DropTarget::fire_dragEnter(const DropTargetDragEnterEvent& e)
469 {
470     OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
471     if( pContainer)
472     {
473         OInterfaceIteratorHelper iter( *pContainer);
474         while( iter.hasMoreElements())
475         {
476             uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
477 
478             try { listener->dragEnter( e); }
479             catch (RuntimeException&) {}
480         }
481     }
482 }
483 
fire_dragExit(const DropTargetEvent & dte)484 void DropTarget::fire_dragExit(const DropTargetEvent& dte)
485 {
486     OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
487 
488     if( pContainer)
489     {
490         OInterfaceIteratorHelper iter( *pContainer);
491         while( iter.hasMoreElements())
492         {
493             uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
494 
495             try { listener->dragExit( dte); }
496             catch (RuntimeException&) {}
497         }
498     }
499 }
500 
fire_dragOver(const DropTargetDragEvent & dtde)501 void DropTarget::fire_dragOver(const DropTargetDragEvent& dtde)
502 {
503     OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
504     if( pContainer)
505     {
506         OInterfaceIteratorHelper iter( *pContainer );
507         while( iter.hasMoreElements())
508         {
509             uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
510 
511             try { listener->dragOver( dtde); }
512             catch (RuntimeException&) {}
513         }
514     }
515 }
516 
fire_dropActionChanged(const DropTargetDragEvent & dtde)517 void DropTarget::fire_dropActionChanged(const DropTargetDragEvent& dtde)
518 {
519     OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
520     if( pContainer)
521     {
522         OInterfaceIteratorHelper iter( *pContainer);
523         while( iter.hasMoreElements())
524         {
525             uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
526 
527             try { listener->dropActionChanged( dtde); }
528             catch (RuntimeException&) {}
529         }
530     }
531 }
532 
getImplementationName()533 OUString SAL_CALL DropTarget::getImplementationName()
534 {
535     return dropTarget_getImplementationName();
536 }
537 
supportsService(const OUString & ServiceName)538 sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName )
539 {
540     return cppu::supportsService(this, ServiceName);
541 }
542 
getSupportedServiceNames()543 Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames(  )
544 {
545     return dropTarget_getSupportedServiceNames();
546 }
547 
548 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
549