| Index: chrome/browser/ui/cocoa/throbber_view.mm
|
| ===================================================================
|
| --- chrome/browser/ui/cocoa/throbber_view.mm (revision 71805)
|
| +++ chrome/browser/ui/cocoa/throbber_view.mm (working copy)
|
| @@ -1,372 +0,0 @@
|
| -// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#import "chrome/browser/ui/cocoa/throbber_view.h"
|
| -
|
| -#include <set>
|
| -
|
| -#include "base/logging.h"
|
| -
|
| -static const float kAnimationIntervalSeconds = 0.03; // 30ms, same as windows
|
| -
|
| -@interface ThrobberView(PrivateMethods)
|
| -- (id)initWithFrame:(NSRect)frame delegate:(id<ThrobberDataDelegate>)delegate;
|
| -- (void)maintainTimer;
|
| -- (void)animate;
|
| -@end
|
| -
|
| -@protocol ThrobberDataDelegate <NSObject>
|
| -// Is the current frame the last frame of the animation?
|
| -- (BOOL)animationIsComplete;
|
| -
|
| -// Draw the current frame into the current graphics context.
|
| -- (void)drawFrameInRect:(NSRect)rect;
|
| -
|
| -// Update the frame counter.
|
| -- (void)advanceFrame;
|
| -@end
|
| -
|
| -@interface ThrobberFilmstripDelegate : NSObject
|
| - <ThrobberDataDelegate> {
|
| - scoped_nsobject<NSImage> image_;
|
| - unsigned int numFrames_; // Number of frames in this animation.
|
| - unsigned int animationFrame_; // Current frame of the animation,
|
| - // [0..numFrames_)
|
| -}
|
| -
|
| -- (id)initWithImage:(NSImage*)image;
|
| -
|
| -@end
|
| -
|
| -@implementation ThrobberFilmstripDelegate
|
| -
|
| -- (id)initWithImage:(NSImage*)image {
|
| - if ((self = [super init])) {
|
| - // Reset the animation counter so there's no chance we are off the end.
|
| - animationFrame_ = 0;
|
| -
|
| - // Ensure that the height divides evenly into the width. Cache the
|
| - // number of frames in the animation for later.
|
| - NSSize imageSize = [image size];
|
| - DCHECK(imageSize.height && imageSize.width);
|
| - if (!imageSize.height)
|
| - return nil;
|
| - DCHECK((int)imageSize.width % (int)imageSize.height == 0);
|
| - numFrames_ = (int)imageSize.width / (int)imageSize.height;
|
| - DCHECK(numFrames_);
|
| - image_.reset([image retain]);
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (BOOL)animationIsComplete {
|
| - return NO;
|
| -}
|
| -
|
| -- (void)drawFrameInRect:(NSRect)rect {
|
| - float imageDimension = [image_ size].height;
|
| - float xOffset = animationFrame_ * imageDimension;
|
| - NSRect sourceImageRect =
|
| - NSMakeRect(xOffset, 0, imageDimension, imageDimension);
|
| - [image_ drawInRect:rect
|
| - fromRect:sourceImageRect
|
| - operation:NSCompositeSourceOver
|
| - fraction:1.0];
|
| -}
|
| -
|
| -- (void)advanceFrame {
|
| - animationFrame_ = ++animationFrame_ % numFrames_;
|
| -}
|
| -
|
| -@end
|
| -
|
| -@interface ThrobberToastDelegate : NSObject
|
| - <ThrobberDataDelegate> {
|
| - scoped_nsobject<NSImage> image1_;
|
| - scoped_nsobject<NSImage> image2_;
|
| - NSSize image1Size_;
|
| - NSSize image2Size_;
|
| - int animationFrame_; // Current frame of the animation,
|
| -}
|
| -
|
| -- (id)initWithImage1:(NSImage*)image1 image2:(NSImage*)image2;
|
| -
|
| -@end
|
| -
|
| -@implementation ThrobberToastDelegate
|
| -
|
| -- (id)initWithImage1:(NSImage*)image1 image2:(NSImage*)image2 {
|
| - if ((self = [super init])) {
|
| - image1_.reset([image1 retain]);
|
| - image2_.reset([image2 retain]);
|
| - image1Size_ = [image1 size];
|
| - image2Size_ = [image2 size];
|
| - animationFrame_ = 0;
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (BOOL)animationIsComplete {
|
| - if (animationFrame_ >= image1Size_.height + image2Size_.height)
|
| - return YES;
|
| -
|
| - return NO;
|
| -}
|
| -
|
| -// From [0..image1Height) we draw image1, at image1Height we draw nothing, and
|
| -// from [image1Height+1..image1Hight+image2Height] we draw the second image.
|
| -- (void)drawFrameInRect:(NSRect)rect {
|
| - NSImage* image = nil;
|
| - NSSize srcSize;
|
| - NSRect destRect;
|
| -
|
| - if (animationFrame_ < image1Size_.height) {
|
| - image = image1_.get();
|
| - srcSize = image1Size_;
|
| - destRect = NSMakeRect(0, -animationFrame_,
|
| - image1Size_.width, image1Size_.height);
|
| - } else if (animationFrame_ == image1Size_.height) {
|
| - // nothing; intermediate blank frame
|
| - } else {
|
| - image = image2_.get();
|
| - srcSize = image2Size_;
|
| - destRect = NSMakeRect(0, animationFrame_ -
|
| - (image1Size_.height + image2Size_.height),
|
| - image2Size_.width, image2Size_.height);
|
| - }
|
| -
|
| - if (image) {
|
| - NSRect sourceImageRect =
|
| - NSMakeRect(0, 0, srcSize.width, srcSize.height);
|
| - [image drawInRect:destRect
|
| - fromRect:sourceImageRect
|
| - operation:NSCompositeSourceOver
|
| - fraction:1.0];
|
| - }
|
| -}
|
| -
|
| -- (void)advanceFrame {
|
| - ++animationFrame_;
|
| -}
|
| -
|
| -@end
|
| -
|
| -typedef std::set<ThrobberView*> ThrobberSet;
|
| -
|
| -// ThrobberTimer manages the animation of a set of ThrobberViews. It allows
|
| -// a single timer instance to be shared among as many ThrobberViews as needed.
|
| -@interface ThrobberTimer : NSObject {
|
| - @private
|
| - // A set of weak references to each ThrobberView that should be notified
|
| - // whenever the timer fires.
|
| - ThrobberSet throbbers_;
|
| -
|
| - // Weak reference to the timer that calls back to this object. The timer
|
| - // retains this object.
|
| - NSTimer* timer_;
|
| -
|
| - // Whether the timer is actively running. To avoid timer construction
|
| - // and destruction overhead, the timer is not invalidated when it is not
|
| - // needed, but its next-fire date is set to [NSDate distantFuture].
|
| - // It is not possible to determine whether the timer has been suspended by
|
| - // comparing its fireDate to [NSDate distantFuture], though, so a separate
|
| - // variable is used to track this state.
|
| - BOOL timerRunning_;
|
| -
|
| - // The thread that created this object. Used to validate that ThrobberViews
|
| - // are only added and removed on the same thread that the fire action will
|
| - // be performed on.
|
| - NSThread* validThread_;
|
| -}
|
| -
|
| -// Returns a shared ThrobberTimer. Everyone is expected to use the same
|
| -// instance.
|
| -+ (ThrobberTimer*)sharedThrobberTimer;
|
| -
|
| -// Invalidates the timer, which will cause it to remove itself from the run
|
| -// loop. This causes the timer to be released, and it should then release
|
| -// this object.
|
| -- (void)invalidate;
|
| -
|
| -// Adds or removes ThrobberView objects from the throbbers_ set.
|
| -- (void)addThrobber:(ThrobberView*)throbber;
|
| -- (void)removeThrobber:(ThrobberView*)throbber;
|
| -@end
|
| -
|
| -@interface ThrobberTimer(PrivateMethods)
|
| -// Starts or stops the timer as needed as ThrobberViews are added and removed
|
| -// from the throbbers_ set.
|
| -- (void)maintainTimer;
|
| -
|
| -// Calls animate on each ThrobberView in the throbbers_ set.
|
| -- (void)fire:(NSTimer*)timer;
|
| -@end
|
| -
|
| -@implementation ThrobberTimer
|
| -- (id)init {
|
| - if ((self = [super init])) {
|
| - // Start out with a timer that fires at the appropriate interval, but
|
| - // prevent it from firing by setting its next-fire date to the distant
|
| - // future. Once a ThrobberView is added, the timer will be allowed to
|
| - // start firing.
|
| - timer_ = [NSTimer scheduledTimerWithTimeInterval:kAnimationIntervalSeconds
|
| - target:self
|
| - selector:@selector(fire:)
|
| - userInfo:nil
|
| - repeats:YES];
|
| - [timer_ setFireDate:[NSDate distantFuture]];
|
| - timerRunning_ = NO;
|
| -
|
| - validThread_ = [NSThread currentThread];
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -+ (ThrobberTimer*)sharedThrobberTimer {
|
| - // Leaked. That's OK, it's scoped to the lifetime of the application.
|
| - static ThrobberTimer* sharedInstance = [[ThrobberTimer alloc] init];
|
| - return sharedInstance;
|
| -}
|
| -
|
| -- (void)invalidate {
|
| - [timer_ invalidate];
|
| -}
|
| -
|
| -- (void)addThrobber:(ThrobberView*)throbber {
|
| - DCHECK([NSThread currentThread] == validThread_);
|
| - throbbers_.insert(throbber);
|
| - [self maintainTimer];
|
| -}
|
| -
|
| -- (void)removeThrobber:(ThrobberView*)throbber {
|
| - DCHECK([NSThread currentThread] == validThread_);
|
| - throbbers_.erase(throbber);
|
| - [self maintainTimer];
|
| -}
|
| -
|
| -- (void)maintainTimer {
|
| - BOOL oldRunning = timerRunning_;
|
| - BOOL newRunning = throbbers_.empty() ? NO : YES;
|
| -
|
| - if (oldRunning == newRunning)
|
| - return;
|
| -
|
| - // To start the timer, set its next-fire date to an appropriate interval from
|
| - // now. To suspend the timer, set its next-fire date to a preposterous time
|
| - // in the future.
|
| - NSDate* fireDate;
|
| - if (newRunning)
|
| - fireDate = [NSDate dateWithTimeIntervalSinceNow:kAnimationIntervalSeconds];
|
| - else
|
| - fireDate = [NSDate distantFuture];
|
| -
|
| - [timer_ setFireDate:fireDate];
|
| - timerRunning_ = newRunning;
|
| -}
|
| -
|
| -- (void)fire:(NSTimer*)timer {
|
| - // The call to [throbber animate] may result in the ThrobberView calling
|
| - // removeThrobber: if it decides it's done animating. That would invalidate
|
| - // the iterator, making it impossible to correctly get to the next element
|
| - // in the set. To prevent that from happening, a second iterator is used
|
| - // and incremented before calling [throbber animate].
|
| - ThrobberSet::const_iterator current = throbbers_.begin();
|
| - ThrobberSet::const_iterator next = current;
|
| - while (current != throbbers_.end()) {
|
| - ++next;
|
| - ThrobberView* throbber = *current;
|
| - [throbber animate];
|
| - current = next;
|
| - }
|
| -}
|
| -@end
|
| -
|
| -@implementation ThrobberView
|
| -
|
| -+ (id)filmstripThrobberViewWithFrame:(NSRect)frame
|
| - image:(NSImage*)image {
|
| - ThrobberFilmstripDelegate* delegate =
|
| - [[[ThrobberFilmstripDelegate alloc] initWithImage:image] autorelease];
|
| - if (!delegate)
|
| - return nil;
|
| -
|
| - return [[[ThrobberView alloc] initWithFrame:frame
|
| - delegate:delegate] autorelease];
|
| -}
|
| -
|
| -+ (id)toastThrobberViewWithFrame:(NSRect)frame
|
| - beforeImage:(NSImage*)beforeImage
|
| - afterImage:(NSImage*)afterImage {
|
| - ThrobberToastDelegate* delegate =
|
| - [[[ThrobberToastDelegate alloc] initWithImage1:beforeImage
|
| - image2:afterImage] autorelease];
|
| - if (!delegate)
|
| - return nil;
|
| -
|
| - return [[[ThrobberView alloc] initWithFrame:frame
|
| - delegate:delegate] autorelease];
|
| -}
|
| -
|
| -- (id)initWithFrame:(NSRect)frame delegate:(id<ThrobberDataDelegate>)delegate {
|
| - if ((self = [super initWithFrame:frame])) {
|
| - dataDelegate_ = [delegate retain];
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (void)dealloc {
|
| - [dataDelegate_ release];
|
| - [[ThrobberTimer sharedThrobberTimer] removeThrobber:self];
|
| -
|
| - [super dealloc];
|
| -}
|
| -
|
| -// Manages this ThrobberView's membership in the shared throbber timer set on
|
| -// the basis of its visibility and whether its animation needs to continue
|
| -// running.
|
| -- (void)maintainTimer {
|
| - ThrobberTimer* throbberTimer = [ThrobberTimer sharedThrobberTimer];
|
| -
|
| - if ([self window] && ![self isHidden] && ![dataDelegate_ animationIsComplete])
|
| - [throbberTimer addThrobber:self];
|
| - else
|
| - [throbberTimer removeThrobber:self];
|
| -}
|
| -
|
| -// A ThrobberView added to a window may need to begin animating; a ThrobberView
|
| -// removed from a window should stop.
|
| -- (void)viewDidMoveToWindow {
|
| - [self maintainTimer];
|
| - [super viewDidMoveToWindow];
|
| -}
|
| -
|
| -// A hidden ThrobberView should stop animating.
|
| -- (void)viewDidHide {
|
| - [self maintainTimer];
|
| - [super viewDidHide];
|
| -}
|
| -
|
| -// A visible ThrobberView may need to start animating.
|
| -- (void)viewDidUnhide {
|
| - [self maintainTimer];
|
| - [super viewDidUnhide];
|
| -}
|
| -
|
| -// Called when the timer fires. Advance the frame, dirty the display, and remove
|
| -// the throbber if it's no longer needed.
|
| -- (void)animate {
|
| - [dataDelegate_ advanceFrame];
|
| - [self setNeedsDisplay:YES];
|
| -
|
| - if ([dataDelegate_ animationIsComplete]) {
|
| - [[ThrobberTimer sharedThrobberTimer] removeThrobber:self];
|
| - }
|
| -}
|
| -
|
| -// Overridden to draw the appropriate frame in the image strip.
|
| -- (void)drawRect:(NSRect)rect {
|
| - [dataDelegate_ drawFrameInRect:[self bounds]];
|
| -}
|
| -
|
| -@end
|
|
|