'From Squeak3.4alpha of 6 November 2002 [latest update: #5134] on 30 November 2002 at 5:02:22 pm'! "Change Set: fileListFixes2-nk Date: 30 November 2002 Author: Ned Konz Version: 3.4a After decompressing gzip files in the FileList, the file list wasn't updated. This change set fixes that without unnecessarily updating the file list (which can be slow via FTP, for instance). I've added a FileModifyingSimpleServiceEntry class that is the same as SimpleServiceEntry but also updates the contents of the file list. This is then used in four places where files actually change, including decompression and linefeed removal. This CS also updates the class comment in FileList to be up to date with the current design. This also removes the compression services from FileList itself (thereby removing some dependencies) and puts them into ArchiveViewer and GzipWriteStream. Also it adds balloon help to the file list menu. Also stops offering to compress already-compressed files. Further improved 30 Nov-1 Dec by Scott Wallace: ¥ Fixes bug that thwarted GZipWriteStream.compressFile: ¥ Moves responsibility for the dynamic file-list buttons to the individual services. This allows the filelist to be updated when needed when the buttons rather than the corresponding menu items are used. ¥ After the file list is updated by a file-modifying service, update the button row as well. Otherwise, after such a service is performed the orphaned dynamic buttons would raise errors when pressed. ¥ Suppress the 'broadcast' button. ¥ Get sys category correct for the ServiceEntry classes ¥ In the alternative window look, use the same style for the dynamic buttons as is used for static buttons. ¥ After a change in sort-order, get the dynamic buttons restored. ¥ Removed the #completed mechanism of update 5115, which is supplanted by Ned's subsequent FileModifyingSimpleServiceEntry approach incorporated in this update."! !FileList commentStamp: 'nk 11/26/2002 11:52' prior: 0! I am model that can be used to navigate the host file system. By omitting the volume list, file list, and template panes from the view, I can also be used as the model for an editor on an individual file. The FileList now provides a registration mechanism to which any tools the filelist uses ***MUST*** register. This way it is possible to dynamically load or unload a new tool and have the FileList automatically updated. This change supports a decomposition of Squeak and removes a problem with dead reference to classes after a major shrink. Tools should implement the following methods (look for implementors in the image): #fileReaderServicesForFile:suffix: (appropriate services for given file, takes a file name and a lowercased suffix) #services (all provided services, to be displayed in full list) These methods both return a collection of SimpleServiceEntry instances. These contain a class, a menu label and a method selector having one argument. They may also provide separate button labels and description. The argument to the specified method will be a string representing the full name of a file when one is selected or the file list itself when there is no selected file. Tools must register with the FileList calling the class method #registerFileReader: when they load. They also must call #unregisterFileReader: when they unload. There is a testSuite called FileListTest that presents some examples. Stef (I do not like really this distinction passing always a file list could be better) Old Comments: FileLists can now see FTP servers anywhere on the net. In the volume list menu: fill in server info... Gives you a form to register a new ftp server you want to use. open server... Choose a server to connect to. local disk Go back to looking at your local volume. Still undone (you can contribute code): [ ] Using a Proxy server to get out through a firewall. What is the convention for proxy servers with FTP? [ ] Fill in the date and size info in the list of remote files. Allow sorting by it. New smarts needed in (ServerDirectory fileNameFormattedFrom:sizePad:sortMode:). [ ] Currently the FileList has no way to delete a directory. Since you can't select a directory without going into it, it would have to be deleting the current directory. Which would usually be empty.! SimpleServiceEntry subclass: #FileModifyingSimpleServiceEntry instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Tools-FileList'! !FileModifyingSimpleServiceEntry commentStamp: 'nk 11/26/2002 12:03' prior: 0! I represent a service that may change the contents of a directory. Such changes include: * file creation * file deletion * file modification! !ArchiveViewer class methodsFor: 'instance creation' stamp: 'nk 11/26/2002 12:45'! openOn: aFileName | newMe | newMe _ self new. newMe createWindow; fileName: aFileName; openInWorld. ^newMe! ! !ArchiveViewer class methodsFor: 'file list services' stamp: 'nk 11/26/2002 12:46'! extractAllFrom: aFileName (self new) fileName: aFileName; extractAll! ! !ArchiveViewer class methodsFor: 'file list services' stamp: 'nk 11/26/2002 13:15'! fileReaderServicesForFile: fullName suffix: suffix | services | services _ OrderedCollection new. services add: self serviceAddToNewZip. (suffix = 'zip' or: [suffix = 'sar']) ifTrue: [services add: self serviceOpenInZipViewer. services add: self serviceExtractAll]. ^ services! ! !ArchiveViewer class methodsFor: 'file list services' stamp: 'nk 11/26/2002 12:48'! serviceAddToNewZip "Answer a service for adding the file to a new zip" ^ FileModifyingSimpleServiceEntry provider: self label: 'add file to new zip' selector: #addFileToNewZip: description: 'add file to new zip' buttonLabel: 'to new zip'! ! !ArchiveViewer class methodsFor: 'file list services' stamp: 'nk 11/26/2002 12:15'! serviceExtractAll "Answer a service for opening in a zip viewer" ^ FileModifyingSimpleServiceEntry provider: self label: 'extract all to...' selector: #extractAllFrom: description: 'extract all files to a user-specified directory' buttonLabel: 'extract all'! ! !FileList methodsFor: 'initialization' stamp: 'sw 11/30/2002 00:05'! buttonSelectorsToSuppress "Answer a list of action selectors whose corresponding services we would prefer *not* to have appear in the filelist's button pane; this can be hand-jimmied to suit personal taste." ^ #(removeLineFeeds: addFileToNewZip: compressFile: putUpdate:)! ! !FileList methodsFor: 'initialization' stamp: 'sw 11/30/2002 14:36'! optionalButtonRow "Answer the button row associated with a file list" | aRow | aRow _ AlignmentMorph newRow beSticky. aRow color: Color transparent. aRow clipSubmorphs: true. aRow layoutInset: 5@1; cellInset: 6. self universalButtonServices do: "just the three sort-by items" [:service | aRow addMorphBack: (service buttonToTriggerIn: self). (service selector == #sortBySize) ifTrue: [aRow addTransparentSpacerOfSize: (4@0)]]. aRow setNameTo: 'buttons'. aRow setProperty: #buttonRow toValue: true. "Used for dynamic retrieval later on" ^ aRow! ! !FileList methodsFor: 'initialization' stamp: 'sw 11/30/2002 01:02'! updateButtonRow "Dynamically update the contents of the button row, if any." | aWindow aRow | Smalltalk isMorphic ifFalse: [^ self]. aWindow _ self dependents detect: [:m | (m isKindOf: SystemWindow) and: [m model == self]] ifNone: [^ self]. aRow _ aWindow findDeepSubmorphThat: [:m | m hasProperty: #buttonRow] ifAbsent: [^ self]. (aRow submorphs size - 4) timesRepeat: [aRow submorphs last delete]. self dynamicButtonServices do: [:service | aRow addMorphBack: (service buttonToTriggerIn: self). service addDependent: self]! ! !FileList methodsFor: 'to be transformed in registration' stamp: 'sw 11/30/2002 15:38'! perform: selector orSendTo: otherTarget "Selector was just chosen from a menu by a user. If it's one of the three sort-by items, handle it specially. If I can respond myself, then perform it on myself. If not, send it to otherTarget, presumably the editPane from which the menu was invoked." ^ (#(sortByDate sortBySize sortByName) includes: selector) ifTrue: [self resort: selector] ifFalse: [(#(get getHex copyName openImageInWindow importImage renameFile deleteFile addNewFile) includes: selector) ifTrue: [self perform: selector] ifFalse: [super perform: selector orSendTo: otherTarget]]! ! !FileList methodsFor: 'file list menu' stamp: 'sw 11/30/2002 01:15'! itemsForAnyFile "Answer a list of universal services that could apply to any file" | services | services := OrderedCollection new: 5. services add: self serviceCopyName. services add: self serviceRenameFile. services add: self serviceDeleteFile. ^ services! ! !FileList methodsFor: 'private' stamp: 'sw 11/30/2002 16:34'! resort: newMode "Re-sort the list of files." | name | listIndex > 0 ifTrue: [name _ self fileNameFromFormattedItem: (list at: listIndex)]. sortMode _ newMode. self pattern: pattern. name ifNotNil: [ fileName _ name. listIndex _ list findFirst: [:item | (self fileNameFromFormattedItem: item) = name. ]. self changed: #fileListIndex]. listIndex = 0 ifTrue: [self changed: #contents]. self updateButtonRow ! ! !FileList methodsFor: 'private' stamp: 'sw 11/30/2002 01:19'! updateFileList "Update my files list with file names in the current directory that match the pattern." Cursor execute showWhile: [list _ (pattern includes: $*) | (pattern includes: $#) ifTrue: [self listForPattern: pattern] ifFalse: [ pattern isEmpty ifTrue: [self listForPattern: '*'] ifFalse: [self listForPattern: '*', pattern, '*']]. listIndex _ 0. volListIndex _ volList size. fileName _ nil. contents _ ''. self changed: #volumeListIndex. self changed: #fileList. self updateButtonRow]! ! !FileList methodsFor: 'updating' stamp: 'sw 11/30/2002 16:49'! update: aParameter "Receive a change notice from an object of whom the receiver is a dependent" (aParameter == #fileListChanged) ifTrue: [self updateFileList]. super update: aParameter! ! !FileStream class methodsFor: 'file reader services' stamp: 'nk 11/26/2002 12:49'! serviceRemoveLineFeeds "Answer a service for removing linefeeds from a file" ^ FileModifyingSimpleServiceEntry provider: self label: 'remove line feeds' selector: #removeLineFeeds: description: 'remove line feeds in file' buttonLabel: 'remove lfs'! ! !GZipReadStream class methodsFor: 'fileIn/Out' stamp: 'nk 11/26/2002 12:11'! serviceDecompressToFile ^ FileModifyingSimpleServiceEntry provider: self label: 'decompress to file' selector: #saveContents: description: 'decompress to file'! ! !GZipWriteStream class methodsFor: 'class initialization' stamp: 'nk 11/26/2002 13:09'! initialize FileList registerFileReader: self! ! !GZipWriteStream class methodsFor: 'class initialization' stamp: 'nk 11/26/2002 13:09'! unload FileList unregisterFileReader: self! ! !GZipWriteStream class methodsFor: 'file list services' stamp: 'sw 11/30/2002 00:11'! compressFile: fileName "Create a compressed file from the file of the given name" (FileStream readOnlyFileNamed: fileName) compressFile! ! !GZipWriteStream class methodsFor: 'file list services' stamp: 'nk 11/26/2002 13:44'! fileReaderServicesForFile: fullName suffix: suffix "Don't offer to compress already-compressed files" ^({ 'gz' . 'sar' . 'zip' . 'gif' . 'jpg' } includes: suffix) ifTrue: [ #() ] ifFalse: [ self services ] ! ! !GZipWriteStream class methodsFor: 'file list services' stamp: 'nk 11/26/2002 13:17'! serviceCompressFile ^ FileModifyingSimpleServiceEntry provider: self label: 'compress file' selector: #compressFile: description: 'compress file using gzip compression, making a new file'! ! !GZipWriteStream class methodsFor: 'file list services' stamp: 'nk 11/26/2002 13:10'! services ^ { self serviceCompressFile }! ! !MenuMorph methodsFor: 'construction' stamp: 'nk 11/26/2002 13:51'! addServices2: services for: served extraLines: linesArray services withIndexDo: [:service :i | self add: service label target: service selector: service requestSelector argument: (service getArgumentsFrom: served). submorphs last setBalloonText: service description. (linesArray includes: i) | service useLineAfter ifTrue: [self addLine] ]. ! ! !MenuMorph methodsFor: 'construction' stamp: 'nk 11/26/2002 13:53'! addServices: services for: served extraLines: linesArray services withIndexDo: [:service :i | self addService: service for: served. submorphs last setBalloonText: service description. (linesArray includes: i) | service useLineAfter ifTrue: [self addLine]]. ! ! !SimpleServiceEntry methodsFor: 'performing service' stamp: 'sw 11/30/2002 14:35'! buttonToTriggerIn: aFileList "Answer a button that will trigger the receiver service in a file list" | aButton | aButton _ PluggableButtonMorph on: self getState: nil action: #performServiceFor:. "aButton argumentsProvider: self argumentsSelector: #fullNameArguments." aButton argumentsProvider: (aFileList blockToGetArgArrayFrom: self) argumentsSelector: #value. aButton color: Color transparent; hResizing: #spaceFill; vResizing: #spaceFill; useRoundedCorners; label: self buttonLabel; askBeforeChanging: true; onColor: Color transparent offColor: Color transparent. aButton setBalloonText: self description. Preferences alternativeWindowLook ifTrue: [aButton borderWidth: 2; borderColor: #raised]. ^ aButton! ! !SimpleServiceEntry methodsFor: 'performing service' stamp: 'hg 8/3/2000 13:45'! performServiceFor: anObject "carry out the service I provide" ^selector numArgs = 0 ifTrue: [provider perform: selector] ifFalse: [provider perform: selector with: anObject]! ! !FileModifyingSimpleServiceEntry methodsFor: 'as yet unclassified' stamp: 'nk 11/26/2002 12:08'! performServiceFor: anObject | retval | retval _ super performServiceFor: anObject. self changed: #fileListChanged. ^retval "is this used anywhere?"! ! !SwikiPage class methodsFor: 'fileIn/Out' stamp: 'nk 11/26/2002 12:52'! serviceGenerateHTML ^ FileModifyingSimpleServiceEntry provider: self label: 'generate HTML' selector: #renderFile: description: 'generate HTML'! ! GZipWriteStream initialize! "Postscript:" SystemOrganization classifyAll: #(FileModifyingSimpleServiceEntry SimpleServiceEntry) under: 'Tools-FileList'. SystemOrganization removeEmptyCategories !