tz@404: " tz@502: COPYRIGHT (c) 1997 by eXept Software AG tz@404: All Rights Reserved tz@404: tz@404: This software is furnished under a license and may be used tz@404: only in accordance with the terms of that license and with the tz@404: inclusion of the above copyright notice. This software may not tz@404: be provided or otherwise made available to, or used by, any tz@404: other person. No title to or ownership of the software is tz@404: hereby transferred. tz@404: " cg@1379: "{ Package: 'stx:libtool2' }" cg@1379: tz@404: SelectionBrowser subclass:#FileSelectionBrowser tz@404: instanceVariableNames:'selectedFileFilter directoryView listOfFileFilters directory' tz@404: classVariableNames:'' tz@404: poolDictionaries:'' tz@619: category:'Interface-Dialogs' tz@404: ! tz@404: tz@404: !FileSelectionBrowser class methodsFor:'documentation'! tz@404: tz@404: copyright tz@404: " tz@502: COPYRIGHT (c) 1997 by eXept Software AG tz@404: All Rights Reserved tz@404: tz@404: This software is furnished under a license and may be used tz@404: only in accordance with the terms of that license and with the tz@404: inclusion of the above copyright notice. This software may not tz@404: be provided or otherwise made available to, or used by, any tz@404: other person. No title to or ownership of the software is tz@404: hereby transferred. tz@404: " tz@404: tz@404: tz@404: ! tz@404: tz@404: documentation tz@404: " tz@745: The FileSelectionBrowser allows you to browse in file directories tz@745: and to select files in order to load file contents or to save tz@745: something to files. tz@745: tz@745: [start with:] tz@745: FileSelectionBrowser open tz@502: tz@502: [author:] tz@745: Thomas Zwick, eXept Software AG tz@404: " tz@404: ! ! tz@404: tz@404: !FileSelectionBrowser class methodsFor:'instance creation'! tz@404: tz@404: request: aTitle fileName: aFileName inDirectory: dir withFileFilters: fileFilters cg@1353: "ask for a file in some directory, cg@1353: using a fileFilter (list of mathcPatterns). cg@1353: Initial selection is aFileName. cg@1353: return the pathName or nil if canceled." cg@1353: cg@957: ^self new cg@957: title: aTitle; cg@957: fileName: aFileName; cg@957: directory: dir; cg@957: listOfFileFilters: fileFilters; cg@957: open cg@957: tz@745: " cg@1353: self cg@1353: request: 'Select Your Profile File' cg@1353: fileName: '.profile' cg@1353: inDirectory: Filename homeDirectory cg@1353: withFileFilters: #('.*') tz@745: " tz@404: cg@957: "Modified: / 17.8.1998 / 10:15:50 / cg" cg@957: ! cg@957: cg@1353: request:aTitle fileName:aFileName withFileFilters:fileFilters cg@1353: "ask for a file in some directory, cg@1353: using a fileFilter (list of mathcPatterns). cg@1353: Initial selection is aFileName. cg@1353: return the pathName or nil if canceled." cg@1353: cg@1353: ^ self new tz@404: title: aTitle; tz@404: fileName: aFileName; tz@404: listOfFileFilters: fileFilters; tz@404: open tz@404: tz@745: " cg@1353: self cg@1353: request: 'Select Your Profile File' cg@1353: fileName: '/etc/fstab' cg@1353: withFileFilters: #('*') cg@1353: " cg@1353: cg@1353: "Modified: / 17.8.1998 / 10:15:50 / cg" cg@1353: ! cg@1353: cg@1353: request:aTitle inDirectory:aPath withFileFilters:fileFilters cg@1353: "ask for a file in some directory, cg@1353: using a fileFilter (list of mathcPatterns) cg@1353: return the pathName or nil if canceled." cg@1353: cg@1353: ^ (self new) cg@1353: title:aTitle; cg@1353: directory:aPath; cg@1353: listOfFileFilters:fileFilters; cg@1353: open cg@1353: cg@1353: " cg@1353: self cg@1353: request: 'Select A File' cg@1353: inDirectory: '/etc' cg@1353: withFileFilters: #('*.conf') tz@745: " tz@462: cg@957: "Modified: / 17.8.1998 / 10:15:50 / cg" tz@745: ! tz@745: cg@1353: request:aTitle pathName:aPath withFileFilters:fileFilters cg@1353: "ask for a file in some directory, cg@1353: using a fileFilter (list of mathcPatterns) cg@1353: return the pathName or nil if canceled. cg@1353: Obsolete - for backward compatibility." cg@1353: cg@1353: ^ self cg@1353: request:aTitle inDirectory:aPath withFileFilters:fileFilters cg@957: tz@745: " cg@1353: self cg@1353: request: 'Select A File' cg@1353: pathName: '/etc' cg@1353: withFileFilters: #('*.conf') tz@745: " tz@745: cg@957: "Modified: / 17.8.1998 / 10:15:50 / cg" tz@745: ! tz@745: cg@1353: request:aTitle withFileFilters:fileFilters cg@1353: "ask for a file in the current directory, cg@1353: using a fileFilter (list of mathcPatterns) cg@1353: return the pathName or nil if canceled" cg@1353: cg@1353: ^ (self new) cg@1353: title:aTitle; cg@1353: listOfFileFilters:fileFilters; cg@957: open cg@1353: tz@745: " cg@1353: self request:'Select A File' withFileFilters:#('.*') cg@1353: self request:'Select A File' withFileFilters:#('*.mak;*.st') tz@745: " tz@745: cg@957: "Modified: / 17.8.1998 / 10:15:49 / cg" tz@462: ! tz@462: tz@404: requestFileName cg@1353: "ask for a file in the current directory; cg@1353: return the pathName or nil if canceled" cg@1353: cg@1353: ^ self new cg@1353: title:'Select A File'; cg@1353: open cg@1353: cg@1353: " cg@1353: self requestFileName tz@745: " cg@1353: cg@1353: "Modified: / 17.8.1998 / 10:15:48 / cg" cg@1353: ! cg@1353: cg@1353: requestFileNameInDirectory:aPath cg@1353: "ask for a file in a directory; cg@1353: return the pathName or nil if canceled" cg@1353: cg@1353: ^ self new cg@1353: title:'Select A File'; cg@1353: directory:aPath; cg@1353: open cg@1353: cg@1353: " cg@1353: self requestFileNameInDirectory:'/etc' tz@745: " tz@404: cg@957: "Modified: / 17.8.1998 / 10:15:48 / cg" tz@404: ! ! tz@404: tz@404: !FileSelectionBrowser class methodsFor:'accessing'! tz@404: tz@404: loadImageFileNameFilters cg@992: ^ OrderedCollection cg@992: withAll: #( cg@992: '*.xpm ; *.xbm ; *.gif ; *.tif ; *.tiff ; *.bmp ; *.jpeg ; *.jpg' cg@992: '*' cg@992: ) tz@404: cg@992: "Modified: / 12.9.1998 / 17:23:42 / cg" tz@404: ! tz@404: tz@404: projectFileNameFilters cg@992: ^Array withAll: #( cg@992: '*.st ; *.*o ; *.s ; *.c ; Make* ; *.project' cg@992: '*' cg@992: ) tz@404: cg@992: "Modified: / 12.9.1998 / 17:23:22 / cg" tz@404: ! tz@404: tz@404: saveImageFileNameFilters cg@992: ^ OrderedCollection cg@992: withAll: #( cg@992: '*.xpm ; *.xbm ; *.gif ; *.tif ; *.tiff ; *.bmp' cg@992: '*' cg@992: ) tz@404: cg@992: "Modified: / 12.9.1998 / 17:23:54 / cg" tz@404: ! ! tz@404: tz@404: !FileSelectionBrowser class methodsFor:'interface specs'! tz@404: tz@404: windowSpec tz@745: "This resource specification was automatically generated tz@745: by the UIPainter of ST/X." tz@404: tz@745: "Do not manually edit this!! If it is corrupted, tz@745: the UIPainter may not be able to read the specification." tz@404: tz@404: " tz@404: UIPainter new openOnClass:FileSelectionBrowser andSelector:#windowSpec tz@404: FileSelectionBrowser new openInterface:#windowSpec tz@745: FileSelectionBrowser open tz@404: " tz@404: tz@404: tz@404: cg@1260: ^ cg@1260: #(#FullSpec cg@1260: #name: #windowSpec cg@1260: #window: cg@1260: #(#WindowSpec cg@1260: #label: 'File Selection Browser' cg@1260: #name: 'File Selection Browser' cg@1260: #min: #(#Point 10 10) cg@1260: #max: #(#Point 1152 900) cg@1260: #bounds: #(#Rectangle 18 51 618 401) cg@1260: ) cg@1260: #component: cg@1260: #(#SpecCollection cg@1260: #collection: #( cg@1260: #(#VariableHorizontalPanelSpec cg@1260: #name: 'panel' cg@1260: #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -40 1.0) cg@1260: #component: cg@1260: #(#SpecCollection cg@1260: #collection: #( cg@1260: #(#ViewSpec cg@1260: #name: 'view1' tz@745: #component: cg@1260: #(#SpecCollection cg@1260: #collection: #( cg@1260: #(#FileSelectionTreeSpec cg@1260: #name: 'directoryTreeView' cg@1260: #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -25 1.0) cg@1260: #model: #selectionOfDirectory cg@1379: #menu: #treeMenu cg@1260: #hasHorizontalScrollBar: true cg@1260: #hasVerticalScrollBar: true cg@1260: #miniScrollerHorizontal: true cg@1260: #showDirectoryIndicatorForRoot: false cg@1260: #showDirectoryIndicator: true cg@1260: #valueChangeSelector: #readDirectory cg@1260: #hierarchicalList: #rootOfDirectory cg@1260: #highlightMode: #line cg@1260: #itemClass: 'Directory' cg@1260: ) cg@1260: #(#InputFieldSpec cg@1260: #name: 'directoryInputField' cg@1260: #layout: #(#LayoutFrame 2 0.0 -22 1 0 1.0 0 1) cg@1260: #model: #selectionOfDirectory cg@1260: #immediateAccept: false cg@1260: #acceptOnPointerLeave: false tz@404: ) cg@1260: ) cg@1260: tz@404: ) cg@1260: ) cg@1260: #(#ViewSpec cg@1260: #name: 'view2' cg@1260: #component: cg@1260: #(#SpecCollection cg@1260: #collection: #( cg@1260: #(#InputFieldSpec cg@1260: #name: 'EditField' cg@1260: #layout: #(#LayoutFrame 1 0.0 -22 1 -2 1.0 0 1) cg@1260: #model: #valueOfFileName cg@1260: #acceptOnReturn: false cg@1260: #acceptOnTab: false cg@1260: #acceptOnPointerLeave: false cg@1260: ) cg@1260: #(#SubCanvasSpec cg@1260: #name: 'subCanvas1' cg@1260: #layout: #(#LayoutFrame 1 0.0 0 0.0 0 1.0 -25 1.0) cg@1260: #clientHolder: #directoryView cg@1260: ) cg@1260: ) cg@1260: cg@1260: ) cg@1260: ) cg@1260: ) cg@1260: tz@404: ) cg@1260: #handles: #(#Any 0.5 1.0) cg@1260: ) cg@1260: #(#UISubSpecification cg@1260: #name: 'SubSpecification' cg@1260: #layout: #(#LayoutFrame 2 0.0 -32 1 0 1.0 0 1.0) cg@1260: #majorKey: #ToolApplicationModel cg@1260: #minorKey: #windowSpecForCommitWithoutChannels cg@1260: ) cg@1260: ) cg@1260: cg@1260: ) tz@404: ) tz@404: ! ! tz@404: cg@1379: !FileSelectionBrowser class methodsFor:'menu specs'! cg@1379: cg@1379: treeMenu cg@1379: "This resource specification was automatically generated cg@1379: by the MenuEditor of ST/X." cg@1379: cg@1379: "Do not manually edit this!! If it is corrupted, cg@1379: the MenuEditor may not be able to read the specification." cg@1379: cg@1379: " cg@1379: MenuEditor new openOnClass:FileSelectionBrowser andSelector:#treeMenu cg@1379: (Menu new fromLiteralArrayEncoding:(FileSelectionBrowser treeMenu)) startUp cg@1379: " cg@1379: cg@1379: cg@1379: cg@1379: ^ cg@1379: #(#Menu cg@1379: #( cg@1379: #(#MenuItem cg@1458: #label: 'Goto Home Directory' cg@1458: #translateLabel: true cg@1458: #value: #menuGotoHomeDirectory cg@1458: ) cg@1458: #(#MenuItem cg@1458: #label: 'Goto Default Directory' cg@1458: #translateLabel: true cg@1458: #value: #menuGotoDefaultDirectory cg@1458: ) cg@1458: #(#MenuItem cg@1458: #label: '-' cg@1458: ) cg@1458: #(#MenuItem cg@1379: #label: 'Create directory...' cg@1379: #translateLabel: true cg@1379: #value: #menuCreateDirectory cg@1379: ) cg@1379: #(#MenuItem cg@1379: #label: '-' cg@1379: ) cg@1379: #(#MenuItem cg@1379: #label: 'Update' cg@1379: #translateLabel: true cg@1379: #value: #menuUpdate cg@1379: ) cg@1379: ) cg@1379: nil cg@1379: nil cg@1379: ) cg@1379: ! ! cg@1379: tz@404: !FileSelectionBrowser methodsFor:'accessing'! tz@404: tz@404: directory: aDirectory tz@404: cg@957: directory := (aDirectory ? Filename currentDirectory) asFilename asAbsoluteFilename pathName tz@745: cg@957: "Modified: / 17.8.1998 / 10:15:53 / cg" tz@404: ! tz@404: tz@404: directoryView cg@998: "return the directory view on the right side (a subcanvas)" tz@404: tz@404: ^directoryView ? (directoryView := DirectoryView new) cg@998: cg@998: "Modified: / 23.9.1998 / 16:46:50 / cg" tz@404: ! tz@404: tz@404: fileName: aFileName cg@1003: |prevDir| tz@404: tz@404: self valueOfFileName value: (aFileName ? '') asFilename baseName. cg@1003: prevDir := directory. cg@957: directory := directory ? ((aFileName ? '') asFilename asAbsoluteFilename directoryName). cg@957: cg@2445: "/ on systems with volumes, we might have to update the root here. cg@1003: (prevDir isNil cg@1003: or:[directory asFilename volume ~= prevDir asFilename volume]) ifTrue:[ cg@1003: self setRootForFile:directory asFilename. cg@1003: ]. cg@1003: cg@1003: "Modified: / 24.9.1998 / 23:41:08 / cg" tz@404: ! tz@404: tz@404: listOfFileFilters: anArray tz@404: tz@404: listOfFileFilters := anArray tz@404: ! ! tz@404: cg@1398: !FileSelectionBrowser methodsFor:'accessing-views'! tz@404: tz@592: directoryInputField tz@592: cg@1597: ^self componentAt: #directoryInputField tz@592: ! tz@592: tz@404: directoryTreeView tz@404: cg@1597: ^self componentAt: #directoryTreeView tz@404: ! ! tz@404: tz@404: !FileSelectionBrowser methodsFor:'aspects'! tz@404: tz@404: rootOfDirectory tz@404: tz@404: |holder| tz@404: (holder := builder bindingAt:#rootOfDirectory) isNil ifTrue:[ cg@998: builder cg@998: aspectAt:#rootOfDirectory cg@998: put: (holder := Filename rootDirectory name asValue). tz@404: ]. tz@404: ^ holder tz@404: cg@1011: "Modified: / 5.10.1998 / 12:46:10 / cg" tz@404: ! tz@404: tz@404: selectionOfDirectory tz@404: tz@404: |holder| tz@404: (holder := builder bindingAt:#selectionOfDirectory) isNil ifTrue:[ tz@404: builder aspectAt:#selectionOfDirectory put: (holder := ValueHolder new) tz@404: ]. tz@404: ^ holder tz@404: tz@404: ! tz@404: cg@998: setRootForFile:aFilename cg@1144: |f root parent| cg@1000: cg@1000: f := aFilename asFilename. cg@1000: root := Filename rootDirectoryOnVolume:f volume. cg@1001: self directory:aFilename. cg@1000: cg@1000: "/ on some systems, the root itself is not readable, cg@1000: "/ but subdirs are (WIN32-network drives and VMS) cg@1001: "/ to avoid trouble in the treeView, search for the cg@1001: "/ last readable directory and fake that as the trees root. cg@1000: cg@1000: (root exists and:[root isDirectory and:[root isReadable]]) cg@1000: ifFalse:[ cg@1000: "/ search backward from aFilename for the last readable dir. cg@1000: root := f. cg@1000: parent := root directory. cg@1005: [parent ~= root cg@1005: and:[parent exists cg@1005: and:[parent isDirectory cg@1005: and:[parent isReadable]]]] cg@1000: whileTrue:[ cg@1000: root := parent. cg@1000: parent := root directory. cg@1000: ]. cg@1000: ]. cg@1000: cg@1011: self rootOfDirectory value:root pathName cg@998: cg@998: "Created: / 23.9.1998 / 16:36:28 / cg" cg@1011: "Modified: / 5.10.1998 / 12:46:06 / cg" cg@998: ! cg@998: tz@404: valueOfFileName tz@404: tz@404: |holder| tz@404: (holder := builder bindingAt:#valueOfFileName) isNil ifTrue:[ tz@404: builder aspectAt:#valueOfFileName put:(holder := ValueHolder new). tz@404: ]. tz@404: ^ holder tz@404: ! ! tz@404: tz@404: !FileSelectionBrowser methodsFor:'callbacks'! tz@404: tz@404: fileDoubleClicked: aFileName tz@404: tz@404: self valueOfFileName value: aFileName. tz@404: accept value: true. cg@1035: self closeRequest tz@404: ! tz@404: cg@1379: menuCreateDirectory cg@1379: |newSubDirName currentDir| cg@1379: cg@1379: currentDir := self selectionOfDirectory value. cg@1379: currentDir isNil ifTrue:[ cg@1379: self warn:'No directory selected.'. cg@1379: ^ self cg@1379: ]. cg@1492: newSubDirName := Dialog cg@1492: request:(resources cg@1915: stringWithCRs:'Create new directory in\\%1: ' cg@1915: with:(currentDir asFilename pathName allBold)). cg@1379: (currentDir asFilename construct:newSubDirName) makeDirectory. cg@1379: self menuUpdate cg@1379: ! cg@1379: cg@1458: menuGotoDefaultDirectory cg@1458: self selectionOfDirectory value:(Filename defaultDirectory pathName) cg@1458: ! cg@1458: cg@1458: menuGotoHomeDirectory cg@1458: self selectionOfDirectory value:(Filename homeDirectory pathName) cg@1458: ! cg@1458: cg@1379: menuUpdate cg@1597: (self componentAt:#directoryTreeView) monitorCycle. cg@1379: ! cg@1379: tz@404: readDirectory cg@2440: self directoryTreeView selection notNil ifTrue: [ cg@1001: directoryView directory: self selectionOfDirectory value. cg@1001: directoryView readDirectory. tz@745: self class lastSelection notNil ifTrue: [self class lastSelection: self selectionOfDirectory value] tz@592: ] cg@1001: cg@1001: "Modified: / 24.9.1998 / 21:58:50 / cg" cg@1001: ! ! cg@1001: cg@1001: !FileSelectionBrowser methodsFor:'misc'! cg@1001: cg@1001: entryCompletion cg@1001: |completedDirectory f dir treeView inputField| cg@1001: cg@1001: treeView := self directoryTreeView. cg@1001: inputField := self directoryInputField. cg@1001: cg@1001: f := inputField contents. cg@2450: f asFilename isRootDirectory ifTrue:[ cg@2450: dir := f. cg@2450: ] ifFalse:[ cg@2450: dir := f asFilename directoryName. cg@2450: ]. cg@1001: cg@2440: completedDirectory := Filename cg@1001: filenameCompletionFor:f cg@1001: directory:dir cg@1001: directoriesOnly:true cg@1001: filesOnly:false cg@1001: ifMultiple: cg@2440: [:dir | cg@2440: treeView selection notNil ifTrue:[ cg@2440: treeView selectedNodeExpand: true cg@2440: ] cg@2440: ]. cg@2440: cg@2440: completedDirectory asFilename exists ifTrue:[ cg@1001: "/ volume changed ... (win32 or VMS) cg@2440: "/ completedDirectory asFilename volume ~= directory asFilename volume ifTrue:[ cg@2440: "/ ]. cg@2440: completedDirectory asFilename volume ~= treeView scrolledView directory asFilename volume ifTrue:[ cg@2440: treeView scrolledView directory:completedDirectory asFilename volume cg@1001: ]. cg@1001: treeView selectPathname: completedDirectory. cg@1001: inputField contents: completedDirectory. cg@1001: self readDirectory cg@1001: ] ifFalse: [ cg@1001: inputField flash cg@1001: ] cg@1001: cg@1001: "Created: / 24.9.1998 / 21:33:37 / cg" cg@1001: "Modified: / 24.9.1998 / 22:20:23 / cg" tz@404: ! ! tz@404: cg@1716: !FileSelectionBrowser methodsFor:'startup & release'! tz@404: tz@404: closeCancel tz@404: tz@404: directoryView release. tz@404: super closeCancel tz@404: ! tz@404: tz@404: closeRequest tz@404: tz@404: directoryView release. tz@404: super closeRequest tz@404: tz@404: ! tz@404: tz@404: open cg@1353: super open. tz@404: cg@1353: "return the selected file or nil" sv@515: accept value ifTrue:[ sv@515: ^ (self selectionOfDirectory value ? '') asFilename sv@515: constructString:(self valueOfFileName value ? '') tz@404: ]. sv@515: ^ nil cg@957: cg@957: "Modified: / 17.8.1998 / 10:15:55 / cg" tz@404: ! tz@404: tz@404: postBuildWith:aBuilder cg@1003: |nm file| tz@404: tz@404: directoryView listOfFileFilters: listOfFileFilters ? #('*'). tz@462: directoryView fileSelectAction: [:aFileName |self valueOfFileName value: aFileName asFilename baseName]. tz@462: directoryView fileDoubleClickAction: [:aFileName |self fileDoubleClicked: aFileName asFilename baseName]. tz@404: directoryView fileFilterSelectAction: [:fileFilter| cg@992: "/ CG: obscure code - isn't the code below the same as ? cg@992: "/ self valueOfFileName value:((self valueOfFileName value ? 'unknown') cg@992: "/ asFilename cg@992: "/ withSuffix:(fileFilter asFilename suffix)) cg@992: self valueOfFileName value: ( cg@992: ((self valueOfFileName value ? 'unknown') readStream upTo: $.), '.', cg@992: (fileFilter copy reverse readStream upTo: $.) reverse) cg@992: ]. cg@1003: nm := directory. cg@1003: nm isNil ifTrue:[ cg@1003: nm := self class lastSelection. cg@1003: nm isNil ifTrue:[ cg@1003: nm := Filename currentDirectory asAbsoluteFilename name. cg@1003: ]. cg@1003: ]. cg@2445: cg@1003: self directoryTreeView selectPathname:nm. tz@592: cg@1001: self directoryInputField cg@1001: entryCompletionBlock:[:f| self entryCompletion]. tz@404: cg@1003: file := self valueOfFileName value. tz@626: directoryView selectionOfFile value: cg@1003: (directoryView listOfFiles cg@1003: detect: [:row | row baseName = file] cg@1003: ifNone: nil). tz@626: cg@2027: super postBuildWith:aBuilder cg@969: cg@1003: "Modified: / 24.9.1998 / 23:41:29 / cg" tz@404: ! ! tz@404: tz@404: !FileSelectionBrowser class methodsFor:'documentation'! tz@404: tz@404: version tz@404: ^ '$Header$' tz@404: ! !