Source/BoardView.m
author Jens Alfke <jens@mooseyard.com>
Thu Jul 31 20:01:26 2008 -0700 (2008-07-31)
changeset 24 db8640a38faf
parent 23 efe5d4523a23
permissions -rw-r--r--
* Put the spots in the right place in 13x13 and 19x19 boards.
* Fixed some front/back layering issues in Grid.
jens@0
     1
/*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
jens@0
     2
    http://developer.apple.com/samplecode/GeekGameBoard/
jens@0
     3
    Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
jens@0
     4
jens@0
     5
    Redistribution and use in source and binary forms, with or without modification, are permitted
jens@0
     6
    provided that the following conditions are met:
jens@0
     7
jens@0
     8
    * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@0
     9
      and the following disclaimer.
jens@0
    10
    * Redistributions in binary form must reproduce the above copyright notice, this list of
jens@0
    11
      conditions and the following disclaimer in the documentation and/or other materials provided
jens@0
    12
      with the distribution.
jens@0
    13
jens@0
    14
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@0
    15
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@0
    16
    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@0
    17
    BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@0
    18
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@0
    19
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@0
    20
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@0
    21
    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
    22
*/
jens@0
    23
#import "BoardView.h"
jens@0
    24
#import "Bit.h"
jens@0
    25
#import "BitHolder.h"
jens@22
    26
#import "Game+Protected.h"
jens@12
    27
#import "Turn.h"
jens@10
    28
#import "Player.h"
jens@0
    29
#import "QuartzUtils.h"
jens@0
    30
#import "GGBUtils.h"
jens@0
    31
jens@0
    32
jens@22
    33
#define kMaxPerspective 0.965   // 55 degrees
jens@22
    34
jens@22
    35
jens@3
    36
@interface BoardView ()
jens@3
    37
- (void) _findDropTarget: (NSPoint)pos;
jens@3
    38
@end
jens@3
    39
jens@3
    40
jens@0
    41
@implementation BoardView
jens@0
    42
jens@0
    43
jens@22
    44
@synthesize table=_table, gameBoardInset=_gameBoardInset;
jens@0
    45
jens@0
    46
jens@0
    47
- (void) dealloc
jens@0
    48
{
jens@0
    49
    [_game release];
jens@0
    50
    [super dealloc];
jens@0
    51
}
jens@0
    52
jens@0
    53
jens@23
    54
#pragma mark -
jens@23
    55
#pragma mark PERSPECTIVE:
jens@23
    56
jens@23
    57
jens@22
    58
- (void) _applyPerspective
jens@22
    59
{
jens@22
    60
    CATransform3D t;
jens@22
    61
    if( fabs(_perspective) >= M_PI/180 ) {
jens@22
    62
        CGSize size = self.layer.bounds.size;
jens@22
    63
        t = CATransform3DMakeTranslation(-size.width/2, -size.height/4, 0);
jens@22
    64
        t = CATransform3DConcat(t, CATransform3DMakeRotation(-_perspective, 1,0,0));
jens@22
    65
        
jens@22
    66
        CATransform3D pers = CATransform3DIdentity;
jens@23
    67
        pers.m34 = 1.0/-2000;
jens@22
    68
        t = CATransform3DConcat(t, pers);
jens@22
    69
        t = CATransform3DConcat(t, CATransform3DMakeTranslation(size.width/2, 
jens@22
    70
                                                                size.height*(0.25 + 0.05*sin(2*_perspective)),
jens@22
    71
                                                                0));
jens@22
    72
        self.layer.borderWidth = 3;
jens@22
    73
    } else {
jens@22
    74
        t = CATransform3DIdentity;
jens@22
    75
        self.layer.borderWidth = 0;
jens@22
    76
    }
jens@22
    77
    self.layer.transform = t;
jens@22
    78
}    
jens@22
    79
jens@22
    80
- (CGFloat) perspective {return _perspective;}
jens@22
    81
jens@22
    82
- (void) setPerspective: (CGFloat)p
jens@22
    83
{
jens@22
    84
    p = MAX(0.0, MIN(kMaxPerspective, p));
jens@22
    85
    if( p != _perspective ) {
jens@22
    86
        _perspective = p;
jens@22
    87
        [self _applyPerspective];
jens@22
    88
        _game.tablePerspectiveAngle = p;
jens@22
    89
    }
jens@22
    90
}
jens@22
    91
jens@23
    92
- (IBAction) tiltUp: (id)sender     {self.perspective -= M_PI/40;}
jens@23
    93
- (IBAction) tiltDown: (id)sender   {self.perspective += M_PI/40;}
jens@23
    94
jens@23
    95
jens@23
    96
#pragma mark -
jens@23
    97
#pragma mark GAME BOARD:
jens@23
    98
jens@22
    99
jens@10
   100
- (void) _removeGameBoard
jens@10
   101
{
jens@16
   102
    if( _table ) {
jens@16
   103
        RemoveImmediately(_table);
jens@16
   104
        _table = nil;
jens@10
   105
    }
jens@10
   106
}
jens@10
   107
jens@10
   108
- (void) createGameBoard
jens@10
   109
{
jens@10
   110
    [self _removeGameBoard];
jens@16
   111
    _table = [[CALayer alloc] init];
jens@16
   112
    _table.frame = [self gameBoardFrame];
jens@16
   113
    _table.autoresizingMask = kCALayerMinXMargin | kCALayerMaxXMargin | kCALayerMinYMargin | kCALayerMaxYMargin;
jens@22
   114
    
jens@10
   115
    // Tell the game to set up the board:
jens@24
   116
    _game.tablePerspectiveAngle = _perspective;
jens@16
   117
    _game.table = _table;
jens@10
   118
jens@16
   119
    [self.layer addSublayer: _table];
jens@16
   120
    [_table release];
jens@10
   121
}
jens@10
   122
jens@10
   123
jens@10
   124
- (Game*) game
jens@10
   125
{
jens@10
   126
    return _game;
jens@10
   127
}
jens@10
   128
jens@10
   129
- (void) setGame: (Game*)game
jens@10
   130
{
jens@10
   131
    if( game!=_game ) {
jens@16
   132
        _game.table = nil;
jens@10
   133
        setObj(&_game,game);
jens@10
   134
        [self createGameBoard];
jens@10
   135
    }
jens@10
   136
}
jens@10
   137
jens@0
   138
- (void) startGameNamed: (NSString*)gameClassName
jens@0
   139
{
jens@10
   140
    Class gameClass = NSClassFromString(gameClassName);
jens@10
   141
    Game *game = [[gameClass alloc] init];
jens@10
   142
    if( game ) {
jens@10
   143
        self.game = game;
jens@10
   144
        [game release];
jens@0
   145
    }
jens@10
   146
}
jens@10
   147
jens@10
   148
jens@23
   149
#pragma mark -
jens@23
   150
#pragma mark VIEW MANIPULATION:
jens@23
   151
jens@23
   152
jens@0
   153
- (CGRect) gameBoardFrame
jens@0
   154
{
jens@22
   155
    return CGRectInset(self.layer.bounds, _gameBoardInset.width,_gameBoardInset.height);
jens@0
   156
}
jens@0
   157
jens@0
   158
jens@0
   159
- (void)resetCursorRects
jens@0
   160
{
jens@0
   161
    [super resetCursorRects];
jens@15
   162
    if( _game.okToMove )
jens@10
   163
        [self addCursorRect: self.bounds cursor: [NSCursor openHandCursor]];
jens@0
   164
}
jens@0
   165
jens@0
   166
jens@18
   167
- (NSView*) fullScreenView
jens@18
   168
{
jens@18
   169
    return _fullScreenView ?: self;
jens@18
   170
}
jens@18
   171
jens@0
   172
- (IBAction) enterFullScreen: (id)sender
jens@0
   173
{
jens@18
   174
    //[self _removeGameBoard];
jens@18
   175
    if( self.fullScreenView.isInFullScreenMode ) {
jens@18
   176
        [self.fullScreenView exitFullScreenModeWithOptions: nil];
jens@0
   177
    } else {
jens@18
   178
        [self.fullScreenView enterFullScreenMode: self.window.screen 
jens@18
   179
                                     withOptions: nil];
jens@0
   180
    }
jens@18
   181
    //[self createGameBoard];
jens@10
   182
}
jens@10
   183
jens@10
   184
jens@10
   185
- (void)viewWillStartLiveResize
jens@10
   186
{
jens@10
   187
    [super viewWillStartLiveResize];
jens@10
   188
    _oldSize = self.frame.size;
jens@10
   189
}
jens@10
   190
jens@10
   191
- (void)setFrameSize:(NSSize)newSize
jens@10
   192
{
jens@10
   193
    [super setFrameSize: newSize];
jens@10
   194
    if( _oldSize.width > 0.0f ) {
jens@16
   195
        CGAffineTransform xform = _table.affineTransform;
jens@10
   196
        xform.a = xform.d = MIN(newSize.width,newSize.height)/MIN(_oldSize.width,_oldSize.height);
jens@18
   197
        BeginDisableAnimations();
jens@22
   198
        [self _applyPerspective];
jens@16
   199
        _table.affineTransform = xform;
jens@18
   200
        EndDisableAnimations();
jens@10
   201
    } else
jens@10
   202
        [self createGameBoard];
jens@10
   203
}
jens@10
   204
jens@10
   205
- (void)viewDidEndLiveResize
jens@10
   206
{
jens@10
   207
    [super viewDidEndLiveResize];
jens@10
   208
    _oldSize.width = _oldSize.height = 0.0f;
jens@10
   209
    [self createGameBoard];
jens@0
   210
}
jens@0
   211
jens@0
   212
jens@0
   213
#pragma mark -
jens@0
   214
#pragma mark KEY EVENTS:
jens@0
   215
jens@0
   216
jens@18
   217
- (BOOL) performKeyEquivalent: (NSEvent*)ev
jens@0
   218
{
jens@18
   219
    if( [ev.charactersIgnoringModifiers hasPrefix: @"\033"] ) {       // Esc key
jens@18
   220
        if( self.fullScreenView.isInFullScreenMode ) {
jens@18
   221
            [self performSelector: @selector(enterFullScreen:) withObject: nil afterDelay: 0.0];
jens@18
   222
            // without the delayed-perform, NSWindow crashes right after this method returns!
jens@18
   223
            return YES;
jens@18
   224
        }
jens@0
   225
    }
jens@18
   226
    return NO;
jens@0
   227
}
jens@0
   228
jens@0
   229
jens@0
   230
#pragma mark -
jens@0
   231
#pragma mark HIT-TESTING:
jens@0
   232
jens@0
   233
jens@5
   234
/** Converts a point from window coords, to this view's root layer's coords. */
jens@5
   235
- (CGPoint) _convertPointFromWindowToLayer: (NSPoint)locationInWindow
jens@5
   236
{
jens@5
   237
    NSPoint where = [self convertPoint: locationInWindow fromView: nil];    // convert to view coords
jens@22
   238
    where = [self convertPointToBase: where];                               // then to layer base coords
jens@22
   239
    return [self.layer convertPoint: NSPointToCGPoint(where)                // then to transformed layer coords
jens@22
   240
                          fromLayer: self.layer.superlayer];
jens@5
   241
}
jens@5
   242
jens@5
   243
jens@0
   244
// Hit-testing callbacks (to identify which layers caller is interested in):
jens@0
   245
typedef BOOL (*LayerMatchCallback)(CALayer*);
jens@0
   246
jens@0
   247
static BOOL layerIsBit( CALayer* layer )        {return [layer isKindOfClass: [Bit class]];}
jens@0
   248
static BOOL layerIsBitHolder( CALayer* layer )  {return [layer conformsToProtocol: @protocol(BitHolder)];}
jens@0
   249
static BOOL layerIsDropTarget( CALayer* layer ) {return [layer respondsToSelector: @selector(draggingEntered:)];}
jens@0
   250
jens@0
   251
jens@0
   252
/** Locates the layer at a given point in window coords.
jens@0
   253
    If the leaf layer doesn't pass the layer-match callback, the nearest ancestor that does is returned.
jens@0
   254
    If outOffset is provided, the point's position relative to the layer is stored into it. */
jens@0
   255
- (CALayer*) hitTestPoint: (NSPoint)locationInWindow
jens@0
   256
         forLayerMatching: (LayerMatchCallback)match
jens@0
   257
                   offset: (CGPoint*)outOffset
jens@0
   258
{
jens@5
   259
    CGPoint where = [self _convertPointFromWindowToLayer: locationInWindow ];
jens@16
   260
    CALayer *layer = [_table hitTest: where];
jens@0
   261
    while( layer ) {
jens@0
   262
        if( match(layer) ) {
jens@0
   263
            CGPoint bitPos = [self.layer convertPoint: layer.position 
jens@0
   264
                              fromLayer: layer.superlayer];
jens@0
   265
            if( outOffset )
jens@0
   266
                *outOffset = CGPointMake( bitPos.x-where.x, bitPos.y-where.y);
jens@0
   267
            return layer;
jens@0
   268
        } else
jens@0
   269
            layer = layer.superlayer;
jens@0
   270
    }
jens@0
   271
    return nil;
jens@0
   272
}
jens@0
   273
jens@0
   274
jens@0
   275
#pragma mark -
jens@0
   276
#pragma mark MOUSE CLICKS & DRAGS:
jens@0
   277
jens@0
   278
jens@0
   279
- (void) mouseDown: (NSEvent*)ev
jens@0
   280
{
jens@15
   281
    if( ! _game.okToMove ) {
jens@10
   282
        NSBeep();
jens@10
   283
        return;
jens@10
   284
    }
jens@10
   285
    
jens@3
   286
    BOOL placing = NO;
jens@0
   287
    _dragStartPos = ev.locationInWindow;
jens@0
   288
    _dragBit = (Bit*) [self hitTestPoint: _dragStartPos
jens@0
   289
                        forLayerMatching: layerIsBit 
jens@0
   290
                                  offset: &_dragOffset];
jens@3
   291
    
jens@3
   292
    if( ! _dragBit ) {
jens@3
   293
        // If no bit was clicked, see if it's a BitHolder the game will let the user add a Bit to:
jens@3
   294
        id<BitHolder> holder = (id<BitHolder>) [self hitTestPoint: _dragStartPos
jens@3
   295
                                                 forLayerMatching: layerIsBitHolder
jens@3
   296
                                                           offset: NULL];
jens@3
   297
        if( holder ) {
jens@3
   298
            _dragBit = [_game bitToPlaceInHolder: holder];
jens@3
   299
            if( _dragBit ) {
jens@3
   300
                _dragOffset.x = _dragOffset.y = 0;
jens@3
   301
                if( _dragBit.superlayer==nil )
jens@5
   302
                    _dragBit.position = [self _convertPointFromWindowToLayer: _dragStartPos];
jens@3
   303
                placing = YES;
jens@3
   304
            }
jens@3
   305
        }
jens@3
   306
    }
jens@3
   307
    
jens@3
   308
    if( ! _dragBit ) {
jens@3
   309
        Beep();
jens@3
   310
        return;
jens@3
   311
    }
jens@3
   312
    
jens@3
   313
    // Clicked on a Bit:
jens@3
   314
    _dragMoved = NO;
jens@3
   315
    _dropTarget = nil;
jens@3
   316
    _oldHolder = _dragBit.holder;
jens@3
   317
    // Ask holder's and game's permission before dragging:
jens@3
   318
    if( _oldHolder ) {
jens@3
   319
        _dragBit = [_oldHolder canDragBit: _dragBit];
jens@0
   320
        if( _dragBit && ! [_game canBit: _dragBit moveFrom: _oldHolder] ) {
jens@0
   321
            [_oldHolder cancelDragBit: _dragBit];
jens@0
   322
            _dragBit = nil;
jens@0
   323
        }
jens@0
   324
        if( ! _dragBit ) {
jens@0
   325
            _oldHolder = nil;
jens@0
   326
            NSBeep();
jens@0
   327
            return;
jens@0
   328
        }
jens@3
   329
    }
jens@3
   330
    
jens@3
   331
    // Start dragging:
jens@3
   332
    _oldSuperlayer = _dragBit.superlayer;
jens@3
   333
    _oldLayerIndex = [_oldSuperlayer.sublayers indexOfObjectIdenticalTo: _dragBit];
jens@3
   334
    _oldPos = _dragBit.position;
jens@3
   335
    ChangeSuperlayer(_dragBit, self.layer, self.layer.sublayers.count);
jens@3
   336
    _dragBit.pickedUp = YES;
jens@3
   337
    [[NSCursor closedHandCursor] push];
jens@3
   338
    
jens@3
   339
    if( placing ) {
jens@3
   340
        if( _oldSuperlayer )
jens@5
   341
            _dragBit.position = [self _convertPointFromWindowToLayer: _dragStartPos];
jens@3
   342
        _dragMoved = YES;
jens@3
   343
        [self _findDropTarget: _dragStartPos];
jens@3
   344
    }
jens@0
   345
}
jens@0
   346
jens@3
   347
jens@0
   348
- (void) mouseDragged: (NSEvent*)ev
jens@0
   349
{
jens@0
   350
    if( _dragBit ) {
jens@0
   351
        // Get the mouse position, and see if we've moved 3 pixels since the mouseDown:
jens@0
   352
        NSPoint pos = ev.locationInWindow;
jens@0
   353
        if( fabs(pos.x-_dragStartPos.x)>=3 || fabs(pos.y-_dragStartPos.y)>=3 )
jens@0
   354
            _dragMoved = YES;
jens@0
   355
        
jens@0
   356
        // Move the _dragBit (without animation -- it's unnecessary and slows down responsiveness):
jens@5
   357
        CGPoint where = [self _convertPointFromWindowToLayer: pos];
jens@0
   358
        where.x += _dragOffset.x;
jens@0
   359
        where.y += _dragOffset.y;
jens@0
   360
        
jens@5
   361
        CGPoint newPos = [_dragBit.superlayer convertPoint: where fromLayer: self.layer];
jens@0
   362
jens@0
   363
        [CATransaction flush];
jens@0
   364
        [CATransaction begin];
jens@0
   365
        [CATransaction setValue:(id)kCFBooleanTrue
jens@0
   366
                         forKey:kCATransactionDisableActions];
jens@0
   367
        _dragBit.position = newPos;
jens@0
   368
        [CATransaction commit];
jens@0
   369
jens@0
   370
        // Find what it's over:
jens@3
   371
        [self _findDropTarget: pos];
jens@3
   372
    }
jens@3
   373
}
jens@3
   374
jens@3
   375
jens@3
   376
- (void) _findDropTarget: (NSPoint)locationInWindow
jens@3
   377
{
jens@3
   378
    locationInWindow.x += _dragOffset.x;
jens@3
   379
    locationInWindow.y += _dragOffset.y;
jens@3
   380
    id<BitHolder> target = (id<BitHolder>) [self hitTestPoint: locationInWindow
jens@3
   381
                                             forLayerMatching: layerIsBitHolder
jens@3
   382
                                                       offset: NULL];
jens@3
   383
    if( target == _oldHolder )
jens@3
   384
        target = nil;
jens@3
   385
    if( target != _dropTarget ) {
jens@3
   386
        [_dropTarget willNotDropBit: _dragBit];
jens@3
   387
        _dropTarget.highlighted = NO;
jens@3
   388
        _dropTarget = nil;
jens@3
   389
    }
jens@3
   390
    if( target ) {
jens@3
   391
        CGPoint targetPos = [(CALayer*)target convertPoint: _dragBit.position
jens@3
   392
                                                 fromLayer: _dragBit.superlayer];
jens@3
   393
        if( [target canDropBit: _dragBit atPoint: targetPos]
jens@3
   394
           && [_game canBit: _dragBit moveFrom: _oldHolder to: target] ) {
jens@3
   395
            _dropTarget = target;
jens@3
   396
            _dropTarget.highlighted = YES;
jens@0
   397
        }
jens@0
   398
    }
jens@0
   399
}
jens@0
   400
jens@3
   401
jens@0
   402
- (void) mouseUp: (NSEvent*)ev
jens@0
   403
{
jens@0
   404
    if( _dragBit ) {
jens@0
   405
        if( _dragMoved ) {
jens@0
   406
            // Update the drag tracking to the final mouse position:
jens@0
   407
            [self mouseDragged: ev];
jens@0
   408
            _dropTarget.highlighted = NO;
jens@0
   409
            _dragBit.pickedUp = NO;
jens@0
   410
jens@0
   411
            // Is the move legal?
jens@0
   412
            if( _dropTarget && [_dropTarget dropBit: _dragBit
jens@0
   413
                                            atPoint: [(CALayer*)_dropTarget convertPoint: _dragBit.position 
jens@0
   414
                                                                            fromLayer: _dragBit.superlayer]] ) {
jens@0
   415
                // Yes, notify the interested parties:
jens@0
   416
                [_oldHolder draggedBit: _dragBit to: _dropTarget];
jens@0
   417
                [_game bit: _dragBit movedFrom: _oldHolder to: _dropTarget];
jens@0
   418
            } else {
jens@0
   419
                // Nope, cancel:
jens@0
   420
                [_dropTarget willNotDropBit: _dragBit];
jens@3
   421
                if( _oldSuperlayer ) {
jens@3
   422
                    ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
jens@3
   423
                    _dragBit.position = _oldPos;
jens@3
   424
                    [_oldHolder cancelDragBit: _dragBit];
jens@3
   425
                } else {
jens@3
   426
                    [_dragBit removeFromSuperlayer];
jens@3
   427
                }
jens@0
   428
            }
jens@0
   429
        } else {
jens@0
   430
            // Just a click, without a drag:
jens@0
   431
            _dropTarget.highlighted = NO;
jens@0
   432
            _dragBit.pickedUp = NO;
jens@0
   433
            ChangeSuperlayer(_dragBit, _oldSuperlayer, _oldLayerIndex);
jens@0
   434
            [_oldHolder cancelDragBit: _dragBit];
jens@0
   435
            if( ! [_game clickedBit: _dragBit] )
jens@0
   436
                NSBeep();
jens@0
   437
        }
jens@9
   438
jens@0
   439
        _dropTarget = nil;
jens@0
   440
        _dragBit = nil;
jens@0
   441
        [NSCursor pop];
jens@0
   442
    }
jens@0
   443
}
jens@0
   444
jens@0
   445
jens@22
   446
- (void)scrollWheel:(NSEvent *)e
jens@22
   447
{
jens@22
   448
    self.perspective += e.deltaY * M_PI/180;
jens@22
   449
    //Log(@"Perspective = %2.0f degrees (%5.3f radians)", self.perspective*180/M_PI, self.perspective);
jens@22
   450
}
jens@22
   451
jens@22
   452
jens@0
   453
#pragma mark -
jens@0
   454
#pragma mark INCOMING DRAGS:
jens@0
   455
jens@0
   456
jens@0
   457
// subroutine to call the target
jens@0
   458
static int tell( id target, SEL selector, id arg, int defaultValue )
jens@0
   459
{
jens@0
   460
    if( target && [target respondsToSelector: selector] )
jens@0
   461
        return (ssize_t) [target performSelector: selector withObject: arg];
jens@0
   462
    else
jens@0
   463
        return defaultValue;
jens@0
   464
}
jens@0
   465
jens@0
   466
jens@0
   467
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
jens@0
   468
{
jens@0
   469
    _viewDropTarget = [self hitTestPoint: [sender draggingLocation]
jens@0
   470
                        forLayerMatching: layerIsDropTarget
jens@0
   471
                                  offset: NULL];
jens@0
   472
    _viewDropOp = _viewDropTarget ?[_viewDropTarget draggingEntered: sender] :NSDragOperationNone;
jens@0
   473
    return _viewDropOp;
jens@0
   474
}
jens@0
   475
jens@0
   476
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
jens@0
   477
{
jens@0
   478
    CALayer *target = [self hitTestPoint: [sender draggingLocation]
jens@0
   479
                        forLayerMatching: layerIsDropTarget 
jens@0
   480
                                  offset: NULL];
jens@0
   481
    if( target == _viewDropTarget ) {
jens@0
   482
        if( _viewDropTarget )
jens@0
   483
            _viewDropOp = tell(_viewDropTarget,@selector(draggingUpdated:),sender,_viewDropOp);
jens@0
   484
    } else {
jens@0
   485
        tell(_viewDropTarget,@selector(draggingExited:),sender,0);
jens@0
   486
        _viewDropTarget = target;
jens@0
   487
        if( _viewDropTarget )
jens@0
   488
            _viewDropOp = [_viewDropTarget draggingEntered: sender];
jens@0
   489
        else
jens@0
   490
            _viewDropOp = NSDragOperationNone;
jens@0
   491
    }
jens@0
   492
    return _viewDropOp;
jens@0
   493
}
jens@0
   494
jens@0
   495
- (BOOL)wantsPeriodicDraggingUpdates
jens@0
   496
{
jens@0
   497
    return (_viewDropTarget!=nil);
jens@0
   498
}
jens@0
   499
jens@0
   500
- (void)draggingExited:(id <NSDraggingInfo>)sender
jens@0
   501
{
jens@0
   502
    tell(_viewDropTarget,@selector(draggingExited:),sender,0);
jens@0
   503
    _viewDropTarget = nil;
jens@0
   504
}
jens@0
   505
jens@0
   506
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
jens@0
   507
{
jens@0
   508
    return tell(_viewDropTarget,@selector(prepareForDragOperation:),sender,YES);
jens@0
   509
}
jens@0
   510
jens@0
   511
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
jens@0
   512
{
jens@0
   513
    return [_viewDropTarget performDragOperation: sender];
jens@0
   514
}
jens@0
   515
jens@0
   516
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
jens@0
   517
{
jens@0
   518
    tell(_viewDropTarget,@selector(concludeDragOperation:),sender,0);
jens@0
   519
}
jens@0
   520
jens@0
   521
- (void)draggingEnded:(id <NSDraggingInfo>)sender
jens@0
   522
{
jens@0
   523
    tell(_viewDropTarget,@selector(draggingEnded:),sender,0);
jens@0
   524
}
jens@0
   525
jens@0
   526
@end