This is Controller.m in view mode; [Download] [Up]
/*
** Copyright (C) 1992 Ronin Consulting, Inc.
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; version 1.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#import <stdio.h>
#import "Controller.h"
#import "Defaults.h"
#import "StringStorage.h"
#import "TokenString.h"
#import "EnhancedText.h"
#import "EnhancedMatrix.h"
#import "Subprocess.h"
#import <appkit/appkit.h>
#import <libc.h>
#import <objc/NXStringTable.h>
#import <soundkit/soundkit.h>
#import "EnhancedApp.h"
#import "FileName.h"
@implementation Controller
- init
{
[super init];
[NXApp appDirectory];
openPanel = [OpenPanel new];
[openPanel allowMultipleFiles: NO];
inFile = [[StringStorage alloc] init];
savePanel = [SavePanel new];
outFile = [[StringStorage alloc] init];
strings = [[NXStringTable alloc] init];
dataFormSwitches = [[NXStringTable alloc] init];
dataSizeSwitches = [[NXStringTable alloc] init];
inHotListStr = [[NXStringTable alloc] init];
inHotListConfig = [[NXStringTable alloc] init];
outHotListStr = [[NXStringTable alloc] init];
outHotListConfig = [[NXStringTable alloc] init];
defaults = [Defaults new]; /* get access to applications defaults */
theSound = [[Sound alloc] init];
[theSound setDelegate: self];
_waiting = NO;
return self;
}
- appDidInit: sender
{
id buffer = [[StringStorage alloc] init];
id dir = [[StringStorage alloc] init];
/* load the effects popup */
if(![strings readFromFile: "English.lproj/effects.strings"])
NXLogError("File effects is missiong from .app");
[self loadPopUpList: effect with: strings from: 1];
[strings empty];
/* load the filetype popups */
if(![strings readFromFile: "English.lproj/filetype.strings"])
NXLogError("File filetype is missiong from .app");
else
{ /* get a list of file types */
int count = 0;
const void *key, *value;
char *aType;
NXHashState state = [strings initState];
NX_MALLOC(fileTypes, char *, [strings count] + 1);
while ([strings nextState: &state key: &key value: &value])
{
NX_MALLOC(aType,char,strlen((char *)value) + 1);
strcpy(aType, (char *)value);
fileTypes[count] = aType;
count++;
}
fileTypes[count] = (char *)0;
}
[self loadPopUpList: inType with: strings from: 1];
[self loadPopUpList: outType with: strings from: 3];
[strings empty];
/* load the rates popups */
if(![strings readFromFile: "English.lproj/rate.strings"])
NXLogError("File rate missing\n");
[self loadPopUpList: inRate with: strings from: 1];
[[inRate target] setTarget: self];
[[inRate target] setAction: @selector(copyValue:)];
[self loadPopUpList: outRate with: strings from: 1];
[[outRate target] setTarget: self];
[[outRate target] setAction: @selector(copyValue:)];
[outRateText setDoubleValue: atof([outRate title])];
[strings empty];
/* load the format popups */
if(![strings readFromFile: "English.lproj/dataFormat.strings"])
NXLogError("File dataFormat missing\n");
[self loadPopUpList: inDataForm with: strings from: 1];
[self loadPopUpList: outDataForm with: strings from: 1];
[strings empty];
/* laod the format swithes */
if(![dataFormSwitches readFromFile: "English.lproj/dataFormatSwitches.strings"])
NXLogError("File dataFormatSwitches missing\n");
/* load data size popups */
if(![strings readFromFile: "English.lproj/dataSize.strings"])
NXLogError("File dataSize missing\n");
[self loadPopUpList: inDataSize with: strings from: 1];
[self loadPopUpList: outDataSize with: strings from: 1];
[strings empty];
/* laod the size swithes */
if(![dataSizeSwitches readFromFile: "English.lproj/dataSizeSwitches.strings"])
NXLogError("File dataSizeSwitches missing\n");
/* load data size popups */
if(![strings readFromFile: "English.lproj/channel.strings"])
NXLogError("File channel missing\n");
[self loadPopUpList: inChannels with: strings from: 1];
[self loadPopUpList: outChannels with: strings from: 1];
[strings empty];
[dir setStringValue: [defaults get: "StoreDirectory"]];
if(!*[dir stringValue])
[dir setStringValue: "English.lproj/"];
else
[dir appendStringValue: "/"];
[buffer setStringValue: [dir stringValue]];
[buffer appendStringValue: "inHotList.strings"];
/* load in hot list */
if(![inHotListStr readFromFile: [buffer stringValue]])
NXLogError("File inHotList missing.\n");
[self loadPopUpList: inHotList with: inHotListStr from: 1];
[[inHotList target] setTarget: self];
[[inHotList target] setAction: @selector(loadFromHotList:)];
[buffer setStringValue: [dir stringValue]];
[buffer appendStringValue: "inHotListConfig.strings"];
/* load in hot list configs */
if(![inHotListConfig readFromFile: [buffer stringValue]])
NXLogError("File inHotListConfig missing.\n");
[buffer setStringValue: [dir stringValue]];
[buffer appendStringValue: "outHotList.strings"];
/* load out hot list */
if(![outHotListStr readFromFile: [buffer stringValue]])
NXLogError("File outHotList missing.\n");
[self loadPopUpList: outHotList with: outHotListStr from: 1];
[[outHotList target] setTarget: self];
[[outHotList target] setAction: @selector(loadFromHotList:)];
[buffer setStringValue: [dir stringValue]];
[buffer appendStringValue: "outHotListConfig.strings"];
/* load out hot list configs */
if(![outHotListConfig readFromFile: [buffer stringValue]])
NXLogError("File outHotListConfig missing.\n");
[self loadFromHotList: [[inHotList target] itemList]];
[self loadFromHotList: [[outHotList target] itemList]];
[panel makeKeyAndOrderFront: self];
[dialog appendString: "Ready...\n"];
[[NXApp appListener] setServicesDelegate: self];
return self;
}
- convert:sender
{
char cmd[1024];
[convert setTitle: "STOP"];
NXPing();
if(![convert state]) /* already doing a conversion - stop it */
{
[dialog appendString: "Stopping conversion\n"];
[subprocess terminate: self];
[convert setTitle: "CONVERT"];
_waiting = NO;
return nil;
}
if (![panel makeFirstResponder: panel])
{
[dialog appendString: "Invalid entry.\n"];
[convert setState: 0];
[convert setTitle: "CONVERT"];
_waiting = NO;
return nil;
}
[panel endEditingFor:nil];
if(!*[inFile stringValue])
{
[dialog appendString: "No input File is set.\n"];
[convert setState: 0];
[convert setTitle: "CONVERT"];
_waiting = NO;
return nil;
}
if(!*[outFile stringValue])
{
[outFile setStringValue: [defaults get: "TmpDirectory"]];
[outFile appendStringValue: "/GISO"];
[outFile mktemp];
[outFile appendStringValue: ".snd"];
}
/*
* Build up the command line.
*/
strcpy(cmd, "sox -V ");
if([volume doubleValue] != 1.0)
sprintf(cmd + strlen(cmd), "-v %6.2f ", [volume doubleValue]);
if([[inType target] indexOfItem: [inType title]])
sprintf(cmd + strlen(cmd), "-t %s ", [inType title]);
if([inRateText doubleValue])
sprintf(cmd + strlen(cmd), "-r %10.3f ", [inRateText doubleValue]);
if([[inDataForm target] indexOfItem: [inDataForm title]])
sprintf(cmd + strlen(cmd), "%s ",
[dataFormSwitches valueForStringKey: [inDataForm title]]);
if([[inDataSize target] indexOfItem: [inDataSize title]])
sprintf(cmd + strlen(cmd), "%s ",
[dataSizeSwitches valueForStringKey: [inDataSize title]]);
if([[inChannels target] indexOfItem: [inChannels title]])
sprintf(cmd + strlen(cmd), "-c %s ", [inChannels title]);
if([swapBytes state])
strcat(cmd, "-x ");
sprintf(cmd + strlen(cmd), "%s -t %s ", [inFile stringValue], [outType title]);
/* -r %10.3f %s %s -c %s %s " */
if([outRateText doubleValue])
sprintf(cmd + strlen(cmd),"-r %10.3f ", [outRateText doubleValue]);
if([[outDataForm target] indexOfItem: [outDataForm title]])
sprintf(cmd + strlen(cmd), "%s ",
[dataFormSwitches valueForStringKey: [outDataForm title]]);
if([[outDataSize target] indexOfItem: [outDataSize title]])
sprintf(cmd + strlen(cmd), "%s ",
[dataSizeSwitches valueForStringKey: [outDataSize title]]);
if([[outChannels target] indexOfItem: [outChannels title]])
sprintf(cmd + strlen(cmd), "-c %s ", [outChannels title]);
sprintf(cmd + strlen(cmd), "%s ", [outFile stringValue]);
if([[effect target] indexOfItem: [effect title]])
sprintf(cmd + strlen(cmd), "%s %s ",
[effect title], [effectArgText stringValue]);
[dialog appendString: cmd];
[dialog appendString: "\n"];
subprocess = [[Subprocess alloc] init: cmd withDelegate: self];
if(*[defaults get: "AutoPlay"] == 'Y')
_waiting = YES;
return self;
}
- openFile:sender
{
static int first = 1;
if (first)
{
[openPanel runModalForDirectory: [defaults get: "OpenDir"] file: "" types: fileTypes];
first = 0;
}
else
{
[openPanel runModalForDirectory: "" file: "" types: fileTypes];
[defaults writeDB: "OpenDir" as: [openPanel directory]];
}
[inFile setStringValue: [openPanel filename]];
if(*[inFile stringValue])
{
[dialog appendString: [inFile stringValue]];
[dialog appendString: " used as input\n"];
if(*[defaults get: "AutoConvert"] == 'Y')
{
[convert setState: 1];
[self perform: @selector(convert:) with: self afterDelay: 1 cancelPrevious: YES];
}
[outFile setStringValue: ""];
}
return self;
}
- saveFile:sender
{
static int first = 1;
id tempFile = [[StringStorage alloc] init];
if(!*[outFile stringValue])
{
[dialog appendString: "No sound created.\n"];
[tempFile free];
return self;
}
[savePanel setRequiredFileType: [outType title]];
if(first)
{
[savePanel runModalForDirectory: [defaults get:"SaveDir"] file: ""];
first = 0;
}
else
{
[savePanel runModalForDirectory: "" file: ""];
[defaults writeDB: "SaveDir" as: [savePanel directory]];
}
[tempFile setStringValue: [savePanel filename]];
if(*[tempFile stringValue])
{
char cmd[128];
[dialog appendString: [savePanel filename]];
[dialog appendString: " used as output\n"];
sprintf(cmd, "cp \"%s\" \"%s\" ", [outFile stringValue], [tempFile stringValue]);
system(cmd);
[outFile setStringValue: [tempFile stringValue]];
}
[tempFile free];
return self;
}
- saveToHotList: sender
{
id buffer = [[StringStorage alloc] init];
id file = [[StringStorage alloc] init];
id dir = [[StringStorage alloc] init];
char num[10], *name;
BOOL inHot;
name = (char *)[entryName stringValue];
if(!name || !*name)
{
NXRunAlertPanel([NXApp appName], "Entry must have a name.",
NULL, NULL, NULL);
[dir free];
[buffer free];
[file free];
return self;
}
[dir setStringValue: [defaults get: "StoreDirectory"]];
if(!*[dir stringValue])
NXRunAlertPanel([NXApp appName], "No directory to store the hot lists - see preferences.",
NULL, NULL, NULL);
else
{
inHot = [whichRadio state];
[buffer setStringValue: [(inHot?inType:outType) title]];
[buffer appendStringValue: "|"];
[buffer appendStringValue: [(inHot?inRateText:outRateText) stringValue]];
[buffer appendStringValue: "|"];
[buffer appendStringValue: [(inHot?inDataForm:outDataForm) title]];
[buffer appendStringValue: "|"];
[buffer appendStringValue: [(inHot?inDataSize:outDataSize) title]];
[buffer appendStringValue: "|"];
[buffer appendStringValue: [(inHot?inChannels:outChannels) title]];
sprintf(num,"%d", [(inHot?inHotListStr: outHotListStr) count] + 1);
[(inHot?inHotListStr:outHotListStr) insertKey: num value: name];
[(inHot?inHotListConfig:outHotListConfig) insertKey: name value: (char *)[buffer stringValue]];
if(inHot)
{
[file setStringValue: [dir stringValue]];
[file appendStringValue: "/inHotList.strings"];
[inHotListStr writeToFile: [file stringValue]];
[file setStringValue: [dir stringValue]];
[file appendStringValue: "/inHotListConfig.strings"];
[inHotListConfig writeToFile: [file stringValue]];
[[inHotList target] addItem: [entryName stringValue]];
}
else
{
[file setStringValue: [dir stringValue]];
[file appendStringValue: "/outHotList.strings"];
[outHotListStr writeToFile: [file stringValue]];
[file setStringValue: [dir stringValue]];
[file appendStringValue: "/outHotListConfig.strings"];
[outHotListConfig writeToFile: [file stringValue]];
[[outHotList target] addItem: [entryName stringValue]];
}
}
[dir free];
[buffer free];
[file free];
[[sender window] performClose: self];
return self;
}
- stopPlay: sender
{
[theSound stop];
return self;
}
- playIt: sender
{
BOOL error = NO;
if(!*[outFile stringValue])
{
[dialog appendString: "No sound has been created.\n"];
error = YES;
}
else if([theSound readSoundfile: (char *)[outFile stringValue]])
{
[dialog appendString: "Could not load resultant sound.\n"];
error = YES;
}
else if(![theSound isPlayable])
{
[dialog appendString: "Sound isn't playable.\n"];
error = YES;
}
else
[theSound play: sender];
if(error)
{
NXBeep();
[playButton setState: 0];
}
return self;
}
- copyValue: sender
{
if(sender == [[inRate target] itemList])
[inRateText setDoubleValue: atof([[sender selectedCell] title])];
else
[outRateText setDoubleValue: atof([[sender selectedCell] title])];
return self;
}
- willPlay: sender
{
[soundMeter run: self];
return self;
}
- didPlay: sender
{
[playButton setState: 0];
[soundMeter stop: self];
return self;
}
- hadError: sender
{
[dialog appendString: "Error playing sound...\n"];
[playButton setState: 0];
return self;
}
- setDialog: anObject
{
dialog = [anObject docView];
return self;
}
- setSoundMeter: anObject
{
soundMeter = anObject;
[soundMeter setSound: theSound];
return self;
}
- loadFromHotList: sender
{
BOOL inHot;
inHot = ((sender == [[inHotList target] itemList])? YES : NO);
return [self loadType: inHot
fromString: [(inHot?inHotListConfig:outHotListConfig)
valueForStringKey: [[sender selectedCell] title]]];
}
- loadType: (BOOL) input fromString: (const char *)str
{
id tokens = [[TokenString alloc] init: str];
const char *title;
[tokens setSeparator: '|'];
if(!(title = [tokens popStringValue]))
{
[dialog appendString: "Malformed hot list entry\n"];
return self;
}
[(input?inType:outType) setTitle: title];
if(!(title = [tokens popStringValue]))
{
[dialog appendString: "Malformed hot list entry\n"];
return self;
}
[(input?inRateText:outRateText) setStringValue: title];
if(!(title = [tokens popStringValue]))
{
[dialog appendString: "Malformed hot list entry\n"];
return self;
}
[(input?inDataForm:outDataForm) setTitle: title];
if(!(title = [tokens popStringValue]))
{
[dialog appendString: "Malformed hot list entry\n"];
return self;
}
[(input?inDataSize:outDataSize) setTitle: title];
if(!(title = [tokens popStringValue]))
{
[dialog appendString: "Malformed hot list entry\n"];
return self;
}
[(input?inChannels:outChannels) setTitle: title];
[tokens free];
return self;
}
- (int) loadPopUpList: button with: stringTable from: (int) x
{
char key[15];
const char *value;
id list = [button target]; /* get the popUpList */
while(1)
{
sprintf(key, "%d", x++);
value = [stringTable valueForStringKey: key];
if(!value)
break;
[list addItem: value];
}
[list removeItem: [button title]]; /* clear out the .nibs item */
[button setTitle: [[[list itemList] cellAt: 0:0] title]];
[[list itemList] selectCellAt: 0 : 0];
return --x;
}
- cleanKill: sender /* clean temporary files before we go */
{
char cmd[strlen([defaults get: "TmpDirectory"]) + 50];
NXLogError("Cleaning tmp..");
sprintf(cmd, "rm -f %s/GISO*.snd", [defaults get: "TmpDirectory"]);
NXLogError(cmd);
system(cmd);
return [NXApp terminate: self];
}
- (BOOL)appAcceptsAnotherFile:sender
{
return YES;
}
- (int)app:sender openFile:(const char *)filename type:(const char *)aType
{
[inFile setStringValue: filename];
if(*[inFile stringValue])
{
[dialog appendString: [inFile stringValue]];
[dialog appendString: " used as input\n"];
[outFile setStringValue: ""];
if(*[defaults get: "AutoConvert"] == 'Y')
{
[convert setState: 1];
[self perform: @selector(convert:) with: self afterDelay: 1 cancelPrevious: YES];
}
return YES;
}
return NO;
}
#ifdef FOOBAR
- playAsSound: (id)pasteboard userData:(const char *)userData error:(char **)msg
{
char *filenames;
const char *tok, *ftype;
id tokens, filename;
int length,x;
BOOL known;
[pasteboard types]; /* pretend to check the pasteboard types */
/* read the ASCII data from the pasteboard */
if ([pasteboard readType:NXFilenamePboardType data:&filenames length:&length])
{
NXLogError("play %s",filenames);
tokens = [[TokenString alloc] init: filenames];
filename = [[StringStorage alloc] init];
while(tok = [tokens popStringValue])
{
[filename setStringValue: tok];
ftype = [filename fileType];
for(x = 0, known = NO; fileTypes[x] && !known; x++)
if(!strcmp(ftype, fileTypes[x]))
known = YES;
if(known)
{
[self loadType: YES fromString: "Use Filename|0|Use Header|Use Header|Use Header"];
[self loadType: NO fromString: "snd|0|Use Header|Use Header|Use Header"];
[self app: self openFile: [filename stringValue] type: ""];
if(*[defaults get: "AutoConvert"] != 'Y')
{
[convert setState: 1];
[self convert: self];
}
_waiting = YES;
}
else
(*msg) = "Unknown sound file extension.";
}
[tokens free];
[filename free];
}
return self;
}
#endif
- convertSound: (id)pasteboard userData:(const char *)userData error:(char **)msg
{
char *filename;
char *sep;
int length;
[pasteboard types]; /* pretend to check the pasteboard types */
/* read the ASCII data from the pasteboard */
if ([pasteboard readType:NXFilenamePboardType data:&filename length:&length])
{
if(sep = index(filename,'\t'))
*sep = (char)0; /* only handle a single sound */
[self loadType: YES fromString: "auto|0|Use Header|Use Header|Use Header"];
[self loadType: NO fromString: "snd|0|Use Header|Use Header|Use Header"];
[self app: self openFile: filename type: ""];
if(*[defaults get: "AutoConvert"] != 'Y')
{
[convert setState: 1];
[self convert: self];
}
}
return self;
}
- subprocess:sender done:(int)exitStatus
{
if(exitStatus)
[dialog appendString: "ABORTED\n"];
else
{
if(_waiting)
[self perform: @selector(playIt:) with: self afterDelay: 1 cancelPrevious: YES];
[dialog appendString: "Done.\n"];
}
_waiting = NO;
[subprocess free];
[convert setState: 0];
[convert setTitle: "CONVERT"];
return self;
}
- subprocess:sender output:(char *)buffer
{
[dialog appendString: buffer];
[dialog appendString: "\n"];
return self;
}
- subprocess:sender stderrOutput:(char *)buffer
{
[dialog appendString: buffer];
[dialog appendString: "\n"];
return self;
}
- subprocess:sender error:(const char *)errorString
{
[dialog appendString: errorString];
[dialog appendString: "\n"];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.