This is MovieApp.m in view mode; [Download] [Up]
#import "MovieApp.h"
#import "MovieView.h"
#import "Animator.h"
#import "SliderDualActing.h"
#import <appkit/Button.h>
#import <appkit/OpenPanel.h>
#import <appkit/Matrix.h>
#import <appkit/nextstd.h>
#import <appkit/publicWraps.h>
#import <string.h>
@implementation MovieApp
/* connect frameSlider, do some SliderDualActing initialization */
- setFrameSlider:anObject
{
frameSlider = anObject;
[frameSlider setUpTarget:self action:@selector(showFrame:)];
return self;
}
/* connect frequencySlider, do some SliderDualActing initialization */
- setFrequencySlider:anObject
{
frequencySlider = anObject;
[frequencySlider setUpTarget:self action:@selector(changeFrequency:)];
[frequencySlider setFormat:NO left:2 right:2];
[frequencySlider setMax:30.0 allowHigher:NO min:0.02 allowLower:NO];
[frequencySlider setAltStep:1 whole:NO default:10.0];
return self;
}
/* other outlets */
- setDirectionButton:anObject;
{ directionButton = anObject; return self; }
- setGoStopButton:anObject;
{ goStopButton = anObject; return self; }
- setEdgeMatrix:anObject
{ edgeMatrix = anObject; return self; }
- setControlPanel:anObject
{ controlPanel = anObject; return self; }
- setInfoPanel:anObject;
{ infoPanel = anObject; return self; }
/* create the timer at appDidInit: time */
- appDidInit:sender
{
timer = [Animator newChronon:0.1 adaptation:3.0 target:self
action:@selector(tickTock:) autoStart:NO
eventMask:NX_ALLEVENTS];
return self;
}
/* bring up the Info Panel (mkof = makeKeyAndOrderFront) */
- mkofInfoPanel:sender
{
if (infoPanel == NULL)
[self loadNibSection:"Info.nib" owner:self withNames:NO];
[infoPanel makeKeyAndOrderFront:self];
return self;
}
/* bring up the Control Panel (mkof = makeKeyAndOrderFront) */
- mkofControlPanel:sender
{
if (mainWindow == NULL) {
NXRunAlertPanel("No Can Do", "The Control Panel is useless if there's no movie opened.", "OK", NULL, NULL);
return self;
}
if (controlPanel == NULL)
[self loadNibSection:"Control.nib" owner:self withNames:NO];
[self updateFrameSlider];
[controlPanel makeKeyAndOrderFront:self];
return self;
}
/* make the frameSlider reflect the main window's current frame */
- updateFrameSlider
{
[frameSlider setMax:[[mainWindow contentView] numFrames] - 1 allowHigher:NO min:0 allowLower:NO];
[frameSlider setAltStep:1 whole:YES default:[[mainWindow contentView] currentFrame]];
return self;
}
/* whenever a movie window resigns main, we stop the animation */
- windowDidResignMain:sender
{
if ([timer isTicking])
[goStopButton performClick:self];
if (mainWindow == NULL)
[controlPanel orderOut:self];
return self;
}
/* Whenever a movie window becomes main, we bring up the Control panel */
- windowDidBecomeMain:sender
{
[self mkofControlPanel:self];
return self;
}
/* We are always ready to open another file. */
- (BOOL) appAcceptsAnotherFile:sender
{
return YES;
}
/*
* appOpenFile is called by the open: method, and by other apps via
* Speaker/Listener
*/
- (int)appOpenFile:(const char *)filename type:(const char *)aType
{
char temp[128];
NXStream *theStream;
int i, width, height, numFrames;
id theNewWin;
/* read in data from movie index */
strcpy(temp, filename);
strcat(temp, "/index");
theStream = NXMapFile(temp, NX_READONLY);
if (theStream == NULL) {
NXRunAlertPanel("Error", "%s has no index.",
"OK", NULL, NULL, filename);
return YES;
}
NXScanf(theStream, "%d%d%d", &width, &height, &numFrames);
NXClose(theStream);
/* is any data bad or missing? */
if ((width < 1) || (height < 1) || (numFrames < 1)) {
NXRunAlertPanel("Error", "%s has a bad index.",
"OK", NULL, NULL, filename);
return YES;
}
/* create the new window, in a good place */
theNewWin = [Window
newContent:[self nextRectForWidth:width Height:height]
style:NX_TITLEDSTYLE
backing:NX_RETAINED
buttonMask:(NX_CLOSEBUTTONMASK | NX_MINIATURIZEBUTTONMASK)
defer:NO];
/* we need to receive windowDidBecomeMain: and windowDidResignMain: */
[theNewWin setDelegate:self];
/*
* create a new MovieView, make it the contentView of our new window,
* and destroy the window's old contentView
*/
[[theNewWin setContentView:[MovieView newPath:filename Width:width Height:height NumFrames:numFrames]] free];
/* make sure the miniwindow has the pretty ".movie" icon */
[theNewWin setMiniwindowIcon:"movie"];
/* give the window a good title */
strcpy(temp, strrchr(filename, '/') + 1);
strcat(temp, " Ð ");
strncat(temp, filename, strrchr(filename, '/') - filename);
[theNewWin setTitle:temp];
/* display the window, and bring it forth */
[theNewWin display];
[theNewWin makeKeyAndOrderFront:self];
/* bring up the control panel */
[self mkofControlPanel:self];
/* show the first frame */
[[theNewWin contentView] showFrameNumber:0];
return YES;
}
/***************************************************************************/
/* nextRectForWidth:Height: - return the next good content rectangle */
/* from Carl F. Sutter's wonderful ViewGif2 'Controller' method... */
/***************************************************************************/
/* nextTopLeft - return the next good top left window position */
/***************************************************************************/
- (NXRect *)nextRectForWidth:(NXCoord)width Height:(NXCoord)height
{
#define OFFSET 10.0
#define MAX_STEPS 20
#define INITIAL_X 356.0
#define INITIAL_Y 241.0
NXPoint nxpTopLeft;
NXRect nxrTemp; /* used to find window height */
NXRect nxrWinHeight; /* bounds of enclosing window */
NXSize nxsScreen; /* size of screen */
static NXRect nxrResult; /* the Answer! */
static int nCurStep = 0;
/* find a good top-left coord */
nxpTopLeft.x = INITIAL_X + nCurStep * OFFSET;
nxpTopLeft.y = INITIAL_Y + nCurStep * OFFSET;
if (++nCurStep > MAX_STEPS)
nCurStep = 0;
/* find window height using nxrTemp */
nxrTemp.size.width = width;
nxrTemp.size.height = height;
nxrTemp.origin.x = nxrTemp.origin.y = 0;
[Window getFrameRect:&nxrWinHeight forContentRect:&nxrTemp
style:NX_TITLEDSTYLE];
[NXApp getScreenSize:&nxsScreen];
/* find the lower-left coord */
nxrResult.origin.x = nxpTopLeft.x;
nxrResult.origin.y = nxsScreen.height - nxrWinHeight.size.height - nxpTopLeft.y;
nxrResult.size.width = width;
nxrResult.size.height = height;
return (&nxrResult);
}
/* open is called by the "Open..." button in the "Movie" menu */
- open:sender
{
const char *const ext[2] = {"movie", NULL};
const char *filename;
id openReq = [OpenPanel new];
if ([openReq runModalForTypes:ext] && (filename = [openReq filename]))
[self appOpenFile:filename type:""];
return self;
}
/* showFrame is called continuously as the "Current Frame" slider is moved */
- showFrame:sender
{
[[mainWindow contentView] showFrameNumber:[sender intValue]];
return self;
}
/*
* changeFrequency is called whenever the user is done manipulating the
* "Frames Per Second" slider. This method actually frees the old timer
* and creates a new one, because Animator's setIncrement: method doesn't
* work the way it should (it doesn't change desireddt).
*/
- changeFrequency:sender
{
BOOL wasTicking = [timer isTicking];
[timer free];
timer = [Animator newChronon:(1.0 / [sender doubleValue])
adaptation:3.0 target:self action:@selector(tickTock:)
autoStart:wasTicking eventMask:NX_ALLEVENTS];
return self;
}
/* toggleGoStop: is called when the user hits the "Go/Stop" Button */
- toggleGoStop:sender
{
if ([timer isTicking])
[timer stopEntry];
else
[timer startEntry];
[timer resetRealTime];
return self;
}
/* tickTock: is called by the timer */
- tickTock:sender
{
int newFrameNum;
newFrameNum = [[mainWindow contentView] currentFrame];
if ([directionButton state])
newFrameNum++;
else
newFrameNum--;
if (newFrameNum == [[mainWindow contentView] numFrames]) {
switch ([edgeMatrix selectedTag]) {
case 0: /* hard edges */
[goStopButton performClick:self];
return;
case 1: /* bouncy edges */
[directionButton performClick:self];
newFrameNum--;
break;
case 2: /* wraparound */
newFrameNum = 0;
break;
}
} else if (newFrameNum == -1) {
switch ([edgeMatrix selectedTag]) {
case 0: /* hard edges */
[goStopButton performClick:self];
return;
case 1: /* bouncy edges */
[directionButton performClick:self];
newFrameNum++;
break;
case 2: /* wraparound */
newFrameNum = [[mainWindow contentView] numFrames] - 1;
break;
}
}
/* show the new frame */
[[mainWindow contentView] showFrameNumber:newFrameNum];
/* change the slider to the new frame number */
[frameSlider setIntValue:newFrameNum];
/* wait for all the Display Postscript to be executed */
NXPing();
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.