HierarchicalFileList.st
author Claus Gittinger <cg@exept.de>
Sun, 23 May 1999 14:56:33 +0200
changeset 1390 62dc950b9140
child 1399 da1eed642569
permissions -rw-r--r--
initial checkin

HierarchicalList subclass:#HierarchicalFileList
	instanceVariableNames:'icons matchBlock'
	classVariableNames:''
	poolDictionaries:''
	category:'AAA-Model'
!

HierarchicalItem subclass:#File
	instanceVariableNames:'fileName baseName icon'
	classVariableNames:''
	poolDictionaries:''
	privateIn:HierarchicalFileList
!

HierarchicalFileList::File subclass:#Directory
	instanceVariableNames:'modificationTime'
	classVariableNames:''
	poolDictionaries:''
	privateIn:HierarchicalFileList::File
!


!HierarchicalFileList class methodsFor:'examples'!

test
    |top sel list item|

    list := HierarchicalFileList new.
    list directory:(Filename homeDirectory).
    list showRoot:false.
    list matchBlock:[:fn :isDir| |suf rslt|
                         (rslt := isDir) ifFalse:[
                             suf := fn suffix.

                             suf size ~~ 0 ifTrue:[
                                 rslt := (    suf = 'c'
                                          or:[suf = 'h'
                                          or:[suf = 'hi']]
                                         )
                             ]
                         ].
                         rslt
                     ].

    top := StandardSystemView new; extent:300@300.
    sel := ScrollableView for:HierarchicalListView miniScroller:true
                       origin:0.0@0.0 corner:1.0@1.0 in:top.

    sel list:list.
    list root expand.

    sel doubleClickAction:[:i| (list at:i) toggleExpand ].
    sel   indicatorAction:[:i| (list at:i) toggleExpand ].

    top open.


! !

!HierarchicalFileList class methodsFor:'resources'!

icons
    "returns set of icons
    "
    |icons resources fileKey resource baseName pathName|

    resources := FileBrowser classResources.
    icons     := Dictionary new.

    #(
        (#directory       'ICON_DIRECTORY'        'tiny_yellow_dir.xpm'       )
        (#directoryLocked 'ICON_DIRECTORY_LOCKED' 'tiny_yellow_dir_locked.xpm')
        (#directoryLink   'ICON_DIRECTORY_LINK'   'tiny_yellow_dir_link.xpm'  )
        (#file            'ICON_FILE'             'tiny_file_plain.xpm'       )
        (#fileLink        'ICON_FILE_LINK'        'tiny_file_link.xpm'        )
        (#fileLocked      'ICON_FILE_LOCKED'      'tiny_file_lock.xpm'        )
        (#imageFile       'ICON_IMAGE_FILE'       'tiny_file_pix.xpm'         )
        (#textFile        'ICON_TEXT_FILE'        'tiny_file_text.xpm'        )
        (#executableFile  'ICON_EXECUTABLEFILE'   'tiny_file_exec.xpm'        )

     ) do:[:entry|
        fileKey  := entry at:1.
        resource := entry at:2.
        baseName := entry at:3.

        (pathName := resources at:(entry at:2) default:nil) isNil ifTrue:[
            pathName := 'bitmaps/xpmBitmaps/document_images/' , baseName
        ].
        icons at:fileKey put:(Image fromFile:pathName).
    ].
    ^ icons




! !

!HierarchicalFileList methodsFor:'accessing'!

directory
    "returns the root directory or nil
    "
    ^ root notNil ifTrue:[root fileName] ifFalse:[nil]

!

directory:aDirectory
    "set the root directory or nil
    "
    |directory|

    monitoringTaskDelay := 1.

    (aDirectory notNil and:[(directory := aDirectory asFilename) exists]) ifTrue:[
        directory isDirectory ifFalse:[
            directory := directory directory
        ]
    ] ifFalse:[
        directory := nil
    ].

    directory = self directory ifFalse:[
        directory notNil ifTrue:[
            directory := File fileName:directory isDirectory:true
        ].
        self root:directory
    ].
! !

!HierarchicalFileList methodsFor:'actions'!

matchBlock
    "set the matchBlock - if non-nil, it controls which files are visible.
    "
    ^ matchBlock
        
!

matchBlock:aBlock
    "set the matchBlock - if non-nil, it controls which files are visible.
    "
    matchBlock := aBlock.
        
! !

!HierarchicalFileList methodsFor:'protocol'!

childrenFor:anItem
    "returns all visible children derived from the physical
     directory contents.
    "
    |contents list block|

    list := #().

    anItem isDirectory ifFalse:[
        ^ list
    ].

    Cursor read showWhile:[
        contents := DirectoryContents directoryNamed:(anItem fileName).

        contents notNil ifTrue:[
            list  := OrderedCollection new.
            block := self matchBlockFor:anItem.

            block isNil ifTrue:[
                contents contentsDo:[:fn :isDir|
                    list add:(File fileName:fn isDirectory:isDir)
                ]
            ] ifFalse:[
                contents contentsDo:[:fn :isDir|
                    (block value:fn value:isDir) ifTrue:[
                        list add:(File fileName:fn isDirectory:isDir)
                    ]
                ]
            ]
        ]
    ].
    ^ list




!

hasChildrenFor:anItem
    "returns true if the physical directory contains at least
     one visible item otherwise false.
    "
    |block|

    anItem isDirectory ifFalse:[
        ^ false
    ].

    (block := self matchBlockFor:anItem) isNil ifTrue:[
        block := [:aFilename :isDirectory| true ]
    ].
    ^ DirectoryContents directoryNamed:(anItem fileName) detect:block
!

iconFor:anItem
    "returns the icon for an item
    "
    |fn key|

    fn := anItem fileName.

    fn isDirectory ifTrue:[
        (fn isReadable and:[fn isExecutable]) ifTrue:[
            key := fn isSymbolicLink ifTrue:[#directoryLink]
                                    ifFalse:[#directory]
        ] ifFalse:[
            key := #directoryLocked
        ]
    ] ifFalse:[
        fn isReadable ifTrue:[
            fn isSymbolicLink ifTrue:[
                key := #fileLink
            ] ifFalse:[
                (Image isImageFileSuffix:(fn suffix)) ifTrue:[
                    key := #imageFile
                ] ifFalse:[
                    key := #file
                ]
            ]
        ] ifFalse:[
            key := #fileLocked
        ]
    ].
    icons isNil ifTrue:[
        icons := self class icons
    ].

  ^ icons at:key ifAbsent:nil
!

matchBlockFor:anItem
    "get the matchBlock - if non-nil, it controls which files are
     visible within the physical directory
    "
    ^ matchBlock        
! !

!HierarchicalFileList::File class methodsFor:'instance creation'!

fileName:aFileName isDirectory:isDirectory
    "instance creation
    "
    |item|

    item := isDirectory ifTrue:[Directory new] ifFalse:[HierarchicalFileList::File new].
    item fileName:aFileName.
  ^ item

! !

!HierarchicalFileList::File methodsFor:'accessing'!

baseName
    "returns the baseName
    "
    ^ baseName


!

children
    "always returns an empty list
    "
    ^ #()
!

fileName
    "returns the fileName
    "
    ^ fileName


!

fileName:fname
    "instance creation
    "
    fileName := fname.
    baseName := fname baseName.
!

icon
    "returns the icon key
    "
    |model|

    icon isNil ifTrue:[
        (model := self model) notNil ifTrue:[
            icon := model iconFor:self
        ]
    ].
    ^ icon


!

label
    "returns the printable name, the baseName
    "
    ^ baseName


!

pathName
    "returns the pathName
    "
    ^ fileName pathName
! !

!HierarchicalFileList::File methodsFor:'accessing hierarchy'!

recursiveExpand
    "redefined to expand
    "
    self expand


! !

!HierarchicalFileList::File methodsFor:'invalidate'!

invalidate
    "invalidate the contents
    "
    self invalidateRepairNow:false

!

invalidateRepairNow
    "invalidate the contents; repair now
    "
    self invalidateRepairNow:true

!

invalidateRepairNow:doRepair
    "invalidate the contents; dependent on the boolean
     do repair immediately
    "


! !

!HierarchicalFileList::File methodsFor:'queries'!

hasChildren
    "always returns false
    "
    ^ false
!

isDirectory
    "always returns false
    "
    ^ false

!

string
    "returns the string from the label or nil
    "
    ^ baseName
! !

!HierarchicalFileList::File::Directory methodsFor:'accessing'!

children
    "returns the list of children
    "
    |model list|

    children isNil ifTrue:[
        children := #().     "/ disable reread
        modificationTime := fileName modificationTime.

        (model := self model) notNil ifTrue:[
            list := model childrenFor:self.

            list size ~~ 0 ifTrue:[
                list do:[:aChild| aChild parent:self].
                children := list.
            ]
        ].
    ].
    ^ children
!

icon
    "returns the icon
    "
    (isExpanded and:[children size ~~ 0]) ifTrue:[
        ^ nil
    ].
    ^ super icon
! !

!HierarchicalFileList::File::Directory methodsFor:'queries'!

hasChildren
    "returns true if children exists
    "
    ^ children isNil or:[children notEmpty]
!

isDirectory
    "always returns true
    "
    ^ true


! !

!HierarchicalFileList::File::Directory methodsFor:'validation'!

invalidateRepairNow:doRepair
    "invalidate contents
    "
    modificationTime := nil.

    doRepair ifTrue:[
        self monitoringCycle
    ] ifFalse:[
        (isExpanded or:[children size == 0]) ifFalse:[
            children := nil
        ]
    ].

!

monitoringCycle
    "run monitoring cycle
    "
    |list size name modifyTime isNotEmpty wasNotEmpty model|

    modifyTime := fileName modificationTime.

    (modificationTime notNil and:[modifyTime <= modificationTime]) ifTrue:[
        ^ self
    ].
    model := self model.
    modificationTime := modifyTime.

    isExpanded ifFalse:[

     "/ CHECK WHETHER CHILDREN EXIST( INDICATOR )
     "/ =========================================

        isNotEmpty := model hasChildrenFor:self.

     "/ check whether has changed durring evaluation
        (isExpanded or:[modificationTime ~= modifyTime]) ifFalse:[
            wasNotEmpty := children isNil.
            children    := isNotEmpty ifTrue:[nil] ifFalse:[#()].

            wasNotEmpty ~~ isNotEmpty ifTrue:[
                self changed
            ]
        ].
        ^ self

    ].

 "/ START MERGING( CONTENTS IS VISIBLE )
 "/ ====================================

    list := model childrenFor:self.

    list size == 0 ifTrue:[                         "/ contents becomes empty
        ^ self removeAll                            "/ clear contents
    ].
    (size := children size) == 0 ifTrue:[           "/ old contents was empty 
        ^ self addAll:list.                         "/ take over new contents
    ].

    size to:1 by:-1 do:[:anIndex|                   "/ remove invisible items
        name := (children at:anIndex) baseName.

        (list findFirst:[:i|i baseName = name]) == 0 ifTrue:[
            self removeIndex:anIndex
        ]
    ].

    list keysAndValuesDo:[:anIndex :anItem|         "/ add new visible items
        name := anItem baseName.

        (children findFirst:[:i|i baseName = name]) == 0 ifTrue:[
            self add:anItem beforeIndex:anIndex
        ]
    ].
! !

!HierarchicalFileList class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libwidg2/HierarchicalFileList.st,v 1.1 1999-05-23 12:56:11 cg Exp $'
! !