HierarchicalFileList.st
author penk
Wed, 11 Sep 2002 14:54:54 +0200
changeset 2180 d01612ed451e
parent 2140 7dfe3b338437
child 2223 c34e7fd25e53
permissions -rw-r--r--
get the icons from the FileBrowser now

"
 COPYRIGHT (c) 1999 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:libwidg2' }"

HierarchicalList subclass:#HierarchicalFileList
	instanceVariableNames:'matchBlock showCursor'
	classVariableNames:''
	poolDictionaries:''
	category:'Views-Support'
!

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

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

!HierarchicalFileList class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1999 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.
"

! !

!HierarchicalFileList class methodsFor:'examples'!

examples
"
                                                                    [exBegin]
    |top sel list|

    list := HierarchicalFileList new.
    list directory:(Filename homeDirectory).
    list showRoot:false.
    list matchBlock:[:fn :isDir| true ].

    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.
                                                                    [exEnd]
"
! !

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

"/    must explicitly enabled by user: ca
"/    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.

    root notNil ifTrue:[
        self recursionLock critical:[
            self stopMonitoringTask.
            root matchBlockChanged.
        ].
        self startMonitoringTask.
    ].

! !

!HierarchicalFileList methodsFor:'private monitoring task'!

monitoringCycle
    "the block evaluated by the monitoring task; test whether directory is expanded
     otherwise we have not to evaluate the directory contents.
     think about remote file-systems
    "
    |index item|

    item  := root.
    index := showRoot ifTrue:[1] ifFalse:[0].

    [item notNil] whileTrue:[
        (item isDirectory and:[item getChildren notNil]) ifTrue:[
            "/ children already initialized; thus we can update the contents
            item monitoringCycle
        ].
        Processor yield.
        index := index + 1.
        item  := self at:index ifAbsent:nil.
    ].
! !

!HierarchicalFileList methodsFor:'protocol'!

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

    anItem isDirectory ifFalse:[^ #()].

    contents := DirectoryContents directoryNamed:(anItem fileName).
    contents ifNil:[ ^ #() ].


    list  := SortedCollection sortBlock:[:a :b| |res|
        a isDirectory == b isDirectory ifTrue:[ res := a baseName <= b baseName ]
                                      ifFalse:[ res := a isDirectory ].
        res
    ].
    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 isEmpty ifTrue:[^ #()].
  ^ list asOrderedCollection
!

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
    "

    (anItem isExpanded and:[anItem hasChildren]) ifTrue:[
        ^ FileBrowser iconForKeyMatching:#directoryOpen
    ].
    ^ FileBrowser iconForFile:(anItem fileName)
!

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

validateIcon:anIcon for:anItem
    ^ anIcon
! !

!HierarchicalFileList methodsFor:'searching'!

detectItemFor:aPathOrFilename
    "make a filename visible
    "
    |file node p1 p2 sz|

    aPathOrFilename isNil ifTrue:[^ nil].

    file := aPathOrFilename asFilename.

    file exists ifFalse:[
        ^ nil
    ].
    (node := root) isNil ifTrue:[^ nil].

    p1 := node pathName.
    p2 := file pathName.

    (p2 startsWith:p1) ifFalse:[^ nil].
    sz := 1 + p1 size.

    (p1 last == Filename separator) ifFalse:[
        sz := sz + 1.
    ].

    p2 size > sz ifFalse:[^ nil].

    p2 :=  Filename components:(p2 copyFrom:sz).

    p2 do:[:bn|
        node := node detect:[:el| el baseName = bn] ifNone:nil.
        node isNil ifTrue:[^ nil ].
    ].
    ^ node
! !

!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
    "
    ^ fileName 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|

    model := self model.
    model ifNil:[^ nil].

    icon isNil ifTrue:[
        icon := model iconFor:self
    ].
    ^ model validateIcon:icon for:self
!

label
    "returns the printable name, the baseName
    "
    ^ self 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
    "


!

matchBlockChanged
    "called if the matchBlock changed
    "

! !

!HierarchicalFileList::File methodsFor:'printing'!

printString

    ^ super printString, ' for: ', self fileName asString
! !

!HierarchicalFileList::File methodsFor:'queries'!

hasChildren
    "always returns false
    "
    ^ false
!

isDirectory
    "always returns false
    "
    ^ false

! !

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

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

    children notNil ifTrue:[
        ^ children
    ].

    (model := self model) isNil ifTrue:[
        "/ must reread later
        modificationTime := children := nil.
        ^ nil
    ].

    children := #().
    modificationTime := fileName modificationTime.
    list := model childrenFor:self.

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

icon
    "reset first and returns the new icon
    "
    icon := nil.    
    ^ super icon
! !

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

expand
    "expand children; must redefine to raise a notification
     if children are not yet initialized and after initialization empty.
    "
    |notInit|

    notInit := children isNil.
    super expand.

    notInit ifTrue:[
        (children notNil and:[children isEmpty]) ifTrue:[
            "/ no children exists
            "/ indicator +/- has changed to unexistant
            self changed:#redraw
        ]
    ].
! !

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

!

matchBlockChanged
    "called if the matchBlock changed
    "
    modificationTime := nil.

    isExpanded ifFalse:[
        children := nil.
    ] ifTrue:[
        self monitoringCycle.

        children size ~~ 0 ifTrue:[
            children do:[:aChild| aChild matchBlockChanged ]
        ]
    ].
!

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.18 2002-09-11 12:54:54 penk Exp $'
! !