This is EKInfoManager.m in view mode; [Download] [Up]
/*
* EKInfoManager
* description: a generic object for having a spiffy info panel
* history:
* 5/10/93 [Erik Kay] - created
* 5/20/93 [Erik Kay] - dissolve from author tiff to app tiff
* 5/23/93 [Erik Kay] - added other animation effects:
* random, checker, circle, slide
* bugs:
* this should be able to deal with multiple authors
* todo:
* could always add a few more animation effects:
* barn door, 4 triangles, rectangle, etc.
*/
#import "EKInfoManager.h"
//! <<<WARNING>>>
//! THIS VARIABLE MUST BE DEFINED ELSEWHERE IN THE APP!
//! define it to be any string you want, but it must be defined
//! I've found that vers_string works well here. - EK
extern char VERS_NUM[];
// timed entry function for animating to author TIFF
void animateToAuthor (DPSTimedEntry te, double now, void *data)
{
static int count = 0;
[(id)data animateToAuthor:++count];
if (count > [(id)data animationSteps]) {
count = 0;
[(id)data finishAnimation];
}
}
// timed entry function for animating to app TIFF
void animateToApp (DPSTimedEntry te, double now, void *data)
{
static int count = 0;
[(id)data animateToApp:++count];
if (count > [(id)data animationSteps]) {
count = 0;
[(id)data finishAnimation];
}
}
@implementation EKInfoManager
- init
{
[super init];
// you've got to have an image named "author.tiff" in your app wrapper
authorTIFF = [NXImage findImageNamed:"author"];
//! you don't need an image named "app.tiff", this will just grab the
//! icon you're using for your app tile
appTIFF = [NXImage findImageNamed:"app"];
te = NULL;
return self;
}
// load the info panel from the nib if not loaded already
- loadInfoPanel:sender
{
NXSize sz;
if (!infoPanel) {
// load the nib
[NXApp loadNibSection:"Info.nib" owner:self withNames:NO];
// initialize the images
[authorTIFF getSize:&sz];
workTIFF = [[NXImage alloc] initSize:&sz];
// display the version string in the version text field
[versionNumber setStringValue:VERS_NUM];
}
return self;
}
// show the info panel
- showInfoPanel:sender
{
NXPoint pt = {0,0};
// load it if not loaded already
[self loadInfoPanel:sender];
// the button starts out as the app icon
currentImage = appTIFF;
// draw it into the work area
[workTIFF lockFocus];
[currentImage composite:NX_SOVER toPoint:&pt];
[workTIFF unlockFocus];
// put it into the button
[iconButton setImage:workTIFF];
// bring the panel up
[infoPanel makeKeyAndOrderFront:sender];
return self;
}
// In your info panel, put a transparent button behind the author's name
// then connect the button to this method
// This action changes the image to the author if it's not at the author
// already.
- switchToAuthor:sender
{
if (!te && (currentImage != authorTIFF)) {
te = DPSAddTimedEntry(0.1,animateToAuthor,
(void *)self,NX_BASETHRESHOLD);
currentImage = authorTIFF;
}
return self;
}
// In your info panel, put a transparent button behind your app's name
// then connect the button to this method
// This action changes the image to the app if it's not at the app
// already.
- switchToApp:sender
{
if (!te && (currentImage != appTIFF)) {
te = DPSAddTimedEntry(0.1,animateToApp,(void *)self,NX_BASETHRESHOLD);
currentImage = appTIFF;
}
return self;
}
// In the info panel, hook up the button that the image is being drawn into
// to this action. It will toggle the image back and forth.
- toggleButton:sender
{
if (currentImage == appTIFF) {
[self switchToAuthor:sender];
} else {
[self switchToApp:sender];
}
return self;
}
// Actually do the animation to the author tiff.
// The example implementation here just does a dissolve to the author tiff.
// This is a prime candidate for subclassing. If you want to use a different
// effect, just subclass away.
- animateToAuthor:(int)i
{
[self dissolveTo:authorTIFF step:i];
return self;
}
// Actually do the animation to the app tiff.
// The example implementation here just does a dissolve to the app tiff.
// This is a prime candidate for subclassing. If you want to use a different
// effect, just subclass away.
- animateToApp:(int)i
{
[self dissolveTo:appTIFF step:i];
return self;
}
// how many steps in the animation?
-(int)animationSteps
{
return animationSteps;
}
- setAnimationSteps:(int)steps
{
animationSteps = steps;
return self;
}
// clean up
- finishAnimation
{
DPSRemoveTimedEntry(te);
te = NULL;
return self;
}
//
// here are all of the various animation effects
//
// do dissolve animation: very simple stuff
- dissolveTo:image step:(int)i
{
NXPoint pt = {0.0,0.0};
float dissolve;
dissolve = (float)i/(float)animationSteps;
[workTIFF lockFocus];
[image dissolve:dissolve toPoint:&pt];
[workTIFF unlockFocus];
[iconButton setImage:workTIFF];
[iconButton display];
[infoPanel flushWindow];
return self;
}
#define LEFT 0
#define RIGHT 1
#define BOTTOM 2
#define UP 3
// slide an image in from a cycling direction
- slideTo:image step:(int)i
{
static int direction = -1;
if (i == 1) {
direction++;
if (direction > UP)
direction = LEFT;
}
[self slideTo:image step:i from:direction];
return self;
}
// slide an image in from a specified direction
- slideTo:image step:(int)i from:(int)direction
{
NXRect r;
NXSize s;
int width, height;
NXPoint pt = {0.0,0.0};
[image getSize:&s];
width = i * (s.width / animationSteps);
height = i * (s.height / animationSteps);
switch (direction) {
case LEFT:
NXSetRect(&r,s.width - width,0,width,s.height);
break;
case RIGHT:
NXSetRect(&r,0,0,width,s.height);
pt.x = s.width - width;
break;
case BOTTOM:
NXSetRect(&r,0,s.height - height,s.width,height);
break;
case UP:
NXSetRect(&r,0,0,s.width,height);
pt.y = s.height - height;
break;
default:
break;
}
[workTIFF lockFocus];
[image composite:NX_COPY fromRect:&r toPoint:&pt];
[workTIFF unlockFocus];
[iconButton setImage:workTIFF];
[iconButton display];
[infoPanel flushWindow];
return self;
}
// For all of the effects after this, they all use some basic compositing
// compositing tricks. Using this technique, you can pretty much do any
// simple animation from one tiff to another with very little work.
// Here are the basic steps:
// (1) create a new image of the same size of the target image
// (2) in a completely opaque color, draw the pattern that defines your
// effect on to the image (all of the real work is here)
// (3) do an NX_SIN composite of the target image on to the pattern image
// (4) NX_COPY the original image (the one being drawn over, not the
// image being animated to) on to the workTIFF
// (5) NX_SOVER the pattern image on to workTIFF
// (6) draw the workTIFF on to the button
// when commenting these functions, I'll just say which step they are
// do the random fill animation
- randomTo:image step:(int)i
{
id img;
NXSize imgSize;
NXRect r, *rects;
NXPoint pt = {0.0,0.0};
int num, x, y, index, count;
float percent;
// step (1)
[image getSize:&imgSize];
img = [[NXImage alloc] initSize:&imgSize];
// step (2) - this is pretty slow. basically I'm drawing a random series
// of 1x1 rectangles on the image. This gives the random
// "beam me up" effect
[img lockFocus];
PSsetgray(0.0);
PSsetalpha(0.0);
NXSetRect(&r,0,0,imgSize.width,imgSize.height);
NXRectFill(&r);
PSsetalpha(1.0);
count = imgSize.width * imgSize.height;
percent = i / (float)animationSteps;
if (i <= animationSteps) {
rects = malloc(sizeof(NXRect) * count);
for (x = 0; x < imgSize.width; x++) {
for (y = 0; y < imgSize.height; y++) {
index = x + y * imgSize.width;
num = random() & 63; //! this is kind of dumb, vaguely size
//! dependant stuff here.
if (num <= (63 * percent))
NXSetRect(&rects[index],x,y,1,1);
else
NXSetRect(&rects[index],0,0,0,0);
}
}
NXRectFillList(rects,count);
free(rects);
} else {
NXRectFill(&r);
}
// step (3)
[image composite:NX_SIN toPoint:&pt];
[img unlockFocus];
// step (4)
[workTIFF lockFocus];
if (image == authorTIFF)
[appTIFF composite:NX_COPY toPoint:&pt];
else
[authorTIFF composite:NX_COPY toPoint:&pt];
// step (5)
[img composite:NX_SOVER toPoint:&pt];
[workTIFF unlockFocus];
// step (6)
[iconButton setImage:workTIFF];
[iconButton display];
[infoPanel flushWindow];
[img free];
return self;
}
// animate in a kind of 'checker board' style
- checkerTo:image step:(int)i
{
NXRect rects[64], r;
NXSize imgSize,rectSize;
NXPoint pt = {0.0,0.0};
float height,h;
int x, y, index;
id img;
// step (1)
[image getSize:&imgSize];
img = [[NXImage alloc] initSize:&imgSize];
// step (2) - we're building up a checkerboard of rectangles. First the
// "black" squares, then the "red" squares.
rectSize.width = imgSize.width / 8;
rectSize.height = imgSize.height / 8;
height = 2 * i * (rectSize.height / animationSteps);
h = height - rectSize.height;
for (x = 0; x < 8; x ++) {
for (y = 0; y < 8; y ++) {
index = x + y * 8;
if (height > rectSize.height) {
if ((x % 2) == (y % 2)) {
NXSetRect(&rects[index],x * rectSize.width,
(y + 1) * rectSize.height - h,
rectSize.width, h);
} else {
NXSetRect(&rects[index],x * rectSize.width,
y * rectSize.height,
rectSize.width,rectSize.height);
}
} else {
if ((x % 2) != (y % 2)) {
NXSetRect(&rects[index],x * rectSize.width,
y * rectSize.height,rectSize.width, height);
} else {
NXSetRect(&rects[index],0,0,0,0);
}
}
}
}
[img lockFocus];
PSsetgray(0.0);
PSsetalpha(0.0);
NXSetRect(&r,0,0,imgSize.width,imgSize.height);
NXRectFill(&r);
PSsetalpha(1.0);
// here's where step (2) actually happens. All of the previous was just
// setup. :-)
NXRectFillList(rects,64);
// step (3)
[image composite:NX_SIN toPoint:&pt];
[img unlockFocus];
// step (4)
[workTIFF lockFocus];
if (image == authorTIFF)
[appTIFF composite:NX_COPY toPoint:&pt];
else
[authorTIFF composite:NX_COPY toPoint:&pt];
// step (5)
[img composite:NX_SOVER toPoint:&pt];
[workTIFF unlockFocus];
// step (6)
[iconButton setImage:workTIFF];
[iconButton display];
[infoPanel flushWindow];
[img free];
return self;
}
// animate as a growing circle in the center
- circleTo:image step:(int)i
{
NXSize sz;
NXRect r;
NXPoint center, pt = {0.0,0.0};
float radius;
id img;
// step (1)
[image getSize:&sz];
img = [[NXImage alloc] initSize:&sz];
// step (2) - just draw a circle
[img lockFocus];
PSsetgray(0.0);
PSsetalpha(0.0);
NXSetRect(&r,0,0,sz.width,sz.height);
NXRectFill(&r);
PSsetalpha(1.0);
center.x = sz.width / 2;
center.y = sz.height / 2;
radius = sqrt(center.x * center.x + center.y * center.y);
radius = i * (radius / animationSteps);
PSarc(center.x,center.y,radius,0,360);
PSfill();
// step (3)
[image composite:NX_SIN toPoint:&pt];
[img unlockFocus];
// step (4)
[workTIFF lockFocus];
if (image == authorTIFF)
[appTIFF composite:NX_COPY toPoint:&pt];
else
[authorTIFF composite:NX_COPY toPoint:&pt];
// step (5)
[img composite:NX_SOVER toPoint:&pt];
[workTIFF unlockFocus];
// step (6)
[iconButton setImage:workTIFF];
[iconButton display];
[infoPanel flushWindow];
[img free];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.