FileDialog.st
changeset 19478 fd69284871c4
parent 19357 0e76e9a27375
child 19482 e8e782b89265
--- a/FileDialog.st	Wed Feb 19 21:06:41 2020 +0100
+++ b/FileDialog.st	Wed Feb 19 21:07:34 2020 +0100
@@ -25,8 +25,8 @@
 		browseMenuItemVisibleHolder selectedDeviceDrive
 		listOfDeviceDrives rootDirectoryHolder initialRoot
 		verticalPanelView okLabelEnabled searchInfoBoxVisibleHolder
-		searchedDirectoryInfoHolder'
-	classVariableNames:'LastExtent LastFindPattern AdditionalFolderItemsInMenu'
+		searchedDirectoryInfoHolder additionalFolderItemsInMenu'
+	classVariableNames:'AdditionalFolderItemsInMenu LastExtent LastFindPattern'
 	poolDictionaries:''
 	category:'Interface-Tools-File'
 !
@@ -51,6 +51,16 @@
 "
     A slightly better file dialog
 
+    API of interest:
+        [class side] additionalFolderItemsInMenu 
+            optional list of additional folders to be presented
+            in the directory menu.
+            If not empty, entries will be added to EVERY file dialog.
+
+        [instance side] additionalFolderItemsInMenu 
+            optional list of additional folders to be presented in the directory menu.
+            If not empty, entries will be added to this file dialog.
+
     [author:]
         Martin Walser (martin@vercingetorix)
 
@@ -210,61 +220,27 @@
     "
 !
 
-requestDirectoryName:titleString default:aFileName ok:okText abort:abortText version:versionSymbol pattern:pattern fromDirectory:aDirectoryPath ifFail:failBlock whenBoxCreatedEvaluate:boxCreatedCallback asLoadDialog:aBoolean
+requestDirectoryName:titleString default:aFileName 
+    ok:okText abort:abortText 
+    version:versionSymbol 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    ifFail:failBlock 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:aBoolean
+
     "same as requestFileName, but only show directories"
 
-    |enteredFileName instance enteredFileNameString|
-
-    instance := self    
-        startApplicationFor:titleString 
-        default:aFileName 
-        ok:okText 
-        abort:abortText 
+    ^ self new
+        requestDirectoryName:titleString default:aFileName 
+        ok:okText abort:abortText 
+        version:versionSymbol 
+        pattern:patternArg 
+        fromDirectory:aDirectoryPath 
         ifFail:failBlock 
-        pattern:pattern 
-        fromDirectory:aDirectoryPath 
-        whenBoxCreatedEvaluate:boxCreatedCallback
+        whenBoxCreatedEvaluate:boxCreatedCallback 
         asLoadDialog:aBoolean
-        viewFiles:false
-        multipleSelect:false.
-
-    enteredFileNameString := instance result.
-    enteredFileNameString isNil ifTrue:[
-        ^ failBlock value
-    ].
-    enteredFileName := enteredFileNameString asFilename.
-    enteredFileName isRelative ifTrue:[
-        enteredFileName := instance directory construct:enteredFileNameString
-    ].
-
-    enteredFileNameString := enteredFileName asString.
-    (enteredFileName notNil 
-    and:[enteredFileNameString notEmpty]) ifTrue:[
-        versionSymbol isNil ifTrue:[ ^ enteredFileNameString].
-        versionSymbol == #mustBeNew ifTrue:[
-            "/ file may not exist
-            enteredFileName exists ifTrue:[^ ''].
-        ].
-        versionSymbol == #new ifTrue:[
-            "/ file may not exist
-            enteredFileName exists ifTrue:[
-                (Dialog confirm:(self classResources stringWithCRs:'''%1'' exists.\\Continue anyway ?' with:enteredFileNameString))
-                    ifFalse:[^ ''].
-            ].
-        ].
-        versionSymbol == #mustBeOld ifTrue:[
-            enteredFileName exists ifFalse:[^ ''].
-        ].
-        versionSymbol == #old ifTrue:[
-            "/ file may not exist
-            enteredFileName exists ifFalse:[
-                (self confirm:(self classResources stringWithCRs:'''%1'' does not exist yet.\\Continue anyway ?' with:enteredFileNameString))
-                ifFalse:[^ ''].
-            ].
-        ].
-        FileSelectionBox lastFileSelectionDirectory:(enteredFileNameString).
-    ].
-    ^ enteredFileNameString
+
 
     "
      FileDialog
@@ -272,6 +248,7 @@
         default:Filename currentDirectory pathName
         ifFail:nil
     "
+
     "
      FileDialog
         requestDirectoryName:'which directory ?' 
@@ -374,7 +351,14 @@
         asLoadDialog:nil
 !
 
-requestFileName:titleString default:defaultName ok:okText abort:abortText version:versionSymbol ifFail:failBlock pattern:patternArg fromDirectory:aDirectoryPath whenBoxCreatedEvaluate:boxCreatedCallback asLoadDialog:aBoolean
+requestFileName:titleString default:defaultName ok:okText abort:abortText 
+    version:versionSymbol 
+    ifFail:failBlock 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:aBoolean
+
     "launch a Dialog, which allows user to enter a filename.
      The files presented initially are those in aDirectoryPathOrNil, or the
      last fileBox directory (default: current directory) (if a nil path is given).
@@ -390,70 +374,79 @@
         #any (other)    - no validation
     "
 
-
-    |patternUsed instance enteredFileName enteredFileNameString|
-
-    patternUsed := FileSelectionBox lastSuffixPatternUsedForDefault:patternArg.
-
-    instance := self    
-        startApplicationFor:titleString 
-        default:defaultName 
-        ok:okText 
-        abort:abortText 
+    ^ self new
+        requestFileName:titleString default:defaultName ok:okText abort:abortText 
+        version:versionSymbol 
         ifFail:failBlock 
-        pattern:patternUsed 
+        pattern:patternArg 
         fromDirectory:aDirectoryPath 
-        whenBoxCreatedEvaluate:boxCreatedCallback
+        whenBoxCreatedEvaluate:boxCreatedCallback 
         asLoadDialog:aBoolean
-        viewFiles:true
-        multipleSelect:false.
-
-    enteredFileNameString := instance result.
-    enteredFileNameString isEmptyOrNil ifTrue:[
-        ^ failBlock value
-    ].
-
-    enteredFileName := enteredFileNameString asFilename.
-    enteredFileName isRelative ifTrue:[
-        (versionSymbol isNil and:[#(http https ftp) includes:(enteredFileNameString upToAll:'://')]) ifTrue:[
-            ^ enteredFileNameString.
-        ] ifFalse:[
-            enteredFileName := instance directory construct:enteredFileNameString.
-            enteredFileNameString := enteredFileName asString.
-        ].
-    ].
-
-    FileSelectionBox lastFileSelectionDirectory:(enteredFileName directoryName).
-    (patternArg notEmptyOrNil and:[instance pattern ~= patternArg]) ifTrue:[
-        FileSelectionBox lastSuffixPatternUsedForDefault:patternArg is:instance pattern
-    ].
-
-    versionSymbol isNil ifTrue:[ ^ enteredFileNameString].
-    versionSymbol == #mustBeNew ifTrue:[
-        "/ file must not exist
-        enteredFileName exists ifTrue:[^ failBlock value].
-    ].
-    versionSymbol == #new ifTrue:[
-        "/ file should not exist
-        enteredFileName exists ifTrue:[
-            (Dialog confirm:(self classResources stringWithCRs:'''%1'' exists.\\Continue anyway ?' with:enteredFileNameString))
-                ifFalse:[^ failBlock value].
-        ].
-    ].
-    versionSymbol == #mustBeOld ifTrue:[
-        "/ file must exist
-        enteredFileName exists ifFalse:[^ failBlock value].
-    ].
-    versionSymbol == #old ifTrue:[
-        "/ file should exist
-        enteredFileName exists ifFalse:[
-            (self confirm:(self classResources stringWithCRs:'''%1'' does not exist yet.\\Continue anyway ?' with:enteredFileNameString))
-            ifFalse:[^ failBlock value].
-        ].
-    ].
-
-    ^ enteredFileNameString
-"
+
+"/    |patternUsed instance enteredFileName enteredFileNameString|
+"/
+"/    patternUsed := FileSelectionBox lastSuffixPatternUsedForDefault:patternArg.
+"/
+"/    instance := self    
+"/        startApplicationFor:titleString 
+"/        default:defaultName 
+"/        ok:okText 
+"/        abort:abortText 
+"/        ifFail:failBlock 
+"/        pattern:patternUsed 
+"/        fromDirectory:aDirectoryPath 
+"/        whenBoxCreatedEvaluate:boxCreatedCallback
+"/        asLoadDialog:aBoolean
+"/        viewFiles:true
+"/        multipleSelect:false.
+"/
+"/    enteredFileNameString := instance result.
+"/    enteredFileNameString isEmptyOrNil ifTrue:[
+"/        ^ failBlock value
+"/    ].
+"/
+"/    enteredFileName := enteredFileNameString asFilename.
+"/    enteredFileName isRelative ifTrue:[
+"/        (versionSymbol isNil and:[#(http https ftp) includes:(enteredFileNameString upToAll:'://')]) ifTrue:[
+"/            ^ enteredFileNameString.
+"/        ] ifFalse:[
+"/            enteredFileName := instance directory construct:enteredFileNameString.
+"/            enteredFileNameString := enteredFileName asString.
+"/        ].
+"/    ].
+"/
+"/    FileSelectionBox lastFileSelectionDirectory:(enteredFileName directoryName).
+"/    (patternArg notEmptyOrNil and:[instance pattern ~= patternArg]) ifTrue:[
+"/        FileSelectionBox lastSuffixPatternUsedForDefault:patternArg is:instance pattern
+"/    ].
+"/
+"/    versionSymbol isNil ifTrue:[ ^ enteredFileNameString].
+"/    versionSymbol == #mustBeNew ifTrue:[
+"/        "/ file must not exist
+"/        enteredFileName exists ifTrue:[^ failBlock value].
+"/    ].
+"/    versionSymbol == #new ifTrue:[
+"/        "/ file should not exist
+"/        enteredFileName exists ifTrue:[
+"/            (Dialog confirm:(self classResources stringWithCRs:'''%1'' exists.\\Continue anyway ?' with:enteredFileNameString))
+"/                ifFalse:[^ failBlock value].
+"/        ].
+"/    ].
+"/    versionSymbol == #mustBeOld ifTrue:[
+"/        "/ file must exist
+"/        enteredFileName exists ifFalse:[^ failBlock value].
+"/    ].
+"/    versionSymbol == #old ifTrue:[
+"/        "/ file should exist
+"/        enteredFileName exists ifFalse:[
+"/            (self confirm:(self classResources stringWithCRs:'''%1'' does not exist yet.\\Continue anyway ?' with:enteredFileNameString))
+"/            ifFalse:[^ failBlock value].
+"/        ].
+"/    ].
+"/
+"/    ^ enteredFileNameString
+
+    "
      FileDialog 
         requestFileName:'enter a fileName:'
         default:''
@@ -465,7 +458,7 @@
         fromDirectory:Filename currentDirectory pathName
         whenBoxCreatedEvaluate:nil
         asLoadDialog:true
-"
+    "
 !
 
 requestFileName:titleString default:defaultName pattern:pattern fromDirectory:aDirectory
@@ -577,7 +570,14 @@
 "
 !
 
-requestFileNames:titleString default:defaultName ok:okText abort:abortText ifFail:failBlock pattern:pattern fromDirectory:aDirectoryPath whenBoxCreatedEvaluate:boxCreatedCallback asLoadDialog:asLoadDialog
+requestFileNames:titleString default:defaultName 
+    ok:okText abort:abortText 
+    ifFail:failBlock 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:asLoadDialog
+
     "launch a Dialog, which allows user to enter a filename.
      The files presented initially are those in aDirectoryPathOrNil, or the
      last fileBox directory (default: current directory) (if a nil path is given).
@@ -586,32 +586,16 @@
      Return all selected Filenames as filenames in a collection, or nil if cancel was pressed
     "
 
-
-    | instance enteredFileNames lastDirectory|
-
-    instance := self    
-        startApplicationFor:titleString 
-        default:defaultName 
-        ok:okText 
-        abort:abortText 
+    ^ self new
+        requestFileNames:titleString default:defaultName 
+        ok:okText abort:abortText 
         ifFail:failBlock 
-        pattern:pattern 
+        pattern:patternArg 
         fromDirectory:aDirectoryPath 
-        whenBoxCreatedEvaluate:boxCreatedCallback
-        asLoadDialog:asLoadDialog 
-        viewFiles:true
-        multipleSelect:true.
-
-    enteredFileNames := instance currentSelectedFiles.
-    (enteredFileNames isEmpty or:[instance result isNil]) ifTrue:[
-        ^ failBlock value
-    ].
-    lastDirectory := enteredFileNames first.
-    lastDirectory := lastDirectory isFilename ifTrue:[lastDirectory directory] ifFalse:[lastDirectory].
-    FileSelectionBox lastFileSelectionDirectory:(lastDirectory directoryName).
-    ^ enteredFileNames
-
-"
+        whenBoxCreatedEvaluate:boxCreatedCallback 
+        asLoadDialog:asLoadDialog
+
+    "
      FileDialog 
         requestFileNames:'enter a fileName:'
         default:''
@@ -622,10 +606,19 @@
         fromDirectory:Filename currentDirectory pathName
         whenBoxCreatedEvaluate:nil
         asLoadDialog:true.
-"
+    "
 !
 
-startApplicationFor:titleString default:initialDefaultFileNameArg ok:okTextArg abort:abortTextArg ifFail:failBlock pattern:pattern fromDirectory:aDirectoryPath whenBoxCreatedEvaluate:boxCreatedCallback asLoadDialog:asLoadDialog viewFiles:viewFiles multipleSelect:multipleSelect
+startApplicationFor:titleString default:initialDefaultFileNameArg 
+    ok:okTextArg abort:abortTextArg 
+    ifFail:failBlock 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:asLoadDialog 
+    viewFiles:viewFilesArg 
+    multipleSelect:multipleSelectArg
+
     "launch a Dialog, which allows user to enter a filename.
      The files presented initially are those in aDirectoryPathOrNil, or the
      last fileBox directory (default: current directory) (if a nil path is given).
@@ -641,24 +634,53 @@
         #any (other)    - no validation
     "
 
-
-    |defaultDir instance defaultFile okText abortText initialDefaultFileName|
-
-    initialDefaultFileName := initialDefaultFileNameArg.
-    initialDefaultFileName notNil ifTrue:[ 
-        initialDefaultFileName := initialDefaultFileName asFilename.
-    ].
-
-    okText := okTextArg.
-    okText isNil ifTrue:[ okText := self resources string:'OK' ]. 
-    abortText := abortTextArg.
-    abortText isNil ifTrue:[ abortText := self resources string:'Cancel' ]. 
-
-    defaultDir := aDirectoryPath.
-"/    defaultDir isNil ifTrue:[
-"/        (initialDefaultFileName size > 0 and:[initialDefaultFileName exists]) ifTrue:[
-"/            defaultDir := initialDefaultFileName asAbsoluteFilename directory.
-"/        ] ifFalse:[
+    ^ self new 
+        startApplicationFor:titleString default:initialDefaultFileNameArg 
+        ok:okTextArg abort:abortTextArg 
+        ifFail:failBlock 
+        pattern:patternArg 
+        fromDirectory:aDirectoryPath 
+        whenBoxCreatedEvaluate:boxCreatedCallback 
+        asLoadDialog:asLoadDialog 
+        viewFiles:viewFilesArg 
+        multipleSelect:multipleSelectArg
+"/
+"/    |defaultDir instance defaultFile okText abortText initialDefaultFileName|
+"/
+"/    initialDefaultFileName := initialDefaultFileNameArg.
+"/    initialDefaultFileName notNil ifTrue:[ 
+"/        initialDefaultFileName := initialDefaultFileName asFilename.
+"/    ].
+"/
+"/    okText := okTextArg.
+"/    okText isNil ifTrue:[ okText := self resources string:'OK' ]. 
+"/    abortText := abortTextArg.
+"/    abortText isNil ifTrue:[ abortText := self resources string:'Cancel' ]. 
+"/
+"/    defaultDir := aDirectoryPath.
+"/"/    defaultDir isNil ifTrue:[
+"/"/        (initialDefaultFileName size > 0 and:[initialDefaultFileName exists]) ifTrue:[
+"/"/            defaultDir := initialDefaultFileName asAbsoluteFilename directory.
+"/"/        ] ifFalse:[
+"/"/            defaultDir := FileSelectionBox lastFileSelectionDirectory.
+"/"/            defaultDir isNil ifTrue:[
+"/"/                defaultDir := Filename currentDirectory asAbsoluteFilename.        
+"/"/            ].
+"/"/            defaultDir asFilename exists ifFalse:[
+"/"/                defaultDir := nil
+"/"/            ].
+"/"/        ]
+"/"/    ].
+"/    (initialDefaultFileName notNil 
+"/    and:[initialDefaultFileName isAbsolute 
+"/    and:[true "initialDefaultFileName asFilename exists"]]) ifTrue:[
+"/        defaultDir := initialDefaultFileName asAbsoluteFilename.
+"/        true "viewFiles" ifTrue:[
+"/            defaultFile := defaultDir asAbsoluteFilename.
+"/            defaultDir := defaultDir directory.
+"/        ].
+"/    ] ifFalse:[
+"/        defaultDir isNil ifTrue:[
 "/            defaultDir := FileSelectionBox lastFileSelectionDirectory.
 "/            defaultDir isNil ifTrue:[
 "/                defaultDir := Filename currentDirectory asAbsoluteFilename.        
@@ -668,67 +690,48 @@
 "/            ].
 "/        ]
 "/    ].
-    (initialDefaultFileName notNil 
-    and:[initialDefaultFileName isAbsolute 
-    and:[true "initialDefaultFileName asFilename exists"]]) ifTrue:[
-        defaultDir := initialDefaultFileName asAbsoluteFilename.
-        true "viewFiles" ifTrue:[
-            defaultFile := defaultDir asAbsoluteFilename.
-            defaultDir := defaultDir directory.
-        ].
-    ] ifFalse:[
-        defaultDir isNil ifTrue:[
-            defaultDir := FileSelectionBox lastFileSelectionDirectory.
-            defaultDir isNil ifTrue:[
-                defaultDir := Filename currentDirectory asAbsoluteFilename.        
-            ].
-            defaultDir asFilename exists ifFalse:[
-                defaultDir := nil
-            ].
-        ]
-    ].
-    defaultDir := defaultDir asFilename asAbsoluteFilename.
-"/    [defaultDir exists] whileFalse:[
-"/        defaultDir := defaultDir directory.
+"/    defaultDir := defaultDir asFilename asAbsoluteFilename.
+"/"/    [defaultDir exists] whileFalse:[
+"/"/        defaultDir := defaultDir directory.
+"/"/    ].
+"/
+"/    defaultFile isNil ifTrue:[
+"/        viewFiles ifFalse:[
+"/            defaultFile := defaultDir asAbsoluteFilename.
+"/        ] ifTrue:[
+"/            (initialDefaultFileName notNil and:[initialDefaultFileName withoutSuffix baseName ~= '*']) ifTrue:[
+"/                defaultFile := defaultDir construct:initialDefaultFileName baseName.
+"/            ] ifFalse:[
+"/                defaultFile := defaultDir.
+"/            ].
+"/        ].
 "/    ].
-
-    defaultFile isNil ifTrue:[
-        viewFiles ifFalse:[
-            defaultFile := defaultDir asAbsoluteFilename.
-        ] ifTrue:[
-            (initialDefaultFileName notNil and:[initialDefaultFileName withoutSuffix baseName ~= '*']) ifTrue:[
-                defaultFile := defaultDir construct:initialDefaultFileName baseName.
-            ] ifFalse:[
-                defaultFile := defaultDir.
-            ].
-        ].
-    ].
-
-    instance := self new.
-    defaultDir notNil ifTrue:[instance directory:defaultDir].
-    instance 
-        multipleSelect:multipleSelect ? false;
-        startFilename:defaultFile;
-        pattern:(pattern isEmptyOrNil ifTrue:['*'] ifFalse:[pattern]);
-        initialText:titleString;
-        beLoadDialog:asLoadDialog ? false;
-        viewFiles:viewFiles ? true.
-
-    instance okLabelHolder value:okText.
-    instance cancelLabelHolder value:abortText.
-
-    self setDoubleClickActionFor:instance.
-    instance allButOpenInterface:#windowSpec.
-    boxCreatedCallback notNil ifTrue:[boxCreatedCallback value:instance].
-
-    "/ Dialog aboutToOpenBoxNotificationSignal raiseRequestWith:instance.
-    instance treeBrowser sortCaseless value: (Filename isCaseSensitive not).
-    instance openWindowModal.
-    ^ instance
-
-    "Modified: / 23-08-2006 / 12:24:54 / cg"
-    "Modified: / 13-12-2006 / 16:25:42 / User"
-    "Modified (format): / 14-02-2017 / 12:49:34 / cg"
+"/
+"/    instance := self new.
+"/    defaultDir notNil ifTrue:[instance directory:defaultDir].
+"/    instance 
+"/        multipleSelect:multipleSelect ? false;
+"/        startFilename:defaultFile;
+"/        pattern:(pattern isEmptyOrNil ifTrue:['*'] ifFalse:[pattern]);
+"/        initialText:titleString;
+"/        beLoadDialog:asLoadDialog ? false;
+"/        viewFiles:viewFiles ? true.
+"/
+"/    instance okLabelHolder value:okText.
+"/    instance cancelLabelHolder value:abortText.
+"/
+"/    self setDoubleClickActionFor:instance.
+"/    instance allButOpenInterface:#windowSpec.
+"/    boxCreatedCallback notNil ifTrue:[boxCreatedCallback value:instance].
+"/
+"/    "/ Dialog aboutToOpenBoxNotificationSignal raiseRequestWith:instance.
+"/    instance treeBrowser sortCaseless value: (Filename isCaseSensitive not).
+"/    instance openWindowModal.
+"/    ^ instance
+"/
+"/    "Modified: / 23-08-2006 / 12:24:54 / cg"
+"/    "Modified: / 13-12-2006 / 16:25:42 / User"
+"/    "Modified (format): / 14-02-2017 / 12:49:34 / cg"
 ! !
 
 !FileDialog class methodsFor:'accessing'!
@@ -742,8 +745,9 @@
 !
 
 additionalFolderItemsInMenu:aCollectionOfFoldernames
-    "any additional folders to be shown in the 'File' menu;
-     each element can be either a filename or a pair with filename and menu-item-label."
+    "any additional folders to be shown in every 'File' menu;
+     each element can be either a filename or a pair with filename and menu-item-label,
+     or an assoc, with key=filename and value=label."
 
     "/ FileDialog additionalFolderItemsInMenu:#('/Users/exept' ('/Users/exept/cg_work' 'WorkDir'))
     AdditionalFolderItemsInMenu := aCollectionOfFoldernames
@@ -1036,6 +1040,87 @@
 
 !FileDialog class methodsFor:'menu specs'!
 
+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:FileDialog andSelector:#mainMenu
+     (Menu new fromLiteralArrayEncoding:(FileDialog mainMenu)) startUp
+    "
+
+    <resource: #menu>
+
+    ^ 
+     #(Menu 
+           (MenuItem
+              label: 'Up'
+              itemValue: doGoDirectoryUp
+           ) 
+           (MenuItem
+              label: '-'
+           ) 
+           (MenuItem
+              activeHelpKey: directoryBack
+              enabled: enableBack
+              label: 'Back'
+              itemValue: doBack
+           ) 
+           (MenuItem
+              activeHelpKey: directoryBack
+              enabled: enableForward
+              label: 'Forward'
+              itemValue: doForward
+           ) 
+           (MenuItem
+              label: '-'
+           ) 
+           (MenuItem
+              enabled: enableHome
+              label: 'Home Directory'
+              itemValue: doGotoHomeDirectory
+              labelImage: (ResourceRetriever ToolbarIconLibrary homeIcon16x16 'Home Directory')
+           ) 
+           (MenuItem
+              enabled: enableGotoDesktopDirectory
+              label: 'Desktop Directory'
+              itemValue: doGotoDesktopDirectory
+              labelImage: (ResourceRetriever ToolbarIconLibrary desktop16x16Icon 'Desktop Directory')
+           ) 
+           (MenuItem
+              enabled: enableGotoDocuments
+              label: 'Documents Directory'
+              itemValue: doGotoDocumentsDirectory
+           ) 
+           (MenuItem
+              enabled: enableGotoDownloads
+              label: 'Downloads Directory'
+              itemValue: doGotoDownloadsDirectory
+           ) 
+           (MenuItem
+              label: 'Menu Slice'
+              submenuChannel: additionalFoldersMenuSliceHolder
+              isMenuSlice: true
+           ) 
+           (MenuItem
+              label: '-'
+           ) 
+           (MenuItem
+              label: 'Bookmarks'
+              submenuChannel: bookmarksMenu
+              labelImage: (ResourceRetriever ToolbarIconLibrary bookmarks14x14 'Bookmarks')
+           ) 
+           (MenuItem
+              label: 'Visited Directories'
+              submenuChannel: visitedDirectoriesMenu
+           )
+         )
+!
+
 mainMenu
     "This resource specification was automatically generated
      by the MenuEditor of ST/X."
@@ -1055,6 +1140,10 @@
      #(Menu 
        (MenuItem
           label: 'Directory'
+          submenuChannel: directoryMenu
+       ) 
+       (MenuItem
+          label: 'Directory'
           submenu: 
          (Menu 
            (MenuItem
@@ -1103,7 +1192,7 @@
            ) 
            (MenuItem
               label: 'Menu Slice'
-              submenuChannel: additionalFoldersMenuSlice
+              submenuChannel: additionalFoldersMenuSliceHolder
               isMenuSlice: true
            ) 
            (MenuItem
@@ -1310,8 +1399,379 @@
 
 ! !
 
+!FileDialog methodsFor:'API'!
+
+requestDirectoryName:titleString default:aFileName 
+    ok:okText abort:abortText 
+    version:versionSymbol 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    ifFail:failBlock 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:aBoolean
+
+    "same as requestFileName, but only show directories"
+
+    |enteredFileName enteredFileNameString|
+
+    self    
+        startApplicationFor:titleString 
+        default:aFileName 
+        ok:okText 
+        abort:abortText 
+        ifFail:failBlock 
+        pattern:patternArg 
+        fromDirectory:aDirectoryPath 
+        whenBoxCreatedEvaluate:boxCreatedCallback
+        asLoadDialog:aBoolean
+        viewFiles:false
+        multipleSelect:false.
+
+    enteredFileNameString := self result.
+    enteredFileNameString isNil ifTrue:[
+        ^ failBlock value
+    ].
+    enteredFileName := enteredFileNameString asFilename.
+    enteredFileName isRelative ifTrue:[
+        enteredFileName := self directory construct:enteredFileNameString
+    ].
+
+    enteredFileNameString := enteredFileName asString.
+    (enteredFileName notNil 
+    and:[enteredFileNameString notEmpty]) ifTrue:[
+        versionSymbol isNil ifTrue:[ ^ enteredFileNameString].
+        versionSymbol == #mustBeNew ifTrue:[
+            "/ file may not exist
+            enteredFileName exists ifTrue:[^ ''].
+        ].
+        versionSymbol == #new ifTrue:[
+            "/ file may not exist
+            enteredFileName exists ifTrue:[
+                (Dialog confirm:(resources stringWithCRs:'''%1'' exists.\\Continue anyway ?' with:enteredFileNameString))
+                    ifFalse:[^ ''].
+            ].
+        ].
+        versionSymbol == #mustBeOld ifTrue:[
+            enteredFileName exists ifFalse:[^ ''].
+        ].
+        versionSymbol == #old ifTrue:[
+            "/ file may not exist
+            enteredFileName exists ifFalse:[
+                (self confirm:(resources stringWithCRs:'''%1'' does not exist yet.\\Continue anyway ?' with:enteredFileNameString))
+                ifFalse:[^ ''].
+            ].
+        ].
+        FileSelectionBox lastFileSelectionDirectory:(enteredFileNameString).
+    ].
+    ^ enteredFileNameString
+
+    "
+     FileDialog
+        requestDirectoryName:'which directory ?' 
+        default:Filename currentDirectory pathName
+        ifFail:nil
+    "
+    "
+     FileDialog
+        requestDirectoryName:'which directory ?' 
+        default:Filename currentDirectory pathName 
+        ok:nil
+        abort:nil 
+        version:nil 
+        pattern:nil 
+        fromDirectory:nil 
+        ifFail:nil 
+        whenBoxCreatedEvaluate:nil
+        asLoadDialog:true
+    "
+!
+
+requestFileName:titleString default:defaultName ok:okText abort:abortText 
+    version:versionSymbol 
+    ifFail:failBlock 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:aBoolean
+
+    "launch a Dialog, which allows user to enter a filename.
+     The files presented initially are those in aDirectoryPathOrNil, or the
+     last fileBox directory (default: current directory) (if a nil path is given).
+     The box will show okText in its okButton, abortText in the abortButton.
+     The matchPattern is set to pattern initially.
+     Return the string, or nil if cancel was pressed
+     The version argument allows validation of the files existance;
+     it may be any of:
+        #mustBeNew      - fail (return empty string) if the file exists
+        #new            - confirm if the file exists
+        #mustBeOld      - fail if the file does not exist
+        #old            - confirm if the file does not exist
+        #any (other)    - no validation
+    "
+
+
+    |patternUsed enteredFileName enteredFileNameString|
+
+    patternUsed := FileSelectionBox lastSuffixPatternUsedForDefault:patternArg.
+
+    self    
+        startApplicationFor:titleString 
+        default:defaultName 
+        ok:okText 
+        abort:abortText 
+        ifFail:failBlock 
+        pattern:patternUsed 
+        fromDirectory:aDirectoryPath 
+        whenBoxCreatedEvaluate:boxCreatedCallback
+        asLoadDialog:aBoolean
+        viewFiles:true
+        multipleSelect:false.
+
+    enteredFileNameString := self result.
+    enteredFileNameString isEmptyOrNil ifTrue:[
+        ^ failBlock value
+    ].
+
+    enteredFileName := enteredFileNameString asFilename.
+    enteredFileName isRelative ifTrue:[
+        (versionSymbol isNil and:[#(http https ftp) includes:(enteredFileNameString upToAll:'://')]) ifTrue:[
+            ^ enteredFileNameString.
+        ] ifFalse:[
+            enteredFileName := self directory construct:enteredFileNameString.
+            enteredFileNameString := enteredFileName asString.
+        ].
+    ].
+
+    FileSelectionBox lastFileSelectionDirectory:(enteredFileName directoryName).
+    (patternArg notEmptyOrNil and:[self pattern ~= patternArg]) ifTrue:[
+        FileSelectionBox lastSuffixPatternUsedForDefault:patternArg is:self pattern
+    ].
+
+    versionSymbol isNil ifTrue:[ ^ enteredFileNameString].
+    versionSymbol == #mustBeNew ifTrue:[
+        "/ file must not exist
+        enteredFileName exists ifTrue:[^ failBlock value].
+    ].
+    versionSymbol == #new ifTrue:[
+        "/ file should not exist
+        enteredFileName exists ifTrue:[
+            (Dialog confirm:(resources stringWithCRs:'''%1'' exists.\\Continue anyway ?' with:enteredFileNameString))
+                ifFalse:[^ failBlock value].
+        ].
+    ].
+    versionSymbol == #mustBeOld ifTrue:[
+        "/ file must exist
+        enteredFileName exists ifFalse:[^ failBlock value].
+    ].
+    versionSymbol == #old ifTrue:[
+        "/ file should exist
+        enteredFileName exists ifFalse:[
+            (self confirm:(resources stringWithCRs:'''%1'' does not exist yet.\\Continue anyway ?' with:enteredFileNameString))
+            ifFalse:[^ failBlock value].
+        ].
+    ].
+
+    ^ enteredFileNameString
+"
+     FileDialog 
+        requestFileName:'enter a fileName:'
+        default:''
+        ok:nil 
+        abort:nil
+        version:nil
+        ifFail:['none']
+        pattern:'*'
+        fromDirectory:Filename currentDirectory pathName
+        whenBoxCreatedEvaluate:nil
+        asLoadDialog:true
+"
+!
+
+requestFileNames:titleString default:defaultName 
+    ok:okText abort:abortText 
+    ifFail:failBlock 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:asLoadDialog
+
+    "launch a Dialog, which allows user to enter a filename.
+     The files presented initially are those in aDirectoryPathOrNil, or the
+     last fileBox directory (default: current directory) (if a nil path is given).
+     The box will show okText in its okButton, abortText in the abortButton.
+     The matchPattern is set to pattern initially.
+     Return all selected Filenames as filenames in a collection, or nil if cancel was pressed
+    "
+
+
+    | enteredFileNames lastDirectory|
+
+    self    
+        startApplicationFor:titleString 
+        default:defaultName 
+        ok:okText 
+        abort:abortText 
+        ifFail:failBlock 
+        pattern:patternArg 
+        fromDirectory:aDirectoryPath 
+        whenBoxCreatedEvaluate:boxCreatedCallback
+        asLoadDialog:asLoadDialog 
+        viewFiles:true
+        multipleSelect:true.
+
+    enteredFileNames := self currentSelectedFiles.
+    (enteredFileNames isEmpty or:[self result isNil]) ifTrue:[
+        ^ failBlock value
+    ].
+    lastDirectory := enteredFileNames first.
+    lastDirectory := lastDirectory isFilename ifTrue:[lastDirectory directory] ifFalse:[lastDirectory].
+    FileSelectionBox lastFileSelectionDirectory:(lastDirectory directoryName).
+    ^ enteredFileNames
+
+    "
+     FileDialog 
+        requestFileNames:'enter a fileName:'
+        default:''
+        ok:nil 
+        abort:nil 
+        ifFail:['none']
+        pattern:'*.conf'
+        fromDirectory:Filename currentDirectory pathName
+        whenBoxCreatedEvaluate:nil
+        asLoadDialog:true.
+    "
+!
+
+startApplicationFor:titleString default:initialDefaultFileNameArg 
+    ok:okTextArg abort:abortTextArg 
+    ifFail:failBlock 
+    pattern:patternArg 
+    fromDirectory:aDirectoryPath 
+    whenBoxCreatedEvaluate:boxCreatedCallback 
+    asLoadDialog:asLoadDialog 
+    viewFiles:viewFilesArg 
+    multipleSelect:multipleSelectArg
+
+    "launch a Dialog, which allows user to enter a filename.
+     The files presented initially are those in aDirectoryPathOrNil, or the
+     last fileBox directory (default: current directory) (if a nil path is given).
+     The box will show okText in its okButton, abortText in the abortButton.
+     The matchPattern is set to patternArg initially.
+     Return the string, or nil if cancel was pressed
+     The version argument allows validation of the files existance;
+     it may be any of:
+        #mustBeNew      - fail (return empty string) if the file exists
+        #new            - confirm if the file exists
+        #mustBeOld      - fail if the file does not exist
+        #old            - confirm if the file does not exist
+        #any (other)    - no validation
+    "
+
+
+    |defaultDir defaultFile okText abortText initialDefaultFileName|
+
+    initialDefaultFileName := initialDefaultFileNameArg.
+    initialDefaultFileName notNil ifTrue:[ 
+        initialDefaultFileName := initialDefaultFileName asFilename.
+    ].
+
+    okText := okTextArg.
+    okText isNil ifTrue:[ okText := self resources string:'OK' ]. 
+    abortText := abortTextArg.
+    abortText isNil ifTrue:[ abortText := self resources string:'Cancel' ]. 
+
+    defaultDir := aDirectoryPath.
+"/    defaultDir isNil ifTrue:[
+"/        (initialDefaultFileName size > 0 and:[initialDefaultFileName exists]) ifTrue:[
+"/            defaultDir := initialDefaultFileName asAbsoluteFilename directory.
+"/        ] ifFalse:[
+"/            defaultDir := FileSelectionBox lastFileSelectionDirectory.
+"/            defaultDir isNil ifTrue:[
+"/                defaultDir := Filename currentDirectory asAbsoluteFilename.        
+"/            ].
+"/            defaultDir asFilename exists ifFalse:[
+"/                defaultDir := nil
+"/            ].
+"/        ]
+"/    ].
+    (initialDefaultFileName notNil 
+    and:[initialDefaultFileName isAbsolute 
+    and:[true "initialDefaultFileName asFilename exists"]]) ifTrue:[
+        defaultDir := initialDefaultFileName asAbsoluteFilename.
+        true "viewFilesArg" ifTrue:[
+            defaultFile := defaultDir asAbsoluteFilename.
+            defaultDir := defaultDir directory.
+        ].
+    ] ifFalse:[
+        defaultDir isNil ifTrue:[
+            defaultDir := FileSelectionBox lastFileSelectionDirectory.
+            defaultDir isNil ifTrue:[
+                defaultDir := Filename currentDirectory asAbsoluteFilename.        
+            ].
+            defaultDir asFilename exists ifFalse:[
+                defaultDir := nil
+            ].
+        ]
+    ].
+    defaultDir := defaultDir asFilename asAbsoluteFilename.
+"/    [defaultDir exists] whileFalse:[
+"/        defaultDir := defaultDir directory.
+"/    ].
+
+    defaultFile isNil ifTrue:[
+        viewFilesArg ifFalse:[
+            defaultFile := defaultDir asAbsoluteFilename.
+        ] ifTrue:[
+            (initialDefaultFileName notNil and:[initialDefaultFileName withoutSuffix baseName ~= '*']) ifTrue:[
+                defaultFile := defaultDir construct:initialDefaultFileName baseName.
+            ] ifFalse:[
+                defaultFile := defaultDir.
+            ].
+        ].
+    ].
+
+    defaultDir notNil ifTrue:[self directory:defaultDir].
+    self multipleSelect:(multipleSelectArg ? false).
+    self startFilename:defaultFile.
+    self pattern:(patternArg isEmptyOrNil ifTrue:['*'] ifFalse:[patternArg]).
+    self initialText:titleString.
+    self beLoadDialog:asLoadDialog ? false.
+    self viewFiles:(viewFilesArg ? true).
+
+    self okLabelHolder value:okText.
+    self cancelLabelHolder value:abortText.
+
+    self doubleClickAction:[:anIndex|
+        | item |
+        item := self treeBrowser fileList at:anIndex ifAbsent:nil.
+        item notNil ifTrue:[
+            (self viewFiles and:[item isDirectory not]) ifTrue:[ 
+                self doAccept.
+            ]
+        ]
+    ].
+    self allButOpenInterface:#windowSpec.
+    boxCreatedCallback notNil ifTrue:[boxCreatedCallback value:self].
+
+    "/ Dialog aboutToOpenBoxNotificationSignal raiseRequestWith:instance.
+    self treeBrowser sortCaseless value: (Filename isCaseSensitive not).
+    self openWindowModal.
+
+    "Modified: / 23-08-2006 / 12:24:54 / cg"
+    "Modified: / 13-12-2006 / 16:25:42 / User"
+    "Modified (format): / 14-02-2017 / 12:49:34 / cg"
+! !
+
 !FileDialog methodsFor:'accessing'!
 
+additionalFolderItemsInMenu:aCollectionOfFoldernames
+    "any additional folders to be shown in this 'File' menu;
+     each element can be either a filename or a pair with filename and menu-item-label,
+     or an assoc, with key=filename and value=label."
+
+    additionalFolderItemsInMenu := aCollectionOfFoldernames.
+!
+
 appendWasPressed
     "valid after the dialog has been closed: true if append was pressed"
 
@@ -1985,35 +2445,6 @@
     "Created: / 03-06-2013 / 17:47:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
-!FileDialog methodsFor:'error handling'!
-
-additionalFoldersMenuSlice
-    |slice|
-
-    "/ AdditionalFolderItemsInMenu := #('/foo' '/etc' '/Users')
-    "/ AdditionalFolderItemsInMenu := #('/foo' ('/etc' 'Other') '/Users')
-    AdditionalFolderItemsInMenu notEmptyOrNil ifTrue:[
-        slice := Menu new.
-        AdditionalFolderItemsInMenu do:[:eachFolderNameOrPair |
-            |item fn label|
-
-            eachFolderNameOrPair isArray ifTrue:[
-                fn := eachFolderNameOrPair first asFilename.
-                label := eachFolderNameOrPair second.
-            ] ifFalse:[
-                fn := eachFolderNameOrPair asFilename.
-                label := eachFolderNameOrPair.
-            ].
-            item := MenuItem new.
-            item label:label.
-            item itemValue:[ self gotoFile:fn ].
-            item enabled:[ fn exists and:[fn isDirectory and:[fn isExecutable]]].
-            slice addItem:item.
-        ].
-    ].
-    ^ slice
-! !
-
 !FileDialog methodsFor:'event handling'!
 
 processEvent:anEvent
@@ -2233,6 +2664,51 @@
 
 !FileDialog methodsFor:'menus'!
 
+additionalFoldersMenuSlice
+    |slice|
+
+    "/ AdditionalFolderItemsInMenu := #('/foo' '/etc' '/Users')
+    "/ AdditionalFolderItemsInMenu := #('/foo' ('/etc' 'Other') '/Users')
+
+    { additionalFolderItemsInMenu . AdditionalFolderItemsInMenu } do:[:moreOrEmpty |
+        moreOrEmpty notEmptyOrNil ifTrue:[
+            slice isNil ifTrue:[
+                slice := Menu new.
+                slice addSeparator.
+            ].
+            moreOrEmpty do:[:eachFolderNameOrPair |
+                |item fn label|
+
+                eachFolderNameOrPair isArray ifTrue:[
+                    fn := eachFolderNameOrPair first.
+                    label := eachFolderNameOrPair second.
+                ] ifFalse:[
+                    eachFolderNameOrPair isAssociation ifTrue:[
+                        fn := eachFolderNameOrPair key.
+                        label := eachFolderNameOrPair value.
+                    ] ifFalse:[
+                        fn := eachFolderNameOrPair.
+                        label := eachFolderNameOrPair.
+                    ].
+                ].
+                fn := fn asFilename.
+
+                item := MenuItem new.
+                item label:label asString.
+                item itemValue:[ self gotoFile:fn ].
+                item enabled:[ fn exists and:[fn isDirectory and:[fn isExecutable]]].
+                slice addItem:item.
+            ].
+        ].
+    ].
+    ^ slice
+!
+
+additionalFoldersMenuSliceHolder
+    ^ [ self additionalFoldersMenuSlice ]
+
+!
+
 bookmarksMenu
     ^ treeBrowser bookmarksMenu
 !