AbstractFileBrowser.st
author Claus Gittinger <cg@exept.de>
Mon, 14 Feb 2011 18:12:07 +0100
changeset 9766 8991b96a132c
parent 9764 47b9c33465a1
child 9784 afeca3b8a748
permissions -rw-r--r--
initial checkin

"
 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' }"

ApplicationModel subclass:#AbstractFileBrowser
	instanceVariableNames:'aspects'
	classVariableNames:'DirectoryHistory RuntimeAspects DirectoryBookmarks
		LastEnforcedNameSpace CommandHistory DefaultCommandPerSuffix
		CommandHistorySize LastFileDiffFile DefaultFilters RootHolder
		LastFileSelection LastMoveDestination LastScriptBlockString'
	poolDictionaries:''
	category:'Interface-Tools-File'
!

AbstractFileBrowser class instanceVariableNames:'DisabledCursorImage EnabledCursorImage'

"
 The following class instance variables are inherited by this class:

	ApplicationModel - ClassResources
	Model - 
	Object - 
"
!

Object subclass:#Clipboard
	instanceVariableNames:'method files'
	classVariableNames:''
	poolDictionaries:''
	privateIn:AbstractFileBrowser
!

Object subclass:#CodeExecutionLock
	instanceVariableNames:'locked'
	classVariableNames:''
	poolDictionaries:''
	privateIn:AbstractFileBrowser
!

List subclass:#DirectoryHistory
	instanceVariableNames:'forwardList backList lastWasForwardPath lastBackPath lastAddPath
		backForwardList backForwardIndex historySize'
	classVariableNames:'HistorySize'
	poolDictionaries:''
	privateIn:AbstractFileBrowser
!

Object subclass:#DirectoryHistoryItem
	instanceVariableNames:'path position'
	classVariableNames:''
	poolDictionaries:''
	privateIn:AbstractFileBrowser::DirectoryHistory
!

OrderedSet subclass:#FilenameHistory
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	privateIn:AbstractFileBrowser
!

Object subclass:#SaveAspectItem
	instanceVariableNames:'value isHolder'
	classVariableNames:''
	poolDictionaries:''
	privateIn:AbstractFileBrowser
!

!AbstractFileBrowser 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
"
    [Author:]
        Christian Penk

    Notice: this certainly needs a redesign - all those abstract-abstract classes which
    define stuff which is only needed in some parts makes this hard to understand.
    Especially the fact, that AbstractFileBrowser defines things both as abstract superclass
    and as a container is almost incomprehensable...
"
! !

!AbstractFileBrowser class methodsFor:'accessing'!

currentSelection

    LastFileSelection isNil ifTrue:[
        LastFileSelection := #().
    ].
    ^ LastFileSelection
!

currentSelection:aFilenameCol
    aFilenameCol notEmptyOrNil ifTrue:[
        LastFileSelection := aFilenameCol collect:[:file | file asFilename]
    ]
!

directoryHistory

    DirectoryHistory isNil ifTrue:[
        DirectoryHistory := self directoryHistoryClass new.
"/        DirectoryHistory inspect.
    ].
    ^ DirectoryHistory
!

resetClassVars
    DirectoryHistory := nil.
    LastFileSelection := nil.

    "
     AbstractFileBrowser resetClassVars
    "
!

rootHolder

    ^ RootHolder
!

rootHolder:aRoot

    RootHolder := aRoot
! !

!AbstractFileBrowser class methodsFor:'accessing-bookmarks'!

addBookmark:aDirectoryPath
    "add aDirectoryPath to the list of our known bookmarks.
     Do not add duplicates."

    |bookmarks|

    bookmarks := self directoryBookmarks.
    (bookmarks includes:aDirectoryPath) ifFalse:[
        bookmarks add:aDirectoryPath asFilename.
    ].
!

bookmarksFrom:aFileNameOrString
    |bookmarks s fileName|

    fileName := aFileNameOrString asFilename.
    [
        s := fileName readStream.
    ] on:OpenError do:[:ex|
        ^ nil.
    ].

    bookmarks := OrderedCollection new.
    [s atEnd] whileFalse:[
        bookmarks add:(Base64Coder decode:s nextLine) asString.
    ].
    s close.

    ^ bookmarks
!

defaultBookMarksFileDirectory
    ^ Filename homeDirectory
!

defaultBookMarksFilename
    ^ '.fileBrowserBookmarks'
!

directoryBookmarks
    DirectoryBookmarks isNil ifTrue:[                     
        self loadBookmarksFrom:(self defaultBookMarksFileDirectory construct:self defaultBookMarksFilename).

        DirectoryBookmarks isEmptyOrNil ifTrue:[
            DirectoryBookmarks := OrderedCollection new.
            DirectoryBookmarks add:Filename homeDirectory asAbsoluteFilename.
            DirectoryBookmarks add:Filename currentDirectory asAbsoluteFilename.
        ]
    ].
    ^ DirectoryBookmarks

    "
     DirectoryBookmarks := nil.
     self directoryBookmarks.
    "
!

directoryBookmarks: collectionOfFilenames

    DirectoryBookmarks := collectionOfFilenames.
    self saveBookmarksInDefaultBookmarksFile.
!

editBookmarksWithDefault: aFilenameOrNil
    | oldBookmarks newBookmarks |

    oldBookmarks := self directoryBookmarks copy.
    newBookmarks := BookmarksEditDialog openWith: oldBookmarks defaultBookmark: aFilenameOrNil.
    newBookmarks isNil ifTrue:[^ self].
    self directoryBookmarks: newBookmarks.

    "Modified: / 13-01-2011 / 12:52:28 / cg"
!

hasBookmarks
    ^ self directoryBookmarks notEmptyOrNil
!

loadBookmarksFrom:aFileNameOrString
    |bookmarks|

    bookmarks := self bookmarksFrom: aFileNameOrString.
    bookmarks isNil ifTrue:[^ self].

    DirectoryBookmarks := OrderedCollection new.
    bookmarks do:[:eachPath |
        self addBookmark:eachPath asFilename
    ].
!

removeBookmark:aDirectoryPath
    |bookmarks|

    bookmarks := self directoryBookmarks.
    bookmarks isEmptyOrNil ifTrue:[ ^ self].

    bookmarks remove:aDirectoryPath ifAbsent:[].
!

saveBookmarks
    |fn|

    fn := Dialog 
            requestFileNameForSave:(self resources string:'Save Bookmarks')
            default:(self defaultBookMarksFilename)
            fromDirectory:(self defaultBookMarksFileDirectory).
    fn isEmptyOrNil ifTrue:[^ self].

    self saveBookmarksIn:fn

    "Modified: / 27-10-2010 / 11:27:09 / cg"
!

saveBookmarks: bookmarks in:aFileNameOrString
    "save the bokmarks in aFileNameOrString.
     Use Base64 coding"

    | bookmarkStream fileName coder|

    fileName := aFileNameOrString asFilename.
    fileName exists ifTrue:[
        fileName renameTo:(fileName withSuffix:'sav').
    ].
    bookmarkStream := fileName writeStream.

    "save each bookmark in one line"
    coder := (Base64Coder on:bookmarkStream) lineLimit:nil.
    bookmarks do:[:eachPath |
        eachPath asFilename pathName acceptVisitor:coder.
        coder flush.
        bookmarkStream cr.
    ].
    bookmarkStream close.
!

saveBookmarksIn:aFileNameOrString
    "save the bokmarks in aFileNameOrString.
     Use Base64 coding"

    | bookmarks |

    bookmarks := self directoryBookmarks.
    self saveBookmarks: bookmarks in:aFileNameOrString
!

saveBookmarksInDefaultBookmarksFile
    self saveBookmarksIn:(self defaultBookMarksFileDirectory construct:self defaultBookMarksFilename)
! !

!AbstractFileBrowser class methodsFor:'accessing-classes'!

directoryHistoryClass

    ^ AbstractFileBrowser::DirectoryHistory
!

filenameHistoryClass

    ^ FilenameHistory
! !

!AbstractFileBrowser class methodsFor:'defaults'!

commandHistory

    CommandHistory isNil ifTrue:[
        CommandHistory := OrderedCollection new.
    ].
    ^ CommandHistory
!

commandHistorySize
    "max no of entries in the HistoryList "

    CommandHistorySize isNil ifTrue:[
        CommandHistorySize := 150
    ].
    ^ CommandHistorySize
!

defaultFilterList

    DefaultFilters isNil ifTrue:[
        DefaultFilters := #(        '*'
                                    '*.st' 
                                    '*.htm*' 
                                    '*.txt' 
                                    '*.gif' 
                                    '*.xpm' 
                                    '*.jpg' 
                                    '*.[h,c]*' 
                                )
    ].
    ^ DefaultFilters.
!

initialCommandFor:fileName in:aDirectory intoBox:aBox
    "set a useful initial command in an execute box."

    |mime cmd select path suffix baseName pathName|

    path := aDirectory filenameFor:fileName.
    baseName := path baseName.
    suffix := path suffix.

    mime := MIMETypes mimeTypeForSuffix:suffix.
"/    mime notNil ifTrue:[
"/        cmd := self initialCommandForMIME:mime file:path
"/    ].

    "/ XXX should be changed to take stuff from a config file
    "/ XXX or from resources.

    (path type == #regular) ifTrue:[
        path isExecutableProgram ifTrue:[
            aBox initialText:(path pathName , ' <arguments>').
            ^ self
        ].

        select := true.

        "some heuristics - my personal preferences ...
         (actually this should come from a configfile)"

        (baseName endsWith:'akefile') ifTrue:[
            aBox initialText:'make target' selectFrom:6 to:11.
            ^ self
        ].

        cmd := MIMETypes defaultCommandPerMIME at:mime ifAbsent:nil.
        cmd notNil ifTrue:[
            select := false
        ].

        cmd isNil ifTrue:[
            suffix := path suffix.
            (suffix = 'C') ifTrue:[
                cmd := 'g++ -c %1'.
                select := 6.
            ] ifFalse:[
                suffix := suffix asLowercase.

                (suffix = 'taz') ifTrue:[
                    aBox initialText:'zcat %1 | tar tvf -'.
                    select := false.
                ].
                (suffix = 'tar') ifTrue:[
                    cmd := 'tar tvf %1'.
                    select := 7.
                ].
                (suffix = 'zoo') ifTrue:[
                    cmd := 'zoo -list %1'.
                    select := 9.
                ].
                (suffix = 'zip') ifTrue:[
                    cmd := 'unzip -l %1'.
                    select := 8.
                ].
                (suffix = 'jar') ifTrue:[
                    cmd := 'unzip -l %1'.
                    select := 8.
                ].
                (suffix = 'z') ifTrue:[
                    (baseName asLowercase endsWith:'tar.z') ifTrue:[
                        cmd := 'zcat %1 | tar tvf -'.
                        select := false.
                    ] ifFalse:[
                        cmd := 'uncompress %1'
                    ].
                ].
                (suffix = 'gz') ifTrue:[
                    (baseName asLowercase endsWith:'tar.gz') ifTrue:[
                        cmd := ('gunzip < %1 | tar tvf -' ).
                        select := false.
                    ] ifFalse:[
                        cmd := 'gunzip %1'.
                    ].
                ].
                (suffix = 'html') ifTrue:[
                    cmd := 'netscape %1'
                ].
                (suffix = 'htm') ifTrue:[
                    cmd := 'netscape %1'
                ].
                (suffix = 'uue') ifTrue:[
                    cmd := 'uudecode %1'
                ].
                (suffix = 'c') ifTrue:[
                    cmd := 'cc -c %1'.
                    select := 5.
                ].
                (suffix = 'cc') ifTrue:[
                    cmd := 'g++ -c %1'.
                    select := 6.
                ].
                (suffix = 'xbm') ifTrue:[
                    cmd := 'bitmap %1'
                ].
                (suffix = 'ps') ifTrue:[
                    cmd := 'ghostview %1'
                ].
                ((suffix = '1') or:[suffix = 'man']) ifTrue:[
                    cmd := 'nroff -man %1'.
                    select := 10.
                ].
            ].
        ].

        cmd isNil ifTrue:[
            DefaultCommandPerSuffix isNil ifTrue:[
                cmd := '<cmd>'
            ] ifFalse:[
                cmd := DefaultCommandPerSuffix 
                        at:suffix
                        ifAbsent:'<cmd>'.
            ].
            cmd := cmd , ' %1'.
        ].

        pathName := path pathName.
        OperatingSystem isUNIXlike ifTrue:[
            pathName includesSeparator ifTrue:[
                pathName := '"' , pathName , '"'
            ]
        ].

        cmd := cmd bindWith:pathName.
        select == false ifTrue:[
            aBox initialText:cmd
        ] ifFalse:[
            select class == Interval ifTrue:[
                aBox initialText:cmd selectFrom:select start to:select stop
            ] ifFalse:[
                select isInteger ifFalse:[
                    select := (cmd indexOf:Character space ifAbsent:[cmd size + 1]) - 1.
                ].
                aBox initialText:cmd selectFrom:1 to:select
            ].
        ]
    ]

    "Modified: / 24.9.1997 / 16:34:52 / stefan"
    "Modified: / 9.4.1998 / 17:15:57 / cg"
!

listOfRuntimeValuesToRemember
    " list of all aspects that will be remembered after closing a FileBrowserV2"
    
    ^ #( 
"/        #filterModel
        #enableFileHistory
        #fileHistory
        #currentSortOrder
        #sortBlockProperty 
       )
!

resetAspects

    RuntimeAspects := nil.

    "
     self resetAspects
    "
!

runtimeAspects

    RuntimeAspects isNil ifTrue:[
        RuntimeAspects := Dictionary new.
    ].
    ^ RuntimeAspects
!

userPreferencesAspectList
    " list of all aspects that will be saved with save settings
      that aspects will be image consistent if the settings are saved in Launcher
      dont forget to add a access methos in UserPreferences if you add a aspect here
    "

    ^ Dictionary 
        withKeysAndValues:#(
           viewDirsInContentsBrowser   true
           showDirectoryTree           true
           showHiddenFiles             false
           viewDescription             false
           viewDetails                 true
           viewDirectoryDescription    false
           viewFilesInDirectoryTree    false
           viewGroup                   false
           viewOwner                   false
           viewPermissions             false
           viewPreview                 false
           viewSize                    true
           viewSizeInKiloBytes         false
           viewSizeInBytes             false
           viewTime                    true
           viewType                    false
           openMultipleApplicationsForType false
           filenameEntryFieldVisibleHolder   true
           toolBarVisibleHolder        true
           sortDirectoriesBeforeFiles  true
           openAlwaysInTextEditor      false
           sortCaseless                false
      )

    "Modified: / 03-04-2007 / 08:47:45 / cg"
! !

!AbstractFileBrowser class methodsFor:'help specs'!

basicFlyByHelpSpec
    <resource: #help>

    ^ super flyByHelpSpec addPairsFrom:#(

#closeTabButton
'Close this Tab'

#addTerminal
'Shell Terminal'

#make
'Make'

#searchFile
'Search a File'

#directoryUp
'Directory Up'

#directoryBack
'Directory Back'

#directoryForward
'Directory Forward'

#directoryHistory
'Directory'

#copyFile
'Copy the Selected File(s)'

#cutFile
'Cut the Selected File(s)'

#editFile
'Edit the Selected File'

#fileHome
'Goto Home Directory'

#fileDesktop
'Goto Desktop Directory'

#fileGotoDefault
'Goto Default Directory (ST/X Start Directory)'

#fileGotoSmalltalkDirectory
'Goto Smalltalk Directory (ST/X Application)'

#fileGotoDefaultDirectory
'Goto Current Directory'

#pasteFile
'Paste File(s)'

#deleteFile
'Delete the Selected File(s)'

#fileIn
'File In'

#fileHistory
'File History'

#fileGotoBookmark
'Goto Bookmarked Directory'

#hideToolBar
'Hide Toolbar'

#hideFilenameEntryField
'Hide Filename & Filter Fields'

#openChangeBrowser
'Open a ChangeBrowser on File'

#showFileDetails
'Show File Details'

#hideFileDetails
'Hide File Details'

#toggleDetails
'Show/Hide File Details'

#viewDetails
'Show/Hide File Details'

#selectDetails
'Select File Details to be Shown'

#showDifferences
'Show Differences between File and Editor Versions'

#toggleHexDisplay
'Toggle between Hexadecimal and Textual Representation'

#findInBrowser
'Navigate the Browsers File List to the Edited File'

#saveFile
'Save the Editor''s Contents into the File'

#reloadFile
'Reload from the File'

#print
'Send to the Printer'

).

    "
     self flyByHelpSpec
    "

    "Modified: / 03-11-2007 / 12:05:01 / cg"
!

flyByHelpSpec
    <resource: #programHelp>

    |spec hist resources|

    spec := self basicFlyByHelpSpec.

    "/ add help items for the history
    hist := self directoryHistory.
    hist notNil ifTrue:[
        resources := self classResources.
        hist canForward ifTrue:[
            spec at:#directoryForward put:[ resources string:'Forward to: %1' with:hist nextForwardItem ].
        ].
        hist canBackward ifTrue:[
            spec at:#directoryBack put:[ resources string:'Back to: %1' with:hist nextBackwardItem ].
        ].
    ].
    ^ spec.

    "
     self flyByHelpSpec
    "

    "Modified: / 03-11-2007 / 12:05:01 / cg"
!

helpSpec
    "This resource specification was automatically generated
     by the UIHelpTool of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the UIHelpTool may not be able to read the specification."

    "
     UIHelpTool openOnClass:AbstractFileBrowser    
    "

    <resource: #help>

    ^ super helpSpec addPairsFrom:#(

#lockFileEncoding
'Do not guess the encoding from the files contents.'

)
! !

!AbstractFileBrowser class methodsFor:'image specs'!

clearHistoryIcon
    <resource: #programImage>

    ^ Icon deleteIcon
!

copyIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary copyIcon
!

cutIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary cut20x20Icon2 "cut28x28Icon"
!

deleteIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary erase20x20Icon "/ delete28x28Icon
!

directoryUpIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary directoryUpIcon
!

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

    <resource: #image>

    ^Icon
        constantNamed:#'AbstractFileBrowser class disabledCursorIcon'
        ifAbsentPut:[(Depth1Image new) width: 32; height: 32; photometric:(#palette); bitsPerSample:(#[1]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@_ @@@XF@@@H@P@@DOB@@BLLP@AA@"@@RHD @H1AD@BRHI@@$QBP@IBH$@BPQI@@"BLP@D QH@ADBB@@H0Q@@AC
8 @@H@P@@A X@@@G8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@b') ; colorMapFromArray:#[0 0 0 255 255 255]; mask:((ImageMask new) width: 32; height: 32; photometric:(#blackIs0); bitsPerSample:(#[1]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@_ @@@_>@@@O?0@@G?>@@C<O0@A?@>@@_8G @O?A<@C38O@@<_C0@OC8<@C0_O@@>C?0@G _8@A<C>@@O0_@@A?
? @@O?0@@A?8@@@G8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@b') ; yourself); yourself]
!

disabledCursorImage
"/    DisabledCursorImage isNil ifTrue:[
"/        DisabledCursorImage := self disabledCursorIcon. "/ Image fromFile:'xpmBitmaps/cursors/no_entry.xpm'
"/    ].
    ^ DisabledCursorImage
!

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

    <resource: #image>

    ^Icon
        constantNamed:#'AbstractFileBrowser class enabledCursorIcon'
        ifAbsentPut:[(Depth1Image new) width: 32; height: 32; photometric:(#palette); bitsPerSample:(#[1]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@E@@@@AP@@@@T@@@@E@@@@AP@@@@T@@@@E@@@@AP@@@@@@@@?0_0@@@@@@C?A?@@@@@@@@AP@@@@T@@@@E@@@@A
P@@@@T@@@@E@@@@AP@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@b') ; colorMapFromArray:#[0 0 0 255 255 255]; mask:((ImageMask new) width: 32; height: 32; photometric:(#blackIs0); bitsPerSample:(#[1]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G@@@@A0@@@@\@@@@G@@@@A0@@@@\@@@@G@@@@A0@@@@\@@@?8?0@O>O<@C?#?@@@G@@@@A0@@@@\@@@@G@@@@A
0@@@@\@@@@G@@@@A0@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@b') ; yourself); yourself]
!

enabledCursorImage
"/    EnabledCursorImage isNil ifTrue:[
"/        EnabledCursorImage := self enabledCursorIcon. "/ Image fromFile:'xpmBitmaps/cursors/double_crossHair.xpm'
"/    ].
    ^ EnabledCursorImage
!

fileInIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary fileInIcon
!

historyBackIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary historyBackIcon
!

historyForwardIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary historyForwardIcon
!

homeIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary homeIcon
!

homeIcon2
    <resource: #programImage>

    ^ ToolbarIconLibrary homeIcon2
!

htmlReloadIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary reload22x22Icon
!

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

    <resource: #image>

    ^Icon
        constantNamed:#'AbstractFileBrowser class leftDownIcon'
        ifAbsentPut:[(Depth2Image new) width: 28; height: 28; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'
UUUUUUUUUUUPAUUUUUUUP@UUUUUUUPB!!UUUUUU@B*AUUUUU@B*(UUUUUPB** UUUUPB***AUUUTB***(EUUUB****%UUUR*****UUUUU@*%UUUUUUPJ)UUUU
UUTB*UUUUUUU@*%UUUUUUPJ)UUUUUUTB*UUUUUUU@*%UUUUUUPJ)UUUUUUTB*UUUUUUU@*$@@@@AUPJ*****)UTB******UU@******%UPJ*****)UTAUUUU
UUUU@@@@@@@AUUUUUUUUUP@a') ; colorMapFromArray:#[255 255 255 0 0 0 40 40 100 255 0 0]; mask:((Depth1Image new) width: 28; height: 28; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'
@@@@@@@@@@@@ @@@@\@@@@O @@@G<@@@C? @@A?<@@@?? @@_?<@@G?? @@C<@@@@?@@@@O0@@@C<@@@@?@@@@O0@@@C<@@@@?@@@@O0@@@C??? @???8@O?
?>@C??? @???8@O??>@@@@@@@@@@@@@a') ; yourself); yourself]
!

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

    <resource: #image>

    ^Icon
        constantNamed:#'AbstractFileBrowser class menuHistoryList9x20Icon'
        ifAbsentPut:[(Depth1Image new) width: 9; height: 20; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@A0@G@@\@A0@G@@\@A0@G@@\@A0@G@A?@C8@G@@H@G<@@@@@@@@a') ; colorMapFromArray:#[255 255 255 0 0 0]; mask:((Depth1Image new) width: 9; height: 20; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@A0@G@@\@A0@G@@\@A0@G@@\@A0@G@A?@C8@G@@H@G<@@@@@@@@a') ; yourself); yourself]
!

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

    <resource: #image>

    ^Icon
        constantNamed:#'AbstractFileBrowser class menuHistoryListIcon'
        ifAbsentPut:[(Depth1Image new) width: 12; height: 12; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'??S?<O?0??C?<O?0??/?=_?0??C?<O?0') ; colorMapFromArray:#[255 255 255 0 0 0]; mask:((Depth1Image new) width: 12; height: 12; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@O@@<@C0A?8C?@G8@O@@X@@@A?8@@@') ; yourself); yourself]
!

newDirectoryIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary newDirectoryIcon
!

removeTabIcon
    <resource: #programImage>

    ^ ToolbarIconLibrary removeTabIcon
!

vt100Terminal
    <resource: #programImage>

    ^ ToolbarIconLibrary shell20x20Icon
! !

!AbstractFileBrowser class methodsFor:'interface specs'!

encodingDialogSpec
    "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:AbstractFileBrowser andSelector:#encodingDialogSpec
     AbstractFileBrowser new openInterface:#encodingDialogSpec
    "

    <resource: #canvas>

    ^ 
     #(FullSpec
        name: encodingDialogSpec
        window: 
       (WindowSpec
          label: 'File Encoding'
          name: 'File Encoding'
          min: (Point 10 10)
          bounds: (Rectangle 0 0 379 590)
        )
        component: 
       (SpecCollection
          collection: (
           (LabelSpec
              label: 'Select the External (File-) Encoding'
              name: 'Label1'
              layout: (LayoutFrame 0 0 0 0 0 1 30 0)
              translateLabel: true
              adjust: left
            )
           (DataSetSpec
              name: 'Table1'
              layout: (LayoutFrame 0 0 30 0 0 1 -60 1)
              model: selectedEncoding
              hasHorizontalScrollBar: true
              hasVerticalScrollBar: true
              dataList: encodingList
              has3Dsepartors: true
              has3Dseparators: true
              columns: 
             (OrderedCollection
                
               (DataSetColumnSpec
                  label: 'Encoding'
                  translateLabel: true
                  labelButtonType: Button
                  model: at:
                  canSelect: false
                  isResizeable: false
                  showRowSeparator: false
                  showColSeparator: false
                ) 
               (DataSetColumnSpec
                  label: 'Description'
                  translateLabel: true
                  labelButtonType: Button
                  model: at:
                  canSelect: false
                  isResizeable: false
                  showRowSeparator: false
                  showColSeparator: false
                )
              )
              doubleClickChannel: accept
            )
           (HorizontalPanelViewSpec
              name: 'ButtonPanel'
              layout: (LayoutFrame 0 0 -30 1 0 1 0 1)
              horizontalLayout: fitSpace
              verticalLayout: center
              horizontalSpace: 3
              verticalSpace: 3
              reverseOrderIfOKAtLeft: true
              component: 
             (SpecCollection
                collection: (
                 (ActionButtonSpec
                    label: 'Cancel'
                    name: 'Button2'
                    translateLabel: true
                    model: cancel
                    extent: (Point 185 22)
                  )
                 (ActionButtonSpec
                    label: 'OK'
                    name: 'Button1'
                    translateLabel: true
                    model: accept
                    isDefault: true
                    extent: (Point 185 22)
                  )
                 )
               
              )
            )
           (CheckBoxSpec
              label: 'Lock Encoding'
              name: 'LockEncodingCheckBox'
              layout: (LayoutFrame 3 0 -60 1 217 0 -30 1)
              activeHelpKey: lockFileEncoding
              model: lockFileEncoding
              translateLabel: true
            )
           )
         
        )
      )
! !

!AbstractFileBrowser class methodsFor:'menu specs'!

baseBookmarksMenuSpec
    "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:AbstractFileBrowser andSelector:#baseBookmarksMenuSpec
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser baseBookmarksMenuSpec)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            enabled: currentFilesHasDirectories
            label: 'Add Bookmark'
            itemValue: addBookmark
            translateLabel: true
          )
         (MenuItem
            enabled: hasBookmarksToRemove
            label: 'Remove Bookmark'
            itemValue: removeBookmark
            translateLabel: true
          )
         (MenuItem
            label: 'Save Bookmarks In...'
            itemValue: saveBookmarks
            translateLabel: true
          )
         )
        nil
        nil
      )
!

baseBookmarksMenuSpec2
    "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:AbstractFileBrowser andSelector:#baseBookmarksMenuSpec2
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser baseBookmarksMenuSpec2)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Edit Bookmarks'
            itemValue: editBookmarks
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: currentFilesHasDirectories
            label: 'Add Bookmark'
            itemValue: addBookmark
            translateLabel: true
          )
         )
        nil
        nil
      )
!

browserMenu
    "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:AbstractFileBrowser andSelector:#browserMenu
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser browserMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Spawn Filebrowser'
            itemValue: doSpawn
            translateLabel: true
            shortcutKey: Ctrln
          )
         (MenuItem
            label: 'Windows Explorer'
            itemValue: doOpenExplorer
            translateLabel: true
            isVisible: systemIsDOS
            shortcutKey: Ctrle
          )
         (MenuItem
            label: 'C Browser'
            itemValue: doOpenCBrowser
            translateLabel: true
            isVisible: cBrowserLoaded
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Add Text Editor Page'
            itemValue: newTextEditor
            translateLabel: true
            shortcutKey: Ctrlt
          )
         (MenuItem
            label: 'Add Shell Terminal Page'
            itemValue: doAddTerminal
            translateLabel: true
          )
         (MenuItem
            label: 'Add Search Page'
            itemValue: doOpenSearchFile
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Settings...'
            itemValue: doOpenSettings
            translateLabel: true
          )
         (MenuItem
            label: 'Encoding...'
            itemValue: fileEncodingDialog
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Exit'
            itemValue: closeRequest
            translateLabel: true
          )
         )
        nil
        nil
      )
!

cvsMenu
    "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:AbstractFileBrowser andSelector:#cvsMenu
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser cvsMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'Commit...'
            #itemValue: #cvsCommit
            #translateLabel: true
          )
         #(#MenuItem
            #enabled: #canCvsAddAndCommit
            #label: 'Add && Commit...'
            #itemValue: #cvsAddAndCommit
            #translateLabel: true
          )
         #(#MenuItem
            #enabled: #canCvsAddAndCommit
            #label: 'Add as Binary && Commit...'
            #itemValue: #cvsAddBinaryAndCommit
            #translateLabel: true
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'Tag...'
            #itemValue: #cvsTagSelection
            #translateLabel: true
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'Revision Log'
            #itemValue: #cvsRevisionLog
            #translateLabel: true
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'Compare with newest in Repository'
            #itemValue: #cvsCompareWithNewest
            #translateLabel: true
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #enabled: #hasSelection
            #label: 'Update selected Files/Directories'
            #itemValue: #cvsUpdateSelection
            #translateLabel: true
          )
         #(#MenuItem
            #label: 'Update Directory Local'
            #itemValue: #cvsUpdateAll
            #translateLabel: true
          )
         #(#MenuItem
            #label: 'Update Directory Recursive'
            #itemValue: #cvsUpdateAllRecursive
            #translateLabel: true
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #enabled: #canRemoveCVSContainer
            #label: 'Remove File && CVS Container...'
            #itemValue: #cvsRemoveFileAndCVSContainer
            #translateLabel: true
          )
         )
        nil
        nil
      )
!

directoryMenu
    "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:AbstractFileBrowser andSelector:#directoryMenu
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser directoryMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Up'
            itemValue: doGoDirectoryUp
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            activeHelpKey: directoryBack
            enabled: enableBack
            label: 'Back'
            itemValue: doBack
            translateLabel: true
          )
         (MenuItem
            activeHelpKey: directoryBack
            enabled: enableForward
            label: 'Forward'
            itemValue: doForward
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: enableHome
            label: 'Home Directory'
            itemValue: doGotoHomeDirectory
            translateLabel: true
          )
         (MenuItem
            enabled: enableGotoDesktopDirectory
            label: 'Desktop'
            itemValue: doGotoDesktopDirectory
            translateLabel: true
            isVisible: gotoDesktopDirectoryIsVisible
          )
         (MenuItem
            enabled: enableGotoDefaultDirectory
            label: 'Default (Current) Directory'
            itemValue: doGotoDefaultDirectory
            translateLabel: true
            isVisible: gotoDefaultDirectoryIsVisible
          )
         (MenuItem
            enabled: enableGotoSmalltalkDirectory
            label: 'Smalltalk (Application) Directory'
            itemValue: doGotoSmalltalkDirectory
            translateLabel: true
          )
         (MenuItem
            enabled: enableGotoTempDirectory
            label: 'Temp Directory'
            itemValue: doGotoTempDirectory
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: enableMakeCurrentDirectory
            label: 'Make this the Default (Current) Directory'
            itemValue: doMakeCurrentDirectory
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Bookmarks'
            translateLabel: true
            submenuChannel: bookmarksMenu
          )
         (MenuItem
            label: 'Visited Directories'
            translateLabel: true
            submenuChannel: visitedDirectoriesMenu
          )
         )
        nil
        nil
      )

    "Modified: / 29-12-2010 / 11:00:58 / cg"
!

editMenu
    "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:AbstractFileBrowser andSelector:#editMenu
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser editMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            enabled: hasFileSelection
            label: 'Copy Selected Filenames to Clipboard'
            itemValue: copySelectedFilenames
            translateLabel: true
          )
         (MenuItem
            enabled: fileListIsNotEmpty
            label: 'Copy All Filenames to Clipboard'
            itemValue: copyFileList
            translateLabel: true
          )
         )
        nil
        nil
      )
!

emptyMenuSpec
    "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:AbstractFileBrowser andSelector:#emptyMenuSpec
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser emptyMenuSpec)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         )
        nil
        nil
      )
!

fileMenu
    "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:AbstractFileBrowser andSelector:#fileMenu
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser fileMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Open'
            itemValue: doShowFileContents
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: hasFileSelection
            label: 'FileIn'
            itemValue: fileFileIn
            translateLabel: true
          )
         (MenuItem
            enabled: hasFileSelection
            label: 'FileIn to Namespace...'
            itemValue: fileFileInToNameSpace
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'New'
            translateLabel: true
            submenuChannel: newMenu
            keepLinkedMenu: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: hasSelection
            label: 'Cut'
            itemValue: cutFiles
            translateLabel: true
          )
         (MenuItem
            enabled: hasSelection
            label: 'Copy'
            itemValue: copyFiles
            translateLabel: true
          )
         (MenuItem
            enabled: canPaste
            label: 'Paste'
            itemValue: pasteFiles
            translateLabel: true
          )
         (MenuItem
            enabled: hasSelection
            label: 'Delete'
            itemValue: deleteFiles
            translateLabel: true
          )
         (MenuItem
            enabled: hasSelection
            label: 'Erase'
            itemValue: eraseFiles
            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: '-'
            isVisible: javaSupportLoaded
          )
         (MenuItem
            enabled: canAddToClassPath
            label: 'Add to Java Class Path'
            translateLabel: true
            isVisible: javaSupportLoaded
          )
         (MenuItem
            enabled: canRemoveFromClassPath
            label: 'Remove from Java Class Path'
            translateLabel: true
            isVisible: javaSupportLoaded
          )
         (MenuItem
            enabled: canAddToSourcePath
            label: 'Add to Java Source Path'
            translateLabel: true
            isVisible: javaSupportLoaded
          )
         (MenuItem
            enabled: canRemoveFromSourcePath
            label: 'Remove from Java Source Path'
            translateLabel: true
            isVisible: javaSupportLoaded
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: enableFileHistory
            label: 'File History'
            translateLabel: true
            submenuChannel: menuFileHistory
          )
         )
        nil
        nil
      )

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

fileOpMenu
    "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:AbstractFileBrowser andSelector:#fileOpMenu
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser fileOpMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Split...'
            itemValue: splitSelectedFiles
            translateLabel: true
          )
         (MenuItem
            label: 'Join...'
            itemValue: joinSelectedFiles
            translateLabel: true
          )
         (MenuItem
            label: 'Copy Corrupted File To...'
            itemValue: copySelectionToRepairingCorruptedFiles
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Rot13...'
            itemValue: filterSelectedFiles:
            translateLabel: true
            argument: rot13
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Truncate...'
            itemValue: truncateSelectedFilesToZeroSize
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Generate Signed Files'
            itemValue: generateSignaturesForSelectedFiles
            translateLabel: true
            enabled: canGenerateSignatureFiles
          )
         (MenuItem
            label: 'Generate Detached Signature Files (use for dll)'
            itemValue: generateDetachedSignaturesForSelectedFiles
            translateLabel: true
            enabled: canGenerateSignatureFiles
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: hasSelection
            label: 'File Differences'
            itemValue: openDiffView
            translateLabel: true
            isVisible: hasTwoFilesSelectedHolder
          )
         (MenuItem
            enabled: hasSelection
            label: 'File Differences...'
            itemValue: openDiffView
            translateLabel: true
            isVisible: hasNotTwoFilesSelectedHolder
          )
         (MenuItem
            enabled: hasSelection
            label: 'Directory Differences'
            itemValue: openDirectoryDiffView
            translateLabel: true
            isVisible: hasTwoDirectoriesSelectedHolder
          )
         (MenuItem
            enabled: hasSelection
            label: 'Directory Differences...'
            itemValue: openDirectoryDiffView
            translateLabel: true
            isVisible: hasNotTwoDirectoriesSelectedHolder
          )
         )
        nil
        nil
      )

    "Modified: / 26-10-2010 / 17:16:05 / cg"
!

newMenu
    "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:DirectoryTreeBrowser andSelector:#newMenu
     (Menu new fromLiteralArrayEncoding:(DirectoryTreeBrowser newMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'Directory...'
            #translateLabel: true
            #value: #newDirectory
          )
         #(#MenuItem
            #label: 'File...'
            #translateLabel: true
            #value: #newFile
          )                                     
         #(#MenuItem
            #label: 'Hard Link...'
            #translateLabel: true
            #isVisible: #systemIsUnix
            #value: #newHardLink
          )
         #(#MenuItem
            #label: 'Symbolic Link...'
            #translateLabel: true
            #isVisible: #systemIsUnix
            #value: #newSoftLink
          )
         )
        nil
        nil
      )
!

showMenuSpec
    "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:AbstractFileBrowser andSelector:#showMenuSpec
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser showMenuSpec)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Hidden Files'
            translateLabel: true
            hideMenuOnActivated: false
            indication: showHiddenFiles
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'Directory Tree'
            translateLabel: true
            hideMenuOnActivated: false
            indication: showDirectoryTree
          )
         (MenuItem
            label: 'Regular Files in TreeView (Left)'
            translateLabel: true
            hideMenuOnActivated: false
            indication: viewFilesInDirectoryTree
          )
         (MenuItem
            label: 'Directories in ContentsView (Right)'
            translateLabel: true
            hideMenuOnActivated: false
            indication: viewDirsInContentsBrowser
          )
         (MenuItem
            enabled: enableViewNoteBookApplication
            label: 'File Applications'
            translateLabel: true
            hideMenuOnActivated: false
            indication: viewNoteBookApplicationHolder
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'DiskUsage'
            translateLabel: true
            hideMenuOnActivated: false
            indication: showDiskUsageHolder
          )
         )
        nil
        nil
      )
!

showMenuSpecForDialog
    "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:AbstractFileBrowser andSelector:#showMenuSpecForDialog
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser showMenuSpecForDialog)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Hidden Files'
            translateLabel: true
            hideMenuOnActivated: false
            indication: showHiddenFiles
          )
         )
        nil
        nil
      )
!

sortMenu
    "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:AbstractFileBrowser andSelector:#sortMenu
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser sortMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'By Filename'
            #translateLabel: true
            #hideMenuOnActivated: false
            #choice: #sortBlockProperty
            #choiceValue: #baseName
          )
         #(#MenuItem
            #label: 'By Extension'
            #translateLabel: true
            #hideMenuOnActivated: false
            #choice: #sortBlockProperty
            #choiceValue: #suffix
          )
         #(#MenuItem
            #label: 'By Permissions'
            #translateLabel: true
            #hideMenuOnActivated: false
            #choice: #sortBlockProperty
            #choiceValue: #permissions
          )
         #(#MenuItem
            #label: 'By Owner'
            #translateLabel: true
            #isVisible: #viewOwner
            #hideMenuOnActivated: false
            #choice: #sortBlockProperty
            #choiceValue: #owner
          )
         #(#MenuItem
            #label: 'By Group'
            #translateLabel: true
            #isVisible: #viewOwner
            #hideMenuOnActivated: false
            #choice: #sortBlockProperty
            #choiceValue: #group
          )
         #(#MenuItem
            #label: 'By Size'
            #translateLabel: true
            #hideMenuOnActivated: false
            #choice: #sortBlockProperty
            #choiceValue: #fileSize
          )
         #(#MenuItem
            #label: 'By Date && Time'
            #translateLabel: true
            #hideMenuOnActivated: false
            #choice: #sortBlockProperty
            #choiceValue: #modificationTime
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Ignore Case in Sort'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #sortCaseless
          )
         #(#MenuItem
            #label: 'Directories before Files'
            #translateLabel: true
            #hideMenuOnActivated: false
            #indication: #sortDirectoriesBeforeFiles
          )
         )
        nil
        nil
      )
!

toolsMenuSpec
    "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:AbstractFileBrowser andSelector:#toolsMenuSpec
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser toolsMenuSpec)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'Open (Win32-Shell)'
            itemValue: doOpenWithShellCommand
            translateLabel: true
            isVisible: systemIsDOS
          )
         (MenuItem
            label: 'Execute UNIX Command...'
            itemValue: doExecuteCommand
            translateLabel: true
            isVisible: systemIsUnix
          )
         (MenuItem
            label: 'Execute DOS Command...'
            itemValue: doExecuteCommand
            translateLabel: true
            isVisible: systemIsDOS
          )
         (MenuItem
            label: 'Execute Script...'
            itemValue: doExecuteScript
            translateLabel: true
          )
         (MenuItem
            enabled: canDoTerminal
            label: 'Shell Terminal'
            itemValue: openTerminal
            translateLabel: true
            isVisible: canDoTerminalAndSystemIsUnix
          )
         (MenuItem
            enabled: canDoTerminal
            label: 'DOS Terminal'
            itemValue: openTerminal
            translateLabel: true
            isVisible: canDoTerminalAndSystemIsDOS
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: hasFileSelection
            label: 'Changes Browser'
            itemValue: openChangesBrowser
            translateLabel: true
          )
         (MenuItem
            enabled: hasFileSelection
            label: 'ChangeSet Browser'
            itemValue: openChangeSetBrowser
            translateLabel: true
            isVisible: changeSetBrowserItemVisible
          )
         (MenuItem
            enabled: hasFileSelection
            label: 'Workspace'
            itemValue: openWorkspace
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            enabled: canReadAbbrevFile
            label: 'Install Autoloaded'
            itemValue: readAbbrevFile
            translateLabel: true
          )
         (MenuItem
            enabled: anySTFilesPresent
            label: 'Install All ST-Files as Autoloaded'
            itemValue: installAllAsAutoloaded
            translateLabel: true
          )
         (MenuItem
            enabled: recursiveAnySTFilesPresent
            label: 'Recursive Install All ST-Files as Autoloaded'
            itemValue: installAllAsAutoloadedRecursive
            translateLabel: true
          )
         (MenuItem
            label: '-'
          )
         (MenuItem
            label: 'File Operations'
            translateLabel: true
            submenuChannel: fileOpMenu
          )
         (MenuItem
            label: 'File Utilities'
            translateLabel: true
            submenu: 
           (Menu
              (
               (MenuItem
                  enabled: hasFileSelection
                  label: 'Editor'
                  itemValue: openEditor
                  translateLabel: true
                )
               (MenuItem
                  enabled: hasFileSelection
                  label: 'HTML Reader'
                  itemValue: openHTMLReader
                  translateLabel: true
                )
               (MenuItem
                  enabled: hasFileSelection
                  label: 'Web Browser'
                  itemValue: openWebBrowser
                  translateLabel: true
                )
               (MenuItem
                  "/ enabled: hasXmlFileSelected
                  label: 'XML Inspector'
                  itemValue: parseXmlFile
                  translateLabel: true
                  isVisible: hasXml
                  showBusyCursorWhilePerforming: true
                )
               (MenuItem
                  enabled: hasFileSelection
                  label: 'Acroread (PDF Viewer)'
                  itemValue: openPDFViewer
                  translateLabel: true
                )
               (MenuItem
                  enabled: hasASN1AndSelection
                  label: 'ASN1 Browser'
                  itemValue: openASN1Browser
                  translateLabel: true
                  isVisible: hasASN1
                )
               (MenuItem
                  enabled: hasCBrowser
                  label: 'C Browser'
                  itemValue: openCBrowser
                  translateLabel: true
                  isVisible: hasCBrowser
                )
               (MenuItem
                  enabled: hasJavaAndSelection
                  label: 'Applet Viewer'
                  itemValue: openAppletViewer
                  translateLabel: true
                  isVisible: hasJava
                )
               (MenuItem
                  enabled: hasMP3PlayerAndSelection
                  label: 'MP3 Player'
                  itemValue: openMP3Player
                  translateLabel: true
                  isVisible: hasMP3Player
                )
               (MenuItem
                  enabled: hasFileSelection
                  label: 'xv (Image Viewer)'
                  itemValue: openXV
                  translateLabel: true
                  isVisible: systemIsUnix
                )
               (MenuItem
                  enabled: currentFilesAreInSameDirectory
                  label: 'Slide Show'
                  itemValue: openSlideShow
                  translateLabel: true
                  isVisible: hasSlideShow
                )
               (MenuItem
                  enabled: hasFileSelection
                  label: 'gv (Postscript Viewer)'
                  itemValue: openGV
                  translateLabel: true
                  isVisible: systemIsUnix
                )
               (MenuItem
                  enabled: hasMP3PlayerAndSelection
                  label: 'MP3 Player'
                  itemValue: openMP3Player
                  translateLabel: true
                  isVisible: hasMP3Player
                )
               (MenuItem
                  enabled: hasFileSelection
                  label: 'Realplay (avi viewer)'
                  itemValue: openRP
                  translateLabel: true
                  isVisible: systemIsUnix
                )
               (MenuItem
                  label: '-'
                )
               (MenuItem
                  label: 'Smalltalk'
                  translateLabel: true
                  submenu: 
                 (Menu
                    (
                     (MenuItem
                        enabled: hasSnapshotSelection
                        label: 'Snapshot Image Browser'
                        itemValue: openSnapshotImageBrowser
                        translateLabel: true
                      )
                     (MenuItem
                        enabled: canCreateNewProject
                        label: 'Create Smalltalk Project'
                        itemValue: createProjectAndOpenProjectBrowser
                        translateLabel: true
                      )
                     (MenuItem
                        label: '-'
                      )
                     (MenuItem
                        enabled: hasResourceFileSelected
                        label: 'Show Contents of Resourcefile'
                        itemValue: readAndShowResources
                        translateLabel: true
                      )
                     (MenuItem
                        enabled: hasResourceFileSelected
                        label: 'Resource File Editor'
                        itemValue: openResourceFileEditor
                        translateLabel: true
                        showBusyCursorWhilePerforming: true
                      )
                     (MenuItem
                        label: '-'
                      )
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Contents as ByteArray'
                        itemValue: fileContentsAsByteArray
                        translateLabel: true
                      )
                     )
                    nil
                    nil
                  )
                )
               (MenuItem
                  label: 'Image'
                  translateLabel: true
                  submenu: 
                 (Menu
                    (
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Image Editor'
                        itemValue: openImageEditor
                        translateLabel: true
                      )
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Image Preview'
                        itemValue: openImagePreview
                        translateLabel: true
                      )
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Image Inspector'
                        itemValue: openImageInspector
                        translateLabel: true
                      )
                     (MenuItem
                        label: '-'
                      )
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Convert to GIF'
                        itemValue: convertImageToGIF
                        translateLabel: true
                      )
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Convert to PNG'
                        itemValue: convertImageToPNG
                        translateLabel: true
                      )
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Convert to XPM'
                        itemValue: convertImageToXPM
                        translateLabel: true
                      )
                     (MenuItem
                        enabled: hasFileSelection
                        label: 'Convert to JPG'
                        itemValue: convertImageToJPG
                        translateLabel: true
                      )
                     )
                    nil
                    nil
                  )
                )
               (MenuItem
                  enabled: hasZipFileSelectedHolder
                  label: 'ZipFile Tool'
                  itemValue: openZipTool
                  translateLabel: true
                )
               (MenuItem
                  enabled: hasFileSelection
                  label: 'Hex Dump'
                  itemValue: fileHexDump
                  translateLabel: true
                )
"/ something I would like to have...
"/               (MenuItem
"/                  enabled: hasFileSelection
"/                  label: 'Hex Dump of First n Bytes...'
"/                  itemValue: fileHexDumpOfFirstNBytes
"/                  translateLabel: true
"/                )
"/               (MenuItem
"/                  enabled: hasFileSelection
"/                  label: 'Hex Dump of Last n Bytes...'
"/                  itemValue: fileHexDumpOfLastNBytes
"/                  translateLabel: true
"/                )
               )
              nil
              nil
            )
          )
         (MenuItem
            label: 'Find'
            translateLabel: true
            submenu: 
           (Menu
              (
               (MenuItem
                  label: 'File...'
                  itemValue: fileFindFile
                  translateLabel: true
                )
               (MenuItem
                  label: 'Duplicate Files'
                  itemValue: fileFindDuplicates
                  translateLabel: true
                )
               (MenuItem
                  enabled: hasSelection
                  label: 'All Duplicate Files (Recursive)'
                  itemValue: fileFindAllDuplicates
                  translateLabel: true
                )
               (MenuItem
                  label: 'Similar Image Files'
                  itemValue: fileFindSimilarImages
                  translateLabel: true
                  isVisible: hasImageColorHistogram
                )
               )
              nil
              nil
            )
          )
         )
        nil
        nil
      )

    "Modified: / 14-02-2011 / 17:17:44 / cg"
!

viewDetailsMenuSpec
    "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:AbstractFileBrowser andSelector:#viewDetailsMenuSpec
     (Menu new fromLiteralArrayEncoding:(AbstractFileBrowser viewDetailsMenuSpec)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Hidden Files'
            #translateLabel: true
            #indication: #showHiddenFiles
          )
         )
        nil
        nil
      )
! !

!AbstractFileBrowser class methodsFor:'misc'!

newLock
    ^ CodeExecutionLock new
! !

!AbstractFileBrowser class methodsFor:'resources'!

classResources
    ^ FileBrowser classResources
! !

!AbstractFileBrowser class methodsFor:'utilities'!

contentsOfFileAsHexDump:f
    ^ self contentsOfFileAsHexDump:f withLimit:nil lastPart:nil
!

contentsOfFileAsHexDump:f withLimit:limitOrNil lastPart:showLastPartOrNil
    |resources fileName stream data offs 
     addrDigits col line lineStream asciiLineStream lines answer sizeLimit showLastPart|

    resources := self classResources.

    fileName := f baseName.
    f isDirectory ifTrue:[
        Dialog warn:(resources string:'%1 is a directory.' with:fileName).
        ^ nil
    ].
    f exists ifFalse:[
        Dialog warn:(resources string:'oops, ''%1'' is gone or unreadable.' with:fileName).
        ^ nil
    ].
    f isReadable ifFalse:[
        Dialog warn:(resources string:'''%1'' is unreadable.' with:fileName).
        ^ nil
    ].
    f fileSize > (1024*1024) ifTrue:[
        limitOrNil notNil ifTrue:[
            sizeLimit := limitOrNil.
            showLastPart := showLastPartOrNil
        ] ifFalse:[
            answer := Dialog 
                            confirmWithCancel:(resources
                                                stringWithCRs:'"%1" is very large (%2).\\Show all or only the first 4 Mb ?' 
                                                with:(fileName contractTo:40) allBold 
                                                with:(UnitConverter fileSizeStringFor:f fileSize))
                            labels:#('Cancel' 'Show All' 'Show First Part' ).

            answer isNil ifTrue:[^ nil].
            answer ifTrue:[
                sizeLimit := 4 * 1024 * 1024
            ].
        ].
    ].

    stream := f readStream binary.
    sizeLimit notNil ifTrue:[
        showLastPart == true ifTrue:[
            stream position:(f fileSize - sizeLimit).
        ].
        data := stream nextBytes:sizeLimit.
    ] ifFalse:[
        data := stream contents.
    ].
    stream close.

    col := 1.
    offs := 0.
    lines := StringCollection new.

    addrDigits := ((f fileSize + 1) log:16) truncated + 1.

    lineStream := String writeStream.
    asciiLineStream := String writeStream.

    lineStream nextPutAll:(offs hexPrintString:addrDigits).
    lineStream nextPutAll:': '.

    data do:[:byte |
        lineStream nextPutAll:(byte hexPrintString:2).
        (byte between:32 and:127) ifTrue:[
            asciiLineStream nextPut:(Character value:byte)
        ] ifFalse:[
            asciiLineStream nextPut:$.
        ].

        offs := offs + 1.
        col := col + 1.
        col > 16 ifTrue:[
            lineStream nextPutAll:'        '.
            lineStream nextPutAll:asciiLineStream contents.
            lines add:(lineStream contents).
            (offs bitAnd:16rFF) == 0 ifTrue:[
                lines add:nil
            ].
            lineStream reset.
            asciiLineStream reset.

            lineStream nextPutAll:(offs hexPrintString:addrDigits).
            lineStream nextPutAll:': '.
            col := 1.
        ] ifFalse:[
            lineStream space
        ]
    ].
    line := lineStream contents paddedTo:(3*16 + addrDigits + 1).
    lines add:(line , '        ' , asciiLineStream contents).
    ^ lines
! !

!AbstractFileBrowser methodsFor:'actions'!

askForCommandFor:fileName thenDo:aBlock
    "setup and launch a querybox to ask for a unix command.
     Then evaluate aBlock passing the command-string as argument."

    |box osName commandString|

    osName := OperatingSystem platformName.

    box := FilenameEnterBox 
                title:(resources string:'Execute %1 command:' with:osName)
               okText:(resources string:'Execute')
               action:[:cmd | commandString := cmd].

    fileName notNil ifTrue:[
        self initialCommandFor:fileName into:box.
    ].
    box directory:(self getDirWithoutFileName:fileName).
    box show.
    box destroy.

    commandString notNil ifTrue:[
        aBlock value:commandString
    ].

    "Modified: / 7.9.1995 / 10:31:54 / claus"
    "Modified: / 16.9.1997 / 15:35:10 / stefan"
    "Modified: / 14.8.1998 / 14:12:52 / cg"
!

changeFileBrowserTitleTo:aString
    self 
        applicationNamed:#FileBrowserV2 
        ifPresentDo:[:app | app changeFileBrowserTitleTo:aString]
!

copyFileList
    "copy the fileList to the clipBoard"

    |fileList|

    fileList := self 
                    applicationNamed:#DirectoryContentsBrowser 
                    ifPresentDo:[:app | app browserFileList].

    fileList notNil ifTrue:[
        self copyFileListToClipBoard:fileList.
    ]
!

copyFileListToClipBoard:fileList
    "copy the itemList to the clipBoard"

    |stream|

    fileList isEmpty ifTrue:[^ self].

    stream := String writeStream.
    fileList size == 1 ifTrue:[
        stream nextPutAll:(fileList first asString).
    ] ifFalse:[
        fileList do:[:fileName |
            (fileName baseName ~= '..') ifTrue:[
                stream nextPutLine:(fileName asString).
            ]
        ].
    ].
    self window setClipboardText:stream contents.
!

copySelectedFilenames
    "copy the selected fileNames to the clipBoard"

    |fileList|

    fileList := self 
                    applicationNamed:#DirectoryContentsBrowser 
                    ifPresentDo:[:appl | appl selectedFileNames].
    fileList notNil ifTrue:[
        self copyFileListToClipBoard:fileList.
    ].
!

doAddTerminal
    |dir|

    dir := self currentDirectory.
    
    dir isNil ifTrue:[
        dir :=  Filename homeDirectory
    ].

    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | appl addTerminalIn:dir].
!

doOpenSearchFile
    |item file|

    file := self firstSelectedFileName.
    file isNil ifTrue:[
        file := Filename homeDirectory asAbsoluteFilename.
    ].
    item := DirectoryContentsBrowser itemClass fileName:file.
    self openSearchFileOn:item.
!

doShowFileContents
    self 
        applicationNamed:#DirectoryContentsBrowser 
        ifPresentDo:[:appl | ^ appl doShowFileContents].

    ^ nil
!

doShowProperties
    "show long stat (file)-info"

    self fileGetInfo:true
!

fileEncodingDialog
    "open a dialog to allow change of the files character encoding.
     Files are converted to internal encoding when read, and converted back
     to this encoding when saved.
     Notice: currently, not too many encodings are supported by the system."

    |bindings encodings encodingDescr idx selectedEncoding|

    encodingDescr := CharacterEncoder supportedExternalEncodings.
    encodings := encodingDescr collect:[:d | d isNil ifTrue:[nil] ifFalse:[d first]].

    bindings := IdentityDictionary new.
    bindings at:#encodingList put:encodingDescr.
    bindings at:#selectedEncoding put:(encodings indexOf:self fileEncoding ifAbsent:1) asValue.
    bindings at:#lockFileEncoding put:(self lockFileEncodingHolder value asValue).

    (self openDialogInterface:#encodingDialogSpec withBindings:bindings)
    ifTrue:[
        idx := (bindings at:#selectedEncoding) value.
        selectedEncoding := encodings at:idx.
        selectedEncoding notNil ifTrue:[
            self fileEncoding:selectedEncoding asSymbol.
        ].
        self lockFileEncoding:(bindings at:#lockFileEncoding) value.
    ].

    "Modified: 30.6.1997 / 14:41:12 / cg"
!

gotoFile:aFilename 
    "select only if the file is not already in the selection"
    
    |currentFileNameHolder currentSel|

    currentFileNameHolder := self currentFileNameHolder.

    currentSel := currentFileNameHolder value.
    (currentSel includes:aFilename) ifFalse:[
        self withWaitCursorDo:[
            currentFileNameHolder value:(OrderedCollection with:(aFilename asAbsoluteFilename)).
        ]
    ].

    "Modified: / 29-12-2010 / 11:04:29 / cg"
!

newTextEditor
    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | ^ appl newTextEditor].

    ^ nil.
!

openApplByFileItem:anItem
    "/ q: is the following always wanted ?
    self window sensor shiftDown ifFalse:[
        anItem fileName isExecutableProgram ifTrue:[
            self executeCommand:anItem fileName pathName.
            ^ self.
        ].

"/        OperatingSystem isMSWINDOWSlike ifTrue:[
"/            Error handle:[:ex |
"/            ] do:[
"/                OperatingSystem
"/                    shellExecute:nil
"/                    lpOperation:'open'
"/                    lpFile:(anItem fileName pathName)
"/                    lpParameters:nil
"/                    lpDirectory:(self directory pathName)
"/                    nShowCmd:#SW_SHOWNORMAL.
"/                ^ self.
"/            ]
"/        ]
    ].
    ^ self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | appl openApplByFileItem:anItem].
!

openApplForFile:aFilename
    ^ self openApplByFileItem:(DirectoryContentsBrowser itemClass fileName:aFilename).
!

openCommandResultApplication

    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | ^ appl openCommandResultApplication].

    ^ nil
!

openNewTextEditorOn:anItem
    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | ^ appl openNewTextEditorOn:anItem ].

    ^ nil
!

openSearchFileOn:anItem
    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | ^ appl openSearchFileOn:anItem].

    ^ nil.
!

openTextEditorForFile:aFilename
    ^ self openTextEditorOn:(DirectoryContentsBrowser itemClass fileName:aFilename).
!

openTextEditorOn:anItem
    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | ^ appl openTextEditorOn:anItem ].

    ^ nil.
!

openTextEditorOn:anItem type:aDirDescrOrFile
    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl | ^ appl openTextEditorOn:anItem type:aDirDescrOrFile].

    ^ nil.
!

setCurrentFileName:aFilename 
    self setCurrentFileNames:(OrderedCollection with:aFilename).
!

setCurrentFileNames:aCollectionOfFilenames 
    self currentFileNameHolder value:aCollectionOfFilenames.
!

updateAndSelect:aColOfFiles
    self updateCurrentDirectory.
    aColOfFiles notNil ifTrue:[ 
        self setCurrentFileNames:aColOfFiles 
    ].
!

updateCurrentDirectory
    self updateCurrentDirectory:false
!

updateCurrentDirectory:withReread
    DirectoryContents flushCachedDirectoryFor:(self getBestDirectory).

    self 
        applicationNamed:#DirectoryContentsBrowser 
        ifPresentDo:[:appl | withReread ifTrue:[appl doUpdate] ifFalse:[appl doUpdateDirectoryContents]].
    self 
        applicationNamed:#DirectoryTreeBrowser     
        ifPresentDo:[:appl | appl doUpdate].
!

updateCurrentDirectoryWithReread
    self updateCurrentDirectory:true
!

withActivityIndicationDo:aBlock

    self activityVisibilityChannel value:true.
    [
        self withWaitCursorDo:[
            aBlock value.
        ]
    ] ensure:[
        self activityVisibilityChannel value:false.
    ]
! !

!AbstractFileBrowser methodsFor:'actions bookmarks'!

addBookmark
    self currentFilesAreInSameDirectory ifFalse:[^ self].
    self addBookmarks:(self currentSelectedDirectories).
    self class saveBookmarksInDefaultBookmarksFile
!

addBookmarks:aColOfDirectories
    aColOfDirectories do:[ :path |
        self class addBookmark:path
    ].
!

editBookmarks

    | default selectedDirectories |

    selectedDirectories := self currentSelectedDirectories.
    selectedDirectories notEmpty ifTrue:[default := selectedDirectories first].
    self class editBookmarksWithDefault: default.
!

hasBookmarks
    ^ self class hasBookmarks
!

hasBookmarksToRemove
    |bookmarks directories|

    directories := self currentSelectedDirectories.
    bookmarks := self class directoryBookmarks.
    ^ (bookmarks size > 0 and:[directories notEmpty])
!

removeBookmark
    self currentSelectedDirectories do:[:dir|
        self class removeBookmark:dir
    ].
!

saveBookmarks
     self class saveBookmarks
! !

!AbstractFileBrowser methodsFor:'actions history'!

addToCommandHistory:aCommandString for:aFilename
    |cmd suffix cmdHist historySize|

    cmdHist := self class commandHistory.
    (aCommandString notEmptyOrNil) ifTrue:[
        cmdHist notNil ifTrue:[
            cmdHist addFirst:aCommandString.
            historySize := self class commandHistorySize.
            (historySize notNil and:[cmdHist size > historySize]) ifTrue:[
                cmdHist removeLast
            ]
        ].
        aFilename notNil ifTrue:[
            cmd := aCommandString copyTo:(aCommandString indexOf:Character space ifAbsent:[aCommandString size + 1])-1.
            DefaultCommandPerSuffix isNil ifTrue:[
                DefaultCommandPerSuffix := Dictionary new.
            ].
            suffix := aFilename asFilename suffix.
            suffix notNil ifTrue:[
                DefaultCommandPerSuffix at:suffix put:cmd.
            ]
        ]
    ]
!

doBack
    "go backward in the history"

    | fileName|

    fileName := self directoryHistory goBackward.
    fileName notNil ifTrue:[
        self gotoFile:(fileName asFilename).
    ]
!

doForward
    "go forward in the history"

    | fileName|

    fileName := self directoryHistory goForward.
    fileName notNil ifTrue:[
        self gotoFile:(fileName asFilename).
    ] ifFalse:[
        self enableForward value:false.

    ].
! !

!AbstractFileBrowser methodsFor:'applications'!

applicationNamed:anApplicationName ifPresentDo:aBlock
    |appl|

    appl := self applications at:anApplicationName ifAbsent:nil.
    appl notNil ifTrue:[
        ^ aBlock value:appl
    ].
    ^ nil.
!

directoryContentsBrowser
    ^ self applications at:#DirectoryContentsBrowser ifAbsent:nil.
! !

!AbstractFileBrowser methodsFor:'aspects'!

applications
    ^ aspects at:#applications
!

backgroundProcesses

    ^ self aspectFor:#backgroundProcesses ifAbsent:[List new]
!

canMake
    ^ self aspectFor:#canMake ifAbsent:[ false asValue ].
!

currentDirectories
    " returns a holder on a Collection of all currently selected directories  
      if only a file is selected, currentDirectories holds the directory of the file
    "

    ^ self aspectFor:#currentDirectories ifAbsent:[ (OrderedCollection new) asValue ].
!

currentDirectoriesValue
    " returns a Collection of all currently selected directories  
      if only a file is selected, currentDirectories holds the directory of the file
    "

    ^ self currentDirectories value
!

currentDirectory
    "return the single current directory or nil"

    |directories|

    directories := self currentSelectedDirectories.
    (directories size ~= 1) ifTrue:[
        ^ nil
    ].
    ^ directories first.
!

currentFileNameHolder
    "return a ValueHolder with an OrderedCollection containing all selected files.
     If no file but a directory is selected it contains the directories 
     in contrast to currentDirectories which have only the directories"

    ^ self aspectFor:#currentFileNameHolder 
        ifAbsent:[
            |currentSel|    

            currentSel := self class currentSelection.
            currentSel isEmpty ifTrue:[
                currentSel := OrderedCollection with:(Filename currentDirectory asAbsoluteFilename)
            ].
            currentSel asValue
        ] 
        notPresentDo:[:holder|
            |filenames newFilenames|
            filenames := holder value.
            newFilenames := Set new.
            filenames do:[:eachFilename|  
                |existingFilename|
                existingFilename := eachFilename. 
                existingFilename notNil ifTrue:[
                    [existingFilename isRootDirectory or:[existingFilename exists]] whileFalse:[
                        existingFilename := existingFilename directory.
                    ].
                    existingFilename exists ifTrue:[
                        newFilenames add:existingFilename.
                    ].
                ]
            ].
            newFilenames asOrderedCollection.
        ]

    "Modified: / 27-11-2010 / 15:42:45 / cg"
!

defaultFileEncoding
    ^ #'iso8859-1' "/ #'utf-8'
!

enableDirectoryUp

    ^ self aspectFor:#enableDirectoryUp ifAbsent:[false asValue]
!

enableGotoDefaultDirectory
    ^ self aspectFor:#enableGotoDefaultDirectory ifAbsent:[ true asValue ].
!

enableGotoDesktop
    ^ self aspectFor:#enableGotoDesktop ifAbsent:[ true asValue ].
!

enableGotoDesktopDirectory
    ^ self aspectFor:#enableGotoDesktopDirectory ifAbsent:[ true asValue ].

    "Created: / 01-10-2010 / 16:19:17 / cg"
!

enableGotoSmalltalkDirectory
    ^ self aspectFor:#enableGotoSmalltalkDirectory ifAbsent:[ true asValue ].
!

enableGotoTempDirectory
    ^ self aspectFor:#enableGotoTempDirectory ifAbsent:[ true asValue ].

    "Created: / 29-12-2010 / 11:01:17 / cg"
!

enableHome

    ^ self aspectFor:#enableHome ifAbsent:[ true asValue ].
!

enableMakeCurrentDirectory
    ^ self aspectFor:#enableMakeCurrentDirectory ifAbsent:[ true asValue ].

    "Created: / 26-10-2010 / 17:17:58 / cg"
!

enableViewNoteBookApplication

    ^ self aspectFor:#enableViewNoteBookApplication ifAbsent:[false asValue]
!

fileEncoding
    ^ self fileEncodingHolder value
!

fileEncoding:newEncoding
    self fileEncodingHolder value:newEncoding.

    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:nb | 
            |appl|

            appl := nb selectedApplication.
            (appl notNil and:[appl isTextEditor]) ifTrue:[
                appl fileEncoding:newEncoding
            ]
        ].
!

fileEncodingHolder
    ^ self 
        aspectFor:#fileEncodingHolder 
        ifAbsent:[
            self 
                applicationNamed:#FileApplicationNoteBook
                ifPresentDo:[:appl | appl fileEncodingHolder].
        ]
!

firstSelectedFileName
    |files|

    files := self currentSelectedObjects.
    files notEmpty ifTrue:[
        ^ files first
    ].
    ^ nil

    "Modified: / 04-12-2006 / 13:14:53 / cg"
!

gotoDefaultDirectoryIsVisible
    ^ true.
"/    ^ (Filename defaultDirectory asAbsoluteFilename) ~= (self smalltalkDirectory asAbsoluteFilename).

    "Modified: / 26-10-2010 / 17:23:51 / cg"
!

gotoDesktopDirectoryIsVisible
    Filename desktopDirectory = Filename homeDirectory ifTrue:[^ false].

    ^ (Filename desktopDirectory asAbsoluteFilename) ~= (self smalltalkDirectory asAbsoluteFilename).

    "Created: / 01-10-2010 / 16:26:27 / cg"
!

hasFileSelection
    ^ self aspectFor:#hasFileSelection ifAbsent:[ false asValue ].
!

hasNotTwoDirectoriesSelectedHolder
    ^ self 
        aspectFor:#hasNotTwoDirectoriesSelectedHolder
        ifAbsent:[
            BlockValue 
                with:[:m | self hasTwoDirectoriesSelected not]
                argument:self currentFileNameHolder
        ].

    "Created: / 20-05-2010 / 10:45:50 / cg"
!

hasNotTwoFilesSelectedHolder
    ^ self 
        aspectFor:#hasNotTwoFilesSelectedHolder
        ifAbsent:[
            BlockValue 
                with:[:m | self hasTwoFilesSelected not]
                argument:self currentFileNameHolder
        ].

    "Modified: / 20-05-2010 / 10:46:01 / cg"
!

hasSelection

    ^ self aspectFor:#hasSelection ifAbsent:[ false asValue ].
!

hasTwoDirectoriesSelectedHolder
    ^ self 
        aspectFor:#hasTwoDirectoriesSelectedHolder
        ifAbsent:[
            BlockValue 
                with:[:m | self hasTwoDirectoriesSelected]
                argument:self currentFileNameHolder
        ].

    "Created: / 20-05-2010 / 10:45:41 / cg"
!

hasTwoFilesSelectedHolder
    ^ self 
        aspectFor:#hasTwoFilesSelectedHolder
        ifAbsent:[
            BlockValue 
                with:[:m | self hasTwoFilesSelected]
                argument:self currentFileNameHolder
        ].

    "Modified: / 20-05-2010 / 10:46:08 / cg"
!

lockFileEncoding:aBoolean
    self lockFileEncodingHolder value:aBoolean.

    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:nb | 
            |appl|

            appl := nb selectedApplication.
            (appl notNil and:[appl isTextEditor]) ifTrue:[
                appl lockFileEncoding:aBoolean
            ]
        ].
!

lockFileEncodingHolder
    ^ self 
        aspectFor:#lockFileEncodingHolder 
        ifAbsent:[
            self 
                applicationNamed:#FileApplicationNoteBook
                ifPresentDo:[:appl | appl lockFileEncodingHolder].
        ]
!

notify:aString
    self notifyChannel value:aString
!

notifyChannel

    ^ self aspectFor:#notifyChannel ifAbsent:['' asValue]
!

progressPercentageHolder
    ^ self aspectFor:#progressPercentageHolder ifAbsent:[ nil asValue ].

    "Created: / 11-10-2010 / 12:54:46 / cg"
!

rootHolder
    "holder, which keeps the rootHolder of the treeView
    "

    ^ self aspectFor:#rootHolder ifAbsent:[
        self class rootHolder asValue.
    ].
!

rootHolder:aHolder

    self aspectFor:#rootHolder put:aHolder
! !

!AbstractFileBrowser methodsFor:'aspects handling'!

aspectFor:something ifAbsent:aBlock
    "returns the model for an aspect; these are stored in a common dictionary"

    ^ self aspectFor:something ifAbsent:aBlock notPresentDo:nil
!

aspectFor:aKey ifAbsent:aBlock notPresentDo:notPresentBlock
    "returns the model for an aspect; these are stored in a common dictionary"

    |holder saveAspectItem aspect|

    holder := aspects at:aKey ifAbsent:[
        masterApplication notNil ifTrue:[
            holder := masterApplication aspectOrNil:aKey forSubApplication:self.
        ].
        holder isNil ifTrue:[
            saveAspectItem := self runtimeAspectValueFor:aKey.
            saveAspectItem notNil ifTrue:[
                saveAspectItem isHolder ifTrue:[
                    holder := ValueHolder with:saveAspectItem value 
                ] ifFalse:[
                    holder := saveAspectItem value 
                ].
            ] ifFalse:[
                aspect := self userPreferencesAspectValueFor:aKey.
                aspect notNil ifTrue:[ 
                    holder := aspect asValue
                ] ifFalse:[
                    holder := aBlock value.
                ]
            ].
        ].
        holder notNil ifTrue:[  
            notPresentBlock notNil ifTrue:[
                holder value:(notPresentBlock value:holder).
            ].
            aspects at:aKey put:holder
        ].
        holder
    ].
    ^ holder
!

aspectFor:something put:aValueHolder
    "stores the model for an aspect; these are stored in a common dictionary"

    aspects at:something put:aValueHolder
!

aspects
    "returns the common aspect dictionary"

    ^ aspects
!

runtimeAspectValueFor:something
    "returns the default aspect item from the class variable RuntimeAspects
    "
    ^ self class runtimeAspects at:something ifAbsent:nil.
!

saveAspectValues
    |saveAspects aspectValue|


    saveAspects := self class userPreferencesAspectList keys.
    saveAspects do:[ :aspectKey |
        aspectValue := (self perform:aspectKey) value.
        UserPreferences current 
            at: ('fileBrowser_',aspectKey asString) asSymbol 
            put: aspectValue.
    ].

    "Modified: / 04-09-2006 / 10:02:41 / cg"
!

saveRuntimeAspectValues
    |savedAspects dictionary value isHolder aspect|

    savedAspects := self class listOfRuntimeValuesToRemember.
    dictionary := Dictionary new.
    savedAspects do:[:aspectKey | 
        aspect := self perform:aspectKey.
        isHolder := aspect isValueModel.
        isHolder ifTrue:[
            value := aspect value
        ] ifFalse:[
            value := aspect
        ].
        dictionary at:aspectKey
            put:(SaveAspectItem withValue:value isHolder:isHolder)
    ].
    RuntimeAspects := dictionary.
!

userPreferencesAspectValueFor:something
    "returns the default aspect from the Userpreferences
    "

    (self class userPreferencesAspectList includesKey:something) ifFalse:[ ^ nil].
    ^ UserPreferences current 
        at:('fileBrowser_',something) asSymbol
        ifAbsent:nil "(self class userPreferencesAspectList at:something)".

    "Modified: / 14-10-2010 / 19:16:38 / cg"
! !

!AbstractFileBrowser methodsFor:'aspects-filter'!

filter:aString
    self filterModel value:aString
!

filterBackgroundColor

    ^ self aspectFor:#filterBackgroundColor ifAbsent:[Color white asValue]
!

filterBlockHolder

    ^ self aspectFor:#filterBlockHolder ifAbsent:[self makeFilterBlock asValue]
!

filterListModel

    ^ self aspectFor:#filterListModel ifAbsent:[self class defaultFilterList]
!

filterModel
    |m|

    m := self aspectFor:#filterModel ifAbsent:[(self filterListModel at:1) asValue].
    ^ m
!

filterModel:aHolder
    self aspectFor:#filterModel put:aHolder.
!

filterValueBox

    ^ self aspectFor:#filterValueBox ifAbsent:[ValueHolder new]
!

makeFilterBlock
    "return a two-arg filterblock on the files path- and base-name. This block should return true for files
     to be shown"

    | filterString filterStrings filters showHidden yesOrNo filterBlock ignoreCase|

    filterString := self filterModel value.
    filterString = '' ifTrue:[filterString := '*'].
    ignoreCase := "ignoreCaseInPattern ? "(Filename isCaseSensitive not).

    filterStrings := filterString asCollectionOfSubstringsSeparatedBy:$;.
    filters := filterStrings
                collect:[:eachPattern |
                        |pattern|

                        pattern := eachPattern withoutSeparators.
                        yesOrNo := true.
                        (pattern startsWith:'~') ifTrue:[
                            yesOrNo := false.
                            pattern := pattern copyFrom:2.
                        ].
                        yesOrNo ifTrue:[
                            [:name :baseName | pattern match:baseName ignoreCase:ignoreCase]
                        ] ifFalse:[
                            [:name :baseName | (pattern match:baseName ignoreCase:ignoreCase) not]
                        ].
                    ].

    filters size == 1 ifTrue:[ 
        filterBlock := filters first 
    ] ifFalse:[
        filterBlock := [:name :baseName | filters contains:[:aFilter | aFilter value:name value:baseName ]].
    ].

    showHidden := self showHiddenFiles value.
    showHidden ifTrue:[
        ^ filterBlock.
    ].
    ^ [:name :baseName | 
                name isHidden not and:[filterBlock value:name value:baseName]].
!

shownFiles

    ^ self aspectFor:#shownFiles ifAbsent:['-/-' asValue]
! !

!AbstractFileBrowser methodsFor:'aspects-history'!

dirHistory
    "obsolete"
    ^ self directoryHistory
!

directoryHistory
    ^ self class directoryHistory
!

enableBack

    ^ self aspectFor:#enableBack ifAbsent:[false asValue]
!

enableFileHistory

    ^ self aspectFor:#enableFileHistory ifAbsent:[false asValue]
!

enableForward

    ^ self aspectFor:#enableForward ifAbsent:[false asValue]
!

fileHistory

    ^ self aspectFor:#fileHistory ifAbsent:[FilenameHistory new]
! !

!AbstractFileBrowser methodsFor:'aspects-visibility'!

activityVisibilityChannel
    " activityVisibilityChannel switches the activity indicator on/off"

    ^ self aspectFor:#activityVisibilityChannel ifAbsent:[ false asValue ].
!

changeSetBrowserItemVisible
    ^ Screen current ctrlDown.
!

openAlwaysInTextEditor
    " aspect for open every file in TextEditor dont use e.g. HtmlEditor for *.html'"

    ^ self aspectFor:#openAlwaysInTextEditor ifAbsent:[ false asValue ].
!

openMultipleApplicationsForType

    " aspect for open more applications for e.g. TextEditor and not change the contents of already 
      open TextEditor "

    ^ self aspectFor:#openMultipleApplicationsForType ifAbsent:[ false asValue ].
!

showDirectoryTree

    ^ self aspectFor:#showDirectoryTree ifAbsent:[ true asValue ]
!

showDiskUsageHolder
    |holder|

    holder := self aspectFor:#showDiskUsageHolder ifAbsent:[ false asValue].
    holder addDependent:self.
    ^ holder
!

showHiddenFiles

    ^ self aspectFor:#showHiddenFiles ifAbsent:[ true asValue ].

    "Modified: / 15-10-2010 / 10:28:17 / cg"
!

showHiddenFiles:aHolder
    self aspectFor:#showHiddenFiles put:aHolder.
!

shownFiles:aHolder
    self aspectFor:#shownFiles put:aHolder.
!

userContextAvailable

    " aspect for show group and user columns in contents view
      windows provides no user context"

    ^ OperatingSystem isMSWINDOWSlike not 
!

viewDescription
    "aspect for show file description in DirectoryContentsBrowser"

    ^ self directoryContentsBrowser viewDescription
!

viewDetails
    "aspect for show more file properties in DirectoryContentsBrowser 
    "

    |directoryContentsBrowser|

    directoryContentsBrowser := self directoryContentsBrowser.
    directoryContentsBrowser isNil ifTrue:[^ nil].    
    ^ directoryContentsBrowser viewDetails
!

viewDirectoryDescription

    " aspect for auto open a TextView for Readme and other see getInfoItem Method files 
      on change directory "

    ^ self aspectFor:#viewDirectoryDescription ifAbsent:[ true asValue ].
!

viewDirsInContentsBrowser

    ^ self aspectFor:#viewDirsInContentsBrowser ifAbsent:[ false asValue ].

    "
     UserPreferences current viewDirsInContentsBrowser
    "
!

viewFilesInContentsBrowser

    ^ self aspectFor:#viewFilesInContentsBrowser ifAbsent:[ true asValue ].
!

viewFilesInDirectoryTree

    " aspect for view files in tree view (not only directories) "

    ^ self aspectFor:#viewFilesInDirectoryTree ifAbsent:[ false asValue ].
!

viewGroup
    " aspect for show group information in DirectoryContentsBrowser "

    ^ self directoryContentsBrowser viewGroup
!

viewIcon
    " aspect for show file-type icon in DirectoryContentsBrowser "

    ^ self directoryContentsBrowser viewIcon
!

viewInodeNumber
    " aspect for show inode numbers in DirectoryContentsBrowser "

    ^ self directoryContentsBrowser viewInodeNumber
!

viewNoteBookApplicationHolder

    ^ self aspectFor:#viewNoteBookApplicationHolder ifAbsent:[ false asValue].
!

viewOwner
    " aspect for show owner information in DirectoryContentsBrowser "

    ^ self directoryContentsBrowser viewOwner
!

viewPermissions
    " aspect for show permission information in DirectoryContentsBrowser "

    ^ self directoryContentsBrowser viewPermissions
!

viewPreview
    " aspect for show image previev in DirectoryContentsBrowser "

    ^ self directoryContentsBrowser viewPreview
!

viewSize
    " aspect for show size information in DirectoryContentsBrowser "

    ^ self directoryContentsBrowser viewSize
!

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

    ^ self directoryContentsBrowser viewSizeInBytes
!

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

    ^ self directoryContentsBrowser viewSizeInKiloBytes
!

viewTime
    " aspect for show time information in DirectoryContentsBrowser"

    ^ self directoryContentsBrowser viewTime
!

viewType
    " aspect for show suffix (type) information in DirectoryContentsBrowser"

    ^ self directoryContentsBrowser viewType
! !

!AbstractFileBrowser methodsFor:'background processing'!

executeCommand:cmd

    self executeCommand:cmd inDirectory:nil
!

executeCommand:cmd inDirectory:aDirectoryOrNil
    | nameString executionBlock|

    executionBlock := self getExecutionBlockForCommand:cmd inDirectory:aDirectoryOrNil.
    nameString := 'Execute: ', cmd.
    self makeExecutionResultProcessFor:executionBlock withName:nameString.
!

getExecutionBlockForCommand:cmd

    ^ self getExecutionBlockForCommand:cmd inDirectory:nil
!

getExecutionBlockForCommand:cmd inDirectory:directoryOrNil
    | dir|

    dir := directoryOrNil.
    directoryOrNil isNil ifTrue:[
        dir := self theSingleSelectedDirectoryOrNil.
        dir isNil ifTrue:[ 
            Dialog warn:'Please select a single directory.'.
            AbortSignal raise.
            ^ nil
        ].
    ].

    ^ [:stream| 
        stream notNil ifTrue:[
            OperatingSystem 
                executeCommand:cmd
                inputFrom:nil 
                outputTo:stream 
                errorTo:stream 
                inDirectory:dir
                onError:[:status| false].
        ]
      ].
!

killAllRunningBackgroundProcesses

    self backgroundProcesses do:[ : process |
        self notify:'kill ', process name.
        process terminate.    
    ].
!

makeExecutionResultProcessFor:aBlock withName:aString
    | stream process appl nameString|

    appl := self openCommandResultApplication.
    stream := appl resultStream.
    nameString := aString ? 'Execution Result'.
    appl changeTabTo:nameString.

    process := [ 
                    [aBlock value:stream] 
                    ensure:[
                        self backgroundProcesses remove:process ifAbsent:[].
                        appl process value:nil.

                        "/ close automatically, if there was no output.
"/                        appl resultStream contents isEmpty ifTrue:[
"/                            appl doClose.
"/                        ]
                        stream nextPutLine:'Done.'
                    ]
               ] newProcess.
    process priority:(Processor userBackgroundPriority).
    process name:nameString.
    self backgroundProcesses add:process.
    appl process value:process.
    process resume.
! !

!AbstractFileBrowser methodsFor:'change & update'!

currentFileNameHolderChanged
    "/ self currentFileNameHolderChangedForCommon
!

currentFileNameHolderChangedForCommon
    |newDirectories oldDirectories size rootInTreeView selection selectionNotEmpty|

    selection := self currentSelectedObjects.
    self class currentSelection:selection.

    selectionNotEmpty := selection notEmpty.
    self hasSelection value:selectionNotEmpty.
    self hasFileSelection value:(selectionNotEmpty and:[self firstSelectedFile notNil]).

    newDirectories := self directoriesForFiles:selection.
    oldDirectories := self currentSelectedDirectories.
    oldDirectories ~= newDirectories ifTrue:[
        self currentDirectories value:newDirectories.
        size := newDirectories size.
        rootInTreeView := self 
                            applicationNamed:#DirectoryTreeBrowser 
                            ifPresentDo:[:appl | appl rootHolder].
        rootInTreeView := rootInTreeView value.
        self enableDirectoryUp value:(((size == 1) and:[newDirectories first isRootDirectory not]) "or:[(rootInTreeView notNil and:[rootInTreeView value asFilename isRootDirectory not])]").

        newDirectories notEmpty ifTrue:[
            self directoryHistory addToHistory:(newDirectories first asString).
        ].
        self enableHome value:((newDirectories includes:(Filename homeDirectory asAbsoluteFilename))not).
        self enableGotoDesktop value:((newDirectories includes:(Filename desktopDirectory asAbsoluteFilename))not).
        self enableGotoDefaultDirectory value:((newDirectories includes:(Filename defaultDirectory asAbsoluteFilename))not).
        self enableGotoSmalltalkDirectory value:((newDirectories includes:(self smalltalkDirectory asAbsoluteFilename))not).
        self enableGotoDesktopDirectory value:((newDirectories includes:(Filename desktopDirectory asAbsoluteFilename))not).
        self enableGotoTempDirectory value:((newDirectories includes:(self tempDirectory asAbsoluteFilename))not).
    ].
    self enableGotoDefaultDirectory value:(self currentDirectory isNil or:[self currentDirectory pathName ~= OperatingSystem getCurrentDirectory]).
    self enableMakeCurrentDirectory value:(newDirectories size == 1
                                           and:[ newDirectories first asFilename pathName ~= OperatingSystem getCurrentDirectory ]).
    self enableForward value:self canForward.
    self enableBack    value:self canBackward.

    self updateCanMake.

    "Modified: / 29-12-2010 / 11:01:52 / cg"
!

filterModelChanged

    self filterBlockHolder value:(self makeFilterBlock).
!

update:something with:aParameter from:changedObject

    " do here all the things that have to be done for every part of the FileBrowserV2
      and the things that have to be done if it runs standalone "

    changedObject == self currentFileNameHolder ifTrue:[
        self currentFileNameHolderChangedForCommon.
        ^ self
    ].
    changedObject == self sortCaseless ifTrue:[
        self sortFileListsBy:#baseName withReverse:false.
        ^ self
    ].             
    changedObject == self sortBlockProperty ifTrue:[
        self currentSortOrder value at:#reverse put:false.
        ^ self
    ].             
    (changedObject == self filterModel or:[changedObject == self showHiddenFiles]) ifTrue:[
        self filterModelChanged.
        ^ self
    ].
    changedObject == self rootHolder ifTrue:[
        self class rootHolder:(self rootHolder value).
        ^ self
    ].
    changedObject == self showDiskUsageHolder ifTrue:[
        self notify:nil.
        ^ self
    ].

    super update:something with:aParameter from:changedObject
!

updateCanMake
    |dir can|

    can := false.
    dir := self currentDirectory.
    dir notNil ifTrue:[    
        can := (dir asFilename construct:'Makefile') exists.
        OperatingSystem isMSWINDOWSlike ifTrue:[
            can ifFalse:[
                can := (self currentDirectory asFilename construct:'nt.mak') exists.
            ]
        ].
    ].
    self canMake value:can.
!

updateListAfterDelete:colOfFiles
    self updateCurrentDirectory
! !

!AbstractFileBrowser methodsFor:'clipboard'!

canPaste

    ^ self aspectFor:#canPaste ifAbsent:[ false asValue ].
!

clipboard

    ^ self aspectFor:#clipboard ifAbsent:[Clipboard new]
!

copyFilesToClipBoard:colOfFiles
    self putInClipBoard:colOfFiles as:#copy.
!

cutFilesToClipBoard:colOfFiles
    "defete current selected files/directories
    "
    self putInClipBoard:colOfFiles as:#cut.
!

emptyClipBoard

    self clipboard files:nil.
    self canPaste value:false.
!

putInClipBoard:colOfFiles as:aSymbol
    | stream clp|

    colOfFiles isEmpty ifTrue:[ ^ self].
    clp := self clipboard.
    clp files:nil.
    clp method:aSymbol.

    stream := String writeStream.
    stream nextPutAll:aSymbol asString.
    stream nextPutAll:' <'.
    stream nextPutAll:colOfFiles first asString.
    colOfFiles size > 1 ifTrue:[
        stream nextPutAll:' ...'.
    ].
    stream nextPutAll:'> to clipboard'.

    self notify:stream contents.
    stream close.
    clp files:colOfFiles.
    self canPaste value:true.
! !

!AbstractFileBrowser methodsFor:'drag & drop'!

canDropFiles:dropedObjects for:filename 
    |filenameDirString filenameDir|

    dropedObjects isEmpty ifTrue:[^ false].

    filenameDir := self getDirWithoutFileName:filename.
    filenameDir isNil ifTrue:[^ false ].
    filenameDir isWritable ifFalse:[^ false].

    filenameDirString := filenameDir asString.

    dropedObjects do:[:aDropObject | 
        (self canDropObject:aDropObject into:filenameDir) ifFalse:[^ false].
"/        |dropFileName dropFileNameString physicalPathName|
"/
"/        dropFileName := aDropObject theObject.
"/        dropFileNameString := dropFileName asString.
"/        dropFileName isDirectory ifTrue:[
"/            (self fileName:filenameDirString startsWith:dropFileNameString) ifTrue:[
"/                self notify:'Cannot drop a directory into one of its parent directories'.
"/                ^ false
"/            ]
"/        ] ifFalse:[
"/            physicalPathName := dropFileName physicalPathName.
"/            (filenameDirString = dropFileName directory asString 
"/            or:[ aDropObject isFileInArchive not and:[physicalPathName notNil
"/                 and:[ filenameDirString = physicalPathName asFilename directory asString]]]) ifTrue:[
"/                self notify:'Cannot drop a file into same directory'.
"/                ^ false
"/            ]
"/        ]
    ].
    self notify:nil.
    ^ true
!

canDropObject:aDropObject into:aDirectory
    |filenameDirString dropFileName dropFileNameString physicalPathName|

    dropFileName := aDropObject theObject.
    dropFileNameString := dropFileName asString.
    dropFileName isDirectory ifTrue:[
        filenameDirString := aDirectory asString.
        (self fileName:filenameDirString startsWith:dropFileNameString) ifTrue:[
            self notify:'Cannot drop a directory into one of its parent directories'.
            ^ false
        ]
    ] ifFalse:[
        physicalPathName := dropFileName physicalPathName.
        (aDirectory = dropFileName directory 
        or:[ aDropObject isFileInArchive not 
             and:[physicalPathName notNil
             and:[ aDirectory = physicalPathName asFilename directory]]]) ifTrue:[
            self notify:'Cannot drop a file into same directory'.
            ^ false
        ]
    ].
    ^ true.
!

canDropObjects:aCollectionOfDropObjects in:aWidget
    ^ aCollectionOfDropObjects contains:[:dropObject | dropObject isFileObject]

    "Created: / 13-10-2006 / 15:52:49 / cg"
!

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

    |hdl|

    hdl := DragAndDropManager new.

    hdl disabledCursor:self class disabledCursorImage.
    hdl enabledCursor:self class enabledCursorImage.
    hdl alienCursor:nil.

    hdl startDragFrom:aView dropSource:aDropSource offset:#topLeft.

    "Modified: / 23-07-2007 / 23:00:38 / cg"
!

dropObjects:aCollectionOfDropObjects in:aWidget at:position
    |fileEntryFieldHolder destDir dropObject fn d|

    fileEntryFieldHolder := self masterApplication notNil 
                              ifTrue:[ self masterApplication fileEntryFieldHolder ]
                              ifFalse:[ self fileEntryFieldHolder ].

    aWidget model == fileEntryFieldHolder ifTrue:[
        dropObject := aCollectionOfDropObjects first.
        dropObject isFileObject ifTrue:[
            fn := dropObject theObject asFilename.
            dropObject isDirectory ifTrue:[
                d := fn.
            ] ifFalse:[
                d := fn directory.
            ].
            fileEntryFieldHolder value:d pathName.
        ].
        ^ self.
    ].

    destDir := self currentDirectory.
    destDir isNil ifTrue:[^ self].

    self withWaitCursorDo:[
        aCollectionOfDropObjects do:[:dropObject |
            |fn|

            dropObject isFileObject ifTrue:[
                fn := dropObject theObject asFilename.
                dropObject isDirectory ifTrue:[
                    fn recursiveCopyTo:destDir
                ] ifFalse:[
                    fn copyTo:(destDir / fn baseName )
                ]
            ]
        ].
    ].
    self directoryContentsBrowser notNil ifTrue:[
        self directoryContentsBrowser 
            directoryContentsChangeFlag:true;
            wakeUp
    ].

    "Created: / 13-10-2006 / 18:26:41 / cg"
! !

!AbstractFileBrowser methodsFor:'file operations'!

copyFile:aSourceFile to:aDestFile
    "copy to"

    ^ self copyFile:aSourceFile to:aDestFile repairCorruptedFiles:false

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

copyFile:aSourceFile to:aDestFile repairCorruptedFiles:repairCorruptedFiles
    "copy to"

    |copyOperation msg|

    repairCorruptedFiles ifTrue:[
        copyOperation := FileOperation copyCorruptedFile:aSourceFile to:aDestFile
    ] ifFalse:[
        copyOperation := FileOperation copyFile:aSourceFile to:aDestFile withOverWriteWarning:true copyFileIfSame:true.
    ].
    copyOperation result ifTrue:[
        msg := ('copy ', aSourceFile baseName, ' to:,', aDestFile baseName).
    ] ifFalse:[
        msg := copyOperation errorString.
    ].
    self notify:msg.
    ^ copyOperation result

    "Created: / 07-02-2007 / 18:46:34 / cg"
!

copyFiles:aColOfSourceFiles to:aDirectory 
    |copy result|

    self withActivityIndicationDo:[
        copy := FileOperation copyFiles:aColOfSourceFiles to:aDirectory.
        copy result ifTrue:[
            result := Dictionary withKeys:(copy collectionOfCopiedFiles) andValues:(copy collectionOfNewFiles).
        ] ifFalse:[
            self notify:copy errorString.
        ].
        self updateCurrentDirectory.
    ].
    ^ result

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

copyOrMoveDialog:aCollectionOfFiles for:aDestinationDirectory
    |size stream msg answer file|

    size := aCollectionOfFiles size.

"/    stream := WriteStream on:'' asText.
"/    stream nextPutAll:'Copy or move'; cr; cr; nextPutAll:'file'.
"/    size == 1 ifFalse:[
"/        stream nextPutAll:'s'.
"/    ].
"/    stream nextPutAll:': '.
"/    stream nextPutAll:aCollectionOfFiles first baseName asString allBold.
"/    size == 1 ifFalse:[
"/        stream nextPutAll:' ... '.
"/        stream nextPutAll:aCollectionOfFiles last baseName asString.
"/    ].
"/    stream cr; nextPutAll:'to: '.
"/    stream nextPutAll:aDestinationDirectory asFilename pathName allBold.
"/    msg := stream contents
    size == 1 ifTrue:[
        file := aCollectionOfFiles first.
        msg := resources    
                stringWithCRs:'Copy or move\\%1:\    %2\to:\    %3 ?'
                with:(file type == #regular ifTrue:'file' ifFalse:[file isDirectory ifTrue:'directory' ifFalse:'object'])
                with:(file baseName allBold)
                with:(aDestinationDirectory asFilename pathName allBold).
    ] ifFalse:[
        msg := resources    
                stringWithCRs:'Copy or move\\    %1 objects\to:\    %2 ?'
                with:size
                with:(aDestinationDirectory asFilename pathName allBold).
    ].

    answer := OptionBox 
                  request:msg 
                  label:(resources string:'Copy or Move')
                  image:(WarningBox iconBitmap)
                  buttonLabels:(resources array:#('Cancel' 'Move' 'Copy' 'Copy As...'))
                  values:#(#cancel #move #copy #copyAs)
                  default:#copy.
    answer isNil ifTrue:[answer := #cancel].
    ^ answer.
!

copyOrMoveFiles:aColOfSourceFiles to:aDestinationDirectory 
    "copy or move aColOfSourceFiles to aDirectory.
     Asks the used if a move or a copy is wanted.
     Returns true if the copyOrMove happened, false if user aborted the operation."

    |answer lastOld lastNew|

    answer := self copyOrMoveDialog:aColOfSourceFiles for:aDestinationDirectory.
    answer == #copyAs ifTrue:[
        aColOfSourceFiles do:[:eachSourceFile |
            |initial destFile srcBase dstBase|

            srcBase := eachSourceFile asFilename baseName.
            lastOld isNil ifTrue:[
                initial := srcBase
            ] ifFalse:[
                initial := DoWhatIMeanSupport
                            goodRenameDefaultForFile:srcBase lastOld:lastOld lastNew:lastNew.
            ].
            dstBase := Dialog request:('Copy %1 as:' bindWith:srcBase) initialAnswer:initial.
            dstBase isNil ifTrue:[^ false].
            self copyFile:eachSourceFile to:(aDestinationDirectory construct:dstBase).
            lastOld := srcBase.
            lastNew := dstBase.
        ].

        ^ true.
    ].
    answer == #copy ifTrue:[
        self copyFiles:aColOfSourceFiles to:aDestinationDirectory.
        ^ true.
    ].
    answer == #move ifTrue:[
        self moveFiles:aColOfSourceFiles to:aDestinationDirectory.
        ^ true.
    ].
    ^ false.
!

copySelectionTo
    "copy the selected file(s) to another directory"

    self copySelectionToRepairingCorruptedFiles:false

    "Modified: / 07-02-2007 / 18:42:48 / cg"
!

copySelectionToRepairingCorruptedFiles
    "copy the selected file(s) to another directory"

    self copySelectionToRepairingCorruptedFiles:true

    "Created: / 07-02-2007 / 18:42:59 / cg"
!

copySelectionToRepairingCorruptedFiles:repairingCorruptedFiles
    "copy the selected file(s) to another directory"

    |selectedFiles msg destination directory|

    selectedFiles := self currentSelectedObjects copy.
    selectedFiles isEmptyOrNil ifTrue:[^ self].

    msg := (selectedFiles size > 1) 
                ifTrue:[ 'Copy Selected Items To Directory:' ]
                ifFalse:[ 'Copy "%1" To Directory:' ].

    destination := Dialog 
                    requestDirectoryName:(resources stringWithCRs:msg with:selectedFiles first pathName) 
                    default:(LastMoveDestination ? self currentDirectory)
                    ok:(resources string:'Copy')
                    abort:nil.

    destination isEmptyOrNil ifTrue:[^ self].
    destination := destination asFilename.

    destination isDirectory ifFalse:[
        selectedFiles size == 1 ifTrue:[
            directory := destination directory.
            directory isDirectory ifTrue:[
                LastMoveDestination := directory.
                self copyFile:selectedFiles first to:destination repairCorruptedFiles:repairingCorruptedFiles.
                ^ self.
            ]
        ]
    ].

    LastMoveDestination := destination.
    repairingCorruptedFiles ifTrue:[
        selectedFiles do:[:eachFile |    
            self copyFile:eachFile to:destination repairCorruptedFiles:repairingCorruptedFiles.
        ].
        ^ self.
    ] ifFalse:[
        self copyFiles:selectedFiles to:destination
    ]

    "Created: / 07-02-2007 / 18:42:35 / cg"
!

deleteFile:aFile
    "delete the selected files/directories"

    ^ self deleteFiles:(OrderedCollection with:aFile).
!

deleteFiles:colOfFiles
    "delete some files/directories"

    ^ self deleteFiles:colOfFiles confirm:true.
!

deleteFiles:colOfFiles confirm:confirm
    "delete some files/directories"

    |delete result|

    self withActivityIndicationDo:[
        ProgressNotification handle:[:info |
            self progressPercentageHolder value:info progressValue.
            info proceed
        ] do:[
            delete := FileOperation deleteFiles:colOfFiles confirm:confirm.
            result := delete result.
        ].
        result notNil ifTrue:[
            result ifFalse:[
                self notify:delete errorString.
            ] ifTrue:[
                self updateListAfterDelete:colOfFiles.
            ]
        ].
    ].
    ^ result.

    "Modified: / 11-10-2010 / 13:08:24 / cg"
!

eraseFiles:colOfFiles
    "erase (clear and delete) some files/directories"

    ^ self eraseFiles:colOfFiles confirm:true.
!

eraseFiles:colOfFiles confirm:confirm
    "erase (clear and delete) some files/directories"

    |deleteOperation result fileSize|

    self withActivityIndicationDo:[
        fileSize := colOfFiles first asFilename fileSize.
        deleteOperation := FileOperation eraseFiles:colOfFiles confirm:confirm.
        (colOfFiles size == 1 and:[ fileSize < (10*1024*1024) ])
        ifTrue:[
            result := deleteOperation result.
        ] ifFalse:[
            ProgressIndicator
                displayProgressNotifications:'Erasing' 
                abortable:true 
                at:nil 
                during:[
                    result := deleteOperation result.
                ].
        ].

        result notNil ifTrue:[
            result ifFalse:[
                self notify:deleteOperation errorString.
            ] ifTrue:[
                self updateListAfterDelete:colOfFiles.
            ]
        ].
    ].
    ^ result.

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

moveFile:aSourceFile to:aDestFile
    "move a file/directory"

    |move|

    move := FileOperation moveFile:aSourceFile to:aDestFile.
    move result ifTrue:[
        self notify:('move ', aSourceFile asString, ' to ', aDestFile asString).
    ] ifFalse:[
        self notify:move errorString.
    ].
    self updateCurrentDirectory.
    ^ move result
!

moveFiles:aColOfSourceFiles to:aDirectory 
   "move some files to aDirectory"

    |move|

    self withActivityIndicationDo:[
        move := FileOperation moveFiles:aColOfSourceFiles to:aDirectory.
        move result ifFalse:[
            self notify:move errorString.
        ].
        self updateCurrentDirectory.
    ].
    ^ move collectionOfMovedFiles

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

moveSelectionTo
    "move the selected file(s) to another directory"

    |destinationDirectory|

    destinationDirectory := Dialog 
                                requestDirectoryName:(resources stringWithCRs:'Move Selected Items To:') 
                                default:(LastMoveDestination ? self currentDirectory)
                                ok:(resources string:'Move')
                                abort:nil.
    destinationDirectory isEmptyOrNil ifTrue:[^ self].

    LastMoveDestination := destinationDirectory.
    self moveFiles:(self currentSelectedObjects copy) to:destinationDirectory asFilename

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

newDirectory
    "ask for and create a new directory"

    |selectedFiles singleSelectedFile defaultAnswer directory createOp newFile mime|

    directory := self currentDirectory.
    directory isNil ifTrue:[
        self warn:'Select a single directory to create a directory'.
        ^ self
    ].

    "/ clever default if selection is an archive...
    selectedFiles := self currentSelectedFiles.
    selectedFiles size == 1 ifTrue:[
        singleSelectedFile := selectedFiles first.

        mime := MIMETypes mimeTypeForFilename:singleSelectedFile.
        (mime notNil and:[mime isArchive]) ifTrue:[
            defaultAnswer := singleSelectedFile asFilename withoutSuffix baseName.

            mime := MIMETypes mimeTypeForFilename:defaultAnswer.
            (mime notNil and:[mime isArchive]) ifTrue:[
                defaultAnswer := defaultAnswer asFilename withoutSuffix baseName
            ].
        ].
    ].


    defaultAnswer notNil ifTrue:[
        createOp := FileOperation createDirectoryIn:directory initialAnswer:defaultAnswer.
    ] ifFalse:[
        createOp := FileOperation createDirectoryIn:directory.
    ].
    createOp result ifFalse:[ ^ self].
    newFile := createOp createdFile.
    newFile notNil ifTrue:[
        self updateCurrentDirectory.
    ]
!

newFile
    "ask for and create a new file"

    | curFile directory create file|

    directory := self currentDirectory.
    directory isNil ifTrue:[
        self warn:'Select a single directory to create a file.'.
        ^ self.
    ].

    curFile := self firstSelectedFile.
    curFile notNil ifTrue:[
        file := curFile.
    ] ifFalse:[
        file := directory.
    ].
    create := FileOperation createFileIn:file.
    create result ifFalse:[ ^ self].
    self updateAndSelect:(OrderedCollection with:(create createdFile)).
!

newHardLink
    "ask for and create a hard link (unix only)"

    self newLink:#hard.
!

newLink:typeOfLink
    "ask for and create a symbolic if symbolic is true otherwise a hard link (unix only)"

    | sel dir create createdFile|

    sel := self currentSelectedObjects.
    (sel size ~= 1) ifTrue:[
        self warn:'Select one directory to link'.
        ^ self
    ].
    dir := sel first.

    create := FileOperation createLinkIn:dir soft:(typeOfLink == #soft).
    create result ifFalse:[ ^ self].
    createdFile := create createdFile.
    createdFile notNil ifTrue:[
        typeOfLink == #soft ifTrue:[
            self updateCurrentDirectory.
        ] ifFalse:[
            self updateAndSelect:(OrderedCollection with:createdFile).
        ]
    ].

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

newSoftLink
    "ask for and create a soft link (unix only)"

    self newLink:#soft.
!

pasteFiles
    "paste from clipBoard"

    |buffer destination files directories copiedFiles thisIsAFileMoveOperation|

    files := self clipboard files.
    files isEmptyOrNil ifTrue:[ ^ self ].

    thisIsAFileMoveOperation := self clipboard method == #cut.

    buffer := self clipboard copy.
    directories := self currentSelectedDirectories.
    directories size ~~ 1 ifTrue:[
        |box dirStringCol|

        dirStringCol := directories collect:[:aDir| aDir asString].
        box := ListSelectionBox new.
        box title:'Paste into which directory ?'.
        box list:dirStringCol.
        box okAction:[:sel | destination := sel asFilename].
        box show.
        box destroy.
    ] ifFalse:[
        destination := directories anElement.
    ].

    thisIsAFileMoveOperation ifTrue:[
        buffer files copy do:[:aFile|
            aFile directory = destination ifTrue:[
                buffer files remove:aFile.
            ].
        ].
    ].
    copiedFiles := self copyFiles:(buffer files) to:destination.
    copiedFiles notEmptyOrNil ifTrue:[    
        thisIsAFileMoveOperation ifTrue:[
            self deleteFiles:(copiedFiles keys) confirm:false.
        ]. 

        "/ select those pasted files.
        self updateCurrentDirectory:true.
        self currentFileNameHolder value:copiedFiles values.
    ]
!

renameFile:aFile 
    "rename the selected file(s)"

    ^ self renameFiles:(OrderedCollection with:aFile).
!

renameFile:filename to:newFileString update:aBoolean
    "rename filename to newFileString"

    |rename|

    rename := FileOperation renameFile:filename to:newFileString.
    rename result ifTrue:[
        aBoolean ifTrue:[
            self updateAndSelect:(rename renamedFiles).
        ].
    ] ifFalse:[
        self notify:rename errorString.
    ].
    ^ rename result
!

renameFiles:aColOfFiles 
    "rename some file(s)"

    |rename|

    rename := FileOperation renameFiles:aColOfFiles.
    rename result ifFalse:[
        self notify:rename errorString
    ] ifTrue:[
        self updateAndSelect:nil. "/ (rename renamedFiles)
    ].
    ^ rename result
!

renameSelection
    "rename the selected file(s)"

    self renameFiles:(self currentSelectedObjects copy)

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

!AbstractFileBrowser methodsFor:'menu accessing'!

bookmarksMenu
    <resource: #programMenu>

    |menu bookmarks|

    menu := self class baseBookmarksMenuSpec decodeAsLiteralArray.

    "/ add the bookmark items ...
    bookmarks := self class directoryBookmarks.
    bookmarks notEmptyOrNil ifTrue:[
        menu addItem:(MenuItem labeled:'-').
        bookmarks do:[:dirName |
            menu addItem:((MenuItem label:dirName asString value:[
                (self currentSelectedDirectories includes:dirName) ifFalse:[
                    self setCurrentFileName:dirName.
                ].
            ])).
        ].
    ].
    menu findGuiResourcesIn:self.
    ^ menu

    "Modified: / 27-03-2007 / 10:55:08 / cg"
!

canBackward
    ^ self directoryHistory canBackward.

    "Modified: / 27-03-2007 / 10:54:55 / cg"
!

canForward
    ^ self directoryHistory canForward.

    "Modified: / 27-03-2007 / 10:54:57 / cg"
!

gotoBookmarksMenu
    <resource: #programMenu>

    |menu bookmarks|

    menu := self class baseBookmarksMenuSpec2 decodeAsLiteralArray.

    "/ add the bookmark items ...
    bookmarks := self class directoryBookmarks.
    bookmarks notEmptyOrNil ifTrue:[
        menu addItem:(MenuItem labeled:'-').
        bookmarks do:[:dirName |
            menu addItem:((MenuItem label:dirName asString value:[
                (self currentSelectedDirectories includes:dirName) ifFalse:[
                    self setCurrentFileName:dirName.
                ].
            ])).
        ].
    ].
    menu findGuiResourcesIn:self.
    ^ menu

    "Modified: / 27-03-2007 / 10:54:50 / cg"
!

menuDirHistory:backOrForward
    "initialize the history menu"

    <resource: #programMenu >

    |hist menu pathList currentSel currentPath|

    hist := self directoryHistory.
    hist isEmpty ifTrue:[^ nil].

    backOrForward == #back ifTrue:[
        currentSel := self currentSelectedDirectories.
        currentSel size == 1 ifTrue:[
            currentPath := currentSel first asString.
        ] ifFalse:[
            currentPath := nil.
        ].
        pathList := hist getBackCollection.
    ] ifFalse:[
        pathList := hist getForwardCollection.
    ].
    pathList isEmpty ifTrue:[ ^ nil].

    pathList size > 30 ifTrue:[
        pathList := pathList copyTo:30
    ].

    menu := Menu new.

    pathList do:[:aPath| 
        | menuItem |
        menuItem := MenuItem new.
        menuItem label:aPath.
        menuItem value:[
            self setCurrentFileName:(aPath asFilename).
        ].
        menu addItem:menuItem.
    ].
    menu findGuiResourcesIn:self.
    ^ menu

    "Modified: / 27-03-2007 / 10:54:29 / cg"
!

menuDirHistoryBack
    "initialize the directory menu
    "
    <resource: #programMenu >

    ^ self menuDirHistory:#back.
!

menuDirHistoryForward
    "initialize the directory menu
    "
    <resource: #programMenu >

    ^ self menuDirHistory:#forward.
!

menuFileHistory
    "initialize the file history menu
    "
    <resource: #programMenu >

    |menu hist text removeItem|

    hist := self fileHistory.
    hist isEmpty ifTrue:[^ nil].

    menu := Menu new.

    hist copy do:[:aFileItem|
        aFileItem fileName exists ifTrue:[
            menu addItem:(MenuItem label: aFileItem fileName asString value:[
                self setCurrentFileName:(aFileItem fileName).
                self openApplByFileItem:aFileItem
            ]).
        ] ifFalse:[
            "/ remove all not existing history entries
            hist remove:aFileItem.
        ]
    ].
    menu addSeparator.

    removeItem := MenuItem new.
    removeItem translateLabel:true.
    text := resources string:'Clear History'.
    "/ text := LabelAndIcon icon:(self class clearHistoryIcon) string:text.
    removeItem label:text.
    removeItem value:[
        self fileHistory removeAll.
        self enableFileHistory value:false.
    ].
    menu addItem:removeItem.
    menu findGuiResourcesIn:self.
    ^ menu

    "Modified: / 27-03-2007 / 10:52:40 / cg"
!

sortMenu
    <resource: #programMenu >

    |menu|

    menu :=  Menu decodeFromLiteralArray:self class sortMenu.
    menu findGuiResourcesIn:self.
    ^ menu

    "Modified: / 27-03-2007 / 10:47:42 / cg"
!

viewDetailsMenuSpec
    |specContentsBrowser itemsContentsBrowser specHere itemsHere spec| 

    specHere := self class viewDetailsMenuSpec.
    itemsHere := specHere at:2.

    specContentsBrowser := self directoryContentsBrowser class viewBrowserMenuSpec.
    itemsContentsBrowser := (specContentsBrowser at:2).

    spec := specHere copy.
    spec at:2 put:(itemsContentsBrowser , itemsHere).
    ^ spec
!

viewInContentsBrowserMenu
    self 
        applicationNamed:#DirectoryContentsBrowser 
        ifPresentDo:[:appl | ^ appl viewBrowserMenu].

    ^ nil.
!

visitedDirectoriesMenu
    <resource: #programMenu >

    |menu histCopy text removeItem|

    histCopy := self directoryHistory.
    histCopy isEmpty ifTrue:[^ nil].

    menu := Menu new.

    histCopy do:[:aFile| 
        menu addItem:(MenuItem label:aFile asString value:[
            self setCurrentFileName:(aFile path asFilename).
        ]).
    ].
    menu addSeparator.

    "/ text := LabelAndIcon icon:(self class clearHistoryIcon) string:(resources string:'Clear History').
    text := (resources string:'Clear History').
    removeItem := MenuItem new.
    removeItem translateLabel:true.
    removeItem label:text.
    removeItem value:[
        self directoryHistory removeAll.
        self enableForward value:self canForward.
        self enableBack value:self canBackward.
    ].

    menu addItem:removeItem.
    menu findGuiResourcesIn:self.
    ^ menu

    "Modified: / 27-03-2007 / 10:50:39 / cg"
! !

!AbstractFileBrowser methodsFor:'menu actions'!

doCompareTwoFiles
    self openDiffView.
!

doGoDirectoryUp
    | upDir directory rootInTreeView|

    self enableDirectoryUp value ifFalse:[ ^ self].
    self currentFilesAreInSameDirectory ifTrue:[
        directory := self currentDirectory.
        directory isNil ifTrue:[ ^ self].
    ] ifFalse:[
        rootInTreeView := self 
                            applicationNamed:#DirectoryTreeBrowser 
                            ifPresentDo:[:appl | appl rootHolder].
        directory := rootInTreeView value asFilename.
    ].
    upDir := directory directory.
    self setCurrentFileName:upDir.
!

doGotoDefaultDirectory
    self gotoFile:(Filename defaultDirectory).
!

doGotoDesktopDirectory
    self gotoFile:(Filename desktopDirectory).
!

doGotoHomeDirectory
    self gotoFile:(Filename homeDirectory).
!

doGotoSmalltalkDirectory
    self gotoFile:(self smalltalkDirectory).
!

doGotoTempDirectory
    self gotoFile:(self tempDirectory).

    "Created: / 29-12-2010 / 11:03:17 / cg"
!

doMakeCurrentDirectory
    OperatingSystem setCurrentDirectory:(self currentDirectory pathName).
    self currentFileNameHolderChangedForCommon.

    "Created: / 26-10-2010 / 17:21:19 / cg"
!

fileGetInfo:longInfo 
    "get info on selected file - show it in a box"

    |string box updater|

    string := self getFileInfoStringForFirstSelectedFile:longInfo.
    string notNil ifTrue:[
        box := InfoBox title:string.
        updater := [
                    [true] whileTrue:[
                        Delay waitForSeconds:2.
                        string := self getFileInfoStringForFirstSelectedFile:longInfo.
                        string isNil ifTrue:[ ^ self].
                        box title:string
                    ]
                ] fork.
        box show.
        updater terminate.
        box destroy
    ]
!

smalltalkDirectory
    |stxPath|

    stxPath := OperatingSystem pathOfSTXExecutable.
    stxPath isNil ifTrue:[
        ^ Filename currentDirectory
    ].
    ^ stxPath asFilename directory.
!

tempDirectory
    ^ Filename tempDirectory.

    "Created: / 29-12-2010 / 11:03:00 / cg"
! !

!AbstractFileBrowser methodsFor:'menu actions-cvs'!

cvsAddAndCommit
    self cvsAddAndCommitAsBinary:false
!

cvsAddAndCommitAsBinary:asBinary
    |sel log logArg binArg cmd dir executionBlock nameString|

    log := Dialog
        requestText:(resources string:'Enter initial log message')
        lines:10
        columns:70
        initialAnswer:nil.
    log isNil ifTrue:[^ self].

    OperatingSystem isMSWINDOWSlike ifTrue:[
        logArg := '-m "' , log , '"'.
    ] ifFalse:[
        logArg := '-m ''' , log , ''''.
    ].

    binArg := ''.
    asBinary ifTrue:[
        binArg := '-kb '.
    ].
    sel := self currentSelectedFiles.
    executionBlock := [ : stream |
        log notNil ifTrue:[
            sel size > 0 ifTrue:[
                sel do:[:fn |
                    |nameArg|

                    nameArg := '"',fn baseName,'"'.

                    dir := self getDirWithoutFileName:fn.
                    cmd := 'cvs add ',logArg,' ',binArg,nameArg.
                    (self getExecutionBlockForCommand:cmd inDirectory:dir) value:stream.

                    cmd := ('cvs commit -l ',logArg,' ',nameArg).
                    (self getExecutionBlockForCommand:cmd inDirectory:dir) value:stream.
                    "/ mhmh - it seems that only old CVS implementations (at least turqoise)
                    "/ support and need the 'admin -kb' command.
                    "/ newer ones use the '-kb' option in the 'cvs add' command
                    asBinary ifTrue:[
                        cmd := ('cvs admin -kb ' , nameArg).
                        (self getExecutionBlockForCommand:cmd inDirectory:dir) value:stream.

                        cmd := ('cvs upd ' , nameArg).
                        (self getExecutionBlockForCommand:cmd inDirectory:dir) value:stream.
                    ].
                ]
            ] 
        ]
    ].
    nameString := 'Command> cvs add and commit'.
    self makeExecutionResultProcessFor:executionBlock withName:nameString.
!

cvsAddBinaryAndCommit
    self cvsAddAndCommitAsBinary:true
!

cvsCommit
    |nSel log logArg msg cmd selectedFiles sel executionBlock nameString|

    selectedFiles:= self currentSelectedFiles.
    nSel := selectedFiles size.

    nSel == 1 ifTrue:[
        msg := resources string:'Enter log message for checkIn of "%1"' with:(selectedFiles first baseName)
    ] ifFalse:[
        nSel > 1 ifTrue:[
            msg := resources string:'Enter log message for %1 files to checkIn' with:nSel printString
        ] ifFalse:[
            msg := resources string:'Enter log message for checkIn'
        ]
    ].

    log := Dialog
        requestText:msg
        lines:10
        columns:70
        initialAnswer:nil.

    log isNil ifTrue:[^ self].

    OperatingSystem isMSWINDOWSlike ifTrue:[
        logArg := '-m "' , log , '"'.
    ] ifFalse:[
        logArg := '-m ''' , log , ''''.
    ].

    sel := self currentSelectedObjects.
    sel isEmpty ifTrue:[ ^ self ].

    executionBlock := [:stream |
        log notNil ifTrue:[
            sel size > 0 ifTrue:[
                sel do:[:fn |
                    | dir nameArg |

                    nameArg := '"',fn baseName,'"'.
                    dir := fn directory.
                    cmd := 'cvs commit ',logArg,' ' , nameArg.
                    (self getExecutionBlockForCommand:cmd inDirectory:dir) value:stream.
                ]
            ] 
        ]
    ].
    nameString := 'Command> cvs commit'.
    self makeExecutionResultProcessFor:executionBlock withName:nameString.

    "Modified: / 04-12-2006 / 13:16:39 / cg"
!

cvsCompareWithNewest
    |selectedFiles|

    selectedFiles:= self currentSelectedFiles.
    selectedFiles do:[:each |
self halt.
    ].
!

cvsRemoveAndRemoveFromCVS:filesToRemove
    "remove the selected file(s) and their CVS containers - no questions asked"

    |toRemove updateRunning executionBlock nameString|

    updateRunning := self backgroundProcesses value notEmpty.
    self killAllRunningBackgroundProcesses.
    toRemove := OrderedCollection new.

    executionBlock := [ : stream |
        |cmd logArg|

        OperatingSystem isMSWINDOWSlike ifTrue:[
            logArg := '-m "' , 'removed via FileBrowser' , '"'.
        ] ifFalse:[
            logArg := '-m ''' , 'removed via FileBrowser' , ''''.
        ].

        filesToRemove do:[:fileName |
            OperatingSystem accessDeniedErrorSignal handle:[:ex|
                "was not able to remove it"
                | lastError msg |
                lastError := OperatingSystem lastErrorString.
                msg := (resources string:'cannot remove ''%1'' !!' with:fileName).
                lastError isNil ifFalse:[
                    msg := msg , '\\(' , lastError , ')'
                ].
                Dialog warn:msg withCRs
            ] do:[
                (fileName isSymbolicLink) ifFalse:[
                    fileName remove.
                    cmd := ('cvs remove -f "',fileName baseName,'"').
                    (self getExecutionBlockForCommand:cmd) value:stream.
                ]
            ].
        ].
        cmd := 'cvs commit -l ',logArg.
        (self getExecutionBlockForCommand:cmd) value:stream.
    ].
    nameString := 'Command> cvs remove and commit ', filesToRemove first baseName.
    filesToRemove size > 1 ifTrue:[
        nameString := nameString, ' ...'.
    ].
    self makeExecutionResultProcessFor:executionBlock withName:nameString.
!

cvsRemoveFileAndCVSContainer
    |sel question aswer|

    sel := self currentSelectedFiles copy.
    sel size > 0 ifTrue:[
        sel size > 1 ifTrue:[
            question := resources string:'Remove %1 selected files and their CVS containers ?' with:(sel size)
        ] ifFalse:[
            question := resources string:'Remove ''%1'' and its CVS container ?' with:(sel first baseName allBold)
        ].

        aswer := Dialog 
                confirm:question withCRs
                yesLabel:(resources string:'Remove')
                noLabel:(resources string:'Cancel').
        aswer ifTrue:[
            self withActivityIndicationDo:[
                self cvsRemoveAndRemoveFromCVS:sel
            ]
        ]
    ]

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

cvsRevisionLog
    | cmd sel executionBlock nameString|

    sel := self currentSelectedObjects.
    sel isEmpty ifTrue:[ ^ self ].

    executionBlock := [:stream |
        sel do:[:fn |
            | dir nameArg |

            nameArg := '"',fn baseName,'"'.
            dir := fn directory.
            cmd := 'cvs log ' , nameArg.
            (self getExecutionBlockForCommand:cmd inDirectory:dir) value:stream.
        ]
    ].
    nameString := 'Command> cvs log'.
    self makeExecutionResultProcessFor:executionBlock withName:nameString.
!

cvsTagSelection
    |tag stream cmd|

    tag := Dialog request:(resources string:'Tag:').

    CVSSourceCodeManager notNil ifTrue:[
        cmd := CVSSourceCodeManager cvsExecutable.
    ] ifFalse:[
        cmd := 'cvs'.
    ].

    stream := WriteStream on:''.
    stream 
        nextPutAll:cmd; 
        nextPutAll:' tag -F ';
        nextPutAll:'"'; 
        nextPutAll:tag; 
        nextPutAll:'" ';
        nextPutAll:(self makeFileNameArgumentString).

    self executeCommand:stream contents.
!

cvsUpdateAll
    | cmd |

    CVSSourceCodeManager notNil ifTrue:[
        cmd := CVSSourceCodeManager cvsExecutable.
    ] ifFalse:[
        cmd := 'cvs'.
    ].

    self executeCommand:(cmd, ' upd -l').
!

cvsUpdateAllRecursive
    | cmd |

    CVSSourceCodeManager notNil ifTrue:[
        cmd := CVSSourceCodeManager cvsExecutable.
    ] ifFalse:[
        cmd := 'cvs'.
    ].
    self executeCommand:(cmd, ' upd -d').
!

cvsUpdateSelection
    |stream cmd|

    CVSSourceCodeManager notNil ifTrue:[
        cmd := CVSSourceCodeManager cvsExecutable.
    ] ifFalse:[
        cmd := 'cvs'.
    ].

    stream := WriteStream on:''.
    stream 
        nextPutAll:cmd; 
        nextPutAll:' upd ';
        nextPutAll:(self makeFileNameArgumentString).

    self executeCommand:stream contents.
!

makeFileNameArgumentString
    |stream|

    stream := WriteStream on:''.
    self currentSelectedFiles do:[: file |
        stream nextPut:$".
        stream nextPutAll:file baseName.
        stream nextPutAll:'" '.
    ].
    ^ stream contents.
! !

!AbstractFileBrowser methodsFor:'menu actions-file'!

copyFiles
    self 
        withSelectedFilesOrDirectoriesDo:[:filesOrDirs | self copyFilesToClipBoard:filesOrDirs]
!

cutFiles
    self 
        withSelectedFilesOrDirectoriesDo:[:filesOrDirs | self cutFilesToClipBoard:filesOrDirs]
!

deleteFiles
    self 
        withSelectedFilesOrDirectoriesDo:[:filesOrDirs | self deleteFiles:filesOrDirs]
!

eraseFiles
    self 
        withSelectedFilesOrDirectoriesDo:[:filesOrDirs | self eraseFiles:filesOrDirs]
!

openSettingsDialog
    |dialog|

    dialog := FileBrowserV2SettingsDialog new.
    dialog settingsDialog:self.
    dialog allButOpen.
    dialog doReload.
    dialog openWindow
!

withSelectedFilesOrDirectoriesDo:aBlock
    |files dirs|

    files := self currentSelectedFiles.
    files notEmpty ifTrue:[
        aBlock value:files.        
    ] ifFalse:[
        dirs := self currentSelectedDirectories.
        dirs notEmpty ifTrue:[
            aBlock value:dirs.        
        ].
    ].
! !

!AbstractFileBrowser methodsFor:'menu actions-help'!

openAboutThisApplication
    "opens an about box for this application."

    Dialog aboutClass:self class.
!

openHTMLDocument:relativeDocPath
    HTMLDocumentView openFullOnDocumentationFile:relativeDocPath
! !

!AbstractFileBrowser methodsFor:'menu actions-tools'!

allFilesInSelectedDirectoriesForWhich:aBlock
    |directories allFiles|

    directories := self currentSelectedDirectories.
    directories isEmpty ifTrue:[^ self].

    allFiles := OrderedCollection new.
    directories do:[:dir|
        [
            |fileNames|

            fileNames := dir directoryContents.
            fileNames notNil ifTrue:[
                fileNames := fileNames 
                                collect:[:fn | dir construct:fn]
                                thenSelect:[:fn | fn isDirectory not and:[aBlock value:fn]].
                allFiles addAll:fileNames.
            ]
        ] on:FileStream openErrorSignal do:[:ex|
            self warn:(resources stringWithCRs:'Cannot access: %1\(%2)' 
                            with:ex pathName
                            with:ex description).
            ex proceedWith:nil.
        ].
    ].

    ^ allFiles
!

conversionChainFrom:inSuffix to:outSuffix
    |conv|

    inSuffix = outSuffix ifTrue:[
        ^ nil
    ].

    "/ q&d hack to get my images converted for old html-browsers...
    "/ this should come from somewhere else (do we need an ImageConverter class ?).

    conv := OrderedCollection new.
    conv add:('anytopnm %1 > %2' -> 'pnm').

    outSuffix = 'png' ifTrue:[
        conv add:('pnmtopng %1 > %2' -> 'png').
        ^ conv.
    ].
    outSuffix = 'gif' ifTrue:[
        conv add:('ppmquant 256 %1 | ppmtogif > %2' -> 'gif').
        ^ conv.
    ].
    outSuffix = 'xpm' ifTrue:[
        conv add:('ppmtoxpm %1 > %2' -> 'xpm').
        ^ conv.
    ].
    outSuffix = 'jpg' ifTrue:[
        conv add:('ppmtojpeg %1 > %2' -> 'jpg').
        ^ conv.
    ].
    self error:'unimplemented conversion'.
!

convertImageFrom:fileName to:outFile onError:exceptionBlock
    |writer image outSuffix|

    outSuffix := outFile suffix.

    ((writer := MIMETypes imageReaderForSuffix:outSuffix) notNil
    and:[ (image := Image fromFile:fileName) notNil
    and:[ writer canRepresent:image ]]) ifTrue:[
        "/ can do it with Smalltalk tools
        writer save:image onFile:outFile.
        ^ self
    ] ifFalse:[
        "/ use external tools (pbm-package, if available)
        self 
            convertImageUsingExternalFileToolsFrom:fileName 
            toSuffix:outSuffix 
            onError:exceptionBlock.
    ].

    "Created: / 20-05-2010 / 11:12:36 / cg"
!

convertImageToGIF
    self convertImageToSuffix:'gif'
!

convertImageToJPG
    self convertImageToSuffix:'jpg'
!

convertImageToPNG
    self convertImageToSuffix:'png'
!

convertImageToSuffix:outSuffix
    |outFile filesToConvert|

    self withActivityIndicationDo:[
        filesToConvert := self currentSelectedObjects copy.
        filesToConvert do:[:fileName |
            self notify:(resources string:'Converting: %1...' with:fileName baseName).

            fileName isDirectory ifFalse:[
                |skip|

                skip := false.
                outFile := fileName withSuffix:outSuffix.
                outFile exists ifTrue:[
                    |answer|

                    answer := Dialog 
                        confirmWithCancel:(resources string:'Overwrite existing %1 ?' with:outFile baseName allBold)
                        default:false.
                    answer isNil ifTrue:[AbortSignal raise].
                    answer ifFalse:[ skip := true ].
                ].
                skip ifFalse:[
                    self 
                        convertImageFrom:fileName 
                        to:outFile 
                        onError:[:errMsg |
                            filesToConvert size == 1 ifTrue:[
                                Dialog warn:errMsg
                            ] ifFalse:[
                                (Dialog confirm:errMsg yesLabel:(resources string:'OK') noLabel:(resources string:'Cancel')) ifFalse:[
                                    ^ self
                                ]
                            ].
                        ].

                    self updateAndSelect:nil.
                ]
            ]
        ].
        self notify:nil.
    ]

    "Modified: / 20-05-2010 / 11:15:35 / cg"
!

convertImageToXPM
    self convertImageToSuffix:'xpm'
!

convertImageUsingExternalFileToolsFrom:fileName toSuffix:outSuffix onError:exceptionBlock
    |inFile outFile chainOfConversions conversionStream tempFileTemplate
     eachConversionSuffixCommandPair eachConversionCommand eachConversionSuffix|

    chainOfConversions := self conversionChainFrom:(fileName suffix) to:outSuffix.

    chainOfConversions isNil ifTrue:[ 
        self warn:('Don''t know how to convert from %1 to %2' bindWith:fileName suffix with:outSuffix).
        ^ false 
    ].
    conversionStream := chainOfConversions readStream.

    tempFileTemplate  := Filename newTemporary.

    inFile := fileName.
    eachConversionSuffixCommandPair := conversionStream next.
    eachConversionCommand := eachConversionSuffixCommandPair key.

    eachConversionCommand == #readToXPM ifTrue:[
        |image tempFileXPM|

        image := Image fromFile:(inFile pathName).
        image isNil ifTrue:[
            self warn:'Unknown format/not an image: ' , inFile baseName.
            ^ false.
        ].
        tempFileXPM  := tempFileTemplate withSuffix:'xpm'.
        image saveOn:tempFileXPM using:XPMReader.
        inFile := tempFileXPM.
        eachConversionSuffixCommandPair := conversionStream next.
    ].

    [
        |command errOutput errMsg|

        [eachConversionSuffixCommandPair notNil] whileTrue:[
            eachConversionCommand := eachConversionSuffixCommandPair key.
            eachConversionSuffix  := eachConversionSuffixCommandPair value.

            self notify:(resources string:'Converting: %1 to %2...' with:fileName baseName with:eachConversionSuffix).

            outFile := tempFileTemplate withSuffix:eachConversionSuffix.
            command := eachConversionCommand 
                            bindWith:(inFile pathName)
                            with:(outFile pathName).

            errOutput := String writeStream.

            (OperatingSystem executeCommand:command errorTo:errOutput) ifFalse:[
                errMsg := resources
                            stringWithCRs:'Conversion of %1 to %2 using %3 failed:\\%4.' 
                            with:inFile baseName allBold
                            with:eachConversionSuffix
                            with:eachConversionCommand allBold
                            with:errOutput contents.
                exceptionBlock value:errMsg.

                (inFile ~= fileName) ifTrue:[inFile delete].
                outFile delete.
                ^ false
            ].
            (inFile ~= fileName) ifTrue:[inFile delete].
            inFile := outFile.
            eachConversionSuffixCommandPair := conversionStream next
        ].
        outFile moveTo:(fileName withSuffix:(chainOfConversions last value)).
    ] ifCurtailed:[
        (inFile ~= fileName) ifTrue:[inFile delete].
        outFile delete.
    ].
    ^ true.

    "Created: / 20-05-2010 / 11:05:50 / cg"
!

createProjectAndOpenProjectBrowser
    |nm f s directory|

    self currentFilesAreInSameDirectory ifFalse:[^ self].
    directory := self currentDirectory.
    nm := directory baseName.
    f := (directory  construct:nm) withSuffix:'prj'.
    f exists ifTrue:[
        Dialog warn:'A file named ' , f baseName , ' already exists.'.
        ^ self.
    ].
    s := f writeStream.
    s nextPutAll:'
name            ''' , nm , '''
type            #classLibrary
package         #''private:' , nm , '''
prerequisites   nil

classes      #( )
'.
    s close.
    ProjectBrowser openOnFile:f.
!

doExecuteCommand
    "execute an OperatingSystem-command"

    | action fileName|

    fileName := self firstSelectedFile.
    action := [:command | 
                self addToCommandHistory:command for:fileName.
                self executeCommand:command.
              ].
    self askForCommandFor:fileName thenDo:action
!

doExecuteScript
    "execute a smalltalk script"

    |textHolder dialog template dummyClass dummyInstance|

    template :=
'"/ Smalltalk script:

"/ the following variables are accessable:
"/      directory       (= ' , (self theSingleSelectedDirectoryOrNil ? Filename homeDirectory) pathName , ')
"/      selectedFiles   (= ...)
"/
"/ Beginner warning: Smalltalk know-how is useful here ;-).
"/
"/ Useful operations are:
"/      directory directoryContentsDo:[:eachBaseNameString | ...]
"/      directory directoryContentsAsFilenamesDo:[:eachFilename | ...]
"/      directory directory
"/
"/ example: move all files which match a aarticular pattern to
"/          a separate directory:
"/ 
"/        |directory idx prefix rest newDir oldFile newFile|
"/
"/        directory := ''/mnt/var/priv/image/tv/frank/frank2/2131_008.jpg'' asFilename.
"/
"/        directory directoryContentsDo:[:eachBaseNameString |
"/            (eachBaseNameString includes:$_) ifTrue:[
"/                idx := eachBaseNameString indexOf:$_.
"/                prefix := eachBaseNameString copyTo:(idx - 1).
"/                rest := eachBaseNameString copyFrom:(idx + 1).
"/                newDir := directory construct:prefix.
"/                newDir exists ifFalse:[
"/                    (Dialog confirm:''Create '' , newDir pathName) ifTrue:[
"/                        newDir makeDirectory.
"/                    ].
"/                ].
"/                newDir exists ifTrue:[
"/                    oldFile := directory construct:eachBaseNameString.
"/                    newFile := newDir construct:rest.
"/                    oldFile moveTo:newFile
"/                ]
"/            ]
"/        ]
'.

    LastScriptBlockString isNil ifTrue:[
        LastScriptBlockString := template.
    ].

    textHolder := ValueHolder new.
    dialog := Dialog 
                 forRequestText:(resources string:'Enter script')
                 editViewClass:CodeView
                 lines:25 
                 columns:70
                 initialAnswer:LastScriptBlockString
                 model:textHolder.
    dialog addButton:(Button label:'Template' action:[textHolder value:template. textHolder changed:#value.]).
    dialog open.
    dialog accepted ifFalse:[^ self].

    LastScriptBlockString := textHolder value.

    Class classConventionViolationConfirmationQuerySignal 
        answer:true
        do:[
            dummyClass := Object class
                    name:#Dummy
                    inEnvironment:nil
                    subclassOf:Object
                    instanceVariableNames:'directory selectedFiles'
                    variable:false
                    words:true
                    pointers:true
                    classVariableNames:''
                    poolDictionaries:''
                    category:#dummyCategory
                    comment:nil
                    changed:true.
        ].

    dummyInstance := dummyClass basicNew.
    dummyInstance instVarAt:1 put:(self theSingleSelectedDirectoryOrNil ? Filename homeDirectory).
    dummyInstance instVarAt:2 put:(self currentSelectedFiles value).

    Compiler
        evaluate:textHolder value
        in:nil 
        receiver:dummyInstance 
        notifying:nil 
        logged:false
        ifFail:[]
        compile:true.

"/    fileName := self firstSelectedFile.
"/    action := [:command | 
"/                self addToCommandHistory:command for:fileName.
"/                self executeCommand:command.
"/              ].
"/    self askForCommandFor:fileName thenDo:action
!

doMake
    |dir cmd|

    cmd := 'make'.
    dir := self theSingleSelectedDirectoryOrNil.

    OperatingSystem isMSWINDOWSlike ifTrue:[
        dir notNil ifTrue:[
            (dir construct:'bmake.bat') exists ifTrue:[
                cmd := 'bmake.bat'.
            ]
        ]
    ].
    self executeCommand:cmd.
!

doOpenCBrowser
    |destDir|

    destDir := self currentDirectory.
    CBrowser::Browser openIn:destDir pathName
!

doOpenExplorer
    OperatingSystem isMSWINDOWSlike ifFalse:[
        self warn:'sorry - this operation is only available under windows'.
    ].

    OperatingSystem
        openApplicationForDocument:(self currentDirectory) 
        operation:#explore.

    "Modified: / 23-07-2007 / 23:01:22 / cg"
!

doOpenSettings
    self openSettingsDialog
!

doOpenWithShellCommand
    "open using win32-shell"

    |fileName|

    fileName := self firstSelectedFile.
    fileName notNil ifTrue:[    
        Win32OperatingSystem
            openApplicationForDocument:fileName pathName 
            operation:#open
    ]
!

editMode:aSymbol
    self 
        applicationNamed:#FileApplicationNoteBook
        ifPresentDo:[:appl |
            |subApp|

            subApp := appl selectedApplication.
            subApp notNil ifTrue:[
                ^ subApp perform:aSymbol
            ]
        ].

    "Created: / 11-09-2006 / 12:42:08 / cg"
!

editModeInsert
    self editMode:#editModeInsert

    "Created: / 11-09-2006 / 12:40:36 / cg"
!

editModeInsertAndSelect
    self editMode:#editModeInsertAndSelect

    "Created: / 11-09-2006 / 12:40:45 / cg"
!

editModeOverwrite
    self editMode:#editModeOverwrite

    "Modified: / 11-09-2006 / 12:42:26 / cg"
!

fileContentsAsByteArray
    |file|

    file := self firstSelectedFile.
    file isNil ifTrue:[^ self ].
    file asFilename fileSize > (1024*1024) ifTrue:[
        file asFilename fileSize > (128*1024*1024) ifTrue:[
            Dialog warn:'File is too big'.
            ^ self.
        ].
        (Dialog confirm:'File is big - proceed ?') ifFalse:[^ self].
    ].
    file asFilename binaryContentsOfEntireFile inspect
!

fileFileIn
    "fileIn the selected file(s)"

    self fileFileInLazy:false 
!

fileFileIn:aFilename lazy:lazy
    "fileIn a file"

    self withActivityIndicationDo:[
        self notify:('File in:', aFilename asFilename baseName).
        self singleFileFileIn:aFilename lazy:lazy.
        self notify:nil.
    ]

    "Created: / 20-09-2006 / 14:28:35 / cg"
!

fileFileInLazy
    "fileIn the selected file(s). Do a quick load (no compilation)"

    self fileFileInLazy:true 
!

fileFileInLazy:lazy
    "fileIn the selected file(s)"

    self currentSelectedFiles do:[:fileName |
        self fileIn:fileName lazy:lazy
    ].

    "Modified: / 20-09-2006 / 14:29:24 / cg"
!

fileFileInToNameSpace
    "fileIn the selected file(s)<into a nameSpace"

    |ns listOfKnownNameSpaces|

    listOfKnownNameSpaces := Set new.
    NameSpace 
        allNameSpaces 
            do:[:eachNameSpace | 
                listOfKnownNameSpaces add:eachNameSpace name
            ].
    listOfKnownNameSpaces := listOfKnownNameSpaces asOrderedCollection sort.

    ns := Dialog 
                request:'During fileIn, new classes are created in nameSpace:'
                initialAnswer:(LastEnforcedNameSpace ? Class nameSpaceQuerySignal query name)
                list:listOfKnownNameSpaces.
    ns isEmptyOrNil ifTrue:[^ self].

    LastEnforcedNameSpace := ns.
    ns := NameSpace name:ns.

    Class nameSpaceQuerySignal 
        answer:ns
        do:[ 
            self fileFileInLazy:false 
        ] 
!

fileFindAllDuplicates
    "scan directory and all subdirs for duplicate files"

    |fileNames dir infoDir filesBySize
     result info dirPrefix myName stream textBox maxLength directories|


    self withActivityIndicationDo:[
        result := Dictionary new.
        myName := self class name asString.
        directories := self currentSelectedDirectories.
        directories isEmpty ifTrue:[^ self].
        
        dir := directories first.
"/        self label: myName, '- gathering file names ...'.
        [
            fileNames := dir recursiveDirectoryContents.
        ] on:FileStream openErrorSignal do:[:ex|
            self warn:(resources stringWithCRs:'Cannot access: %1\(%2)' 
                            with:ex pathName
                            with:ex description).
            ^ self
        ].
        fileNames := fileNames 
                        collect:[:fn | dir construct:fn]
                        thenSelect:[:fn | fn isDirectory not].

"/        self label:myName , '- gathering sizes ...'.
        infoDir := Dictionary new.
        fileNames do:[:fn |
            infoDir at:fn put:(fn fileSize)
        ].

        "/ for each, get the files size.
        "/ in a first pass, look for files of the same size and
        "/ compare them ...

"/        self label:myName , '- preselect possible duplicates ...'.
        filesBySize := Dictionary new.
        infoDir keysAndValuesDo:[:fn :sz |
            |entry|

            entry := filesBySize at:sz ifAbsentPut:[Set new].
            entry add:fn.
        ].

        "/ any of same size ?

"/        self label:myName , '- checking for duplicates ...'.
        filesBySize do:[:entry |
            |files|

            entry size > 1 ifTrue:[
                files := entry asArray.
                1 to:files size-1 do:[:idx1 |
                    idx1+1 to:files size do:[:idx2 |
                        |fn1 fn2|

                        fn1 := files at:idx1.
                        fn2 := files at:idx2.

"/                        self label:myName , '- checking ' , fn1 baseName , ' vs. ' , fn2 baseName , ' ...'.
                        (result at:fn2 ifAbsent:nil) ~= fn1 ifTrue:[
                            "/ compare the files
                            (fn1 sameContentsAs:fn2) ifTrue:[
"/                                Transcript show:'Same: '; show:fn1 baseName; show:' and '; showCR:fn2 baseName.
                                result at:fn1 put:fn2.
                            ]
                        ]
                    ]
                ]
            ]
        ].

"/        self label:myName , '- sorting ...'.
        dirPrefix := dir pathName.
        result := result associations.
        result := result collect:[:assoc |
                                        |f1 f2|

                                        f1 := assoc key name.
                                        f2 := assoc value name.
                                        (f1 startsWith:dirPrefix) ifTrue:[
                                            f1 := f1 copyFrom:dirPrefix size + 2.
                                        ].
                                        (f2 startsWith:dirPrefix) ifTrue:[
                                            f2 := f2 copyFrom:dirPrefix size + 2.
                                        ].
                                        f1 < f2 ifTrue:[
                                            f2 -> f1
                                        ] ifFalse:[
                                            f1 -> f2
                                        ]
                                ].
        result sort:[:f1 :f2 | f2 value < f1 value].

        info := OrderedCollection new.
        result do:[:assoc |
            info add:(assoc key , ' same as ' , assoc value)
        ].
        info isEmpty ifTrue:[
            Dialog information:'No duplicate files found.'.
            ^ self.
        ].
    ].

    stream := WriteStream on:''.
    info do:[:el|
        stream nextPutLine:el.
    ].

    textBox := TextBox new.
    textBox initialText:(stream contents).
    stream close.
    textBox title:'File duplicates in directory: ', dir asString.
    textBox readOnly:true.
    textBox noCancel.
    textBox label:'Duplicates in ', dir asString.
    maxLength := 10.
    info do:[: el |
        maxLength := maxLength max:(el size).
    ].
    textBox extent:((maxLength * 5)@(info size * 20)); sizeFixed:false.
    textBox maxExtent:Screen current extent.
    textBox openModeless.

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

fileFindDuplicates
    "scan directory for duplicate files"

    |infoDir filesBySize result info stream textBox maxLength directories allFiles titleStream size|

    directories := self currentSelectedDirectories.

    self withActivityIndicationDo:[
        result := Dictionary new.

        directories := self currentSelectedDirectories.
        directories isEmpty ifTrue:[^ self].

        allFiles := self allFilesInSelectedDirectoriesForWhich:[:f | true].

        infoDir := Dictionary new.
        allFiles do:[:fn |
            infoDir at:fn put:(fn info)
        ].

        "/ for each, get the files size.
        "/ in a first pass, look for files of the same size and
        "/ compare them ...

        filesBySize := Dictionary new.
        infoDir keysAndValuesDo:[:fn :info |
            |sz entry|

            sz := info size.
            entry := filesBySize at:sz ifAbsentPut:[Set new].
            entry add:fn.
        ].

        "/ any of same size ?

        filesBySize do:[:entry |
            |files|

            entry size > 1 ifTrue:[
                files := entry asArray.
                1 to:files size-1 do:[:idx1 |
                    idx1+1 to:files size do:[:idx2 |
                        |fn1 fn2|

                        fn1 := files at:idx1.
                        fn2 := files at:idx2.

                        (result at:fn2 ifAbsent:nil) ~= fn1 ifTrue:[
                            "/ compare the files
                            (fn1 sameContentsAs:fn2) ifTrue:[
"/                                Transcript show:'Same: '; show:fn1 baseName; show:' and '; showCR:fn2 baseName.
                                result at:fn1 put:fn2.
                            ]
                        ]
                    ]
                ]
            ]
        ].

        result := result associations.
        result := result collect:[:assoc |
                                        |f1 f2|

                                        f1 := assoc key asString.
                                        f2 := assoc value asString.
                                        f1 < f2 ifTrue:[
                                            f2 -> f1
                                        ] ifFalse:[
                                            f1 -> f2
                                        ]
                                ].
        "/ result sort:[:f1 :f2 | f1 key > f2 key "f2 value < f1 key value"].
        result sort:[:f1 :f2 | " f1 key > f2 key" f2 value < f1 key value].

        info := OrderedCollection new.
        size := self getBestDirectory asString size.
        result do:[:assoc |
            |fn1 fn2|

            fn1 := assoc key.
            fn2 := assoc value.
            size > 1 ifTrue:[
                fn1 := ('..', (fn1 copyFrom:(size + 1))).
                fn2 := ('..', (fn2 copyFrom:(size + 1))).
            ].
            (fn1 includes:Character space) ifTrue:[
                fn1 := '"' , fn1 , '"'
            ].
            (fn2 includes:Character space) ifTrue:[
                fn2 := '"' , fn2 , '"'
            ].
            info add:(fn1 , ' same as ' , fn2)
        ].
        info isEmpty ifTrue:[
            Dialog information:'No duplicate files found.'.
            ^ self.
        ].
    ].
    stream := WriteStream on:''.
    info do:[:el|
        stream nextPutLine:el.
    ].
    titleStream := WriteStream on:''.
    titleStream nextPutAll:'File duplicates in director'.
    directories size == 1 ifTrue:[
        titleStream nextPutAll:'y: ', directories first asString.
    ] ifFalse:[
        titleStream nextPutLine:'ies: '.
        directories do:[:dir|
            size > 1 ifTrue:[
                titleStream nextPutAll:'..'.
                titleStream nextPutLine:((dir asString) copyFrom:(size + 1)).
            ] ifFalse:[
                titleStream nextPutLine:(dir asString).
            ].
        ]
    ].

    textBox := TextBox new.
    textBox initialText:(stream contents).
    textBox title:(titleStream contents).
    textBox readOnly:true.
    textBox noCancel.
    stream := WriteStream on:'Duplicates in '.
    directories do:[ :aDirectory |
        stream nextPutAll:aDirectory baseName.
        stream space.
    ].
    textBox label:stream contents.
    maxLength := 10.
    info do:[: el |
        maxLength := maxLength max:(el size).
    ].
    textBox extent:((maxLength * 5)@((info size max:40)* 10)).
    textBox maxExtent:Screen current extent.
    textBox openModeless. "/ showAtPointer.

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

fileFindFile
    |filename|

    filename := self firstSelectedFileName.
    filename isNil ifTrue:[
        filename := Filename homeDirectory.
    ].
    FindFileApplication openOnFileName:filename for:self.
!

fileFindSimilarImages
    "scan directory for similar image files"

    |result stream textBox directories imageFiles histogramsV histogramsH 
     similarV similarH similar alreadyPrinted|

self withWaitCursorDo:[
    self withActivityIndicationDo:[
        result := Dictionary new.

        directories := self currentSelectedDirectories.
        directories isEmpty ifTrue:[^ self].

        imageFiles := self allFilesInSelectedDirectoriesForWhich:[:f | f mimeTypeFromName isImage].
        "/ imageFiles := imageFiles select:[:f | f baseName startsWith:'foo'].
        imageFiles sort:[:a :b | a pathName < b pathName].
        
        "/ for each, get the color histogram
        histogramsV := Dictionary new.
        histogramsH := Dictionary new.
        imageFiles do:[:fn |
            |hist image|

            image := Image fromFile:fn.
            Transcript showCR:'generating histogram for ',fn baseName.
            hist := ImageColorHistogram new forImage:image.
            histogramsV at:fn put:(hist histogramVectorV).
            histogramsH at:fn put:(hist histogramVectorH).
        ].

        similarH := Dictionary new.
        similarV := Dictionary new.
        similar := Dictionary new.

        (self firstSelectedFile notNil
            ifTrue:[ Array with:self firstSelectedFile ]
            ifFalse:[ imageFiles ])
        do:[:eachFile1 |
            |setOfSimilar distancesV distancesH|

            distancesV := OrderedCollection new.
            distancesH := OrderedCollection new.
            setOfSimilar := OrderedCollection new.

            imageFiles do:[:eachFile2 |
                |v1 v2 dV dH|

                eachFile1 ~= eachFile2 ifTrue:[
                    v1 := histogramsV at:eachFile1.
                    v2 := histogramsV at:eachFile2.
                    dV := ImageColorHistogram distanceFrom:v1 to:v2.
                    distancesV add:(eachFile2 -> dV).
                    v1 := histogramsH at:eachFile1.
                    v2 := histogramsH at:eachFile2.
                    dH := ImageColorHistogram distanceFrom:v1 to:v2.
                    distancesH add:(eachFile2 -> dH).
                    (dV < 10000 and:[dH < 10000]) ifTrue:[
                        setOfSimilar add:eachFile2->(dV + dH).
                    ].
                ]
            ].
            distancesV sort:[:a1 :a2 | a1 value < a2 value].
            distancesH sort:[:a1 :a2 | a1 value < a2 value].
            similarV at:eachFile1 put:distancesV.
            similarH at:eachFile1 put:distancesH.
            setOfSimilar notEmpty ifTrue:[ similar at:eachFile1 put:setOfSimilar ].
        ].

        similar isEmpty ifTrue:[
            Dialog information:'No similarities found.'.
            ^ self.
        ].
    ].

    stream := WriteStream on:''.
    alreadyPrinted := Set new.

    imageFiles do:[:eachFile |
        |eachSet|

        (alreadyPrinted includes:eachFile) ifFalse:[
            eachSet := similar at:eachFile ifAbsent:nil.
            eachSet notNil ifTrue:[
                stream nextPutLine:eachFile pathName.
                eachSet do:[:info |
                    |fn2 dist|

                    fn2 := info key.
                    dist := info value.
                    stream nextPutAll:'    '.
                    stream nextPutAll:fn2 pathName.
                    stream nextPutLine:' (',dist printString,')'.
                    alreadyPrinted add:fn2.
                ].
                stream cr.
            ].
        ].
    ].
    ].

    textBox := TextBox new.
    textBox initialText:(stream contents).
    textBox title:'File similarities:'.
    textBox readOnly:true.
    textBox noCancel.

    textBox label:'File similarities'.
    textBox extent:(400@((similar size max:40)* 10)).
    textBox maxExtent:Screen current extent.
    textBox openModeless. "/ showAtPointer.

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

fileHexDump

    | file item|

    file := self firstSelectedFile.
    file notNil ifTrue:[
        item := DirectoryContentsBrowser itemClass fileName:file.
        self 
            applicationNamed:#FileApplicationNoteBook
            ifPresentDo:[:appl | appl openTextEditorWithHexPresentationOn:item].
    ].
!

fileIn:aFilename
    "fileIn a file"

    self fileIn:aFilename lazy:false

    "Created: / 20-09-2006 / 14:30:06 / cg"
!

fileIn:aFilename lazy:lazy
    "fileIn a file"

    self withActivityIndicationDo:[
        self notify:('File in:', aFilename asFilename baseName).
        self singleFileFileIn:aFilename lazy:lazy.
        self notify:nil.
    ]

    "Created: / 20-09-2006 / 14:29:21 / cg"
!

filterSelectedFiles:whichFilter
    |selectedFiles numFiles msg filterBlock|

    selectedFiles := self currentSelectedObjects.
    (numFiles := selectedFiles size) == 0 ifTrue:[^ self].

    msg := 'Replace contents of file ''%2'' with output of %3-filter ?'.
    numFiles > 1 ifTrue:[
        msg := 'Replace contents of %1 files with output of %3-filter ?'.
    ].

    (Dialog 
        confirm:(resources stringWithCRs:msg with:numFiles with:selectedFiles first baseName allBold with:whichFilter)
        initialAnswer:false
    ) ifFalse:[
        ^ self
    ].

    whichFilter == #rot13 ifTrue:[
        filterBlock := [:charIn | charIn rot13 ].
    ].
    "/ add more here...

    filterBlock isNil ifTrue:[
        self information:'No such filter: ' , whichFilter.
        ^ self.
    ].

    selectedFiles do:[:fileName |
        |s|

        self notify:('Processing:',  fileName baseName).
        fileName isDirectory ifFalse:[
            [
                |inFile outFile in out|

                inFile := fileName asFilename.
                outFile := (fileName pathName , '.filter') asFilename.

                [
                    |charIn charOut|

                    in := inFile readStream.
                    out := outFile writeStream.
                    [in atEnd] whileFalse:[
                        charIn := in next.
                        charOut := filterBlock value:charIn.
                        out nextPut:charOut.
                    ].
                    outFile renameTo:inFile.
                ] ensure:[
                    out notNil ifTrue:[ out close ].
                    in notNil ifTrue:[ in close ].
                    outFile delete.
                ]
            ] on:FileStream openErrorSignal do:[:ex|
                self warn:('Cannot process "%1".' bindWith:fileName baseName allBold).
            ].
        ]
    ].
    self notify:nil.

    "Modified: / 04-12-2006 / 13:14:48 / cg"
!

forEachSelectedFileIgnoringDirectories:ignoreDirs do:aBlock 
    |numItems path files|

    files := self currentSelectedFiles.
    
    (numItems := files size) > 2 ifTrue:[
        (self 
            confirm:(resources string:'Open for each of the %1 items ?' 
                                 with:numItems)) ifFalse:[^ self].
    ].

    self withActivityIndicationDo:[
        files do:[:fileName |
            (ignoreDirs not or:[fileName isDirectory not]) ifTrue:[
                fileName isAbsolute ifTrue:[
                    path := fileName getName.
                ] ifFalse:[
                    path := fileName pathName.
                ].
                aBlock value:path
            ]
        ].
    ].
!

generateDetachedSignaturesForSelectedFiles
    "generate detached signature (pkcs7) files from the contents of the selected files.
     For smalltalk text files, better use #generateSignaturesForSelectedFiles."

    self withActivityIndicationDo:[
        self currentSelectedFiles do:[:fn |
            |data hash signature signatureFile|

            self notify:'Generating detached signature file for ', (fn baseName),'...'.

            data := fn contentsAsString.
            hash := SHA1Stream hashValueOf:data.

            signature := Expecco::KeyFileGenerator new signExpeccoCode:hash.

            signatureFile := fn addSuffix:'sig'.
            signatureFile contents:signature.
        ].
        self notify:nil.
    ]
!

generateSignaturesForSelectedFiles
    "generate signed pkcs7 files from the contents of the selected files"

    self withActivityIndicationDo:[
        self currentSelectedFiles do:[:fn |
            |pkcs7SignedData signatureFilename|

            self notify:'Generating signed file from: ', (fn baseName), '...'.

            pkcs7SignedData := Expecco::KeyFileGenerator new signExpeccoCode:fn contentsOfEntireFile.
            signatureFilename := fn addSuffix:'signed'.
            signatureFilename contents:pkcs7SignedData.
        ].
        self notify:nil.
    ]
!

installAllAsAutoloaded
    "install all classes found here as autoloaded classes"

    self installAllAsAutoloadedRecursive:false
!

installAllAsAutoloadedRecursive
    "install all classes found here and in subdirectories as autoloaded classes"

    self installAllAsAutoloadedRecursive:true
!

installAllAsAutoloadedRecursive:aBoolean
    "install all classes found here (and in subdirs if aBoolean is true) as autoloaded classes"

    [
        |installAction|

        installAction := 
            [:fn |
                (fn suffix = 'st') ifTrue:[
                    self notify:('Install as autoloaded: ', fn baseName).
                    self installAsAutoloaded:fn.
                ]
            ].

        self withActivityIndicationDo:[
            self currentSelectedDirectories do:[:dir|
                aBoolean ifTrue:[
                    dir recursiveDirectoryContentsAsFilenamesDo:installAction
                ] ifFalse:[
                    dir directoryContentsAsFilenamesDo:installAction
                ].
            ].
            self notify:nil.
        ]
    ] fork.
!

installAsAutoloaded:aFilename
    "install aFilename as autoloaded class"

    |chunks filename|

    filename := aFilename asFilename.

    filename readingFileDo:[:s|
        chunks := ChangeSet fromStream:s.
    ].
    chunks 
        select:[:eachChunk | eachChunk isClassDefinitionChange]
        thenDo:[:eachClassChunk | 
                eachClassChunk installAsAutoloadedClassIfPublicWithFilename:filename asAbsoluteFilename "withoutSuffix" name "baseName"
        ].
!

joinSelectedFiles
    |selectedFiles numFiles msg outFileName outFile outStream|

    selectedFiles := self currentSelectedObjects.
    (numFiles := selectedFiles size) <= 1 ifTrue:[^ self].

    msg := 'Join each of the %1 files into single file named:\\Attention: order in which files were selected is relevant here !!'.
    outFileName := Dialog request:(resources stringWithCRs:msg with:numFiles with:selectedFiles first baseName).
    outFileName isEmptyOrNil ifTrue:[^ self].
    outFile := selectedFiles first directory construct:outFileName.

    outStream := outFile writeStream.

    selectedFiles do:[:fileName |
        |inStream|

        self notify:('Adding:',  fileName baseName).
        inStream := fileName readStream.
        inStream copyToEndInto:outStream.
        inStream close.
    ].
    outStream close.
    self notify:nil.

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

loadImageThenDo:aBlock
    |img path files|

    files := self currentSelectedFiles.
    files isEmpty ifTrue:[ ^ self].
    files do:[:fileName |
        path := fileName.
        path isDirectory ifFalse:[
            img := Image fromFile:(path pathName).
            img notNil ifTrue:[
                aBlock value:img
            ] ifFalse:[
                Dialog warn:'Unknown format: ' , fileName asString
            ]
        ]
    ].
!

openASN1Browser

    self openTool:OSI::ASN1Browser 
!

openAppletViewer
    |numItems files|

    files := self currentSelectedFiles.
    (numItems := files size) > 2 ifTrue:[
        (self 
            confirm:(resources string:'open for each of the %1 items ?' 
                                 with:numItems)) ifFalse:[^ self].
    ].

    Java startupJavaSystem.
"/    Java markAllClassesUninitialized.
"/    Java initAllStaticFields.
"/    Java initAllClasses.

    files do:[:fileName |
        |p path|

        path := fileName.
        path isDirectory ifFalse:[
            p := Java 
                    javaProcessForMainOf:(Java classForName:'sun.applet.AppletViewer')
                    argumentString:path pathName.
            p resume.
        ]
    ].
!

openCBrowser
    |dir files|

    dir := self theSingleSelectedDirectoryOrNil.
    dir isNil ifTrue:[ ^ self].
    self withActivityIndicationDo:[
        files := self currentSelectedObjects.
        files notEmptyOrNil ifTrue:[
            CBrowser::Browser openOn:files first.
        ] ifFalse:[
            CBrowser::Browser openIn:dir.
        ]
    ]

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

openChangeSetBrowser
    self
        openTool:ChangeSetBrowser 
        with:#openOnFile: 
        ignoreDirectories:true
!

openChangesBrowser
    "open a change browser on the selected file(s)"

    self openTool:(UserPreferences current changesBrowserClass)
!

openDiffView
    "open a diff-view"

    |name1 name2 text1 text2 d err nm l1 files title 
     defaultName lastFile sameContents msg|

    files := self currentSelectedObjects.
    files isEmpty ifTrue:[
        Dialog warn:(resources string:'You have to select a file first').
        ^ self.
    ].

    (files size == 2) ifTrue:[
        name1 := files last.
        name2 := files first.
    ] ifFalse:[
        LastFileDiffFile notNil ifTrue:[
            | directory |
            directory := self currentDirectory.
            directory notNil ifTrue:[
                lastFile := directory asFilename construct:(LastFileDiffFile baseName).
                (lastFile exists and:[lastFile isReadable]) ifTrue:[
                    name1 := lastFile.
                ]
            ]
        ].
        name2 := files first.
        title := resources string:'Show differences between "%1" and:' with:name2 baseName.
        defaultName := name1 notNil ifTrue:[name1 baseName] ifFalse:[nil].
        name1 := DialogBox 
                    requestFileName:title 
                    default:defaultName 
                    ok:(resources string:'OK') 
                    abort:(resources string:'Compare against File List') 
                    pattern:'*' 
                    fromDirectory:(name2 asFilename directory).
    ].

    self withWaitCursorDo:[
        (name1 isNil or:[name1 asString isEmpty]) ifTrue:[
            text1 := self getAllFilesAsStrings asStringCollection withTabs.
            text1 := text1 collect:[:l | l isNil ifTrue:[' '] ifFalse:[l]].
            name1 := nil.
            l1 := 'browser contents'
        ] ifFalse:[
            name1 := name1 asFilename.
            LastFileDiffFile := name1.
            name1 isReadable ifFalse:[
                nm := name1.
                name1 exists ifFalse:[
                    err := '"%1" does not exist.'.
                ] ifTrue:[
                    err := '"%1" is not readable.'
                ].
            ].
            l1 := name1 pathName
        ].

        err isNil ifTrue:[
            name2 isReadable ifFalse:[
                nm := name2.
                name2 exists ifFalse:[
                    err := '"%1" does not exist.'.
                ] ifTrue:[
                    err := '"%1" is not readable.'
                ].
            ].
        ].
        err notNil ifTrue:[
            Dialog warn:(resources string:err with:nm pathName allBold).
            ^ self
        ].

        self withActivityIndicationDo:[
            ((name1 notNil and:[name1 fileSize > (1024*1024*8)])
            or:[ name2 fileSize > (1024*1024*8) ]) ifTrue:[
                name1 fileSize = name2 fileSize ifTrue:[
                    ProgressIndicator
                        displayBusyIndicator:'Comparing...'
                        at:(Screen default center)
                        during:[
                            sameContents := (name1 sameContentsAs:name2).
                        ].
                    sameContents ifTrue:[
                        self information:'Same contents.'
                    ] ifFalse:[
                        self information:'Different contents (File(s) too big for more details).'
                    ].
                ] ifFalse:[
                    self information:'Different size (File(s) too big for more details).'
                ].
                ^ self.
            ].

            name1 notNil ifTrue:[
                name1 isDirectory ifTrue:[
                    text1 := name1 directoryContents asString.
                ] ifFalse:[
                    text1 := name1 contents.
                ]
            ].
            name2 isDirectory ifTrue:[
                text2 := name2 directoryContents asString.
            ] ifFalse:[
                text2 := name2 contents.
            ].
            text1 = text2 ifTrue:[
                (name1 isDirectory or:[name2 isDirectory]) ifTrue:[
                    msg := 'Same filename lists.'
                ] ifFalse:[
                    msg := 'Same contents.'
                ].
                self information:(resources string:msg)
            ] ifFalse:[
                d := DiffTextView 
                        openOn:text1 label:l1
                        and:text2 label:name2 pathName.
                d topView label:(resources string:'File Differences').
            ]
        ]
    ]

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

openDirectoryDiffView
    "open a directory diff-view"

    |name1 name2 files|

    files := self currentSelectedObjects.
    (files size == 2) ifFalse:[
        Dialog warn:(resources string:'You have to select exactly 2 directories').
        ^ self.
    ].
    name1 := files last.
    name2 := files first.
    self information:'Sorry - not yet implemented'.

"/    self withWaitCursorDo:[
"/        (name1 isNil or:[name1 asString isEmpty]) ifTrue:[
"/            text1 := self getAllFilesAsStrings asStringCollection withTabs.
"/            text1 := text1 collect:[:l | l isNil ifTrue:[' '] ifFalse:[l]].
"/            name1 := nil.
"/            l1 := 'browser contents'
"/        ] ifFalse:[
"/            name1 := name1 asFilename.
"/            LastFileDiffFile := name1.
"/            name1 isReadable ifFalse:[
"/                nm := name1.
"/                name1 exists ifFalse:[
"/                    err := '"%1" does not exist.'.
"/                ] ifTrue:[
"/                    err := '"%1" is not readable.'
"/                ].
"/            ].
"/            l1 := name1 pathName
"/        ].
"/
"/        err isNil ifTrue:[
"/            name2 isReadable ifFalse:[
"/                nm := name2.
"/                name2 exists ifFalse:[
"/                    err := '"%1" does not exist.'.
"/                ] ifTrue:[
"/                    err := '"%1" is not readable.'
"/                ].
"/            ].
"/        ].
"/        err notNil ifTrue:[
"/            Dialog warn:(resources string:err with:nm pathName allBold).
"/            ^ self
"/        ].
"/
"/        self withActivityIndicationDo:[
"/            ((name1 notNil and:[name1 fileSize > (1024*1024*8)])
"/            or:[ name2 fileSize > (1024*1024*8) ]) ifTrue:[
"/                name1 fileSize = name2 fileSize ifTrue:[
"/                    ProgressIndicator
"/                        displayBusyIndicator:'Comparing...'
"/                        at:(Screen default center)
"/                        during:[
"/                            sameContents := (name1 sameContentsAs:name2).
"/                        ].
"/                    sameContents ifTrue:[
"/                        self information:'Same contents.'
"/                    ] ifFalse:[
"/                        self information:'Different contents (File(s) too big for more details).'
"/                    ].
"/                ] ifFalse:[
"/                    self information:'Different size (File(s) too big for more details).'
"/                ].
"/                ^ self.
"/            ].
"/
"/            name1 notNil ifTrue:[
"/                name1 isDirectory ifTrue:[
"/                    text1 := name1 directoryContents asString.
"/                ] ifFalse:[
"/                    text1 := name1 contents.
"/                ]
"/            ].
"/            name2 isDirectory ifTrue:[
"/                text2 := name2 directoryContents asString.
"/            ] ifFalse:[
"/                text2 := name2 contents.
"/            ].
"/            text1 = text2 ifTrue:[
"/                (name1 isDirectory or:[name2 isDirectory]) ifTrue:[
"/                    msg := 'Same filename lists.'
"/                ] ifFalse:[
"/                    msg := 'Same contents.'
"/                ].
"/                self information:(resources string:msg)
"/            ] ifFalse:[
"/                d := DiffTextView 
"/                        openOn:text1 label:l1
"/                        and:text2 label:name2 pathName.
"/                d topView label:(resources string:'File Differences').
"/            ]
"/        ]
"/    ]
"/
"/

    "Created: / 20-05-2010 / 15:01:17 / cg"
!

openEditor
    self openTool:EditTextView
!

openGV
    self openOSCommandWithFiles:'gv'
!

openHTMLReader

    self openTool:HTMLDocumentView ignoreDirectories:false
!

openImageEditor
    [
        self loadImageThenDo:[:img | img edit]
    ] fork
!

openImageInspector
    [
        self loadImageThenDo:[:img | img inspect]
    ] fork
!

openImagePreview
    [
        self 
            loadImageThenDo:
                [:img |
                    |i top viewer|

                    top := StandardSystemView new.

                    viewer := ImageView origin:0.0@0.0 corner:1.0@1.0 in:top.
                    i := img.
                    top extent:200@200.
                    top label:(img fileName asFilename directory baseName , Filename separator , img fileName asFilename baseName).
                    top openAndWait.

                    (i width > 200 or:[i height > 200]) ifTrue:[
                        i := i magnifiedPreservingRatioTo:200@200.
                    ].
                    viewer image:i.
                ].
    ] fork
!

openMP3Player
    |file|

    file := self currentSelectedFiles.
    (file isEmpty) ifTrue:[
        ^ self.
    ].
    file := file first.
    (MP3PlayerApplication ? SaugFix::MP3PlayerApplication) playSong:file.
!

openOSCommandWithFiles:command
    |files fileNames|

    files := self currentSelectedFiles.
    (files isEmpty) ifTrue:[
        ^ self.
    ].

    fileNames := files collect:[:each |
                    '"' , each asFilename pathName , '"'
                 ].

    [
        OperatingSystem 
            executeCommand:command , ' ' , fileNames asStringCollection asString
            inDirectory:(files first asFilename directory).
    ] fork
!

openPDFViewer
    |files fileNames cmd arg|

    files := self currentSelectedFiles.
    (files isEmpty) ifTrue:[
        ^ self.
    ].
    cmd := MIMETypes defaultCommandForMimeType:'application/pdf'.
    cmd isNil ifTrue:[
        Dialog warn:'No viewer has been defined for pdf-files (See MIMETypes initialization).'.
        ^ self.
    ].

    fileNames := 
        files 
            collect:
                [:each |
                    '"' , each asFilename pathName , '"'
                ].
    arg := fileNames asStringCollection asStringWith:' '.

    (cmd includesString:'"%1"') ifTrue:[
        cmd := cmd copyReplaceString:'"%1"' withString:'%1'    
    ].

    [
        OperatingSystem executeCommand:(cmd bindWith:arg).
    ] fork

    "Modified: / 12-05-2004 / 12:31:20 / cg"
!

openRP
    self openOSCommandWithFiles:'realplay'
!

openResourceFileEditor

    self 
        openTool: Tools::InternationalLanguageTranslationEditor
        with: #openOnFile:
!

openSlideShow
    |dir|

    dir := self theSingleSelectedDirectoryOrNil.
    dir isNil ifTrue:[^ self].

    CodingExamples_GUI::SlideShow openIn:dir.
!

openSnapshotImageBrowser
    "Sorry, for now, only the old browser can handle snapShotImages."

    ^ self openTool:SystemBrowser with:#openOnSnapShotImage: ignoreDirectories:true
!

openTerminal
    |dir|

    dir := self theSingleSelectedDirectoryOrHomeDir.

    TerminalApplication notNil ifTrue:[
        TerminalApplication openIn:dir
    ] ifFalse:[
        VT100TerminalView openShellIn:dir.
    ].
!

openTool:aToolClass
    "open a tool on the selected file(s)"

    ^ self openTool:aToolClass ignoreDirectories:true
!

openTool:aToolClass ignoreDirectories:ignoreDirs
    "open a tool on the selected file(s)"

    ^ self
        openTool:aToolClass 
        with:#openOn: 
        ignoreDirectories:ignoreDirs
!

openTool:aToolClass with:aSelector
    "open a tool on the selected file(s)"

    ^ self openTool:aToolClass with:aSelector ignoreDirectories:true
!

openTool:aToolClass with:aSelector ignoreDirectories:ignoreDirs
    "open a tool on the selected file(s)"

    |tool|

    aToolClass isNil ifTrue:[
        Dialog warn:'Sorry, that tool seems to be not available'.
        ^ nil.
    ].
    self forEachSelectedFileIgnoringDirectories:ignoreDirs do:[:path |
        tool := aToolClass perform:aSelector with:path.
    ].
    ^ tool
!

openWebBrowser
    self 
        forEachSelectedFileIgnoringDirectories:true 
        do:[:path |
            Error
                handle:[:ex |
                    Dialog warn:'Shell execution failed: ',ex description
                ] do:[
                    OperatingSystem openApplicationForDocument:path operation:#open.
                    ^ self.
                ]
        ].
!

openWorkspace
    self 
        openTool:WorkspaceApplication 
        with:#openOnFile:
        ignoreDirectories:true
!

openXV
    self openOSCommandWithFiles:'xv'
!

openZipTool
    |zipTool|

    (zipTool := self openTool:ZipTool) notNil ifTrue:[
        zipTool initialExtractDirectory:(self theSingleSelectedDirectoryOrHomeDir).
    ].
!

parseXmlFile
    "Show an XML file - either in an XMLInspector or fall back to
     a plain inspector on the XML tree"

    self withActivityIndicationDo:[
        | selectedFiles xmlDocument |

        selectedFiles:= self currentSelectedFiles.
        selectedFiles do:[:fileName |
            XML::XMLSignal handle:[:ex |
                Dialog information:('Error while reading XML:\    %1' bindWith:ex description) withCRs.
                ^ self.
            ] do:[
                |s d magic encoder document|

                s := fileName asFilename readStream.
                s binary.
                magic := (s next:2).
                magic = #[254 255] ifTrue:[
                    "/ UTF16BE
                    document := CharacterEncoder decodeString:(s upToEnd) from:#utf16be.
                ] ifFalse:[ 
                    magic = #[255 254] ifTrue:[
                        "/ UTF16LE
                        document := CharacterEncoder decodeString:(s upToEnd) from:#utf16le.
                    ].
                ]. 
                s close.

                document notNil ifTrue:[
                    xmlDocument := XML::XMLParser 
                                    processDocumentString:document 
                                    beforeScanDo:[:parse| parse validate:false].
                ] ifFalse:[
                    xmlDocument := XML::XMLParser 
                                    processDocumentInFilename:fileName 
                                    beforeScanDo:[:parse| parse validate:false].
                ]
            ].
            XML::XMLInspector notNil ifTrue:[
                XML::XMLInspector openOn:xmlDocument.
"/                XML::XMLInspector openWithoutDetailsOn:xmlDocument.
            ] ifFalse:[
                "fall back..."
                xmlDocument inspect.
            ]
        ].
    ]
!

readAbbrevFile
    "read the abbrev file and install classes found there as autoloaded classes"

    |directories|

    directories := self currentSelectedDirectories.
    directories isEmpty ifTrue:[^ self].

    self withActivityIndicationDo:[
        directories do:[:eachDir |
            Smalltalk installAutoloadedClassesFrom:(eachDir construct:'abbrev.stc').
        ]
    ]

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

readAndShowResources
    self withActivityIndicationDo:[
        | selectedFiles|

        selectedFiles:= self currentSelectedFiles.
        selectedFiles do:[:fileName |
            resources := ResourcePack fromFile:fileName.
            resources inspect.
        ].
    ]
!

singleFileFileIn:fileName lazy:lazy
    "fileIn the selected file(s)"

    |aStream wasLazy notifyString dontAskSignals lang|

    fileName isRegularFile ifFalse:[
        ^ self.
    ].

    [
        (ObjectFileLoader notNil
         and:[ObjectFileLoader hasValidBinaryExtension:fileName]) ifTrue:[
            AbortOperationRequest catch:[
                |p|

                "/
                "/ look if already loaded ...  then unload first
                "/
                p := fileName pathName.
                (ObjectFileLoader loadedObjectFiles includes:p) ifTrue:[
                    (Dialog confirm:(resources 
                                        string:'%1 is already loaded; load anyway ?'
                                        with:p)) ifFalse:[
                        ^ self
                    ].
                    notifyString := 'unloading old ' , p , ' ...'.
                    Transcript showCR:notifyString.
                    self notify:notifyString.
                    ObjectFileLoader unloadObjectFile:p. 
                ].

                notifyString := 'loading ' , p , ' ...'.
                Transcript showCR:notifyString.
                self notify:notifyString.
                ObjectFileLoader loadObjectFile:p.
                Class addInfoRecord:('fileIn ' , fileName asFilename pathName) 
            ]
        ] ifFalse:[ ((fileName hasSuffix:'cls') 
                     and:[((fileName mimeTypeOfContents ? '') startsWith:'application/x-smalltalk-source') not ]) ifTrue:[
            "/ loading a binary class file
            aStream := fileName readStreamOrNil.
            aStream notNil ifTrue:[
                aStream fileInBinary.
            ]
        ] ifFalse:[
            ((fileName hasSuffix:'class') or:[(fileName hasSuffix:'cla')]) ifTrue:[
                "/ loading a java class file
                JavaClassReader notNil ifTrue:[
                    JavaClassReader loadFile:fileName
                ]
            ] ifFalse:[ (fileName hasSuffix:'sif') ifTrue:[
                "/ loading a sif (smalltalk interchange format) file
                SmalltalkInterchangeSTXFileInManager autoload.    
                SmalltalkInterchangeFileManager newForFileIn
                    fileName: fileName pathName;
                    fileIn.
            ] ifFalse:[ (fileName hasSuffix:'pcl') ifTrue:[
                Parcel isNil ifTrue:[
                    Dialog warn:'Parcel support not loaded.'
                ] ifFalse:[
                    Parcel loadParcelFrom: fileName pathName
                ]
            ] ifFalse:[
                "/ ask programming languages...
                lang := ProgrammingLanguage allDetect:[:l | l canReadSourceFile:fileName] ifNone:nil.
                (lang notNil and:[lang ~= SmalltalkLanguage]) ifTrue:[
                    lang fileIn:fileName.
                ] ifFalse:[
                    "/ loading a regular (chunk) or xml source file
                    Error handle:[:ex |
                        self errorNotify:'Error on file in: ', ex description.
                    ] do:[
                        aStream := fileName readStream.
                        [
                            Class withoutUpdatingChangesDo:[
                                wasLazy := Compiler compileLazy:lazy.
                                Smalltalk fileInStream:aStream.
                            ].
                            Class addInfoRecord:('fileIn ' , fileName asString) 
                        ] ensure:[
                            Compiler compileLazy:wasLazy.
                            aStream close
                        ]
                    ]
                ]
            ]]]
        ]]
    ] on:Error, HaltInterrupt, Class packageRedefinitionNotification do:[:ex| 
        |sig msg label labels values action proceedValue isRedef redefKind|

        isRedef := false.
        sig := ex signal.
        (sig == NoHandlerError and:[ex parameter rejected]) ifTrue:[
            ex reject
        ].
        sig == Class methodRedefinitionNotification ifTrue:[
            msg := 'trying to overwrite method:\\    ' , ex oldMethod whoString , '\\in package ''' 
                   , ex oldPackage , ''' with method from package ''' , ex newPackage , ''''.
            label := 'Method redefinition in fileIn'.
            redefKind := 'method'.
            isRedef := true.
        ] ifFalse:[sig == Class classRedefinitionNotification ifTrue:[
            msg := 'trying to redefine class: ' , ex oldClass name allBold , '\\in package ''' 
                   , ex oldPackage , ''' with new definition from package ''' , ex newPackage , ''''.
            label := 'Class redefinition in fileIn'.
            redefKind := 'class'.
            isRedef := true.
        ] ifFalse:[sig == HaltInterrupt ifTrue:[ |sender|
            label := msg := 'Breakpoint/Halt in fileIn'.
            sender := ex suspendedContext.
            msg := msg , '\\in ' , sender receiver class name , '>>' , sender sender selector
        ] ifFalse:[
            label := 'Error in fileIn'.
            msg := 'error in fileIn: %1'
        ]]].

        msg := msg bindWith:ex description.

        labels := #('Cancel' 'Skip' 'Debug' 'Compare Sources').
        values := #(abort     skip  debug   compareSources).

        isRedef ifTrue:[
              msg := msg, ('%<cr>%<cr>Continue will install the change and assign the %1 to the new package.
Keep Package will install the change, but keep the %1 in the old package.' bindWith:redefKind).

              labels := labels , #('Keep Package' ).
              values := values , #(keep).
        ].

        labels := labels , #( 'Continue' 'ContinueForAll').
        values := values , #(  continue   continueForAll).

        AbortAllOperationWantedQuery query ifTrue:[
              labels := #('Cancel All') , labels.
              values := #(cancelAll) , values.
        ].

        action := Dialog 
                      choose:(msg withCRs) 
                      label:label
                      image:(WarningBox iconBitmap)
                      labels:labels
                      values:values
                      default:#continue
                      onCancel:#abort.

        action == #continueForAll ifTrue:[
            dontAskSignals isNil ifTrue:[
                dontAskSignals := IdentityDictionary new.
            ].
            dontAskSignals at:sig put:#continue.
            action := #continue.
        ].

        (action == #compareSources) ifTrue:[
            DiffCodeView 
                openOn:(ex oldMethod source) label:'Old Source'
                and:(ex newMethod source) label:'New Source'
                title:ex description.
            ex resignalAs:ex
        ].
        (action == #continue or:[action == #keep]) ifTrue:[
            ex proceedWith:action "(isRedef ifTrue:[#keep] ifFalse:[#continue])".
        ].
        action == #abort ifTrue:[
            AbortOperationRequest raise.
            ex return
        ].
        action == #cancelAll ifTrue:[
            AbortAllOperationRequest raise.
            ex return
        ].
        action == #skip ifTrue:[
            ex proceedWith:nil
        ].
        action == #debug ifTrue:[
            Debugger enter:ex returnableSuspendedContext 
                     withMessage:ex description 
                     mayProceed:true.
            ex proceedWith:nil.
        ].
        ex reject
    ]

    "Modified: / 09-02-2011 / 13:52:54 / cg"
!

splitFile:infile intoPiecesOfSize:kiloBytes
    |bytesPerSplit splitIndex bufferSize buffer n
     inStream outFile outStream remaining remainingInThisOutfile|

    bufferSize := 32*1024.

    inStream := infile readStream.

    bytesPerSplit := kiloBytes * 1024.

    remaining := infile fileSize.
    splitIndex := 1.
    [remaining > 0] whileTrue:[
        outFile := infile pathName , '_' , (splitIndex printString leftPaddedTo:3 with:$0).
        outStream := outFile asFilename writeStream.
        remainingInThisOutfile := remaining min:bytesPerSplit.
        [remainingInThisOutfile > 0] whileTrue:[
            buffer := ByteArray new:bufferSize.
            n := inStream 
                    nextBytes:(bufferSize min:remainingInThisOutfile)
                    into:buffer 
                    startingAt:1.
            outStream nextPutBytes:n from:buffer startingAt:1.
            remainingInThisOutfile := remainingInThisOutfile - n.
            remaining := remaining - n.
        ].
        outStream close.
        splitIndex := splitIndex + 1.
    ].
    inStream close.
!

splitSelectedFiles
    |selectedFiles numFiles msg sizeString kiloBytes|

    selectedFiles := self currentSelectedObjects.
    (numFiles := selectedFiles size) == 0 ifTrue:[^ self].

    msg := (numFiles > 1) 
                ifTrue:'Split each of the %1 files into pieces of size (in kB):'
                ifFalse:'Split %2 into pieces of size (in kB):'.

    sizeString := Dialog request:(resources stringWithCRs:msg with:numFiles with:selectedFiles first baseName).
    sizeString isEmptyOrNil ifTrue:[^ self].
    kiloBytes := Integer readFrom:sizeString onError:nil.
    kiloBytes isNil ifTrue:[^ self].

    selectedFiles do:[:fileName |
        self notify:('Splitting:',  fileName baseName).
        fileName isDirectory ifFalse:[
            self splitFile:fileName intoPiecesOfSize:kiloBytes.
        ]
    ].
    self notify:nil.

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

truncateSelectedFilesToZeroSize
    |selectedFiles numFiles msg|

    selectedFiles := self currentSelectedObjects.
    (numFiles := selectedFiles size) == 0 ifTrue:[^ self].

    msg := 'Really truncate file ''%2'' to zero length ?\\WARNING: contents of file is lost !!\This cannot be undone.'.
    numFiles > 1 ifTrue:[
        msg := 'Really truncate %1 files to zero length ?\\WARNING: contents of files is lost !!\This cannot be undone.'.
    ].

    (Dialog 
        confirm:(resources stringWithCRs:msg with:numFiles with:selectedFiles first baseName allBold)
        initialAnswer:false
    ) ifFalse:[
        ^ self
    ].

    selectedFiles do:[:fileName |
        |s|

        self notify:('Truncating:',  fileName baseName).
        fileName isDirectory ifFalse:[
            [
                s := fileName writeStream.
                s close.
            ] on:FileStream openErrorSignal do:[:ex|
                self warn:('Cannot truncate "%1".' bindWith:fileName baseName allBold).
            ].
        ]
    ].
    self notify:nil.

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

!AbstractFileBrowser methodsFor:'menu queries-cvs'!

canCvsAddAndCommit

    |selectedFiles|

    selectedFiles := self currentSelectedFiles.
    ^ (selectedFiles notEmpty and:[self currentFilesAreInSameDirectory]).
!

canRemoveCVSContainer
    ^ self currentSelectedFiles 
        contains:[:fileName|
            |dirOfFile cvsDir|

            dirOfFile := self getDirWithoutFileName:fileName.
            cvsDir := dirOfFile construct:'CVS'.
            cvsDir isDirectory
        ].
! !

!AbstractFileBrowser methodsFor:'menu queries-tools'!

anySTFilesOrDirectoriesPresent
    ^ self anyDirectoriesOrFilesPresentWithSuffix:'st'
!

anySTFilesPresent
    ^ self anyFilesPresentWithSuffix:'st'
!

canCreateNewProject

    | selectedFiles |

    self currentFilesAreInSameDirectory ifFalse:[^ false].
    selectedFiles := self currentSelectedFiles.
    ^ [ 
        ((selectedFiles detect:[:fileName| | suffix|
            suffix := fileName suffix asLowercase.
            (suffix = 'prj' or:[suffix = 'st'])
        ] ifNone:[nil]) notNil)
      ]
!

canDoTerminal

    ^ OperatingSystem isUNIXlike
      or:[OperatingSystem isMSWINDOWSlike]
!

canDoTerminalAndSystemIsDOS

    ^ self canDoTerminal and:[OperatingSystem isMSWINDOWSlike]
!

canDoTerminalAndSystemIsUnix

    ^ self canDoTerminal and:[OperatingSystem isUNIXlike]
!

canGenerateSignatureFiles
    "we need the both the KeyFileGenerator (for the secret expecco key) and the KeyFile"

    Expecco::KeyFileGenerator isNil ifTrue:[ ^ false ].
    Expecco::KeyFile isNil ifTrue:[ ^ false ].

    ^ self currentSelectedFiles notEmptyOrNil
!

canReadAbbrevFile

    |currentDirectory|

    self currentFilesAreInSameDirectory ifFalse:[^ false].
    currentDirectory := self currentDirectory.
    ^ currentDirectory notNil 
      and:[ (currentDirectory construct:'abbrev.stc') exists ]
!

canRemoveFromSourcePath

    ^ false
!

hasASN1
    ^ [ OSI::ASN1Parser notNil 
        and:[OSI::ASN1Parser isLoaded]]

!

hasASN1AndSelection
    ^ [ self hasSelection value 
        and:[self currentSelectedFiles size > 0
        and:[OSI::ASN1Parser notNil 
        and:[OSI::ASN1Parser isLoaded]]]]
!

hasCBrowser
    ^ [ CBrowser::Browser notNil ]
!

hasJava

    ^ [ JavaClassReader notNil 
        and:[JavaClassReader isLoaded]]
!

hasJavaAndSelection

    ^ [ self currentSelectedFiles size > 0
        and:[JavaClassReader notNil 
        and:[JavaClassReader isLoaded]]]
!

hasMP3Player

    ^ [ MP3PlayerApplication notNil
        or:[SaugFix::MP3PlayerApplication notNil ]]
!

hasMP3PlayerAndSelection

    ^ [ self currentSelectedFiles size > 0
        and:[ self hasMP3Player value]]
!

hasResourceFileSelected

    ^ [
        |sel|

        sel := self currentSelectedFiles.
        sel notEmptyOrNil ifTrue:[
            sel conform:[:fn | fn suffix asLowercase = 'rs'].
        ] ifFalse:[
            false
        ]
      ]
!

hasSlideShow

    ^ [CodingExamples_GUI::SlideShow notNil]
!

hasSnapshotSelection

    ^ [ 
        | files |

        files := self currentSelectedFiles.
        ((files size == 1)
        and:[ files first hasSuffix:'img' ])
      ]
!

hasXml
    ^ [ XML::XMLParser notNil ]
!

hasXmlFileSelected
    "/ cg - no longer use this to enable XML-inspector (always enabled).
    "/ then handle the error when it ever fails during xml-parsing.

    ^ [
        |sel fileName|

        sel := self currentSelectedFiles.
        sel size == 1 ifTrue:[
            fileName := sel first.
            fileName notNil ifTrue:[
                #('xml' 'vdx') includes:fileName suffix asLowercase
            ] ifFalse:[
                false
            ]
        ] ifFalse:[
            false
        ]
      ]
!

hasZipFileSelected
    |sel fileName suff|

    sel := self currentSelectedFiles.
    sel size == 1 ifFalse:[^ false].

    fileName := sel first.
    fileName isNil ifTrue:[^ false].

    suff := fileName suffix asLowercase.
    (suff = 'zip' or:[suff = 'jar']) ifTrue:[^ true].

    ^ fileName mimeTypeOfContents = 'application/x-zip-compressed'

    "Modified: / 14-02-2011 / 17:17:03 / cg"
!

hasZipFileSelectedHolder
    ^ [ self hasZipFileSelected ]

    "Created: / 14-02-2011 / 17:16:08 / cg"
!

javaSupportLoaded

    ^ false
! !

!AbstractFileBrowser methodsFor:'presentation'!

getModeString:modeBits
    "convert file-mode bits into a more user-friendly string.
     This is wrong here - should be moved into OperatingSystem."

    ^ self getModeString:modeBits 
                    with:#( 'owner:' $r $w $x 
                            ' group:' $r $w $x 
                            ' others:' $r $w $x )
!

getModeString:modeBits with:texts
    "convert file-mode bits into a more user-friendly string.
     This is wrong here - should be moved into OperatingSystem."

    |bits modeString|

    bits := modeBits bitAnd:8r777.
    modeString := ''.

    #( nil 8r400 8r200 8r100 nil 8r040 8r020 8r010 nil 8r004 8r002 8r001 ) 
    with: texts do:[:bitMask :access |
        |ch|

        bitMask isNil ifTrue:[
            modeString := modeString , access
        ] ifFalse:[
            (bits bitAnd:bitMask) == 0 ifTrue:[
                ch := $-
            ] ifFalse:[
                ch := access
            ].
            modeString := modeString copyWith:ch 
        ]
    ].
    ^ modeString
! !

!AbstractFileBrowser methodsFor:'private'!

theSingleSelectedDirectoryOrHomeDir
    |dir|

    dir := self theSingleSelectedDirectoryOrNil.
    dir isNil ifTrue:[ dir := Filename homeDirectory].
    ^ dir.
!

theSingleSelectedDirectoryOrNil
    |dirs|

    dirs := self currentSelectedDirectories.
    dirs size ~= 1 ifTrue:[ ^ nil].
    ^ dirs anElement.
! !

!AbstractFileBrowser methodsFor:'queries'!

cBrowserLoaded
    ^ CBrowser::Browser notNil
!

getAllFilesAsStrings
    "raise an error: must be redefined in concrete subclass(es)"

    ^ self subclassResponsibility
!

getFileInfoStringForFirstSelectedFile:longInfo
    "get stat info on selected file - return a string which can be
     shown in a box"

    |selFiles filename fileOutput modeBits modeString s info
     sizeString fileSize tAccess tModification buffer|

    selFiles := self currentSelectedObjects.
    selFiles size ~~ 1 ifTrue:[ ^ nil].
    filename := selFiles first.

    info := filename linkInfo.
    info isNil ifTrue:[
        ^ 'Cannot get fileInfo'.
        ^ nil
    ].

    buffer := Text writeStream.

    buffer 
        nextPutAll:'filename: '; 
        nextPutLine:filename asString.

    OperatingSystem isMSDOSlike ifTrue: [
        (info alternativeName notNil
        and:[ info alternativeName ~= info fullName ]) ifTrue:[
            buffer nextPutAll:'shortname: '.
            buffer nextPutLine:info alternativeName.
        ]
    ].

    buffer nextPutAll:'type:   '.
    info isSymbolicLink ifTrue:[
        filename exists ifFalse:[
            buffer nextPutAll:'broken '.
        ].
        buffer 
            nextPutAll:'symbolic link to: ';
            nextPutLine:(info path ? '???').
    ] ifFalse:[
        (longInfo and:[info isRegular]) ifTrue:[
            fileOutput := filename fileType.
        ].

        fileOutput isNil ifTrue:[
            s := info type asString
        ] ifFalse:[
            s := 'regular (' , fileOutput , ')'
        ].
        buffer nextPutLine:s.
    ].

    fileSize := info fileSize.
    sizeString := 'size:   ', fileSize printString.
    longInfo ifTrue:[
        fileSize > 1024  ifTrue:[
            sizeString := sizeString,' (',(UnitConverter fileSizeStringFor:fileSize),')'.
        ].
    ].
    buffer nextPutLine:sizeString.

    modeBits := info mode.
    modeString := self getModeString:modeBits.

    buffer nextPutAll:'access: '; nextPutAll:modeString.
    longInfo ifTrue:[
        buffer nextPutAll:' ('; nextPutAll:(modeBits printStringRadix:8); nextPut:$).
    ].
    buffer
        cr;
        nextPutAll:'owner:  ';
        nextPutLine:(OperatingSystem getUserNameFromID:(info uid)).

    longInfo ifTrue:[
        buffer 
            nextPutAll:'group:  ';
            nextPutLine:(OperatingSystem getGroupNameFromID:(info gid)).

        tAccess := info accessTime.
        buffer 
            nextPutAll:'last access:       '; 
            nextPutAll:tAccess asDate printString; 
            space;
            nextPutLine:tAccess asTime printString.

        tModification := info modificationTime.
        buffer 
            nextPutAll:'last modification: '; 
            nextPutAll:tModification asDate printString; 
            space;
            nextPutLine:tModification asTime printString.
    ].
    ^ buffer contents

    "Modified: / 07-02-2007 / 10:38:14 / cg"
!

hasImageColorHistogram
    ^ ImageColorHistogram notNil
!

initialCommandFor:fileName into:aBox
    "set a useful initial command for execute box."

    self class initialCommandFor:fileName in:Filename currentDirectory intoBox:aBox
!

isAbstractFileBrowser

    ^ true
!

systemIsDOS

    ^ OperatingSystem isMSDOSlike
!

systemIsUnix
    ^ OperatingSystem isUNIXlike
! !

!AbstractFileBrowser methodsFor:'queries-file'!

allItemsOfCurrentDirectory
    self 
        applicationNamed:#DirectoryContentsBrowser 
        ifPresentDo:[:app | ^ app allItemsOfCurrentDirectory].

    ^ nil
!

anyFilesPresentWithSuffix:suffix
    self currentSelectedDirectories do:[:dir|
        [
            (dir directoryContentsAsFilenamesDo:[:fn | (fn suffix = suffix) ifTrue:[ ^ true]])
        ] on:FileStream openErrorSignal do:[:ex|].
    ].
    ^ false.
!

commonPrefixOfSelectedFiles
    |selFiles stringCol firstPre|

    selFiles := self currentSelectedObjects.
    selFiles isEmpty ifTrue:[^ nil].

    stringCol := selFiles collect:[:file | file asString].
    firstPre := stringCol at:1.
    stringCol from:2 do:[:el|
         firstPre := firstPre commonPrefixWith:el.
    ].
    ^ firstPre asFilename

    "Modified: / 04-12-2006 / 13:14:25 / cg"
!

currentFilesAreInSameDirectory

    ^ self parentDirectoriesOfCurrentFiles size < 2
!

currentFilesHasDirectories

    ^ self currentSelectedDirectories notEmpty.
!

directoriesForFiles:aFileCol

    ^ (aFileCol collect:[:file| self getDirWithoutFileName:file]) asSet.
!

fileListIsEmpty
    self 
        applicationNamed:#DirectoryContentsBrowser 
        ifPresentDo:[:appl | ^ appl fileListIsEmpty].

    ^ nil.
!

fileListIsNotEmpty

    ^ self fileListIsEmpty not.   
!

fileName:aFilename1 startsWith:aFilename2
    " check if aFilename2 is a prefix of aFilename1"

    | file1 file2 |

    (aFilename2 isNil or:[aFilename1 isNil]) ifTrue:[ ^ false].
    file1 := aFilename1 asFilename.
    file2 := aFilename2 asFilename.

    file2 isRootDirectory ifTrue:[ ^ true].

    ((file1 isDirectory) 
    and:[(file2 isDirectory)
    and:[file1 directory = file2 directory]]) ifTrue:[
        ^ file1 baseName = file2 baseName
    ].
    ^ (file1 pathName) startsWith:(file2 pathName)
!

getAllFilesAsStringCollection
    self 
        applicationNamed:#DirectoryContentsBrowser 
        ifPresentDo:[:appl | ^ appl getAllFilesAsStringCollection].

    ^ nil
!

getBestDirectory

    |selFiles stringCol firstPre|

    selFiles := self currentSelectedDirectories.
    selFiles isEmpty ifTrue:[^ nil].
    selFiles size == 1 ifTrue:[^ selFiles first].
    stringCol := (selFiles collect:[:file| file asString]) asOrderedCollection.
    firstPre := stringCol at:1.
    stringCol from:2 do:[:el|
         firstPre :=  firstPre commonPrefixWith:el.
    ].
    (firstPre endsWith:(OperatingSystem fileSeparator)) ifTrue:[
        firstPre removeLast.
    ].
    ^ firstPre asFilename
!

getDirWithoutFileName:aFileName

    | dir |
    aFileName isNil ifTrue:[
        ^ aFileName.    
    ].
    dir := aFileName asFilename.
    dir isDirectory ifFalse:[
        dir := dir directory.
    ].
    ^ dir.
!

getInfoItem
    "get filename of a description-file (.dir.info, README etc.);
     This file is automatically shown when a directory is enterred.
     You can add more names below if you like."

    |dir|

    dir := self theSingleSelectedDirectoryOrNil.
    dir isNil ifTrue:[ ^ nil].

    #( 
       '.dir.info'
       'README'
       'ReadMe'
       'Readme'
       'readme'
       'read.me'
       'Read.me'
       'READ.ME'
       'README.TXT'
       'readme.txt'
       'Readme.txt'
       'Info.txt'
       'info.txt'
       'INFO.TXT'
    ) do:[:f | 
        |n|

        n := dir construct:f.
        (n isReadable and:[n isDirectory not]) ifTrue:[
            ^ DirectoryContentsBrowser itemClass fileName:n
        ]
    ].
    ^ nil
!

parentDirectoriesOfCurrentFiles

    ^ (self currentSelectedObjects 
        collect:[:file | file directory]) asSet

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

recursiveAnyFilesPresentWithSuffix:suffix
    ^ true. "/ the code below is too slow for a menu enabling operation

"/    |directories|
"/
"/    directories := self currentDirectories value.
"/    directories do:[:dir|
"/        [
"/            dir recursiveDirectoryContentsDo:[:f |
"/                (f asFilename suffix = suffix) ifTrue:[ ^ true]
"/            ]
"/        ] on:FileStream openErrorSignal do:[:ex|].
"/    ].
"/    ^ false.
!

recursiveAnySTFilesPresent
    ^ self recursiveAnyFilesPresentWithSuffix:'st'
! !

!AbstractFileBrowser methodsFor:'selection'!

currentSelectedDirectories
    ^ self currentDirectoriesValue.
!

currentSelectedFiles
    ^ self currentSelectedObjects select:[:file | file isDirectory not].

    "Modified: / 04-12-2006 / 13:14:39 / cg"
!

currentSelectedObjects
    ^ self currentFileNameHolder value 
!

firstSelectedFile
    ^ self currentSelectedObjects 
        detect:[:file | file asFilename isDirectory not]
        ifNone:[].

    "Modified: / 04-12-2006 / 13:14:50 / cg"
!

hasOnlyDirectoriesSelected
    ^ self currentSelectedObjects conform:[:file | file isDirectory].

    "Created: / 20-05-2010 / 10:45:03 / cg"
!

hasOnlyFilesSelected

    ^ self currentSelectedObjects conform:[:file | file isDirectory not].

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

hasTwoDirectoriesSelected

    ^ self currentSelectedObjects size == 2 
        and:[self hasOnlyDirectoriesSelected]

    "Created: / 20-05-2010 / 10:45:18 / cg"
!

hasTwoFilesSelected

    ^ self currentSelectedObjects size == 2 
        and:[self hasOnlyFilesSelected]

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

!AbstractFileBrowser methodsFor:'sorting'!

currentSortOrder
    ^ self aspectFor:#currentSortOrder ifAbsent:[ Dictionary new asValue ].
!

sortBlockHolder
    ^ self aspectFor:#sortBlockHolder ifAbsent:[
        (FileSorter directoriesBeforeFiles:(self sortDirectoriesBeforeFiles value and:[self viewDirsInContentsBrowser value]) 
                    selector:#fileName 
                    sortCaseless:self sortCaseless value 
                    sortReverse:false) asValue.
    ].
!

sortBlockProperty
    |v|
    
    v := self aspectFor:#sortBlockProperty ifAbsent:[ #baseName asValue ].
    v addDependent:self.
    ^ v.
!

sortCaseless
    " aspect for sort caseless "

    ^ self aspectFor:#sortCaseless ifAbsent:[
            (Filename isCaseSensitive not) asValue.
        ]
!

sortDirectoriesBeforeFiles

    " aspect for sort directories always before files"
    ^ self aspectFor:#sortDirectoriesBeforeFiles ifAbsent:[ true asValue ].
!

sortFileListsBy:instanceName 

    self sortFileListsBy:instanceName withReverse:true
!

sortFileListsBy:instanceName withReverse:aBoolean
    | aSymbol sortCaselessLocal currentSortOrder sorter isReverse|

    aSymbol := instanceName asSymbol.
    self sortBlockProperty setValue:instanceName asSymbol.
    sortCaselessLocal := self sortCaseless value.
    currentSortOrder := self currentSortOrder value.
    currentSortOrder isEmptyOrNil ifTrue:[
        self currentSortOrder value:(currentSortOrder := Dictionary new).
        currentSortOrder at:#column put:aSymbol.
        currentSortOrder at:#reverse put:false.
        currentSortOrder at:#sortCaseless put:sortCaselessLocal.
    ] ifFalse:[
        (currentSortOrder at:#sortCaseless) ~= sortCaselessLocal ifTrue:[
            "/ sort caseless changed
            currentSortOrder at:#sortCaseless put:sortCaselessLocal.
        ] ifFalse:[
            (currentSortOrder at:#column) = aSymbol ifTrue:[
                "/ same column like before - change sort order ifReverse is true
                aBoolean ifTrue:[
                    isReverse := currentSortOrder at:#reverse.
                    currentSortOrder at:#reverse put:(isReverse not).
                ].
            ] ifFalse:[
                "/ another column - remark column
                currentSortOrder at:#column put:aSymbol.
            ]
        ]
    ].
    self currentSortOrder changed.

    sorter := FileSorter directoriesBeforeFiles:(self sortDirectoriesBeforeFiles value and:[self viewDirsInContentsBrowser value]) 
                            selector:instanceName 
                            sortCaseless:sortCaselessLocal 
                            sortReverse:(currentSortOrder at:#reverse).
    self sortBlockHolder value:sorter.

    "Modified: / 18-09-2007 / 09:42:47 / cg"
! !

!AbstractFileBrowser methodsFor:'startup & release'!

makeDependent
    self filterModel addDependent:self.            
    self currentFileNameHolder addDependent:self.
    self sortDirectoriesBeforeFiles addDependent:self.
    self sortCaseless addDependent:self.
    self showHiddenFiles addDependent:self.
    self rootHolder addDependent:self.
!

masterApplication:anApplication

    masterApplication isNil ifTrue:[
        (anApplication notNil and:[anApplication askFor:#isAbstractFileBrowser]) ifTrue:[
            aspects := anApplication aspects
        ] ifFalse:[
            aspects isNil ifTrue:[
                aspects := IdentityDictionary new.
                aspects at:#applications put:(IdentityDictionary new).
            ]
        ].
        self applications at:(self className) put:self.
"/        self makeDependent.
    ].
    ^ super masterApplication:anApplication.
!

postBuildWith:aBuilder
    self makeDependent.
    super postBuildWith:aBuilder.
!

postOpenWith:aBuilder

    super postOpenWith:aBuilder.
    self windowGroup showWaitCursorWhenBusyForMillis:100.

    "Modified: / 25-07-2006 / 09:12:45 / cg"
! !

!AbstractFileBrowser::Clipboard methodsFor:'accessing'!

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

    ^ files
!

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

    files := something.
!

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

    ^ method
!

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

    method := something.
! !

!AbstractFileBrowser::CodeExecutionLock methodsFor:'accessing'!

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

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

!AbstractFileBrowser::CodeExecutionLock methodsFor:'actions'!

doIfUnLocked:aBlock

    self locked ifFalse:[
        aBlock value.
    ].
!

doLocked:aBlock

    locked  := true.
    aBlock ensure:[
        locked := false.
    ]
! !

!AbstractFileBrowser::DirectoryHistory class methodsFor:'defaults'!

defaultHistorySize
    ^ 50
! !

!AbstractFileBrowser::DirectoryHistory class methodsFor:'instance creation'!

new
    ^ (super new) initializeHistory.
! !

!AbstractFileBrowser::DirectoryHistory methodsFor:'accessing'!

historySize
    historySize isNil ifTrue:[^ self class defaultHistorySize].
    ^ historySize
!

historySize:aNumber
    historySize := aNumber
! !

!AbstractFileBrowser::DirectoryHistory methodsFor:'actions'!

addToHistory:aPath
    | item pathToAdd |

    pathToAdd := aPath.
    (pathToAdd endsWith:(Filename separator)) ifTrue:[
        pathToAdd asFilename isRootDirectory ifFalse:[
            pathToAdd := pathToAdd copyWithoutLast:(Filename separator asString size).    
        ]    
    ].

    item := self getItemFor:pathToAdd.
    self backForwardListAdd:pathToAdd.
    item isNil ifTrue:[
        " not already in history"
        self size >= self historySize ifTrue:[
            self removeLast.
        ].
        item := DirectoryHistoryItem path:pathToAdd.
        self addFirst:item
    ] ifFalse:[
        "already been there before; move the entry to
         the beginning, so it will fall out later."
        self remove:item.
        self addFirst:item
    ].

    "Modified: / 06-12-2006 / 11:59:50 / cg"
!

backForwardListAdd:aPath
    |pathIndex|

    pathIndex := backForwardList indexOf:aPath.
    pathIndex == 0 ifTrue:[
        " a new path comes in remove forward paths "
        (backForwardList size) > backForwardIndex ifTrue:[
            backForwardList removeFromIndex:(backForwardIndex + 1) toIndex:(backForwardList size).    
        ].
    ] ifFalse:[
        backForwardList remove:aPath.
        pathIndex <= backForwardIndex ifTrue:[
            backForwardIndex := backForwardIndex - 1.
        ]
    ].
    backForwardList add:aPath afterIndex:backForwardIndex.
    backForwardIndex := backForwardIndex + 1.
    backForwardIndex > self historySize ifTrue:[
        backForwardList removeFirst.
        backForwardIndex := backForwardIndex - 1.
    ].
!

goBackward
    "return the first path of the backward list;
     remove it from the back list and add it ti the forward list."

    |retPath|

    self canBackward ifTrue:[
        retPath := backForwardList at:(backForwardIndex - 1).
        backForwardIndex := backForwardIndex - 1.
    ].
    ^ retPath.
!

goForward
    "return the first path of the forward list;
     remove it from the list."

    |retPath|

    self canForward ifTrue:[
        retPath := backForwardList at:backForwardIndex + 1.
        backForwardIndex := backForwardIndex + 1.
    ].
    ^ retPath.
!

resetForwardBackward

    self initialize
!

setPosition:aPosition for:aPath

    self do:[:item| 
        item path = aPath ifTrue:[
            item position:aPosition.
        ]
    ].
! !

!AbstractFileBrowser::DirectoryHistory methodsFor:'initialization'!

initializeHistory

    backForwardList := OrderedCollection new.
    self reverseDo:[:el|
        backForwardList add:el path.
    ].
    backForwardIndex := backForwardList size.
! !

!AbstractFileBrowser::DirectoryHistory methodsFor:'queries'!

canBackward

    ^ backForwardIndex > 1 
!

canForward

    ^ (backForwardList size >= (backForwardIndex + 1))
!

getBackCollection

    | backCol |

    backCol := OrderedCollection new.
    self canBackward ifTrue:[
        | tmpCol |
        tmpCol := backForwardList copyTo:backForwardIndex - 1.
        tmpCol reverseDo:[ :el|
            backCol add:el.
        ]
    ].
    ^ backCol
!

getForwardCollection

    | forwCol |

    forwCol := OrderedCollection new.
    self canForward ifTrue:[
        forwCol := backForwardList copyFrom:(backForwardIndex + 1)    
    ].
    ^ forwCol
!

getItemFor:aPath
    ^ self detect:[:item| item path = aPath] ifNone:nil.
"/    self do:[:item| item path = aPath ifTrue:[^ item]].
"/    ^ nil.
!

getPositionFor:aPath

    |item|

    item := self getItemFor:aPath.

    item notNil ifTrue:[
        ^ item position.
    ].
    ^ nil
!

includesPath:aPath
    ^ self contains:[:item | item path = aPath].

"/    self do:[:item| item path = aPath ifTrue:[^ true]].
"/    ^ false.
!

nextBackwardItem
    "return the first path of the backward list"

    |retPath|

    self canBackward ifTrue:[
        retPath := backForwardList at:backForwardIndex.
    ].
    ^ retPath.
!

nextForwardItem
    "return the first path of the forward list"

    |retPath|

    self canForward ifTrue:[
        retPath := backForwardList at:backForwardIndex + 1.
    ].
    ^ retPath.
! !

!AbstractFileBrowser::DirectoryHistory::DirectoryHistoryItem class methodsFor:'instance creation'!

path:aPath

    ^ self new path:aPath
! !

!AbstractFileBrowser::DirectoryHistory::DirectoryHistoryItem methodsFor:'accessing'!

asFilename
    ^ path asFilename
!

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

    ^ path
!

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

    path := something.
!

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

    ^ position
!

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

    position := something.
!

printString

    ^ self path asString
! !

!AbstractFileBrowser::FilenameHistory class methodsFor:'accessing-attributes'!

filenameHistorySize

    ^ 20
! !

!AbstractFileBrowser::FilenameHistory methodsFor:'adding & removing'!

add:aFileItem
    (self includes:aFileItem) ifTrue:[
        self remove:aFileItem.
    ].
    self addFirst:aFileItem.
! !

!AbstractFileBrowser::SaveAspectItem class methodsFor:'instance creation'!

withValue:aValue isHolder:aBoolean

    | instance |

    instance := self basicNew.
    instance value:aValue.
    instance isHolder:aBoolean.
    ^ instance
! !

!AbstractFileBrowser::SaveAspectItem methodsFor:'accessing'!

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

    ^ isHolder
!

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

    isHolder := something.
!

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

    ^ value
!

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

    value := something.
! !

!AbstractFileBrowser class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libtool/AbstractFileBrowser.st,v 1.456 2011-02-14 16:23:19 cg Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libtool/AbstractFileBrowser.st,v 1.456 2011-02-14 16:23:19 cg Exp $'
! !