API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPDragServer.j
Go to the documentation of this file.
1 /*
2  * CPDragServer.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
33 
34 #define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
35 
38 
40 
43 
44 /*
45  CPDraggingInfo is a container of information about a specific dragging session.
46  @ignore
47 */
48 @implementation CPDraggingInfo : CPObject
49 {
50  id __doxygen__;
51 }
52 
53 - (CPPasteboard)draggingPasteboard
54 {
55  if ([CPPlatform supportsDragAndDrop])
56  return [_CPDOMDataTransferPasteboard DOMDataTransferPasteboard];
57 
59 }
60 
61 - (id)draggingSource
62 {
64 }
65 
66 /*
67 - (unsigned)draggingSourceOperationMask
68 */
69 
70 - (CPPoint)draggingLocation
71 {
73 }
74 
75 - (CPWindow)draggingDestinationWindow
76 {
77  return DRAGGING_WINDOW([[CPDragServer sharedDragServer] draggingDestination]);
78 }
79 
80 - (CPImage)draggedImage
81 {
82  return [[self draggedView] image];
83 }
84 
85 - (CGPoint)draggedImageLocation
86 {
87  return [self draggedViewLocation];
88 }
89 
90 - (CPView)draggedView
91 {
93 }
94 
95 - (CGPoint)draggedViewLocation
96 {
97  var dragServer = [CPDragServer sharedDragServer];
98 
99  return [DRAGGING_WINDOW([dragServer draggingDestination]) convertPlatformWindowToBase:[[dragServer draggedView] frame].origin];
100 }
101 
102 @end
103 
108 
109 @implementation CPDragServer : CPObject
110 {
111  BOOL _isDragging;
112 
113  CPWindow _draggedWindow;
114  CPView _draggedView;
115  CPImageView _imageView;
116 
117  BOOL _isDraggingImage;
118 
119  CGSize _draggingOffset;
120 
121  CPPasteboard _draggingPasteboard;
122 
123  id _draggingSource;
124  unsigned _implementedDraggingSourceMethods;
125 
126  CGPoint _draggingLocation;
127  id _draggingDestination;
128  BOOL _draggingDestinationWantsPeriodicUpdates;
129 
130  CGPoint _startDragLocation;
131  BOOL _shouldSlideBack;
132  unsigned _dragOperation;
133 
134  CPTimer _draggingUpdateTimer;
135 }
136 
137 /*
138  Private Objective-J/Cappuccino method
139  @ignore
140 */
141 + (void)initialize
142 {
143  if (self !== [CPDragServer class])
144  return;
145 
147 }
148 
149 + (CPDragServer)sharedDragServer
150 {
151  if (!CPSharedDragServer)
153 
154  return CPSharedDragServer;
155 }
156 
157 /*
158  @ignore
159 */
160 - (id)init
161 {
162  self = [super init];
163 
164  if (self)
165  {
166  _draggedWindow = [[CPWindow alloc] initWithContentRect:_CGRectMakeZero() styleMask:CPBorderlessWindowMask];
167 
168  [_draggedWindow setLevel:CPDraggingWindowLevel];
169  }
170 
171  return self;
172 }
173 
174 - (id)draggingDestination
175 {
176  return _draggingDestination;
177 }
178 
179 - (CGPoint)draggingLocation
180 {
181  return _draggingLocation
182 }
183 
184 - (void)draggingStartedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation
185 {
186  if (_isDraggingImage)
187  {
188  if ([_draggingSource respondsToSelector:@selector(draggedImage:beganAt:)])
189  [_draggingSource draggedImage:[_draggedView image] beganAt:aLocation];
190  }
191  else
192  {
193  if ([_draggingSource respondsToSelector:@selector(draggedView:beganAt:)])
194  [_draggingSource draggedView:_draggedView beganAt:aLocation];
195  }
196 
197  if (![CPPlatform supportsDragAndDrop])
198  [_draggedWindow orderFront:self];
199 }
200 
201 - (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation
202 {
203  if (![CPPlatform supportsDragAndDrop])
204  [_draggedWindow setFrameOrigin:_CGPointMake(aGlobalLocation.x - _draggingOffset.width, aGlobalLocation.y - _draggingOffset.height)];
205 
206  if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_)
207  [_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation];
208 
209  else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_movedTo_)
210  [_draggingSource draggedView:_draggedView movedTo:aGlobalLocation];
211 }
212 
213 - (CPDragOperation)draggingUpdatedInPlatformWindow:(CPPlatformWindow)aPlatformWindow location:(CGPoint)aLocation
214 {
215  [_draggingUpdateTimer invalidate];
216  _draggingUpdateTimer = nil;
217 
218  var dragOperation = CPDragOperationCopy,
219  // We have to convert base to bridge since the drag event comes from the source window, not the drag window.
220  draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]];
221 
222  if (draggingDestination)
223  _draggingLocation = [DRAGGING_WINDOW(draggingDestination) convertPlatformWindowToBase:aLocation];
224 
225  if (draggingDestination !== _draggingDestination)
226  {
227  if ([_draggingDestination respondsToSelector:@selector(draggingExited:)])
228  [_draggingDestination draggingExited:CPDragServerDraggingInfo];
229 
230  _draggingDestination = draggingDestination;
231 
232  if ([_draggingDestination respondsToSelector:@selector(wantsPeriodicDraggingUpdates)])
233  _draggingDestinationWantsPeriodicUpdates = [_draggingDestination wantsPeriodicDraggingUpdates];
234  else
235  _draggingDestinationWantsPeriodicUpdates = YES;
236 
237  if ([_draggingDestination respondsToSelector:@selector(draggingEntered:)])
238  dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo];
239  }
240  else if ([_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
241  dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo];
242 
243  if (!_draggingDestination)
244  dragOperation = CPDragOperationNone;
245  else
246  {
247  if (_draggingDestinationWantsPeriodicUpdates)
248  _draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval
249  target:self
251  userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}]
252  repeats:NO];
253 
254  var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil;
255  if (scrollView)
256  {
257  var contentView = [scrollView contentView],
258  bounds = [contentView bounds],
259  insetBounds = CGRectInset(bounds, 30, 30),
260  eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
261  deltaX = 0,
262  deltaY = 0;
263 
264  if (!CGRectContainsPoint(insetBounds, eventLocation))
265  {
266  if ([scrollView hasVerticalScroller])
267  {
268  if (eventLocation.y < _CGRectGetMinY(insetBounds))
269  deltaY = _CGRectGetMinY(insetBounds) - eventLocation.y;
270  else if (eventLocation.y > _CGRectGetMaxY(insetBounds))
271  deltaY = _CGRectGetMaxY(insetBounds) - eventLocation.y;
272  if (deltaY < -insetBounds.size.height)
273  deltaY = -insetBounds.size.height;
274  if (deltaY > insetBounds.size.height)
275  deltaY = insetBounds.size.height;
276  }
277 
278  if ([scrollView hasHorizontalScroller])
279  {
280  if (eventLocation.x < _CGRectGetMinX(insetBounds))
281  deltaX = _CGRectGetMinX(insetBounds) - eventLocation.x;
282  else if (eventLocation.x > _CGRectGetMaxX(insetBounds))
283  deltaX = _CGRectGetMaxX(insetBounds) - eventLocation.x;
284  if (deltaX < -insetBounds.size.width)
285  deltaX = -insetBounds.size.width;
286  if (deltaX > insetBounds.size.width)
287  deltaX = insetBounds.size.width;
288  }
289 
290  var scrollPoint = _CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY);
291 
292  [contentView scrollToPoint:scrollPoint];
293  [[scrollView _headerView] scrollPoint:scrollPoint];
294 
295  }
296  }
297  }
298 
299  return dragOperation;
300 }
301 
302 - (void)_sendPeriodicDraggingUpdate:(CPTimer)aTimer
303 {
304  var userInfo = [aTimer userInfo];
305  _dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"]
306  location:[userInfo objectForKey:@"location"]];
307 }
308 
309 - (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation
310 {
311  [_draggingUpdateTimer invalidate];
312  _draggingUpdateTimer = nil;
313 
314  [_draggedView removeFromSuperview];
315 
316  if (![CPPlatform supportsDragAndDrop])
317  [_draggedWindow orderOut:self];
318 
319  if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_endedAt_operation_)
320  [_draggingSource draggedImage:[_draggedView image] endedAt:aLocation operation:anOperation];
321  else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_endedAt_operation_)
322  [_draggingSource draggedView:_draggedView endedAt:aLocation operation:anOperation];
323 
324  _isDragging = NO;
325 }
326 
327 - (void)performDragOperationInPlatformWindow:(CPPlatformWindow)aPlatformWindow
328 {
329  if (_draggingDestination &&
330  (![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
331  (![_draggingDestination respondsToSelector:@selector(performDragOperation:)] || [_draggingDestination performDragOperation:CPDragServerDraggingInfo]) &&
332  [_draggingDestination respondsToSelector:@selector(concludeDragOperation:)])
333  [_draggingDestination concludeDragOperation:CPDragServerDraggingInfo];
334 }
335 
348 - (void)dragView:(CPView)aView fromWindow:(CPWindow)aWindow at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)mouseDownEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
349 {
350  _isDragging = YES;
351 
352  _draggedView = aView;
353  _draggingPasteboard = aPasteboard || [CPPasteboard pasteboardWithName:CPDragPboard];
354  _draggingSource = aSourceObject;
355  _draggingDestination = nil;
356  _shouldSlideBack = slideBack;
357 
358  // The offset is based on the distance from where we want the view to be initially from where the mouse is initially
359  // Hence the use of mouseDownEvent's location and view's location in global coordinates.
360  var mouseDownWindow = [mouseDownEvent window],
361  mouseDownEventLocation = [mouseDownEvent locationInWindow];
362 
363  if (mouseDownEventLocation)
364  {
365  if (mouseDownWindow)
366  mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation];
367 
368  _draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
369  }
370  else
371  _draggingOffset = _CGSizeMakeZero();
372 
373  if ([CPPlatform isBrowser])
374  [_draggedWindow setPlatformWindow:[aWindow platformWindow]];
375 
376  [aView setFrameOrigin:_CGPointMakeZero()];
377 
378  var mouseLocation = [CPEvent mouseLocation];
379 
380  // Place it where the mouse pointer is.
381  _startDragLocation = _CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height);
382  [_draggedWindow setFrameOrigin:_startDragLocation];
383  [_draggedWindow setFrameSize:[aView frame].size];
384 
385  [[_draggedWindow contentView] addSubview:aView];
386 
387  _implementedDraggingSourceMethods = 0;
388 
389  if (_draggedView === _imageView)
390  {
391  if ([_draggingSource respondsToSelector:@selector(draggedImage:movedTo:)])
392  _implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_movedTo_;
393 
394  if ([_draggingSource respondsToSelector:@selector(draggedImage:endedAt:operation:)])
395  _implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_endedAt_operation_;
396  }
397  else
398  {
399  if ([_draggingSource respondsToSelector:@selector(draggedView:movedTo:)])
400  _implementedDraggingSourceMethods |= CPDraggingSource_draggedView_movedTo_;
401 
402  if ([_draggingSource respondsToSelector:@selector(draggedView:endedAt:operation:)])
403  _implementedDraggingSourceMethods |= CPDraggingSource_draggedView_endedAt_operation_;
404  }
405 
406  if (![CPPlatform supportsDragAndDrop])
407  {
408  [self draggingStartedInPlatformWindow:[aWindow platformWindow] globalLocation:mouseLocation];
409  [self trackDragging:mouseDownEvent];
410  }
411 }
412 
425 - (void)dragImage:(CPImage)anImage fromWindow:(CPWindow)aWindow at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
426 {
427  _isDraggingImage = YES;
428 
429  var imageSize = [anImage size];
430 
431  if (!_imageView)
432  _imageView = [[CPImageView alloc] initWithFrame:_CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)];
433 
434  [_imageView setImage:anImage];
435 
436  [self dragView:_imageView fromWindow:aWindow at:imageLocation offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
437 }
438 
439 - (void)trackDragging:(CPEvent)anEvent
440 {
441  var type = [anEvent type],
442  platformWindow = [_draggedWindow platformWindow],
443  platformWindowLocation = [[anEvent window] convertBaseToPlatformWindow:[anEvent locationInWindow]];
444 
445  if (type === CPLeftMouseUp)
446  {
447  // Make sure we do not finalize (cancel) the drag if the last drag update was disallowed
448  if (_dragOperation !== CPDragOperationNone)
449  [self performDragOperationInPlatformWindow:platformWindow];
450 
451  [self draggingEndedInPlatformWindow:platformWindow globalLocation:platformWindowLocation operation:_dragOperation];
452 
453  // Stop tracking events.
454  return;
455  }
456  else if (type === CPKeyDown)
457  {
458  var characters = [anEvent characters];
459  if (characters === CPEscapeFunctionKey)
460  {
461  _dragOperation = CPDragOperationNone;
462  [self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
463  return;
464  }
465  }
466  else
467  {
468  [self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
469  _dragOperation = [self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation];
470  }
471 
472  // If we're not a mouse up, then we're going to want to grab the next event.
473  [CPApp setTarget:self selector:@selector(trackDragging:)
474  forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask
475  untilDate:nil inMode:0 dequeue:NO];
476 }
477 
478 @end
479 
481 
482 /* @ignore */
483 - (id)_dragHitTest:(CGPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
484 {
485  // If none of our views or ourselves has registered for drag events...
486  if (!_inclusiveRegisteredDraggedTypes)
487  return nil;
488 
489 // We don't need to do this because the only place this gets called
490 // -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
491 // be safe?
492 // if (![self containsPoint:aPoint])
493 // return nil;
494 
495  var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
496  hitView = [_windowView hitTest:adjustedPoint];
497 
498  while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
499  hitView = [hitView superview];
500 
501  if (hitView)
502  return hitView;
503 
504  if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
505  return self;
506 
507  return nil;
508 }
509 
510 @end
511 
513 
517 - (BOOL)isDragging
518 {
519  return _isDragging;
520 }
521 
525 - (CPWindow)draggedWindow
526 {
527  return _draggedWindow;
528 }
529 
533 - (CPView)draggedView
534 {
535  return _draggedView;
536 }
537 
541 - (CGSize)draggingOffset
542 {
543  return _draggingOffset;
544 }
545 
549 - (CPPasteboard)draggingPasteboard
550 {
551  return _draggingPasteboard;
552 }
553 
557 - (id)draggingSource
558 {
559  return _draggingSource;
560 }
561 
562 @end