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