# HG changeset patch # User tz # Date 887723885 -3600 # Node ID d6e1a9fced34695a1075f94f8e77de5506cdc6e1 # Parent f8ba35e5a678d58144acb5ea48008ed17ed68525 initial checkin diff -r f8ba35e5a678 -r d6e1a9fced34 ShellView.st --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ShellView.st Tue Feb 17 14:58:05 1998 +0100 @@ -0,0 +1,511 @@ +" + COPYRIGHT (c) 1997 by eXept Software AG + All Rights Reserved + + This software is furnished under a license and may be used + only in accordance with the terms of that license and with the + inclusion of the above copyright notice. This software may not + be provided or otherwise made available to, or used by, any + other person. No title to or ownership of the software is + hereby transferred. +" + + + +ApplicationModel subclass:#ShellView + instanceVariableNames:'task directory commands currentCommand numberOfMaxLines' + classVariableNames:'' + poolDictionaries:'' + category:'Interface-Support' +! + +!ShellView class methodsFor:'documentation'! + +copyright +" + COPYRIGHT (c) 1997 by eXept Software AG + All Rights Reserved + + This software is furnished under a license and may be used + only in accordance with the terms of that license and with the + inclusion of the above copyright notice. This software may not + be provided or otherwise made available to, or used by, any + other person. No title to or ownership of the software is + hereby transferred. +" + + +! + +documentation +" + [author:] + Thomas Zwick +" + + +! ! + +!ShellView class methodsFor:'interface specs'! + +windowSpec + "this window spec was automatically generated by the ST/X UIPainter" + + "do not manually edit this - the painter/builder may not be able to + handle the specification if its corrupted." + + " + UIPainter new openOnClass:ShellView andSelector:#windowSpec + ShellView new openInterface:#windowSpec + " + "ShellView open" + + + + ^ + + #(#FullSpec + #'window:' + #(#WindowSpec + #'name:' 'unnamed canvas' + #'layout:' #(#LayoutFrame 197 0 172 0 496 0 440 0) + #'label:' 'unnamed canvas' + #'min:' #(#Point 10 10) + #'max:' #(#Point 1152 864) + #'bounds:' #(#Rectangle 197 172 497 441) + #'usePreferredExtent:' false + ) + #'component:' + #(#SpecCollection + #'collection:' + #( + #(#TextEditorSpec + #'name:' 'fileContentsView' + #'layout:' #(#LayoutFrame 0 0 0 0.0 0 1 -22 1) + #'model:' #valueOfFileContents + #'hasHorizontalScrollBar:' true + #'hasVerticalScrollBar:' true + #'miniScrollerHorizontal:' true + ) + #(#SequenceViewSpec + #'name:' 'shellOutputView' + #'layout:' #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -22 1.0) + #'model:' #selectionOfShellOutput + #'style:' #(#FontDescription #courier #medium #roman 12) + #'hasHorizontalScrollBar:' true + #'hasVerticalScrollBar:' true + #'miniScrollerHorizontal:' true + #'doubleClickSelector:' #'listDoubleClicked:' + #'useIndex:' true + #'sequenceList:' #listOfShellOutput + ) + #(#ActionButtonSpec + #'name:' 'fileNameButton' + #'layout:' #(#LayoutFrame 0 0.0 -22 1 -60 1.0 0 1.0) + #'model:' #toggleViews + ) + #(#ActionButtonSpec + #'name:' 'saveButton' + #'layout:' #(#LayoutFrame -60 1 -22 1 0 1.0 0 1.0) + #'label:' 'Save' + #'model:' #saveFileContents + ) + #(#ActionButtonSpec + #'name:' 'stopButton' + #'layout:' #(#LayoutFrame 0 0 -22 1 0 1.0 0 1) + #'label:' 'Stop' + #'model:' #terminateTask + ) + #(#InputFieldSpec + #'name:' 'commandInputField' + #'layout:' #(#LayoutFrame 0 0 -22 1 0 1.0 0 1) + #'model:' #valueOfCommand + #'type:' #string + #'acceptOnReturn:' false + #'acceptOnTab:' false + ) + ) + ) + ) +! ! + +!ShellView methodsFor:'accessing'! + +directory + + ^directory ? (directory := Filename currentDirectory asAbsoluteFilename name) +! + +directory: aDirectory + + directory := aDirectory asFilename asAbsoluteFilename name +! + +numberOfMaxLines + + ^numberOfMaxLines ? (numberOfMaxLines := 500) +! + +numberOfMaxLines: anInteger + + numberOfMaxLines := anInteger +! ! + +!ShellView methodsFor:'accessing - views'! + +commandInputField + + ^builder componentAt: #commandInputField +! + +fileContentsView + + ^builder componentAt: #fileContentsView +! + +fileNameButton + + ^builder componentAt: #fileNameButton +! + +saveButton + + ^builder componentAt: #saveButton +! + +shellOutputView + + ^builder componentAt: #shellOutputView +! + +stopButton + + ^builder componentAt: #stopButton +! ! + +!ShellView methodsFor:'actions'! + +executeCommand: aCommand + + |s| + aCommand isNil | task notNil | directory isNil ifTrue: [^nil]. + aCommand key size = 0 ifTrue: [^aCommand key: '']. + + self valueOfCommand value: ''. + self listOfShellOutput + at: self listOfShellOutput size + put: self getDirectoryTextString, (Text string: aCommand key color: Color blue). + + aCommand key trimBlanks = 'clear' + ifTrue: + [ + self shellOutputView raise. + ^self listOfShellOutput contents: (Array with: self getDirectoryTextString). + ]. + + (((s := aCommand key readStream) nextWord = 'cd') and: [(s next = Character space) | s atEnd]) + ifTrue: + [ + s := s upToEnd trimBlanks. + + s size = 0 + ifTrue: + [ + s := Filename homeDirectory + ] + ifFalse: + [ + s first = $/ + ifTrue: [s := s asFilename] + ifFalse: [s := self directory asFilename constructDirectory: s] + ]. + (s exists and: [s isDirectory]) + ifTrue: + [ + directory := s name. + ^self listOfShellOutput contents: (Array with: self getDirectoryTextString). + ]. + ]. + + self stopButton raise. + self shellOutputView raise. + + self + execute: aCommand key + exit: + [ + self commandInputField raise. + aCommand value: true. + self executeNextCommand. + self append: self getDirectoryTextString + ]. +! ! + +!ShellView methodsFor:'aspects'! + +listOfShellOutput + + |holder| + (holder := builder bindingAt:#listOfShellOutput) isNil ifTrue:[ + builder aspectAt:#listOfShellOutput put:(holder := List new). + holder add: self getDirectoryTextString + ]. + ^holder +! + +selectionOfShellOutput + + |holder| + (holder := builder bindingAt:#selectionOfShellOutput) isNil ifTrue:[ + builder aspectAt:#selectionOfShellOutput put: (holder := 1 asValue) + ]. + ^holder +! + +valueOfCommand + + |holder| + (holder := builder bindingAt:#valueOfCommand) isNil ifTrue:[ + builder aspectAt:#valueOfCommand put: + (holder := AspectAdaptor new subject: self; forAspect: #currentCommand). + ]. + ^ holder +! + +valueOfFileContents + + |holder| + (holder := builder bindingAt:#valueOfFileContents) isNil ifTrue:[ + builder aspectAt:#valueOfFileContents put:(holder := ValueHolder new). + ]. + ^holder +! ! + +!ShellView methodsFor:'callbacks'! + +listDoubleClicked: anLineIndex + + |stream streamAtLine possibleFileName selectedFileName fileFound| + possibleFileName := ''. + stream := (self listOfShellOutput at: anLineIndex) readStream. + fileFound := false. + [stream atEnd | fileFound] + whileFalse: + [ + possibleFileName := possibleFileName, stream next. + ((((selectedFileName := possibleFileName) asFilename exists) or: + [(selectedFileName := directory, Filename separator, possibleFileName) asFilename exists]) + and: [selectedFileName asFilename isDirectory not]) + ifTrue: + [ + |possibleLineIndex listSize lineNumber| + fileFound := true. + self openFile: selectedFileName. + [stream atEnd | lineNumber notNil] + whileFalse: + [ + lineNumber := Integer readFrom: stream nextAlphaNumericWord onError: nil. + ]. + lineNumber := lineNumber ? 1. + listSize := self fileContentsView list size. + (lineNumber between: 1 and: listSize) + ifTrue: + [ + self fileContentsView selectLine: lineNumber + ]. + lineNumber > listSize + ifTrue: + [ + self fileContentsView selectLine: listSize + ]. + ]. + ] + +! ! + +!ShellView methodsFor:'private'! + +append:anElement + + anElement notNil + ifTrue: + [ + self listOfShellOutput size > self numberOfMaxLines + ifTrue: + [ + self listOfShellOutput contents: (self listOfShellOutput copyFrom: + self listOfShellOutput size - (self numberOfMaxLines//5) + to: self listOfShellOutput size) + ]. + self listOfShellOutput add: anElement. + self selectionOfShellOutput value: self listOfShellOutput size. + ] + +! + +execute: cmd exit:exitAction + + |outStream| + self terminateTask. + outStream:= PipeStream readingFrom: cmd errorDisposition:#inline inDirectory: directory. + task := + [ + [ + outStream canReadWithoutBlocking + ifTrue: + [ + outStream readWait. + self append: (outStream upTo: Character cr). + ]. + ] doWhile:[outStream atEnd not] + ] forkAt: Processor userBackgroundPriority. + + task addExitAction: + [ + task := nil. + outStream shutDown. + exitAction value + ] +! + +executeNextCommand + + self executeCommand: (commands detect: [:str| str value = false] ifNone: nil) + +! + +getDirectoryTextString + + ^Text string: ('[',self directory, '] > ') emphasis: #bold +! + +listDirectory: dir + + |s match d| + match := (dir name copy reverse upTo: Filename separator) reverse. + + s := dir name copy reverse readStream. + s through: Filename separator. + s := s upToEnd reverse. + dir name = Filename separator asString ifTrue: [s := Filename separator asString]. + + ((d := dir) isDirectory or: + [((d := s asFilename) isDirectory or: + [(d := directory asFilename construct: s) isDirectory])]) + + ifTrue: [ + d directoryContents do: + [:dirEntry| + ((match, '*') match: dirEntry) ifTrue: [self append:dirEntry] + ]. + self append: self getDirectoryTextString + ] +! ! + +!ShellView methodsFor:'selection'! + +currentCommand + + ^currentCommand +! + +currentCommand: aString + + self commandInputField crAction: [self executeCommands: (Array with: currentCommand)]. + self commandInputField entryCompletionBlock:[:contents | + |newString| + newString := Filename + filenameCompletionFor:contents + directory:directory asFilename + directoriesOnly:false + filesOnly:false + ifMultiple: + [:dir | + self listDirectory: dir. + self commandInputField flash. + ]. + self commandInputField contents:newString. + self commandInputField cursorToEndOfLine. + ]. + currentCommand := aString. + self listOfShellOutput size > 0 + ifTrue: + [ + self listOfShellOutput + at: self listOfShellOutput size + put: self getDirectoryTextString, currentCommand + ]. + +! ! + +!ShellView methodsFor:'user actions'! + +executeCommands: aStingCollection + + commands := aStingCollection collect: [:str| str->false]. + + self executeNextCommand +! + +openFile: aFileName + + |fileName| + task isNil & (((fileName := aFileName asFilename) exists) + or: [(fileName := directory asFilename construct: fileName) exists]) + ifTrue: + [ + self fileNameButton raise; sizeFixed: true;label: fileName name. + self fileContentsView raise. + self saveButton raise. + Stream readErrorSignal + handle: + [:ex| + self warn: 'Reading file contents failed!!'. + self toggleViews. + ] + do: + [ + self valueOfFileContents value: fileName contentsOfEntireFile + ] + ] +! + +saveFileContents + + self fileContentsView saveAs: self fileNameButton label +! + +terminateTask + + task notNil ifTrue: + [ + task terminate. + task := nil + ] +! + +toggleViews + + self fileNameButton label isEmpty + ifFalse: + [ + task notNil + ifTrue: [self stopButton raise] + ifFalse: [self commandInputField raise]. + self shellOutputView raise. + self fileNameButton sizeFixed: true; label: ''. + self valueOfFileContents value: ''. + ] + ifTrue: + [ + self fileNameButton raise. + self infoLabel raise. + self fileContentsView raise + ] +! ! + +!ShellView class methodsFor:'documentation'! + +version + ^ '$Header$' +! !