ShellView.st
changeset 643 d6e1a9fced34
child 757 d28fb65967a9
--- /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"
+
+    <resource: #canvas>
+
+    ^
+     
+       #(#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$'
+! !