DirectoryContentsBrowser.st
author Claus Gittinger <cg@exept.de>
Tue, 21 Oct 2003 20:21:29 +0200
changeset 5290 8aeb40fef0f7
parent 5288 ded5fff91032
child 5312 63b275aa2c69
permissions -rw-r--r--
wait cursor

"
 COPYRIGHT (c) 2002 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

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

copyright
"
 COPYRIGHT (c) 2002 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    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: ''
         #labelButtonType: #Group
         #width: 22
         #minWidth: 22
         #height: 16
         #model: #icon
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Filename'
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'baseName'
         #minWidth: 150
         #model: #baseName
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Suffix'
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'suffix'
         #usePreferredWidth: true
         #width: 40
         #model: #suffix
         #canSelect: false
         #isResizeable: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Inode'
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'inodeNumber'
         #usePreferredWidth: true
         #width: 75
         #model: #inodeNumber
         #canSelect: false
         #isResizeable: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Perm'
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'permissions'
         #usePreferredWidth: true
         #width: 75
         #model: #permissions
         #canSelect: false
         #isResizeable: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Owner'
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'owner'
         #usePreferredWidth: true
         #width: 50
         #model: #owner
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Group'
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'group'
         #usePreferredWidth: true
         #width: 40
         #model: #group
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Size'
         #translateLabel: true
         #labelAlignment: #right
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'fileSize'
         #columnAlignment: #decimal
         #usePreferredWidth: true
         #minWidth: 45
         #model: #sizeString
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Bytes'
         #translateLabel: true
         #labelAlignment: #right
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'fileSize'
         #columnAlignment: #right
         #usePreferredWidth: true
         #minWidth: 45
         #model: #sizeInBytesString
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Date & 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'
         #labelAlignment: #left
         #labelButtonType: #Button
         #labelActionSelector: #sortFileListsBy:
         #labelActionArgument: 'fileType'
         #width: 250
         #model: #fileInfoString
         #canSelect: false
         #showRowSeparator: false
         #showColSeparator: false
       )
      #(#DataSetColumnSpec
         #label: 'Pre +/-'
         #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)
          bounds: (Rectangle 14 46 602 524)
          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
                overSelector: dropOver:
                dragArgument: contentsbrowser
                startDragSelector: doStartDrag:in:
                displayObjectSelector: getDisplayObjects:
                dropObjectSelector: getDropObjects:
                dropArgument: browser
                canDropSelector: canDrop:
                leaveSelector: dropLeave:
                enterSelector: dropEnter:
                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: '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
            #label: 'Operations'
            #translateLabel: true
            #submenuChannel: #fileOpMenu
          )
         #(#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: 'Tools'
            #translateLabel: true
            #submenuChannel: #toolsMenuSpec
          )
         #(#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: 'Show Details'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewDetails
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #enabled: true
            #label: 'Icon'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewIcon
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Suffix'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewType
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Inode'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewInodeNumber
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Permissions'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewPermissions
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Owner'
            #translateLabel: true
            #isVisible: #userContextAvailable
            #hideMenuOnActivated: false
            #indication: #viewOwner
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Group'
            #translateLabel: true
            #isVisible: #userContextAvailable
            #hideMenuOnActivated: false
            #indication: #viewGroup
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Size'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewSize
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Size (Bytes)'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewSizeInBytes
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'Date && Time'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewTime
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: 'File Info'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #viewDescription
          )
         #(#MenuItem
            #enabled: #viewDetails
            #label: '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.
!

browserFileList
    ^ self browserItemList collect:[:item | item fileName]
!

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
    "
    columnDescriptors = aListOfColumns ifTrue:[
        ^ self
    ].

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

    aListOfColumns keysAndValuesDo:[:anIndex :aDesc| 
        |col readSelector|

        col := aDesc isSequenceable 
                ifTrue:[DataSetColumnSpec new fromLiteralArrayEncoding:aDesc]
                ifFalse:[aDesc].
        columnDescriptors add:col.

        "/ cg: UGLY CODE NOTIFICATION
        readSelector := col readSelector.
        readSelector == #preview ifTrue:[
            previewIndex := anIndex.
            iconExtent := Point x:(col width) y:(col height).
        ] ifFalse:[
            readSelector == #fileInfoString ifTrue:[
                fileDescriptionIndex := anIndex.
            ] ifFalse:[
                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 selectedItemsWithoutDotDotDirectory.
    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.

    directory notNil ifTrue:[
        self flushRememberedDiskUsageInfoFor:directory.
    ] ifFalse:[
        self flushRememberedDiskUsageInfo.
    ].
    self startDiskUsageInfoProcess.
!

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 fileName isDirectory) ifTrue:[
        self currentFileNameHolder value:(OrderedCollection with:filename).
        ^ self.
    ].
    self withWaitCursorDo:[
        self openApplByFileItem:anItem.
    ]
!

noOfAddedFiles:noOfAddedFiles noOfFiles:noOfFiles

    |info filterBox clr|

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

    filterBox := self filterValueBox value.
    filterBox isNil ifTrue:[^ self ].
    noOfAddedFiles ~= noOfFiles ifTrue:[
        clr := Color red lightened lightened lightened.
        [ View drawingOnClosedDrawableSignal catch:[ 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:[
        locItems := OrderedCollection new.
        directoryUpItem := DirectoryContentsBrowser itemClass fileName:(self directory construct:'..').
        directoryUpItem notNil ifTrue:[
            locItems add: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.
!

valueHolderForColumn:id
    "xlate #suffix -> viewType, etc...
     i.e. for a columns readSelector, return my valueHolder"

    ^ self perform:('view' , id asUppercaseFirst) asSymbol
!

viewFileInfoString
    ^ self viewDescription
!

viewSizeInBytesString
    ^ self viewSizeInBytes
!

viewSizeString
    ^ self viewSize
!

viewSuffix
    ^ self viewType
!

viewTimeAndDate
    ^ self viewTime
! !

!DirectoryContentsBrowser methodsFor:'aspects-visibility'!

colVisibilityAspectFor:aKey ifAbsent:absentBlock
    "aspect for a columns visibility"

    |holder|

    holder := self aspectFor:aKey ifAbsent:absentBlock.
    holder onChangeSend:#viewedColumnsChanged to:self.
    ^ holder
!

viewDescription
    "aspect for show file description"

    ^ self colVisibilityAspectFor:#viewDescription ifAbsent:[ false asValue ].
!

viewDetails
    "aspect for show more file properties 
    "

    ^ self colVisibilityAspectFor:#viewDetails ifAbsent:[ false asValue ].
!

viewGroup
    " aspect for show group information "

    ^ self colVisibilityAspectFor:#viewGroup ifAbsent:[ false asValue ].
!

viewIcon
    " aspect for show file-type icon"

    ^ self colVisibilityAspectFor:#viewIcon ifAbsent:[ true asValue ].
!

viewInodeNumber
    " aspect for show inode number "

    ^ self colVisibilityAspectFor:#viewInodeNumber ifAbsent:[ false asValue ].
!

viewOwner
    " aspect for show owner information "

    ^ self colVisibilityAspectFor:#viewOwner ifAbsent:[ false asValue ].
!

viewPermissions
    " aspect for show permission information "

    ^ self colVisibilityAspectFor:#viewPermissions ifAbsent:[ false asValue ].
!

viewPreview

    " aspect for show image previev "

    ^ self colVisibilityAspectFor:#viewPreview ifAbsent:[ false asValue ].
!

viewSize

    " aspect for show size information "

    ^ self colVisibilityAspectFor:#viewSize ifAbsent:[ false asValue ].
!

viewSizeInBytes
    " aspect for show size-in-bytes information "

    ^ self colVisibilityAspectFor:#viewSizeInBytes ifAbsent:[ false asValue ].
!

viewTime

    " aspect for show time information "

    ^ self colVisibilityAspectFor:#viewTime ifAbsent:[ false asValue ].
!

viewType
    " aspect for show suffix (type) information "

    ^ self colVisibilityAspectFor:#viewType ifAbsent:[ false asValue ].
! !

!DirectoryContentsBrowser methodsFor:'change & update'!

currentFileNameHolderChanged
    "filename changed
    "
    |newDir currentDir aColOfFiles|

    aColOfFiles := self currentFileNameHolder value.
    aColOfFiles isEmpty ifTrue:[
        "/ selection is gone keep the current directory list
        ^ self
    ].
    (self currentFilesAreInSameDirectory not) ifTrue:[
        self directory:nil.
        ^ self
    ].
    self currentDirectories value size > 1 ifTrue:[
        self directory:nil.
        ^ self
    ].
    aColOfFiles size == 1 ifTrue:[
        self startDiskUsageInfoProcess.
    ].
    newDir := (self directoriesForFiles:aColOfFiles) first asCanonicalizedFilename. 
    currentDir := self directory.
    (currentDir notNil and:[newDir = currentDir asCanonicalizedFilename]) 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:changedObject
    "one of my models changed
    "

    changedObject == self currentFileNameHolder ifTrue:[
        super update:something with:aParameter from:changedObject.
        self updateContentsSelection doIfUnLocked:[
            self currentFileNameHolderChanged.
        ].
        ^ self.
    ].             
    changedObject == self selectionInFileList ifTrue:[
        self updateToExternFileHolderLock doIfUnLocked:[
            self updateContentsSelection doLocked:[
                self selectionChanged.
            ]
        ].
        ^ self.
    ].
    changedObject == self currentSortOrder ifTrue:[
        self currentSortOrderChanged.
        ^ self.
    ].
    changedObject == self filterBlockHolder ifTrue:[
        self filterBlockHolderChanged.
        ^ self.
    ].             
    changedObject == self sortBlockHolder ifTrue:[
        self wakeUpForSortBlockChanged.
        ^ self.
    ].             
    changedObject == self viewDirsInContentsBrowser ifTrue:[
        self wakeUpForFilterChanged.
        ^ self
    ].
    changedObject == self showDiskUsageHolder ifTrue:[
        self notify:''.
        changedObject value ifTrue:[
            self startDiskUsageInfoProcess.
        ] ifFalse:[
            self flushRememberedDiskUsageInfo
        ].
        ^ self
    ].

    super update:something with:aParameter from:changedObject
!

viewPreviewChanged

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

viewedColumnsChanged

    |columns buffer currentSortOrder currentSortOrderColumn currentSortOrderReverse selection holder|

    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 readSelector.
            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 == #baseName) or:[id == #baseNameWithOutSuffix]) ifTrue:[
                    self viewType value ifTrue:[
                        col readSelector:#baseNameWithOutSuffix
                    ] ifFalse:[
                        col readSelector:#baseName
                    ].
                ] ifFalse:[
                    holder := self valueHolderForColumn:id.
                    holder value ifFalse:[
                        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 withWaitCursorDo:[
            self fileFileIn.
        ].
        ^ true.
    ].
    key == #Paste ifTrue:[
        self pasteFiles.
        ^ true.
    ].
"/ strange feeling, because behavior is different depending on
"/ where the mouse pointer is (treeView vs. contentsView)
"/
"/    (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 enterAction.
        ^ true.
    ].
    key == #Replace ifTrue:[
        self renameSelection.
        ^ 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'!

doShowFileContents

    | selectedFileItems|

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

enterAction

    | selectedItems|

    selectedItems := self selectedItems.
    (selectedItems size == 1) ifTrue:[ 
        self enterActionFor:(selectedItems 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 selectedItemsWithoutDotDotDirectory select:[:item | item isDirectory not].
    ^ sel
!

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

selectedFiles

    ^ self selectedItemsWithoutDotDotDirectory collect:[:item| item fileName].
!

selectedItems
    | selection |

    selection := self selectionInFileList value copy.
    selection isNil ifTrue:[ ^ #()].
    ^ selection.
!

selectedItemsWithoutDotDotDirectory
    | 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.
!

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 sortFileListsBy:(self sortBlockProperty value) withReverse:(self currentSortOrder value).
    self matchBlock:self filterBlockHolder value.
    self currentFileNameHolderChangedForCommon.
    self currentFileNameHolderChanged.
    self viewedColumnsChanged.
    self startUpdateTask.
    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:anItem

    | type|

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

getIconFor:anItem

    | key icon|

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

getIconForMimeType:mime for:anItem

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

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

getPreviewFor:anItem

    | column icon|
    
    column := browser columnAt:previewIndex.
    column isNil ifTrue:[
        previewIndex := 0.
    ] ifFalse:[
        (anItem hasMimeType and:[anItem mimeType isImage]) ifTrue:[
            Error handle:[:ex|
                icon := nil.
            ] do:[ |scale extent w h |
                icon := Image fromFile:(anItem 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].
        anItem preview:(icon ? '').
    ].
    ^ icon
!

updateChangesFor:anItem

    anItem 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 copy do:[: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:anItemsList

    |showDir filterBlock newItemList noOfAddedFiles noOfFiles|

    newItemList := OrderedCollection new.
    noOfFiles := 0.
    noOfAddedFiles := 0.
    showDir := self viewDirsInContentsBrowser value.
    filterBlock := self matchBlock.
    anItemsList size ~~ 0 ifTrue:[ 
        anItemsList do:[:eachItem | |isDir|
            isDir := eachItem isDirectory.
            isDir not ifTrue:[noOfFiles := noOfFiles + 1].
            ((isDir and:[showDir]) or:[isDir not and:[filterBlock value:eachItem baseName]]) ifTrue:[
                isDir not ifTrue:[noOfAddedFiles := noOfAddedFiles + 1].
                newItemList add:eachItem 
            ].
        ].
    ].
    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:[:eachItem|
            list add:(DirectoryContentsBrowser itemClass forInfoItem:eachItem).
        ].
    ].
    ^ 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 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 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.
!

flushRememberedDiskUsageInfo
    diskUsageInfo := nil.
!

flushRememberedDiskUsageInfoFor:aDirectory
    |dirPath keysToRemove|

    diskUsageInfo isNil ifTrue:[ ^ self ].

    dirPath := aDirectory pathName.
    keysToRemove := diskUsageInfo keys 
                        select:[:key |
                            key startsWith:dirPath
                        ].
    diskUsageInfo removeAllKeys:keysToRemove.
!

isDiskUsageInfoStillValid:infoForDirectory for:aDirectoryPath
    |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
    ].
"/    (aDirectoryPath asFilename modificationTime > infoTimestamp) 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").
!

startDiskUsageInfoProcess
    |aColOfFiles selectedFile|

    aColOfFiles := self currentFileNameHolder value.
    (aColOfFiles isEmpty or:[self currentFilesAreInSameDirectory not]) ifTrue:[
        ^ 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.
        ].
    ].
!

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 for:dirPath) 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::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
!

linkTargetIsDirectory
    self isSymbolicLink ifFalse:[^ false].

    ^ fileName info isDirectory
!

mimeType
    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'!

= anItem

"/    Transcript showCR:(anItem species == self species).
    anItem species == self species ifFalse:[  ^ false].
    anItem isNil ifTrue:[ ^ false].
    ^ (self fileName = anItem 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
!

inodeNumber
    "returns the inode number
    "
    self getFileInfo.
    fileInfo isNil ifTrue:[^ nil].
    ^ fileInfo id
!

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, ']' 
!

sizeInBytesString
    |locSize|

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

    ^ locSize printString "/ leftPaddedTo:8.
!

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
    "
    |format modTime date|

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

"/    format := '%(year)-%(mon)-%(day) %h:%m:%s'.
    date := modTime asDate.
    date = Date today ifTrue:[
        format := (FileBrowser resources string:'Today') , ', %(yearOrTime)'
    ] ifFalse:[
        false "date = Date yesterday" ifTrue:[
            format := (FileBrowser resources string:'Yesterday') , ', %(yearOrTime)'
        ] ifFalse:[
            Smalltalk languageTerritory == #us ifTrue:[
                format := '%(ShortMonthName), %(dayPadded) %(yearOrTime)'.
            ] ifFalse:[
                format :='%(day)-%(ShortMonthName) %(yearOrTime)'.
            ].
        ].
    ].

    ^ modTime printStringFormat:format.
! !

!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.134 2003-10-21 18:20:17 cg Exp $'
! !