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