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