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 
64 OUString dropTarget_getImplementationName()
65 {
66   return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1"));
67 }
68 
69 
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 
82   inline void CocoaToVCL(NSPoint& rPoint, const NSRect& bounds)
83   {
84 	rPoint.y = bounds.size.height - rPoint.y;
85   }
86 
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   return mDropTarget->performDragOperation(sender);
137 }
138 
139 
140 -(void)concludeDragOperation:(id <NSDraggingInfo>)sender
141 {
142   mDropTarget->concludeDragOperation(sender);
143 }
144 
145 
146 @end
147 
148 
149 DropTarget::DropTarget() :
150   WeakComponentImplHelper5<XInitialization, XDropTarget, XDropTargetDragContext, XDropTargetDropContext, XServiceInfo>(m_aMutex),
151   mView(nil),
152   mpFrame(NULL),
153   mDropTargetHelper(nil),
154   mbActive(false),
155   mDragSourceSupportedActions(DNDConstants::ACTION_NONE),
156   mSelectedDropAction(DNDConstants::ACTION_NONE),
157   mDefaultActions(DNDConstants::ACTION_COPY_OR_MOVE | DNDConstants::ACTION_LINK | DNDConstants::ACTION_DEFAULT)
158 {
159   mDataFlavorMapper = DataFlavorMapperPtr_t(new DataFlavorMapper());
160 }
161 
162 
163 DropTarget::~DropTarget()
164 {
165     if( AquaSalFrame::isAlive( mpFrame ) )
166         [(id <DraggingDestinationHandler>)mView unregisterDraggingDestinationHandler:mDropTargetHelper];
167     [mDropTargetHelper release];
168 }
169 
170 
171 sal_Int8 DropTarget::determineDropAction(sal_Int8 dropActions, id sender) const
172 {
173   sal_Int8 dropAct = dropActions;
174   bool srcAndDestEqual = false;
175 
176   if ([sender draggingSource] != nil)
177 	{
178 	  // Internal DnD
179 	  NSView* destView = [[sender draggingDestinationWindow] contentView];
180 	  srcAndDestEqual = (DragSource::g_DragSourceView == destView);
181 	}
182 
183   // If ACTION_DEFAULT is set this means NSDragOperationGeneric
184   // has been set and we map this to ACTION_MOVE or ACTION_COPY
185   // depending on whether or not source and dest are equal,
186   // this hopefully satisfies all parties
187   if( (dropActions == DNDConstants::ACTION_DEFAULT)
188   || ((dropActions == mDragSourceSupportedActions)
189      && !(~mDragSourceSupportedActions & DNDConstants::ACTION_COPY_OR_MOVE ) ) )
190 	{
191 	  dropAct = srcAndDestEqual ? DNDConstants::ACTION_MOVE :
192 		DNDConstants::ACTION_COPY;
193 	}
194      // if more than one drop actions have been specified
195      // set ACTION_DEFAULT in order to let the drop target
196      // decide which one to use
197   else if (dropActions != DNDConstants::ACTION_NONE &&
198 		   dropActions != DNDConstants::ACTION_MOVE &&
199 		   dropActions != DNDConstants::ACTION_COPY &&
200 		   dropActions != DNDConstants::ACTION_LINK)
201 	{
202 	  if (srcAndDestEqual)
203 		{
204           dropAct = dropActions;
205 		}
206 	  else // source and destination are different
207 		{
208 		  if (dropActions & DNDConstants::ACTION_COPY)
209 			dropAct = DNDConstants::ACTION_COPY;
210 		  else if (dropActions & DNDConstants::ACTION_MOVE)
211 			dropAct = DNDConstants::ACTION_MOVE;
212 		  else if (dropActions & DNDConstants::ACTION_LINK)
213 			dropAct = DNDConstants::ACTION_LINK;
214 		}
215 
216 	  dropAct |= DNDConstants::ACTION_DEFAULT;
217 	}
218 
219   return dropAct;
220 }
221 
222 
223 NSDragOperation DropTarget::draggingEntered(id sender)
224 {
225   // Initially when DnD will be started no modifier key can be pressed yet
226   // thus we are getting all actions that the drag source supports, we save
227   // this value because later the system masks the drag source actions if
228   // a modifier key will be pressed
229   mDragSourceSupportedActions = SystemToOfficeDragActions([sender draggingSourceOperationMask]);
230 
231   // Only if the drop target is really interessted in the drag actions
232   // supported by the source
233   if (mDragSourceSupportedActions & mDefaultActions)
234 	{
235 	  sal_Int8 currentAction = determineDropAction(mDragSourceSupportedActions, sender);
236 
237 	  NSRect bounds = [mView bounds];
238 	  NSPoint dragLocation = [sender draggedImageLocation];
239 
240 	  CocoaToVCL(dragLocation, bounds);
241 
242 	  sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
243 	  sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
244 
245 	  NSPasteboard* dragPboard = [sender draggingPasteboard];
246 	  mXCurrentDragClipboard = new AquaClipboard(dragPboard, false);
247 
248 	  uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable.is() ?
249 		DragSource::g_XTransferable : mXCurrentDragClipboard->getContents();
250 
251 	  DropTargetDragEnterEvent dtdee(static_cast<OWeakObject*>(this),
252 									 0,
253 									 this,
254 									 currentAction,
255 									 posX,
256 									 posY,
257 									 mDragSourceSupportedActions,
258 									 xTransferable->getTransferDataFlavors());
259 
260 	  fire_dragEnter(dtdee);
261 	}
262 
263   return OfficeToSystemDragActions(mSelectedDropAction);
264 }
265 
266 
267 NSDragOperation DropTarget::draggingUpdated(id sender)
268 {
269   sal_Int8 currentDragSourceActions =
270 	SystemToOfficeDragActions([sender draggingSourceOperationMask]);
271   NSDragOperation dragOp = NSDragOperationNone;
272 
273   if (currentDragSourceActions & mDefaultActions)
274 	{
275 	  sal_Int8 currentAction = determineDropAction(currentDragSourceActions, sender);
276 	  NSRect bounds = [mView bounds];
277 	  NSPoint dragLocation = [sender draggedImageLocation];
278 
279 	  CocoaToVCL(dragLocation, bounds);
280 
281 	  sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
282 	  sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
283 
284 	  DropTargetDragEvent dtde(static_cast<OWeakObject*>(this),
285 							   0,
286 							   this,
287 							   currentAction,
288 							   posX,
289 							   posY,
290 							   mDragSourceSupportedActions);
291 
292 	  fire_dragOver(dtde);
293 
294       // drag over callbacks likely have rendered something
295       [mView setNeedsDisplay: TRUE];
296 
297 	  dragOp = OfficeToSystemDragActions(mSelectedDropAction);
298 
299 	  //NSLog(@"Drag update: Source actions: %x proposed action %x selected action %x", mDragSourceSupportedActions, currentAction, mSelectedDropAction);
300 	}
301 
302   // Weird but it appears as if there is no method in Cocoa
303   // to create a kThemeCopyArrowCursor hence we have to use
304   // Carbon to do it
305   if (dragOp == NSDragOperationNone)
306 	SetThemeCursor(kThemeNotAllowedCursor);
307   else if (dragOp == NSDragOperationCopy)
308     SetThemeCursor(kThemeCopyArrowCursor);
309   else
310 	SetThemeCursor(kThemeArrowCursor);
311 
312   return dragOp;
313 }
314 
315 
316 void DropTarget::draggingExited(id /*sender*/)
317 {
318 	DropTargetEvent dte(static_cast<OWeakObject*>(this), 0);
319 	fire_dragExit(dte);
320 	mDragSourceSupportedActions = DNDConstants::ACTION_NONE;
321 	mSelectedDropAction = DNDConstants::ACTION_NONE;
322 	SetThemeCursor(kThemeArrowCursor);
323 }
324 
325 
326 BOOL DropTarget::prepareForDragOperation(id /*sender*/)
327 {
328 	return 1;
329 }
330 
331 
332 BOOL DropTarget::performDragOperation(id sender)
333 {
334   bool bSuccess = false;
335 
336   if (mSelectedDropAction != DNDConstants::ACTION_NONE)
337 	{
338 	    uno::Reference<XTransferable> xTransferable = DragSource::g_XTransferable;
339 
340 	  if (!DragSource::g_XTransferable.is())
341 		{
342 		  xTransferable = mXCurrentDragClipboard->getContents();
343 		}
344 
345 	  NSRect bounds = [mView bounds];
346 	  NSPoint dragLocation = [sender draggedImageLocation];
347 
348 	  CocoaToVCL(dragLocation, bounds);
349 
350 	  sal_Int32 posX = static_cast<sal_Int32>(dragLocation.x);
351 	  sal_Int32 posY = static_cast<sal_Int32>(dragLocation.y);
352 
353 	  DropTargetDropEvent dtde(static_cast<OWeakObject*>(this),
354 							   0,
355 							   this,
356 							   mSelectedDropAction,
357 							   posX,
358 							   posY,
359 							   mDragSourceSupportedActions,
360 							   xTransferable);
361 
362 	  fire_drop(dtde);
363 
364 	  bSuccess = true;
365 	}
366 
367   return bSuccess;
368 }
369 
370 
371 void DropTarget::concludeDragOperation(id /*sender*/)
372 {
373 	mDragSourceSupportedActions = DNDConstants::ACTION_NONE;
374 	mSelectedDropAction = DNDConstants::ACTION_NONE;
375 	mXCurrentDragClipboard = uno::Reference<XClipboard>();
376 	SetThemeCursor(kThemeArrowCursor);
377 }
378 
379 
380   // called from WeakComponentImplHelperX::dispose
381   // WeakComponentImplHelper calls disposing before it destroys
382   // itself.
383   void SAL_CALL DropTarget::disposing()
384   {
385   }
386 
387 
388   void SAL_CALL DropTarget::initialize(const Sequence< Any >& aArguments)
389 	throw(Exception)
390   {
391 	if (aArguments.getLength() < 2)
392 	  {
393 		throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("DropTarget::initialize: Cannot install window event handler")),
394 							   static_cast<OWeakObject*>(this));
395 	  }
396 
397 	Any pNSView = aArguments[0];
398 	sal_uInt64 tmp = 0;
399 	pNSView >>= tmp;
400 	mView = (id)tmp;
401 	mpFrame = [(SalFrameView*)mView getSalFrame];
402 
403 	mDropTargetHelper = [[DropTargetHelper alloc] initWithDropTarget: this];
404 
405 	[(id <DraggingDestinationHandler>)mView registerDraggingDestinationHandler:mDropTargetHelper];
406 	[mView registerForDraggedTypes: mDataFlavorMapper->getAllSupportedPboardTypes()];
407 
408 	id wnd = [mView window];
409 	NSWindow* parentWnd = [wnd parentWindow];
410 	unsigned int topWndStyle = (NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask);
411 	unsigned int wndStyles = [wnd styleMask] & topWndStyle;
412 
413 	if (parentWnd == nil && (wndStyles == topWndStyle))
414 	  {
415 		[wnd registerDraggingDestinationHandler:mDropTargetHelper];
416 		[wnd registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
417 	  }
418   }
419 
420 
421   void SAL_CALL DropTarget::addDropTargetListener(const uno::Reference<XDropTargetListener>& dtl)
422 	throw(RuntimeException)
423   {
424 	rBHelper.addListener(::getCppuType(&dtl), dtl);
425   }
426 
427 
428   void SAL_CALL DropTarget::removeDropTargetListener(const uno::Reference<XDropTargetListener>& dtl)
429 	throw(RuntimeException)
430   {
431 	rBHelper.removeListener(::getCppuType(&dtl), dtl);
432   }
433 
434 
435   sal_Bool SAL_CALL DropTarget::isActive(  ) throw(RuntimeException)
436   {
437 	return mbActive;
438   }
439 
440 
441   void SAL_CALL DropTarget::setActive(sal_Bool active) throw(RuntimeException)
442   {
443 	mbActive = active;
444   }
445 
446 
447   sal_Int8 SAL_CALL DropTarget::getDefaultActions() throw(RuntimeException)
448   {
449 	return mDefaultActions;
450   }
451 
452 
453   void SAL_CALL DropTarget::setDefaultActions(sal_Int8 actions) throw(RuntimeException)
454   {
455 	OSL_ENSURE( actions < 8, "No valid default actions");
456 	mDefaultActions= actions;
457   }
458 
459 
460   // XDropTargetDragContext
461 
462   void SAL_CALL DropTarget::acceptDrag(sal_Int8 dragOperation) throw (RuntimeException)
463   {
464 	mSelectedDropAction = dragOperation;
465   }
466 
467 
468   void SAL_CALL DropTarget::rejectDrag() throw (RuntimeException)
469   {
470 	mSelectedDropAction = DNDConstants::ACTION_NONE;
471   }
472 
473 
474   //XDropTargetDropContext
475 
476   void SAL_CALL DropTarget::acceptDrop(sal_Int8 dropOperation) throw( RuntimeException)
477   {
478 	mSelectedDropAction = dropOperation;
479   }
480 
481 
482   void SAL_CALL DropTarget::rejectDrop() throw (RuntimeException)
483   {
484 	mSelectedDropAction = DNDConstants::ACTION_NONE;
485   }
486 
487 
488   void SAL_CALL DropTarget::dropComplete(sal_Bool success) throw (RuntimeException)
489   {
490 	// Reset the internal transferable used as shortcut in case this is
491 	// an internal D&D operation
492 	DragSource::g_XTransferable = uno::Reference<XTransferable>();
493     DragSource::g_DropSuccessSet = true;
494     DragSource::g_DropSuccess = success;
495   }
496 
497 
498   void DropTarget::fire_drop( const DropTargetDropEvent& dte)
499   {
500       OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
501 	if( pContainer)
502 	  {
503 		OInterfaceIteratorHelper iter( *pContainer);
504 		while( iter.hasMoreElements())
505 		  {
506 		      uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
507 
508 			try { listener->drop( dte); }
509 			catch(RuntimeException&) {}
510 		  }
511 	  }
512   }
513 
514 
515   void DropTarget::fire_dragEnter(const DropTargetDragEnterEvent& e)
516   {
517       OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
518 	if( pContainer)
519 	  {
520 		OInterfaceIteratorHelper iter( *pContainer);
521 		while( iter.hasMoreElements())
522 		  {
523 		      uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
524 
525 			try { listener->dragEnter( e); }
526 			catch (RuntimeException&) {}
527 		  }
528 	  }
529   }
530 
531 
532   void DropTarget::fire_dragExit(const DropTargetEvent& dte)
533   {
534       OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
535 
536 	if( pContainer)
537 	  {
538 		OInterfaceIteratorHelper iter( *pContainer);
539 		while( iter.hasMoreElements())
540 		  {
541 		      uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
542 
543 			try { listener->dragExit( dte); }
544 			catch (RuntimeException&) {}
545 		  }
546 	  }
547   }
548 
549 
550   void DropTarget::fire_dragOver(const DropTargetDragEvent& dtde)
551   {
552       OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
553 	if( pContainer)
554 	  {
555 		OInterfaceIteratorHelper iter( *pContainer );
556 		while( iter.hasMoreElements())
557 		  {
558 		      uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
559 
560 			try { listener->dragOver( dtde); }
561 			catch (RuntimeException&) {}
562 		  }
563 	  }
564   }
565 
566 
567   void DropTarget::fire_dropActionChanged(const DropTargetDragEvent& dtde)
568   {
569       OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (uno::Reference<XDropTargetListener>* )0 ) );
570 	if( pContainer)
571 	  {
572 		OInterfaceIteratorHelper iter( *pContainer);
573 		while( iter.hasMoreElements())
574 		  {
575 		      uno::Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
576 
577 			try { listener->dropActionChanged( dtde); }
578 			catch (RuntimeException&) {}
579 		  }
580 	  }
581   }
582 
583 
584   // XServiceInfo
585 
586   OUString SAL_CALL DropTarget::getImplementationName() throw (RuntimeException)
587   {
588 	return dropTarget_getImplementationName();
589   }
590 
591 
592   sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName ) throw (RuntimeException)
593   {
594 	return ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDropTarget")));
595   }
596 
597 
598   Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames(  ) throw (RuntimeException)
599   {
600 	return dropTarget_getSupportedServiceNames();
601   }
602 
603