/*
* CCNotifications
*
* Copyright (c) 2010 ForzeField Studios S.L.
* http://forzefield.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#import "CCNotifications.h"
#import "CCArray.h"
#import "notificationDesign.h"
@implementation ccNotificationData
@synthesize title = title_;
@synthesize message = message_;
@synthesize media = media_;
@synthesize mediaType = mediaType_;
@synthesize tag = tag_;
@synthesize animated = animated_;
- (void) dealloc
{
[self setTitle:nil];
[self setMessage:nil];
[self setMedia:nil];
[super dealloc];
}
@end
@interface CCNotifications (Private)
- (void) _updateAnimationIn;
- (void) _updateAnimationOut;
- (CCActionInterval*) _animation:(char)type time:(ccTime)time;
- (void) _showNotification;
- (void) _addNotificationToArray:(ccNotificationData*)data cached:(BOOL)isCached;
- (void) _startScheduler;
- (void) _hideNotification;
- (void) _hideNotificationScheduler;
- (void) registerWithTouchDispatcher;
- (void) _setState:(char)states;
@end
@implementation CCNotifications
@synthesize position = position_;
@synthesize notificationDesign = template_;
@synthesize animationIn = animationIn_;
@synthesize animationOut = animationOut_;
@synthesize delegate = delegate_;
@synthesize showingTime = showingTime_;
@synthesize currentNotification = currentNotification_;
static CCNotifications *sharedManager;
+ (CCNotifications *)sharedManager
{
if (!sharedManager)
sharedManager = [[CCNotifications alloc] init];
return sharedManager;
}
+ (id) alloc
{
NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton.");
return [super alloc];
}
+ (id) systemWithTemplate:(CCNode <CCNotificationDesignProtocol> *)notifications
{
NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton. You should use setTemplate");
sharedManager = [[CCNotifications alloc] initWithTemplate:notifications];
return sharedManager;
}
+ (void) purgeSharedManager
{
[sharedManager release];
}
- (id) init
{
CCNode <CCNotificationDesignProtocol> *templates = [[[CCNotificationDefaultDesign alloc] init] autorelease];
self = [self initWithTemplate:templates];
return self;
}
- (id) initWithTemplate:(CCNode <CCNotificationDesignProtocol> *)templates
{
if( (self = [super init]) ) {
self.notificationDesign = templates;
delegate_ = nil;
state_ = kCCNotificationStateHide;
typeAnimationIn_ = kCCNotificationAnimationMovement;
typeAnimationOut_ = kCCNotificationAnimationMovement;
timeAnimationIn_ = 0.0f;
timeAnimationOut_ = 0.0f;
cachedNotifications_ = [[CCArray alloc] initWithCapacity:4];
//Default settings
showingTime_ = 4.0f;
position_ = kCCNotificationPositionTop;
[self setAnimation:kCCNotificationAnimationMovement time:0.5f];
//[self setAnimationIn:kCCNotificationAnimationMovement time:0.5f];
//[self setAnimationOut:kCCNotificationAnimationScale time:0.5f];
}
return self;
}
- (void) _setState:(char)states
{
if(state_==states) return;
state_ = states;
if([delegate_ respondsToSelector:@selector(notification:newState:)])
[delegate_ notification:currentNotification_ newState:state_];
if([delegate_ respondsToSelector:@selector(notificationChangeState:tag:)])
[delegate_ notificationChangeState:state_ tag:[currentNotification_ tag]];
}
- (void) setPosition:(char)positions
{
position_ = positions;
[self updateAnimations];
}
- (void) setNotificationDesign:(CCNode <CCNotificationDesignProtocol>*) templates
{
if(state_!=kCCNotificationStateHide)
[template_ stopAllActions];
if(state_==kCCNotificationStateShowing)
{
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
#endif
[[CCScheduler sharedScheduler] unscheduleSelector:@selector(_hideNotificationScheduler) forTarget:self];
}
[templates retain];
[template_ release];
template_ = templates;
[template_ setVisible:NO];
[template_ setIsRelativeAnchorPoint:YES];
[self _setState:kCCNotificationStateHide];
}
#pragma mark Notification Actions
- (CCActionInterval*) _animation:(char)type time:(ccTime)time
{
CCActionInterval *action = nil;
switch (type){
case kCCNotificationAnimationMovement:
if(position_==kCCNotificationPositionBottom)
action = [CCMoveBy actionWithDuration:time position:ccp(0, template_.contentSize.height)];
else if(position_ == kCCNotificationPositionTop)
action = [CCMoveBy actionWithDuration:time position:ccp(0, -template_.contentSize.height)];
break;
case kCCNotificationAnimationScale:
action = [CCScaleBy actionWithDuration:time scale:(1.0f-KNOTIFICATIONMIN_SCALE)/KNOTIFICATIONMIN_SCALE];
break;
default: break;
}
return action;
}
- (void) _updateAnimationIn
{
self.animationIn = [CCSequence actionOne:[self _animation:typeAnimationIn_ time:timeAnimationIn_] two:[CCCallFunc actionWithTarget:self selector:@selector(_startScheduler)]];
}
- (void) _updateAnimationOut
{
CCActionInterval *tempAction = [self _animation:typeAnimationOut_ time:timeAnimationOut_];
self.animationOut = [CCSequence actionOne:[tempAction reverse] two:[CCCallFunc actionWithTarget:self selector:@selector(_hideNotification)]];
}
- (void) updateAnimations
{
[self _updateAnimationIn];
[self _updateAnimationOut];
}
- (void) setAnimationIn:(char)type time:(ccTime)time
{
typeAnimationIn_ = type;
timeAnimationIn_ = time;
[self _updateAnimationIn];
}
- (void) setAnimationOut:(char)type time:(ccTime)time
{
typeAnimationOut_ = type;
timeAnimationOut_ = time;
[self _updateAnimationOut];
}
- (void) setAnimation:(char)type time:(ccTime)time
{
typeAnimationIn_ = typeAnimationOut_ = type;
timeAnimationIn_ = timeAnimationOut_ = time;
[self updateAnimations];
}
#pragma mark Notification steps
- (void) _startScheduler
{
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
[self registerWithTouchDispatcher];
#endif
[self _setState:kCCNotificationStateShowing];
[template_ stopAllActions];
[[CCScheduler sharedScheduler] scheduleSelector:@selector(_hideNotificationScheduler) forTarget:self interval:showingTime_ paused:NO];
}
- (void) _hideNotification
{
[self _setState:kCCNotificationStateHide];
[template_ setVisible:NO];
[template_ stopAllActions];
[template_ onExit];
//Release current notification
[cachedNotifications_ removeObject:currentNotification_];
self.currentNotification = nil;
//Check next notification
[self _showNotification];
}
- (void) _hideNotificationScheduler
{
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
#endif
[[CCScheduler sharedScheduler] unscheduleSelector:@selector(_hideNotificationScheduler) forTarget:self];
if([currentNotification_ animated])
{
[self _setState:kCCNotificationStateAnimationOut];
[template_ runAction:animationOut_];
}else
[self _hideNotification];
}
#pragma mark Manager Notifications
- (void) _addNotificationToArray:(ccNotificationData*)data cached:(BOOL)isCached
{
if(isCached)
{
[cachedNotifications_ addObject:data];
if([cachedNotifications_ count]==1)
[self _showNotification];
}else{
if(currentNotification_)
{
[cachedNotifications_ removeObject:currentNotification_];
}
[cachedNotifications_ insertObject:data atIndex:0];
[self _showNotification];
}
}
- (ccNotificationData*) addWithTitle:(NSString*)title message:(NSString*)message image:(NSString*)image tag:(int)tag animate:(BOOL)animate waitUntilDone:(BOOL)isCached
{
ccNotificationData *data = [[ccNotificationData alloc] init];
data.title = title;
data.message = message;
data.media = image;
data.mediaType = kCCNotificationMediaPath;
data.tag = tag;
data.animated = animate;
[self _addNotificationToArray:data cached:isCached];
[data release];
return data;
}
- (ccNotificationData*) addWithTitle:(NSString*)title message:(NSString*)message texture:(CCTexture2D*)texture tag:(int)tag animate:(BOOL)animate waitUntilDone:(BOOL)isCached
{
ccNotificationData *data = [[ccNotificationData alloc] init];
data.title = title;
data.message = message;
data.media = texture;
data.mediaType = kCCNotificationMediaTexture;
data.tag = tag;
data.animated = animate;
[self _addNotificationToArray:data cached:isCached];
[data release];
return data;
}
- (ccNotificationData*) addWithTitle:(NSString*)title message:(NSString*)message image:(NSString*)image tag:(int)tag animate:(BOOL)animate
{
return [self addWithTitle:title message:message image:image tag:tag animate:animate waitUntilDone:YES];
}
- (ccNotificationData*) addWithTitle:(NSString*)title message:(NSString*)message texture:(CCTexture2D*)texture tag:(int)tag animate:(BOOL)animate
{
return [self addWithTitle:title message:message texture:texture tag:tag animate:animate waitUntilDone:YES];
}
/* Fast methods */
- (ccNotificationData*) addWithTitle:(NSString*)title message:(NSString*)message image:(NSString*)image
{
return [self addWithTitle:title message:message image:image tag:-1 animate:YES waitUntilDone:YES];
}
- (ccNotificationData*) addWithTitle:(NSString*)title message:(NSString*)message texture:(CCTexture2D*)texture
{
return [self addWithTitle:title message:message texture:texture tag:-1 animate:YES waitUntilDone:YES];
}
/* Deprecated */
- (void) addSafelyWithTitle:(NSString*)title message:(NSString*)message image:(NSString*)image tag:(int)tag animate:(BOOL)animate
{
[self addWithTitle:title message:message image:image tag:tag animate:animate waitUntilDone:YES];
}
- (void) _showNotification
{
if([cachedNotifications_ count]==0) return;
//Get notification data
self.currentNotification = [cachedNotifications_ objectAtIndex:0];
//Stop system
if(state_==kCCNotificationStateShowing)
{
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
#else
[[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self];
#endif
}
if(state_!=kCCNotificationStateHide)
{
[self _setState:kCCNotificationStateHide];
[template_ setVisible:NO];
[template_ stopAllActions];
[template_ onExit];
[[CCScheduler sharedScheduler] unscheduleSelector:@selector(_hideNotificationScheduler) forTarget:self];
}
//Get variables
CCTexture2D *texture = (currentNotification_.media) ? ((currentNotification_.mediaType==kCCNotificationMediaTexture) ? (CCTexture2D*)currentNotification_.media : [[CCTextureCache sharedTextureCache] addImage:(NSString*)currentNotification_.media]) : nil;
//Prepare template
[template_ setVisible:NO];
[template_ stopAllActions];
[template_ onExit];
//Prepare animation
CGSize winSize = CGSizeMake(480,320);
if(currentNotification_.animated)
{
if(position_==kCCNotificationPositionBottom){
[template_ setAnchorPoint:ccp(0.5f, 0)];
switch (typeAnimationIn_) {
case kCCNotificationAnimationMovement:
[template_ setScale:1.0f];
[template_ setPosition:ccp(winSize.width/2.0f, -template_.contentSize.height)];
break;
case kCCNotificationAnimationScale:
[template_ setScale:KNOTIFICATIONMIN_SCALE];
[template_ setPosition:ccp(winSize.width/2.0f, 0)];
break;
default: return;
}
}else if(position_==kCCNotificationPositionTop)
{
[template_ setAnchorPoint:ccp(0.5f, 1)];
switch (typeAnimationIn_){
case kCCNotificationAnimationMovement:
[template_ setScale:1.0f];
[template_ setPosition:ccp(winSize.width/2.0f, winSize.height+template_.contentSize.height)];
break;
case kCCNotificationAnimationScale:
[template_ setScale:KNOTIFICATIONMIN_SCALE];
[template_ setPosition:ccp(winSize.width/2.0f, winSize.height)];
break;
default: return;
}
}
[self _setState:kCCNotificationStateAnimationIn];
[template_ onEnter];
[template_ runAction:animationIn_];
}else{
if(position_==kCCNotificationPositionBottom)
{
[template_ setAnchorPoint:ccp(0.5f, 0)];
[template_ setPosition:ccp(winSize.width/2.0f, 0)];
}else if(position_==kCCNotificationPositionTop)
{
[template_ setAnchorPoint:ccp(0.5f, 1)];
[template_ setPosition:ccp(winSize.width/2.0f, winSize.height)];
}
[self _startScheduler];
}
//Update template
[template_ setTitle:[currentNotification_ title] message:[currentNotification_ message] texture:texture];
[template_ setVisible:YES];
}
#pragma mark Touch Events
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
- (void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:INT_MIN];
}
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
CGRect rect = [template_ boundingBox];
if(CGRectContainsPoint(rect, point))
if([delegate_ respondsToSelector:@selector(touched:)] && [delegate_ touched:[currentNotification_ tag]])
[self _hideNotificationScheduler];
}
#endif
#pragma mark Other methods
- (void) visit
{
[template_ visit];
}
- (NSString*) description
{
return [NSString stringWithFormat:@"<%@ = %08X>", [self class], self];
}
-(void) dealloc
{
CCLOG(@"cocos2d: deallocing %@", self);
sharedManager = nil;
[cachedNotifications_ release];
[self setCurrentNotification:nil];
[self setNotificationDesign:nil];
[self setDelegate:nil];
[self setAnimationIn:nil];
[self setAnimationOut:nil];
[super dealloc];
}
@end