![]() |
API 0.9.5
|
00001 /* 00002 * CPDragServer.j 00003 * AppKit 00004 * 00005 * Created by Francisco Tolmasky. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 00025 CPDragOperationNone = 0; 00026 CPDragOperationCopy = 1 << 1; 00027 CPDragOperationLink = 1 << 1; 00028 CPDragOperationGeneric = 1 << 2; 00029 CPDragOperationPrivate = 1 << 3; 00030 CPDragOperationMove = 1 << 4; 00031 CPDragOperationDelete = 1 << 5; 00032 CPDragOperationEvery = -1; 00033 00034 #define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window]) 00035 00036 var CPDragServerPreviousEvent = nil, 00037 CPDragServerPeriodicUpdateInterval = 0.05; 00038 00039 var CPSharedDragServer = nil; 00040 00041 var CPDragServerSource = nil, 00042 CPDragServerDraggingInfo = nil; 00043 00044 /* 00045 CPDraggingInfo is a container of information about a specific dragging session. 00046 @ignore 00047 */ 00048 @implementation CPDraggingInfo : CPObject 00049 { 00050 id __doxygen__; 00051 } 00052 00053 - (CPPasteboard)draggingPasteboard 00054 { 00055 if ([CPPlatform supportsDragAndDrop]) 00056 return [_CPDOMDataTransferPasteboard DOMDataTransferPasteboard]; 00057 00058 return [[CPDragServer sharedDragServer] draggingPasteboard]; 00059 } 00060 00061 - (id)draggingSource 00062 { 00063 return [[CPDragServer sharedDragServer] draggingSource]; 00064 } 00065 00066 /* 00067 - (unsigned)draggingSourceOperationMask 00068 */ 00069 00070 - (CPPoint)draggingLocation 00071 { 00072 return [[CPDragServer sharedDragServer] draggingLocation]; 00073 } 00074 00075 - (CPWindow)draggingDestinationWindow 00076 { 00077 return DRAGGING_WINDOW([[CPDragServer sharedDragServer] draggingDestination]); 00078 } 00079 00080 - (CPImage)draggedImage 00081 { 00082 return [[self draggedView] image]; 00083 } 00084 00085 - (CGPoint)draggedImageLocation 00086 { 00087 return [self draggedViewLocation]; 00088 } 00089 00090 - (CPView)draggedView 00091 { 00092 return [[CPDragServer sharedDragServer] draggedView]; 00093 } 00094 00095 - (CGPoint)draggedViewLocation 00096 { 00097 var dragServer = [CPDragServer sharedDragServer]; 00098 00099 return [DRAGGING_WINDOW([dragServer draggingDestination]) convertPlatformWindowToBase:[[dragServer draggedView] frame].origin]; 00100 } 00101 00102 @end 00103 00104 var CPDraggingSource_draggedImage_movedTo_ = 1 << 0, 00105 CPDraggingSource_draggedImage_endedAt_operation_ = 1 << 1, 00106 CPDraggingSource_draggedView_movedTo_ = 1 << 2, 00107 CPDraggingSource_draggedView_endedAt_operation_ = 1 << 3; 00108 00109 @implementation CPDragServer : CPObject 00110 { 00111 BOOL _isDragging; 00112 00113 CPWindow _draggedWindow; 00114 CPView _draggedView; 00115 CPImageView _imageView; 00116 00117 BOOL _isDraggingImage; 00118 00119 CGSize _draggingOffset; 00120 00121 CPPasteboard _draggingPasteboard; 00122 00123 id _draggingSource; 00124 unsigned _implementedDraggingSourceMethods; 00125 00126 CGPoint _draggingLocation; 00127 id _draggingDestination; 00128 BOOL _draggingDestinationWantsPeriodicUpdates; 00129 00130 CGPoint _startDragLocation; 00131 BOOL _shouldSlideBack; 00132 unsigned _dragOperation; 00133 00134 CPTimer _draggingUpdateTimer; 00135 } 00136 00137 /* 00138 Private Objective-J/Cappuccino method 00139 @ignore 00140 */ 00141 + (void)initialize 00142 { 00143 if (self !== [CPDragServer class]) 00144 return; 00145 00146 CPDragServerDraggingInfo = [[CPDraggingInfo alloc] init]; 00147 } 00148 00149 + (CPDragServer)sharedDragServer 00150 { 00151 if (!CPSharedDragServer) 00152 CPSharedDragServer = [[CPDragServer alloc] init]; 00153 00154 return CPSharedDragServer; 00155 } 00156 00157 /* 00158 @ignore 00159 */ 00160 - (id)init 00161 { 00162 self = [super init]; 00163 00164 if (self) 00165 { 00166 _draggedWindow = [[CPWindow alloc] initWithContentRect:_CGRectMakeZero() styleMask:CPBorderlessWindowMask]; 00167 00168 [_draggedWindow setLevel:CPDraggingWindowLevel]; 00169 } 00170 00171 return self; 00172 } 00173 00174 - (id)draggingDestination 00175 { 00176 return _draggingDestination; 00177 } 00178 00179 - (CGPoint)draggingLocation 00180 { 00181 return _draggingLocation 00182 } 00183 00184 - (void)draggingStartedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation 00185 { 00186 if (_isDraggingImage) 00187 { 00188 if ([_draggingSource respondsToSelector:@selector(draggedImage:beganAt:)]) 00189 [_draggingSource draggedImage:[_draggedView image] beganAt:aLocation]; 00190 } 00191 else 00192 { 00193 if ([_draggingSource respondsToSelector:@selector(draggedView:beganAt:)]) 00194 [_draggingSource draggedView:_draggedView beganAt:aLocation]; 00195 } 00196 00197 if (![CPPlatform supportsDragAndDrop]) 00198 [_draggedWindow orderFront:self]; 00199 } 00200 00201 - (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation 00202 { 00203 if (![CPPlatform supportsDragAndDrop]) 00204 [_draggedWindow setFrameOrigin:_CGPointMake(aGlobalLocation.x - _draggingOffset.width, aGlobalLocation.y - _draggingOffset.height)]; 00205 00206 if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_) 00207 [_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation]; 00208 00209 else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_movedTo_) 00210 [_draggingSource draggedView:_draggedView movedTo:aGlobalLocation]; 00211 } 00212 00213 - (CPDragOperation)draggingUpdatedInPlatformWindow:(CPPlatformWindow)aPlatformWindow location:(CGPoint)aLocation 00214 { 00215 [_draggingUpdateTimer invalidate]; 00216 _draggingUpdateTimer = nil; 00217 00218 var dragOperation = CPDragOperationCopy, 00219 // We have to convert base to bridge since the drag event comes from the source window, not the drag window. 00220 draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]]; 00221 00222 if (draggingDestination) 00223 _draggingLocation = [DRAGGING_WINDOW(draggingDestination) convertPlatformWindowToBase:aLocation]; 00224 00225 if (draggingDestination !== _draggingDestination) 00226 { 00227 if ([_draggingDestination respondsToSelector:@selector(draggingExited:)]) 00228 [_draggingDestination draggingExited:CPDragServerDraggingInfo]; 00229 00230 _draggingDestination = draggingDestination; 00231 00232 if ([_draggingDestination respondsToSelector:@selector(wantsPeriodicDraggingUpdates)]) 00233 _draggingDestinationWantsPeriodicUpdates = [_draggingDestination wantsPeriodicDraggingUpdates]; 00234 else 00235 _draggingDestinationWantsPeriodicUpdates = YES; 00236 00237 if ([_draggingDestination respondsToSelector:@selector(draggingEntered:)]) 00238 dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo]; 00239 } 00240 else if ([_draggingDestination respondsToSelector:@selector(draggingUpdated:)]) 00241 dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo]; 00242 00243 if (!_draggingDestination) 00244 dragOperation = CPDragOperationNone; 00245 else 00246 { 00247 if (_draggingDestinationWantsPeriodicUpdates) 00248 _draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval 00249 target:self 00250 selector:@selector(_sendPeriodicDraggingUpdate:) 00251 userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}] 00252 repeats:NO]; 00253 00254 var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil; 00255 if (scrollView) 00256 { 00257 var contentView = [scrollView contentView], 00258 bounds = [contentView bounds], 00259 insetBounds = CGRectInset(bounds, 30, 30), 00260 eventLocation = [contentView convertPoint:_draggingLocation fromView:nil], 00261 deltaX = 0, 00262 deltaY = 0; 00263 00264 if (!CGRectContainsPoint(insetBounds, eventLocation)) 00265 { 00266 if ([scrollView hasVerticalScroller]) 00267 { 00268 if (eventLocation.y < _CGRectGetMinY(insetBounds)) 00269 deltaY = _CGRectGetMinY(insetBounds) - eventLocation.y; 00270 else if (eventLocation.y > _CGRectGetMaxY(insetBounds)) 00271 deltaY = _CGRectGetMaxY(insetBounds) - eventLocation.y; 00272 if (deltaY < -insetBounds.size.height) 00273 deltaY = -insetBounds.size.height; 00274 if (deltaY > insetBounds.size.height) 00275 deltaY = insetBounds.size.height; 00276 } 00277 00278 if ([scrollView hasHorizontalScroller]) 00279 { 00280 if (eventLocation.x < _CGRectGetMinX(insetBounds)) 00281 deltaX = _CGRectGetMinX(insetBounds) - eventLocation.x; 00282 else if (eventLocation.x > _CGRectGetMaxX(insetBounds)) 00283 deltaX = _CGRectGetMaxX(insetBounds) - eventLocation.x; 00284 if (deltaX < -insetBounds.size.width) 00285 deltaX = -insetBounds.size.width; 00286 if (deltaX > insetBounds.size.width) 00287 deltaX = insetBounds.size.width; 00288 } 00289 00290 var scrollPoint = _CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY); 00291 00292 [contentView scrollToPoint:scrollPoint]; 00293 [[scrollView _headerView] scrollPoint:scrollPoint]; 00294 00295 } 00296 } 00297 } 00298 00299 return dragOperation; 00300 } 00301 00302 - (void)_sendPeriodicDraggingUpdate:(CPTimer)aTimer 00303 { 00304 var userInfo = [aTimer userInfo]; 00305 _dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"] 00306 location:[userInfo objectForKey:@"location"]]; 00307 } 00308 00309 - (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation 00310 { 00311 [_draggingUpdateTimer invalidate]; 00312 _draggingUpdateTimer = nil; 00313 00314 [_draggedView removeFromSuperview]; 00315 00316 if (![CPPlatform supportsDragAndDrop]) 00317 [_draggedWindow orderOut:self]; 00318 00319 if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_endedAt_operation_) 00320 [_draggingSource draggedImage:[_draggedView image] endedAt:aLocation operation:anOperation]; 00321 else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_endedAt_operation_) 00322 [_draggingSource draggedView:_draggedView endedAt:aLocation operation:anOperation]; 00323 00324 _isDragging = NO; 00325 } 00326 00327 - (void)performDragOperationInPlatformWindow:(CPPlatformWindow)aPlatformWindow 00328 { 00329 if (_draggingDestination && 00330 (![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) && 00331 (![_draggingDestination respondsToSelector:@selector(performDragOperation:)] || [_draggingDestination performDragOperation:CPDragServerDraggingInfo]) && 00332 [_draggingDestination respondsToSelector:@selector(concludeDragOperation:)]) 00333 [_draggingDestination concludeDragOperation:CPDragServerDraggingInfo]; 00334 } 00335 00348 - (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 00349 { 00350 _isDragging = YES; 00351 00352 _draggedView = aView; 00353 _draggingPasteboard = aPasteboard || [CPPasteboard pasteboardWithName:CPDragPboard]; 00354 _draggingSource = aSourceObject; 00355 _draggingDestination = nil; 00356 _shouldSlideBack = slideBack; 00357 00358 // The offset is based on the distance from where we want the view to be initially from where the mouse is initially 00359 // Hence the use of mouseDownEvent's location and view's location in global coordinates. 00360 var mouseDownWindow = [mouseDownEvent window], 00361 mouseDownEventLocation = [mouseDownEvent locationInWindow]; 00362 00363 if (mouseDownEventLocation) 00364 { 00365 if (mouseDownWindow) 00366 mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation]; 00367 00368 _draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y); 00369 } 00370 else 00371 _draggingOffset = _CGSizeMakeZero(); 00372 00373 if ([CPPlatform isBrowser]) 00374 [_draggedWindow setPlatformWindow:[aWindow platformWindow]]; 00375 00376 [aView setFrameOrigin:_CGPointMakeZero()]; 00377 00378 var mouseLocation = [CPEvent mouseLocation]; 00379 00380 // Place it where the mouse pointer is. 00381 _startDragLocation = _CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height); 00382 [_draggedWindow setFrameOrigin:_startDragLocation]; 00383 [_draggedWindow setFrameSize:[aView frame].size]; 00384 00385 [[_draggedWindow contentView] addSubview:aView]; 00386 00387 _implementedDraggingSourceMethods = 0; 00388 00389 if (_draggedView === _imageView) 00390 { 00391 if ([_draggingSource respondsToSelector:@selector(draggedImage:movedTo:)]) 00392 _implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_movedTo_; 00393 00394 if ([_draggingSource respondsToSelector:@selector(draggedImage:endedAt:operation:)]) 00395 _implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_endedAt_operation_; 00396 } 00397 else 00398 { 00399 if ([_draggingSource respondsToSelector:@selector(draggedView:movedTo:)]) 00400 _implementedDraggingSourceMethods |= CPDraggingSource_draggedView_movedTo_; 00401 00402 if ([_draggingSource respondsToSelector:@selector(draggedView:endedAt:operation:)]) 00403 _implementedDraggingSourceMethods |= CPDraggingSource_draggedView_endedAt_operation_; 00404 } 00405 00406 if (![CPPlatform supportsDragAndDrop]) 00407 { 00408 [self draggingStartedInPlatformWindow:[aWindow platformWindow] globalLocation:mouseLocation]; 00409 [self trackDragging:mouseDownEvent]; 00410 } 00411 } 00412 00425 - (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 00426 { 00427 _isDraggingImage = YES; 00428 00429 var imageSize = [anImage size]; 00430 00431 if (!_imageView) 00432 _imageView = [[CPImageView alloc] initWithFrame:_CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)]; 00433 00434 [_imageView setImage:anImage]; 00435 00436 [self dragView:_imageView fromWindow:aWindow at:imageLocation offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack]; 00437 } 00438 00439 - (void)trackDragging:(CPEvent)anEvent 00440 { 00441 var type = [anEvent type], 00442 platformWindow = [_draggedWindow platformWindow], 00443 platformWindowLocation = [[anEvent window] convertBaseToPlatformWindow:[anEvent locationInWindow]]; 00444 00445 if (type === CPLeftMouseUp) 00446 { 00447 // Make sure we do not finalize (cancel) the drag if the last drag update was disallowed 00448 if (_dragOperation !== CPDragOperationNone) 00449 [self performDragOperationInPlatformWindow:platformWindow]; 00450 00451 [self draggingEndedInPlatformWindow:platformWindow globalLocation:platformWindowLocation operation:_dragOperation]; 00452 00453 // Stop tracking events. 00454 return; 00455 } 00456 else if (type === CPKeyDown) 00457 { 00458 var characters = [anEvent characters]; 00459 if (characters === CPEscapeFunctionKey) 00460 { 00461 _dragOperation = CPDragOperationNone; 00462 [self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation]; 00463 return; 00464 } 00465 } 00466 else 00467 { 00468 [self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation]; 00469 _dragOperation = [self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation]; 00470 } 00471 00472 // If we're not a mouse up, then we're going to want to grab the next event. 00473 [CPApp setTarget:self selector:@selector(trackDragging:) 00474 forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask 00475 untilDate:nil inMode:0 dequeue:NO]; 00476 } 00477 00478 @end 00479 00480 @implementation CPWindow (CPDraggingAdditions) 00481 00482 /* @ignore */ 00483 - (id)_dragHitTest:(CGPoint)aPoint pasteboard:(CPPasteboard)aPasteboard 00484 { 00485 // If none of our views or ourselves has registered for drag events... 00486 if (!_inclusiveRegisteredDraggedTypes) 00487 return nil; 00488 00489 // We don't need to do this because the only place this gets called 00490 // -_dragHitTest: in CPPlatformWindow does this already. Perhaps to 00491 // be safe? 00492 // if (![self containsPoint:aPoint]) 00493 // return nil; 00494 00495 var adjustedPoint = [self convertPlatformWindowToBase:aPoint], 00496 hitView = [_windowView hitTest:adjustedPoint]; 00497 00498 while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]]) 00499 hitView = [hitView superview]; 00500 00501 if (hitView) 00502 return hitView; 00503 00504 if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]]) 00505 return self; 00506 00507 return nil; 00508 } 00509 00510 @end 00511 00512 @implementation CPDragServer (CPSynthesizedAccessors) 00513 00517 - (BOOL)isDragging 00518 { 00519 return _isDragging; 00520 } 00521 00525 - (CPWindow)draggedWindow 00526 { 00527 return _draggedWindow; 00528 } 00529 00533 - (CPView)draggedView 00534 { 00535 return _draggedView; 00536 } 00537 00541 - (CGSize)draggingOffset 00542 { 00543 return _draggingOffset; 00544 } 00545 00549 - (CPPasteboard)draggingPasteboard 00550 { 00551 return _draggingPasteboard; 00552 } 00553 00557 - (id)draggingSource 00558 { 00559 return _draggingSource; 00560 } 00561 00562 @end