comparison pkg-src/tree-nx/TreeController.m @ 163:0132846995bd r20-3b8

Import from CVS: tag r20-3b8
author cvs
date Mon, 13 Aug 2007 09:43:35 +0200
parents
children
comparison
equal deleted inserted replaced
162:4de2936b4e77 163:0132846995bd
1
2 #import <stdio.h>
3
4 #import "TreeController.h"
5 #import "NamedTree.h"
6 #import "TreeView.h"
7
8 @implementation TreeController
9
10 - appDidInit:(Application *)sender
11 {
12 BOOL haveOpenedDocument = NO; // whether we have opened a document
13
14 // // Gets the public port for SomeApp
15 // port_t thePort = NXPortFromName("Emacs", NULL);
16 //
17 // if (thePort != PORT_NULL)
18 // // Sets the Speaker to send its next message to SomeApp's port
19 // [[NXApp appSpeaker] setSendPort:thePort];
20
21 if (NXArgc > 1)
22 {
23 int i;
24
25 for (i = 1; i < NXArgc; i++)
26 {
27 haveOpenedDocument = [self openFile: NXArgv[i]] || haveOpenedDocument;
28 }
29 }
30
31 return self;
32 }
33
34 - init
35 {
36 [super init];
37 first = YES;
38 nextX = 200;
39 nextY = 600;
40 return self;
41 }
42
43 - info:sender // bring up the info panel, obviously
44 {
45 if(!infoPanel)
46 [NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
47 return [infoPanel orderFront:sender];
48 }
49
50 - open:sender
51 { // use open panel to select a file -- only with .tree extension.
52 // only one file may be loaded at a time.
53 const char *const *files;
54 char *file;
55 const char *dir;
56 static const char *const ft[2] = {"tree", NULL};
57
58 id openPanel = [OpenPanel new];
59 [openPanel allowMultipleFiles:NO];
60 if (first) {
61 [openPanel runModalForDirectory:[[NXBundle mainBundle] directory]
62 file:NULL types:ft];
63 first = NO;
64 } else [openPanel runModalForTypes:ft];
65 files = [openPanel filenames];
66 dir = [openPanel directory];
67 file = malloc(strlen(files[0]) + strlen(dir) + 8);
68 strcpy(file, dir);
69 strcat(file,"/");
70 strcat(file, files[0]);
71 strcat(file, "\0");
72 [self openFile:file];
73 return self;
74 }
75
76 char nextChar; // This allows me to do single character lookahead in scan
77
78 id readToNewline(FILE *file) // used to parse a file; reads until a newline
79 { // returns a string object... reads until EOL to get string value.
80 id newString = [[String alloc] init];
81 char *buffer = (char *)malloc(1024); // should be plenty big
82 char *spot = buffer;
83
84 while (nextChar != '\n')
85 {
86 spot[0] = nextChar; spot++; nextChar = fgetc(file);
87 }
88 spot[0] = '\0'; // terminate the string
89 nextChar = fgetc(file); // Get next char for next invocation of this function
90 [newString setString:buffer];
91 free(buffer);
92 return newString;
93 }
94
95 char prevChar; // This allows me to do single character lookback in scan
96
97 id readToSep(FILE *file) // used to parse a file; reads until a newline or a "^^" sequence
98 { // returns a string object... reads until EOL to get string value.
99 id newString = [[String alloc] init];
100 char *buffer = (char *)malloc(1024); // should be plenty big
101 char *spot = buffer;
102 int c;
103
104 while (nextChar != '\n')
105 {
106 if (nextChar == '^')
107 if ((c = fgetc(file)) == '^')
108 break;
109 else
110 ungetc(c, file);
111 spot[0] = nextChar; spot++; nextChar = fgetc(file);
112 }
113 spot[0] = '\0'; // terminate the string
114 prevChar = nextChar;
115 nextChar = fgetc(file); // Get next char for next invocation of this function
116 [newString setString:buffer];
117 free(buffer);
118 return newString;
119 }
120
121 // This actually opens a file. WorkSpace and Open panel methods both
122 // eventually get to here to do the real work. This code is pretty much
123 // worth ignoring, unless you _really_ care about how I read in the
124 // files. It's mostly specific to the file format so it's not
125 // generally useful. The framework for this code came from the
126 // code in my "Viewer.app" that is in with some PD raytracers I ported
127 // to the NeXT--allows viewing an rgb bitmap file; I wrote it before
128 // GW and ImageViewer existed... (See raytracers.tar.Z on sonata/orst)
129 - (BOOL)openFile:(const char *)name
130 {
131 // id alert;
132 id aString, treeName, rootNode, workingNode, tempNode;
133 id newString, stack = [[List alloc] init];
134 int indLevel, numSpaces, indent = 0;
135 char *tempString;
136 BOOL rStat = YES;
137 FILE *file;
138 // for debugging:
139 //NXStream *out = NXOpenFile(fileno(stdout), NX_WRITEONLY);
140
141 // get a new doc window.
142 [NXApp loadNibSection:"DocWindow.nib" owner:self withNames:NO];
143 [[treeView window] setTitleAsFilename:name];
144 // put up an alert panel to let user know we're busy
145 // alert = NXGetAlertPanel(NULL, "Reading tree and creating image.",
146 // NULL, NULL, NULL);
147 // [alert makeKeyAndOrderFront:self];
148 // Read the tree file. NOTE THAT THIS DOES NOT DO ERROR CHECKING.
149 file = fopen(name, "r");
150 nextChar = fgetc(file); // prime the system
151 treeName = readToNewline(file); // first line is tree's name
152 aString = readToSep(file); // Get the name of the root node.
153 rootNode = [[[NamedTree alloc]
154 initLabelString:aString] setTreeName:treeName];
155 if (prevChar != '\n')
156 [rootNode setValue: readToSep(file)]; // Set the node's value.
157 [stack insertObject:rootNode at:0];
158 workingNode = rootNode;
159 // figure out the indentation
160 while (nextChar == ' ') {
161 indent++;
162 nextChar = fgetc(file);
163 }
164 aString = readToSep(file); // get name of child node
165 tempNode = [[[NamedTree alloc]
166 initLabelString:aString] setTreeName:treeName];
167 if (prevChar != '\n')
168 [tempNode setValue: readToSep(file)]; // Set the node's value.
169 [workingNode addBranch:tempNode];
170 [stack insertObject:tempNode at:0];
171 workingNode = tempNode;
172 // now that we know the file's char's, we read in the other nodes
173 // I use a List object as if it were a stack and push parent nodes on
174 // it while working on children rather than doing a recursive function.
175 // the comments are sparse, just know that it's mostly pushing and
176 // popping from the stack to get at the right parent to add a child to.
177 while (!feof(file)) {
178 aString = readToSep(file); // next node name + indentation.
179 // find out # of indentation spaces and strip them off
180 // *** This gives a warning: ignore it, it's unimportant here.
181 tempString = [aString stringValue]; numSpaces = 0;
182 while (tempString[0] == ' ') {
183 numSpaces++; tempString++;
184 }
185 indLevel = numSpaces / indent;
186 if (indLevel == ([stack count] - 1)) // same level as last object
187 {
188 [stack removeObjectAt:0];
189 workingNode = [stack objectAt:0];
190 newString = [[String alloc] initString:tempString];
191 [aString free];
192 tempNode = [[[NamedTree alloc]
193 initLabelString:newString] setTreeName:treeName];
194 if (prevChar != '\n')
195 [tempNode setValue: readToSep(file)]; // Set the node's value.
196 [workingNode addBranch:tempNode];
197 [stack insertObject:tempNode at:0];
198 workingNode = tempNode;
199 } else if (indLevel == ([stack count])) { // child of last node
200 newString = [[String alloc] initString:tempString];
201 [aString free];
202 tempNode = [[[NamedTree alloc]
203 initLabelString:newString] setTreeName:treeName];
204 if (prevChar != '\n')
205 [tempNode setValue: readToSep(file)]; // Set the node's value.
206 [workingNode addBranch:tempNode];
207 [stack insertObject:tempNode at:0];
208 workingNode = tempNode;
209 } else if (indLevel < [stack count]) { // higher level, so pop
210 while (indLevel < [stack count]) { // pop until at right level
211 [stack removeObjectAt:0];
212 workingNode = [stack objectAt:0];
213 } // now add new node since we're at the level
214 newString = [[String alloc] initString:tempString];
215 [aString free];
216 tempNode = [[[NamedTree alloc]
217 initLabelString:newString] setTreeName:treeName];
218 if (prevChar != '\n')
219 [tempNode setValue: readToSep(file)]; // Set the node's value.
220 [workingNode addBranch:tempNode];
221 [stack insertObject:tempNode at:0];
222 workingNode = tempNode;
223 } else { // typically, if user goes in two levels at once, which
224 // doesn't make any sense semantically
225 fprintf(stderr, "Error: level too deep!\n");
226 rStat = NO;
227 }
228 }
229 // Debugging code to pretty print the parsed tree. If this output
230 // is correct, then we know that the parse was OK.
231 //printf("\nHere's the parsed tree:\n");
232 //printf("Tree name: \"%s\".", [treeName stringValue]);
233 //[rootNode dumpTree:out level:0 indent:" "];
234 //printf("\n\n");
235 //NXClose(out);
236 // end debug code
237 // rStat = return status of tree reader YES = success
238 // Now attach the Tree to the TreeView...
239 [treeView attachTree:rootNode];
240 // and now bring up the window for the user
241 [[treeView window] moveTo:nextX :(nextY-218)];
242 // Get rid of the alert
243 // [alert orderOut:self];
244 // [alert free];
245 nextX += 22; nextY -= 25;
246 if (nextX > 370)
247 {
248 nextX = 200; nextY = 600;
249 }
250 [[treeView window] makeKeyAndOrderFront:self];
251 return rStat;
252 }
253
254
255 // The next two methods allow the WorkSpace to open a .tree file when
256 // it is double-clicked. (Or any file that's cmd-dragged over our icon.)
257
258 - (BOOL)appAcceptsAnotherFile:sender { return YES; }
259 - (int)app:sender openFile:(const char *)file type:(const char *)type
260 {
261 return [self openFile:file];
262 }
263
264
265 @end