This is Controller.m in view mode; [Download] [Up]
/**************************************************
* SynthBuilder
* Copyright 1993 Nick Porcaro All Rights Reserved
**************************************************/
/*
* Controller.m
* Eric Jordan, independent work, Spring, 1992
* Re-written by Nick Porcaro Summer 1993
*/
#import <stdlib.h>
#import <strings.h>
#import <appkit/Matrix.h>
#import <appkit/View.h>
#import <appkit/Application.h>
#import <appkit/Panel.h>
#import <appkit/Button.h>
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import <musickit/Midi.h>
#import "Controller.h"
#import "FakeSynthPatch.h"
#import "DragView.h"
#import "FakePatchPoint.h"
#import "MKCodeGen.h"
#import "ClmCodeGen.h"
#import "ElementController.h"
#import "Utilities.h"
#import "MainWindowDelegate.h"
extern char *getAppName();
static id openPanel = nil;
static char TempString[1024];
char *getOpenPath(char *banner)
{
static const char dir[1024];
if (!openPanel)
{
openPanel = [OpenPanel new];
}
[openPanel setTitle:banner];
[openPanel allowMultipleFiles:NO];
if ([openPanel runModalForDirectory:dir file:NULL types:NULL])
{
return ((char *) [openPanel filename]);
}
else
{
return NULL;
}
}
static id savePanel = nil;
char *getSavePath(char *banner)
/* Set up and run the Save panel for the given type. */
{
static const char dir[1024];
static const char file[1024];
if (!savePanel)
{
savePanel = [SavePanel new];
}
[savePanel setTitle:banner];
if ([savePanel runModalForDirectory:dir file:file])
{
return ((char *) [savePanel filename]);
}
else
{
return NULL;
}
}
@implementation Controller
static id theController = nil;
+ theController
{
return theController;
}
- awakeFromNib
{
// malloc_debug(0xFFFF);
[dragView setPatch:theSP];
return self;
}
- init
{
theController = self;
return self;
}
- displayMainWindow
/* Called after the ViewController gets its act together */
{
[theWindow makeKeyAndOrderFront:self];
return self;
}
- setHidingScene:sender
{
if ([sender intValue])
stopSoundOnHide = YES;
else
stopSoundOnHide = NO;
return self;
}
- newSP:sender
/* This just erases what we already have. The user
* is asked to save the current SP
*/
{
int val;
val = NXRunAlertPanel(getAppName(),
"Do you want to save the current patch first?",
"YES", /* default */
"NO", /* alt */
"Cancel"); /* other */
switch (val)
{
case(NX_ALERTDEFAULT):
{
[self save:self];
break;
}
case(NX_ALERTALTERNATE):
{
break;
}
default:
{
return self;
}
}
[theSP removeSelf];
[theSP display];
[dragView setPatch:theSP];
[self setFileName:""];
[[theSP window] makeKeyAndOrderFront:self];
return self;
}
static BOOL didFirstOpen = NO;
- open:sender
{
char *file_name;
int val;
if (didFirstOpen)
{
val = NXRunAlertPanel(getAppName(),
"Do you want to save the current patch first?",
"YES", /* default */
"NO", /* alt */
"Cancel"); /* other */
switch (val)
{
case(NX_ALERTDEFAULT):
{
[self save:self];
break;
}
case(NX_ALERTALTERNATE):
{
break;
}
default:
{
return self;
}
}
}
else
{
didFirstOpen = YES;
}
sprintf(TempString, "%s Open Patch", getAppName());
file_name = getOpenPath(TempString);
if (! file_name)
{
return nil;
}
[self openFile:file_name];
return self;
}
- openFile:(char *) fileName
{
NXTypedStream *ts;
id theSuperview = [theSP superview];
id theOldSP = theSP;
id aSP;
ts = NXOpenTypedStreamForFile(fileName, NX_READONLY);
if (ts)
{
[theWindow setTitle: "Loading ..."];
aSP = NXReadObject(ts);
NXCloseTypedStream(ts);
}
else
{
NXRunAlertPanel("Open", "Cannot open file: %s", NULL, NULL, NULL, fileName);
return nil;
}
/* Make sure all the interface controls that
* theSP are responsible for are wired up properly
*/
[mainWindowDelegate setOwner:aSP];
[theSP deallocatePatch:self];
[aSP setController: self];
[aSP setWindowDelegate: [theSP windowDelegate]];
[aSP setMemory: [theSP memory]];
[aSP setUtilities: [theSP utilities]];
[aSP setMidiPortSwitch: [theSP midiPortSwitch]];
[aSP setSoundOutDeviceSwitch: [theSP soundOutDeviceSwitch]];
[aSP setSamplingRateSwitch: [theSP samplingRateSwitch]];
[aSP setDefaultArgValSwitch: [theSP defaultArgValSwitch]];
[aSP setAllocOnOpenSwitch: [theSP allocOnOpenSwitch]];
[aSP setAlwaysUpdateSwitch: [theSP alwaysUpdateSwitch]];
[aSP setNoteOnButton: [theSP noteOnButton]];
[aSP setNoteOffButton: [theSP noteOffButton]];
[aSP setStopButton: [theSP stopButton]];
[aSP setResetControl: [theSP resetControl]];
[aSP setCloseAllControl: [theSP closeAllControl]];
[aSP setDisplayAllControl: [theSP displayAllControl]];
[aSP setPrintSelfControl: [theSP printSelfControl]];
[aSP setOrch: [theSP getOrch]];
[theOldSP removeSelf];
theSP = aSP;
[theSuperview addSubview:theSP];
[dragView setPatch:theSP];
[theSP updateElements];
if ([theSP allocOnOpen])
{
[theSP allocatePatch];
}
[theSP display];
[theSP makeSelected:nil];
if ([renamePPSwitch intValue])
{
[self fixPatchPointNames];
}
[theSP sortFakePatchPointsByAllocOrder];
[theSP sortFakeUGsByAllocOrder];
[[theSP window] orderFront:self];
[self setFileName:fileName];
[theOldSP free];
return self;
}
- fixPatchPointNames
{
int i, len;
id fakePatchPoints, thePatchPoint;
char pp_name[256], *format_str;
static char patch_name[256];
/* Make sure all the patch points have the
* correct (based on feeder pin) name
*
* Here's the sense to this naming:
*
* In the feedback example, constantpp should be a patchpoint, not a synthData.
* That is, you should say:
* constantpp13Out = [template addPatchpoint:MK_xPatch];
* rather than:
* constantpp13Out = [template addSynthData:MK_xData length:16];
*
* What you did is ok, but not as clear.
*
* Also, I wouldn't use the name "Out" when something is a constantpp.
* Instead just use the name, i.e. "constantpp13".
* I also wouldn't use the name "Out" when something is a synthdata.
* The only time you should use that is when you have
* something that's an "implicit" patchpoint, i.e. something that was
* created by this program, not the user, in order to connect two unit generators.
*/
fakePatchPoints = [theSP getFakePatchPoints];
for (i=0; i<[fakePatchPoints count]; i++)
{
thePatchPoint = [[[fakePatchPoints objectAt:i] getFromNode] superview];
if (([thePatchPoint hasConstant]) || ([thePatchPoint isData]))
{
format_str = "%s";
}
else
{
format_str = "%sOut";
}
sprintf(pp_name, format_str,
[[[[fakePatchPoints objectAt:i] getFromNode] superview] getName]);
len = strlen(pp_name);
strcpy(patch_name, pp_name);
[ (FakePatchPoint *) [fakePatchPoints objectAt:i] setName:patch_name];
}
return self;
}
- save:sender
{
if (currentFileName)
{
[self doSave:currentFileName];
}
else
{
[self saveAs:self];
}
return self;
}
- saveAs:sender
{
char *fileName;
sprintf(TempString, "%s Save Patch", getAppName());
if (! (fileName = getSavePath(TempString)))
{
return self;
}
[self doSave:fileName]; /* fileName should have the full path */
return self;
}
- doSave:(char *)fileName
{
char *str;
int len;
NXTypedStream *ts;
ts = NXOpenTypedStreamForFile(fileName, NX_WRITEONLY);
if (ts)
{
[theWindow setTitle: "Saving ..."];
NXWriteRootObject(ts, theSP);
NXCloseTypedStream(ts);
/* Sorry this is needed because setFileName
* and the caller of this function can
* interfere with each other
*/
len = strlen(fileName) + 1;
str = (char *) NXZoneCalloc([self zone], len, sizeof(char));
strcpy(str, fileName);
[self setFileName:str];
NXZoneFree([self zone], str);
}
else
{
NXRunAlertPanel(getAppName(), "Can't create file: %s",
NULL, NULL, NULL, fileName);
}
return self;
}
- setFileName:(char *) file
{
int len;
if (currentFileName)
{
NXZoneFree([self zone], currentFileName);
}
currentFileName = NULL;
len = strlen(file);
if ( (! file) || (len <= 0) || (strcmp(file, "") == 0) )
{
[theWindow setTitle: "Untitled"];
return self;
}
len++;
currentFileName = (char *) NXZoneCalloc([self zone], len, sizeof(char));
if (! currentFileName)
{
NXRunAlertPanel(getAppName(), "Out of Memory", NULL, NULL, NULL);
return self;
}
strcpy(currentFileName, file);
[theWindow setTitle: [utilities getPathLeaf:currentFileName]];
return self;
}
- removeSelected:sender
{
[theSP removeSelected:sender];
return self;
}
- setNameOfSelectedObject:sender
{
[theSP setNameOfSelectedObject:sender];
return self;
}
- getHelp:sender
{
[NXApp showHelpPanel:self];
/*************
if (!helpWindow)
{
[NXApp loadNibSection:"Help.nib" owner:self];
}
[helpWindow makeKeyAndOrderFront:self];
****************/
return self;
}
- getInfo:sender
{
if (!infoPanel)
{
[NXApp loadNibSection:"Info.nib" owner:self];
}
[infoPanel makeKeyAndOrderFront:self];
return self;
}
- theSP
{
return theSP;
}
- elementController
{
return theElementController;
}
- displayAdminPanel:sender
{
int val;
val = NXRunAlertPanel(getAppName(),
"DANGER: Only for administrators. Are you sure?",
"NO", /* default */
"YES", /* alt */
"Cancel"); /* other */
switch (val)
{
case(NX_ALERTDEFAULT):
{
break;
}
case(NX_ALERTALTERNATE):
{
[adminPanel makeKeyAndOrderFront:self];
break;
}
default:
{
break;
}
}
return self;
}
- changeMidiPort:sender
/* this is a temporary hack to get around a problem where
* when you load a file with a midi object, SynthBuilder will
* crash when you attempt to change the midi port.
*/
{
[theSP restartMidi:sender];
return self;
}
- showLegal:sender
{
if (!legalPanel)
{
[NXApp loadNibSection:"Legal.nib" owner:self];
}
[legalPanel makeKeyAndOrderFront:self];
return self;
}
@end
@implementation Controller(ApplicationDelegate)
/*
* We have set up the Controller to be
* the delegate of the Application object
* This is done in the Interface Builder
* by connnecting the File's Owner delegate
* outlet to the ControllerInstance.
* Since Controller is a delegate of Application,
* forwards the appDid* and appWill* methods
* to Controller. This is a good thing to do, as
* opposed to making a subclass of Application
* and overriding it's appDid* appWill methods
*/
/*
* Set some defaults when the app starts
*/
- appDidInit:sender
{
if ([soundHidingPref intValue])
stopSoundOnHide = YES;
else
stopSoundOnHide = NO;
return self;
}
/* If hidden, kill the orchestra if the user
* has set this as a preference
*/
- appDidResignActive:sender
{
if (stopSoundOnHide)
{
[theSP releaseDSP:self];
}
return self;
}
- appWillTerminate:sender
/* This is what gets called when the terminate message
* is sent to the app. We can clean up the midi mess here
* If this method were to return nil, the app would not terminate.
* For example you could add code in here to check for unsaved documents
*/
{
int val;
val = NXRunAlertPanel(getAppName(),
"Do you want to save the current patch first?",
"YES", /* default */
"NO", /* alt */
NULL); /* other */
switch (val)
{
case(NX_ALERTDEFAULT):
{
[self save:self];
break;
}
}
return self;
}
#ifdef HELP_PANEL_TRICK
> - app:sender willShowHelpPanel:panel
> /* This method does nothing non-standard.
> * I was trying to figure out how to
> * get the help panel to resize. I ran out of time. (Nick)
> */
> {
> char path[MAXPATHLEN + 1];
>
> sprintf (path, "%s/%s", [panel helpDirectory],
> "TableOfContents.rtf");
>
> [panel showFile:path atMarker:NULL];
> return self;
> }
>
#endif HELP_PANEL_TRICK
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.