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