1/* 2 NSAnimation.m 3 4 Created by Dr. H. Nikolaus Schaller on Sat Mar 06 2006. 5 Copyright (c) 2007 Free Software Foundation, Inc. 6 7 Author: Xavier Glattard (xgl) <xavier.glattard@online.fr> 8 9 This file used to be part of the mySTEP Library. 10 This file now is part of the GNUstep GUI Library. 11 12 This library is free software; you can redistribute it and/or 13 modify it under the terms of the GNU Lesser General Public 14 License as published by the Free Software Foundation; either 15 version 2 of the License, or (at your option) any later version. 16 17 This library is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public 23 License along with this library; see the file COPYING.LIB. 24 If not, see <http://www.gnu.org/licenses/> or write to the 25 Free Software Foundation, 51 Franklin Street, Fifth Floor, 26 Boston, MA 02110-1301, USA. 27*/ 28 29#import <Foundation/NSDate.h> 30#import <Foundation/NSDebug.h> 31#import <Foundation/NSDictionary.h> 32#import <Foundation/NSException.h> 33#import <Foundation/NSLock.h> 34#import <Foundation/NSNotification.h> 35#import <Foundation/NSRunLoop.h> 36#import <Foundation/NSThread.h> 37#import <Foundation/NSValue.h> 38#import <GNUstepBase/GSLock.h> 39 40#import "AppKit/NSAnimation.h" 41#import "AppKit/NSApplication.h" 42// needed by NSViewAnimation 43#import "AppKit/NSView.h" 44#import "AppKit/NSWindow.h" 45 46#include <math.h> 47 48NSString* NSAnimationBlockingRunLoopMode = @"NSAnimationBlockingRunLoopMode"; 49 50/*===================* 51 * NSAnimation class * 52 *===================*/ 53#define GSI_ARRAY_NO_RETAIN 54#define GSI_ARRAY_NO_RELEASE 55#define GSIArrayItem NSAnimationProgress 56#include <GNUstepBase/GSIArray.h> 57 58// 'reasonable value' ? 59#define GS_ANIMATION_DEFAULT_FRAME_RATE 25.0 60 61static NSArray* _NSAnimationDefaultRunLoopModes; 62 63static inline void 64_GSBezierComputeCoefficients(_GSBezierDesc *b) 65{ 66 b->a[0] = b->p[0]; 67 b->a[1] =-3.0*b->p[0] + 3.0*b->p[1]; 68 b->a[2] = 3.0*b->p[0] - 6.0*b->p[1] + 3.0*b->p[2]; 69 b->a[3] =- b->p[0] + 3.0*b->p[1] - 3.0*b->p[2] + b->p[3]; 70 b->areCoefficientsComputed = YES; 71} 72 73static inline float 74_GSBezierEval(_GSBezierDesc *b, float t) 75{ 76 if (!b->areCoefficientsComputed) 77 _GSBezierComputeCoefficients(b); 78 return b->a[0] + t * (b->a[1] + t * (b->a[2] + t * b->a[3])); 79} 80 81static inline float 82_GSBezierDerivEval(_GSBezierDesc *b, float t) 83{ 84 if (!b->areCoefficientsComputed) 85 _GSBezierComputeCoefficients(b); 86 return b->a[1] + t * (2.0 * b->a[2] + t * 3.0 * b->a[3]); 87} 88 89static inline void 90_GSRationalBezierComputeBezierDesc(_GSRationalBezierDesc *rb) 91{ 92 unsigned i; 93 94 for (i = 0; i < 4; i++) 95 rb->n.p[i] = (rb->d.p[i] = rb->w[i]) * rb->p[i]; 96 _GSBezierComputeCoefficients(&rb->n); 97 _GSBezierComputeCoefficients(&rb->d); 98 rb->areBezierDescComputed = YES; 99} 100 101static inline float 102_GSRationalBezierEval(_GSRationalBezierDesc *rb, float t) 103{ 104 if (!rb->areBezierDescComputed) 105 _GSRationalBezierComputeBezierDesc(rb); 106 return _GSBezierEval(&(rb->n), t) / _GSBezierEval(&(rb->d), t); 107} 108 109static inline float 110_GSRationalBezierDerivEval(_GSRationalBezierDesc *rb, float t) 111{ 112 float h; 113 if (!rb->areBezierDescComputed) 114 _GSRationalBezierComputeBezierDesc(rb); 115 h = _GSBezierEval(&(rb->d), t); 116 return (_GSBezierDerivEval(&(rb->n), t) * h 117 - _GSBezierEval (&(rb->n), t) * _GSBezierDerivEval(&(rb->d), t)) 118 / (h*h); 119} 120 121static 122_NSAnimationCurveDesc _gs_animationCurveDesc[] = 123{ 124 // easeInOut : endGrad = startGrad & startGrad <= 1/3 125 { 0.0,1.0, 1.0/3,1.0/3 , {{2.0,2.0/3,2.0/3,2.0}} }, 126 // easeIn : endGrad = 1/startGrad & startGrad >= 1/6 127 { 0.0,1.0, 0.25,4.0 , {{4.0,3.0,2.0,1.0}} }, 128 // easeOut : endGrad = 1/startGrad & startGrad <= 6 129 { 0.0,1.0, 4.0 ,0.25, {{1.0,2.0,3.0,4.0}} }, 130 // linear (not used) 131 { 0.0,1.0, 1.0 ,1.0 , {{1.0,1.0,1.0,1.0}} }, 132 // speedInOut: endGrad = startGrad & startGrad >=3 133 { 0.0,1.0, 3.0 ,3.0 , {{2.0/3,2.0,2.0,2.0/3}} } 134}; 135 136/* Translate the NSAnimationCurveDesc data (start/end points and start/end 137 * gradients) to GSRBezier data (4 control points), then evaluate it. 138 */ 139static inline float 140_gs_animationValueForCurve(_NSAnimationCurveDesc *c, float t, float t0) 141{ 142 if (!c->isRBezierComputed) 143 { 144 c->rb.p[0] = c->s; 145 c->rb.p[1] = c->s + (c->sg*c->rb.w[0]) / (3*c->rb.w[1]); 146 c->rb.p[2] = c->e - (c->eg*c->rb.w[3]) / (3*c->rb.w[2]); 147 c->rb.p[3] = c->e; 148 _GSRationalBezierComputeBezierDesc (&c->rb); 149 c->isRBezierComputed = YES; 150 } 151 return _GSRationalBezierEval ( &(c->rb), (t-t0) / (1.0-t0) ); 152} 153 154@interface NSAnimation (PrivateNotificationCallbacks) 155- (void) _gs_startAnimationReachesProgressMark: (NSNotification*)notification; 156- (void) _gs_stopAnimationReachesProgressMark: (NSNotification*)notification; 157@end 158 159@interface NSAnimation (Private) 160- (void) _gs_didReachProgressMark: (NSAnimationProgress)progress; 161- (void) _gs_startAnimationInOwnLoop; 162- (void) _gs_startThreadedAnimation; 163- (_NSAnimationCurveDesc*) _gs_curveDesc; 164- (NSAnimationProgress) _gs_curveShift; 165@end 166 167NSComparisonResult 168nsanimation_progressMarkSorter(NSAnimationProgress first, NSAnimationProgress second) 169{ 170 float diff = first - second; 171 return (NSComparisonResult)(diff / fabs(diff)); 172} 173 174/* Thread locking/unlocking support macros. 175 * _isThreaded flag is an ivar that records whether the 176 * NSAnimation is running in thread mode. 177 * __gs_isLocked flag is local to each method and records 178 * whether the thread is locked and must be locked before 179 * the method exits. 180 * Both are needed because _isThreaded is reset when the 181 * NSAnimation stops : that may happen at any time between 182 * a lock/unlock pair. 183 */ 184#define _NSANIMATION_LOCKING_SETUP \ 185 BOOL __gs_isLocked = NO; 186 187#define _NSANIMATION_LOCK \ 188 if (_isThreaded) \ 189 { \ 190 NSAssert(__gs_isLocked == NO, NSInternalInconsistencyException); \ 191 NSDebugMLLog(@"NSAnimationLock",\ 192 @"LOCK %@", [NSThread currentThread]);\ 193 [_isAnimatingLock lock]; \ 194 __gs_isLocked = YES; \ 195 } 196 197#define _NSANIMATION_UNLOCK \ 198 if (__gs_isLocked) \ 199 { \ 200 /* NSAssert(__gs_isLocked == YES, NSInternalInconsistencyException); */ \ 201 NSDebugMLLog(@"NSAnimationLock",\ 202 @"UNLOCK %@", [NSThread currentThread]);\ 203 __gs_isLocked = NO; \ 204 [_isAnimatingLock unlock]; \ 205 } 206 207@implementation NSAnimation 208 209+ (void) initialize 210{ 211 unsigned i; 212 213 for (i = 0; i < 5; i++) // compute Bezier curve parameters... 214 _gs_animationValueForCurve(&_gs_animationCurveDesc[i], 0.0, 0.0); 215 _NSAnimationDefaultRunLoopModes 216 = [[NSArray alloc] initWithObjects: 217 NSDefaultRunLoopMode, 218 NSModalPanelRunLoopMode, 219 NSEventTrackingRunLoopMode, 220 nil]; 221} 222 223- (void) addProgressMark: (NSAnimationProgress)progress 224{ 225 _NSANIMATION_LOCKING_SETUP; 226 227 if (progress < 0.0) progress = 0.0; 228 if (progress > 1.0) progress = 1.0; 229 230 _NSANIMATION_LOCK; 231 if (GSIArrayCount(_progressMarks) == 0) 232 { // First mark 233 GSIArrayAddItem (_progressMarks,progress); 234 NSDebugMLLog (@"NSAnimationMark", 235 @"Insert 1st mark for %f (next:#%d)", 236 progress, _nextMark); 237 _nextMark = (progress >= [self currentProgress])? 0 : 1; 238 } 239 else 240 { 241 unsigned index; 242 index = GSIArrayInsertionPosition (_progressMarks, 243 progress, 244 &nsanimation_progressMarkSorter); 245 if (_nextMark < GSIArrayCount(_progressMarks)) 246 if (index <= _nextMark 247 && progress < GSIArrayItemAtIndex(_progressMarks,_nextMark)) 248 _nextMark++; 249 GSIArrayInsertItem (_progressMarks,progress,index); 250 NSDebugMLLog (@"NSAnimationMark", 251 @"Insert mark #%d/%d for %f (next:#%d)", 252 index,GSIArrayCount(_progressMarks),progress,_nextMark); 253 } 254 _isCachedProgressMarkNumbersValid = NO; 255 256 _NSANIMATION_UNLOCK; 257} 258 259- (NSAnimationBlockingMode) animationBlockingMode 260{ 261 NSAnimationBlockingMode m; 262 _NSANIMATION_LOCKING_SETUP; 263 264 _NSANIMATION_LOCK; 265 m = _blockingMode; 266 _NSANIMATION_UNLOCK; 267 return m; 268} 269 270- (NSAnimationCurve) animationCurve 271{ 272 NSAnimationCurve c; 273 _NSANIMATION_LOCKING_SETUP; 274 275 _NSANIMATION_LOCK; 276 c = _curve; 277 _NSANIMATION_UNLOCK; 278 return c; 279} 280 281- (void) clearStartAnimation 282{ 283 _NSANIMATION_LOCKING_SETUP; 284 285 _NSANIMATION_LOCK; 286 [[NSNotificationCenter defaultCenter] 287 removeObserver: self 288 name: NSAnimationProgressMarkNotification 289 object: _startAnimation]; 290 [_startAnimation removeProgressMark: _startMark]; 291 _startAnimation = nil; 292 _NSANIMATION_UNLOCK; 293} 294 295- (void) clearStopAnimation 296{ 297 _NSANIMATION_LOCKING_SETUP; 298 299 _NSANIMATION_LOCK; 300 [[NSNotificationCenter defaultCenter] 301 removeObserver: self 302 name: NSAnimationProgressMarkNotification 303 object: _stopAnimation]; 304 [_stopAnimation removeProgressMark: _stopMark]; 305 _stopAnimation = nil; 306 _NSANIMATION_UNLOCK; 307} 308 309- (NSAnimationProgress) currentProgress 310{ 311 NSAnimationProgress p; 312 _NSANIMATION_LOCKING_SETUP; 313 314 _NSANIMATION_LOCK; 315 p = _currentProgress; 316 _NSANIMATION_UNLOCK; 317 return p; 318} 319 320- (float) currentValue 321{ 322 float value; 323 _NSANIMATION_LOCKING_SETUP; 324 325 _NSANIMATION_LOCK; 326 327 if (_delegate_animationValueForProgress) 328 { // method is cached (the animation is running) 329 NSDebugMLLog (@"NSAnimationDelegate", 330 @"[delegate animationValueForProgress] (cached)"); 331 value = (*_delegate_animationValueForProgress) 332 (_currentDelegate, 333 @selector (animation:valueForProgress:), 334 self, _currentProgress); 335 } 336 else // method is not cached (the animation did not start yet) 337 if ( _delegate != nil 338 && [_delegate respondsToSelector: 339 @selector (animation:valueForProgress:)] ) 340 { 341 NSDebugMLLog (@"NSAnimationDelegate", 342 @"[delegate animationValueForProgress]"); 343 value = [_delegate animation: self 344 valueForProgress: _currentProgress]; 345 } 346 else // default -- FIXME ?? 347 /* switch (_curve) 348 { 349 case NSAnimationEaseInOut: 350 case NSAnimationEaseIn: 351 case NSAnimationEaseOut: 352 case NSAnimationSpeedInOut:*/ 353 value = _gs_animationValueForCurve ( 354 &_curveDesc, _currentProgress, _curveProgressShift 355 ); 356 /* break; 357 case NSAnimationLinear: 358 value = _currentProgress; break; 359 }*/ 360 361 _NSANIMATION_UNLOCK; 362 363 return value; 364} 365 366- (id) delegate 367{ 368 id d; 369 _NSANIMATION_LOCKING_SETUP; 370 371 _NSANIMATION_LOCK; 372 d = (_delegate == nil)? nil : _delegate; 373 _NSANIMATION_UNLOCK; 374 return d; 375} 376 377- (NSTimeInterval) duration 378{ 379 NSTimeInterval d; 380 _NSANIMATION_LOCKING_SETUP; 381 382 _NSANIMATION_LOCK; 383 d = _duration; 384 _NSANIMATION_UNLOCK; 385 return d; 386} 387 388- (float) frameRate 389{ 390 float f; 391 _NSANIMATION_LOCKING_SETUP; 392 393 _NSANIMATION_LOCK; 394 f = _frameRate; 395 _NSANIMATION_UNLOCK; 396 return f; 397} 398 399- (id) initWithDuration: (NSTimeInterval)duration 400 animationCurve: (NSAnimationCurve)curve 401{ 402 if ((self = [super init])) 403 { 404 if (duration<=0.0) 405 [NSException raise: NSInvalidArgumentException 406 format: @"%@ Duration must be > 0.0 (passed: %f)",self,duration]; 407 _duration = duration; 408 _frameRate = GS_ANIMATION_DEFAULT_FRAME_RATE; 409 _curve = curve; 410 _curveDesc = _gs_animationCurveDesc[_curve]; 411 _curveProgressShift = 0.0; 412 413 _currentProgress = 0.0; 414 _progressMarks = NSZoneMalloc ([self zone], sizeof(GSIArray_t)); 415 GSIArrayInitWithZoneAndCapacity (_progressMarks, [self zone], 16); 416 _cachedProgressMarkNumbers = NULL; 417 _cachedProgressMarkNumberCount = 0; 418 _isCachedProgressMarkNumbersValid = NO; 419 _nextMark = 0; 420 421 _startAnimation = _stopAnimation = nil; 422 _startMark = _stopMark = 0.0; 423 424 _blockingMode = NSAnimationBlocking; 425 _animator = nil; 426 _isANewAnimatorNeeded = YES; 427 428 _delegate = nil; 429 _delegate_animationDidReachProgressMark = 430 (void (*)(id,SEL,NSAnimation*,NSAnimationProgress)) NULL; 431 _delegate_animationValueForProgress = 432 (float (*)(id,SEL,NSAnimation*,NSAnimationProgress)) NULL; 433 _delegate_animationDidEnd = 434 (void (*)(id,SEL,NSAnimation*)) NULL; 435 _delegate_animationDidStop = 436 (void (*)(id,SEL,NSAnimation*)) NULL; 437 _delegate_animationShouldStart = 438 (BOOL (*)(id,SEL,NSAnimation*)) NULL; 439 440 _isThreaded = NO; 441 _isAnimatingLock = [GSLazyRecursiveLock new]; 442 } 443 return self; 444} 445 446- (id) copyWithZone: (NSZone*)zone 447{ 448 NSAnimation *c = (NSAnimation*)NSCopyObject (self, 0, zone); 449 450 c->_progressMarks = GSIArrayCopyWithZone(_progressMarks, zone); 451 c->_animator = nil; 452 c->_isANewAnimatorNeeded = YES; 453 c->_isAnimatingLock = [GSLazyRecursiveLock new]; 454 return c; 455} 456 457- (void) dealloc 458{ 459 [self stopAnimation]; 460 461 GSIArrayEmpty(_progressMarks); 462 NSZoneFree([self zone], _progressMarks); 463 if (_cachedProgressMarkNumbers != NULL) 464 { 465 unsigned i; 466 467 for (i = 0; i < _cachedProgressMarkNumberCount; i++) 468 RELEASE(_cachedProgressMarkNumbers[i]); 469 NSZoneFree([self zone], _cachedProgressMarkNumbers); 470 } 471 472 [self clearStartAnimation]; 473 [self clearStopAnimation]; 474 475 TEST_RELEASE(_animator); 476 RELEASE(_isAnimatingLock); 477 478 [super dealloc]; 479} 480 481- (BOOL) isAnimating 482{ 483 BOOL f; 484 _NSANIMATION_LOCKING_SETUP; 485 486 _NSANIMATION_LOCK; 487 f = (_animator != nil) ? [_animator isAnimationRunning] : NO; 488 _NSANIMATION_UNLOCK; 489 return f; 490} 491 492- (NSArray*) progressMarks 493{ 494 NSNumber **cpmn; 495 unsigned count; 496 _NSANIMATION_LOCKING_SETUP; 497 498 _NSANIMATION_LOCK; 499 500 count = GSIArrayCount(_progressMarks); 501 502 if (!_isCachedProgressMarkNumbersValid) 503 { 504 unsigned i; 505 506 if (_cachedProgressMarkNumbers != NULL) 507 { 508 for (i = 0; i < _cachedProgressMarkNumberCount; i++) 509 RELEASE(_cachedProgressMarkNumbers[i]); 510 _cachedProgressMarkNumbers = 511 (NSNumber**)NSZoneRealloc([self zone], _cachedProgressMarkNumbers, 512 count * sizeof(NSNumber*)); 513 } 514 else 515 { 516 _cachedProgressMarkNumbers = 517 (NSNumber**)NSZoneMalloc([self zone], count * sizeof(NSNumber*)); 518 } 519 520 for (i = 0; i < count; i++) 521 { 522 _cachedProgressMarkNumbers[i] = 523 [NSNumber numberWithFloat: GSIArrayItemAtIndex (_progressMarks,i)]; 524 } 525 _cachedProgressMarkNumberCount = count; 526 _isCachedProgressMarkNumbersValid = YES; 527 } 528 529 cpmn = _cachedProgressMarkNumbers; 530 _NSANIMATION_UNLOCK; 531 532 return [NSArray arrayWithObjects: cpmn count: count]; 533} 534 535- (void) removeProgressMark: (NSAnimationProgress)progress 536{ 537 NSUInteger index; 538 _NSANIMATION_LOCKING_SETUP; 539 540 _NSANIMATION_LOCK; 541 542 index = GSIArraySearch(_progressMarks, progress, 543 nsanimation_progressMarkSorter); 544 if (index < GSIArrayCount(_progressMarks) 545 && progress == GSIArrayItemAtIndex (_progressMarks,index)) 546 { 547 GSIArrayRemoveItemAtIndex(_progressMarks,index); 548 _isCachedProgressMarkNumbersValid = NO; 549 if (_nextMark > index) _nextMark--; 550 NSDebugMLLog(@"NSAnimationMark",@"Remove mark #%lu (%f) for (next:#%d)", 551 (unsigned long)index, progress, _nextMark); 552 } 553 else 554 NSWarnMLog(@"Unexistent progress mark"); 555 556 _NSANIMATION_UNLOCK; 557} 558 559- (NSArray*) runLoopModesForAnimating 560{ 561 return nil; 562} 563 564- (void) setAnimationBlockingMode: (NSAnimationBlockingMode)mode 565{ 566 _NSANIMATION_LOCKING_SETUP; 567 568 _NSANIMATION_LOCK; 569 _isANewAnimatorNeeded |= (_blockingMode != mode); 570 _blockingMode = mode; 571 _NSANIMATION_UNLOCK; 572} 573 574- (void) setAnimationCurve: (NSAnimationCurve)curve 575{ 576 _NSANIMATION_LOCKING_SETUP; 577 578 _NSANIMATION_LOCK; 579 580 if (_currentProgress <= 0.0f || _currentProgress >= 1.0f) 581 { 582 _curveDesc = _gs_animationCurveDesc[curve]; 583 } 584 else 585 { // FIXME ?? 586 _GSRationalBezierDesc newrb; 587 588 _GSRationalBezierDesc *rb1 = &(_curveDesc.rb); 589 float t1 = (_currentProgress - _curveProgressShift) / (1.0 - _curveProgressShift); 590 _GSRationalBezierDesc *rb2 = &(_gs_animationCurveDesc[curve].rb); 591 float t2 = _currentProgress; 592 float K; 593 newrb.p[0] = _GSRationalBezierEval ( rb1, t1 ); 594 newrb.w[0] = _GSBezierEval (&rb1->d,t1 ); 595 newrb.w[1] = 596 rb1->w[1] 597 + t1*( 2*( rb1->w[2] - rb1->w[1] ) 598 + t1*( rb1->w[1] - 2*rb1->w[2] + rb1->w[3] )); 599 newrb.p[1] = ( 600 rb1->w[1]*rb1->p[1] 601 + t1*( 2*( rb1->w[2]*rb1->p[2] - rb1->w[1]*rb1->p[1] ) 602 + t1*( rb1->w[1]*rb1->p[1] - 2*rb1->w[2]*rb1->p[2] + rb1->w[3]*rb1->p[3] )) 603 ) / newrb.w[1]; 604 newrb.w[2] = rb2->w[2] + t2*(rb2->w[3] - rb2->w[2] ); 605 newrb.p[2] = ( 606 rb2->w[2]*rb2->p[2] + t2*(rb2->w[3]*rb2->p[3] - rb2->w[2]*rb2->p[2]) 607 ) / newrb.w[2]; 608 609 // 3rd point is moved to the right by scaling : w3*p3 = w1*p1 + (w1*p1 - w0*p0) 610 K = ( 2*newrb.w[1]*newrb.p[1]-newrb.w[0]*newrb.p[0] ) / (newrb.w[2]*newrb.p[2]); 611 newrb.p[3] = rb2->p[3]; 612 newrb.w[3] = rb2->w[3] * K; 613 newrb.w[2] = newrb.w[2]* K; 614 615 _GSRationalBezierComputeBezierDesc (&newrb); 616#if 0 617 NSLog (@"prgrss = %f shift = %f",_currentProgress,_curveProgressShift); 618 switch (curve) 619 { case 0:NSLog (@"EaseInOut t=%f - %f",t1,t2);break; 620 case 1:NSLog (@"EaseIn t=%f - %f",t1,t2);break; 621 case 2:NSLog (@"EaseOut t=%f - %f",t1,t2);break; 622 case 3:NSLog (@"Linear t=%f - %f",t1,t2);break; 623 default:NSLog (@"???"); 624 } 625 NSLog (@"a=%f b=%f c=%f d=%f",newrb.p[0],newrb.p[1],newrb.p[2],newrb.p[3]); 626 NSLog (@" %f %f %f %f",newrb.w[0],newrb.w[1],newrb.w[2],newrb.w[3]); 627#endif 628 _curveProgressShift = _currentProgress; 629 _curveDesc.rb = newrb; 630 _curveDesc.isRBezierComputed = YES; 631 } 632 _curve = curve; 633 634 _NSANIMATION_UNLOCK; 635} 636 637- (void) setCurrentProgress: (NSAnimationProgress)progress 638{ 639 BOOL needSearchNextMark = NO; 640 NSAnimationProgress markedProgress; 641 _NSANIMATION_LOCKING_SETUP; 642 643 if (progress < 0.0) progress = 0.0; 644 if (progress > 1.0) progress = 1.0; 645 646 _NSANIMATION_LOCK; 647 648 // NOTE: In the case of a forward jump the marks between the 649 // previous progress value and the new (excluded) progress 650 // value are never reached. 651 // In the case of a backward jump (rewind) the marks will 652 // be reached again ! 653 if (_nextMark < GSIArrayCount(_progressMarks)) 654 { 655 markedProgress = GSIArrayItemAtIndex (_progressMarks,_nextMark); 656 if (markedProgress == progress) 657 [self _gs_didReachProgressMark: markedProgress]; 658 else 659 { 660 // the following should never happens if the progress 661 // is reached during the normal run of the animation 662 // (method called from animatorStep) 663 if (markedProgress < progress) // forward jump ? 664 needSearchNextMark = YES; 665 } 666 } 667 needSearchNextMark |= progress < _currentProgress; // rewind ? 668 669 if (needSearchNextMark) 670 { 671 _nextMark = GSIArrayInsertionPosition (_progressMarks,progress,&nsanimation_progressMarkSorter); 672 673 if (_nextMark < GSIArrayCount(_progressMarks)) 674 NSDebugMLLog(@"NSAnimationMark",@"Next mark #%d for %f", 675 _nextMark, GSIArrayItemAtIndex(_progressMarks,_nextMark)); 676 } 677 678 NSDebugMLLog(@"NSAnimation",@"Progress = %f", progress); 679 _currentProgress = progress; 680 681 if (progress >= 1.0 && _animator != nil) 682 [_animator stopAnimation]; 683 684 _NSANIMATION_UNLOCK; 685} 686 687- (void) setDelegate: (id)delegate 688{ 689 _NSANIMATION_LOCKING_SETUP; 690 691 _NSANIMATION_LOCK; 692 _delegate = (delegate == nil)? nil : delegate; 693 _NSANIMATION_UNLOCK; 694} 695 696- (void) setDuration: (NSTimeInterval)duration 697{ 698 _NSANIMATION_LOCKING_SETUP; 699 700 if (duration<=0.0) 701 [NSException raise: NSInvalidArgumentException 702 format: @"%@ Duration must be > 0.0 (passed: %f)",self,duration]; 703 _NSANIMATION_LOCK; 704 _duration = duration; 705 _NSANIMATION_UNLOCK; 706} 707 708- (void) setFrameRate: (float)fps 709{ 710 _NSANIMATION_LOCKING_SETUP; 711 712 if (fps<0.0) 713 [NSException raise: NSInvalidArgumentException 714 format: @"%@ Framerate must be >= 0.0 (passed: %f)",self,fps]; 715 _NSANIMATION_LOCK; 716 _isANewAnimatorNeeded |= (_frameRate != fps); 717 if ( _frameRate != fps && [self isAnimating] ) 718 { // a new animator is needed *now* 719 // FIXME : should I have been smarter ? 720 [self stopAnimation]; 721 [self startAnimation]; 722 } 723 _frameRate = fps; 724 _NSANIMATION_UNLOCK; 725} 726 727- (void) setProgressMarks: (NSArray*)marks 728{ 729 _NSANIMATION_LOCKING_SETUP; 730 731 _NSANIMATION_LOCK; 732 GSIArrayEmpty(_progressMarks); 733 _nextMark = 0; 734 if (marks != nil) 735 { 736 unsigned i, count = [marks count]; 737 738 for (i = 0; i < count; i++) 739 [self addProgressMark: [(NSNumber*)[marks objectAtIndex:i] floatValue]]; 740 } 741 _isCachedProgressMarkNumbersValid = NO; 742 _NSANIMATION_UNLOCK; 743} 744 745- (void) startAnimation 746{ 747 unsigned i; 748 749 if ([self isAnimating]) 750 return; 751 752 NSDebugMLLog(@"NSAnimationStart",@""); 753 754 for (i = 0; i < GSIArrayCount(_progressMarks); i++) 755 NSDebugMLLog(@"NSAnimationMark", @"Mark #%d : %f", 756 i, GSIArrayItemAtIndex(_progressMarks,i)); 757 758 if ([self currentProgress] >= 1.0) 759 { 760 [self setCurrentProgress: 0.0]; 761 _nextMark = 0; 762 } 763 764 _curveDesc = _gs_animationCurveDesc[_curve]; 765 _curveProgressShift = 0.0; 766 767 if (_delegate != nil) 768 { 769 id delegate; 770 771 NSDebugMLLog(@"NSAnimationDelegate", @"Cache delegation methods"); 772 // delegation methods are cached while the animation is running 773 delegate = _delegate; 774 _delegate_animationDidReachProgressMark = 775 ([delegate respondsToSelector: @selector (animation:didReachProgressMark:)]) ? 776 (void (*)(id,SEL,NSAnimation*,NSAnimationProgress)) 777 [delegate methodForSelector: @selector (animation:didReachProgressMark:)] 778 : NULL; 779 _delegate_animationValueForProgress = 780 ([delegate respondsToSelector: @selector (animation:valueForProgress:)]) ? 781 (float (*)(id,SEL,NSAnimation*,NSAnimationProgress)) 782 [delegate methodForSelector: @selector (animation:valueForProgress:)] 783 : NULL; 784 _delegate_animationDidEnd = 785 ([delegate respondsToSelector: @selector (animationDidEnd:)]) ? 786 (void (*)(id,SEL,NSAnimation*)) 787 [delegate methodForSelector: @selector (animationDidEnd:)] 788 : NULL; 789 _delegate_animationDidStop = 790 ([delegate respondsToSelector: @selector (animationDidStop:)]) ? 791 (void (*)(id,SEL,NSAnimation*)) 792 [delegate methodForSelector: @selector (animationDidStop:)] 793 : NULL; 794 _delegate_animationShouldStart = 795 ([delegate respondsToSelector: @selector (animationShouldStart:)]) ? 796 (BOOL (*)(id,SEL,NSAnimation*)) 797 [delegate methodForSelector: @selector (animationShouldStart:)] 798 : NULL; 799 NSDebugMLLog(@"NSAnimationDelegate", 800 @"Delegation methods : %p %p %p %p %p", 801 _delegate_animationDidReachProgressMark, 802 _delegate_animationValueForProgress, 803 _delegate_animationDidEnd, 804 _delegate_animationDidStop, 805 _delegate_animationShouldStart); 806 _currentDelegate = _delegate; 807 } 808 else 809 { 810 NSDebugMLLog(@"NSAnimationDelegate", 811 @" No delegate : clear delegation methods"); 812 _delegate_animationDidReachProgressMark = 813 (void (*)(id,SEL,NSAnimation*,NSAnimationProgress)) NULL; 814 _delegate_animationValueForProgress = 815 (float (*)(id,SEL,NSAnimation*,NSAnimationProgress)) NULL; 816 _delegate_animationDidEnd = 817 (void (*)(id,SEL,NSAnimation*)) NULL; 818 _delegate_animationDidStop = 819 (void (*)(id,SEL,NSAnimation*)) NULL; 820 _delegate_animationShouldStart = 821 (BOOL (*)(id,SEL,NSAnimation*)) NULL; 822 _currentDelegate = nil; 823 } 824 825 if (_animator == nil || _isANewAnimatorNeeded) 826 { 827 TEST_RELEASE(_animator); 828 829 _animator = [[GSAnimator allocWithZone: [self zone]] 830 initWithAnimation: self 831 frameRate: _frameRate]; 832 NSAssert(_animator,@"Can not create a GSAnimator"); 833 NSDebugMLLog(@"NSAnimationAnimator", @"New GSAnimator: %@", _animator); 834 _isANewAnimatorNeeded = NO; 835 } 836 837 switch (_blockingMode) 838 { 839 case NSAnimationBlocking: 840 [self _gs_startAnimationInOwnLoop]; 841 //[_animator setRunLoopModesForAnimating: 842 // [NSArray arrayWithObject: NSAnimationBlockingRunLoopMode]]; 843 //[_animator startAnimation]; 844 break; 845 case NSAnimationNonblocking: 846 { 847 NSArray *runLoopModes; 848 849 runLoopModes = [self runLoopModesForAnimating]; 850 if (runLoopModes == nil) 851 runLoopModes = _NSAnimationDefaultRunLoopModes; 852 [_animator setRunLoopModesForAnimating: runLoopModes]; 853 } 854 [_animator startAnimation]; 855 break; 856 case NSAnimationNonblockingThreaded: 857 _isThreaded = YES; 858 [NSThread 859 detachNewThreadSelector: @selector (_gs_startThreadedAnimation) 860 toTarget: self 861 withObject: nil]; 862 } 863} 864 865- (void) startWhenAnimation: (NSAnimation*)animation 866 reachesProgress: (NSAnimationProgress)start 867{ 868 _NSANIMATION_LOCKING_SETUP; 869 870 _NSANIMATION_LOCK; 871 872 _startAnimation = animation; 873 _startMark = start; 874 875 [_startAnimation addProgressMark: _startMark]; 876 NSDebugMLLog (@"NSAnimationMark",@"register for progress %f", start); 877 [[NSNotificationCenter defaultCenter] 878 addObserver: self 879 selector: @selector (_gs_startAnimationReachesProgressMark:) 880 name: NSAnimationProgressMarkNotification 881 object: _startAnimation]; 882 883 _NSANIMATION_UNLOCK; 884} 885 886- (void) stopAnimation 887{ 888 _NSANIMATION_LOCKING_SETUP; 889 890 if ([self isAnimating]) 891 { 892 _NSANIMATION_LOCK; 893 [_animator stopAnimation]; 894 _NSANIMATION_UNLOCK; 895 } 896} 897 898- (void) stopWhenAnimation: (NSAnimation*)animation 899 reachesProgress: (NSAnimationProgress)stop 900{ 901 _NSANIMATION_LOCKING_SETUP; 902 903 _NSANIMATION_LOCK; 904 905 _stopAnimation = animation; 906 _stopMark = stop; 907 908 [_stopAnimation addProgressMark: _stopMark]; 909 NSDebugMLLog (@"NSAnimationMark",@"register for progress %f", stop); 910 [[NSNotificationCenter defaultCenter] 911 addObserver: self 912 selector: @selector (_gs_stopAnimationReachesProgressMark:) 913 name: NSAnimationProgressMarkNotification 914 object: _stopAnimation]; 915 916 _NSANIMATION_UNLOCK; 917} 918 919- (void) encodeWithCoder: (NSCoder*)aCoder 920{ 921 if ([aCoder allowsKeyedCoding]) 922 { 923 [aCoder encodeInt: (int)[self animationCurve] 924 forKey: @"NSAnimationAnimationCurve"]; 925 [aCoder encodeDouble: [self duration] 926 forKey: @"NSAnimationDuration"]; 927 } 928 else 929 { 930 [self notImplemented: _cmd]; 931 } 932} 933 934- (id) initWithCoder: (NSCoder*)aCoder 935{ 936 if ([aCoder allowsKeyedCoding]) 937 { 938 NSTimeInterval duration = 1.0; 939 NSAnimationCurve animationCurve = 0; 940 941 if ([aCoder containsValueForKey: @"NSAnimationAnimationCurve"]) 942 { 943 animationCurve = [aCoder decodeIntForKey: @"NSAnimationAnimationCurve"]; 944 } 945 if ([aCoder containsValueForKey: @"NSAnimationDuration"]) 946 { 947 duration = [aCoder decodeDoubleForKey: @"NSAnimationDuration"]; 948 } 949 return [self initWithDuration: duration 950 animationCurve: animationCurve]; 951 } 952 else 953 { 954 [self notImplemented: _cmd]; 955 } 956 return self; 957} 958 959/* 960 * protocol GSAnimation (callbacks) 961 */ 962 963- (void) animatorDidStart 964{ 965 id delegate; 966 _NSANIMATION_LOCKING_SETUP; 967 968 NSDebugMLLog(@"NSAnimationAnimator",@""); 969 970 _NSANIMATION_LOCK; 971 972 delegate = _currentDelegate; 973 974 if (_delegate_animationShouldStart) // method is cached (the animation is running) 975 { 976 NSDebugMLLog(@"NSAnimationDelegate",@"[delegate animationShouldStart] (cached)"); 977 _delegate_animationShouldStart (delegate,@selector(animationShouldStart:),self); 978 } 979 RETAIN (self); 980 981 _NSANIMATION_UNLOCK; 982} 983 984- (void) animatorDidStop 985{ 986 id delegate; 987 _NSANIMATION_LOCKING_SETUP; 988 989 NSDebugMLLog(@"NSAnimationAnimator",@"Progress = %f", _currentProgress); 990 991 _NSANIMATION_LOCK; 992 993 delegate = _currentDelegate; 994 if (_currentProgress < 1.0) 995 { 996 if (_delegate_animationDidStop) // method is cached (the animation is running) 997 { 998 NSDebugMLLog(@"NSAnimationDelegate",@"[delegate animationDidStop] (cached)"); 999 _delegate_animationDidStop (delegate,@selector(animationDidStop:),self); 1000 } 1001 } 1002 else 1003 { 1004 if (_delegate_animationDidEnd) // method is cached (the animation is running) 1005 { 1006 NSDebugMLLog(@"NSAnimationDelegate",@"[delegate animationDidEnd] (cached)"); 1007 _delegate_animationDidEnd (delegate,@selector(animationDidEnd:),self); 1008 } 1009 } 1010 RELEASE (self); 1011 1012 _NSANIMATION_UNLOCK; 1013} 1014 1015- (void) animatorStep: (NSTimeInterval) elapsedTime; 1016{ 1017 NSAnimationProgress progress; 1018 _NSANIMATION_LOCKING_SETUP; 1019 1020 NSDebugMLLog(@"NSAnimationAnimator", @"Elapsed time : %f", elapsedTime); 1021 1022 _NSANIMATION_LOCK; 1023 1024 progress = (elapsedTime / _duration); 1025 1026 { // have some marks been passed ? 1027 // NOTE: the case where progress == markedProgress is 1028 // treated in [-setCurrentProgress] 1029 unsigned count = GSIArrayCount (_progressMarks); 1030 NSAnimationProgress markedProgress; 1031 while ( _nextMark < count 1032 && progress > (markedProgress = GSIArrayItemAtIndex (_progressMarks,_nextMark)) ) // is a mark reached ? 1033 { 1034 [self _gs_didReachProgressMark: markedProgress]; 1035 } 1036 } 1037 1038 [self setCurrentProgress: progress]; 1039 1040 _NSANIMATION_UNLOCK; 1041} 1042 1043@end //implementation NSAnimation 1044 1045@implementation NSAnimation (PrivateNotificationCallbacks) 1046 1047- (void) _gs_startAnimationReachesProgressMark: (NSNotification*)notification 1048{ 1049 NSAnimation *animation; 1050 NSAnimationProgress mark; 1051 _NSANIMATION_LOCKING_SETUP; 1052 1053 _NSANIMATION_LOCK; 1054 animation = [notification object]; 1055 mark = [[[notification userInfo] objectForKey: NSAnimationProgressMark] floatValue]; 1056 1057 NSDebugMLLog(@"NSAnimationMark", 1058 @"Start Animation %@ reaches %f", animation, mark); 1059 1060 if ( animation == _startAnimation && mark == _startMark) 1061 { 1062 // [self clearStartAnimation]; 1063 [self startAnimation]; 1064 } 1065 1066 _NSANIMATION_UNLOCK; 1067} 1068 1069 1070- (void) _gs_stopAnimationReachesProgressMark: (NSNotification*)notification 1071{ 1072 NSAnimation *animation; 1073 NSAnimationProgress mark; 1074 _NSANIMATION_LOCKING_SETUP; 1075 1076 _NSANIMATION_LOCK; 1077 animation = [notification object]; 1078 mark = [[[notification userInfo] objectForKey: NSAnimationProgressMark] floatValue]; 1079 1080 NSDebugMLLog(@"NSAnimationMark", 1081 @"Stop Animation %@ reaches %f",animation, mark); 1082 1083 1084 if ( animation == _stopAnimation && mark == _stopMark) 1085 { 1086 // [self clearStopAnimation]; 1087 [self stopAnimation]; 1088 } 1089 1090 _NSANIMATION_UNLOCK; 1091} 1092 1093@end // implementation NSAnimation (PrivateNotificationCallbacks) 1094 1095@implementation NSAnimation (Private) 1096 1097- (void) _gs_didReachProgressMark: (NSAnimationProgress) progress 1098{ 1099 _NSANIMATION_LOCKING_SETUP; 1100 1101 NSDebugMLLog(@"NSAnimationMark", @"progress %f", progress); 1102 1103 _NSANIMATION_LOCK; 1104 1105 // calls delegate's method 1106 if (_delegate_animationDidReachProgressMark) // method is cached (the animation is running) 1107 { 1108 NSDebugMLLog(@"NSAnimationDelegate", 1109 @"[delegate animationdidReachProgressMark] (cached)"); 1110 _delegate_animationDidReachProgressMark (_currentDelegate, 1111 @selector(animation:didReachProgressMark:), 1112 self,progress); 1113 } 1114 else // method is not cached (the animation did not start yet) 1115 if ( _delegate != nil 1116 && [_delegate 1117 respondsToSelector: @selector(animation:didReachProgressMark:)] ) 1118 { 1119 NSDebugMLLog(@"NSAnimationDelegate", 1120 @"[delegate animationdidReachProgressMark]"); 1121 [_delegate animation: self didReachProgressMark: progress]; 1122 } 1123 1124 // posts a notification 1125 NSDebugMLLog(@"NSAnimationNotification", 1126 @"Post NSAnimationProgressMarkNotification : %f", progress); 1127 [[NSNotificationCenter defaultCenter] 1128 postNotificationName: NSAnimationProgressMarkNotification 1129 object: self 1130 userInfo: [NSDictionary 1131 dictionaryWithObject: [NSNumber numberWithFloat: progress] 1132 forKey: NSAnimationProgressMark 1133 ] 1134 ]; 1135 1136 // skips marks with the same progress value 1137 while ( 1138 (++_nextMark) < GSIArrayCount(_progressMarks) 1139 && GSIArrayItemAtIndex(_progressMarks, _nextMark) == progress 1140 ) 1141 ; 1142 1143 _NSANIMATION_UNLOCK; 1144 1145 NSDebugMLLog(@"NSAnimationMark", 1146 @"Next mark #%d for %f", 1147 _nextMark, GSIArrayItemAtIndex(_progressMarks, _nextMark - 1)); 1148} 1149 1150- (void) _gs_startThreadedAnimation 1151{ 1152 // NSAssert(_isThreaded); 1153 CREATE_AUTORELEASE_POOL(pool); 1154 NSDebugMLLog(@"NSAnimationThread", 1155 @"Start of %@", [NSThread currentThread]); 1156 [self _gs_startAnimationInOwnLoop]; 1157 NSDebugMLLog(@"NSAnimationThread", 1158 @"End of %@", [NSThread currentThread]); 1159 [pool drain]; 1160 _isThreaded = NO; 1161} 1162 1163 1164- (void) _gs_startAnimationInOwnLoop 1165{ 1166 NSRunLoop *loop; 1167 NSDate *end; 1168 1169 [_animator setRunLoopModesForAnimating: 1170 [NSArray arrayWithObject: NSAnimationBlockingRunLoopMode]]; 1171 [_animator startAnimation]; 1172 loop = [NSRunLoop currentRunLoop]; 1173 end = [NSDate distantFuture]; 1174 for (;;) 1175 { 1176 if ([loop runMode: NSAnimationBlockingRunLoopMode beforeDate: end] == NO) 1177 { 1178 NSDate *d; 1179 CREATE_AUTORELEASE_POOL(pool); 1180 1181 d = [loop limitDateForMode: NSAnimationBlockingRunLoopMode]; 1182 if (d == nil) 1183 { 1184 [pool drain]; 1185 break; // No inputs and no timers. 1186 } 1187 [NSThread sleepUntilDate: d]; 1188 [pool drain]; 1189 } 1190 } 1191} 1192 1193- (_NSAnimationCurveDesc*) _gs_curveDesc 1194{ return &self->_curveDesc; } 1195 1196- (NSAnimationProgress) _gs_curveShift 1197{ return _curveProgressShift; } 1198 1199@end // implementation NSAnimation (Private) 1200 1201@implementation NSAnimation (GNUstep) 1202 1203- (unsigned int) frameCount 1204{ 1205 unsigned c; 1206 _NSANIMATION_LOCKING_SETUP; 1207 1208 _NSANIMATION_LOCK; 1209 c = (_animator != nil)? [_animator frameCount] : 0; 1210 _NSANIMATION_UNLOCK; 1211 return c; 1212} 1213 1214- (void) resetCounters 1215{ 1216 _NSANIMATION_LOCKING_SETUP; 1217 1218 _NSANIMATION_LOCK; 1219 if (_animator != nil) [_animator resetCounters]; 1220 _NSANIMATION_UNLOCK; 1221} 1222 1223- (float) actualFrameRate; 1224{ 1225 float r; 1226 _NSANIMATION_LOCKING_SETUP; 1227 1228 _NSANIMATION_LOCK; 1229 r = (_animator != nil)? [_animator frameRate] : 0.0; 1230 _NSANIMATION_UNLOCK; 1231 return r; 1232} 1233 1234@end 1235 1236/*=======================* 1237 * NSViewAnimation class * 1238 *=======================*/ 1239 1240@interface _GSViewAnimationBaseDesc : NSObject 1241{ 1242 id _target; 1243 NSRect _startFrame; 1244 NSRect _endFrame; 1245 NSString* _effect; 1246} 1247 1248- (id) initWithProperties: (NSDictionary*)properties; 1249- (void) setCurrentProgress: (float)progress; 1250- (void) setTargetFrame: (NSRect) frame; 1251 1252@end 1253 1254@interface _GSViewAnimationDesc : _GSViewAnimationBaseDesc 1255{ 1256 BOOL _shouldHide; 1257 BOOL _shouldUnhide; 1258} 1259@end 1260 1261@interface _GSWindowAnimationDesc : _GSViewAnimationBaseDesc 1262{ 1263 float _startAlpha; 1264} 1265@end 1266 1267@implementation _GSViewAnimationBaseDesc 1268 1269- (id) initWithProperties: (NSDictionary*)properties 1270{ 1271 if ([self isMemberOfClass: [_GSViewAnimationBaseDesc class]]) 1272 { 1273 NSZone* zone; 1274 id target; 1275 1276 zone = [self zone]; 1277 RELEASE (self); 1278 target = [properties objectForKey: NSViewAnimationTargetKey]; 1279 if (target != nil) 1280 { 1281 if ([target isKindOfClass: [NSView class]]) 1282 self = [[_GSViewAnimationDesc allocWithZone: zone] 1283 initWithProperties: properties]; 1284 else if ([target isKindOfClass: [NSWindow class]]) 1285 self = [(_GSWindowAnimationDesc*)[_GSWindowAnimationDesc allocWithZone: zone] 1286 initWithProperties: properties]; 1287 else 1288 [NSException 1289 raise: NSInvalidArgumentException 1290 format: @"Invalid viewAnimation property :" 1291 @"target is neither a NSView nor a NSWindow"]; 1292 } 1293 else 1294 [NSException 1295 raise: NSInvalidArgumentException 1296 format: @"Invalid viewAnimation property :" 1297 @"target is nil"]; 1298 } 1299 else 1300 { // called from a subclass 1301 if ((self = [super init])) 1302 { 1303 NSValue* startValue; 1304 NSValue* endValue; 1305 _target = [properties objectForKey: NSViewAnimationTargetKey]; 1306 startValue = [properties objectForKey: NSViewAnimationStartFrameKey]; 1307 endValue = [properties objectForKey: NSViewAnimationEndFrameKey]; 1308 _effect = [properties objectForKey: NSViewAnimationEffectKey]; 1309 1310 _startFrame = (startValue!=nil) ? 1311 [startValue rectValue] 1312 : [_target frame]; 1313 _endFrame = (endValue!=nil) ? 1314 [endValue rectValue] 1315 : [_target frame]; 1316 } 1317 } 1318 return self; 1319} 1320 1321- (void) setCurrentProgress: (float)progress 1322{ 1323 if (progress < 1.0f) 1324 { 1325 NSRect r; 1326 1327 r.origin.x = _startFrame.origin.x 1328 + progress*( _endFrame.origin.x - _startFrame.origin.x ); 1329 r.origin.y = _startFrame.origin.y 1330 + progress*( _endFrame.origin.y - _startFrame.origin.y ); 1331 r.size.width = _startFrame.size.width 1332 + progress*( _endFrame.size.width - _startFrame.size.width ); 1333 r.size.height = _startFrame.size.height 1334 + progress*( _endFrame.size.height - _startFrame.size.height ); 1335 1336 [self setTargetFrame: r]; 1337 1338 if (_effect == NSViewAnimationFadeOutEffect) 1339 { 1340 [self subclassResponsibility: _cmd]; 1341 } 1342 if (_effect == NSViewAnimationFadeInEffect) 1343 { 1344 [self subclassResponsibility: _cmd]; 1345 } 1346 } 1347 else 1348 { 1349 [self setTargetFrame: _endFrame]; 1350 } 1351} 1352 1353- (void) setTargetFrame: (NSRect)frame 1354{ 1355 [self subclassResponsibility: _cmd]; 1356} 1357 1358@end // implementation _GSViewAnimationDesc 1359 1360@implementation _GSViewAnimationDesc 1361 1362- (id) initWithProperties: (NSDictionary*)properties 1363{ 1364 if ((self = [super initWithProperties: properties])) 1365 { 1366 _shouldHide = ([properties objectForKey: NSViewAnimationEndFrameKey] == nil); 1367 _shouldUnhide = ( _effect == NSViewAnimationFadeInEffect 1368 && [_target isHidden] 1369 && !_shouldHide); 1370 } 1371 return self; 1372} 1373 1374- (void) setCurrentProgress: (float)progress 1375{ 1376 [super setCurrentProgress: progress]; 1377 if (_effect == NSViewAnimationFadeOutEffect) {} 1378 /* ??? TODO */; 1379 if (_effect == NSViewAnimationFadeInEffect) {} 1380 /* ??? TODO */; 1381 1382 if (progress>=1.0f) 1383 { 1384 if (_shouldHide) 1385 [_target setHidden:YES]; 1386 else if (_shouldUnhide) 1387 [_target setHidden:NO]; 1388 } 1389} 1390 1391- (void) setTargetFrame: (NSRect)frame 1392{ 1393 [_target setFrame: frame]; 1394} 1395 1396@end // implementation _GSViewAnimationDesc 1397 1398@implementation _GSWindowAnimationDesc 1399 1400- (id) initWithProperties: (NSDictionary*)properties 1401{ 1402 if ((self = [super initWithProperties: properties])) 1403 { 1404 _startAlpha = [_target alphaValue]; 1405 } 1406 return self; 1407} 1408 1409- (void) setCurrentProgress: (float)progress 1410{ 1411 [super setCurrentProgress: progress]; 1412 if (_effect == NSViewAnimationFadeOutEffect) 1413 [_target setAlphaValue: _startAlpha * (1.0f - progress)]; 1414 if (_effect == NSViewAnimationFadeInEffect) 1415 [_target setAlphaValue: _startAlpha + (1.0f - _startAlpha) * progress]; 1416 1417 if (progress >= 1.0f) 1418 { 1419 if (_effect == NSViewAnimationFadeOutEffect) 1420 [_target orderBack: self]; 1421 if (_effect == NSViewAnimationFadeInEffect) 1422 [_target orderFront: self]; 1423 } 1424} 1425 1426- (void) setTargetFrame: (NSRect) frame 1427{ 1428 [_target setFrame: frame display: YES]; 1429} 1430 1431@end // implementation _GSWindowAnimationDesc 1432 1433@implementation NSViewAnimation 1434 1435- (id) initWithViewAnimations: (NSArray*)animations 1436{ 1437 self = [self initWithDuration: 0.5 animationCurve: NSAnimationEaseInOut]; 1438 if (self) 1439 { 1440 [self setAnimationBlockingMode: NSAnimationNonblocking]; 1441 [self setViewAnimations: animations]; 1442 } 1443 return self; 1444} 1445 1446- (void) dealloc 1447{ 1448 DESTROY(_viewAnimations); 1449 DESTROY(_viewAnimationDesc); 1450 [super dealloc]; 1451} 1452 1453- (void) setViewAnimations: (NSArray*)animations 1454{ 1455 _NSANIMATION_LOCKING_SETUP; 1456 1457 _NSANIMATION_LOCK; 1458 if (_viewAnimations != animations) 1459 DESTROY(_viewAnimationDesc); 1460 ASSIGN(_viewAnimations, animations) ; 1461 _NSANIMATION_UNLOCK; 1462} 1463 1464- (NSArray*) viewAnimations 1465{ 1466 NSArray *a; 1467 _NSANIMATION_LOCKING_SETUP; 1468 1469 _NSANIMATION_LOCK; 1470 a = _viewAnimations; 1471 _NSANIMATION_UNLOCK; 1472 return a; 1473} 1474 1475- (void) startAnimation 1476{ 1477 _NSANIMATION_LOCKING_SETUP; 1478 1479 _NSANIMATION_LOCK; 1480 if (_viewAnimationDesc == nil) 1481 { 1482 unsigned int i, c; 1483 1484 c = [_viewAnimations count]; 1485 _viewAnimationDesc = [[NSMutableArray alloc] initWithCapacity: c]; 1486 for (i = 0; i < c; i++) 1487 { 1488 _GSViewAnimationBaseDesc *vabd; 1489 1490 vabd = [[_GSViewAnimationBaseDesc alloc] 1491 initWithProperties: [_viewAnimations objectAtIndex:i]]; 1492 [_viewAnimationDesc addObject: vabd]; 1493 RELEASE(vabd); 1494 } 1495 } 1496 [super startAnimation]; 1497 _NSANIMATION_UNLOCK; 1498} 1499 1500- (void) stopAnimation 1501{ 1502 _NSANIMATION_LOCKING_SETUP; 1503 1504 _NSANIMATION_LOCK; 1505 [super stopAnimation]; 1506 [self setCurrentProgress: 1.0]; 1507 _NSANIMATION_UNLOCK; 1508} 1509 1510- (void) _gs_updateViewsWithValue: (NSNumber*) value 1511{ 1512 // Runs in main thread : must not call any NSAnimation method to avoid a deadlock 1513 unsigned int i, c; 1514 float v; 1515 1516 v = [value floatValue]; 1517 if (_viewAnimationDesc != nil) 1518 for (i = 0, c = [_viewAnimationDesc count]; i < c; i++) 1519 [[_viewAnimationDesc objectAtIndex: i] setCurrentProgress: v]; 1520} 1521 1522 1523- (void) setCurrentProgress: (NSAnimationProgress)progress 1524{ 1525 _NSANIMATION_LOCKING_SETUP; 1526 1527 _NSANIMATION_LOCK; 1528 [super setCurrentProgress: progress]; 1529 [self performSelectorOnMainThread: @selector (_gs_updateViewsWithValue:) 1530 withObject: [NSNumber numberWithFloat:[self currentValue]] 1531 waitUntilDone: YES]; 1532 _NSANIMATION_UNLOCK; 1533} 1534 1535@end // implementation NSViewAnimation 1536 1537