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