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 {
185 if (aArguments.getLength() < 2)
186 {
187 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Not enough parameter.")),
188 static_cast<OWeakObject*>(this));
189 }
190
191 Any pNSView = aArguments[1];
192 sal_uInt64 tmp = 0;
193 pNSView >>= tmp;
194 mView = (NSView*)tmp;
195
196 /* All SalFrameView the base class for all VCL system views inherits from
197 NSView in order to get mouse and other events. This is the only way to
198 get these events. In order to start a drag operation we need to provide
199 the mouse event which was the trigger. SalFrameView therefor implements
200 a hook mechanism so that we can get mouse events for our purpose.
201 */
202 if (![mView respondsToSelector: @selector(registerMouseEventListener:)] ||
203 ![mView respondsToSelector: @selector(unregisterMouseEventListener:)])
204 {
205 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Provided view doesn't support mouse listener")),
206 static_cast<OWeakObject*>(this));
207 }
208 NSWindow* pWin = [mView window];
209 if( ! pWin || ![pWin respondsToSelector: @selector(getSalFrame)] )
210 {
211 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Provided view is not attached to a vcl frame")),
212 static_cast<OWeakObject*>(this));
213 }
214 mpFrame = (AquaSalFrame*)[pWin performSelector: @selector(getSalFrame)];
215
216 mDragSourceHelper = [[DragSourceHelper alloc] initWithDragSource: this];
217
218 if (mDragSourceHelper == nil)
219 {
220 throw Exception(OUString(RTL_CONSTASCII_USTRINGPARAM("DragSource::initialize: Cannot initialize DragSource")),
221 static_cast<OWeakObject*>(this));
222 }
223
224 [(id <MouseEventListener>)mView registerMouseEventListener: mDragSourceHelper];
225 }
226
227
228 //----------------------------------------------------
229 // XDragSource
230 //----------------------------------------------------
231
isDragImageSupported()232 sal_Bool SAL_CALL DragSource::isDragImageSupported( )
233 {
234 return true;
235 }
236
237
getDefaultCursor(sal_Int8)238 sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ )
239 {
240 return 0;
241 }
242
243
startDrag(const DragGestureEvent & trigger,sal_Int8 sourceActions,sal_Int32,sal_Int32,const uno::Reference<XTransferable> & transferable,const uno::Reference<XDragSourceListener> & listener)244 void SAL_CALL DragSource::startDrag(const DragGestureEvent& trigger,
245 sal_Int8 sourceActions,
246 sal_Int32 /*cursor*/,
247 sal_Int32 /*image*/,
248 const uno::Reference<XTransferable >& transferable,
249 const uno::Reference<XDragSourceListener >& listener )
250 {
251 MutexGuard guard(m_aMutex);
252
253 OSL_ASSERT(listener.is() && "DragSource::startDrag: No XDragSourceListener provided\n");
254 OSL_ASSERT(transferable.is() && "DragSource::startDrag: No transferable provided\n");
255
256 trigger.Event >>= mMouseEvent;
257 m_MouseButton= mMouseEvent.Buttons;
258 mXDragSrcListener = listener;
259 mXCurrentContext = static_cast<XDragSourceContext*>(new DragSourceContext(this));
260 auto_ptr<AquaClipboard> clipb(new AquaClipboard(NULL, false));
261 g_XTransferable = transferable;
262 clipb->setContents(g_XTransferable, uno::Reference<XClipboardOwner>());
263 mDragSourceActions = sourceActions;
264 g_DragSourceView = mView;
265
266 NSSize sz;
267 sz.width = 5;
268 sz.height = 5;
269
270 NSImage* dragImage;
271 dragImage = [[NSImage alloc] initWithSize: sz];
272
273 NSRect bounds;
274 bounds.origin = NSMakePoint(0,0);
275 bounds.size = sz;
276
277 [dragImage lockFocus];
278 [[NSColor blackColor] set];
279 [NSBezierPath fillRect: bounds];
280 [dragImage unlockFocus];
281
282 NSPoint pInWnd = [mLastMouseEventBeforeStartDrag locationInWindow];
283 NSPoint p;
284 p = [mView convertPoint: pInWnd fromView: nil];
285 p.x = p.x - sz.width/2;
286 p.y = p.y - sz.height/2;
287
288 // reset drop success flags
289 g_DropSuccessSet = false;
290 g_DropSuccess = false;
291
292 [mView dragImage: dragImage
293 at: p
294 offset: NSMakeSize(0,0)
295 event: mLastMouseEventBeforeStartDrag
296 pasteboard: clipb->getPasteboard()
297 source: mDragSourceHelper
298 slideBack: 1];
299
300 [dragImage release];
301
302 g_XTransferable = uno::Reference<XTransferable>();
303 g_DragSourceView = nil;
304
305 // reset drop success flags
306 g_DropSuccessSet = false;
307 g_DropSuccess = false;
308 }
309
310
311 // In order to initiate a D&D operation we need to
312 // provide the triggering mouse event which we get
313 // from the SalFrameView that is associated with
314 // this DragSource
saveMouseEvent(NSEvent * theEvent)315 void DragSource::saveMouseEvent(NSEvent* theEvent)
316 {
317 if (mLastMouseEventBeforeStartDrag != nil)
318 {
319 [mLastMouseEventBeforeStartDrag release];
320 }
321
322 mLastMouseEventBeforeStartDrag = theEvent;
323 }
324
325
326 /* isLocal indicates whether or not the DnD operation is OOo
327 internal.
328 */
getSupportedDragOperations(bool isLocal) const329 unsigned int DragSource::getSupportedDragOperations(bool isLocal) const
330 {
331 unsigned int srcActions = OfficeToSystemDragActions(mDragSourceActions);
332
333 if (isLocal)
334 {
335 // Support NSDragOperation generic which means we can
336 // decide which D&D operation to choose. We map
337 // NSDragOperationGenric to DNDConstants::ACTION_DEFAULT
338 // in SystemToOfficeDragActions to signal this and
339 // use it in DropTarget::determineDropAction
340 srcActions |= NSDragOperationGeneric;
341 }
342 else
343 {
344 // Mask out link and move operations on external DnD
345 srcActions &= ~(NSDragOperationMove | NSDragOperationLink);
346 }
347
348 return srcActions;
349 }
350
351
352 //################################
353 // XServiceInfo
354 //################################
355
getImplementationName()356 OUString SAL_CALL DragSource::getImplementationName( )
357 {
358 return dragSource_getImplementationName();
359 }
360
361
supportsService(const OUString & ServiceName)362 sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName )
363 {
364 return ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.OleDragSource")));
365 }
366
367
getSupportedServiceNames()368 Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames()
369 {
370 return dragSource_getSupportedServiceNames();
371 }
372