DirectoryContentsBrowser.st
author Claus Gittinger <cg@exept.de>
Sat, 12 Oct 2002 02:48:29 +0200
changeset 3980 3f8311a42ca7
parent 3978 951776c3a9a3
child 3983 dd593a7482fa
permissions -rw-r--r--
menus reorganized

"{ Package: 'stx:libtool' }"

AbstractFileBrowser subclass:#DirectoryContentsBrowser
	instanceVariableNames:'browser updateDirectoryContentsTask modificationTime directory
		columnDescriptors filterBlock iconIndex fileDescriptionIndex
		iconExtent tableColumns sortBlock currentSortOrder previewIndex
		allItemsList updateColumnsTask currentItemList wantSelectFiles
		updateToExternFileHolderLock updateTaskSema updateFromSensorTask
		inDropMode draggedItem canDropItem viewBrowserMenu'
	classVariableNames:''
	poolDictionaries:''
	category:'Interface-Tools-File'
!

Object subclass:#DirectoryContentsItem
	instanceVariableNames:'fileName fileInfo icon fileType
		contentsBrowserChangeModificationTime suffix preview group owner
		timeAndDate modifiedByMe mimeType iconKey mimeTypeForContents'
	classVariableNames:'LastUIDToUserNameMapping LastGIDToGroupNameMapping'
	poolDictionaries:''
	privateIn:DirectoryContentsBrowser
!

!DirectoryContentsBrowser class methodsFor:'documentation'!

documentation

"
    DirectoryContentsBrowser openIn:'/home/ca/work/stx/goodies/bitmaps/xpmBitmaps'
    DirectoryContentsBrowser openIn:'/home/ca/TEST'
"
! !

!DirectoryContentsBrowser class methodsFor:'instance creation'!

openIn:aDirectory
    "opens a directory (standalone) for a directory
    "
    self openIn:aDirectory withDirectories:false

"
self openIn:(Filename currentDirectory construct:'.cvsignore')
"
!

openIn:aDirectory withDirectories:withDirectoriesBoolean
    "opens a directory (standalone) for a directory
    "
    |browser|

    browser := DirectoryContentsBrowser new.
    browser open.
    aDirectory notNil ifTrue:[
        browser currentFileNameHolder value:(OrderedCollection with:(aDirectory asFilename))
    ].
    browser viewDirectoriesInDirectoryContentsBrowser value:withDirectoriesBoolean.
  ^ browser

"
self openIn:(Filename currentDirectory asAbsoluteFilename) withDirectories:false
"
! !

!DirectoryContentsBrowser class methodsFor:'classAccess'!

itemClass

    ^ DirectoryContentsItem


"
self itemClass
"
! !

!DirectoryContentsBrowser class methodsFor:'constant'!

updateTaskPriority
    "returns the priority of the update task
    "
    ^ 8
! !

!DirectoryContentsBrowser class methodsFor:'image specs'!

detailsMenuIcon2
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the ImageEditor may not be able to read the specification."

    "
     self detailsMenuIcon2 inspect
     ImageEditor openOnClass:self andSelector:#detailsMenuIcon2
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'DirectoryContentsBrowser class detailsMenuIcon2'
        ifAbsentPut:[(Depth1Image new) width: 11; height: 5; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 255 255]; mask:((Depth1Image new) width: 11; height: 5; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'* AT@B @D@@@@@@a') ; yourself); yourself]
!

detailsMenuIconR
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the ImageEditor may not be able to read the specification."

    "
     self detailsMenuIconR inspect
     ImageEditor openOnClass:self andSelector:#detailsMenuIconR
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'DirectoryContentsBrowser class detailsMenuIconR'
        ifAbsentPut:[(Depth1Image new) width: 5; height: 7; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 255 255]; mask:((Depth1Image new) width: 5; height: 7; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:' DB TJA@ @@a') ; yourself); yourself]
! !

!DirectoryContentsBrowser class methodsFor:'interface specs'!

detailsMenuIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the ImageEditor may not be able to read the specification."

    "
     self detailsMenuIcon inspect
     ImageEditor openOnClass:self andSelector:#detailsMenuIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'DirectoryContentsBrowser class detailsMenuIcon'
        ifAbsentPut:[(Depth1Image new) width: 7; height: 5; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@b') ; colorMapFromArray:#[0 0 0 255 255 255]; mask:((Depth1Image new) width: 7; height: 5; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@W49DPDb') ; yourself); yourself]
!

tableColumns
    "This resource specification was automatically generated
     by the DataSetBuilder of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the DataSetBuilder may not be able to read the specification."

    "
     DataSetBuilder new openOnClass:DirectoryContentsBrowser andSelector:#tableColumns
    "

    <resource: #tableColumns>

    ^#(
      #(#DataSetColumnSpec
         #label: #detailsMenuIconR
         #labelIsImage: true
         #labelButtonType: #Group
         #labelActionSelector: #showDetailMenu
         #width: 22
         #minWidth: 22
         #height: 16
         #model: #icon
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Filename'
         #id: #filename
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'baseName'
         #minWidth: 150
         #model: #baseName
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Suffix'
         #id: #suffix
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'suffix'
         #width: 40
         #model: #suffix
         #canSelect: false
         #isResizeable: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Perm'
         #id: #perm
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'permissions'
         #width: 75
         #model: #permissions
         #canSelect: false
         #isResizeable: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Owner'
         #id: #owner
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'owner'
         #usePreferredWidth: true
         #width: 50
         #model: #owner
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Group'
         #id: #group
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'group'
         #usePreferredWidth: true
         #width: 40
         #model: #group
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Size'
         #id: #size
         #translateLabel: true
         #labelAlignment: #right
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'size'
         #columnAlignment: #right
         #usePreferredWidth: true
         #model: #sizeString
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Date & Time'
         #id: #time
         #labelAlignment: #right
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'modificationTime'
         #columnAlignment: #right
         #usePreferredWidth: true
         #width: 140
         #model: #timeAndDate
         #writeSelector: #date:
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'File Info'
         #id: #fileInfo
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortList:
         #labelActionArgument: 'fileType'
         #width: 250
         #model: #fileInfoString
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Pre +/-'
         #id: #preview
         #labelButtonType: #Button
         #labelActionSelector: #doResizeImage
         #labelActionArgument: ''
         #columnAlignment: #center
         #width: 45
         #height: 16
         #model: #preview
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      )
!

windowSpec
    "This resource specification was automatically generated
     by the UIPainter of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the UIPainter may not be able to read the specification."

    "
     UIPainter new openOnClass:DirectoryContentsBrowser andSelector:#windowSpec
     DirectoryContentsBrowser new openInterface:#windowSpec
     DirectoryContentsBrowser open
    "

    <resource: #canvas>

    ^ 
     #(#FullSpec
        #name: #windowSpec
        #window: 
       #(#WindowSpec
          #label: 'Directory ContentsBrowser'
          #name: 'Directory ContentsBrowser'
          #min: #(#Point 10 10)
          #max: #(#Point 1152 900)
          #bounds: #(#Rectangle 16 42 604 520)
          #menu: #menu
        )
        #component: 
       #(#SpecCollection
          #collection: #(
           #(#DataSetSpec
              #name: 'browser'
              #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
              #model: #selectionInFileList
              #menu: #directoryContentsBrowserMenu
              #hasHorizontalScrollBar: true
              #hasVerticalScrollBar: true
              #miniScrollerHorizontal: false
              #miniScrollerVertical: false
              #dataList: #descriptions
              #has3Dsepartors: false
              #doubleClickSelector: #doubleClickedAt:
              #columnHolder: #tableColumns
              #multipleSelectOk: true
              #verticalSpacing: 0
              #columns: 
             #(#OrderedCollection
                
               #(#DataSetColumnSpec
                  #labelButtonType: #Group
                  #width: 22
                  #minWidth: 22
                  #height: 16
                  #model: #icon
                  #canSelect: false
                  #showRowSeparator: false
                  #showColSeparator: false
                ) 
               #(#DataSetColumnSpec
                  #label: 'file name'
                  #labelAlignment: #left
                  #labelButtonType: #Button
                  #model: #baseName
                  #canSelect: false
                  #showRowSeparator: false
                  #showColSeparator: false
                )
                
               #(#DataSetColumnSpec
                  #label: '+/-'
                  #labelButtonType: #Button
                  #labelActionSelector: #doResizeImage
                  #labelActionArgument: ''
                  #columnAlignment: #center
                  #width: 30
                  #height: 16
                  #model: #preview
                  #canSelect: false
                  #showRowSeparator: false
                  #showSelectionHighLighted: false
                  #showColSeparator: false
                )
              )
              #viewClassName: 'DirectoryContentsBrowser::Widget'
              #postBuildCallback: #postBuildBrowser:
              #properties: 
             #(#PropertyListDictionary
                #enterSelector: #dropEnter:
                #dragArgument: #contentsbrowser
                #startDragSelector: #doStartDrag:in:
                #displayObjectSelector: #getDisplayObjects:
                #dropObjectSelector: #getDropObjects:
                #dropArgument: #browser
                #canDropSelector: #canDrop:
                #leaveSelector: #dropLeave:
                #overSelector: #dropOver:
                #dropSelector: #doDrop:
              )
            )
           )
         
        )
      )
! !

!DirectoryContentsBrowser class methodsFor:'menu specs'!

directoryContentsBrowserMenu
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:DirectoryContentsBrowser andSelector:#directoryContentsBrowserMenu
     (Menu new fromLiteralArrayEncoding:(DirectoryContentsBrowser directoryContentsBrowserMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'Copy'
            #translateLabel: true
            #value: #doCopy
            #shortcutKeyCharacter: #Copy
          )
         #(#MenuItem
            #label: 'Cut'
            #translateLabel: true
            #value: #doCut
            #shortcutKeyCharacter: #Cut
          )
         #(#MenuItem
            #label: 'Paste'
            #translateLabel: true
            #value: #pasteFiles
            #enabled: #canPaste
            #shortcutKeyCharacter: #Paste
          )
         #(#MenuItem
            #label: 'Delete'
            #translateLabel: true
            #nameKey: #delete
            #value: #doDelete
            #shortcutKeyCharacter: #Delete
          )
         #(#MenuItem
            #label: 'Rename...'
            #translateLabel: true
            #value: #renameSelection
            #enabled: #hasSelection
            #shortcutKeyCharacter: #Replace
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'View'
            #translateLabel: true
            #nameKey: #View
            #submenuChannel: #viewBrowserMenu
          )
         #(#MenuItem
            #label: 'Sort'
            #translateLabel: true
            #nameKey: #Sort
            #submenuChannel: #sortMenu
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'New'
            #translateLabel: true
            #submenuChannel: #newMenu
          )
         #(#MenuItem
            #label: 'Properties...'
            #translateLabel: true
            #value: #doShowProperties
            #enabled: #hasSelection
          )
         #(#MenuItem
            #label: 'More'
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #label: 'File In'
                  #translateLabel: true
                  #value: #fileFileIn
                  #enabled: #hasSelection
                  #shortcutKeyCharacter: #Accept
                )
               #(#MenuItem
                  #label: 'File in to NameSpace...'
                  #translateLabel: true
                  #value: #fileFileInToNameSpace
                  #enabled: #hasSelection
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #label: 'Open in TextEditor'
                  #translateLabel: true
                  #value: #doShowFileContents
                  #enabled: #hasSelection
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #label: 'Select All'
                  #translateLabel: true
                  #value: #doSelectAll
                )
               #(#MenuItem
                  #label: 'Copy File List'
                  #translateLabel: true
                  #value: #copyFileList
                  #enabled: #fileListIsNotEmpty
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Update'
            #translateLabel: true
            #value: #doUpdate
          )
         )
        nil
        nil
      )
!

viewBrowserMenu
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:DirectoryContentsBrowser andSelector:#viewBrowserMenu
     (Menu new fromLiteralArrayEncoding:(DirectoryContentsBrowser viewBrowserMenu)) startUp
    "

    <resource: #menu>

    ^ Menu new fromLiteralArrayEncoding:self viewBrowserMenuSpec
!

viewBrowserMenuSpec
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:DirectoryContentsBrowser andSelector:#viewBrowserMenu
     (Menu new fromLiteralArrayEncoding:(DirectoryContentsBrowser viewBrowserMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'View Details'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewDetails
          )
         #(#MenuItem
            #label: 'View Suffix'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewType
          )
         #(#MenuItem
            #label: 'View Permissions'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewPermissions
          )
         #(#MenuItem
            #label: 'View Owner'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewOwner
          )
         #(#MenuItem
            #label: 'View Group'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewGroup
          )
         #(#MenuItem
            #label: 'View Size'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewSize
          )
         #(#MenuItem
            #label: 'View Date && Time'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewTime
          )
         #(#MenuItem
            #label: 'View File Info'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewDescription
          )
         #(#MenuItem
            #label: 'View Preview'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewPreview
          )
         #(#MenuItem
            #label: '-'
            #isVisible: false
          )
         #(#MenuItem
            #label: 'Show Hidden Files'
            #translateLabel: true
            #indication: #showHiddenFiles
            #isVisible: false
          )
         #(#MenuItem
            #label: 'Update'
            #translateLabel: true
            #value: #doUpdate
            #isVisible: false
          )
         )
        nil
        nil
      )
!

viewDetailsMenuSpec
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:DirectoryContentsBrowser andSelector:#viewBrowserMenu
     (Menu new fromLiteralArrayEncoding:(DirectoryContentsBrowser viewBrowserMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'Suffix'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewType
          )
         #(#MenuItem
            #label: 'Permissions'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewPermissions
          )
         #(#MenuItem
            #label: 'Owner'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewOwner
          )
         #(#MenuItem
            #label: 'Group'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewGroup
          )
         #(#MenuItem
            #label: 'Size'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewSize
          )
         #(#MenuItem
            #label: 'Date && Time'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewTime
          )
         #(#MenuItem
            #label: 'File Info'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewDescription
          )
         #(#MenuItem
            #label: 'Preview'
            #translateLabel: true
            #hideMenuOnActivated: false
            #enabled: #viewDetails
            #indication: #viewPreview
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Hidden Files'
            #translateLabel: true
            #indication: #showHiddenFiles
          )
         )
        nil
        nil
      )
! !

!DirectoryContentsBrowser methodsFor:'accessing'!

columnDescriptors
    "get the column description
    "
    ^ columnDescriptors
!

columnDescriptors:aListOfColumns
    "set the column description
    "
    columnDescriptors = aListOfColumns ifTrue:[
        ^ self
    ].

    columnDescriptors    := OrderedCollection new.
    previewIndex        := 0.
    iconIndex            := 0.
    fileDescriptionIndex := 0.
    iconExtent           := nil.

    aListOfColumns keysAndValuesDo:[:anIndex :aDesc| |col|
        col := aDesc isSequenceable ifTrue:[DataSetColumnSpec new fromLiteralArrayEncoding:aDesc]
                                   ifFalse:[aDesc].
        columnDescriptors add:col.

        col readSelector == #preview ifTrue:[
            previewIndex := anIndex.
            iconExtent := Point x:(col width) y:(col height).
        ] ifFalse:[
            col readSelector == #fileInfoString ifTrue:[
                fileDescriptionIndex := anIndex.
            ] ifFalse:[
                col readSelector == #icon ifTrue:[
                    iconIndex  := anIndex.
                ]
            ]
        ]
    ].

!

directory
    "returns the current directory or nil
    "
    ^ directory

!

directory:aDirectory
    "change the current directory and read the items
    "
    directory := aDirectory.
    self readDirectory.
!

filterBlock
    "return the value of the instance variable 'filterBlock' (automatically generated)"

    ^ filterBlock
!

filterBlock:something
    "set the value of the instance variable 'filterBlock' (automatically generated)"

    filterBlock := something.
!

sortBlock


    sortBlock isNil ifTrue:[
        self sortBlock:[ : a : b |
                            self sortCaseless value ifTrue:[
                                a asString asLowercase < b asString asLowercase.
                            ] ifFalse:[
                                a asString < b asString.
                            ]
                        ].
    ].
    ^ sortBlock
!

sortBlock:aSortBlock

" sort directories before files anyway "
    sortBlock := [:a :b|
        | res |
        (self viewDirectoriesInDirectoryContentsBrowser
        and:[(a isDirectory == b isDirectory) not]) ifTrue:[
            res := a isDirectory 
        ] ifFalse:[
            res := aSortBlock value:a value:b.
        ].
        res
    ].
    ^ sortBlock
!

updateColumnsTask
    "return the value of the instance variable 'updateColumnsTask' (automatically generated)"

    ^ updateColumnsTask
!

updateColumnsTask:something
    "set the value of the instance variable 'updateColumnsTask' (automatically generated)"

    updateColumnsTask := something.
!

updateDirectoryContentsTask
    "return the value of the instance variable 'updateDirectoryContentsTask' (automatically generated)"

    ^ updateDirectoryContentsTask
!

updateDirectoryContentsTask:something
    "set the value of the instance variable 'updateDirectoryContentsTask' (automatically generated)"

    updateDirectoryContentsTask := something.
!

updateToExternFileHolderLock
    "return the value of the instance variable 'updateToExternFileHolder' (automatically generated)"

    updateToExternFileHolderLock isNil ifTrue:[
        updateToExternFileHolderLock := self class newLock.
    ].
    ^ updateToExternFileHolderLock
! !

!DirectoryContentsBrowser methodsFor:'actions'!

doResizeImage
    |column w h|

    previewIndex == 0 ifTrue:[^ self].


    column := columnDescriptors at:previewIndex.
    w := iconExtent x.
    h := iconExtent y.

    w == column width ifTrue:[
        w := w * 2.
        h := h * 2.
    ].
    column width:w.
    column height:h.

    self updateColumnsTaskStop.
    self descriptions do:[:el| el resetImageFile ].
    browser columnDescriptors:columnDescriptors.
    self updateColumnsTaskStart.
!

doUpdate

    modificationTime := nil.
    self updateDirectoryContentsTaskRestart.
!

doubleClickedAt:anIndex
    |item|

    item := self descriptions at:anIndex ifAbsent:nil.
    self withWaitCursorDo:[
        self enterActionFor:item.
    ]
!

enterActionFor:anItem
    | filename|

    filename := anItem fileName.

    filename = '..' asFilename ifTrue:[
        self currentFileNameHolder value:(OrderedCollection with:(self directory directory)).
        ^ self.
    ].
    (anItem isRemoteDirectory 
    or:[filename isDirectory]) ifTrue:[
        self currentFileNameHolder value:(OrderedCollection with:filename).
        ^ self.
    ].
    self openApplByFileItem:anItem.
!

selectFileName:aColOfFilenames
    | indexInDescriptions curSel descr index|

    descr := self descriptions.
    indexInDescriptions := OrderedCollection new.
    aColOfFilenames do:[ : file |
        index := descr findFirst:[: item |
            item fileName = file.
        ].
        index ~= 0 ifTrue:[
            indexInDescriptions add:index.
        ]
    ].
    curSel := self selectionInFileList value.
    curSel = indexInDescriptions ifTrue:[
        ^ self.
    ].
    self selectionInFileList value:indexInDescriptions withoutNotifying:self.
! !

!DirectoryContentsBrowser methodsFor:'aspects'!

descriptions
    ^ self browserFileList
!

descriptions:aCol

    | showDir directoryUpItem|

    self updateColumnsTaskStop.  "/ dont access rows while rows changed
    self updateToExternFileHolderLock doLocked:[
        showDir := self viewDirectoriesInDirectoryContentsBrowser value.
        (showDir and:[self directory notNil and:[self directory isRootDirectory not]]) ifTrue:[
            directoryUpItem := DirectoryContentsItem with:('..' asFilename).
            directoryUpItem icon:(FileBrowser iconForKeyMatching:#directory).
            aCol addFirst:directoryUpItem.
        ].
        self descriptions contents:aCol.
    ].
    self updateColumnsTaskStart.
    wantSelectFiles notNil ifTrue:[
        self selectFileName:wantSelectFiles.
    ].
!

tableColumns
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept it in the browser."
    "*** (and replace this comment by something more useful ;-)"

    tableColumns isNil ifTrue:[
        tableColumns := self class tableColumns asValue.
"/ if your app needs to be notified of changes, uncomment one of the lines below:
"/       tableColumns addDependent:self.
"/       tableColumns onChangeSend:#tableColumnsChanged to:self.
    ].
    ^ tableColumns.
! !

!DirectoryContentsBrowser methodsFor:'change & update'!

fileNamesChanged:aColOfFiles
    "filename changed
    "
    | newDir currentDir colDirectories|


    wantSelectFiles := aColOfFiles. "/ set the file to the current filename to change selection if update process have added the list
    aColOfFiles isEmpty ifTrue:[
        self directory:nil.
        ^ self
    ].
    colDirectories := self getDirectoriesForFileList:aColOfFiles.
    colDirectories asSet size > 1 ifTrue:[
        self directory:nil.
        ^ self
    ].
    newDir := colDirectories first.
    currentDir := self directory.
    newDir = currentDir ifTrue:[
        self selectFileName:aColOfFiles.
        ^ self.
    ] ifFalse:[
        "/ change of the directory filename 
        self directory:newDir.
    ].
!

filterChanged

    self filterBlock: [: el | 
                            (self showHiddenFiles value 
                                ifFalse:[ 
                                    (el asString startsWith:'.') not
                                ]
                                ifTrue:[
                                    true
                                ]
                            )
                            and:[self filterModel value match:el]].

    self evaluateFilter.
!

update:something with:aParameter from:aModel
    "one of my models changed
    "
    | selection |

    aModel == self filterModel ifTrue:[
        self filterChanged.
        ^ self.
    ].             
    aModel == self sortBlockHolder ifTrue:[
        self sortList:(self sortBlockHolder value) withReverse:false.
        ^ self.
    ].             
    aModel == self currentFileNameHolder ifTrue:[
"/        | window sensor |
"/        window := self window.
"/        window ifNotNil:[
"/            sensor := window sensor.
"/            sensor ifNotNil:[
"/                sensor flushUserEventsFor:self withType:#updateFromModel.
"/                sensor pushUserEvent:#updateFromModel for:self.
"/                ^ self
"/            ].
"/        ].
        self fileNamesChanged:aModel value.
        " do not return here because the AbstractFileBrowser have to do something
          for every application "  
"/        ^ self.
    ].             
    aModel == self sortCaseless ifTrue:[
        ^ self sortList:#baseName withReverse:false.
    ].             
    aModel == self selectionInFileList ifTrue:[
        self updateToExternFileHolderLock doIfUnLocked:[
            self directory isNil ifTrue:[^ self].   
            selection := self getSelectedFiles.
            selection isEmpty ifTrue:[
                self currentFileNameHolder value:(OrderedCollection with:self directory) withoutNotifying:self.
            ] ifFalse:[
                wantSelectFiles := selection.
                self currentFileNameHolder value:selection withoutNotifying:self.
            ].
        ].
        ^ self.
    ].
    aModel == self viewDirectoriesInDirectoryContentsBrowser ifTrue:[
        self evaluateFilter.
        ^ self
    ].

    super update:something with:aParameter from:aModel
!

updateFromModel

    updateTaskSema isNil ifTrue:[
        [
            updateTaskSema isNil ifTrue:[
                updateTaskSema := Semaphore forMutualExclusion.
            ].
        ] valueUninterruptably
    ].
    updateTaskSema critical:[
        self updateFromSensorTaskStop.
        updateFromSensorTask := Process for:[ |window sensor|
            [
                window := self window.
                window notNil ifTrue:[
                    sensor := window sensor.
                    sensor ifNotNil:[
                        sensor flushUserEventsFor:self withType:#updateFromModel.
                    ].
                ].
                self fileNamesChanged:(self currentFileNameHolder value).
            ] valueNowOrOnUnwindDo:[
                updateFromSensorTask := nil
            ].
        ] priority:8.
        updateFromSensorTask resume.
    ].
!

updateFromSensorTaskStop
    |task|

    (task := updateFromSensorTask) notNil ifTrue:[
        updateFromSensorTask := nil.

        Object errorSignal handle:[:ex|
            Dialog warn:ex description.
        ]do:[
            task isDead ifFalse:[
                self updateColumnsTaskStop.
                self updateDirectoryContentsTaskStop.
                task terminate.
                task waitUntilTerminated.
            ]
        ]
    ].
!

viewedColumnsChanged

    | columns buffer |

    columns := OrderedCollection new.
    self class tableColumns do:[:el|
        columns add:(DataSetColumnSpec new fromLiteralArrayEncoding:el).
    ].
    buffer := columns copy.
    self viewDetails value = false ifTrue:[
        browser showLabels:false.
        buffer do:[:col |
            (col readSelector = #baseName or:[col readSelector = #icon]) ifFalse:[
                columns remove:col.                
            ]
        ]
    ] ifFalse:[
        browser showLabels:true.
        buffer do:[:col |
            | id |
            id := col id.
            id notNil ifTrue:[

                (id == #filename) ifTrue:[
                        self viewType value ifTrue:[
                            col readSelector:#baseNameWithOutSuffix
                        ] ifFalse:[
                            col readSelector:#baseName
                        ].
                    ] ifFalse:[
                    (id == #suffix and:[self viewType value not]) ifTrue:[
                        columns remove:col. 
                        ] ifFalse:[
                            (id == #group and:[self viewGroup value not]) ifTrue:[
                                columns remove:col. 
                            ] ifFalse:[
                                (id == #owner and:[self viewOwner value not]) ifTrue:[
                                    columns remove:col.
                                ] ifFalse:[
                                    (id == #perm and:[self viewPermissions value not]) ifTrue:[
                                        columns remove:col.
                                    ] ifFalse:[
                                        (id == #preview and:[self viewPreview value not]) ifTrue:[
                                            columns remove:col.
                                        ] ifFalse:[
                                            (id == #size and:[self viewSize value not]) ifTrue:[
                                                columns remove:col.
                                            ] ifFalse:[
                                                (id == #time and:[self viewTime value not]) ifTrue:[
                                                    columns remove:col.
                                                ] ifFalse:[
                                                    (id == #fileInfo and:[self viewDescription value not]) ifTrue:[
                                                        columns remove:col.
                                                ] 
                                            ]
                                        ]
                                    ]
                                ]
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ].
    self tableColumns value:columns.
    self columnDescriptors:(self tableColumns value).
! !

!DirectoryContentsBrowser methodsFor:'directory contents'!

evaluateFilter
    |filterBlock showDir|

    showDir := self viewDirectoriesInDirectoryContentsBrowser value.

    currentItemList := OrderedCollection new.
    allItemsList size ~~ 0 ifTrue:[ 
        filterBlock := self filterBlock.
        allItemsList do:[:aItem | 
            (showDir or:[aItem isDirectory not]) ifTrue:[
                (filterBlock isNil 
                or:[filterBlock value:aItem baseName]) ifTrue:[
                    currentItemList add:aItem 
                ]
            ]
        ].
    ].
    self evaluateSortBlock.
!

evaluateSortBlock

    | newList|

    newList := SortedCollection sortBlock:(self sortBlock).
    newList addAll:currentItemList.
    self descriptions:(newList asList).
!

readDirectory

    self updateDirectoryContentsTaskStop.
    allItemsList := self readDirectoryItems.
    self evaluateFilter.  "/ filter makes a sort anyway
    self updateDirectoryContentsTaskStart.
!

readDirectoryItems
    |dir list mountPoints mountInfo|

    mountPoints := OperatingSystem mountPoints.

    dir := self directory.
    list := OrderedCollection new.
    dir notNil ifTrue:[
        modificationTime := dir modificationTime.
        dir directoryContentsAsFilenamesDo:[:eachFileOrDirectory |
            mountInfo := mountPoints detect:[:mInfo | mInfo mountPointPath = eachFileOrDirectory name] ifNone:nil.
            (mountInfo notNil and:[mountInfo isRemote]) ifTrue:[
                list add:(DirectoryContentsItem withRemoteDirectory:eachFileOrDirectory).
            ] ifFalse:[
                list add:(DirectoryContentsItem with:eachFileOrDirectory).
            ].
        ].
    ].
    ^ list
! !

!DirectoryContentsBrowser methodsFor:'drag & drop'!

canDrop:aContext

    ^ canDropItem
!

doDrop:aContext 

    |col destinationPath receiver|

    draggedItem isNil ifTrue:[
        destinationPath := self directory.
    ] ifFalse:[
        destinationPath := self getDirWithoutFileName:(draggedItem fileName).
        destinationPath = '..' asFilename ifTrue:[
            destinationPath := self directory directory.
        ]
    ].
    self dropLeave:aContext.
    aContext dropSource argument == #archivApplication ifTrue:[
        receiver := aContext dropSource receiver.
        receiver extractSelectedFilesTo:destinationPath askForExtractOptions:true.
        ^ true.
    ].
    col := OrderedCollection new.
    aContext dropObjects do:[:obj|
        col add:(obj theObject).
    ].
    self copyOrMoveFiles:col to:destinationPath.
    ^ true.
!

doStartDrag:aDropSource in:aView
    "set the cursors before starting the drag & drop operation
    "
    |sel|


    sel := self selectionInFileList value.
    ((((self descriptions at:(sel first)) fileName) = ('..' asFilename)) and:[ sel size == 1 ]) ifTrue:[
        ^ self.
    ].
    ^ super doStartDrag:aDropSource in:aView
!

dropEnter:aContext

    |dropedObjects|

    self dropTargetItemChangedTo:nil in:aContext.
    inDropMode := false.

    self directory isNil ifTrue:[^ self].
    dropedObjects := aContext dropObjects.

    dropedObjects do:[:aObject| 
        |checkObject checkObjectString|
        aObject isFileObject ifFalse:[^ self].
        checkObject := aObject theObject.
        checkObject isFilename not ifTrue:[^ self].
        ((aContext dropSource argument == #archivApplication) not) ifTrue:[
            checkObject isSpecialFile ifTrue:[^ self].
            checkObject isReadable ifFalse:[^ self].
        ].
    ].
    inDropMode := true.
!

dropLeave:aDropContext

    inDropMode ifTrue:[
        self dropTargetItemChangedTo:nil in:aDropContext.
        inDropMode := false.
    ].
!

dropOver:aContext 
    "send the last time, when leaving the widget
    "
    |lineNr newDest destinationFile|

    inDropMode ifFalse:[^ self].

    lineNr  := self getLineNumberFor:aContext.

    lineNr notNil ifTrue:[
        newDest := self descriptions at:lineNr.
        (newDest notNil and:[newDest isDirectory]) ifTrue:[
            destinationFile := newDest fileName.
            destinationFile = '..' asFilename ifTrue:[
                destinationFile := self directory directory.
            ]
        ].
    ].
    newDest == draggedItem ifTrue:[^ self].

    destinationFile isNil ifTrue:[
        destinationFile := self directory.
    ].

    newDest ~~ draggedItem ifTrue:[
        aContext dropSource argument == #archivApplication ifTrue:[
            canDropItem := true.
        ] ifFalse:[
            canDropItem := self canDropFiles:(aContext dropObjects) for:destinationFile.
        ].
    ].
    self dropTargetItemChangedTo:newDest in:aContext.
!

dropTargetItemChangedTo:anItem in:aDropContext

    | current |

    
"/    (anItem notNil and:[anItem isDirectory]) ifTrue:[ self halt].
    current := draggedItem.
"/    Transcript showCR:'current:', (current isNil ifTrue:['nil'] ifFalse:[current fileName baseName]).
"/    Transcript showCR:'anItem:', (anItem isNil ifTrue:['nil'] ifFalse:[anItem fileName baseName]).
    current == anItem ifTrue:[^ self].
    draggedItem := anItem.

    aDropContext contentsWillChange.

    current ifNotNil:[
        current isDirectory ifTrue:[
            current icon:(self getIconFor:current).
        ].
    ].

    anItem ifNotNil:[
        anItem isDirectory ifTrue:[
            anItem icon:(FileBrowser iconForKeyMatching:#directoryOpenGray).
        ].
    ].
    browser shown ifTrue:[
        browser invalidateVisibleRow:current colAt:iconIndex.
        browser invalidateVisibleRow:anItem colAt:iconIndex.
        browser repairDamage.
    ].
!

getDisplayObjects:anArgument

    | sel string size fnName stream|
    sel := self selectionInFileList value.
    (((self descriptions at:(sel first)) fileName) = ('..' asFilename)) ifTrue:[
        sel removeFirst.
    ].
    size := sel size.
    size == 0  ifTrue:[^ ''].
    stream := WriteStream on:''.
    stream nextPutAll:(self descriptions at:sel first) fileName baseName asString.
    size == 1 ifTrue:[
        fnName := 'ui_menuitem.xpm'.
    ] ifFalse:[
        fnName := 'ui_submenu_open.xpm'.
        stream nextPutAll:' ... '.
        stream nextPutAll:(self descriptions at:sel last) fileName baseName asString.
    ].
    string := stream contents.
    stream close.
    ^ Array with:(LabelAndIcon icon:(Image fromFile:fnName)
                             string:(Text string:string emphasis:#bold)
                 )
!

getDropObjects:anArgument

    | sel ret file|
    sel := self selectionInFileList value.
    ret := sel collect:[:el| 
        file := (self descriptions at:el) fileName.
        file ~= '..' asFilename ifTrue:[
            DropObject newFile:((self descriptions at:el) fileName)
        ]
    ].
    ^ ret
!

getLineNumberFor:aDropContext
    | yVisible|

    yVisible := (aDropContext targetPoint y).
    ^ browser yVisibleToRowNr:yVisible.
! !

!DirectoryContentsBrowser methodsFor:'event handling'!

processEvent:anEvent
    "filter keyboard events.
     Return true, if I have eaten the event"

    |focusView key rawKey|

    anEvent isKeyPressEvent ifTrue:[
        focusView := anEvent targetView.
        key := anEvent key.
        rawKey := anEvent rawKey.

        (focusView == browser) ifTrue:[
"/            (key ~= #'Alt_L') ifTrue:[self halt.].
            key == #Paste ifTrue:[
                self pasteFiles.
                ^ true.
            ].
"/            (key == #CursorLeft) ifTrue:[
"/                self doGoDirectoryUp.
"/                ^ true
"/            ].
"/            (key == #CursorRight) ifTrue:[
"/                self doBack.
"/                ^ true
"/            ].
            self descriptions size == 0 ifTrue:[
                ^ false
            ].
            key == #Copy ifTrue:[
                self doCopy.
                ^ true.
            ].
            (key == #Return) ifTrue:[
                self doOpenSelectedFile.
                ^ true.
            ].
            key == #Delete ifTrue:[
                self doDelete.
                ^ true.
            ].
            key == #Cut ifTrue:[
                self doCut.
                ^ true.
            ].
        ]
    ].
    ^ false
! !

!DirectoryContentsBrowser methodsFor:'file actions'!

doCopy
    "copy current selected files/directories
    "

    self copyFilesToClipBoard:(self getFilesFromSelection).
!

doCut
    "cut current selected files/directories
    "

    self cutFilesToClipBoard:(self getFilesFromSelection).
!

doDelete
    "delete current selected files/directories
    "
    self deleteFiles:(self getFilesFromSelection).
! !

!DirectoryContentsBrowser methodsFor:'menu accessing'!

viewBrowserMenu

    <resource: #programMenu >

    |menu|

    viewBrowserMenu isNil ifTrue:[
        viewBrowserMenu :=  self class viewBrowserMenu.
    ].
    viewBrowserMenu ifNil:[ ^ nil ].
    menu := viewBrowserMenu.
    menu receiver:self.
    ^ menu
!

viewDetailsMenuSpec
    ^self class viewDetailsMenuSpec
! !

!DirectoryContentsBrowser methodsFor:'menu actions'!

doOpenSelectedFile

    | sel index item|

    sel := self selectionInFileList value.
    (sel notNil and:[sel notEmpty]) ifTrue:[
        index := sel first.
        item := self descriptions at:index ifAbsent:nil.
        self enterActionFor:item.
    ].
!

doSelectAll

    | sel |

    sel := OrderedCollection new.
    self descriptions keysAndValuesDo:[:index :value |
        (index == 1 and:[(value fileName asString = '..')]) ifFalse:[
            sel add:index.
        ]
    ].
    self selectionInFileList value:sel.
!

doShowFileContents

    | sel index item|

    sel := self selectionInFileList value.
    (sel notNil and:[sel notEmpty]) ifTrue:[
        index := sel first.
        item := self descriptions at:index ifAbsent:nil.
        self openTextEditorOn:item.
    ].
! !

!DirectoryContentsBrowser methodsFor:'queries'!

allItemsOfCurrentDirectory

    ^ self descriptions.
!

fileListIsEmpty

    ^ self descriptions isEmpty
!

getAllFilesAsStringCollection
    |files items|

    items := self descriptions.
    files := OrderedCollection new.
    items isEmpty ifTrue:[
        ^ files
    ].
    items do:[:aItem | 
        aItem isDirectory not ifTrue:[
            files add:aItem fileName asString
        ].
    ].
    ^ files.
!

getFileItemsFromSelection
    |indices files|

    indices := self selectionInFileList value.
    files := OrderedCollection new.
    (indices isNil or:[indices isEmpty]) ifTrue:[
        ^ files
    ].
    indices do:[:i | 
        |item|

        item := self descriptions at:i.
        item isDirectory not ifTrue:[
            files add:item
        ]
    ].
    ^ files.
!

getFirstItemFromSelection
    |indices|

    indices := self selectionInFileList value.
    (indices isNil or:[indices isEmpty]) ifTrue:[
        ^ nil
    ].
    ^ self descriptions at:(indices first).
!

getItemsFromSelection
    |indices items|

    indices := self selectionInFileList value.
    items := OrderedCollection new.
    (indices isNil or:[indices isEmpty]) ifTrue:[
        ^ items
    ].
    indices do:[:i | 
        |filename|

        items add:(self descriptions at:i)
    ].
    ^ items.
!

getSelectedFiles

    |indices files|

    indices := self selectionInFileList value.
    files := OrderedCollection new.
    (indices isNil or:[indices isEmpty]) ifTrue:[
        ^ files
    ].
    indices do:[:i | 
        |filename|
        filename := (self descriptions at:i) fileName.
        filename asString ~= '..' ifTrue:[
            files add:(self descriptions at:i) fileName
        ]
    ].
    ^ files.
! !

!DirectoryContentsBrowser methodsFor:'sorting'!

sortListChangedWith:currentSortOrder and:instanceName

    | aSymbol cmpOp sortCaselessLocal|

    aSymbol := instanceName asSymbol.
    sortCaselessLocal := self sortCaseless value.
    (currentSortOrder at:#reverse) ifTrue:[
        cmpOp := #'>'
    ] ifFalse:[
        cmpOp := #'<'
    ].
    self sortBlock: [:a :b | 
            |entry1 entry2|

            entry1 := (a perform:aSymbol).
            entry2 := (b perform:aSymbol).
            ((entry1 isNil) or:[entry2 isNil]) ifTrue:[
                ((entry1 isNil) and:[entry2 isNil]) ifTrue:[true] ifFalse:[
                    ((entry1 notNil) and:[entry2 isNil]) ifTrue:[
                       (currentSortOrder at:#reverse)
                    ].
                    (currentSortOrder at:#reverse) not
                ]
            ] ifFalse:[
                (aSymbol = #baseName) ifTrue:[
                    sortCaselessLocal ifTrue:[
                        entry1 := entry1 asString asLowercase.
                        entry2 := entry2 asString asLowercase.
                    ] ifFalse:[
                        entry1 := entry1 asString.
                        entry2 := entry2 asString.
                    ].
                ].
                entry1 perform:cmpOp with:entry2
            ]
    ].
    self sortBlockHolder value:instanceName.
    self evaluateSortBlock.
! !

!DirectoryContentsBrowser methodsFor:'startup / release'!

makeDependent

    self currentFileNameHolder addDependent:self.
    self viewDirectoriesInDirectoryContentsBrowser addDependent:self.
    self filterModel addDependent:self.
    self sortBlockHolder addDependent:self.
    self selectionInFileList addDependent:self.
    self sortCaseless addDependent:self.
    self viewDescription onChangeSend:#viewedColumnsChanged to:self.
    self viewDetails onChangeSend:#viewedColumnsChanged to:self.
    self viewGroup onChangeSend:#viewedColumnsChanged to:self.
    self viewOwner onChangeSend:#viewedColumnsChanged to:self.
    self viewPermissions onChangeSend:#viewedColumnsChanged to:self.
    self viewPreview onChangeSend:#viewedColumnsChanged to:self.
    self viewSize onChangeSend:#viewedColumnsChanged to:self.
    self viewTime onChangeSend:#viewedColumnsChanged to:self.
    self viewType onChangeSend:#viewedColumnsChanged to:self.
    self showHiddenFiles onChangeSend:#filterChanged to:self.
!

postBuildBrowser:aWidget

    browser       := aWidget scrolledView.
    browser wantsFocusWithPointerEnter.
"/ dont register icons anymore
"/    FileBrowser icons keysAndValuesDo:[:aKey :anIcon|
"/        browser registerImage:anIcon key:aKey.
"/    ].
    self columnDescriptors:(self class tableColumns).
!

postBuildWith:aBuilder

    self viewedColumnsChanged.
    self filterChanged.
    ^ super postBuildWith:aBuilder.
!

postOpenAsSubcanvasWith:aBuilder

    self windowGroup addPreEventHook:self.
    ^ super postOpenAsSubcanvasWith:aBuilder.
!

postOpenWith:aBuilder

    "
    only invoked if the application not started from a master
    "
    self currentFileNameHolder changed.
    self windowGroup addPreEventHook:self.
    ^ super postOpenWith:aBuilder.
!

preBuildWith:aBuilder

    self masterApplication isNil ifTrue:[
        self masterApplication:nil.
    ].
    ^ super preBuildWith:aBuilder.
!

release
    "release my resources
    "                    
    self updateColumnsTaskStop.
    self updateDirectoryContentsTaskStop.
    self updateFromSensorTaskStop.
    super release.
!

releaseAsSubCanvas

    self updateColumnsTaskStop.
    self updateDirectoryContentsTaskStop.
    ^ super releaseAsSubCanvas.
! !

!DirectoryContentsBrowser methodsFor:'task update columns'!

findNextBuildInDescFor:aBlock
    |vis idx|

    vis := browser indexOfFirstRowShown.
    vis = 0 ifTrue:[vis := 1].
    idx := self descriptions findFirst:aBlock startingAt:vis.
    (browser isRowVisible:idx) ifFalse:[^ nil].

    idx == 0 ifTrue:[
        (idx := self descriptions findLast:aBlock startingAt:(vis - 1)) == 0 ifTrue:[
            ^ nil
        ]
    ].
    ^ self descriptions at:idx ifAbsent:nil
!

findNextItemFor:aBlock

   ^ allItemsList detect:aBlock ifNone:[nil].
!

getFileInfoFor:aItem

    | type|

    type := aItem fileName fileType.
    aItem fileType:type.
    ^ type
!

getIconFor:aItem

    | key icon|

    key := aItem iconKey.
    icon := FileBrowser iconForKeyMatching:key.
    aItem isRemoteDirectory ifTrue:[
        ^ icon "/ do not query attributes
    ].
    ^ FileBrowser addOnIconsFor:(aItem fileName) to:icon.
!

getIconForMimeTypeByContents:aItem

    | currentIcon newIcon icon|
    newIcon := FileBrowser iconForKeyMatching:(aItem mimeTypeForContents).
    currentIcon := aItem icon.
    (currentIcon class == MultiImage) ifTrue:[
        | images |
        images := currentIcon images.
        images removeFirst.
        images addFirst:newIcon.
        icon := currentIcon.
    ] ifFalse:[
        icon := newIcon.
    ].
    ^ icon
!

getPreviewFor:aItem

    | column icon|
    
    column := browser columnAt:previewIndex.
    column isNil ifTrue:[
        previewIndex := 0.
    ] ifFalse:[
        (aItem hasMimeType and:[aItem mimeType isImage]) ifTrue:[
            Exception handle:[:ex|
                icon := nil.
            ] do:[ |scale extent w h |
                icon := Image fromFile:(aItem fileName).
                icon notNil ifTrue:[
                    extent := icon extent.
                    w := column width - 4.
                    h := column height.

                    (extent x > w or:[extent y > h]) ifTrue:[
                        scale := extent / (w@h).
                        scale := scale x max:(scale y).
                        icon  := icon magnifiedTo:((extent / scale max:8.0) asInteger).
                    ].
                    icon := icon onDevice:(browser device).
                    icon clearMaskedPixels.
                ]
            ]
        ] ifFalse:[ icon := nil].
        aItem preview:(icon ? '').
    ].
    ^ icon
!

updateChangesFor:aItem

    aItem modifiedByMe ifTrue:[
        aItem modifiedByMe:false.
    ] ifFalse:[
        self notifyChannel value:aItem fileName asString, ' was changed by someone else'.
        aItem resetItemForChangesFromSomeOneElse.
    ].
!

updateColumnsTaskCycle

" update items in the foolowing order
    - update item if item properties are changed
    - update icon for visible items by suffix
    - update icon for visible items by contents
    - update file info for visible items if file info column is shown 
    - update preview for visible items if preview column is shown 

    - update icon for invisible items by suffix
    - update icon for invisible items by contents
    - update file info for invisible items if file info column is shown 
    - update preview for invisible items if preview column is shown 
    - update file info for invisible items if file info column is not shown 
"

    |desc icon type preview|

    currentItemList notEmpty ifTrue:[
        "/ update item if item properties are changed
        desc := self findNextBuildInDescFor:[:n| n hasBeenModified ].
        
        desc notNil ifTrue:[
            browser invalidateVisibleRow:desc.
            self updateChangesFor:desc.
            ^ true.
        ].
        "/ update icon for visible items by suffix
        desc := self findNextBuildInDescFor:[:n| n icon isNil ].
        desc notNil ifTrue:[  
            icon := self getIconFor:desc.
            desc icon:icon.
            (icon notNil and:[iconIndex ~~ 0]) ifTrue:[
                browser invalidateVisibleRow:desc colAt:iconIndex
            ].
            ^ true
        ].
        "/ update icon for visible items by contents
        desc := self findNextBuildInDescFor:[:n| 
            (n iconKey == #file 
            and:[n mimeTypeForContents asSymbol ~= #unknown])
        ].
        desc notNil ifTrue:[
            icon := self getIconForMimeTypeByContents:desc.
            desc icon:icon.
            (icon notNil and:[iconIndex ~~ 0]) ifTrue:[
                browser invalidateVisibleRow:desc colAt:iconIndex
            ].
            ^ true
        ].
        "/ update file info for visible items if file info column is shown
        fileDescriptionIndex ~~ 0 ifTrue:[
            desc := self findNextBuildInDescFor:[:n| n fileType isNil ].
            desc notNil ifTrue:[
                type := self getFileInfoFor:desc.
                type notNil ifTrue:[browser invalidateVisibleRow:desc colAt:fileDescriptionIndex].
                ^ true
            ]
        ].
        "/ update preview for visible items if preview column is shown
        previewIndex ~~ 0 ifTrue:[
            desc := self findNextBuildInDescFor:[:n| n preview isNil ].
            desc notNil ifTrue:[
                preview := self getPreviewFor:desc.
                preview notNil ifTrue:[
                    browser invalidateVisibleRow:desc colAt:previewIndex
                ].
                ^ true
            ]
        ].
    ].

    "/ may we want to see the other items later make this items to
    allItemsList notEmpty ifTrue:[
        "/ update icon for invisible items by suffix
        desc := self findNextItemFor:[:n| n icon isNil ].
        desc notNil ifTrue:[
            icon := self getIconFor:desc.
            desc icon:icon.
            ^ true
        ].
        "/ update icon for invisible items by contents
        desc := self findNextItemFor:[:n| (n iconKey == #file and:[n mimeTypeForContents ~= #unknown])].
        desc notNil ifTrue:[
            icon := self getIconForMimeTypeByContents:desc.
            desc icon:icon.
            ^ true
        ].
        "/ update file info for invisible items if file info column is shown
        fileDescriptionIndex ~~ 0 ifTrue:[
            desc := self findNextItemFor:[:n| n fileType isNil ].
            desc notNil ifTrue:[
                self getFileInfoFor:desc.
                ^ true
            ].
        ].
        "/ update preview for invisible items if preview column is shown 
        previewIndex ~~ 0 ifTrue:[
            desc := self findNextItemFor:[:n| n preview isNil ].
            desc notNil ifTrue:[
                self getPreviewFor:desc.
                ^ true
            ].
        ].
        "/ update file info for invisible items if file info column is not shown
        desc := self findNextItemFor:[:n| n fileType isNil ].
        desc notNil ifTrue:[
            self getFileInfoFor:desc.
            ^ true
        ].
    ].
    ^ false
!

updateColumnsTaskRestart

    self updateColumnsTaskStop.
    self updateColumnsTaskStart.
!

updateColumnsTaskStart

    | somethigToDo p |

    somethigToDo := true.
    self directory notNil ifTrue:[
        p := [ 
                 [   
                     [true] whileTrue:[
                         somethigToDo := false.
                         (browser shown and:[self directory notNil]) ifTrue:[
                             somethigToDo := self updateColumnsTaskCycle.
                             Processor yield.
                         ].
                         somethigToDo ifFalse:[
                             Delay waitForSeconds:1.0
                         ]
                     ]
                 ] valueNowOrOnUnwindDo:[
                     self updateColumnsTask:nil.
                 ]
             ] newProcess.
        p priority:(Processor systemBackgroundPriority).

        self updateColumnsTask: p.   
        self updateColumnsTask name:('DirectoryContentsBrowser update columns[', self directory baseName, ']').
        self updateColumnsTask resume.
    ]
!

updateColumnsTaskStop
    |task|

    (task := self updateColumnsTask) notNil ifTrue:[
        self updateColumnsTask:nil.

        Object errorSignal handle:[:ex|
            Dialog warn:ex description.
        ]do:[
            task isDead ifFalse:[
                task terminateWithAllSubprocessesInGroup.
                task waitUntilTerminated.
            ]
        ]
    ].
! !

!DirectoryContentsBrowser methodsFor:'task update dir contents'!

updateDirectoryContentsTaskCycle

    | contents|

    "/ DIRECTORY CONTNETS HAVE CHANGED
    (modificationTime ~= self directory modificationTime) ifTrue:[
        modificationTime := self directory modificationTime.
        contents := self directory directoryContentsAsFilenames.
        contents size == 0 ifTrue:[ 
            allItemsList removeAll. 
        ] ifFalse:[
            "/ MERGE WITH CURRENT CONTENTS
            allItemsList reverseDo:[:aDesc|
                "/ remove no longer valid files
                (contents detect:[:f| aDesc fileName = f] ifNone:nil) isNil ifTrue:[
                    allItemsList removeIdentical:aDesc
                ]
            ].
            allItemsList size ~~ contents size ifTrue:[
                "/ add new files
                | addedFiles |
                addedFiles := OrderedCollection new.
                contents reverseDo:[:aFile|
                    (allItemsList detect:[:item| item fileName = aFile] ifNone:[nil]) isNil ifTrue:[
                        addedFiles add:(DirectoryContentsItem with:aFile)
                    ]
                ].
                allItemsList addAll:addedFiles.
            ]
        ].
        self evaluateFilter.
    ].
!

updateDirectoryContentsTaskRestart

    self updateDirectoryContentsTaskStop.
    self updateDirectoryContentsTaskStart.
!

updateDirectoryContentsTaskStart
    |p|

    self directory notNil ifTrue:[
        p := [ 
                 [   
                     [true] whileTrue:[
                         (browser shown and:[self directory notNil]) ifTrue:[
                             self updateDirectoryContentsTaskCycle.
                             Processor yield.
                         ].
                         Delay waitForSeconds:1.0
                     ]
                 ] valueNowOrOnUnwindDo:[
                     self updateDirectoryContentsTask:nil.
                     self updateColumnsTaskStop.
                 ]
              ] newProcess.

        p priority:(Processor systemBackgroundPriority).
        self updateDirectoryContentsTask:p.

        p name:('DirectoryContentsBrowser update contents[', self directory baseName, ']').
        p resume.
    ]
!

updateDirectoryContentsTaskStop
    |task|

    (task := self updateDirectoryContentsTask) notNil ifTrue:[
        self updateDirectoryContentsTask:nil.

        Object errorSignal handle:[:ex|
            Dialog warn:ex description.
        ]do:[
            task isDead ifFalse:[
                task terminateWithAllSubprocessesInGroup.
                task waitUntilTerminated.
            ]
        ]
    ].
! !

!DirectoryContentsBrowser::DirectoryContentsItem class methodsFor:'instance creation'!

with:aFilename

    | instance |
    instance := self new.
    instance fileName:aFilename.
    instance initialize.
    ^ instance
!

withRemoteDirectory:aFilename

    | instance |
    instance := self new.
    instance fileName:aFilename.
    instance beRemoteDirectory.
    instance initialize.
    ^ instance
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'accessing'!

fileName
    "returns the fileName
    "
    ^ fileName


!

fileName:aFilename
    "set fileName for instance
    "
    fileName := aFilename.
!

fileType
    "returns the type of the file
    "

    ^ fileType 
!

fileType:aType
    "returns the type of the file
    "

    fileType := aType.
!

icon
    "returns the icon assigned to the file
    "
    ^ icon


!

icon:anIcon

    icon := anIcon.
!

iconKey
    "return the value of the instance variable 'iconKey' (automatically generated)"

    iconKey isNil ifTrue:[
        self isRemoteDirectory ifTrue:[
            iconKey := FileBrowser iconKeyForRemoteDirectory:fileName.
        ] ifFalse:[
            iconKey := FileBrowser iconKeyForFile:fileName.
        ]
    ].
    ^ iconKey
!

isDirectory
    self isRemoteDirectory ifTrue:[^ true].

    self getFileInfo.
    fileInfo isNil ifTrue:[^ false].
    ^ fileInfo isDirectory
!

isSymbolicLink
    self isRemoteDirectory ifTrue:[^ false].

    self getFileInfo.
    fileInfo isNil ifTrue:[^ false].
    ^ fileInfo isSymbolicLink
!

lastButOneSuffix
    "returns the suffix of the file
    "

    ^ self suffixes at:2
!

lastSuffix
    "returns the suffix of the file
    "

    ^ self suffixes at:1
!

link
    "returns the type of the file
    "
    self halt.
    ^ self isSymbolicLink.
!

linkInfo
    "returns the type of the file
    "
self halt.
    ^ self fileName linkInfo.
!

mimeType
    "return the value of the instance variable 'mimeType' (automatically generated)"

    mimeType isNil ifTrue:[
        mimeType := MIMETypes mimeTypeForFilename:(self fileName).
        mimeType isNil ifTrue:[
            mimeType := #unknown
        ]
    ].
    ^ mimeType
!

modifiedByMe
    "return the value of the instance variable 'modifiedByMe' (automatically generated)"

    ^ modifiedByMe
!

modifiedByMe:something
    "set the value of the instance variable 'modifiedByMe' (automatically generated)"

    modifiedByMe := something.
!

preview

    ^ preview
!

preview:aLabel

    preview := aLabel.
!

suffix
    "returns the suffix of the file
    "
    suffix isNil ifTrue:[
        (OperatingSystem isUNIXlike and:[(self baseName at:1) == $.]) ifTrue:[
            suffix := ''.
        ] ifFalse:[
            suffix := fileName suffix.
        ]
    ].
    ^ suffix
!

suffixes

    | lastSuff lastButOneSuff|

    (fileName name occurrencesOf:$.) <= 1 ifTrue:[
        ^ Array with:(self suffix) with:nil.
    ].
    (fileName baseName occurrencesOf:$.) <= 1 ifTrue:[
        ^ Array with:(self suffix) with:nil.
    ].

    ((lastSuff := self suffix) isEmpty) ifTrue:[  
        lastSuff := nil.
    ] ifFalse:[
        lastSuff := lastSuff asLowercase. 
    ].

    ((lastButOneSuff := fileName withoutSuffix suffix) isEmpty) ifTrue:[  
        lastButOneSuff := nil.
    ] ifFalse:[
        lastButOneSuff := lastButOneSuff asLowercase. 
    ].

    (lastSuff notNil and:[lastButOneSuff notNil]) ifTrue:[
        (lastSuff = 'bak' or:[lastSuff = 'sav']) ifTrue:[
            lastSuff := lastButOneSuff.
            lastButOneSuff := nil.
        ]
    ].
    ^ Array with:lastSuff with:lastButOneSuff
!

type
    "returns the type of the file
    "
    ^ self valueAt:#type

! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'actions'!

resetImageFile

    " if the image preview extent is changed the icon will set to nil to force the update task to reload the image "

    (mimeType notNil and:[ mimeType isImage]) ifTrue:[
        preview := nil
    ]
!

resetItemForChangesFromSomeOneElse

    " dont know what someone else is doing with the file - read all item properties new "
    fileInfo := nil.
    icon := nil.
    fileType := nil.
    preview := nil.
    owner := nil.
    timeAndDate := nil.
    group := nil.
!

resetItemForTextEditorChange

    " read size and time item properties new "
    fileInfo := nil.
    timeAndDate := nil.
    modifiedByMe := true.
"/    textEditorChangeModificationTime := nil.
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'change queries'!

hasBeenModified

    "check if item modification time is changed 
    "
    | time info|

    self isRemoteDirectory ifTrue:[^ false].

    info := fileName info.
    info isNil ifTrue:[ ^ false].
    time := info modificationTime.

    contentsBrowserChangeModificationTime isNil ifTrue:[
        contentsBrowserChangeModificationTime := time.
        fileInfo := nil.
        ^ false.
    ].
    ((contentsBrowserChangeModificationTime = time) not) ifTrue:[
        contentsBrowserChangeModificationTime := time.
        fileInfo := nil.
        ^ true
    ].
    ^ false
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'compare'!

= aItem

"/    Transcript showCR:(aItem species == self species).
    aItem species == self species ifFalse:[  ^ false].
    aItem isNil ifTrue:[ ^ false].
    ^ (self fileName = aItem fileName)
!

hash
    ^ fileName hash
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'presentation'!

baseName
    "returns the baseName of the file
    "
    ^ fileName baseName.
!

baseName:aName
    "rename the file
    "
    ^ self
!

baseNameWithOutSuffix
    "returns the baseName of the file
    "
    | baseNameWithOutSuff |

    baseNameWithOutSuff := self baseName asFilename withoutSuffix asString.
    baseNameWithOutSuff isEmpty ifTrue:[
        ^ self baseName
    ].
    ^ baseNameWithOutSuff.
!

date
    "returns the modTimeString
    "
    | modTime |

    modTime := self modificationTime.
    modTime isNil ifTrue:[
        ^ '???'
    ].
    ^ modTime asDate printString.
!

fileInfoString
    "get stat info on selected file - return a string which can be
     shown in a box"

    | afileType |

    fileName isNil ifTrue:[^ nil].
    afileType := self fileType.
    afileType isNil ifTrue:[
        ^ ''.
    ].
    ^ afileType
!

fileSize
    "returns the fileSize
    "
    self getFileInfo.
    fileInfo isNil ifTrue:[^ nil].

    ^ fileInfo fileSize
!

group
    "returns the printable group
    "
    |gid|

    group isNil ifTrue:[
        self getFileInfo.
        fileInfo isNil ifTrue:[^ '???'].

        gid := fileInfo gid.
        (LastGIDToGroupNameMapping notNil and:[gid == LastGIDToGroupNameMapping key]) ifTrue:[
            group := LastGIDToGroupNameMapping value
        ] ifFalse:[
            group := OperatingSystem getGroupNameFromID:gid.
            LastGIDToGroupNameMapping := gid -> group.
        ]
    ].
    ^ group
!

modificationTime
    "returns the absolute time of modification
    "
    self getFileInfo.
    fileInfo isNil ifTrue:[^ nil].

    ^ fileInfo modificationTime
!

owner
    "returns the printable owner
    "
    |uid|

    owner isNil ifTrue:[
        self getFileInfo.
        fileInfo isNil ifTrue:[^ '???'].

        uid := fileInfo uid.
        (LastUIDToUserNameMapping notNil and:[uid == LastUIDToUserNameMapping key]) ifTrue:[
            owner := LastUIDToUserNameMapping value
        ] ifFalse:[
            owner := OperatingSystem getUserNameFromID:uid.
            LastUIDToUserNameMapping := uid -> owner.
        ]
    ].
    ^ owner
!

pathName
    "returns the pathName of the file
    "
self halt.
    ^ fileName pathName.
!

permissions
    "returns the permissions as printable string
    "
    |mode permissionString|

    self getFileInfo.
    fileInfo isNil ifTrue:[^ '???'.].

    mode := fileInfo mode.
    permissionString := String new:9 withAll:$-.

    1 to:9 by:3 do:[:i|
        (mode bitAt:i    ) == 1 ifTrue:[permissionString at:10 - i put:$x].
        (mode bitAt:i + 1) == 1 ifTrue:[permissionString at:9  - i put:$w].
        (mode bitAt:i + 2) == 1 ifTrue:[permissionString at:8  - i put:$r].
    ].
    ^ permissionString
!

printString
    ^ fileName asString
!

size
    "returns the size of the file
    "
self halt.
    ^ self valueAt:#size 
!

sizeString
    "returns the size of the file
    "
    | unitString n locSize|

    unitString := ''.
    locSize := self fileSize.
    locSize isNil ifTrue:[^ '???'].

    locSize < (500 * 1024) ifTrue:[
        locSize < 1024 ifTrue:[
            n := locSize
        ] ifFalse:[
            n := (locSize * 10 // 1024 / 10.0).
            unitString := ' Kb'
        ]
    ] ifFalse:[
        n := (locSize * 10 // 1024 // 1024 / 10.0).
        unitString := ' Mb'
    ].
    ^ (n printStringLeftPaddedTo:5) , unitString.
!

timeAndDate
    "returns the date
    "
    |modTime|

    modTime := self modificationTime.
    modTime isNil ifTrue:[
        ^  '???'
    ].
    Smalltalk languageTerritory == #us ifTrue:[
        ^ modTime printStringFormat:'%(ShortMonthName), %(dayPadded) %(yearOrTime)'.
    ].
    ^ modTime printStringFormat:'%(day)-%(ShortMonthName) %(yearOrTime)'.

"/    ^ modTime printStringFormat:'%(year)-%(mon)-%(day) %h:%m:%s'.
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'private'!

getFileInfo
    fileInfo isNil ifTrue:[
        self isRemoteDirectory ifTrue:[
            "/ do not access, to avoid automount
        ] ifFalse:[
            fileInfo := fileName linkInfo.
            fileInfo isNil ifTrue:[
                fileInfo := fileName info.
            ].
        ].
    ].
    ^ fileInfo
!

valueAt:aKey
    self getFileInfo.
    fileInfo isNil ifTrue:[
        ^ nil.
    ].
self halt.
    aKey == #size ifTrue:[^ fileInfo size].
    aKey == #mode ifTrue:[^ fileInfo mode].
    aKey == #type ifTrue:[^ fileInfo type].
    aKey == #uid ifTrue:[^ fileInfo uid].
    aKey == #gid ifTrue:[^ fileInfo gid].
    aKey == #accessed ifTrue:[^ fileInfo accessTime].
    aKey == #modified ifTrue:[^ fileInfo modificationTime].

    ^ nil
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'queries'!

beRemoteDirectory
    fileType := #remoteDirectory.
!

exists
    "returns the fileName
    "
    ^ fileName exists 
!

hasMimeType

    ^ (self mimeType ~= #unknown)
!

isRemoteDirectory
    ^ fileType == #remoteDirectory
!

mimeTypeForContents

    mimeTypeForContents isNil ifTrue:[
        mimeTypeForContents := MIMETypes mimeTypeOfContents:(self fileName).
        mimeTypeForContents isNil ifTrue:[
            mimeTypeForContents := #unknown
        ] ifFalse:[
            " set mimeType to mimeType of contents if a better mimeType is found "
            mimeType := mimeTypeForContents.
            iconKey := mimeTypeForContents.
        ]
    ].
    ^ mimeTypeForContents
! !

!DirectoryContentsBrowser class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libtool/DirectoryContentsBrowser.st,v 1.15 2002-10-12 00:48:10 cg Exp $'
! !