1/*
2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "WebWindowAnimation.h"
27#import "WebKitSystemInterface.h"
28#import <wtf/Assertions.h>
29
30static const CGFloat slowMotionFactor = 10.;
31
32static NSTimeInterval WebWindowAnimationDurationFromDuration(NSTimeInterval duration)
33{
34    return ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) ? duration * slowMotionFactor : duration;
35}
36
37static NSRect scaledRect(NSRect _initialFrame, NSRect _finalFrame, double factor)
38{
39    NSRect currentRect = _initialFrame;
40    currentRect.origin.x += (NSMinX(_finalFrame) - NSMinX(_initialFrame)) * factor;
41    currentRect.origin.y += (NSMinY(_finalFrame) - NSMinY(_initialFrame)) * factor;
42    currentRect.size.width += (NSWidth(_finalFrame) - NSWidth(_initialFrame)) * factor;
43    currentRect.size.height += (NSHeight(_finalFrame) - NSHeight(_initialFrame)) * factor;
44    return currentRect;
45}
46
47static CGFloat squaredDistance(NSPoint point1, NSPoint point2)
48{
49    CGFloat deltaX = point1.x - point2.x;
50    CGFloat deltaY = point1.y - point2.y;
51    return deltaX * deltaX + deltaY * deltaY;
52}
53
54@implementation WebWindowScaleAnimation
55
56- (id)init
57{
58    self = [super init];
59    if (!self)
60        return nil;
61#ifndef BUILDING_ON_TIGER
62    [self setAnimationBlockingMode:NSAnimationNonblockingThreaded];
63#endif
64    [self setFrameRate:60.];
65    return self;
66}
67
68- (id)initWithHintedDuration:(NSTimeInterval)duration window:(NSWindow *)window initalFrame:(NSRect)initialFrame finalFrame:(NSRect)finalFrame
69{
70    self = [self init];
71    if (!self)
72        return nil;
73    _hintedDuration = duration;
74    _window = window;
75    _initialFrame = initialFrame;
76    _finalFrame = finalFrame;
77    _realFrame = [window frame];
78    return self;
79}
80
81- (void) dealloc
82{
83    [_subAnimation release];
84    [super dealloc];
85}
86
87- (void)setDuration:(NSTimeInterval)duration
88{
89    [super setDuration:WebWindowAnimationDurationFromDuration(duration)];
90}
91
92- (void)setWindow:(NSWindow *)window
93{
94    _window = window;
95}
96
97- (float)currentValue
98{
99    return 0.5 - 0.5 * cos(M_PI * (1 - [self currentProgress]));
100}
101
102- (NSRect)currentFrame
103{
104    return scaledRect(_finalFrame, _initialFrame, [self currentValue]);
105}
106
107- (void)setCurrentProgress:(NSAnimationProgress)progress
108{
109    if (!_window)
110        return;
111
112    [super setCurrentProgress:progress];
113
114    NSRect currentRect = [self currentFrame];
115#ifndef BUILDING_ON_TIGER
116    WKWindowSetScaledFrame(_window, currentRect, _realFrame);
117#else
118    [_window setFrame:currentRect display:YES];
119#endif
120    [_subAnimation setCurrentProgress:progress];
121}
122
123- (void)setSubAnimation:(NSAnimation *)animation
124{
125    id oldAnimation = _subAnimation;
126    _subAnimation = [animation retain];
127    [oldAnimation release];
128}
129
130- (NSTimeInterval)additionalDurationNeededToReachFinalFrame
131{
132    static const CGFloat maxAdditionalDuration = 1.0;
133    static const CGFloat speedFactor = 0.0001;
134
135    CGFloat maxDist = squaredDistance(_initialFrame.origin, _finalFrame.origin);
136    CGFloat dist;
137
138    dist = squaredDistance(NSMakePoint(NSMaxX(_initialFrame), NSMinY(_initialFrame)), NSMakePoint(NSMaxX(_finalFrame), NSMinY(_finalFrame)));
139    if (dist > maxDist)
140        maxDist = dist;
141
142    dist = squaredDistance(NSMakePoint(NSMaxX(_initialFrame), NSMaxY(_initialFrame)), NSMakePoint(NSMaxX(_finalFrame), NSMaxY(_finalFrame)));
143    if (dist > maxDist)
144        maxDist = dist;
145
146    dist = squaredDistance(NSMakePoint(NSMinX(_initialFrame), NSMinY(_initialFrame)), NSMakePoint(NSMinX(_finalFrame), NSMinY(_finalFrame)));
147    if (dist > maxDist)
148        maxDist = dist;
149
150    return MIN(sqrt(maxDist) * speedFactor, maxAdditionalDuration);
151}
152
153- (void)startAnimation
154{
155    // Compute extra time
156    if (_hintedDuration)
157        [self setDuration:_hintedDuration + [self additionalDurationNeededToReachFinalFrame]];
158    [super startAnimation];
159}
160
161- (void)stopAnimation
162{
163    _window = nil;
164    [super stopAnimation];
165    [_subAnimation stopAnimation];
166}
167
168@end
169
170@implementation WebWindowFadeAnimation
171
172- (id)init
173{
174    self = [super init];
175    if (!self)
176        return nil;
177#ifndef BUILDING_ON_TIGER
178    [self setAnimationBlockingMode:NSAnimationNonblockingThreaded];
179#endif
180    [self setFrameRate:60];
181    [self setAnimationCurve:NSAnimationEaseInOut];
182    return self;
183}
184
185- (id)initWithDuration:(NSTimeInterval)duration window:(NSWindow *)window initialAlpha:(CGFloat)initialAlpha finalAlpha:(CGFloat)finalAlpha
186{
187    self = [self init];
188    if (!self)
189        return nil;
190    _window = window;
191    _initialAlpha = initialAlpha;
192    _finalAlpha = finalAlpha;
193    return self;
194}
195
196- (void)setDuration:(NSTimeInterval)duration
197{
198    [super setDuration:WebWindowAnimationDurationFromDuration(duration)];
199}
200
201- (CGFloat)currentAlpha
202{
203    return MAX(0.0, MIN(1.0, _initialAlpha + [self currentValue] * (_finalAlpha - _initialAlpha)));
204}
205
206- (void)setCurrentProgress:(NSAnimationProgress)progress
207{
208    if (_isStopped)
209        return;
210
211    ASSERT(_window);
212    [super setCurrentProgress:progress];
213
214#ifndef BUILDING_ON_TIGER
215    WKWindowSetAlpha(_window, [self currentAlpha]);
216#else
217    [_window setAlphaValue:[self currentAlpha]];
218#endif
219}
220
221- (void)setWindow:(NSWindow*)window
222{
223    _window = window;
224}
225
226- (void)stopAnimation
227{
228    // This is relevant when we are a sub animation of a scale animation.
229    // In this case we are hosted in the animated thread of the parent
230    // and even after [super stopAnimation], the parent might call
231    // setCurrrentProgress.
232    _isStopped = YES;
233
234    [super stopAnimation];
235}
236
237@end
238
239