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 
31 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
32 #include <com/sun/star/datatransfer/XTransferable.hpp>
33 #include <com/sun/star/awt/MouseButton.hpp>
34 
35 #include "rtl/unload.h"
36 #include "rtl/ustring.hxx"
37 
38 #include "comphelper/makesequence.hxx"
39 
40 #include "DragSource.hxx"
41 #include "DragSourceContext.hxx"
42 #include "aqua_clipboard.hxx"
43 #include "DragActionConversion.hxx"
44 
45 #include "aqua/salframe.h"
46 
47 #include <memory>
48 
49 
50 using namespace rtl;
51 using namespace cppu;
52 using namespace osl;
53 using namespace com::sun::star;
54 using namespace com::sun::star::datatransfer;
55 using namespace com::sun::star::datatransfer::clipboard;
56 using namespace com::sun::star::datatransfer::dnd;
57 using namespace com::sun::star::datatransfer::dnd::DNDConstants;
58 using namespace com::sun::star::uno;
59 using namespace com::sun::star::awt::MouseButton;
60 using namespace com::sun::star::awt;
61 using namespace com::sun::star::lang;
62 using namespace comphelper;
63 using namespace std;
64 
65 
66 // For OOo internal D&D we provide the Transferable without NSDragPboard
67 // interference as a shortcut
68 uno::Reference<XTransferable> DragSource::g_XTransferable;
69 NSView* DragSource::g_DragSourceView = nil;
70 bool DragSource::g_DropSuccessSet = false;
71 bool DragSource::g_DropSuccess = false;
72 
73 
74 OUString dragSource_getImplementationName()
75 {
76   return OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.datatransfer.dnd.OleDragSource_V1"));
77 }
78 
79 Sequence<OUString> dragSource_getSupportedServiceNames()
80 {
81   return makeSequence(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDragSource")));
82 }
83 
84 
85 @implementation DragSourceHelper;
86 
87 -(DragSourceHelper*)initWithDragSource: (DragSource*) pds
88 {
89   self = [super init];
90 
91   if (self)
92 	{
93 	  mDragSource = pds;
94 	}
95 
96   return self;
97 }
98 
99 
100 -(void)mouseDown: (NSEvent*)theEvent
101 {
102   mDragSource->saveMouseEvent(theEvent);
103 }
104 
105 
106 -(void)mouseDragged: (NSEvent*)theEvent
107 {
108   mDragSource->saveMouseEvent(theEvent);
109 }
110 
111 
112 -(unsigned int)draggingSourceOperationMaskForLocal: (BOOL)isLocal
113 {
114   return mDragSource->getSupportedDragOperations(isLocal);
115 }
116 
117 
118 -(void)draggedImage:(NSImage*)anImage beganAt:(NSPoint)aPoint
119 {
120     (void)anImage;
121     (void)aPoint;
122     DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource),
123                              new DragSourceContext(mDragSource),
124                              mDragSource,
125                              DNDConstants::ACTION_COPY,
126                              DNDConstants::ACTION_COPY);
127 
128     mDragSource->mXDragSrcListener->dragEnter(dsde);
129 }
130 
131 
132 -(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
133 {
134     (void)anImage;
135     (void)aPoint;
136     // an internal drop can accept the drop but fail with dropComplete( false )
137     // this is different than the Cocoa API
138     bool bDropSuccess = operation != NSDragOperationNone;
139     if( DragSource::g_DropSuccessSet )
140         bDropSuccess = DragSource::g_DropSuccess;
141 
142     DragSourceDropEvent dsde(static_cast<OWeakObject*>(mDragSource),
143                              new DragSourceContext(mDragSource),
144                              static_cast< XDragSource* >(mDragSource),
145                              SystemToOfficeDragActions(operation),
146                              bDropSuccess );
147 
148     mDragSource->mXDragSrcListener->dragDropEnd(dsde);
149     mDragSource->mXDragSrcListener = uno::Reference<XDragSourceListener>();
150 }
151 
152 
153 -(void)draggedImage:(NSImage *)draggedImage movedTo:(NSPoint)screenPoint
154 {
155     (void)draggedImage;
156     (void)screenPoint;
157     DragSourceDragEvent dsde(static_cast<OWeakObject*>(mDragSource),
158                              new DragSourceContext(mDragSource),
159                              mDragSource,
160                              DNDConstants::ACTION_COPY,
161                              DNDConstants::ACTION_COPY);
162 
163     mDragSource->mXDragSrcListener->dragOver(dsde);
164 }
165 
166 @end
167 
168 
169 DragSource::DragSource():
170   WeakComponentImplHelper3<XDragSource, XInitialization, XServiceInfo>(m_aMutex),
171   mView(NULL),
172   mpFrame(NULL),
173   mLastMouseEventBeforeStartDrag(nil),
174   m_MouseButton(0)
175 {
176 }
177 
178 
179 DragSource::~DragSource()
180 {
181     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
182         [(id <MouseEventListener>)mView unregisterMouseEventListener: mDragSourceHelper];
183     [mDragSourceHelper release];
184 }
185 
186 
187 void SAL_CALL DragSource::initialize(const Sequence< Any >& aArguments)
188   throw(Exception)
189 {
190   if (aArguments.getLength() < 2)
191 	{
192 	  throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Not enough parameter.")),
193 					  static_cast<OWeakObject*>(this));
194 	}
195 
196   Any pNSView = aArguments[1];
197   sal_uInt64 tmp = 0;
198   pNSView >>= tmp;
199   mView = (NSView*)tmp;
200 
201   /* All SalFrameView the base class for all VCL system views inherits from
202 	 NSView in order to get mouse and other events. This is the only way to
203 	 get these events. In order to start a drag operation we need to provide
204 	 the mouse event which was the trigger. SalFrameView therefor implements
205 	 a hook mechanism so that we can get mouse events for our purpose.
206   */
207   if (![mView respondsToSelector: @selector(registerMouseEventListener:)] ||
208 	  ![mView respondsToSelector: @selector(unregisterMouseEventListener:)])
209 	{
210 	  throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Provided view doesn't support mouse listener")),
211 					  static_cast<OWeakObject*>(this));
212 	}
213   NSWindow* pWin = [mView window];
214   if( ! pWin || ![pWin respondsToSelector: @selector(getSalFrame)] )
215   {
216 	  throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Provided view is not attached to a vcl frame")),
217 					  static_cast<OWeakObject*>(this));
218   }
219   mpFrame = (AquaSalFrame*)[pWin performSelector: @selector(getSalFrame)];
220 
221   mDragSourceHelper = [[DragSourceHelper alloc] initWithDragSource: this];
222 
223   if (mDragSourceHelper == nil)
224 	{
225 	  throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Cannot initialize DragSource")),
226 					  static_cast<OWeakObject*>(this));
227 	}
228 
229   [(id <MouseEventListener>)mView registerMouseEventListener: mDragSourceHelper];
230 }
231 
232 
233 //----------------------------------------------------
234 // XDragSource
235 //----------------------------------------------------
236 
237 sal_Bool SAL_CALL DragSource::isDragImageSupported(  )
238   throw(RuntimeException)
239 {
240   return true;
241 }
242 
243 
244 sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ )
245   throw( IllegalArgumentException, RuntimeException)
246 {
247   return 0;
248 }
249 
250 
251 void SAL_CALL DragSource::startDrag(const DragGestureEvent& trigger,
252 									sal_Int8 sourceActions,
253 									sal_Int32 /*cursor*/,
254 									sal_Int32 /*image*/,
255 									const uno::Reference<XTransferable >& transferable,
256 									const uno::Reference<XDragSourceListener >& listener )
257   throw( RuntimeException)
258 {
259   MutexGuard guard(m_aMutex);
260 
261   OSL_ASSERT(listener.is() && "DragSource::startDrag: No XDragSourceListener provided\n");
262   OSL_ASSERT(transferable.is() && "DragSource::startDrag: No transferable provided\n");
263 
264   trigger.Event >>= mMouseEvent;
265   m_MouseButton= mMouseEvent.Buttons;
266   mXDragSrcListener = listener;
267   mXCurrentContext = static_cast<XDragSourceContext*>(new DragSourceContext(this));
268   auto_ptr<AquaClipboard> clipb(new AquaClipboard(NULL, false));
269   g_XTransferable = transferable;
270   clipb->setContents(g_XTransferable, uno::Reference<XClipboardOwner>());
271   mDragSourceActions = sourceActions;
272   g_DragSourceView = mView;
273 
274   NSSize sz;
275   sz.width = 5;
276   sz.height = 5;
277 
278   NSImage* dragImage;
279   dragImage = [[NSImage alloc] initWithSize: sz];
280 
281   NSRect bounds;
282   bounds.origin = NSMakePoint(0,0);
283   bounds.size = sz;
284 
285   [dragImage lockFocus];
286   [[NSColor blackColor] set];
287   [NSBezierPath fillRect: bounds];
288   [dragImage unlockFocus];
289 
290   NSPoint pInWnd = [mLastMouseEventBeforeStartDrag locationInWindow];
291   NSPoint p;
292   p = [mView convertPoint: pInWnd fromView: nil];
293   p.x = p.x - sz.width/2;
294   p.y = p.y - sz.height/2;
295 
296   // reset drop success flags
297   g_DropSuccessSet = false;
298   g_DropSuccess = false;
299 
300   [mView dragImage: dragImage
301    at: p
302    offset: NSMakeSize(0,0)
303    event: mLastMouseEventBeforeStartDrag
304    pasteboard: clipb->getPasteboard()
305    source: mDragSourceHelper
306    slideBack: 1];
307 
308   [dragImage release];
309 
310   g_XTransferable = uno::Reference<XTransferable>();
311   g_DragSourceView = nil;
312 
313   // reset drop success flags
314   g_DropSuccessSet = false;
315   g_DropSuccess = false;
316 }
317 
318 
319 // In order to initiate a D&D operation we need to
320 // provide the triggering mouse event which we get
321 // from the SalFrameView that is associated with
322 // this DragSource
323 void DragSource::saveMouseEvent(NSEvent* theEvent)
324 {
325   if (mLastMouseEventBeforeStartDrag != nil)
326 	{
327 	  [mLastMouseEventBeforeStartDrag release];
328 	}
329 
330   mLastMouseEventBeforeStartDrag = theEvent;
331 }
332 
333 
334 /* isLocal indicates whether or not the DnD operation is OOo
335    internal.
336  */
337 unsigned int DragSource::getSupportedDragOperations(bool isLocal) const
338 {
339   unsigned int srcActions = OfficeToSystemDragActions(mDragSourceActions);
340 
341   if (isLocal)
342 	{
343 	  // Support NSDragOperation generic which means we can
344 	  // decide which D&D operation to choose. We map
345 	  // NSDragOperationGenric to DNDConstants::ACTION_DEFAULT
346 	  // in SystemToOfficeDragActions to signal this and
347 	  // use it in DropTarget::determineDropAction
348 	  srcActions |= NSDragOperationGeneric;
349 	}
350   else
351 	{
352 	  // Mask out link and move operations on external DnD
353 	  srcActions &= ~(NSDragOperationMove | NSDragOperationLink);
354 	}
355 
356   return srcActions;
357 }
358 
359 
360 //################################
361 // XServiceInfo
362 //################################
363 
364 OUString SAL_CALL DragSource::getImplementationName(  ) throw (RuntimeException)
365 {
366   return dragSource_getImplementationName();
367 }
368 
369 
370 sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) throw (RuntimeException)
371 {
372   return ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDragSource")));
373 }
374 
375 
376 Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames() throw (RuntimeException)
377 {
378   return dragSource_getSupportedServiceNames();
379 }
380 
381 
382 
383 
384