This is TreeController.m in view mode; [Download] [Up]
// Written by Don Yacktman Copyright (c) 1994 by Don Yacktman.
// Version 1.0. All rights reserved.
//
// Modified by Aleksey Sudakov <zander@cnext.crec.mipt.ru>
// * Aug. 17, 1995 * Added save to EPS/TIFF
//
// This notice may not be removed from this source code.
//
// This program is included in the MiscKit by permission from the author
// and its use is governed by the MiscKit license, found in the file
// "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
// for a list of all applicable permissions and restrictions.
//
#import <stdio.h>
#import <misckit/MiscString.h>
#import "TreeController.h"
#import "NamedTree.h"
#import "TreeView.h"
#import "SavePanelWithPopUp.subproj/SavePanelWithPopUp.h"
@implementation TreeController
+ initialize
{
[SavePanel setSavePanelFactory: [SavePanelWithPopUp class]];
return self;
}
- appDidInit:(Application *)sender
{
BOOL haveOpenedDocument = NO; // whether we have opened a document
// // Gets the public port for SomeApp
// port_t thePort = NXPortFromName("Emacs", NULL);
//
// if (thePort != PORT_NULL)
// // Sets the Speaker to send its next message to SomeApp's port
// [[NXApp appSpeaker] setSendPort:thePort];
if (NXArgc > 1)
{
int i;
for (i = 1; i < NXArgc; i++)
{
haveOpenedDocument = [self openFile: NXArgv[i]] || haveOpenedDocument;
}
}
return self;
}
- init
{
[super init];
first = YES;
nextX = 200;
nextY = 600;
return self;
}
- info:sender // bring up the info panel, obviously
{
if(!infoPanel)
[NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
return [infoPanel orderFront:sender];
}
- open:sender
{ // use open panel to select a file -- only with .tree extension.
// only one file may be loaded at a time.
const char *const *files;
char *file;
const char *dir;
static const char *const ft[2] = {"tree", NULL};
id openPanel = [OpenPanel new];
[openPanel allowMultipleFiles:NO];
if (first) {
[openPanel runModalForDirectory:[(NXBundle *)[NXBundle mainBundle] directory]
file:NULL types:ft];
first = NO;
} else [openPanel runModalForTypes:ft];
files = [openPanel filenames];
dir = [(OpenPanel *)openPanel directory];
file = malloc(strlen(files[0]) + strlen(dir) + 8);
strcpy(file, dir);
strcat(file,"/");
strcat(file, files[0]);
strcat(file, "\0");
[self openFile:file];
return self;
}
- save:sender
{
id save = [SavePanelWithPopUp new];
[save addType: "eps" withDescription:"Encapsulated PostScript"];
[save addType: "tiff" withDescription:"Tag Image File Format"];
[save registerPopUp];
if( [save runModal] == 1 )
{
NXStream* aStream = NXOpenMemory(0,0,NX_WRITEONLY);
if( !strcmp( [save selectedType], "eps") )
[self copyPSToStream:aStream forView:treeView];
else
[self copyTIFFToStream:aStream forView:treeView];
NXSaveToFile(aStream, [save filename]);
NXCloseMemory(aStream, NX_FREEBUFFER);
}
return self;
}
char nextChar; // This allows me to do single character lookahead in scan
id readToNewline(FILE *file) // used to parse a file; reads until a newline
{ // returns a string object... reads until EOL to get string value.
id newString = [[MiscString alloc] init];
char *buffer = (char *)malloc(1024); // should be plenty big
char *spot = buffer;
while (nextChar != '\n')
{
spot[0] = nextChar; spot++; nextChar = fgetc(file);
}
spot[0] = '\0'; // terminate the string
nextChar = fgetc(file); // Get next char for next invocation of this function
[newString setString:buffer];
free(buffer);
return newString;
}
char prevChar; // This allows me to do single character lookback in scan
id readToSep(FILE *file) // used to parse a file; reads until a newline or a "^^" sequence
{ // returns a string object... reads until EOL to get string value.
id newString = [[MiscString alloc] init];
char *buffer = (char *)malloc(1024); // should be plenty big
char *spot = buffer;
int c;
while (nextChar != '\n')
{
if (nextChar == '^')
if ((c = fgetc(file)) == '^')
break;
else
ungetc(c, file);
spot[0] = nextChar; spot++; nextChar = fgetc(file);
}
spot[0] = '\0'; // terminate the string
prevChar = nextChar;
nextChar = fgetc(file); // Get next char for next invocation of this function
[newString setString:buffer];
free(buffer);
return newString;
}
// This actually opens a file. WorkSpace and Open panel methods both
// eventually get to here to do the real work. This code is pretty much
// worth ignoring, unless you _really_ care about how I read in the
// files. It's mostly specific to the file format so it's not
// generally useful. The framework for this code came from the
// code in my "Viewer.app" that is in with some PD raytracers I ported
// to the NeXT--allows viewing an rgb bitmap file; I wrote it before
// GW and ImageViewer existed... (See raytracers.tar.Z on sonata/orst)
- (BOOL)openFile:(const char *)name
{
// id alert;
id aString, treeName, rootNode, workingNode, tempNode;
id newString, stack = [[List alloc] init];
int indLevel, numSpaces, indent = 0;
const char *tempString;
BOOL rStat = YES;
FILE *file;
// for debugging:
//NXStream *out = NXOpenFile(fileno(stdout), NX_WRITEONLY);
// get a new doc window.
[NXApp loadNibSection:"DocWindow.nib" owner:self withNames:NO];
[[treeView window] setTitleAsFilename:name];
// put up an alert panel to let user know we're busy
// alert = NXGetAlertPanel(NULL, "Reading tree and creating image.",
// NULL, NULL, NULL);
// [alert makeKeyAndOrderFront:self];
// Read the tree file. NOTE THAT THIS DOES NOT DO ERROR CHECKING.
file = fopen(name, "r");
nextChar = fgetc(file); // prime the system
treeName = readToNewline(file); // first line is tree's name
aString = readToSep(file); // Get the name of the root node.
rootNode = [[[NamedTree alloc]
initLabelString:aString] setTreeName:treeName];
if (prevChar != '\n')
[rootNode setValue: readToSep(file)]; // Set the node's value.
[stack insertObject:rootNode at:0];
workingNode = rootNode;
// figure out the indentation
while (nextChar == ' ') {
indent++;
nextChar = fgetc(file);
}
aString = readToSep(file); // get name of child node
tempNode = [[[NamedTree alloc]
initLabelString:aString] setTreeName:treeName];
if (prevChar != '\n')
[tempNode setValue: readToSep(file)]; // Set the node's value.
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
// now that we know the file's char's, we read in the other nodes
// I use a List object as if it were a stack and push parent nodes on
// it while working on children rather than doing a recursive function.
// the comments are sparse, just know that it's mostly pushing and
// popping from the stack to get at the right parent to add a child to.
while (!feof(file)) {
aString = readToSep(file); // next node name + indentation.
// find out # of indentation spaces and strip them off
// *** This gives a warning: ignore it, it's unimportant here.
tempString = [aString stringValue]; numSpaces = 0;
while (tempString[0] == ' ') {
numSpaces++; tempString++;
}
indLevel = numSpaces / indent;
if (indLevel == ([stack count] - 1)) // same level as last object
{
[stack removeObjectAt:0];
workingNode = [stack objectAt:0];
newString = [[MiscString alloc] initString:tempString];
[aString free];
tempNode = [[[NamedTree alloc]
initLabelString:newString] setTreeName:treeName];
if (prevChar != '\n')
[tempNode setValue: readToSep(file)]; // Set the node's value.
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
} else if (indLevel == ([stack count])) { // child of last node
newString = [[MiscString alloc] initString:tempString];
[aString free];
tempNode = [[[NamedTree alloc]
initLabelString:newString] setTreeName:treeName];
if (prevChar != '\n')
[tempNode setValue: readToSep(file)]; // Set the node's value.
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
} else if (indLevel < [stack count]) { // higher level, so pop
while (indLevel < [stack count]) { // pop until at right level
[stack removeObjectAt:0];
workingNode = [stack objectAt:0];
} // now add new node since we're at the level
newString = [[MiscString alloc] initString:tempString];
[aString free];
tempNode = [[[NamedTree alloc]
initLabelString:newString] setTreeName:treeName];
if (prevChar != '\n')
[tempNode setValue: readToSep(file)]; // Set the node's value.
[workingNode addBranch:tempNode];
[stack insertObject:tempNode at:0];
workingNode = tempNode;
} else { // typically, if user goes in two levels at once, which
// doesn't make any sense semantically
fprintf(stderr, "Error: level too deep!\n");
rStat = NO;
}
}
// Debugging code to pretty print the parsed tree. If this output
// is correct, then we know that the parse was OK.
//printf("\nHere's the parsed tree:\n");
//printf("Tree name: \"%s\".", [treeName stringValue]);
//[rootNode dumpTree:out level:0 indent:" "];
//printf("\n\n");
//NXClose(out);
// end debug code
// rStat = return status of tree reader YES = success
// Now attach the Tree to the TreeView...
[treeView attachTree:rootNode];
// and now bring up the window for the user
[[treeView window] moveTo:nextX :(nextY-218)];
// Get rid of the alert
// [alert orderOut:self];
// [alert free];
nextX += 22; nextY -= 25;
if (nextX > 370)
{
nextX = 200; nextY = 600;
}
[[treeView window] makeKeyAndOrderFront:self];
return rStat;
}
- copyPSToStream:(NXStream*)aStream forView:view
{
NXRect bounds;
[view getBounds: &bounds];
[view copyPSCodeInside:&bounds to:aStream];
return self;
}
- copyTIFFToStream:(NXStream*)aStream forView:view
{
NXRect bounds;
id image;
NXStream* stream;
stream = NXOpenMemory(0,0,NX_READWRITE);
[view getBounds: &bounds];
[view copyPSCodeInside:&bounds to:stream];
NXSeek(stream,0L,NX_FROMSTART);
image = [[NXImage alloc] initFromStream: stream];
[image setCacheDepthBounded: NO];
[image writeTIFF: aStream];
[image free];
NXCloseMemory(stream,NX_FREEBUFFER);
return self;
}
// The next two methods allow the WorkSpace to open a .tree file when
// it is double-clicked. (Or any file that's cmd-dragged over our icon.)
- (BOOL)appAcceptsAnotherFile:sender { return YES; }
- (int)app:sender openFile:(const char *)file type:(const char *)type
{
return [self openFile:file];
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.