This is Spewer.m in view mode; [Download] [Up]
// Copyright (c) 1997 by Don Yacktman
// All rights reserved.
// Permission to use this code in your application, whether
// commercial, shareware, or freeware, is granted.
// This notice may not be removed or altered.
#import "Spewer.h"
#import "FlyingWindow.h"
#import "Animator.h"
#import <libc.h>
@implementation Spewer:NSObject
- init
{
[super init];
// seed the random number generator with the current time.
// This call should really only be done once, before the random
// number generator is ever used. Since this object is typically
// only instantiated once so that's not a problem.
srandom(time(0));
// clear this out, since we've not been started yet.
initiator = nil;
// get an animator instance to help us run the animation.
animator = [[Animator allocWithZone:[self zone]] initChronon:0.05
adaptation:0.0 target:self action:@selector(move:)
autoStart:NO eventMask:0];
// set up an extra list used for swapping during animation
_extraList = [[NSMutableArray allocWithZone:[self zone]] init];
// set up the list of extra, idle windows
idleWindows = [[NSMutableArray allocWithZone:[self zone]] init];
// set up the list of active windows
windowList = [[NSMutableArray allocWithZone:[self zone]] init];
// it is not currently OK to create ne windows.
createOK = NO;
return self;
}
- start:sender
{
// start the animation. It is OK to instantiate new windows.
createOK = YES;
// the sender is the one who started us off, so cache that info
initiator = sender;
// get a new window so that we have at least one window to animate
[self addWindow];
// start the animation
[animator startEntry];
return self;
}
- (void)stop:(id)sender
{
// we need to stop the animation, but need all the windows to
// fall off the screen first. So we tell ourself that the
// initiator is gone and disallow creation of new windows.
createOK = NO;
initiator = nil;
}
- (BOOL)windowShouldClose:(id)sender
{
// if the window is closing, then we automatically stop the
// animation. Make sure we are the winow's delegate (hooked
// up as such in IB) or else we won't get this message!
[self stop:self];
return YES;
}
- (void)dealloc
{ // clean out any windows, remove the timed antry and free everything
int i;
// stop the animation and get rid of the animator
[animator stopEntry];
[animator release];
animator = nil;
// get rid of all our windows--get them off the screen
for (i=0; i<[windowList count]; i++) {
[[windowList objectAtIndex:i] orderOut:self];
}
// free the active window list and its contents
[windowList release];
windowList = nil;
// free the idle window list and its contents
[idleWindows release];
idleWindows = nil;
[super dealloc];
return;
}
- addWindow
{ // put up a new window on the screen
NSPoint theCenterPoint;
NSRect senderFrame;
id newWindow = nil;
// if we're not allowed to start any new windows, then return immediately
if (!createOK) return nil;
// find the point where the new window should appear on the screen.
// find out the frame of the control which sent the -start: message
senderFrame = [initiator frame];
// Get the center point of the initiator's frame
theCenterPoint.x = NSMinX(senderFrame) + NSWidth(senderFrame) / 2;
theCenterPoint.y = NSMinY(senderFrame) + NSHeight(senderFrame) / 2;
// Convert the point from view to screen coordinates
theCenterPoint = [[initiator window] convertBaseToScreen:theCenterPoint];
// recycle a window if there are any idle ones; otherwise make a new one
// get an idle window
newWindow = [idleWindows lastObject];
// if we got one, then take it out of the list of idle windows
if (newWindow) [idleWindows removeLastObject];
// if there weeren't any idle windows, then create a new one and set it
// to appear at the point calculated above. If we found an idle window,
// then get it to set itself up as the same point.
if (!newWindow) newWindow = [[FlyingWindow allocWithZone:[self zone]]
initAt:&theCenterPoint];
else [newWindow reInitAt:&theCenterPoint];
// add the new window to the list of active windows.
[windowList addObject:newWindow];
return self;
}
- move:sender
{
// get a new active list
int i; id newList = _extraList;
// add any new windows spewing about if necessary...
// if we don't have too many windows out already, then check
// against a random number to decide if we should add a new
// window. The larger "FREQUENCY" is, the longer the average
// interval will be between new windows coming onto the screen.
if (([windowList count] < MAX_SPEWS) && !(random() % FREQUENCY)) {
[self addWindow];
}
// move all the active windows
for (i=0; i<[windowList count]; i++) [[windowList objectAtIndex:i] move];
// remove any windows that left the screen
for (i=0; i<[windowList count]; i++) {
id theWindow = [windowList objectAtIndex:i];
// if the window is on the screen, transfer it to a new active list
if ([theWindow onScreen]) [newList addObject:theWindow];
// windows that left the screen are taken out of the animation and moved to the idle list
else {
[theWindow orderOut:self];
[idleWindows addObject:theWindow]; // put in recycler
}
}
// make sure no stray windows were left behind.
[windowList removeAllObjects];
// get rid of the old active list and set up the new one.
_extraList = windowList;
windowList = newList;
// if there are no more windows in the active list, and it is no longer
// OK to create new ones, then the animation has ended, so we'll stop
// the timer.
if (([windowList count] < 1) && !(createOK))
// stop TE after all windows gone
[animator stopEntry];
return self;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.