This is Script.m in view mode; [Download] [Up]
#import <appkit/appkit.h>
#import <objc/List.h>
#import <sys/file.h>
#import "MoveMatrix.h"
#import "StateManager.h"
#import "Filter.h"
#import "Script.h"
#import "FilterInspector.h"
#import "ScriptInspector.h"
#import "HelpManager.h"
#import "support.h"
#import "pscode.h"
@implementation Script
#define SCRIPT_VERSION 1
#define NOTSET strdup("")
static id speaker;
+ initialize
{
[Script setVersion: SCRIPT_VERSION];
return self;
}
+ alloc
{
self = [super alloc];
filterList = [[List alloc] init];
scriptName = strdup("Untitled");
resultIconPath = NOTSET;
helpText = NOTSET;
return self;
}
- setStateManager: anObject
{
stateManager = anObject;
inspector = [stateManager scriptInspector];
return self;
}
- createWindow
{
static NXRect wRect={{160.0,500.0},{211.0,119.0}};
static NXSize intercell = { 0.0, 1.0 };
NXRect wBounds;
NXRect optionFrame, textFrame, filterFrame, aFrame;
NXSize cellSize;
id scrollView, aButtonCell, aTextCell, aFilterCell;
window = [[Window alloc] initContent:&wRect
style:NX_TITLEDSTYLE
backing:NX_BUFFERED
buttonMask:NX_ALLBUTTONS
defer:NO];
[window setMiniwindowIcon: "script"];
[window setDelegate: self];
[[window contentView] getFrame: &aFrame];
scrollView = [[ScrollView alloc] initFrame: &aFrame];
[scrollView setVertScrollerRequired:YES];
[scrollView setHorizScrollerRequired:YES];
[ScrollView getContentSize: &docFrame.size
forFrameSize: &aFrame.size
horizScroller:(BOOL) YES
vertScroller:(BOOL) YES
borderType: NX_NOBORDER];
docFrame.origin.x = docFrame.origin.y = 0.0;
optionFrame = docFrame;
optionFrame.origin.x += 48.0;
optionFrame.size.width -= 96.0;
filterFrame = textFrame = optionFrame;
docView = [[View alloc] initFrame: &docFrame];
[docView setClipping: NO];
[scrollView setDocView: docView];
[window setContentView: scrollView];
aButtonCell = [[ButtonCell alloc] initTextCell: ""];
[aButtonCell setIcon: "popup"];
[aButtonCell setIconPosition: NX_ICONRIGHT];
[aButtonCell setAltIcon: "popupH"];
[aButtonCell setHighlightsBy: NX_NONE];
[aButtonCell setAlignment: NX_LEFTALIGNED];
[aButtonCell sendActionOn: NX_MOUSEDOWNMASK];
optionFrame.origin.y = 0.0;
optionFrame.size.height = 20.0;
optionMatrix = [[Matrix alloc] initFrame: &optionFrame
mode: NX_TRACKMODE
prototype: aButtonCell
numRows: 1 numCols: 0];
cellSize.width = 96.0;
cellSize.height = 20.0;
[optionMatrix setCellSize: &cellSize];
[optionMatrix sizeToCells];
[optionMatrix setIntercell: &intercell];
[docView addSubview: optionMatrix];
aTextCell = [[TextFieldCell alloc] initTextCell: "empty"];
[aTextCell setEditable: YES];
[aTextCell setAlignment: NX_CENTERED];
textFrame.origin.y = 20.0;
textFrame.size.height = 32.0;
textMatrix = [[Matrix alloc] initFrame: &textFrame
mode: NX_TRACKMODE
prototype: aTextCell
numRows: 1 numCols: 0];
cellSize.height = 32.0;
[textMatrix setCellSize: &cellSize];
[textMatrix sizeToCells];
[textMatrix setIntercell: &intercell];
[textMatrix setTarget: self];
[textMatrix setAction: @selector(filterNameChanged)];
[docView addSubview: textMatrix];
aFilterCell = [[ButtonCell alloc] initIconCell: "filterArrow"]; // create prototype
[aFilterCell setAltIcon: "filterArrowH"];
[aFilterCell setBordered: NO];
[aFilterCell setType: NX_RADIOBUTTON];
filterFrame.origin.y = 52.0;
matrix = [[MoveMatrix alloc] initFrame: &filterFrame
mode: NX_RADIOMODE
prototype: aFilterCell
numRows: 1 numCols: 0];
[matrix setMoveDelegate: self]; // notify me
[matrix allowEmptySel: YES];
[matrix setIntercell: &intercell];
[matrix setTarget: self];
[matrix setAction: @selector(buttonPressed)];
cellSize.height = 48.0;
[matrix setCellSize: &cellSize];
[matrix sizeToCells];
[docView addSubview: matrix];
[window display];
// register this window to window server (for icon dragging)
speaker = [NXApp appSpeaker];
NXConvertWinNumToGlobal([window windowNum], &globalWindowNum);
[speaker setSendPort:
NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
[speaker registerWindow: globalWindowNum
toPort:[[NXApp appListener] listenPort]];
return self;
}
- addFilters
{
int i, n;
id aFilter;
n = [filterList count];
for (i = 0; i < n; ++i) {
[matrix addCol];
[textMatrix addCol];
[optionMatrix addCol];
aFilter= [filterList objectAt: i];
[aFilter setStateManager: stateManager];
[aFilter setNameCell: [textMatrix cellAt: 0 : i]];
[aFilter setOptionCell: [optionMatrix cellAt: 0 : i]];
[aFilter update];
}
sizeChanged = YES;
return self;
}
- addFilter: newFilter
{
int index;
[matrix addCol];
[textMatrix addCol];
[optionMatrix addCol];
[filterList addObject: newFilter];
index = [filterList count] -1;
[[filterList lastObject] setNameCell: [textMatrix cellAt: 0 : index]];
[[filterList lastObject] setOptionCell: [optionMatrix cellAt: 0 : index]];
sizeChanged = YES;
return self;
}
- removeFilter
{
int col;
[filterList removeObject: activeFilter];
activeFilter = nil;
col = [matrix selectedCol];
[matrix clearSelectedCell];
[matrix removeColAt: col andFree: YES];
[textMatrix removeColAt: col andFree: YES];
[optionMatrix removeColAt: col andFree: YES];
[[matrix cellList] removeObjectAt: col];
[[textMatrix cellList] removeObjectAt: col];
[[optionMatrix cellList] removeObjectAt: col];
sizeChanged = YES;
return self;
}
- workspaceBitmap:(char *)path
{
int ok, length;
char *tiffData;
NXStream *bitmapStream;
id aBitmap;
[speaker getFileIconFor:path TIFF:&tiffData
TIFFLength:&length ok:&ok];
if (!ok) {
return nil;
}
bitmapStream = NXOpenMemory(tiffData, length, NX_READONLY);
if (!bitmapStream) {
return nil;
}
aBitmap = [[NXImage alloc] initFromStream: bitmapStream];
NXClose(bitmapStream);
return aBitmap;
}
- drawBitmap: aBitmap side: (int) side
{
static NXPoint aPoint = {0.0, 52.0};
[docView lockFocus];
if (side == 0) {
aPoint.x = 0.0;
[aBitmap composite: NX_SOVER toPoint: &aPoint];
} else {
aPoint.x = docFrame.size.width - 48.0;
[aBitmap composite: NX_SOVER toPoint: &aPoint];
}
[docView unlockFocus];
}
// Delegate for Window class
- windowDidBecomeKey: sender
{
[stateManager setActiveScript: self];
if ([matrix selectedCol] >= 0) {
activeFilter = [filterList objectAt: [matrix selectedCol]];
} else {
activeFilter = nil;
}
[stateManager setActiveFilter: activeFilter];
return self;
}
- windowWillClose: sender
{
int response;
if ([window isDocEdited]) {
response = NXRunAlertPanel("Save",
pathname ? "Save changes to %s?" : "Save changes?",
"Save", "No", "Cancel",
pathname);
switch (response) {
case NX_ALERTDEFAULT:
[stateManager saveScript: self];
break;
case NX_ALERTALTERNATE:
break;
case NX_ALERTOTHER:
default:
return nil;
}
}
[speaker unregisterWindow: globalWindowNum];
[stateManager removeScript: self];
[stateManager removeFromActivate: self];
return self;
}
- windowWillMiniaturize: sender toMiniwindow: aWindow
{
miniwindow = aWindow;
NXConvertWinNumToGlobal([miniwindow windowNum], &globalMiniwindowNum);
[stateManager miniaturized: self];
miniaturized = YES;
[speaker unregisterWindow: globalWindowNum];
[speaker registerWindow: globalMiniwindowNum
toPort:[[NXApp appListener] listenPort]];
return self;
}
- windowDidDeminiaturize: sender
{
[stateManager deminiaturized: self];
miniaturized = NO;
[speaker unregisterWindow: globalMiniwindowNum];
[speaker registerWindow: globalWindowNum
toPort:[[NXApp appListener] listenPort]];
return self;
}
// Delegate for MoveMatrix class
- matrixDidExchange: (int) index1 : (int) index2
{
id aFilter, list1, list2;
char *str1, str2;
if (index1 < index2) {
aFilter = [filterList removeObjectAt: index1];
[filterList insertObject: aFilter at: index2-1];
aFilter = [filterList removeObjectAt: index2];
[filterList insertObject: aFilter at: index1];
} else {
aFilter = [filterList removeObjectAt: index2];
[filterList insertObject: aFilter at: index1-1];
aFilter = [filterList removeObjectAt: index1];
[filterList insertObject: aFilter at: index2];
}
[[filterList objectAt: index1] setNameCell: [textMatrix cellAt: 0 : index1]];
[[filterList objectAt: index2] setNameCell: [textMatrix cellAt: 0 : index2]];
[[[filterList objectAt: index1] setOptionCell: [optionMatrix cellAt: 0 : index1]] update];
[[[filterList objectAt: index2] setOptionCell: [optionMatrix cellAt: 0 : index2]] update];
return self;
}
- (unsigned int) globalWindowNum
{
return globalWindowNum;
}
- (unsigned int) globalMiniwindowNum
{
return globalMiniwindowNum;
}
- window
{
return window;
}
- miniwindow
{
return miniwindow;
}
- update
{
NXRect mRect, tRect, oRect;
NXSize sSize;
if (sizeChanged) {
[matrix sizeToCells];
[textMatrix sizeToCells];
[optionMatrix sizeToCells];
[matrix getFrame: &mRect];
[textMatrix getFrame: &tRect];
[optionMatrix getFrame: &oRect];
docFrame = mRect;
docFrame.size.height = mRect.size.height + tRect.size.height + oRect.size.height;
docFrame.size.width += 96.0;
[docView setFrame: &docFrame];
sizeChanged = NO;
if ([stateManager autosize]) {
if (docFrame.size.width < 144.0) docFrame.size.width = 144.0;
if (docFrame.size.width > 960.0) docFrame.size.width = 960.0;
[ScrollView getFrameSize: &sSize
forContentSize: &docFrame.size
horizScroller: YES
vertScroller:YES
borderType:NX_NOBORDER];
[window sizeWindow: sSize.width : sSize.height];
}
}
[window setTitle: scriptName];
[stateManager removeFromActivate: self];
[stateManager addToActivate: self];
[window display];
return self;
}
- activate: sender // glue for activate menu
{
[window makeKeyAndOrderFront: self];
return self;
}
// methods to manipulate filter objects
- parameterChanged
{
if (strcmp(scriptName, [inspector scriptName])) {
[stateManager removeFromActivate: self];
scriptName = stringDup(scriptName, [inspector scriptName]);
[stateManager addToActivate: self];
} else {
scriptName = stringDup(scriptName, [inspector scriptName]);
}
confirm = [inspector confirm];
errorTerminate = [inspector errorTerminate];
foreground = [inspector foreground];
iconType = [inspector iconType];
resultIconPath = stringDup(resultIconPath, [inspector resultIconPath]);
helpText = stringDup(helpText, [inspector helpText]);
return self;
}
- (char *) scriptName {return scriptName;}
- (int) confirm {return confirm;}
- (int) errorTerminate {return errorTerminate;}
- (int) foreground {return foreground;}
- (int) iconType {return iconType;}
- (char *) resultIconPath{return resultIconPath; }
- (char *) helpText {return helpText; }
- setPathname: (char *) aString
{
pathname = stringDup(pathname, aString);
return self;
}
- (char *) pathname
{
return pathname;
}
- write: (NXTypedStream *)ts
{
[super write: ts];
NXWriteTypes(ts, "*iiii**@", &scriptName, &confirm, &errorTerminate, &foreground,
&iconType, &resultIconPath, &helpText, &filterList);
return self;
}
- read: (NXTypedStream *)ts
{
[super read: ts];
if (NXTypedStreamClassVersion(ts, [self name]) != SCRIPT_VERSION) {
NXRunAlertPanel(NULL, "Specified file contains contains incompatible data.\n"
"You might have specified non-script file.",
"OK", NULL, NULL);
return nil;
}
NXReadTypes(ts, "*iiii**@", &scriptName, &confirm, &errorTerminate, &foreground,
&iconType, &resultIconPath, &helpText, &filterList);
return self;
}
- buttonPressed
{
activeFilter = [filterList objectAt: [matrix selectedCol]];
[stateManager setActiveFilter: activeFilter];
}
- filterNameChanged
{
[[filterList objectAt: [textMatrix selectedCol]] nameChanged];
return self;
}
- (int)iconEntered:(int)windowNum at:(double)x :(double)y
iconWindow:(int)iconWindowNum
iconX:(double)iconX iconY:(double)iconY
iconWidth:(double)iconWidth
iconHeight:(double)iconHeight
pathList:(char *)pathList
{
NXSize size = {48.0, 48.0};
NXRect bounds;
bounds.origin.x = bounds.origin.y = 0.0;
bounds.size = size;
bitmap = [[NXImage alloc] initSize: &size];
[bitmap lockFocus];
PSsetgray(NX_LTGRAY);
NXRectFill(&bounds);
copyIconPicture(iconWindowNum, (float) iconX, (float) iconY,
(float) iconWidth, (float) iconHeight);
[bitmap unlockFocus];
iconPathList = stringDup(iconPathList, pathList);
return 0;
}
- fromFile: (char *) inFile toFile: (char **) outFile
pipeTo: (int *) pipefd waitFor: (int *) waitpid
{
int infd = 0, outfd = 1, pfd[2], pid;
char *infile, *outfile, *commandline;
id filter, nextFilter;
int invoke(char *, int, char *, int, char *);
filter = [filterList objectAt: filterIndex];
if (++filterIndex < [matrix cellCount])
nextFilter = [filterList objectAt: filterIndex];
else
nextFilter = nil;
if ([filter inputFromStdin]) {
if (inFile != NULL) {
// namely, previous process wrote output to file; although
// this process demands input from stdin
infile = inFile;
infd = -1;
} else {
// conventional piped execution (right side)
pipe(pfd);
*pipefd = pfd[1];
infd = pfd[0];
}
} else {
if (inFile != NULL) {
// conventional file-to-file, sequential execution
// no redirection needed
} else {
// this must not occur.
}
}
commandline = strdup([filter assignVariable: inFile to: [filter command]]);
if ([filter outputToStdout]) {
if (nextFilter == nil || ! [nextFilter inputFromStdin]) {
// needs redirection to temporary file
outfile = strdup("/tmp/faXXXXXX");
mktemp(outfile);
*outFile = outfile;
outfd = -1;
} else {
// conventional piped execution (left side)
[self fromFile: NULL toFile: outFile pipeTo: &outfd waitFor: waitpid];
}
} else {
*outFile = [filter destination];
if (*outFile[0] == '\0') {
// discard result
} else {
*outFile = strdup([filter assignVariable: inFile to: *outFile]);
}
}
pid = invoke(commandline, infd, infile, outfd, outfile);
free(commandline);
if (outfd <= 1) {
*waitpid = pid;
}
}
- showTargetIcon
{
NXStream *bitmapStream;
char filename[256];
if (iconType) {
[self drawBitmap: [self workspaceBitmap: finalOutputFile] side: 1];
} else {
filename[0] = '\0';
if (resultIconPath[0] != '/') {
strcpy(filename, NXGetDefaultValue(APPNAME, "IconOpenPath"));
strcat(filename, "/");
}
strcat(filename, resultIconPath);
tilde_expand(filename);
bitmapStream = NXOpenFile(open(filename, O_RDONLY), NX_READONLY);
if (!bitmapStream) {
return nil;
}
[self drawBitmap: [[NXImage alloc] initFromStream: bitmapStream] side: 1];
}
}
- animate
{
int i;
if (!iconShown && (iconType == 0 && resultIconPath || iconType && finalOutputFile)) {
[self showTargetIcon];
iconShown = YES;
}
for (i = firstFilter; i < lastFilter; ++i) {
[[matrix cellAt: 0 : i] setIcon:
(flip == 0 ? "filterAnim1" : (flip == 1 ? "filterAnim2" : "filterAnim3"))];
}
flip = ++flip % 3;
return self;
}
void DrawIt (DPSTimedEntry te, double timeNow, void *data)
{
[(id)data animate];
}
- (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
{
int response;
if (processing && foreground) {
*flag = NO;
return 0;
}
if (confirm) {
response = NXRunAlertPanel("Do it?", "Apply %s to \"%s\" ?", "OK", "Cancel", NULL,
scriptName, iconPathList);
if (response == NX_ALERTALTERNATE) {
*flag = NO;
return 0;
}
}
[self drawBitmap: bitmap side: 0];
[window flushWindow];
fromFile = strdup(iconPathList);
filterIndex = 0;
firstFilter = lastFilter = 0;
iconShown = NO;
finalOutputFile = NULL;
processing = YES;
chdir([[filterList objectAt: 0] assignVariable: iconPathList to: "$s"]);
[self executeOneStep];
if (foreground) {
selectedCol = [matrix selectedCol];
[matrix clearSelectedCell];
timedEntry = DPSAddTimedEntry(0.8, &DrawIt, self, NX_BASETHRESHOLD);
}
*flag = YES;
return 0;
}
- executeOneStep
{
char *toFile;
int waitpid, i, n;
if (filterIndex < [filterList count]) {
for (i = firstFilter; i < lastFilter; ++i) {
[[matrix cellAt: 0 : i] setIcon: "filterArrow"];
}
firstFilter = filterIndex;
[self fromFile: fromFile toFile: &toFile pipeTo: NULL waitFor: &waitpid];
lastFilter = filterIndex;
if (fromFile)
free(fromFile);
fromFile = toFile;
if (lastFilter == [filterList count]) // goal
finalOutputFile = fromFile;
[stateManager registerProcess: self id: waitpid];
} else {
processing = NO;
if (foreground) {
n = [matrix cellCount];
for (i = 0; i < n; ++i) {
[[matrix cellAt: 0 : i] setIcon: "filterArrow"];
}
[matrix selectCellAt: 0 : selectedCol];
[window display];
DPSRemoveTimedEntry(timedEntry);
}
}
return self;
}
- childTerminated: (int) pid status: (union wait *) status
{
int response;
int proceed = 1;
if (WIFSTOPPED(*status)) {
response = NXRunAlertPanel(NULL,
"Process %d has stopped due to signal #%d. What now?",
"Kill", "Continue", NULL,
pid, status->w_stopsig);
switch (response) {
case NX_ALERTDEFAULT:
kill(pid, SIGKILL);
proceed = 0;
/* fallthrough */
case NX_ALERTALTERNATE:
kill(pid, SIGCONT);
break;
default:
; // ignore
}
} else if (WIFSIGNALED(*status)) {
NXRunAlertPanel(NULL, "Process %d was terminated by signal #%d. %s",
"OK", NULL, NULL,
pid, status->w_termsig, (status->w_coredump) ? "(Core dumped)" : "");
proceed = 0;
} else if (WIFEXITED(*status)) {
if (errorTerminate && status->w_retcode)
proceed = 0;
}
if (!proceed)
filterIndex = 1000; // such that (filterIndex > [filterList count]) holds
[self executeOneStep];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.