DirectoryContentsBrowser.st
author Stefan Vogel <sv@exept.de>
Thu, 03 Apr 2003 15:39:26 +0200
changeset 4778 e1c81c22bf36
parent 4768 36eaed9339da
child 4781 08aacafe88ab
permissions -rw-r--r--
Avoid slow grow operations on Array.

"{ Package: 'stx:libtool' }"

AbstractDirectoryBrowser subclass:#DirectoryContentsBrowser
	instanceVariableNames:'modificationTime directory columnDescriptors iconIndex
		fileDescriptionIndex iconExtent tableColumns previewIndex
		draggedItem viewBrowserMenu updateContentsSelection
		selectionInFileList browserItemList matchBlock updateTask
		directoryChangeFlag directoryContentsChangeFlag filterChangeFlag
		sortBlockChangeFlag isBusy changeSema filteredItems allItems
		diskUsageInfo diskUsageUpdateProcess'
	classVariableNames:''
	poolDictionaries:''
	category:'Interface-Tools-File'
!

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

!DirectoryContentsBrowser class methodsFor:'documentation'!

documentation
"
    An application for displaying a directories' contents flat (or as a tree, alternatively).
    To be used as a building block in file browsers and fileSelection dialogs.
    This is used as the top-right component in the new fileBrowser.

    [author:]
        Christian Penk (penk@bierfix)

    [see also:]
        FileBrowserV2
"
!

examples
"
                                                                [exBegin]
    DirectoryContentsBrowser openIn:(Filename currentDirectory pathName)
                                                                [exEnd]
"
! !

!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 viewDirsInContentsBrowser value:withDirectoriesBoolean.
    ^ browser

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

!DirectoryContentsBrowser class methodsFor:'classAccess'!

itemClass

    ^ DirectoryContentsItem


"
self itemClass
"
! !

!DirectoryContentsBrowser class methodsFor:'constant'!

debug

    ^ false
!

updateTaskCyleTime

    " time for search on changed directories or changed columns in msecs "

    ^ 4000
!

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

!DirectoryContentsBrowser class methodsFor:'image specs'!

detailsMenuIconDown
    "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 detailsMenuIconDown inspect
     ImageEditor openOnClass:self andSelector:#detailsMenuIconDown
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'DirectoryContentsBrowser class detailsMenuIconDown'
        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:'@A@(UJ(b') ; 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]
!

detailsMenuIconUp
    "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 detailsMenuIconUp inspect
     ImageEditor openOnClass:self andSelector:#detailsMenuIconUp
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'DirectoryContentsBrowser class detailsMenuIconUp'
        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:'*%P(D@@b') ; yourself); yourself]
! !

!DirectoryContentsBrowser class methodsFor:'interface specs'!

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: ''
         #id: #icon
         #labelButtonType: #Group
         #width: 22
         #minWidth: 22
         #height: 16
         #model: #icon
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Filename'
         #id: #filename
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'baseName'
         #minWidth: 150
         #model: #baseName
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Suffix'
         #id: #suffix
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'suffix'
         #width: 40
         #model: #suffix
         #canSelect: false
         #isResizeable: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Perm'
         #id: #perm
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'permissions'
         #width: 75
         #model: #permissions
         #canSelect: false
         #isResizeable: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Owner'
         #id: #owner
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'owner'
         #usePreferredWidth: true
         #width: 50
         #model: #owner
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Group'
         #id: #group
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #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: #sortFileListsBy:
         #labelActionArgument: 'fileSize'
         #columnAlignment: #decimal
         #usePreferredWidth: true
         #model: #sizeString
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Date & Time'
         #id: #time
         #labelAlignment: #right
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #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: #sortFileListsBy:
         #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: #browserItemList
              #useIndex: false
              #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
                #dropSelector: #doDrop:
                #dragArgument: #contentsbrowser
                #startDragSelector: #doStartDrag:in:
                #displayObjectSelector: #getDisplayObjects:
                #dropObjectSelector: #getDropObjects:
                #dropArgument: #browser
                #canDropSelector: #canDrop:
                #leaveSelector: #dropLeave:
                #enterSelector: #dropEnter:
                #overSelector: #dropOver:
              )
            )
           )
         
        )
      )
! !

!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: 'Cut'
            #itemValue: #doCut
            #translateLabel: true
            #shortcutKey: #Cut
          )
         #(#MenuItem
            #label: 'Copy'
            #itemValue: #doCopy
            #translateLabel: true
            #shortcutKey: #Copy
          )
         #(#MenuItem
            #enabled: #canPaste
            #label: 'Paste'
            #itemValue: #pasteFiles
            #translateLabel: true
            #shortcutKey: #Paste
          )
         #(#MenuItem
            #label: 'Delete'
            #itemValue: #doDelete
            #nameKey: #delete
            #translateLabel: true
            #shortcutKey: #Delete
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'New'
            #translateLabel: true
            #submenuChannel: #newMenu
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'Rename...'
            #itemValue: #renameSelection
            #translateLabel: true
            #shortcutKey: #Replace
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'Properties...'
            #itemValue: #doShowProperties
            #translateLabel: true
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'Edit'
            #itemValue: #doShowFileContents
            #translateLabel: true
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'File In'
            #itemValue: #fileFileIn
            #translateLabel: true
            #shortcutKey: #Accept
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'More'
            #translateLabel: true
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #label: 'Select All'
                  #itemValue: #selectAll
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #enabled: #fileListIsNotEmpty
                  #label: 'Copy Filenames'
                  #itemValue: #copyFileList
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #enabled: #hasFileSelection
                  #label: 'File InTo Namespace...'
                  #itemValue: #fileFileInToNameSpace
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #enabled: #hasTwoFilesSelected
                  #label: 'Compare with Each Other'
                  #itemValue: #doCompareTwoFiles
                  #translateLabel: true
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'View'
            #translateLabel: true
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #label: 'Details'
                  #nameKey: #View
                  #translateLabel: true
                  #submenuChannel: #viewBrowserMenu
                )
               #(#MenuItem
                  #label: 'Sort'
                  #nameKey: #Sort
                  #translateLabel: true
                  #submenuChannel: #sortMenu
                )
               #(#MenuItem
                  #label: 'Show'
                  #translateLabel: true
                  #submenu: 
                 #(#Menu
                    #(
                     #(#MenuItem
                        #label: 'Hidden Files'
                        #translateLabel: true
                        #indication: #showHiddenFiles
                      )
                     #(#MenuItem
                        #label: 'Directories'
                        #translateLabel: true
                        #indication: #viewDirsInContentsBrowser
                      )
                     )
                    nil
                    nil
                  )
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Update'
            #itemValue: #updateCurrentDirectory
            #translateLabel: true
          )
         )
        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: #programMenu>

    ^ 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:#viewBrowserMenuSpec
     (Menu new fromLiteralArrayEncoding:(DirectoryContentsBrowser viewBrowserMenuSpec)) startUp
    "

    <resource: #menu>

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

!DirectoryContentsBrowser methodsFor:'accessing'!

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

    ^ allItems
!

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

    allItems := something.
!

changeSema
    "return the value of the instance variable 'changeSema' (automatically generated)"
    changeSema isNil ifTrue:[
        changeSema := Semaphore new. 
    ].
    ^ changeSema
!

columnDescriptors
    "get the column description
    "
    ^ columnDescriptors
!

columnDescriptors:aListOfColumns
    "set the column description
    "
    |selection|

    columnDescriptors = aListOfColumns ifTrue:[
        ^ self
    ].

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

    selection := self selectedItems.
    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 wakeUpForDirectoryChanged.
!

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

    directoryChangeFlag isNil ifTrue:[
        directoryChangeFlag := false.
    ].
    ^ directoryChangeFlag
!

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

    directoryChangeFlag := something.
!

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

    directoryContentsChangeFlag isNil ifTrue:[
        directoryContentsChangeFlag := false.
    ].
    ^ directoryContentsChangeFlag
!

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

    directoryContentsChangeFlag := something.
!

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

    filterChangeFlag isNil ifTrue:[
        filterChangeFlag := false.
    ].
    ^ filterChangeFlag
!

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

    filterChangeFlag := something.
!

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

    isBusy isNil ifTrue:[
        isBusy := false.
    ].
    ^ isBusy
!

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

    isBusy := something.
!

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

    ^ matchBlock
!

matchBlock:filterBlock

    matchBlock := filterBlock.
!

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

    sortBlockChangeFlag isNil ifTrue:[
        sortBlockChangeFlag := false.
    ].
    ^ sortBlockChangeFlag
!

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

    sortBlockChangeFlag := something.
!

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

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

!DirectoryContentsBrowser methodsFor:'actions'!

browserItemListAdd:addItemCol remove:remItemCol
    | browserList selection|

    (addItemCol isEmpty and:[remItemCol isEmpty]) ifTrue:[ ^ self].
    selection := self selectedItems.
    remItemCol notEmpty ifTrue:[
        self browserItemList removeAllFoundIn:remItemCol        
    ].
    addItemCol notEmpty ifTrue:[
        browserList := self browserItemList copy.
        addItemCol do:[: aNewItem |
            (browserList includes:aNewItem) not ifTrue:[
                browserList add:aNewItem.
                browserList sort:self sortBlockHolder value.
                self browserItemList add:aNewItem beforeIndex:(browserList indexOf:aNewItem).
            ]
        ].
    ].
    self selectItems:selection.
!

currentSortOrderChanged

"/    self wakeUpForSortBlockChanged.
"/    have to reread the columns and set the icon for sort direction
"/    very expensive until table label can be changed directly
    self viewedColumnsChanged.
!

doResizeImage
    |column w h selection|

    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.

"/Processor activeProcess ~~ self windowGroup process ifTrue:[self halt].
    selection := self selectedItems.
    browserItemList do:[:el| el resetImageFile ].
    browser columnDescriptors:(self tableColumns value).
    self selectItems:selection.
    self wakeUp.
!

doUpdate
"/    DirectoryContents flushCache.
    self wakeUpForDirectoryContentsChanged.
!

doubleClickedAt:anItemIndex

    self withWaitCursorDo:[
        self enterActionFor:(self browserItemList at:anItemIndex). "/ anItemIndex.
    ]
!

enterActionFor:anItem
    | filename|

    filename := anItem fileName.

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

noOfAddedFiles:noOfAddedFiles noOfFiles:noOfFiles

    |info filterBox clr|

    noOfAddedFiles = noOfFiles ifTrue:[
        info := '%1 files'.
    ] ifFalse:[
        info := '%1 files shown (out of %2)'.
    ].
    self shownFiles value:(resources string:info with:noOfAddedFiles with:noOfFiles).

    filterBox := self filterValueBox value.
    filterBox isNil ifTrue:[^ self ].
    noOfAddedFiles ~= noOfFiles ifTrue:[
        clr := Color red lightened lightened lightened.
        [ filterBox flash] fork.                 
    ] ifFalse:[
        clr := self filterBackgroundColor value.
    ].
    filterBox backgroundColor:clr.
!

setBrowserItemList:filteredItems

    |showDir directoryUpItem locItems|

    showDir := self viewDirsInContentsBrowser value.
    (showDir and:[self directory notNil and:[self directory isRootDirectory not]]) ifTrue:[
        directoryUpItem := DirectoryContentsBrowser itemClass fileName:(self directory construct:'..').
        locItems := OrderedCollection with:directoryUpItem.
        locItems addAll:filteredItems.
    ] ifFalse:[
        locItems := filteredItems.
    ].
    self updateToExternFileHolderLock doLocked:[
        self browserItemList contents:locItems asList.
    ].
"/    Transcript showCR:'after insert list', AbsoluteTime now asString.
    self selectCurrentFiles.
! !

!DirectoryContentsBrowser methodsFor:'aspects'!

browserItemList

    browserItemList isNil ifTrue:[
        browserItemList := List new.
    ].
    ^ browserItemList
!

selectionInFileList

    selectionInFileList isNil ifTrue:[
        selectionInFileList := ValueHolder new.
        selectionInFileList addDependent:self.
    ].
    ^ selectionInFileList
!

tableColumns

    tableColumns isNil ifTrue:[
        tableColumns := self class tableColumns asValue.
    ].
    ^ tableColumns.
! !

!DirectoryContentsBrowser methodsFor:'change & update'!

currentFileNameHolderChanged
    "filename changed
    "
    |newDir currentDir aColOfFiles selectedFile|

    aColOfFiles := self currentFileNameHolder value.
    (aColOfFiles isEmpty or:[self currentFilesAreInSameDirectory not]) ifTrue:[
        self directory:nil.
        ^ self
    ].
    aColOfFiles size == 1 ifTrue:[
        selectedFile := aColOfFiles first.
        selectedFile isSymbolicLink ifTrue:[
            self notify:'Symbolic link to: ' , (selectedFile linkInfo at:#path) allBold
        ] ifFalse:[
            self startDiskUsageInfoProcessFor:selectedFile.
        ].
    ].

    newDir := self currentDirectories value first.
    currentDir := self directory.
    newDir = currentDir ifTrue:[
        self selectFiles:aColOfFiles.
        ^ self.
    ].
    "/ change of the directory filename 
    self directory:newDir.
!

filterBlockHolderChanged

    self matchBlock:self filterBlockHolder value.
    self wakeUpForFilterChanged.
!

selectionChanged

    | selection newCurrentFilename |

    self directory isNil ifTrue:[^ self].   
    selection := self selectedFiles.
    selection isEmpty ifTrue:[
        newCurrentFilename := OrderedCollection with:self directory.
    ] ifFalse:[
        newCurrentFilename := selection.
    ].
    self currentFileNameHolder value ~= newCurrentFilename ifTrue:[
         self currentFileNameHolder value:newCurrentFilename withoutNotifying:self
    ]
!

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

    aModel == self currentFileNameHolder ifTrue:[
        super update:something with:aParameter from:aModel.
        self updateContentsSelection doIfUnLocked:[
            self currentFileNameHolderChanged.
        ].
        ^ self.
    ].             
    aModel == self selectionInFileList ifTrue:[
        self updateToExternFileHolderLock doIfUnLocked:[
            self updateContentsSelection doLocked:[
                self selectionChanged.
            ]
        ].
        ^ self.
    ].
    aModel == self currentSortOrder ifTrue:[
        self currentSortOrderChanged.
        ^ self.
    ].
    aModel == self filterBlockHolder ifTrue:[
        self filterBlockHolderChanged.
        ^ self.
    ].             
    aModel == self sortBlockHolder ifTrue:[
        self wakeUpForSortBlockChanged.
        ^ self.
    ].             
    aModel == self viewDirsInContentsBrowser ifTrue:[
        self wakeUpForFilterChanged.
        ^ self
    ].

    super update:something with:aParameter from:aModel
!

viewPreviewChanged

    browserItemList do:[:el| el resetImageFile ].
    self viewedColumnsChanged.
!

viewedColumnsChanged

    |columns buffer currentSortOrder currentSortOrderColumn currentSortOrderReverse selection|

    selection := self selectedItems.
    buffer := self class tableColumns 
                collect:[:col| (DataSetColumnSpec new fromLiteralArrayEncoding:col)].

    columns := buffer asOrderedCollection.
    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.
        currentSortOrder := self currentSortOrder value.
        currentSortOrderColumn := currentSortOrder at:#column ifAbsent:nil.
        currentSortOrderReverse := currentSortOrder at:#reverse ifAbsent:nil.
        buffer do:[:col |
            | id |
            id := col id.
            id notNil ifTrue:[
                (col labelActionArgument notNil and:[col labelActionArgument asSymbol == currentSortOrderColumn]) ifTrue:[
                    | label icon|
                    label := col label.
                    icon := currentSortOrderReverse ifTrue:[self class detailsMenuIconDown] ifFalse:[self class detailsMenuIconUp].
                    col label:(LabelAndIcon label:label icon:icon).
                ].
                (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 updateToExternFileHolderLock doLocked:[
        self tableColumns value:columns.
    ].
    self columnDescriptors:(self tableColumns value).
    self selectItems:selection.
    self wakeUp.
! !

!DirectoryContentsBrowser methodsFor:'drag & drop'!

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

    selectedFiles := self selectedFiles.
    (selectedFiles isEmpty) ifTrue:[
        ^ self.
    ].
    ^ super doStartDrag:aDropSource in:aView
!

dropDestinationPath
    |destinationPath|

    draggedItem isNil ifTrue:[
        destinationPath := self directory.
    ] ifFalse:[
        destinationPath := self getDirWithoutFileName:(draggedItem fileName).
        destinationPath baseName = '..' ifTrue:[
            destinationPath := self directory directory.
        ]
    ].

    ^ destinationPath
!

dropOver:aDropContext 
    "called during drag & drop while moving over the widget."

    |lineNr newDest destinationFile|

    inDropMode ifFalse:[^ self].

    lineNr  := self getLineNumberFor:aDropContext.
    lineNr notNil ifTrue:[
        newDest := browserItemList at:lineNr.
        (newDest notNil and:[newDest isDirectory]) ifTrue:[
            destinationFile := newDest fileName.
            destinationFile baseName = '..' ifTrue:[
                destinationFile := self directory directory.
            ]
        ].
    ].

    newDest == draggedItem ifTrue:[
        "/ beyond last contents-line
        newDest isNil ifTrue:[
            canDropItem := self canDropFiles:(aDropContext dropObjects) for:self directory.
        ].
        ^ self
    ].

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

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

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
    |selectedItems string fnName stream|

    selectedItems := self selectedFiles.
    selectedItems isEmpty ifTrue:[ ^ ''].

    stream := WriteStream on:''.
    stream nextPutAll:(selectedItems first baseName).
    selectedItems size == 1 ifTrue:[
        fnName := 'ui_menuitem.xpm'.
    ] ifFalse:[
        fnName := 'ui_submenu_open.xpm'.
        stream nextPutAll:' ... '.
        stream nextPutAll:(selectedItems last baseName).
    ].
    string := stream contents.
    stream close.

    ^ Array with:(LabelAndIcon icon:(Image fromFile:fnName)
                             string:(Text string:string emphasis:#bold)
                 )
!

removeExpandItemTimedBlock
    "left blank"
! !

!DirectoryContentsBrowser methodsFor:'event handling'!

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

    |focusView key rawKey|

    anEvent isKeyPressEvent ifFalse:[^ false].

    focusView := anEvent targetView.
    (focusView isNil or:[focusView ~~ browser]) ifTrue:[ ^ false].

    key := anEvent key.
    rawKey := anEvent rawKey.

   "/ (key ~= #'Alt_L') ifTrue:[self halt.].
    key == #Accept ifTrue:[
        self fileFileIn.
        ^ true.
    ].
    key == #Paste ifTrue:[
        self pasteFiles.
        ^ true.
    ].
"/    (key == #CursorLeft) ifTrue:[
"/        self doGoDirectoryUp.
"/        ^ true
"/    ].
"/    (key == #CursorRight) ifTrue:[
"/        self doBack.
"/        ^ true
"/    ].
    browserItemList size == 0 ifTrue:[
        ^ false
    ].

    key == #Cut ifTrue:[
        self doCut.
        ^ true.
    ].
    key == #Copy ifTrue:[
        self doCopy.
        ^ true.
    ].
    key == #Delete ifTrue:[
        self doDelete.
        ^ true.
    ].
    (key == #Return) ifTrue:[
        self doOpenSelectedFile.
        ^ true.
    ].

    ^ false
! !

!DirectoryContentsBrowser methodsFor:'menu accessing'!

viewBrowserMenu

    <resource: #programMenu >

    |menu|

    viewBrowserMenu isNil ifTrue:[
        viewBrowserMenu :=  self class viewBrowserMenu.
    ].
    viewBrowserMenu ifNil:[ ^ nil ].
    viewBrowserMenu findGuiResourcesIn:self.

    menu := viewBrowserMenu.
    menu receiver:self.
    ^ menu
! !

!DirectoryContentsBrowser methodsFor:'menu actions'!

doOpenSelectedFile

    | selectedItems|

    selectedItems := self selectedItems.
    (selectedItems size == 1) ifTrue:[ 
        self enterActionFor:(selectedItems first).
    ].
!

doShowFileContents

    | selectedFileItems|

    selectedFileItems := self selectedFileItems.
    (selectedFileItems size == 1) ifTrue:[ 
        self openTextEditorOn:(selectedFileItems first).
    ].
! !

!DirectoryContentsBrowser methodsFor:'queries'!

allItemsOfCurrentDirectory
    self viewDirsInContentsBrowser value ifTrue:[
        ^ browserItemList copyFrom:2.      "/ remove the '..' item
    ].
    ^ browserItemList.
!

fileListIsEmpty

    ^ browserItemList isEmpty
!

getAllFilesAsStrings
    browserItemList isEmpty ifTrue:[
        ^ #()
    ].
    ^ browserItemList 
        select:[:anItem | anItem isDirectory not]
        thenCollect:[:eachItem | eachItem fileName asString].
!

hasFilesFiltered

    ^ self allItems size ~~ browserItemList size
! !

!DirectoryContentsBrowser methodsFor:'selection'!

selectAll

    | sel|

    sel := browserItemList copy.
    self viewDirsInContentsBrowser value ifTrue:[
        sel removeIdentical:(browserItemList first) ifAbsent:[nil]
    ].
    self selectionInFileList value:sel.
!

selectCurrentFiles

    |currentSelection|

    ((currentSelection := self currentFileNameHolder value select:[:file| file isDirectory not]) notEmpty) ifTrue:[
        self selectFiles:currentSelection.
    ].
!

selectFiles:aColOfFilenames
    | curSel newSel|

    curSel := self selectedFiles.
    aColOfFilenames size = curSel size ifTrue:[
        | index |
        index := curSel findFirst:[:file| (aColOfFilenames includes:file) not].
        index == 0 ifTrue:[ ^ self].
    ].
    newSel := OrderedCollection new.
    browserItemList do:[: item|
        (aColOfFilenames includes:(item fileName)) ifTrue:[
            newSel add:item.
        ].
    ].
    self updateToExternFileHolderLock doLocked:[
        self selectionInFileList value:newSel withoutNotifying:self.
    ].
!

selectItems:aColOfItems
    self selectFiles:(aColOfItems collect:[:eachItem | eachItem fileName]).

"/    | curSel newSel|
"/
"/    curSel := self selectedItems.
"/    aColOfItems size = curSel size ifTrue:[
"/        | index |
"/        index := curSel findFirst:[:item| (aColOfItems includes:item) not].
"/        index == 0 ifTrue:[ ^ self].
"/    ].
"/    newSel := OrderedCollection new.
"/    browserItemList do:[: item|
"/        (aColOfItems includes:item) ifTrue:[
"/            newSel add:item.
"/        ].
"/    ].
"/    self updateToExternFileHolderLock doLocked:[
"/        self selectionInFileList value:newSel withoutNotifying:self.
"/    ].
!

selectedFileItems

    | sel |

    sel := self selectedItems select:[:item | item isDirectory not].
    ^ sel
!

selectedFileNames
    ^ self selectedFileItems collect:[:item| item fileName].
!

selectedItems
    | selection |

    selection := self selectionInFileList value copy.
    selection isNil ifTrue:[ ^ #()].
    (self viewDirsInContentsBrowser value and:[browserItemList notEmpty]) ifTrue:[
        selection removeIdentical:(browserItemList first) ifAbsent:[nil]
    ].
    ^ selection.
! !

!DirectoryContentsBrowser methodsFor:'startup & release'!

makeDependent

    super makeDependent.
    self currentFileNameHolder addDependent:self.
    self currentSortOrder addDependent:self.
    self viewDirsInContentsBrowser addDependent:self.
    self filterBlockHolder addDependent:self.
    self sortBlockHolder 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:#viewPreviewChanged to:self.
    self viewSize onChangeSend:#viewedColumnsChanged to:self.
    self viewTime onChangeSend:#viewedColumnsChanged to:self.
    self viewType onChangeSend:#viewedColumnsChanged 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 tableColumns value).
!

postOpen

    self startUpdateTask.
    self matchBlock:self filterBlockHolder value.
    self currentFileNameHolderChangedForCommon.
    self currentFileNameHolderChanged.
    self viewedColumnsChanged.
    self windowGroup addPreEventHook:self.
!

release
    "release my resources
    "                    
    self stopUpdateTask.
    super release.
!

releaseAsSubCanvas

    self stopUpdateTask.
    ^ super releaseAsSubCanvas.
! !

!DirectoryContentsBrowser methodsFor:'update columns cycle'!

findNextItemFor:aBlock

    ^ self allItems detect:aBlock ifNone:[nil].
!

findNextVisibleItemFor:aConditionBlock
    |vis idx stillSearching |

    vis := browser indexOfFirstRowShown.
    vis = 0 ifTrue:[vis := 1].

    idx := vis.
    stillSearching := true.
    [ stillSearching ] whileTrue:[
        ((idx <= browserItemList size) and:[ (browser isRowVisible:idx) ]) ifFalse:[
            stillSearching := false.
            idx := nil.
        ] ifTrue:[
            (aConditionBlock value:(browserItemList at:idx)) ifTrue:[
                "/ found a one
                stillSearching := false.
            ] ifFalse:[
                idx := idx + 1.
            ]
        ].
    ].
    idx isNil ifTrue:[^ nil].
    ^ browserItemList at:idx ifAbsent: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.
!

getIconForMimeType:mime for:aItem

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

getIconForMimeTypeByContents:aItem
    ^ self getIconForMimeType:(aItem mimeTypeForContents) for:aItem
!

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 pathName).
                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 resetItemForChanges.
!

updateColumnsCycle

" 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 mime icon type preview|

    browserItemList notEmpty ifTrue:[
        "/ update item if item properties are changed
        desc := self findNextVisibleItemFor:[:n| n hasBeenModified ].

        desc notNil ifTrue:[
            self updateChangesFor:desc.
            browser invalidateVisibleRow:desc.
            self debugMessage:'item properties are changed', desc baseName.
            ^ true.
        ].
        "/ update icon for visible items by suffix
        desc := self findNextVisibleItemFor:[: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
            ].
            self debugMessage:'icon for visible items by suffix', desc baseName.
            ^ true
        ].
        "/ update icon for visible items by contents
        desc := self findNextVisibleItemFor:[:n| 
            (n iconKey == #file 
            and:[(mime := n mimeTypeForContents asSymbol) ~= #unknown])
        ].
        desc notNil ifTrue:[
            icon := self getIconForMimeType:mime for:desc.
"/            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 findNextVisibleItemFor:[:n| n fileType isEmpty ].
            desc notNil ifTrue:[
                type := self getFileInfoFor:desc.
                type notNil ifTrue:[browser invalidateVisibleRow:desc colAt:fileDescriptionIndex].
                self debugMessage:'file info for visible items if file info column is shown', desc baseName.
                ^ true
            ]
        ].
        "/ update preview for visible items if preview column is shown
        previewIndex ~~ 0 ifTrue:[
            desc := self findNextVisibleItemFor:[:n| n preview isNil ].
            desc notNil ifTrue:[
                preview := self getPreviewFor:desc.
                preview notNil ifTrue:[
                    browser invalidateVisibleRow:desc colAt:previewIndex
                ].
                self debugMessage:'preview for visible items if preview column is shown', desc baseName.
                ^ true
            ]
        ].
    ].

    "/ validate invisible items to make them available after scrolling
    self allItems 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.
            self debugMessage:'icon for invisible items by suffix', desc baseName.
            ^ 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.
            self debugMessage:'icon for invisible items by contents', desc baseName.
            ^ 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.
                self debugMessage:'update file info for invisible items if file info column is shown', desc baseName.
                ^ 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.
                self debugMessage:'update preview for invisible items if preview column is shown:', desc baseName.
                ^ 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.
            self debugMessage:'update file info for invisible items if file info column is not shown:', desc baseName.
            ^ true
        ].
    ].
    ^ false
! !

!DirectoryContentsBrowser methodsFor:'update modification cycle'!

modificationCycle

    | oldModificationTime |
    directory notNil ifTrue:[
        directory exists not ifTrue:[
            self directoryChangeFlag:true.
            ^ self
        ].
        oldModificationTime := modificationTime.
        modificationTime := directory modificationTime.
        (directory notNil and:[oldModificationTime ~= directory modificationTime]) ifTrue:[
            self wakeUpForDirectoryContentsChanged.
        ].
    ].
! !

!DirectoryContentsBrowser methodsFor:'update task'!

createItemList

"/    |filterTime sortTime getAllTime|

    directory notNil ifTrue:[
        modificationTime := directory modificationTime.
"/        getAllTime := Time millisecondsToRun:[
            self allItems:self readDirectoryItems.
"/        ].
"/        filterTime := Time millisecondsToRun:[
            filteredItems := self evaluateFilter:(self allItems).
"/        ].
"/        sortTime := Time millisecondsToRun:[
            filteredItems sort:self sortBlockHolder value.
"/        ].
"/        Transcript showCR:'getAllTime:', getAllTime asString.
"/        Transcript showCR:'filterTime:', filterTime asString.
"/        Transcript showCR:'sortTime:', sortTime asString.
"/        Transcript showCR:'time after sort', AbsoluteTime now asString.
    ] ifFalse:[
        self allItems:OrderedCollection new.
        filteredItems := OrderedCollection new.
    ].
    self
        enqueueMessage:#value 
        for:[ self setBrowserItemList:filteredItems ]
        arguments:#().
!

debugMessage:aString

    self class debug ifTrue:[
        Transcript showCR:aString
    ].
!

directoryChanged

    self setBrowserItemList:#().
    self createItemList.
!

directoryContentsChanged

    |oldItems addItems remItems newItems newAllItems theAllItems|

    directory isNil ifTrue:[^ self].
    modificationTime := directory modificationTime.
    newAllItems := self readDirectoryItems.
    newAllItems size > 150 ifTrue:[ 
        self createItemList.
        ^ self
    ].
    newItems := self evaluateFilter:newAllItems.
    oldItems := filteredItems copy.
    addItems := OrderedCollection new.
    remItems := OrderedCollection new.
    filteredItems size == 0 ifTrue:[ 
        remItems := oldItems. 
    ] ifFalse:[
        "/ MERGE WITH CURRENT CONTENTS
        oldItems reverseDo:[:anOldItem|
            "/ remove no longer valid files
            (newItems detectLast:[:aNewItem| aNewItem = anOldItem] ifNone:nil) isNil ifTrue:[
                remItems add:anOldItem.
                oldItems removeIdentical:anOldItem
            ]
        ].
    ].
    "/ add new files
    newItems do:[:aNewItem|
        (oldItems detect:[:anOldItem| anOldItem = aNewItem] ifNone:nil) isNil ifTrue:[
            addItems add:aNewItem.
        ]
    ].
    theAllItems := self allItems.
    remItems notEmpty ifTrue:[
        theAllItems removeAll:remItems        
    ].
    addItems do:[: aNewItem |
        (theAllItems includes:aNewItem) not ifTrue:[
            theAllItems add:aNewItem.
        ]
    ].

    self
        enqueueMessage:#value 
        for:[ self browserItemListAdd:addItems remove:remItems. ]
        arguments:#().
    filteredItems := newItems.
!

evaluateFilter:aItemsList

    |showDir filterBlock newItemList noOfAddedFiles noOfFiles|

    newItemList := OrderedCollection new.
    noOfFiles := 0.
    noOfAddedFiles := 0.
    showDir := self viewDirsInContentsBrowser value.
    filterBlock := self matchBlock.
    aItemsList size ~~ 0 ifTrue:[ 
        aItemsList do:[:aItem | |isDir|
            isDir := aItem isDirectory.
            isDir not ifTrue:[noOfFiles := noOfFiles + 1].
            ((isDir and:[showDir]) or:[isDir not and:[filterBlock value:aItem baseName]]) ifTrue:[
                isDir not ifTrue:[noOfAddedFiles := noOfAddedFiles + 1].
                newItemList add:aItem 
            ].
        ].
    ].
    self noOfAddedFiles:noOfAddedFiles noOfFiles:noOfFiles.
    ^ newItemList
!

filterChanged

    filteredItems := self evaluateFilter:self allItems copy.
    filteredItems sort:self sortBlockHolder value.
    self
        enqueueMessage:#value 
        for:[ self setBrowserItemList:filteredItems ]
        arguments:nil.
!

readDirectoryItems
    | list contents|

    list := OrderedCollection new.
    (directory notNil and:[directory exists]) ifTrue:[ 
        contents := DirectoryContents directoryNamed:directory.
        contents itemsDo:[:aItem|
            list add:(DirectoryContentsBrowser itemClass forInfoItem:aItem).
        ].
    ].
    ^ list
!

sortBlockChanged

    filteredItems isNil ifTrue:[ ^ self].
    filteredItems sort:self sortBlockHolder value.
    self
        enqueueMessage:#value 
        for:[ self setBrowserItemList:filteredItems ]
        arguments:nil.
!

startUpdateTask
    updateTask isNil ifTrue:[
        updateTask := [ 
                        [true] whileTrue:[
                            self updateStep
                        ] 
                      ] newProcess.
        updateTask name:'DirectoryContentsBrowser updateTask'.
        updateTask priorityRange:(Processor systemBackgroundPriority to:Processor activePriority).
        updateTask restartable:true.
        updateTask resume.
    ].
!

stopUpdateTask

    updateTask notNil ifTrue:[
        updateTask terminate.
    ].
!

updateBlock

    ^ [ : timeOut | 
"/        Transcript showCR:'START'.
        self isBusy:true.
        [
            self directoryChangeFlag ifTrue:[ 
                self directoryChangeFlag:false.
                self debugMessage:'directoryChanged'.
                self directoryChanged.
            ].
            self directoryContentsChangeFlag ifTrue:[
                self directoryContentsChangeFlag:false.
                self debugMessage:'directoryContentsChanged'.
                self directoryContentsChanged.
            ].
            self filterChangeFlag ifTrue:[
                self filterChangeFlag:false.
                self debugMessage:'filterChanged'.
                self filterChanged.
            ].
            self sortBlockChangeFlag ifTrue:[
                self sortBlockChangeFlag:false.
                self debugMessage:'sortBlockChanged'.
                self sortBlockChanged.
            ].
            timeOut ifTrue:[
                self debugMessage:'evaluate modification'.
                self modificationCycle
            ].
            self updateColumnsCycle ifTrue:[
                self debugMessage:'next time another columns update'.
                self changeSema signal
            ].
        ] ensure:[
            self isBusy:false.
        ].
"/        Transcript showCR:'STOP'.
    ]
!

updateStep
    AbortSignal handle:[
        self debugMessage:'got an abort signal'.
        self directoryContentsChangeFlag:false.
        self filterChangeFlag:false.
        self sortBlockChangeFlag:false.
    ] do:[
        | timedOut |

        self debugMessage:'sema wait'.
        timedOut := (self changeSema waitWithTimeoutMs:(self class updateTaskCyleTime)) isNil.
        self debugMessage:'timedOut:', timedOut asString.

        (self filterChangeFlag 
        or:[self sortBlockChangeFlag 
        or:[self directoryChangeFlag]]) ifTrue:[
            self withCursor:Cursor execute do:[
                self updateBlock value:timedOut.
            ]
        ] ifFalse:[
            self updateBlock value:timedOut.
        ]
    ]
! !

!DirectoryContentsBrowser methodsFor:'update task - disk usage'!

diskUsageInKiloBytesFor:aDirectory
    |line kb|

    OperatingSystem isUNIXlike ifTrue:[
        line := OperatingSystem getCommandOutputFrom:('du -s -k ' , aDirectory pathName).
        line notNil ifTrue:[
            kb := Number readFrom:line readStream.
        ].
        ^ kb
    ].

    ^ nil "/ unknown - for now.
!

isDiskUsageInfoStillValid:infoForDirectory
    |diskUsageRememberTime infoTimestamp infoKiloBytes infoMegaBytes|

    diskUsageRememberTime := 30.

    infoTimestamp := infoForDirectory key.
    infoKiloBytes := infoForDirectory value.
    infoMegaBytes := infoKiloBytes // 1024.

    "/ only update info every 30 seconds
    "/ plus another second for every 5 megabytes
    "/ (i.e. for 100 mb, keep info for 50seconds)

    diskUsageRememberTime := diskUsageRememberTime + (infoMegaBytes / 5). "/ another second for every megabyte

    (AbsoluteTime now getSeconds - infoTimestamp getSeconds) > diskUsageRememberTime ifTrue:[
        ^ false
    ].
    ^ true
!

showDiskUsageInfoFor:aDirectory as:kiloBytes
    |kbString|

    kbString := UnitConverter fileSizeStringFor:(kiloBytes * 1024).
    self notify:('%2 in %1' 
                    bindWith:(aDirectory baseName "pathName" allBold) 
                    with:(kbString , 'b') "allBold").
!

startDiskUsageInfoProcessFor:selectedFile
    |directory dirPath infoForDirectory infoValue|

    self showDiskUsageHolder value ifFalse:[^ self].

    OperatingSystem isUNIXlike ifFalse:[
        ^ self. "/ not yet implemented - see diskUsageInKiloBytesFor:
    ].

    selectedFile isDirectory ifTrue:[
        directory := selectedFile
    ] ifFalse:[
        directory := selectedFile directory
    ].

    dirPath := directory pathName.
    diskUsageInfo isNil ifTrue:[
        diskUsageInfo := Dictionary new.
    ].
    infoForDirectory := diskUsageInfo at:dirPath ifAbsent:nil.
    infoForDirectory notNil ifTrue:[
        (self isDiskUsageInfoStillValid:infoForDirectory) ifTrue:[
            infoValue := infoForDirectory value.
        ].
    ].

    infoValue notNil ifTrue:[
        self showDiskUsageInfoFor:directory as:infoValue.
        ^ self.
    ].

    diskUsageUpdateProcess notNil ifTrue:[
        diskUsageUpdateProcess terminate
    ].
    diskUsageUpdateProcess :=
        [
            |kiloBytes|

            self notify:('collecting disk usage of %1...' bindWith:directory baseName).
            kiloBytes := self diskUsageInKiloBytesFor:directory.
            kiloBytes notNil ifTrue:[
                diskUsageInfo at:dirPath put:(AbsoluteTime now -> kiloBytes).
                self showDiskUsageInfoFor:directory as:kiloBytes.
            ]
        ] fork.
! !

!DirectoryContentsBrowser methodsFor:'update task trigger'!

wakeUp

    self changeSema signal.
!

wakeUpForDirectoryChanged

    self isBusy ifTrue:[
        updateTask interruptWith:[AbortSignal raise].
    ].
    self directoryChangeFlag:true.
    self wakeUp.
!

wakeUpForDirectoryContentsChanged

    self directoryContentsChangeFlag:true.
    self wakeUp.
!

wakeUpForFilterChanged

    self filterChangeFlag:true.
    self wakeUp.
!

wakeUpForSortBlockChanged

    self sortBlockChangeFlag:true.
    self wakeUp.
! !

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

fileName:aFilename 

    | instance fileItem|
    instance := self new.
    aFilename asString = '..' ifTrue:[
        instance fileName:aFilename.
        ^ instance.
    ].
    fileItem := DirectoryContents contentsItemForFileName:aFilename.
    fileItem notNil ifTrue:[^ self forInfoItem:fileItem].
    ^ nil

    "
     DirectoryContentsItem fileName:'..'
    "
!

forInfoItem:anItem
    | instance |
    instance := self new.
    instance fileName:anItem fileName.
    anItem isRemoteDirectory ifTrue:[
        instance beRemoteDirectory.
    ] ifFalse:[
        instance fileInfo:anItem info.
    ].
    ^ instance
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'accessing'!

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

    fileInfo := something.
!

fileName
    "returns the fileName
    "
    ^ fileName


!

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

fileType
    "returns the type of the file
    "
    fileType isNil ifTrue:[
        fileType := ''.
    ].
    ^ 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
!

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
!

preview

    ^ preview
!

preview:aLabel

    preview := aLabel.
!

suffix
    "returns the suffix of the file
    "
    self isDirectory ifTrue:[^''].
    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
    ]
!

resetItemForChanges

    " 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.
    group := nil.
    iconKey := nil.
    mimeTypeForContents := nil.
    mimeType := 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.
!

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

    self isDirectory ifTrue:[^ self baseName].
    baseNameWithOutSuff := self baseName asFilename withoutSuffix asString.
    (baseNameWithOutSuff isEmpty or:[baseNameWithOutSuff = '.']) 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
!

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

    ^ 'a DirectoryContentsItem [', fileName asString, ']' 
!

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

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

    unitString := UnitConverter fileSizeStringFor:locSize.
    ^ unitString withoutSeparators "/ leftPaddedTo:8.

"/    unitString := ''.
"/    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
! !

!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.97 2003-04-03 13:39:26 stefan Exp $'
! !