This is MovieView.m in view mode; [Download] [Up]
#include <objc/NXBundle.h>
#include <dpsclient/psops.h>
#include "wraps.h"
#import "MovieView.h"
/*
* Added xanim support
* (c) 1995 Andreas Windemuth
*/
#define VERBOSE 0
#define XANIM 1
#if XANIM
#import "Animation.h"
#endif
/*
* Movie 2.51 - 5/7/92 pjf
*
* Differences between 2.5 and 2.51:
* - the save: method actually has a prayer of working when the
* user saves on top of an existing movie and the old copy can't be renamed.
*
* Differences between 2.0 and 2.5:
* - buttons to control cache depth
*
* - turn off multiframe .tiffs by default (define BC_VERSION_1 if you
* have a .tiff movie and are too lazy to use tiffutil to turn
* it into an .anim directory)
*
* - now able to save movie (currently-selected depth)
*
*/
#define maxFrames 1024
@implementation MovieView
//
- initFrame:(const NXRect *) frameRect
{
const char *x;
[(self = [super initFrame:frameRect]) allocateGState];
state = STOPPED;
mode = LOOP;
maxSize.width = maxSize.height = -1.0;
moviePath = NULL;
movieFrame = NULL;
frameCount = 0;
anim = nil;
pingDuringDisplay=NO;
x=NXGetDefaultValue("Movie","DefaultDepth");
if (!x) dmode=D_DEF; /* use default depth */
else switch(atoi(x)) {
default:
case 0: dmode=D_DEF; break;
case 2: dmode=D_2; break;
case 8: dmode=D_8; break;
case 12: dmode=D_12; break;
case 24: dmode=D_24; break;
};
updateControls = NO;
showFrameNumber = YES;
noOriginals = YES;
fromStream = YES;
frameRate = 15.0;
loading = NO;
willClose = NO;
numFrames = 0;
return self;
}
- updateControls
{
if (loading) {
[[fNumSlider setMaxValue:numFrames-1] setEnabled:YES];
[nFramesText setIntValue:numFrames-1];
}
[fNumSlider setIntValue:currentFrameIndex];
[fNumText setIntValue:currentFrameIndex];
[fNumSlider setEnabled:YES];
return self;
}
- drawSelf:(const NXRect *) rects :(int)count
{
NXImage *image;
NXPoint origin = {0.0,0.0};
if (!movieFrame) return nil;
image = movieFrame[currentFrameIndex].image;
if (!image) return self;
[image composite:NX_SOVER toPoint:&origin];
if ((!fromStream && loading) || showFrameNumber) {
NXRect r;
[self getBounds:&r];
if (!fromStream && loading)
PSWtext(r.size.width, r.size.height, "Loading ...", currentFrameIndex);
else
PSWframe(r.size.width, r.size.height, currentFrameIndex);
}
if (pingDuringDisplay) NXPing();
if ((frameCount >= (int)frameRate)) {
if (state != STOPPED) {
double t=[anim getDoubleRealTime]+[anim getSyncTime];
double afps=frameCount/(t-oldt);
[actualFpsText setDoubleValue:afps];
oldt=t;
}
frameCount=0;
}
if (updateControls || !anim) [self updateControls];
return self;
}
//
- (BOOL)open:sender
{
#if XANIM
const char *const types[] = {
"tiff", "anim", "mpg", "mpeg", "iff", "gif", "txt", "fli", "dl", "pfx", "rle", "avi", "qt", "mov",
NULL };
#else
const char *const types[] = { "tiff", "anim", "mpg", "mpeg", (const char *) NULL };
#endif
id pan = [OpenPanel new];
const char *const *filenames;
char filename[FILENAME_MAX];
if (![pan runModalForTypes:types]) return NO;
if ((filenames = [pan filenames]) == NULL) return NO;
sprintf(filename,"%s/%s", [pan directory], filenames[0]);
return [self openFile:filename];
}
- (BOOL)load:sender
{
fromStream = NO;
noOriginals = NO;
return [self open:sender];
}
- makeWindow;
{
Window *w=[self window];
[w sizeWindow:maxSize.width:maxSize.height]; /* will recache images */
[w setMiniwindowIcon:"movieDoc.tiff"];
[w makeKeyAndOrderFront:self];
[w display];
return self;
}
- makePanel:(char *)filename;
{
char ptitle[FILENAME_MAX];
char *ptr=rindex(filename,'/')+1;
if (ptr == (char *)1) ptr=filename;
sprintf(ptitle,"Controls for %s",ptr);
[panel setTitle:ptitle];
[panel setNextResponder:[self window]];
[depthButtons selectCellAt:(int)dmode:0];
[self setFps:frameRate];
[self updateControls];
[panel orderFront:self];
return self;
}
- setFps:(float)fps;
{
frameRate = fps;
[fpsSlider setFloatValue:fps];
[fpsText setFloatValue:fps];
switch(state) {
case FORWARD:
case REVERSE: if (anim) {
double period = 1.0/frameRate;
[anim free];
anim = [[Animator alloc] initChronon:period adaptation:0.05
target:self action:@selector(tick:)
autoStart:YES eventMask:0];
break;
};
case STOPPED: break;
};
return self;
}
- setNoOriginals;
{
int i;
Window *w=[self window];
if (noOriginals) return self;
if (movieFrame) for (i=0; i<numFrames; i++) if (movieFrame[i].original) {
[movieFrame[i].original free];
movieFrame[i].original = nil;
}
noOriginals = TRUE;
[w setMinSize:&maxSize];
[w setMaxSize:&maxSize];
return self;
}
//
// Terrible kludge to be able to accept event from both
// the panel and the window (to close). Why can there
// be no two parallel sessions? It seems to crash, then ...
//
- beginModal;
{
[NXApp beginModalSession:&panelSession for:panel];
return self;
}
#define EVENT 0
- runModal;
{
#if EVENT
NXEvent ev;
if ([NXApp peekNextEvent:[[self window] eventMask] into:&ev]) {
if (ev.window==[[self window] windowNum]) {
[NXApp endModalSession:&panelSession];
[NXApp beginModalSession:&windowSession for:[self window]];
[NXApp runModalSession:&windowSession];
[NXApp endModalSession:&windowSession];
[NXApp beginModalSession:&panelSession for:panel];
}
}
if ([NXApp peekNextEvent:[panel eventMask] into:&ev]) {
if (ev.window==[panel windowNum]) {
#endif
[NXApp runModalSession:&panelSession];
#if EVENT
}
}
#endif
return self;
}
- endModal;
{
[NXApp endModalSession:&panelSession];
return self;
}
- (BOOL)openFile:(char *)filename
{
if (VERBOSE) debug("openFile: %s\n", filename);
[self makePanel:filename];
[[self window] setTitleAsFilename:filename];
moviePath = copy(filename);
numFrames = 0;
loading = YES;
[self fwd:self];
return YES;
}
- addBitmap:bm copy:(BOOL)copyFlag;
{
NXSize sz;
NXRect r;
int flen, flag=0;
double period;
// if (VERBOSE) debug("Add bitmap %d/%d (%d)\n", currentFrameIndex, numFrames, stopFrame);
[bm getSize:&sz];
if (sz.width > maxSize.width) maxSize.width=sz.width, flag=1;
if (sz.height > maxSize.height) maxSize.height=sz.height, flag=1;
if (flag || !numFrames) [[self window] sizeWindow:maxSize.width:maxSize.height];
if (!numFrames) [self makeWindow];
flen = ([bm pixelsWide]*[bm pixelsHigh]*[bm bitsPerPixel])>>3;
if (numFrames*flen>20000000) [self setNoOriginals];
if (!movieFrame) {
currentFrameIndex = 0;
NX_MALLOC(movieFrame, movieFrameStruct, currentFrameIndex+1);
} else {
currentFrameIndex++;
NX_REALLOC(movieFrame, movieFrameStruct, currentFrameIndex+1);
}
if (numFrames<=currentFrameIndex) numFrames = currentFrameIndex+1;
if (!noOriginals)
movieFrame[currentFrameIndex].original = copyFlag?[bm copy]:bm;
else movieFrame[currentFrameIndex].original=nil;
// printf("Frame # %d, Size: %8.3f %8.3f\n", currentFrameIndex, sz.width, sz.height);
if (!fromStream || !currentFrameIndex) {
movieFrame[currentFrameIndex].image=[[NXImage alloc] initSize:&sz];
[movieFrame[currentFrameIndex].image setUnique:YES]; /* make caches disjoint */
[movieFrame[currentFrameIndex].image setBackgroundColor:NX_COLORBLACK];
} else {
movieFrame[currentFrameIndex].image=movieFrame[currentFrameIndex-1].image;
}
[self getBounds:&r];
if ([movieFrame[currentFrameIndex].image lockFocus]) {
[bm drawIn:&r];
[movieFrame[currentFrameIndex].image unlockFocus];
} else error("Could not lock focus on image");
[self display];
if (noOriginals && !copyFlag) [bm free];
period = 1.0/frameRate;
[self runModal];
while (currentFrameIndex>=stopFrame || state==STOPPED) {
if (state==STOPPED) stopFrame = currentFrameIndex;
[self runModal];
usleep(10000);
if (willClose) return self;
}
frameCount++;
NXPing();
return self;
}
- loadFrames
{
char *ptr, *p1, *p2;
int n, i;
if (VERBOSE) debug("loadFrames: %d %s\n", state, moviePath);
if (!moviePath) error("Cannot load frames: no path\n");
stopFrame = 0;
currentFrameIndex = 0;
/* get the bitmaps */
ptr = rindex(moviePath,'.');
if (ptr && !strcmp(ptr, ".anim")) { /* the file is an Icon-style .anim directory */
p1 = rindex(moviePath,'/');
p1 = p1?(p1+1):moviePath;
p2 = stringf("%s", p1);
p2[ptr-p1] = '\0';
[self openAnimDirectory: stringf("%s/%s", moviePath, p2)];
}
else if (ptr && (!strcmp(ptr,".mpg")||!strcmp(ptr,".mpeg"))) { /* an MPEG file */
[self openMPEGfile:moviePath];
}
else if (ptr && !strcmp(ptr,".tiff")) { /* a slew o' TIFFs in one file */
List *bitmaps;
bitmaps = [NXBitmapImageRep newListFromFile:moviePath];
if (!bitmaps) {
NXRunAlertPanel(NULL,"Couldn't get bitmaps from %s",
NULL,NULL,NULL, moviePath);
return self;
};
n = [bitmaps count];
for (i=0; i<n; i++) [self addBitmap:[bitmaps objectAt:i] copy:NO];
[bitmaps free]; /* does not free elements */
}
#if XANIM
else if ([self openAnimation:moviePath]) {}
#endif
else { /* this shouldn't happen */
[self stop:self];
NXRunAlertPanel(NULL,"Unknown movie type %s",NULL,NULL,NULL,moviePath);
return self;
};
if (willClose) return self;
if (fromStream) {
[self freeFrames];
movieFrame = NULL;
[self updateControls];
if (numFrames>1 && mode == LOOP) [self loadFrames];
if (willClose) return self;
if (mode == BOUNCE)
NXRunAlertPanel(NULL, "Cannot bounce while loading",NULL,NULL,NULL);
}
[self endModal];
[self updateControls];
loading = NO;
[self fwd:self];
return self;
}
- (List *)listAnimDirectory:(char *)filenameRoot
{
List *bitmaps = [[List alloc] init];
int i=0;
while (1) {
char buf[FILENAME_MAX];
NXBitmapImageRep *newbitmap;
sprintf(buf,"%s.%d.tiff",filenameRoot,i++);
if ((access(buf,R_OK)) == -1) break;
newbitmap = [[NXBitmapImageRep alloc] initFromFile:buf];
if (!newbitmap) {
NXRunAlertPanel(NULL,"Couldn't get bitmap from %s",NULL,NULL,NULL,
buf);
[[bitmaps freeObjects] free];
return nil;
}
else
[bitmaps addObject:newbitmap];
};
return bitmaps;
}
- openAnimDirectory:(char *)rfn;
{
int i;
NXBitmapImageRep *newbitmap;
char *fn;
for (i=1; TRUE; i++) {
fn = stringf("%s.%d.tiff", rfn, i);
if ((access(fn, R_OK)) == -1) break;
newbitmap = [[NXBitmapImageRep alloc] initFromFile:fn];
if (!newbitmap) {
NXRunAlertPanel(NULL,"Couldn't get bitmap from %s",NULL,NULL,NULL, fn);
continue;
}
[self addBitmap:newbitmap copy:NO];
if (willClose) break;
}
return self;
}
#if XANIM
- (BOOL)openAnimation:(const char *)filename
{
int i;
NXBitmapImageRep *bm;
Animation *a;
a = [[Animation alloc] initFrom:filename];
if (!a) {
[a free];
return NO;
}
for (i=0; TRUE; i++) {
bm = [a next];
if (bm) [self addBitmap:bm copy:YES];
if ([a isLast] || willClose) break;
}
[a getMaxSize:&maxSize];
return YES;
}
#endif
- openMPEGfile:(char *)filename
{
int i, n, flen;
char command[256];
FILE *fd;
mpegInfo *pInfo;
long data;
BOOL swab;
NXStream *pStream;
NXBitmapImageRep *bm;
if (!(fd = fopen(filename, "r"))) error("Could not open %s", filename);
fread(&data, 4, 1, fd);
if (data!=0x000001b3 && data!=0xb3010000)
error("%s does not contain an mpeg stream: %x", filename, data);
if (data==0xb3010000) swab=YES; else swab=NO;
if (!(pInfo = calloc(1, sizeof(mpegInfo)))) error("Could not allocate pInfo.");
// Get horizontal and vertical size of image space
// as two 12 bit words, respectively
// then aspect ratio and picture rate
// as two 4 bit words.
fread(&data, 4, 1, fd); if (swab) data = NXSwapLong(data);
pInfo->picture_rate = 0x0F & data;
data >>= 4;
pInfo->aspect_ratio = 0x0F & data;
data >>= 4;
// In Motorola format, least significant bits come last
// v_size is actually the second value in the file
// i.e. h:12,v:12,a:4,p:4
pInfo->v_size = 0x0FFF & data;
pInfo->h_size = 0x0FFF & data >> 12;
maxSize.width = ((pInfo->h_size + 15) / 16) * 16.0;
maxSize.height = ((pInfo->v_size + 15) / 16) * 16.0;
// Get bit rate, vbv buffer size, and constrained parameter flag
fread(&data, 4, 1, fd); if (swab) data = NXSwapLong(data);
// throw away (non) intra quant matrix flags
data >>= 2;
pInfo->const_param_flag = 1 & data;
data >>= 1;
pInfo->vbv_buffer_size = 0x03FF & data;
data >>= 10 + 1; // 1 marker bit
pInfo->bit_rate = 0x03FFFF & data;
fclose(fd);
pInfo->fps = pInfo->picture_rate;
flen = 3 * (int)maxSize.width * (int)maxSize.height;
// printf("Dimensions: %d %d, buffer size:%d\n", (int)maxSize.width, (int)maxSize.height, flen);
sprintf(command, "exec %s/mpegDecode %s",
[[NXBundle mainBundle] directory], filename);
if (!(fd = popen(command, "r")))
error("Could not create MPEG process:\n %s", command);
pStream = NXOpenFile(fileno(fd), O_RDONLY);
// printf("Reading frames:\n");
if (!numFrames) [self setFps:pInfo->fps];
bm = [[NXBitmapImageRep alloc] initData:NULL
pixelsWide:(int)maxSize.width
pixelsHigh:(int)maxSize.height
bitsPerSample:8
samplesPerPixel:3 // (cSpace == RGB_COLOR) ? 3 : 1
hasAlpha:NO
isPlanar:NO
colorSpace:NX_RGBColorSpace
bytesPerRow:0
bitsPerPixel:0
];
for (i=0; TRUE; i++) {
if (4!=NXRead(pStream, &data, 4)) break;
// printf("Frame # %d.\n", data);
n = NXRead(pStream, [bm data], flen);
if (n!=flen) error("Error reading image data (%d/%d bytes read).", n, flen);
[self addBitmap:bm copy:YES];
if (willClose) break;
}
[bm free];
if (numFrames<2) {
NXRunAlertPanel("Read MPEG",
"Problem reading mpeg stream, no frames found",NULL,NULL,NULL);
}
return self;
}
NXWindowDepth deps[] = {
NX_DefaultDepth, NX_TwoBitGrayDepth,
NX_EightBitGrayDepth, NX_TwelveBitRGBDepth,
NX_TwentyFourBitRGBDepth /*,NX_PurinaCatChow__ChowChowChowDepth*/
};
// set up Frame data structures and find max frame size
- allocateFrames:(List *)frames
{
int i;
numFrames=[frames count];
NX_MALLOC(movieFrame,movieFrameStruct,numFrames);
for(i=0;i<numFrames;i++) {
NXImage *nxi;
NXBitmapImageRep *bm=[frames objectAt:i];
NXSize sz;
[bm getSize:&sz];
movieFrame[i].original=bm;
nxi=movieFrame[i].image=[[NXImage alloc] initSize:&sz];
[nxi setUnique:YES]; /* make caches disjoint */
[nxi setBackgroundColor:NX_COLORBLACK];
/* keep track of largest frame */
if (sz.width > maxSize.width) maxSize.width=sz.width;
if (sz.height > maxSize.height) maxSize.height=sz.height;
};
return self;
}
/*****************************************************************
*****************************************************************/
- superviewSizeChanged:(NXSize *)old
{
[anim stopEntry];
[super superviewSizeChanged:old];
if (noOriginals) {
if (!fromStream)
NXRunAlertPanel("Resize","Can't resize, no originals.",NULL,NULL,NULL);
}
else if (!loading) {
[self recache];
[self renderFrames];
}
if (movieFrame) [[self window] display];
NXPing();
[anim resetRealTime];
[anim startEntry];
return self;
}
- renderFrames
{
int cfi;
NXRect r;
error("Fix renderFrames!\n");
[self getBounds:&r];
cfi = currentFrameIndex;
for(currentFrameIndex=0; currentFrameIndex<numFrames; currentFrameIndex++) {
if ([movieFrame[currentFrameIndex].image lockFocus]) {
[movieFrame[currentFrameIndex].original drawIn:&r];
[movieFrame[currentFrameIndex].image unlockFocus];
} else {
fprintf(stderr,"Barf: couldn't lockFocus on image %d\n",
(int)movieFrame[currentFrameIndex].image);
abort();
}
[self display];
NXPing();
}
currentFrameIndex = cfi;
return self;
}
- recache
// assume depth & size both changed
//
// Appkit bug? Can one render down from 24 bit color to 2 bit gray?
//
{
NXRect r;
int i;
[self getBounds:&r];
[self freeCaches];
for(i=0; i<numFrames; i++) {
movieFrame[i].image=[[NXImage alloc] initSize:&r.size];
[movieFrame[i].image useCacheWithDepth:deps[(int)dmode]];
};
return self;
}
- save:sender
{
const char *type = "anim"; // will only save in .anim format.
SavePanel *sp = [SavePanel new];
[sp setDelegate:self];
[sp setRequiredFileType:type];
if ([sp runModal]) { // OK was hit
int i;
char cwd[MAXPATHLEN];
/* if directory exists, rename it with a wiggle in back. */
if (access([sp filename],F_OK) == 0) {
/* I could do this with a couple of calls to system(), but noooo,
* I had to do it the had way. yeccch. */
char *buf=malloc(strlen([sp filename]+2));
sprintf(buf,"%s~",[sp filename]);
if (!getwd(cwd)) {
NXRunAlertPanel("FATAL","Couldn't get current directory.",NULL,NULL,NULL);
abort();
};
if (rename([sp filename],buf) == -1) {
// sledgehammer time.
struct direct *de;
DIR *dp;
chdir([sp filename]);
dp=opendir(".");
while(de=readdir(dp)) unlink(de->d_name);
closedir(dp);
chdir(cwd);
unlink([sp filename]);
};
};
mkdir([sp filename],0755);
chdir([sp filename]);
for(i=0;i<numFrames;i++) {
char buf3[MAXPATHLEN];
char buf2[MAXPATHLEN];
char *ptr;
int fd;
NXStream *s;
strcpy(buf3,[sp filename]);
ptr=rindex(buf3,'/')+1;
*(rindex(ptr,'.'))='\0';
sprintf(buf2,"./%s.%d.tiff",ptr,i+1);
fd=open(buf2,O_WRONLY|O_CREAT,0644);
s=NXOpenFile(fd,NX_WRITEONLY);
[movieFrame[i].image writeTIFF:s];
NXClose(s);
close(fd);
};
chdir(cwd);
};
return self;
}
- (BOOL) panelValidateFilenames:sender
{
if (!strcmp([sender filename],moviePath)) {
NXRunAlertPanel("Save","Cannot overwrite original movie",NULL,NULL,NULL);
return NO;
};
return YES;
}
- freeCaches
{
int i;
if (fromStream) [movieFrame[currentFrameIndex].image free];
else for(i=0;i<numFrames;i++) [movieFrame[i].image free];
return self;
}
- freeOriginals
{
int i;
if (!noOriginals) for(i=0;i<numFrames;i++) {
[movieFrame[i].original free];
movieFrame[i].original = nil;
}
return self;
}
- freeFrames
{
[self freeCaches];
if (!noOriginals) [self freeOriginals];
NX_FREE(movieFrame);
return self;
}
- free
{
cfree(moviePath);
[self freeGState];
if (anim) [anim free];
return [super free];
}
- copy:sender
{
char *buffer;
NXStream *stream;
int length, maxLength;
Pasteboard *pasteboard = [Pasteboard new];
runState s=state;
[anim stopEntry];
if (state!=STOPPED) [self stop:self];
[pasteboard declareTypes:&NXPostScriptPboardType num:1 owner:self];
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
[self copyPSCodeInside:&bounds to:stream];
NXFlush(stream);
NXGetMemoryBuffer(stream, &buffer, &length, &maxLength);
[pasteboard writeType:NXPostScriptPboard data:buffer length:length];
NXCloseMemory(stream, NX_FREEBUFFER);
switch(s) {
case STOPPED: break;
case FORWARD:
case REVERSE: [anim startEntry]; break;
};
return self;
}
- tick:sender
{
int next, end;
// if (VERBOSE) debug("tick: %d %d\n", state, loading);
if (state==STOPPED) error("Orphaned tick\n");
if (loading) {
if (movieFrame) {
if (stopFrame<=currentFrameIndex) stopFrame++;
return self;
}
if (state!=FORWARD) error("Wrong loading direction: %d\n", state);
[self beginModal];
[self loadFrames];
[self endModal];
if (willClose) [window performClose:self];
return self;
}
end = (state == FORWARD) ? numFrames-1 : 0;
switch(mode) {
case ONCE:
if (currentFrameIndex == end) {
[self stop:self];
return self;
} else currentFrameIndex += (int)state;
break;
case LOOP:
next = currentFrameIndex + (int)state;
if (((state == FORWARD)&&(next>end)) ||
((state == REVERSE)&&(next<end))) {
currentFrameIndex = (state < 0) ? numFrames-1 : 0;
} else currentFrameIndex = next;
break;
case BOUNCE:
next = currentFrameIndex + (int)state;
if (((state == FORWARD)&&(next>end)) ||
((state == REVERSE)&&(next<end))) {
if (state == FORWARD) [self selectStateButton:REV];
if (state == REVERSE) [self selectStateButton:FWD];
state *= -1;
currentFrameIndex += (int)state;
} else currentFrameIndex = next;
break;
};
frameCount++;
[self display];
return self;
}
/*****************************************************************
*****************************************************************/
- fwd:sender
{
if (VERBOSE) debug("fwd: \n");
if (state != STOPPED) [self stop:self];
state = FORWARD;
[self move:sender];
return self;
}
- rev:sender
{
if (VERBOSE) debug("rev: \n");
if (loading) {
NXRunAlertPanel(NULL,"Cannot reverse while loading.",NULL,NULL,NULL);
return self;
}
if (state != STOPPED) [self stop:self];
state = REVERSE;
[self move:sender];
return self;
}
- move:sender
{
double period = 1.0/frameRate;
if (VERBOSE) debug("move: %d\n", state);
anim = [[Animator alloc]
initChronon:period adaptation:0.05 /*?*/
target:self action:@selector(tick:)
autoStart:YES eventMask:0
];
if (state == FORWARD) [self selectStateButton:FWD];
if (state == REVERSE) [self selectStateButton:REV];
[fNumText setStringValue:""];
[fNumSlider setEnabled:NO];
oldt=[anim getSyncTime];
frameCount = 0;
return self;
}
- stop:sender
{
if (loading && state==STOPPED) { /* Quit this movie even while loading */
willClose = YES; /* Has to propagate through recursive loadFrames calls */
return self;
}
switch(state) {
case FORWARD:
case REVERSE: if (anim) [anim free]; anim=nil;
case STOPPED: break;
}
state = STOPPED;
[self selectStateButton:STOP];
[self updateControls];
// [self display];
return self;
}
- fwdStep:sender
{
[self step:(int) FORWARD];
return self;
}
- revStep:sender
{
if (loading) {
NXRunAlertPanel(NULL, "Cannot reverse while loading.",NULL,NULL,NULL);
return self;
}
[self step:(int) REVERSE];
return self;
}
- step:(int) direction
{
if (VERBOSE) debug("Step: %d %d %d %d\n", direction,
currentFrameIndex, stopFrame, numFrames);
if (state != STOPPED) [self stop:self];
if (loading) {
stopFrame += direction;
state = direction;
} else {
if (((currentFrameIndex = currentFrameIndex + direction) % numFrames) < 0)
currentFrameIndex = numFrames + currentFrameIndex;
}
[self selectStateButton:STOP];
return [self display];
}
- reSize:(NXSize *)s
{
if (noOriginals)
NXRunAlertPanel("Resize","Resize disabled for lack of memory.",NULL,NULL,NULL);
else [[self window] sizeWindow:s->width :s->height];
return self;
}
- expand2x:sender
{
NXRect r;
[self getBounds:&r];
r.size.width *= 2.0;
r.size.height *= 2.0;
[self reSize:&r.size];
return self;
}
- reduce50pct:sender
{
NXRect r;
[self getBounds:&r];
r.size.width *= 0.5;
r.size.height *= 0.5;
[self reSize:&r.size];
return self;
}
- restore:sender
{
[self reSize:&maxSize];
return self;
}
- modeButtonsChanged:sender
{
mode = (runMode)[sender selectedRow];
return self;
}
- fNumSliderChanged:sender
{
stopFrame = [sender intValue];
if (loading || currentFrameIndex == stopFrame) return self;
currentFrameIndex = stopFrame;
[self stop:self];
[self display];
return self;
}
- fpsSliderChanged:sender
{
[self setFps:[sender floatValue]];
return self;
}
- pingButtonChanged:sender
{
switch([sender selectedRow]) {
case 0: pingDuringDisplay=NO; break;
case 1: pingDuringDisplay=YES; break;
};
return self;
}
- selectStateButton:(runState)b
{
[stateButtons selectCellAt:0:((int)b)];
return self;
}
- depthButtonsChanged:sender
{
if (noOriginals) {
NXRunAlertPanel("Depth","Can't change depth, no originals.",NULL,NULL,NULL);
return self;
}
dmode=(depthMode)[sender selectedRow];
[anim stopEntry];
[self recache];
[self renderFrames];
[self display];
[anim resetRealTime];
[anim startEntry];
return self;
}
- updateCheckBoxChanged:sender
{
updateControls = !updateControls;
return self;
}
- frameCheckBoxChanged:sender
{
showFrameNumber = !showFrameNumber;
return self;
}
// Window's delegate methods
- windowWillClose:sender
{
[panel close];
[self free];
return self;
}
-windowDidMiniaturize:sender
{
[panel orderOut:sender];
[anim stopEntry];
return self;
}
-windowDidDeminiaturize:sender
{
[panel orderFront:sender];
[anim resetRealTime];
[anim startEntry];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.