libs-base/Source/NSTimer.m

374 lines
9.6 KiB
Mathematica
Raw Permalink Normal View History

/** Implementation of NSTimer for GNUstep
Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
Created: March 1996
Rewrite by: Richard Frith-Macdonald <rfm@gnu.org>
This file is part of the GNUstep Base Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
<title>NSTimer class reference</title>
$Date$ $Revision$
*/
#import "common.h"
#define EXPOSE_NSTimer_IVARS 1
#import "Foundation/NSTimer.h"
#import "Foundation/NSDate.h"
#import "Foundation/NSException.h"
#import "Foundation/NSRunLoop.h"
#import "Foundation/NSInvocation.h"
@class NSGDate;
@interface NSGDate : NSObject // Help the compiler
@end
static Class NSDate_class;
/**
* <p>An <code>NSTimer</code> provides a way to send a message at some time in
* the future, possibly repeating every time a fixed interval has passed. To
* use a timer, you can either create one that will automatically be added to
* the run loop in the current thread (using the -addTimer:forMode: method),
* or you can create it without adding it then add it to an [NSRunLoop]
* explicitly later.
* </p>
* <p>NB. You may not use -init or +new to create a timer, as the timer must
* be properly initialised to send an action after some interval.
* </p>
*/
@implementation NSTimer
+ (void) initialize
{
if (self == [NSTimer class])
{
NSDate_class = [NSGDate class];
}
}
/* For MacOS-X compatibility, this returns nil.
*/
- (id) init
{
DESTROY(self);
return nil;
}
/** <init />
* Initialise the receive, a newly allocated NSTimer object.<br />
* The ti argument specifies the time (in seconds) between the firing.
* If it is less than or equal to 0.0 then a small interval is chosen
* automatically.<br />
* The fd argument specifies an initial fire date copied by the timer...
* if it is not supplied (a nil object) then the ti argument is used to
* create a start date relative to the current time.<br />
* The f argument specifies whether the timer will fire repeatedly
* or just once.<br />
* If the selector argument is zero, then then object is an invocation
* to be used when the timer fires. otherwise, the object is sent the
* message specified by the selector and with the timer as an argument.<br />
* The object and info arguments will be retained until the timer is
* invalidated.
*/
- (id) initWithFireDate: (NSDate*)fd
interval: (NSTimeInterval)ti
target: (id)object
selector: (SEL)selector
userInfo: (id)info
repeats: (BOOL)f
{
if (ti <= 0.0)
{
ti = 0.0001;
}
if (fd == nil)
{
_date = [[NSDate_class allocWithZone: NSDefaultMallocZone()]
initWithTimeIntervalSinceNow: ti];
}
else
{
_date = [fd copyWithZone: NSDefaultMallocZone()];
}
_target = RETAIN(object);
_selector = selector;
_info = RETAIN(info);
if (f == YES)
{
_repeats = YES;
_interval = ti;
}
else
{
_repeats = NO;
_interval = 0.0;
}
return self;
}
/**
* Create a timer which will fire after ti seconds and, if f is YES,
* every ti seconds thereafter. On firing, invocation will be performed.<br />
* NB. To make the timer operate, you must add it to a run loop.
*/
+ (NSTimer*) timerWithTimeInterval: (NSTimeInterval)ti
invocation: (NSInvocation*)invocation
repeats: (BOOL)f
{
return AUTORELEASE([[self alloc] initWithFireDate: nil
interval: ti
target: invocation
selector: NULL
userInfo: nil
repeats: f]);
}
/**
* Create a timer which will fire after ti seconds and, if f is YES,
* every ti seconds thereafter. On firing, the target object will be
* sent a message specified by selector and with the timer as its
* argument.<br />
* NB. To make the timer operate, you must add it to a run loop.
*/
+ (NSTimer*) timerWithTimeInterval: (NSTimeInterval)ti
target: (id)object
selector: (SEL)selector
userInfo: (id)info
repeats: (BOOL)f
{
return AUTORELEASE([[self alloc] initWithFireDate: nil
interval: ti
target: object
selector: selector
userInfo: info
repeats: f]);
}
/**
* Create a timer which will fire after ti seconds and, if f is YES,
* every ti seconds thereafter. On firing, invocation will be performed.<br />
* This timer will automatically be added to the current run loop and
* will fire in the default run loop mode.
*/
+ (NSTimer*) scheduledTimerWithTimeInterval: (NSTimeInterval)ti
invocation: (NSInvocation*)invocation
repeats: (BOOL)f
{
id t = [[self alloc] initWithFireDate: nil
interval: ti
target: invocation
selector: NULL
userInfo: nil
repeats: f];
[[NSRunLoop currentRunLoop] addTimer: t forMode: NSDefaultRunLoopMode];
RELEASE(t);
return t;
}
/**
* Create a timer which will fire after ti seconds and, if f is YES,
* every ti seconds thereafter. On firing, the target object will be
* sent a message specified by selector and with the timer as its
* argument.<br />
* This timer will automatically be added to the current run loop and
* will fire in the default run loop mode.
*/
+ (NSTimer*) scheduledTimerWithTimeInterval: (NSTimeInterval)ti
target: (id)object
selector: (SEL)selector
userInfo: (id)info
repeats: (BOOL)f
{
id t = [[self alloc] initWithFireDate: nil
interval: ti
target: object
selector: selector
userInfo: info
repeats: f];
[[NSRunLoop currentRunLoop] addTimer: t forMode: NSDefaultRunLoopMode];
RELEASE(t);
return t;
}
- (void) dealloc
{
if (_invalidated == NO)
{
[self invalidate];
}
RELEASE(_date);
[super dealloc];
}
/**
* Fires the timer ... either performs an invocation or sends a message
* to a target object, depending on how the timer was set up.<br />
* If the timer is not set to repeat, it is automatically invalidated.<br />
* Exceptions raised during firing of the timer are caught and logged.
*/
- (void) fire
{
id target;
/* We retain the target so it won't be deallocated while we are using it
* (if this timer gets invalidated while we are firing).
*/
target = [_target retain];
/* We check that we have not been invalidated before we fire.
*/
if (NO == _invalidated)
{
if (_selector == 0)
{
NS_DURING
{
[(NSInvocation*)target invoke];
}
NS_HANDLER
{
NSLog(@"*** NSTimer ignoring exception '%@' (reason '%@') "
@"raised during posting of timer with target %p "
@"and selector '%@'",
[localException name], [localException reason], target,
NSStringFromSelector([target selector]));
}
NS_ENDHANDLER
}
else
{
NS_DURING
{
[target performSelector: _selector withObject: self];
}
NS_HANDLER
{
NSLog(@"*** NSTimer ignoring exception '%@' (reason '%@') "
@"raised during posting of timer with target %p and "
@"selector '%@'",
[localException name], [localException reason], target,
NSStringFromSelector(_selector));
}
NS_ENDHANDLER
}
}
[target release];
if (_repeats == NO)
{
[self invalidate];
}
}
/**
* Marks the timer as invalid, causing its target/invocation and user info
* objects to be released.<br />
* Invalidated timers are automatically removed from the run loop when it
* detects them.
*/
- (void) invalidate
{
/* OPENSTEP allows this method to be called multiple times. */
_invalidated = YES;
if (_target != nil)
{
DESTROY(_target);
}
if (_info != nil)
{
DESTROY(_info);
}
}
/**
* Checks to see if the timer has been invalidated.
*/
- (BOOL) isValid
{
if (_invalidated == NO)
{
return YES;
}
else
{
return NO;
}
}
/**
* Returns the date/time at which the timer is next due to fire.
*/
- (NSDate*) fireDate
{
return _date;
}
/**
* Change the fire date for the receiver.<br />
* NB. You should <em>NOT</em> use this method for a timer which has
* been added to a run loop. The only time when it is safe to modify
* the fire date of a timer in a run loop is for a repeating timer
* when the timer is actually in the process of firing.
*/
- (void) setFireDate: (NSDate*)fireDate
{
ASSIGN(_date, fireDate);
}
/**
* Returns the interval between firings, or zero if the timer
* does not repeat.
*/
- (NSTimeInterval) timeInterval
{
return _interval;
}
/**
* Returns the user info which was set for the timer when it was created,
* or nil if none was set or the timer is invalid.
*/
- (id) userInfo
{
return _info;
}
/**
* Compares timers based on the date at which they should next fire.
*/
- (NSComparisonResult) compare: (id)anotherTimer
{
if (anotherTimer == self)
{
return NSOrderedSame;
}
else if (anotherTimer == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"nil argument for compare:"];
}
else
{
return [_date compare: ((NSTimer*)anotherTimer)->_date];
}
return 0;
}
@end