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