Mercurial > hg > xemacs-beta
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 |