ShellView.st
author tz
Sun, 22 Feb 1998 21:32:37 +0100
changeset 661 34af6869d5b8
parent 643 d6e1a9fced34
child 757 d28fb65967a9
permissions -rw-r--r--
consistent ID-naming of the widgets

"
 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$'
! !