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