|
1 " |
|
2 COPYRIGHT (c) 1997 by eXept Software AG |
|
3 All Rights Reserved |
|
4 |
|
5 This software is furnished under a license and may be used |
|
6 only in accordance with the terms of that license and with the |
|
7 inclusion of the above copyright notice. This software may not |
|
8 be provided or otherwise made available to, or used by, any |
|
9 other person. No title to or ownership of the software is |
|
10 hereby transferred. |
|
11 " |
|
12 |
|
13 |
|
14 |
|
15 ApplicationModel subclass:#ShellView |
|
16 instanceVariableNames:'task directory commands currentCommand numberOfMaxLines' |
|
17 classVariableNames:'' |
|
18 poolDictionaries:'' |
|
19 category:'Interface-Support' |
|
20 ! |
|
21 |
|
22 !ShellView class methodsFor:'documentation'! |
|
23 |
|
24 copyright |
|
25 " |
|
26 COPYRIGHT (c) 1997 by eXept Software AG |
|
27 All Rights Reserved |
|
28 |
|
29 This software is furnished under a license and may be used |
|
30 only in accordance with the terms of that license and with the |
|
31 inclusion of the above copyright notice. This software may not |
|
32 be provided or otherwise made available to, or used by, any |
|
33 other person. No title to or ownership of the software is |
|
34 hereby transferred. |
|
35 " |
|
36 |
|
37 |
|
38 ! |
|
39 |
|
40 documentation |
|
41 " |
|
42 [author:] |
|
43 Thomas Zwick |
|
44 " |
|
45 |
|
46 |
|
47 ! ! |
|
48 |
|
49 !ShellView class methodsFor:'interface specs'! |
|
50 |
|
51 windowSpec |
|
52 "this window spec was automatically generated by the ST/X UIPainter" |
|
53 |
|
54 "do not manually edit this - the painter/builder may not be able to |
|
55 handle the specification if its corrupted." |
|
56 |
|
57 " |
|
58 UIPainter new openOnClass:ShellView andSelector:#windowSpec |
|
59 ShellView new openInterface:#windowSpec |
|
60 " |
|
61 "ShellView open" |
|
62 |
|
63 <resource: #canvas> |
|
64 |
|
65 ^ |
|
66 |
|
67 #(#FullSpec |
|
68 #'window:' |
|
69 #(#WindowSpec |
|
70 #'name:' 'unnamed canvas' |
|
71 #'layout:' #(#LayoutFrame 197 0 172 0 496 0 440 0) |
|
72 #'label:' 'unnamed canvas' |
|
73 #'min:' #(#Point 10 10) |
|
74 #'max:' #(#Point 1152 864) |
|
75 #'bounds:' #(#Rectangle 197 172 497 441) |
|
76 #'usePreferredExtent:' false |
|
77 ) |
|
78 #'component:' |
|
79 #(#SpecCollection |
|
80 #'collection:' |
|
81 #( |
|
82 #(#TextEditorSpec |
|
83 #'name:' 'fileContentsView' |
|
84 #'layout:' #(#LayoutFrame 0 0 0 0.0 0 1 -22 1) |
|
85 #'model:' #valueOfFileContents |
|
86 #'hasHorizontalScrollBar:' true |
|
87 #'hasVerticalScrollBar:' true |
|
88 #'miniScrollerHorizontal:' true |
|
89 ) |
|
90 #(#SequenceViewSpec |
|
91 #'name:' 'shellOutputView' |
|
92 #'layout:' #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -22 1.0) |
|
93 #'model:' #selectionOfShellOutput |
|
94 #'style:' #(#FontDescription #courier #medium #roman 12) |
|
95 #'hasHorizontalScrollBar:' true |
|
96 #'hasVerticalScrollBar:' true |
|
97 #'miniScrollerHorizontal:' true |
|
98 #'doubleClickSelector:' #'listDoubleClicked:' |
|
99 #'useIndex:' true |
|
100 #'sequenceList:' #listOfShellOutput |
|
101 ) |
|
102 #(#ActionButtonSpec |
|
103 #'name:' 'fileNameButton' |
|
104 #'layout:' #(#LayoutFrame 0 0.0 -22 1 -60 1.0 0 1.0) |
|
105 #'model:' #toggleViews |
|
106 ) |
|
107 #(#ActionButtonSpec |
|
108 #'name:' 'saveButton' |
|
109 #'layout:' #(#LayoutFrame -60 1 -22 1 0 1.0 0 1.0) |
|
110 #'label:' 'Save' |
|
111 #'model:' #saveFileContents |
|
112 ) |
|
113 #(#ActionButtonSpec |
|
114 #'name:' 'stopButton' |
|
115 #'layout:' #(#LayoutFrame 0 0 -22 1 0 1.0 0 1) |
|
116 #'label:' 'Stop' |
|
117 #'model:' #terminateTask |
|
118 ) |
|
119 #(#InputFieldSpec |
|
120 #'name:' 'commandInputField' |
|
121 #'layout:' #(#LayoutFrame 0 0 -22 1 0 1.0 0 1) |
|
122 #'model:' #valueOfCommand |
|
123 #'type:' #string |
|
124 #'acceptOnReturn:' false |
|
125 #'acceptOnTab:' false |
|
126 ) |
|
127 ) |
|
128 ) |
|
129 ) |
|
130 ! ! |
|
131 |
|
132 !ShellView methodsFor:'accessing'! |
|
133 |
|
134 directory |
|
135 |
|
136 ^directory ? (directory := Filename currentDirectory asAbsoluteFilename name) |
|
137 ! |
|
138 |
|
139 directory: aDirectory |
|
140 |
|
141 directory := aDirectory asFilename asAbsoluteFilename name |
|
142 ! |
|
143 |
|
144 numberOfMaxLines |
|
145 |
|
146 ^numberOfMaxLines ? (numberOfMaxLines := 500) |
|
147 ! |
|
148 |
|
149 numberOfMaxLines: anInteger |
|
150 |
|
151 numberOfMaxLines := anInteger |
|
152 ! ! |
|
153 |
|
154 !ShellView methodsFor:'accessing - views'! |
|
155 |
|
156 commandInputField |
|
157 |
|
158 ^builder componentAt: #commandInputField |
|
159 ! |
|
160 |
|
161 fileContentsView |
|
162 |
|
163 ^builder componentAt: #fileContentsView |
|
164 ! |
|
165 |
|
166 fileNameButton |
|
167 |
|
168 ^builder componentAt: #fileNameButton |
|
169 ! |
|
170 |
|
171 saveButton |
|
172 |
|
173 ^builder componentAt: #saveButton |
|
174 ! |
|
175 |
|
176 shellOutputView |
|
177 |
|
178 ^builder componentAt: #shellOutputView |
|
179 ! |
|
180 |
|
181 stopButton |
|
182 |
|
183 ^builder componentAt: #stopButton |
|
184 ! ! |
|
185 |
|
186 !ShellView methodsFor:'actions'! |
|
187 |
|
188 executeCommand: aCommand |
|
189 |
|
190 |s| |
|
191 aCommand isNil | task notNil | directory isNil ifTrue: [^nil]. |
|
192 aCommand key size = 0 ifTrue: [^aCommand key: '']. |
|
193 |
|
194 self valueOfCommand value: ''. |
|
195 self listOfShellOutput |
|
196 at: self listOfShellOutput size |
|
197 put: self getDirectoryTextString, (Text string: aCommand key color: Color blue). |
|
198 |
|
199 aCommand key trimBlanks = 'clear' |
|
200 ifTrue: |
|
201 [ |
|
202 self shellOutputView raise. |
|
203 ^self listOfShellOutput contents: (Array with: self getDirectoryTextString). |
|
204 ]. |
|
205 |
|
206 (((s := aCommand key readStream) nextWord = 'cd') and: [(s next = Character space) | s atEnd]) |
|
207 ifTrue: |
|
208 [ |
|
209 s := s upToEnd trimBlanks. |
|
210 |
|
211 s size = 0 |
|
212 ifTrue: |
|
213 [ |
|
214 s := Filename homeDirectory |
|
215 ] |
|
216 ifFalse: |
|
217 [ |
|
218 s first = $/ |
|
219 ifTrue: [s := s asFilename] |
|
220 ifFalse: [s := self directory asFilename constructDirectory: s] |
|
221 ]. |
|
222 (s exists and: [s isDirectory]) |
|
223 ifTrue: |
|
224 [ |
|
225 directory := s name. |
|
226 ^self listOfShellOutput contents: (Array with: self getDirectoryTextString). |
|
227 ]. |
|
228 ]. |
|
229 |
|
230 self stopButton raise. |
|
231 self shellOutputView raise. |
|
232 |
|
233 self |
|
234 execute: aCommand key |
|
235 exit: |
|
236 [ |
|
237 self commandInputField raise. |
|
238 aCommand value: true. |
|
239 self executeNextCommand. |
|
240 self append: self getDirectoryTextString |
|
241 ]. |
|
242 ! ! |
|
243 |
|
244 !ShellView methodsFor:'aspects'! |
|
245 |
|
246 listOfShellOutput |
|
247 |
|
248 |holder| |
|
249 (holder := builder bindingAt:#listOfShellOutput) isNil ifTrue:[ |
|
250 builder aspectAt:#listOfShellOutput put:(holder := List new). |
|
251 holder add: self getDirectoryTextString |
|
252 ]. |
|
253 ^holder |
|
254 ! |
|
255 |
|
256 selectionOfShellOutput |
|
257 |
|
258 |holder| |
|
259 (holder := builder bindingAt:#selectionOfShellOutput) isNil ifTrue:[ |
|
260 builder aspectAt:#selectionOfShellOutput put: (holder := 1 asValue) |
|
261 ]. |
|
262 ^holder |
|
263 ! |
|
264 |
|
265 valueOfCommand |
|
266 |
|
267 |holder| |
|
268 (holder := builder bindingAt:#valueOfCommand) isNil ifTrue:[ |
|
269 builder aspectAt:#valueOfCommand put: |
|
270 (holder := AspectAdaptor new subject: self; forAspect: #currentCommand). |
|
271 ]. |
|
272 ^ holder |
|
273 ! |
|
274 |
|
275 valueOfFileContents |
|
276 |
|
277 |holder| |
|
278 (holder := builder bindingAt:#valueOfFileContents) isNil ifTrue:[ |
|
279 builder aspectAt:#valueOfFileContents put:(holder := ValueHolder new). |
|
280 ]. |
|
281 ^holder |
|
282 ! ! |
|
283 |
|
284 !ShellView methodsFor:'callbacks'! |
|
285 |
|
286 listDoubleClicked: anLineIndex |
|
287 |
|
288 |stream streamAtLine possibleFileName selectedFileName fileFound| |
|
289 possibleFileName := ''. |
|
290 stream := (self listOfShellOutput at: anLineIndex) readStream. |
|
291 fileFound := false. |
|
292 [stream atEnd | fileFound] |
|
293 whileFalse: |
|
294 [ |
|
295 possibleFileName := possibleFileName, stream next. |
|
296 ((((selectedFileName := possibleFileName) asFilename exists) or: |
|
297 [(selectedFileName := directory, Filename separator, possibleFileName) asFilename exists]) |
|
298 and: [selectedFileName asFilename isDirectory not]) |
|
299 ifTrue: |
|
300 [ |
|
301 |possibleLineIndex listSize lineNumber| |
|
302 fileFound := true. |
|
303 self openFile: selectedFileName. |
|
304 [stream atEnd | lineNumber notNil] |
|
305 whileFalse: |
|
306 [ |
|
307 lineNumber := Integer readFrom: stream nextAlphaNumericWord onError: nil. |
|
308 ]. |
|
309 lineNumber := lineNumber ? 1. |
|
310 listSize := self fileContentsView list size. |
|
311 (lineNumber between: 1 and: listSize) |
|
312 ifTrue: |
|
313 [ |
|
314 self fileContentsView selectLine: lineNumber |
|
315 ]. |
|
316 lineNumber > listSize |
|
317 ifTrue: |
|
318 [ |
|
319 self fileContentsView selectLine: listSize |
|
320 ]. |
|
321 ]. |
|
322 ] |
|
323 |
|
324 ! ! |
|
325 |
|
326 !ShellView methodsFor:'private'! |
|
327 |
|
328 append:anElement |
|
329 |
|
330 anElement notNil |
|
331 ifTrue: |
|
332 [ |
|
333 self listOfShellOutput size > self numberOfMaxLines |
|
334 ifTrue: |
|
335 [ |
|
336 self listOfShellOutput contents: (self listOfShellOutput copyFrom: |
|
337 self listOfShellOutput size - (self numberOfMaxLines//5) |
|
338 to: self listOfShellOutput size) |
|
339 ]. |
|
340 self listOfShellOutput add: anElement. |
|
341 self selectionOfShellOutput value: self listOfShellOutput size. |
|
342 ] |
|
343 |
|
344 ! |
|
345 |
|
346 execute: cmd exit:exitAction |
|
347 |
|
348 |outStream| |
|
349 self terminateTask. |
|
350 outStream:= PipeStream readingFrom: cmd errorDisposition:#inline inDirectory: directory. |
|
351 task := |
|
352 [ |
|
353 [ |
|
354 outStream canReadWithoutBlocking |
|
355 ifTrue: |
|
356 [ |
|
357 outStream readWait. |
|
358 self append: (outStream upTo: Character cr). |
|
359 ]. |
|
360 ] doWhile:[outStream atEnd not] |
|
361 ] forkAt: Processor userBackgroundPriority. |
|
362 |
|
363 task addExitAction: |
|
364 [ |
|
365 task := nil. |
|
366 outStream shutDown. |
|
367 exitAction value |
|
368 ] |
|
369 ! |
|
370 |
|
371 executeNextCommand |
|
372 |
|
373 self executeCommand: (commands detect: [:str| str value = false] ifNone: nil) |
|
374 |
|
375 ! |
|
376 |
|
377 getDirectoryTextString |
|
378 |
|
379 ^Text string: ('[',self directory, '] > ') emphasis: #bold |
|
380 ! |
|
381 |
|
382 listDirectory: dir |
|
383 |
|
384 |s match d| |
|
385 match := (dir name copy reverse upTo: Filename separator) reverse. |
|
386 |
|
387 s := dir name copy reverse readStream. |
|
388 s through: Filename separator. |
|
389 s := s upToEnd reverse. |
|
390 dir name = Filename separator asString ifTrue: [s := Filename separator asString]. |
|
391 |
|
392 ((d := dir) isDirectory or: |
|
393 [((d := s asFilename) isDirectory or: |
|
394 [(d := directory asFilename construct: s) isDirectory])]) |
|
395 |
|
396 ifTrue: [ |
|
397 d directoryContents do: |
|
398 [:dirEntry| |
|
399 ((match, '*') match: dirEntry) ifTrue: [self append:dirEntry] |
|
400 ]. |
|
401 self append: self getDirectoryTextString |
|
402 ] |
|
403 ! ! |
|
404 |
|
405 !ShellView methodsFor:'selection'! |
|
406 |
|
407 currentCommand |
|
408 |
|
409 ^currentCommand |
|
410 ! |
|
411 |
|
412 currentCommand: aString |
|
413 |
|
414 self commandInputField crAction: [self executeCommands: (Array with: currentCommand)]. |
|
415 self commandInputField entryCompletionBlock:[:contents | |
|
416 |newString| |
|
417 newString := Filename |
|
418 filenameCompletionFor:contents |
|
419 directory:directory asFilename |
|
420 directoriesOnly:false |
|
421 filesOnly:false |
|
422 ifMultiple: |
|
423 [:dir | |
|
424 self listDirectory: dir. |
|
425 self commandInputField flash. |
|
426 ]. |
|
427 self commandInputField contents:newString. |
|
428 self commandInputField cursorToEndOfLine. |
|
429 ]. |
|
430 currentCommand := aString. |
|
431 self listOfShellOutput size > 0 |
|
432 ifTrue: |
|
433 [ |
|
434 self listOfShellOutput |
|
435 at: self listOfShellOutput size |
|
436 put: self getDirectoryTextString, currentCommand |
|
437 ]. |
|
438 |
|
439 ! ! |
|
440 |
|
441 !ShellView methodsFor:'user actions'! |
|
442 |
|
443 executeCommands: aStingCollection |
|
444 |
|
445 commands := aStingCollection collect: [:str| str->false]. |
|
446 |
|
447 self executeNextCommand |
|
448 ! |
|
449 |
|
450 openFile: aFileName |
|
451 |
|
452 |fileName| |
|
453 task isNil & (((fileName := aFileName asFilename) exists) |
|
454 or: [(fileName := directory asFilename construct: fileName) exists]) |
|
455 ifTrue: |
|
456 [ |
|
457 self fileNameButton raise; sizeFixed: true;label: fileName name. |
|
458 self fileContentsView raise. |
|
459 self saveButton raise. |
|
460 Stream readErrorSignal |
|
461 handle: |
|
462 [:ex| |
|
463 self warn: 'Reading file contents failed!!'. |
|
464 self toggleViews. |
|
465 ] |
|
466 do: |
|
467 [ |
|
468 self valueOfFileContents value: fileName contentsOfEntireFile |
|
469 ] |
|
470 ] |
|
471 ! |
|
472 |
|
473 saveFileContents |
|
474 |
|
475 self fileContentsView saveAs: self fileNameButton label |
|
476 ! |
|
477 |
|
478 terminateTask |
|
479 |
|
480 task notNil ifTrue: |
|
481 [ |
|
482 task terminate. |
|
483 task := nil |
|
484 ] |
|
485 ! |
|
486 |
|
487 toggleViews |
|
488 |
|
489 self fileNameButton label isEmpty |
|
490 ifFalse: |
|
491 [ |
|
492 task notNil |
|
493 ifTrue: [self stopButton raise] |
|
494 ifFalse: [self commandInputField raise]. |
|
495 self shellOutputView raise. |
|
496 self fileNameButton sizeFixed: true; label: ''. |
|
497 self valueOfFileContents value: ''. |
|
498 ] |
|
499 ifTrue: |
|
500 [ |
|
501 self fileNameButton raise. |
|
502 self infoLabel raise. |
|
503 self fileContentsView raise |
|
504 ] |
|
505 ! ! |
|
506 |
|
507 !ShellView class methodsFor:'documentation'! |
|
508 |
|
509 version |
|
510 ^ '$Header$' |
|
511 ! ! |