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.