DirectoryContentsBrowser.st
author Claus Gittinger <cg@exept.de>
Wed, 28 Oct 2009 19:54:31 +0100
changeset 9116 469bd38ba4c6
parent 9105 e848e5001118
child 9225 f724089858b3
permissions -rw-r--r--
changed: #startDiskUsageInfoProcessFor:

"
 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 multipleSelect
		doubleClickAction updateCycleSemaphore accessLock'
	classVariableNames:'Debug'
	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
                                                                [exEnd]
"
! !

!DirectoryContentsBrowser class methodsFor:'instance creation'!

openIn:aDirectory
    "open a standalone broser for a directory"

    self openIn:aDirectory withDirectories:false

    "
      self openIn:'.cvsignore' asFilename
    "
!

openIn:aDirectory withDirectories:withDirectoriesBoolean
    "open a broswer (standalone) for a directory"

    |browser|

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

    "
      self openIn:Filename currentDirectory withDirectories:true
    "
! !

!DirectoryContentsBrowser class methodsFor:'classAccess'!

itemClass

    ^ DirectoryContentsItem


"
self itemClass
"
! !

!DirectoryContentsBrowser class methodsFor:'constant'!

updateTaskCyleTime
    "time (in ms) to search for changed directories or changed columns"

    ^ 4000
!

updateTaskPriority
    "priority of the update task"

    ^ 8
! !

!DirectoryContentsBrowser class methodsFor:'image specs'!

detailsMenuIconDown
    <resource: #programImage>

    ^ DataSetLabel sortReverseIndicator
!

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
    <resource: #programImage>

    ^ DataSetLabel sortIndicator
! !

!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'
         labelAlignment: right
         labelButtonType: Button
         labelActionSelector: sortFileListsBy:
         labelActionArgument: 'fileSize'
         columnAlignment: decimal
         usePreferredWidth: true
         minWidth: 45
         model: sizeString
         canSelect: false
         showRowSeparator: false
         showColSeparator: false
       )
      (DataSetColumnSpec
         label: 'KB'
         labelAlignment: right
         labelButtonType: Button
         labelActionSelector: sortFileListsBy:
         labelActionArgument: 'fileSize'
         columnAlignment: right
         usePreferredWidth: true
         minWidth: 50
         model: sizeInKiloBytesString
         canSelect: false
         showRowSeparator: false
         showColSeparator: false
       )
      (DataSetColumnSpec
         label: 'Bytes'
         labelAlignment: right
         labelButtonType: Button
         labelActionSelector: sortFileListsBy:
         labelActionArgument: 'fileSize'
         columnAlignment: right
         usePreferredWidth: true
         minWidth: 60
         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 0 0 588 478)
          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
                )
              )
              doubleClickChannel: doDoubleClick:
              viewClassName: 'DirectoryContentsBrowser::Widget'
              postBuildCallback: postBuildBrowser:
              properties: 
             (PropertyListDictionary
                dropSelector: doDrop:
                dragArgument: contentsbrowser
                startDragSelector: doStartDrag:in:
                displayObjectSelector: getDisplayObjects:
                dropObjectSelector: getDropObjects:
                overSelector: dropOver:
                dropArgument: browser
                canDropSelector: canDrop:
                leaveSelector: dropLeave:
                enterSelector: dropEnter:
              )
            )
           )
         
        )
      )
! !

!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
            enabled: hasSelection
            label: 'Open'
            itemValue: doShowFileContents
            translateLabel: true
          )
         (MenuItem
            enabled: hasSelection
            label: 'FileIn'
            itemValue: fileFileIn
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'New'
            translateLabel: true
            submenuChannel: newMenu
            keepLinkedMenu: true
          )
         (MenuItem
            label: '-'
          )
         (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: 'Erase'
            itemValue: doErase
            nameKey: erase
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: hasSelection
            label: 'Rename...'
            itemValue: renameSelection
            translateLabel: true
            shortcutKey: Rename
          )
         (MenuItem
            enabled: hasSelection
            label: 'Move To...'
            itemValue: moveSelectionTo
            translateLabel: true
          )
         (MenuItem
            enabled: hasSelection
            label: 'Copy To...'
            itemValue: copySelectionTo
            translateLabel: true
          )
         (MenuItem
            enabled: hasSelection
            label: 'Properties...'
            itemValue: doShowProperties
            translateLabel: true
          )
         (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: 'FileIn to 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
            keepLinkedMenu: true
          )
         (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: updateCurrentDirectoryWithReread
            translateLabel: true
          )
         )
        nil
        nil
      )

    "Modified: / 07-02-2007 / 18:44:44 / cg"
!

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 decodeFromLiteralArray:self viewBrowserMenuSpec

    "Modified: / 27-03-2007 / 08:46:24 / cg"
!

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
            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 (KByte)'
            translateLabel: true
            hideMenuOnActivated: false
            indication: viewSizeInKiloBytes
          )
         (MenuItem
            enabled: viewDetails
            label: 'Size (Byte)'
            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: updateCurrentDirectoryWithReread
            translateLabel: true
            isVisible: false
          )
         )
        nil
        nil
      )
! !

!DirectoryContentsBrowser methodsFor:'accessing'!

allItems
    ^ allItems ? #()
!

allItems:something
    allItems := something.
!

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

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 decodeFromLiteralArray: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.
                ]
            ]
        ]
    ].

    "Modified: / 27-03-2007 / 08:46:33 / cg"
!

directory
    "returns the current directory or nil
    "
    ^ directory

!

directory:aDirectory
    "change the current directory and read the items
    "

    directory notNil ifTrue:[
        AbstractFileBrowser directoryHistory setPosition:(browser firstLineShown) for:(directory pathName).
    ].
    directory := aDirectory.
    self wakeUpForDirectoryChanged.
!

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

    "Modified: / 01-10-2007 / 19:22:31 / cg"
!

directoryChangeFlag:something
    directoryChangeFlag := something.

    "Modified: / 01-10-2007 / 19:22:35 / cg"
!

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

doubleClickAction

    ^ doubleClickAction
!

doubleClickAction:aBlock

    " aBlock is a one arg block with selected index as argument "

    doubleClickAction := aBlock
!

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

multipleSelect
    ^ multipleSelect ? false
!

multipleSelect:aBoolean

    multipleSelect := aBoolean.
    browser notNil ifTrue:[
        browser multipleSelectOk:aBoolean.
    ].
!

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) ifFalse:[
                browserList add:aNewItem.
                browserList := self sortBlockHolder value sortItemList:browserList.
                self browserItemList add:aNewItem beforeIndex:(browserList indexOf:aNewItem).
            ]
        ].
    ].
    self selectItems:selection.
!

currentSortOrderChanged

"/    self wakeUpForSortBlockChanged.

    "/ have to reread the columns to set the icon for sort direction
    "/ very expensive. Need to change table label 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 wakeUpForDirectoryChanged.
    self updateDiskUsage.
    "/ updateCycleSemaphore wait.
!

doUpdateDirectoryContents
"/    DirectoryContents flushCache.
    self wakeUpForDirectoryContentsChanged.
    self updateDiskUsage.
    "/ updateCycleSemaphore wait.
!

doubleClickedAt:anItemIndex
    |action|

"/    self withWaitCursorDo:[
        (action := self doubleClickAction) notNil ifTrue:[
            action value:anItemIndex.
        ].
        self enterActionFor:(self browserItemList at:anItemIndex). "/ anItemIndex.
"/    ]

    "Modified: / 25-07-2006 / 09:07:55 / cg"
!

enterActionFor:anItem
    | filename info targetItem|

    filename := anItem fileName.

    filename baseName = '..' ifTrue:[
        self setCurrentFileName:(self directory directory).
        ^ self.
    ].
    targetItem := anItem.

    [
       (info := filename linkInfo) isNil ifTrue:[ ^ self ].
        info isSymbolicLink
    ] whileTrue:[
        targetItem := nil.
        filename := info path.
        filename isNil ifTrue:[^ self].
        filename := filename asFilename.
    ].

    (filename isDirectory) ifTrue:[
        self setCurrentFileName:filename.
        ^ self.
    ].

    targetItem isNil ifTrue:[
        targetItem := anItem class fileName:filename.
    ].

"/    self withWaitCursorDo:[
        self openApplByFileItem:targetItem.
"/    ]

    "Modified: / 25-07-2006 / 09:08:00 / cg"
!

noOfAddedFiles:noOfAddedFiles noOfFiles:noOfFiles

    |info filterBox clr|

    info := (noOfAddedFiles == 1 ifTrue:['%1 file'] ifFalse:['%1 files']).
    noOfAddedFiles = noOfFiles ifFalse:[
        info := info,' (%2 total)'.
    ].
    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.
        [ 
            View drawingOnClosedDrawableSignal catch:[ 
                filterBox flash 
            ] 
        ] fork.                 
    ] ifFalse:[
        clr := self filterBackgroundColor value.
    ].
    filterBox backgroundColor:clr.
!

setBrowserItemList:aFilteredItems

    |showDir directoryUpItem locItems browserList|

    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:aFilteredItems.
    ] ifFalse:[
        locItems := aFilteredItems.
    ].
    self updateToExternFileHolderLock doLocked:[
        browserList := self browserItemList.
        "/ bugfix - first remove all entries in list
        browserList removeAll.
        browserList contents:locItems.
    ].
    self selectCurrentFiles.
!

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

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

viewSizeInKiloBytesString
    ^ self viewSizeInKiloBytes
!

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:[ true asValue ].

    "Modified: / 04-09-2006 / 09:46:43 / cg"
!

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:[ true asValue ].
!

viewPermissions
    " aspect for show permission information "

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

    "Modified: / 04-09-2006 / 09:46:12 / cg"
!

viewPreview

    " aspect for show image previev "

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

viewSize
    " aspect for show-size information "

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

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

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

viewSizeInKiloBytes
    " aspect for show size-in-kilobytes information "

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

viewTime

    " aspect for show time information "

    ^ self colVisibilityAspectFor:#viewTime ifAbsent:[ true 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 currentSelectedObjects.
    aColOfFiles isEmpty ifTrue:[
        "/ selection is gone keep the current directory list
        ^ self
    ].
    (self currentFilesAreInSameDirectory) ifFalse:[
        self directory:nil.
        ^ self
    ].
    self currentSelectedDirectories 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.

    "Modified: / 04-12-2006 / 13:15:32 / cg"
!

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 currentSelectedObjects ~= newCurrentFilename ifTrue:[
         self currentFileNameHolder value:newCurrentFilename withoutNotifying:self
    ].

    "Modified: / 04-12-2006 / 13:15:37 / cg"
!

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 sortBlockHolder value directoriesBeforeFiles:(self sortDirectoriesBeforeFiles value and:[self viewDirsInContentsBrowser value]).
        self wakeUpForFilterChanged.
        ^ self
    ].
    changedObject == self sortDirectoriesBeforeFiles ifTrue:[
        self sortBlockHolder value directoriesBeforeFiles:(self sortDirectoriesBeforeFiles value and:[self viewDirsInContentsBrowser value]).
        ^ self
    ].
    changedObject == self showDiskUsageHolder ifTrue:[
        self notify:''.
        changedObject value ifTrue:[
            self startDiskUsageInfoProcess.
        ] ifFalse:[
            self flushRememberedDiskUsageInfo
        ].
        ^ self
    ].

    super update:something with:aParameter from:changedObject
!

updateListAfterDelete:collectionOfFiles
    |indices firstIndex lastIndex nextIndex nextItem|

    collectionOfFiles size > 100 ifTrue:[
        super updateListAfterDelete:collectionOfFiles.
        ^ self
    ].

    indices := collectionOfFiles collect:[:eachFile | browserItemList findFirst:[:item | item fileName = eachFile]].
    lastIndex := indices max.
    firstIndex := indices min.
    nextIndex := lastIndex+1.
    nextIndex > browserItemList size ifTrue:[ nextIndex := firstIndex-1].
    nextItem := browserItemList at:nextIndex ifAbsent:nil.

    accessLock critical:[
        indices asSortedCollection 
            reverseDo:[:eachIndexToRemove |
                eachIndexToRemove ~~ 0 ifTrue:[
                        browserItemList removeIndex:eachIndexToRemove
                    ]
                ].
    ].

    nextItem notNil ifTrue:[
        self
            enqueueMessage:#value 
            for:[ self selectionInFileList value:(OrderedCollection with:nextItem).
                  self selectItems:(OrderedCollection with:nextItem)  ]
            arguments:#().
"/        self selectionInFileList value:(OrderedCollection with:nextItem).
"/        self selectItems:(OrderedCollection with:nextItem).
    ].

    "Modified: / 18-12-2006 / 15:39:42 / cg"
!

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 decodeFromLiteralArray: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 label icon |

            id := col readSelector.
            id notNil ifTrue:[
                (col labelActionArgument notNil and:[col labelActionArgument asSymbol == currentSortOrderColumn]) ifTrue:[
                    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.

    "Modified: / 27-03-2007 / 08:46:28 / cg"
! !

!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)
                 )
! !

!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 withActivityIndicationDo:[
            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 == #Rename ifTrue:[
        self renameSelection.
        ^ true.
    ].

    ^ false

    "Modified: / 25-07-2006 / 09:08:16 / cg"
! !

!DirectoryContentsBrowser methodsFor:'initialization'!

initialize
    changeSema := Semaphore new. 
    updateCycleSemaphore := Semaphore new.
    accessLock := RecursionLock new.
    super initialize.

    "Modified: / 12-12-2006 / 17:10:05 / cg"
! !

!DirectoryContentsBrowser methodsFor:'menu accessing'!

viewBrowserMenu

    <resource: #programMenu >

    |menu|

    viewBrowserMenu isNil ifTrue:[
        viewBrowserMenu :=  self class viewBrowserMenu.
    ].
    viewBrowserMenu isNil ifTrue:[ ^ 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 currentSelectedObjects select:[:file| file isDirectory not]) notEmpty) ifTrue:[
        self selectFiles:currentSelection.
    ].

    "Modified: / 04-12-2006 / 13:15:34 / cg"
!

selectFiles:aColOfFilenames
    | curSel newSel|

    curSel := self selectedFiles.
    aColOfFilenames size = curSel size ifTrue:[
        "/ anything changed ?
        (curSel contains:[:file| (aColOfFilenames includes:file) not]) ifFalse:[^ 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 isSequenceable ifFalse:[
        selection := OrderedCollection with:selection
    ].
    ^ selection.
!

selectedItemsWithoutDotDotDirectory
    | selection |

    selection := self selectionInFileList value.
    selection isNil ifTrue:[ ^ #()].
    selection isSequenceable ifFalse:[
        selection := OrderedCollection with:selection.
    ].
    selection := selection copyAsOrderedCollection.
    (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 sortDirectoriesBeforeFiles 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).
!

postOpenFromMaster:fromMaster

    self sortFileListsBy:(self sortBlockProperty value) withReverse:(self currentSortOrder value).
    self matchBlock:(self filterBlockHolder value).
    fromMaster ifFalse:[
        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:aConditionBlock
    |checkRowAndReturnItemIfConditionIsTrue 
     firstRowShown lastRowShown rLow rHigh numItems|

    checkRowAndReturnItemIfConditionIsTrue := 
        [:row | |item|
            item := browserItemList at:row ifAbsent:nil.
            item notNil ifTrue:[
                (aConditionBlock value:item) ifTrue:[
                    ^ item.
                ]
            ].
        ].

    firstRowShown := browser indexOfFirstRowShown.
    lastRowShown := browser indexOfLastRowShown.

    firstRowShown to:lastRowShown do:checkRowAndReturnItemIfConditionIsTrue.

    numItems := browserItemList size.

    rLow := firstRowShown - 1.
    rHigh := lastRowShown + 1.
    [(rLow >= 1) or:[ rHigh <= numItems]] whileTrue:[
        rLow >= 1 ifTrue:[
            checkRowAndReturnItemIfConditionIsTrue value:rLow.
            rLow := rLow - 1
        ].
        rHigh <= numItems ifTrue:[
            checkRowAndReturnItemIfConditionIsTrue value:rHigh.
            rHigh := rHigh + 1
        ].
    ].
    ^ nil.

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

findNextVisibleItemFor:aConditionBlock
    |firstRow row item|

    firstRow := browser indexOfFirstRowShown.
    firstRow <= 0 ifTrue:[firstRow := 1].

    row := firstRow.
    [true] whileTrue:[
        ((row <= browserItemList size) and:[ (browser isRowVisible:row) ]) ifFalse:[
            ^ nil.
        ].
        item := browserItemList at:row ifAbsent:[^ nil "possible race condition - item removed from browserItemList"].
        (aConditionBlock value:item) ifTrue:[
            ^ item.
        ].
        row := row + 1.
    ]

    "Modified: / 18-07-2006 / 08:56:47 / cg"
!

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 images|

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

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
!

updateColumnsCycle
    "update items in the following 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
     Return true, if more is to be done.
    "

    |desc mime icon type preview|

    browserItemList notEmpty ifTrue:[
        "/ update item if item's properties have changed
"/        desc := self findNextVisibleItemFor:[:n| n accessFirstTime ].
"/        desc notNil ifTrue:[
"/            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.
            icon notNil ifTrue:[
                desc icon:icon.
                (icon notNil and:[iconIndex ~~ 0]) ifTrue:[
                    browser invalidateVisibleRow:desc colAt:iconIndex
                ].
                self debugMessage:'icon for visible items ', 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.
            icon notNil ifTrue:[
                desc icon:icon.
                browser invalidateVisibleRow:desc colAt:iconIndex.
                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.
            browser invalidateVisibleRow:desc colAt:iconIndex.
            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  desc|

    directory isNil ifTrue:[^ self].

    directory exists ifFalse:[
        self directoryChangeFlag:true.
        ^ self
    ].
    directory isRootDirectory ifTrue:[
        ^ self
    ].

    oldModificationTime := modificationTime.
    modificationTime := directory modificationTime.

    oldModificationTime ~= modificationTime ifTrue:[
        self wakeUpForDirectoryContentsChanged.
        ^ self
    ].

    desc := self findNextItemFor:[:n| n hasBeenModified ].

    desc notNil ifTrue:[
        desc resetItem.
        browser invalidateVisibleRow:desc.
        self debugMessage:'item is modified ', desc baseName.
        ^ self.
    ].
! !

!DirectoryContentsBrowser methodsFor:'update task'!

createItemList
    "reread the whole list of items"

    |rawFilteredItems|

    directory notNil ifTrue:[
        self allItems:self readDirectoryItems.
        rawFilteredItems := self evaluateFilter:(self allItems).
        filteredItems := self sortBlockHolder value sortItemList:rawFilteredItems.

"/        Transcript showCR:'getAllTime:', getAllTime asString.
"/        Transcript showCR:'filterTime:', filterTime asString.
"/        Transcript showCR:'sortTime:', sortTime asString.
"/        Transcript showCR:'time after sort', Timestamp now asString.
    ] ifFalse:[
        self allItems:OrderedCollection new.
        filteredItems := OrderedCollection new.
    ].
    self
        enqueueMessage:#setBrowserItemList: 
        for:self
        arguments:(Array with:filteredItems).
!

debugMessage:aString

    Debug == true ifTrue:[
        Transcript showCR:aString
    ].
!

directoryChanged
    "reread the whole list of items"

    |pos|

    self setBrowserItemList:#().
    self createItemList.

    directory notNil ifTrue:[
        pos := AbstractFileBrowser directoryHistory getPositionFor:(directory pathName).
"/ self halt.
        pos notNil ifTrue:[
            browser scrollToLine:pos.
        ]
    ].
!

directoryContentsChanged
    |oldItems addItems remItems newItems newAllItems theAllItems|

    directory isNil ifTrue:[^ self].
    newAllItems := self readDirectoryItems.
    
    newAllItems size > 150 ifTrue:[ 
        self createItemList.
        ^ self
    ].
    newItems := self evaluateFilter:newAllItems.
    oldItems := filteredItems copy.
    addItems := OrderedCollection new.
    remItems := OrderedCollection new.

    oldItems isEmptyOrNil ifTrue:[ 
        remItems := oldItems. 
    ] ifFalse:[
        "/ MERGE WITH CURRENT CONTENTS
        oldItems copy do:[:anOldItem|
            "/ remove no longer valid files
            (newItems includes:anOldItem) ifFalse:[
                remItems add:anOldItem.
                oldItems removeIdentical:anOldItem
            ]
        ].
    ].
    "/ add new files
    newItems do:[:aNewItem|
        (oldItems includes:aNewItem) ifFalse:[
            addItems add:aNewItem.
        ]
    ].
    theAllItems := self allItems.
    remItems notEmpty ifTrue:[
        theAllItems removeAll:remItems        
    ].
    addItems do:[: aNewItem |
        (theAllItems includes:aNewItem) ifFalse:[
            theAllItems add:aNewItem.
        ]
    ].

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

    "Modified: / 12-12-2006 / 17:29:14 / cg"
!

evaluateFilter:anItemsList
    "return filtered items from anItemsList"

    |showDir showHidden filterBlock newItemList noOfAddedFiles noOfFiles showFiles|

    noOfFiles := 0.
    noOfAddedFiles := 0.

    showHidden := self showHiddenFiles value.
    showDir := self viewDirsInContentsBrowser value.
    showFiles := self viewFilesInContentsBrowser value.
    
    filterBlock := self matchBlock.
    newItemList := anItemsList 
                        select:[:eachItem |
                            |isDir showIt|

                            isDir := eachItem isDirectory.
                            isDir ifFalse:[noOfFiles := noOfFiles + 1].

                            showIt := isDir 
                                        ifTrue:[ showHidden or:[ eachItem fileName isHidden not ]]
                                        ifFalse:[ showFiles and:[filterBlock 
                                                                    value:eachItem fileName 
                                                                    value:eachItem baseName]].

                            showIt ifTrue:[
                                isDir ifFalse:[noOfAddedFiles := noOfAddedFiles + 1].
                            ].
                            showIt
                        ].

    self noOfAddedFiles:noOfAddedFiles noOfFiles:noOfFiles.
    ^ newItemList
!

filterChanged
    |newFilteredItems|

    newFilteredItems := self evaluateFilter:self allItems copy.
    newFilteredItems ~= filteredItems ifTrue:[
        filteredItems := newFilteredItems.
        filteredItems := self sortBlockHolder value sortItemList:filteredItems.
        self
            enqueueMessage:#value 
            for:[ self setBrowserItemList:filteredItems ]
            arguments:#().
    ].
!

readDirectoryItems
    |list contents itemClass|

    list     := OrderedCollection new.
    contents := DirectoryContents directoryNamed:directory.

    contents notEmptyOrNil ifTrue:[
        itemClass := DirectoryContentsBrowser itemClass.
        modificationTime := contents modificationTime.

        contents itemsDo:[:eachItem|
            list add:(itemClass forInfoItem:eachItem).
        ].
    ].
    ^ list
!

sortBlockChanged

    filteredItems isNil ifTrue:[ ^ self].
    filteredItems := self sortBlockHolder value sortItemList:filteredItems.
    self
        enqueueMessage:#value 
        for:[ self setBrowserItemList:filteredItems ]
        arguments:#().
!

startUpdateTask
    updateTask isNil ifTrue:[
        updateTask := [ 
                        [self isOpen] 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.
        updateTask := nil.
    ].
!

updateStep
    |timedOut sensor|

    "/ Processor yield.

    sensor := self windowGroup sensor.
    (sensor anyButtonPressed or:[sensor shiftDown]) ifTrue:[ 
        "/ no update while scrolling
        timedOut := ( changeSema waitWithTimeoutMs:(self class updateTaskCyleTime) ) isNil.
        timedOut ifFalse:[changeSema signal].
        ^ self 
    ].

    AbortOperationRequest handle:[
        "if directory changed all running update flags must be reset"

        self debugMessage:'got an abort signal'.
        self directoryContentsChangeFlag:false.
        self filterChangeFlag:false.
        self sortBlockChangeFlag:false.
    ] do:[
        self debugMessage:'sema wait'.
        timedOut := (changeSema waitWithTimeoutMs:(self class updateTaskCyleTime)) isNil.
        self debugMessage:'timedOut:', timedOut asString.

"/        (self filterChangeFlag 
"/        or:[self sortBlockChangeFlag 
"/        or:[self directoryChangeFlag
"/        or:[self directoryContentsChangeFlag]]]) ifTrue:[
            accessLock critical:[
                self updateWithTimeout:timedOut.
            ].
"/        ].
    ].

    "Modified: / 02-11-2007 / 13:08:37 / cg"
!

updateWithTimeout: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'.
            changeSema signal
        ] ifFalse:[
            self debugMessage:'next time no column update'.
        ].
    ] ensure:[
        self isBusy:false.
        updateCycleSemaphore signalIf
    ].
"/        Transcript showCR:'STOP'.
! !

!DirectoryContentsBrowser methodsFor:'update task trigger'!

wakeUp
    changeSema signal.
!

wakeUpForDirectoryChanged

    self isBusy ifTrue:[
        self isBusy:false.
        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 sum kb|

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

    sum := 0.
    aDirectory recursiveDirectoryContentsDo:[:fnString |
        |fn|

        fn := aDirectory construct:fnString.
        fn isDirectory ifFalse:[
            fn exists ifTrue:[     "/ cares for missing links in WinNT
                sum := sum + fn fileSize
            ].
        ].
    ].
    ^ sum / 1024

    "Modified: / 04-07-2006 / 11:27:38 / cg"
!

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

    (Timestamp 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 info|

    aColOfFiles := self currentSelectedObjects.

    (aColOfFiles isEmpty or:[self currentFilesAreInSameDirectory not]) ifTrue:[
        ^ self
    ].
    aColOfFiles size == 1 ifTrue:[
        selectedFile := aColOfFiles first.
        info := selectedFile linkInfo.
        (info notNil and:[info isSymbolicLink]) ifTrue:[
"/            self notify:'Symbolic link to: ' , (info path ? '') allBold
        ] ifFalse:[
            self startDiskUsageInfoProcessFor:selectedFile.
        ].
    ].

    "Modified: / 04-12-2006 / 13:15:40 / cg"
!

startDiskUsageInfoProcessFor:selectedFile
    |directory dirPath infoForDirectory infoValue|

    self showDiskUsageHolder value ifFalse:[^ self].

    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 notNil ifTrue:[
                    diskUsageInfo at:dirPath put:(Timestamp now -> kiloBytes).
                    self showDiskUsageInfoFor:directory as:kiloBytes.
                ].
            ]
        ] fork.
! !

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

fileName:aFilename 
    | instance fileItem|

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

    "
     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
    fileInfo := something.
!

fileName
    ^ fileName
!

fileName:aFilename
    "set fileName"

    fileName := aFilename asFilename.
    self assert:(fileName notNil).
!

fileType
    "returns the type of the file"

    fileType isNil ifTrue:[
        fileType := ''.
    ].
    ^ fileType 
!

fileType:aType
    fileType := aType.
!

icon
    "returns the icon assigned to the item"

    ^ icon
!

icon:anIcon

    icon := anIcon.
!

iconKey
    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
    ]
!

resetItem
    " 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 has changed"

    |info|

    self isRemoteDirectory ifTrue:[^ false].
    "/ root directories are not verified
    fileName isRootDirectory ifTrue:[ ^ false ].


    fileInfo isNil ifTrue:[
        "first time of invokation "
        info := self getFileInfo.
        info isNil ifTrue:[ ^ false].
        ^ true
    ].
    fileInfo isSymbolicLink ifTrue:[
        "/ symbolic links are not verified
        ^ false
    ].

    self modificationTime  ~= fileName modificationTime ifTrue:[
        ^ true
    ].
    ^ false
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'comparing'!

= 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
!

sizeInBytesString
    |locSize|

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

    ^ locSize printString "/ leftPaddedTo:8.
!

sizeInKiloBytesString
    |locSize|

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

    (locSize \\ 1024) == 0 ifTrue:[
        ^ (locSize // 1024) printString , '  '
    ].
    ^ ((locSize / 1024) asFixedPoint:1) printString
!

sizeString
    "returns the size of the file"

    | unitString locSize|

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

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

timeAndDate
    "returns the time- and date string"

    |resources format modTime date|

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

    resources := FileBrowser resources.

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

    ^ modTime printStringFormat:format.
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'printing'!

printOn:aStream
    super printOn:aStream.
    aStream 
        nextPut:$[;
        nextPutAll:fileName asString;
        nextPut:$]. 
! !

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'private'!

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

!DirectoryContentsBrowser::DirectoryContentsItem methodsFor:'queries'!

beRemoteDirectory
    fileType := #remoteDirectory.
!

exists
    ^ 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.228 2009-10-28 18:54:31 cg Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libtool/DirectoryContentsBrowser.st,v 1.228 2009-10-28 18:54:31 cg Exp $'
! !