David T. Lewis uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-dtl.1360.mcz
==================== Summary ====================
Name: Morphic-dtl.1360
Author: dtl
Time: 11 November 2017, 4:04:34.153784 pm
UUID: 0e1f3870-5f57-4dc6-a1e7-5d8ce68b71b5
Ancestors: Morphic-dtl.1359
World global elimination. Once the current project has been entered, Project current world == World. Begin eliminating references to the global variable World in cases where it is not required.
=============== Diff against Morphic-dtl.1359 ===============
Item was changed:
----- Method: ComplexProgressIndicator>>backgroundWorldDisplay (in category 'as yet unclassified') -----
backgroundWorldDisplay
self flag: #bob. "really need a better way to do this"
"World displayWorldSafely."
"ugliness to try to track down a possible error"
+ [Project current world displayWorld] ifError: [ :a :b |
- [World displayWorld] ifError: [ :a :b |
| f |
stageCompleted := 999.
f := FileDirectory default fileNamed: 'bob.errors'.
f nextPutAll: a printString,' ',b printString; cr; cr.
f nextPutAll: 'worlds equal ',(formerWorld == World) printString; cr; cr.
f nextPutAll: thisContext longStack; cr; cr.
f nextPutAll: formerProcess suspendedContext longStack; cr; cr.
f close. Beeper beep.
].
!
Item was changed:
----- Method: Debugger class>>morphicOpenInterrupt:onProcess: (in category '*Morphic-opening') -----
morphicOpenInterrupt: aString onProcess: interruptedProcess
"Open a notifier in response to an interrupt. An interrupt occurs when the user types the interrupt key (cmd-. on Macs, ctrl-c or alt-. on other systems) or when the low-space watcher detects that memory is low."
| debugger |
<primitive: 19> "Simulation guard"
debugger := self new.
debugger
process: interruptedProcess
controller: nil
context: interruptedProcess suspendedContext.
debugger externalInterrupt: true.
Preferences logDebuggerStackToFile ifTrue:
[(aString includesSubstring: 'Space') & (aString includesSubstring: 'low')
ifTrue: [Smalltalk logError: aString inContext: debugger interruptedContext to: 'LowSpaceDebug.log']
"logging disabled for 4.3 release, see
http://lists.squeak.org/pipermail/squeak-dev/2011-December/162503.html"
"ifFalse: [Smalltalk logSqueakError: aString inContext: debugger interruptedContext]"].
+ Preferences eToyFriendly ifTrue: [Project current world stopRunningAll].
- Preferences eToyFriendly ifTrue: [World stopRunningAll].
^debugger
openNotifierContents: nil label: aString;
yourself
!
Item was changed:
----- Method: Morph>>updateFromResource (in category 'fileIn/out') -----
updateFromResource
| pathName newMorph f |
(pathName := self valueOfProperty: #resourceFilePath) ifNil: [^self].
(pathName asLowercase endsWith: '.morph')
ifTrue:
[newMorph := (FileStream readOnlyFileNamed: pathName) fileInObjectAndCode.
(newMorph isMorph)
ifFalse: [^self error: 'Resource not a single morph']]
ifFalse:
[f := Form fromFileNamed: pathName.
f ifNil: [^self error: 'unrecognized image file format'].
+ newMorph := Project current world drawingClass withForm: f].
- newMorph := World drawingClass withForm: f].
newMorph setProperty: #resourceFilePath toValue: pathName.
self owner replaceSubmorph: self by: newMorph!
Item was changed:
----- Method: MorphHierarchyListMorph>>createContainer (in category 'private') -----
createContainer
"Private - Create a container"
| container |
container := BorderedMorph new.
+ container extent: (Project current world extent * (1 / 4 @ (2 / 3))) rounded.
- container extent: (World extent * (1 / 4 @ (2 / 3))) rounded.
container layoutPolicy: TableLayout new.
container hResizing: #rigid.
container vResizing: #rigid.
container
setColor: (Color gray: 0.9)
borderWidth: 1
borderColor: Color gray.
container layoutInset: 0.
"container useRoundedCorners."
""
container setProperty: #morphHierarchy toValue: true.
container setNameTo: 'Objects Hierarchy' translated.
""
^ container!
Item was changed:
----- Method: MorphicProject>>storeSegment (in category 'file in/out') -----
storeSegment
"Store my project out on the disk as an ImageSegment. Keep the outPointers in memory. Name it <project name>.seg. *** Caller must be holding (Project alInstances) to keep subprojects from going out. ***"
| is sizeHint |
+ (Project current world == world) ifTrue: [^ false].
- (World == world) ifTrue: [^ false].
"self inform: 'Can''t send the current world out'."
world isInMemory ifFalse: [^ false]. "already done"
world ifNil: [^ false]. world presenter ifNil: [^ false].
ScrapBook default emptyScrapBook.
World checkCurrentHandForObjectToPaste.
world releaseSqueakPages.
sizeHint := self projectParameters at: #segmentSize ifAbsent: [0].
is := ImageSegment
copyFromRootsLocalFileFor: {world presenter. world} "world, and all Players"
sizeHint: sizeHint.
is state = #tooBig ifTrue: [^ false].
is segment size < 2000 ifTrue: ["debugging"
Transcript show: self name, ' only ', is segment size printString,
'bytes in Segment.'; cr].
self projectParameters at: #segmentSize put: is segment size.
is extract; writeToFile: self name.
^ true!
Item was changed:
----- Method: MorphicProject>>storeSegmentNoFile (in category 'file in/out') -----
storeSegmentNoFile
"For testing. Make an ImageSegment. Keep the outPointers in memory. Also useful if you want to enumerate the objects in the segment afterwards (allObjectsDo:)"
| is |
+ (Project current world == world) ifTrue: [^ self]. " inform: 'Can''t send the current world out'."
- (World == world) ifTrue: [^ self]. " inform: 'Can''t send the current world out'."
world isInMemory ifFalse: [^ self]. "already done"
world ifNil: [^ self]. world presenter ifNil: [^ self].
"Do this on project enter"
World flapTabs do: [:ft | ft referent adaptToWorld: World].
"Hack to keep the Menu flap from pointing at my project"
"Preferences setPreference: #useGlobalFlaps toValue: false."
"Utilities globalFlapTabsIfAny do:
[:aFlapTab | Utilities removeFlapTab: aFlapTab keepInList: false].
Utilities clobberFlapTabList. "
"project world deleteAllFlapArtifacts."
"self currentWorld deleteAllFlapArtifacts. "
ScrapBook default emptyScrapBook.
World checkCurrentHandForObjectToPaste2.
is := ImageSegment
copyFromRootsLocalFileFor: {world presenter. world} "world, and all Players"
sizeHint: 0.
is segment size < 800 ifTrue: ["debugging"
Transcript show: self name, ' did not get enough objects'; cr. ^ Beeper beep].
is extract.
"is instVarAt: 2 put: is segment clone." "different memory"!
Item was changed:
----- Method: TheWorldMainDockingBar>>startMessageTally (in category 'menu actions') -----
startMessageTally
+ | world |
+ world := Project current world.
(self confirm: 'MessageTally will start now,
and stop when the cursor goes
to the top of the screen') ifTrue:
[MessageTally spyOn:
+ [[Sensor peekPosition y > 0] whileTrue: [world doOneCycle]]]!
- [[Sensor peekPosition y > 0] whileTrue: [World doOneCycle]]]!
Item was changed:
----- Method: TheWorldMenu>>startMessageTally (in category 'commands') -----
startMessageTally
+ | world |
+ world := Project current world.
-
(self confirm: 'MessageTally will start now,
and stop when the cursor goes
to the top of the screen') ifTrue:
[MessageTally spyOn:
+ [[Sensor peekPosition y > 0] whileTrue: [world doOneCycle]]]!
- [[Sensor peekPosition y > 0] whileTrue: [World doOneCycle]]]!
tim Rowledge uploaded a new version of Tools to project The Trunk:
http://source.squeak.org/trunk/Tools-tpr.773.mcz
==================== Summary ====================
Name: Tools-tpr.773
Author: tpr
Time: 13 November 2017, 11:51:08.236067 am
UUID: 348383f7-66f6-41a4-8cc0-60df2edc835f
Ancestors: Tools-tpr.772
Updates to add use of pattern for the saver dialog (not certain that is the right way to do it), to speed up making the file list, update class comments and make sure the initial file is selected where appropriate
=============== Diff against Tools-tpr.772 ===============
Item was changed:
Model subclass: #FileAbstractSelectionDialog
instanceVariableNames: 'pattern directory directoryCache message listIndex fileName finalChoice nameList sizeList dateList'
classVariableNames: ''
poolDictionaries: ''
category: 'Tools-FileDialogs'!
+ !FileAbstractSelectionDialog commentStamp: 'tpr 11/13/2017 11:08' prior: 0!
- !FileAbstractSelectionDialog commentStamp: 'tpr 11/9/2017 15:37' prior: 0!
FileAbstractSelectionDialog is the abstract superclass for the file chooser & saver modal dialogs.
+ The UI provides a message to the user, a text input field, a directory tree widget and a list of files within any chosen directory, and buttons to accept the selected file name/path or cancel the operation. See subclass comments and class side methods for specific usage examples.
- The UI provides a text input field, a directory tree widget and a list of files within any chosen directory, and buttons to accept the selected file name/path or cancel the operation. See subclass comments and class side methods for specific usage examples.
Instance Variables
directory: <FileDirectory> used for the currently selected directory
directoryCache: <WeakIdentityKeyDictionary> used to cache a boolean to help us more quickly populate the directory tree widget when revisiting a directory
fileName: <String|nil> the name of the currently selected file, if any
finalChoice: <String|nil> pathname of the finally chosen file, returned as the result of accepting; nil is returned otherwise
list: <Array> the list of String of filenames (and date/size) that match the current pattern
listIndex: <Integer> list index of the currently selected file
+ pattern: <String> the pattern is held as a string that may include * or # wildcasrds. See FileAbstractSelectionDialog>>#parsePatternString for details
+ message: <String> a message to the user to explain what is expected
+ nameList,DateList, sizeList: <Array> the list of file names matching the pattern and the appropriate date and size values, formatted for a PluggableMultiColumnListMorph!
- pattern: <String> the pattern is held as a string that may include * or # wildcasrds. See FileAbstractSelectionDialog>>#parsePatternString for details!
Item was changed:
----- Method: FileAbstractSelectionDialog>>entriesMatching: (in category 'file list') -----
+ entriesMatching: patternList
+ "Answer a list of directory entries which match any of the patterns.
+ See #parsePatternString for the pattern rules"
- entriesMatching: patternString
- "Answer a list of directory entries which match the patternString.
- The patternString may consist of multiple patterns separated by ';'.
- Each pattern can include a '*' or '#' as wildcards - see String>>match:"
+ | entries |
+ "This odd clause helps supports MVC projects; the file list & directory views are built from a list that includes directories. In Morphic we filter out the directories because they are entirely handled by the direcctory tree morph"
+ entries := Smalltalk isMorphic
+ ifTrue:[directory fileEntries ]
+ ifFalse:[directory entries].
+
+ (patternList anySatisfy: [:each | each = '*'])
- | entries patterns |
- entries := directory entries reject:[:e| Smalltalk isMorphic and: [e isDirectory]].
- patterns := patternString findTokens: ';'.
- (patterns anySatisfy: [:each | each = '*'])
ifTrue: [^ entries].
+
+ ^ entries select: [:entry | patternList anySatisfy: [:each | each match: entry name]]!
- ^ entries select: [:entry | patterns anySatisfy: [:each | each match: entry name]]!
Item was changed:
----- Method: FileAbstractSelectionDialog>>listForPatterns: (in category 'path and pattern') -----
listForPatterns: arrayOfPatterns
"return a list of those file names which match any of the patterns in the array."
| newList |
newList := Set new.
+ newList addAll: (self entriesMatching: arrayOfPatterns).
- arrayOfPatterns do: [ :pat | newList addAll: (self entriesMatching: pat) ].
newList := newList sorted: [:a :b|
a name <= b name].
nameList := newList collect:[:e| e name].
dateList := newList collect:[:e| ((Date fromSeconds: e modificationTime )
printFormat: #(3 2 1 $. 1 1 2)) , ' ' ,
(String streamContents: [:s |
(Time fromSeconds: e modificationTime \\ 86400)
print24: true on: s])].
sizeList := newList collect:[:e| e fileSize asStringWithCommas]
!
Item was changed:
FileAbstractSelectionDialog subclass: #FileChooserDialog
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Tools-FileDialogs'!
+ !FileChooserDialog commentStamp: 'tpr 11/13/2017 11:46' prior: 0!
- !FileChooserDialog commentStamp: 'tpr 10/28/2017 12:57' prior: 0!
A FileChooserDialog is a modal dialog to allow choosing a file. The full file name is returned, or nil if no selection was made.
Users can enter a pattern in the text input field that will be read as a directory path and an optional pattern (see comments about pattern in my superclass) to define the files in the file list.
Normal usage would be
+ myFilename := FileChooserDialog openOn: myApplicationDefaultDirectory pattern: '*.myapp' message: 'Choose the file to load'
- myFilename := FileChooserDialog openOn: myApplicationDefaultDirectory pattern: '*.myapp'
to find a file with a name matching *.myapp and with the directory initial choice set to myApplicationDefaultDirectory. It would be quite possible to choose a file from any other directory and with any other pattern match if the user wishes, so the file name must be carefully checked.
Simpler usage might be
myFilename := FileChooserDialog open
or
myFilename := FileChoosverDialog openOn: FileDirectory default
- see the class side methods for details. See my parent class for most implementation details!
Item was added:
+ ----- Method: FileChooserDialog class>>openOn:pattern:message: (in category 'instance creation') -----
+ openOn: aDirectory pattern: aPatternString message: messageString
+ "open a modal dialog to choose a file. Start the dialog with aDirectory selected and files matching the pattern"
+
+ ^self new openOn: aDirectory pattern: aPatternString message: messageString
+
+ !
Item was changed:
----- Method: FileChooserDialog>>openOn:pattern: (in category 'initialize-release') -----
openOn: aDirectory pattern: aPatternString
"open a modal dialog to choose a file from aDirectory as filtered by aPattern"
+ ^self openOn: aDirectory pattern: aPatternString message: nil
+ !
- directory := aDirectory.
- self pattern: aPatternString.
-
- ToolBuilder open: self.
- ^self finalChoice!
Item was added:
+ ----- Method: FileChooserDialog>>openOn:pattern:message: (in category 'initialize-release') -----
+ openOn: aDirectory pattern: aPatternString message: messageString
+ "open a modal dialog to choose a file from aDirectory as filtered by aPattern"
+
+ directory := aDirectory.
+ self pattern: aPatternString.
+ message := messageString.
+
+ ToolBuilder open: self.
+ ^self finalChoice!
Item was changed:
----- Method: FileList>>directoryNameOf: (in category 'directory tree') -----
directoryNameOf: aDirectory
"Attempt to find the name of ServerDirectories when used."
^(aDirectory isRemoteDirectory and:[aDirectory isKindOf: ServerDirectory])
+ ifTrue:[ServerDirectory servers keyAtIdentityValue: aDirectory ifAbsent:[aDirectory localName]]
- ifTrue:[ServerDirectory servers keyAtIdentityValue: aDirectory]
ifFalse:[aDirectory localName]!
Item was changed:
FileAbstractSelectionDialog subclass: #FileSaverDialog
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Tools-FileDialogs'!
+ !FileSaverDialog commentStamp: 'tpr 11/13/2017 11:49' prior: 0!
- !FileSaverDialog commentStamp: 'tpr 10/28/2017 12:58' prior: 0!
A FileSaverDialog is a modal dialog for choosing a file name to use for saving a file.
Users can enter a filename in the text input view that will
a) if it exists in the current directry listing, be selected
b) over ride any filenames in the current directry, providing a way to specify a completely new file.
This will not affect the selected directory path.
Normal usage would be
myFilename := FileSaverDialog openOn: myApplicationDefaultDirectory initialFilename: 'foo.myapp'
+ to find a file with a name matching foo.myapp and with the directory initial choice set to myApplicationDefaultDirectory. It would be quite possible to choose a file from any other directory and with any other name if the user wishes, so the file name must be carefully checked. The full set of options would invovle
+ myFilename := FileSaverDialog openOn: myApplicationDefaultDirectory initialFilename: 'foo.myapp' pattern: '*.mya' message: 'Save your myApp file to ... '
- to find a file with a name matching foo.myapp and with the directory initial choice set to myApplicationDefaultDirectory. It would be quite possible to choose a file from any other directory and with any other name if the user wishes, so the file name must be carefully checked.
Simpler usage might be
myFilename := FileSaverDialog open
or
myFilename := FileSaverDialog openOn: FileDirectory default
- see the class side methods for details. See my parent class for most implementation details!
Item was added:
+ ----- Method: FileSaverDialog class>>openOn:initialFilename:pattern: (in category 'instance creation') -----
+ openOn: aDirectory initialFilename: aString pattern: patternString
+ "open a modal dialog to save a file. Start the dialog with aDirectory selected and the suggested file name. Visible filenames are limited by the pattern"
+
+ ^self new openOn: aDirectory initialFilename: aString pattern: patternString
+
+ !
Item was added:
+ ----- Method: FileSaverDialog class>>openOn:initialFilename:pattern:message: (in category 'instance creation') -----
+ openOn: aDirectory initialFilename: aString pattern: patternString message: messageString
+ "open a modal dialog to save a file. Start the dialog with aDirectory selected and the suggested file name. Visible filenames are limited by the pattern. Use the messageString to explain what ther user needs to know"
+
+ ^self new openOn: aDirectory initialFilename: aString pattern: patternString message: messageString
+
+ !
Item was added:
+ ----- Method: FileSaverDialog>>buildWith: (in category 'toolbuilder') -----
+ buildWith: builder
+ "assemble the spec for the common chooser/saver dialog UI"
+
+ | window |
+ window := super buildWith: builder.
+ self inputText: fileName.
+ ^window
+ !
Item was changed:
----- Method: FileSaverDialog>>inputText: (in category 'filename') -----
+ inputText: aText
+ "user has entered a potential filename in the text field.
+ Check it against the current pattern; if it is ok we can accept it and then if it is a file in
+ the current list, highlight it.
+ If it would not match the pattern, alert the user as best we can"
+ | candidate |
+ candidate := aText asString.
+ (self parsePatternString anySatisfy: [:p | p match: candidate])
+ ifTrue: [fileName := candidate.
+ listIndex := nameList findFirst: [:nm | nm = fileName].
+ self changed: #fileListIndex.
+ ^true]
+ ifFalse: [fileName := nil.
+ ^false]!
- inputText: aText
- "user has entered a potential filename in the text field. If it is a file in the current list, highlight it"
-
- fileName := aText asString.
- listIndex := nameList findFirst:[: nm| nm = fileName].
-
- self
- changed: #fileListIndex;
- changed: #inputText!
Item was changed:
----- Method: FileSaverDialog>>openOn:initialFilename: (in category 'initialize-release') -----
openOn: aDirectory initialFilename: aFilename
"open a modal dialog to choose a file name to save to aDirectory"
+ ^self openOn: aDirectory initialFilename: aFilename pattern: nil!
- directory := aDirectory.
- fileName := aFilename.
-
- ToolBuilder open: self.
- ^self finalChoice!
Item was added:
+ ----- Method: FileSaverDialog>>openOn:initialFilename:pattern: (in category 'initialize-release') -----
+ openOn: aDirectory initialFilename: aFilename pattern: patternString
+ "open a modal dialog to choose a file name to save to aDirectory; limit visible files in the file list with the pattern"
+
+ ^self openOn: aDirectory initialFilename: aFilename pattern: patternString message: nil
+ !
Item was added:
+ ----- Method: FileSaverDialog>>openOn:initialFilename:pattern:message: (in category 'initialize-release') -----
+ openOn: aDirectory initialFilename: aFilename pattern: patternString message: messageString
+ "open a modal dialog to choose a file name to save to aDirectory; limit visible files in the file list with the pattern. Set the user message"
+
+ directory := aDirectory.
+ fileName := aFilename.
+ message:= messageString.
+ self pattern: patternString.
+
+ ToolBuilder open: self.
+ ^self finalChoice!
David T. Lewis uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-dtl.1359.mcz
==================== Summary ====================
Name: Morphic-dtl.1359
Author: dtl
Time: 11 November 2017, 3:55:27.800992 pm
UUID: e50e4090-12f4-4551-b700-1e5bcf30e5ff
Ancestors: Morphic-dtl.1358
Fix missing super return in makeThumbnail, needed for ProjectSorterMorph new openInWorld
=============== Diff against Morphic-dtl.1358 ===============
Item was changed:
----- Method: MorphicProject>>makeThumbnail (in category 'menu messages') -----
makeThumbnail
"Make a thumbnail image of this project from the Display."
world displayWorldSafely. "clean pending damage"
+ ^super makeThumbnail.!
- super makeThumbnail.!
David T. Lewis uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-dtl.1358.mcz
==================== Summary ====================
Name: Morphic-dtl.1358
Author: dtl
Time: 10 November 2017, 7:39:02.562749 pm
UUID: c99ed855-5b48-48ff-827a-c1a6e87cd1e6
Ancestors: Morphic-tpr.1357
Yellow button menu for a workspace generally acts upon the text selection, not the full workspace text. Make it so for pretty printing the selected text in a workspace.
=============== Diff against Morphic-tpr.1357 ===============
Item was changed:
----- Method: TextEditor>>prettyPrint: (in category 'menu messages') -----
prettyPrint: decorated
"Reformat the contents of the receiver's view (a Browser or Workspace)."
+ model selectedClassOrMetaClass
+ ifNil: [ "arbitrary text selection in a workspace, not directly associated with a class"
+ (Compiler new formatNoPattern: self selection environment: model environment)
+ ifNotNilDo: [:newText | self replaceSelectionWith: newText]]
+ ifNotNil: [:selectedClass | "source for a method in the selected class"
+ (selectedClass newCompiler
+ format: self text
+ in: selectedClass
+ notifying: self
+ decorated: decorated)
+ ifNotNilDo: [ :newText |
+ self deselect; selectInvisiblyFrom: 1 to: paragraph text size.
+ self replaceSelectionWith: (selectedClass ifNil: [newText] ifNotNil: [newText asText makeSelectorBoldIn: selectedClass]).
+ self selectAt: self text size + 1 ]].
+ !
- | selectedClass newText |
- selectedClass := model selectedClassOrMetaClass.
- newText := selectedClass
- ifNil: [
- Compiler new
- formatNoPattern: self text
- environment: model environment ]
- ifNotNil: [:cls |
- selectedClass newCompiler
- format: self text
- in: cls
- notifying: self
- decorated: decorated ].
- newText ifNotNil:
- [ self
- deselect ;
- selectInvisiblyFrom: 1
- to: paragraph text size.
- self replaceSelectionWith: (selectedClass ifNil: [newText] ifNotNil: [newText asText makeSelectorBoldIn: selectedClass]).
- self selectAt: self text size + 1 ]!
tim Rowledge uploaded a new version of Tools to project The Trunk:
http://source.squeak.org/trunk/Tools-tpr.772.mcz
==================== Summary ====================
Name: Tools-tpr.772
Author: tpr
Time: 10 November 2017, 4:32:21.463175 pm
UUID: 87c216c9-58b6-4a00-90ce-c7761cd1fa86
Ancestors: Tools-eem.771
Fixes and extensions to FileChooser/Saver Dialogs -
use a mutli-column list for the file list
clean up operation so we don't repeatedly read the directory contents repeatedly again
add hooks for a user message, and a default for each kind of dialog
=============== Diff against Tools-eem.771 ===============
Item was changed:
Model subclass: #FileAbstractSelectionDialog
+ instanceVariableNames: 'pattern directory directoryCache message listIndex fileName finalChoice nameList sizeList dateList'
- instanceVariableNames: 'pattern directory directoryCache list listIndex fileName finalChoice'
classVariableNames: ''
poolDictionaries: ''
category: 'Tools-FileDialogs'!
+ !FileAbstractSelectionDialog commentStamp: 'tpr 11/9/2017 15:37' prior: 0!
- !FileAbstractSelectionDialog commentStamp: 'tpr 10/28/2017 12:55' prior: 0!
FileAbstractSelectionDialog is the abstract superclass for the file chooser & saver modal dialogs.
The UI provides a text input field, a directory tree widget and a list of files within any chosen directory, and buttons to accept the selected file name/path or cancel the operation. See subclass comments and class side methods for specific usage examples.
Instance Variables
directory: <FileDirectory> used for the currently selected directory
directoryCache: <WeakIdentityKeyDictionary> used to cache a boolean to help us more quickly populate the directory tree widget when revisiting a directory
fileName: <String|nil> the name of the currently selected file, if any
finalChoice: <String|nil> pathname of the finally chosen file, returned as the result of accepting; nil is returned otherwise
list: <Array> the list of String of filenames (and date/size) that match the current pattern
listIndex: <Integer> list index of the currently selected file
+ pattern: <String> the pattern is held as a string that may include * or # wildcasrds. See FileAbstractSelectionDialog>>#parsePatternString for details!
- pattern: <String> the pattern is held as a string with three simple tokens;
- a) a ; or LF or CR splits the string into separate patterns and filenames matching any of them will be included in list
- b) a * matches any number of characters
- c) a # matches one character
- The usage of pattern (see entriesMatching:, updateFileList , listForPatterns:) definitely needs improving.!
Item was changed:
----- Method: FileAbstractSelectionDialog>>acceptFileName (in category 'initialize-release') -----
acceptFileName
+ "User clicked to accept the current state so save the filename and close the dialog"
finalChoice := fileName.
self changed: #close!
Item was changed:
----- Method: FileAbstractSelectionDialog>>buildFileListWith: (in category 'toolbuilder') -----
buildFileListWith: builder
| listSpec |
+ listSpec := builder pluggableMultiColumnListSpec new.
- listSpec := builder pluggableListSpec new.
listSpec
model: self ;
list: #fileList ;
getIndex: #fileListIndex ;
setIndex: #fileListIndex: ;
menu: nil ;
keyPress: nil ;
frame:
(self
frameOffsetFromTop:0
fromLeft: 0
width: 1
+ bottomFraction: 1);
+ hScrollBarPolicy: #always .
- bottomFraction: 1) .
^listSpec!
Item was changed:
----- Method: FileAbstractSelectionDialog>>buildWindowWith: (in category 'toolbuilder') -----
buildWindowWith: builder
"Since a file chooser is a modal dialog we over-ride the normal window build to use a dialog as the top component"
| windowSpec |
windowSpec := builder pluggableDialogSpec new.
windowSpec model: self;
label: #windowTitle;
+ message: #userMessage;
extent: self initialExtent;
children: OrderedCollection new;
buttons: OrderedCollection new.
^windowSpec!
Item was changed:
----- Method: FileAbstractSelectionDialog>>buildWith: (in category 'toolbuilder') -----
buildWith: builder
"assemble the spec for the common chooser/saver dialog UI"
+
- "ToolBuilder open: FileChooserDialog"
- "ToolBuilder open: FileSaverDialog"
| windowSpec window |
windowSpec := self buildWindowWith: builder specs: {
(self topConstantHeightFrame: self textViewHeight
fromLeft: 0
width: 1) -> [self buildTextInputWith: builder].
(self frameOffsetFromTop: self textViewHeight
fromLeft: 0.25
width: 0.75
offsetFromBottom: self buttonHeight) -> [self buildFileListWith: builder].
(self frameOffsetFromTop: self textViewHeight
fromLeft: 0
width: 0.25
offsetFromBottom: self buttonHeight) -> [self buildDirectoryTreeWith: builder].
}.
windowSpec buttons add:( builder pluggableButtonSpec new
model: self;
label: 'Accept';
action: #acceptFileName).
windowSpec buttons add:( builder pluggableButtonSpec new
model: self;
label: 'Cancel';
action: #cancelFileChooser).
window := builder build: windowSpec.
self changed: #selectedPath.
^window
!
Item was changed:
----- Method: FileAbstractSelectionDialog>>cancelFileChooser (in category 'initialize-release') -----
cancelFileChooser
+ "User clicked to cancel the current state so nil the filename and close the dialog"
fileName := nil.
self changed: #close.!
Item was added:
+ ----- Method: FileAbstractSelectionDialog>>defaultPattern (in category 'path and pattern') -----
+ defaultPattern
+
+ ^'*'!
Item was changed:
----- Method: FileAbstractSelectionDialog>>directory (in category 'directory tree') -----
directory
+ "If nobody has set a specific directory we need a plausible default"
+ ^ directory ifNil: [ FileDirectory default]!
- ^ directory!
Item was changed:
----- Method: FileAbstractSelectionDialog>>directory: (in category 'directory tree') -----
directory: aFileDirectory
+ "Set the path of the directory to be displayed in the directory tree pane"
+
- "Set the path of the directory to be displayed."
self okToChange ifFalse: [ ^ self ].
self modelSleep.
directory := aFileDirectory.
self modelWakeUp.
+ self changed: #directory!
- self changed: #directory.
- self pattern: pattern!
Item was changed:
----- Method: FileAbstractSelectionDialog>>fileList (in category 'file list') -----
fileList
+ "return the list of files in the currently selected directory; if we haven't yet read an actual directory return empty lists for now"
+
+ nameList ifNil: [nameList := dateList := sizeList := #()].
+ ^Array with: nameList with: dateList with: sizeList!
- "return the list of files in the currently selected directory"
-
- ^list!
Item was changed:
----- Method: FileAbstractSelectionDialog>>fileListIndex (in category 'file list') -----
fileListIndex
+ "return the index in the list of files for the currently selected file; we initialise this to 0 so that the initial listmorph doesn't get upset before we actually populate it with file details - which we don't do until a directory is selected"
- "return the index in the list of files for the currently selected filey"
^listIndex!
Item was removed:
- ----- Method: FileAbstractSelectionDialog>>fileNameFormattedFrom:sizePad: (in category 'path and pattern') -----
- fileNameFormattedFrom: entry sizePad: sizePad
- "entry is a 5-element array of the form:
- (name creationTime modificationTime dirFlag fileSize)"
- | sizeStr nameStr dateStr |
- nameStr := entry isDirectory
- ifTrue: [entry name , self folderString]
- ifFalse: [entry name].
- dateStr := ((Date fromSeconds: entry modificationTime )
- printFormat: #(3 2 1 $. 1 1 2)) , ' ' ,
- (String streamContents: [:s |
- (Time fromSeconds: entry modificationTime \\ 86400)
- print24: true on: s]).
- sizeStr := entry fileSize asStringWithCommas.
- ^ nameStr , ' (' , dateStr , ' ' , sizeStr , ')'
- !
Item was removed:
- ----- Method: FileAbstractSelectionDialog>>fileNameFromFormattedItem: (in category 'file list') -----
- fileNameFromFormattedItem: item
- "Extract fileName and folderString from a formatted fileList item string"
-
- | from to |
- from := item lastIndexOf: $(.
- to := item lastIndexOf: $).
- ^ (from * to = 0
- ifTrue: [item]
- ifFalse: [item copyReplaceFrom: from to: to with: '']) withBlanksTrimmed!
Item was changed:
----- Method: FileAbstractSelectionDialog>>hasMoreDirectories: (in category 'directory tree') -----
hasMoreDirectories: aDirectory
+ "The directory tree morph needs to know if a specific directory has subdirectories; we cache the answer to speed up later visits to the same directory"
+
^directoryCache at: aDirectory ifAbsentPut:[
[aDirectory directoryNames notEmpty] on: Error do:[:ex| true].
].!
Item was changed:
----- Method: FileAbstractSelectionDialog>>initialize (in category 'initialize-release') -----
initialize
super initialize.
directoryCache := WeakIdentityKeyDictionary new.
+ listIndex := 0.
+ pattern := self defaultPattern!
- self directory: FileDirectory default!
Item was changed:
----- Method: FileAbstractSelectionDialog>>listForPatterns: (in category 'path and pattern') -----
+ listForPatterns: arrayOfPatterns
- listForPatterns: anArray
"return a list of those file names which match any of the patterns in the array."
+ | newList |
- | sizePad newList |
newList := Set new.
+ arrayOfPatterns do: [ :pat | newList addAll: (self entriesMatching: pat) ].
- anArray do: [ :pat | newList addAll: (self entriesMatching: pat) ].
- sizePad := (newList inject: 0 into: [:mx :entry | mx max: entry fileSize])
- asStringWithCommas size.
- newList := newList collect: [ :e | self fileNameFormattedFrom: e sizePad: sizePad ].
+ newList := newList sorted: [:a :b|
+ a name <= b name].
+ nameList := newList collect:[:e| e name].
+ dateList := newList collect:[:e| ((Date fromSeconds: e modificationTime )
+ printFormat: #(3 2 1 $. 1 1 2)) , ' ' ,
+ (String streamContents: [:s |
+ (Time fromSeconds: e modificationTime \\ 86400)
+ print24: true on: s])].
+ sizeList := newList collect:[:e| e fileSize asStringWithCommas]
+ !
- ^ newList asArray!
Item was added:
+ ----- Method: FileAbstractSelectionDialog>>parsePatternString (in category 'file list') -----
+ parsePatternString
+ "The pattern is held as a string that may have three simple tokens included along with normal characters;
+ a) a ; or LF or CR splits the string into separate patterns and filenames matching any of them will be included in list
+ b) a * matches any number of characters
+ c) a # matches one character"
+
+ | patterns |
+ patterns := OrderedCollection new.
+ (pattern findTokens: (String with: Character cr with: Character lf with: $;))
+ do: [ :each |
+ (each includes: $*) | (each includes: $#)
+ ifTrue: [ patterns add: each]
+ ifFalse: [each isEmpty
+ ifTrue: [ patterns add: '*']
+ ifFalse: [ patterns add: '*' , each , '*']]].
+
+ ^patterns!
Item was changed:
----- Method: FileAbstractSelectionDialog>>pattern: (in category 'path and pattern') -----
pattern: textOrStringOrNil
+ "Make sure the pattern source string is neither nil nor empty"
textOrStringOrNil
ifNil: [pattern := '*']
ifNotNil: [pattern := textOrStringOrNil asString].
+ pattern isEmpty ifTrue: [pattern := '*']!
- pattern isEmpty ifTrue: [pattern := '*'].
- self updateFileList.
- ^ true
- !
Item was changed:
----- Method: FileAbstractSelectionDialog>>rootDirectoryList (in category 'directory tree') -----
rootDirectoryList
+ "Return a list of know root directories; forms the root nodes ot the directory tree morph"
+
| dirList dir |
dir := FileDirectory on: ''.
dirList := dir directoryNames collect:[:each| dir directoryNamed: each]..
dirList isEmpty ifTrue:[dirList := Array with: FileDirectory default].
^dirList!
Item was changed:
----- Method: FileAbstractSelectionDialog>>selectedPath (in category 'path and pattern') -----
selectedPath
+ "Return an array of directories representing the path from directory up to the root; used to build the directory tree morph"
+
| top here |
top := FileDirectory root.
here := directory.
^(Array streamContents:[:s| | next |
s nextPut: here.
[next := here containingDirectory.
top pathName = next pathName] whileFalse:[
s nextPut: next.
here := next.
]]) reversed.!
Item was changed:
----- Method: FileAbstractSelectionDialog>>setDirectoryTo: (in category 'directory tree') -----
setDirectoryTo: dir
"Set the current directory shown in the FileList.
Does not allow setting the directory to nil since this blows up in various places."
+
dir ifNil:[^self].
self directory: dir.
+ self updateFileList.
+ self changed: #inputText!
- self changed: #fileList.
- self changed: #inputText.!
Item was changed:
----- Method: FileAbstractSelectionDialog>>subDirectoriesOf: (in category 'directory tree') -----
subDirectoriesOf: aDirectory
+
^aDirectory directoryNames collect:[:each| aDirectory directoryNamed: each].!
Item was changed:
----- Method: FileAbstractSelectionDialog>>textViewHeight (in category 'ui details') -----
textViewHeight
" Take a whole font line and 50 % for space "
+
^ (self textViewFont height * 1.5) ceiling!
Item was changed:
----- Method: FileAbstractSelectionDialog>>updateFileList (in category 'file list') -----
updateFileList
+ "Update my files list with file names in the current directory that match the pattern.
+ The pattern string may have embedded newlines or semicolons; these separate multiple different patterns."
- "Update my files list with file names in the current directory
- that match the pattern.
- The pattern string may have embedded newlines or semicolons; these separate different patterns."
- | patterns |
- patterns := OrderedCollection new.
- Cursor wait showWhile: [
- (pattern findTokens: (String with: Character cr with: Character lf with: $;))
- do: [ :each |
- (each includes: $*) | (each includes: $#)
- ifTrue: [ patterns add: each]
- ifFalse: [each isEmpty
- ifTrue: [ patterns add: '*']
- ifFalse: [ patterns add: '*' , each , '*']]].
+ Cursor wait
+ showWhile: [self listForPatterns: self parsePatternString.
+ listIndex := 0.
+ self changed: #fileList]!
- list := self listForPatterns: patterns.
- listIndex := 0.
- fileName := nil.
- self changed: #fileList]!
Item was changed:
----- Method: FileChooserDialog>>fileListIndex: (in category 'file list') -----
fileListIndex: anInteger
+ "We've selected the file at the given index, so find the file name."
- "We've selected the file at the given index,so find the file name."
self okToChange ifFalse: [^ self].
listIndex := anInteger.
listIndex = 0
ifTrue: [fileName := nil]
+ ifFalse: [fileName :=nameList at: anInteger]. "open the file selected"
- ifFalse:
- [fileName := self fileNameFromFormattedItem: (list at: anInteger)]. "open the file selected"
+ self changed: #fileListIndex!
- self
- changed: #fileListIndex!
Item was changed:
----- Method: FileChooserDialog>>inputText (in category 'path and pattern') -----
inputText
"Answers path and pattern together"
+
^directory fullName, directory slash, pattern!
Item was changed:
----- Method: FileChooserDialog>>inputText: (in category 'path and pattern') -----
inputText: stringOrText
+ "both path and pattern are in the text, so split them apart and then change both directory and the match for the filenames before updating the file list"
+
- "both path and pattern are in the text, so split them apart"
| base pat aString |
aString := stringOrText asString.
base := aString copyUpToLast: directory pathNameDelimiter.
pat := aString copyAfterLast: directory pathNameDelimiter.
self changed: #inputText. "avoid asking if it's okToChange"
+
- pattern := pat.
self directory: (FileDirectory on: base).
+ self pattern: pat.
+ self updateFileList.
self changed: #inputText.
self changed: #selectedPath.!
Item was changed:
----- Method: FileChooserDialog>>openOn:pattern: (in category 'initialize-release') -----
openOn: aDirectory pattern: aPatternString
"open a modal dialog to choose a file from aDirectory as filtered by aPattern"
directory := aDirectory.
+ self pattern: aPatternString.
- pattern := aPatternString.
ToolBuilder open: self.
^self finalChoice!
Item was added:
+ ----- Method: FileChooserDialog>>userMessage (in category 'ui details') -----
+ userMessage
+ "return the string to present to the user in order to explain the purpose of this dialog appearing"
+
+ ^message ifNil:['Choose a file name']!
Item was added:
+ ----- Method: FileSaverDialog class>>openOn:initialFilename: (in category 'instance creation') -----
+ openOn: aDirectory initialFilename: aString
+ "open a modal dialog to save a file. Start the dialog with aDirectory selected and the suggested file name"
+
+ ^self new openOn: aDirectory initialFilename: aString
+
+ !
Item was added:
+ ----- Method: FileSaverDialog class>>openOnInitialFilename: (in category 'instance creation') -----
+ openOnInitialFilename: aString
+ "open a modal dialog to save a file. Start the dialog with the default directory selected and the suggested file name"
+
+ ^self new openOn: FileDirectory default initialFilename: aString
+
+ !
Item was added:
+ ----- Method: FileSaverDialog>>acceptFileName (in category 'initialize-release') -----
+ acceptFileName
+ "make sure to accept any edit in the filename before closing"
+
+ self changed: #acceptChanges.
+ ^super acceptFileName!
Item was changed:
----- Method: FileSaverDialog>>fileListIndex: (in category 'file list') -----
fileListIndex: anInteger
+ "We've selected the file at the given index, so find the file name."
- "We've selected the file at the given index,so find the file name."
self okToChange ifFalse: [^ self].
listIndex := anInteger.
listIndex = 0
ifTrue: [fileName := nil]
+ ifFalse: [fileName := nameList at: anInteger]. "open the file selected"
- ifFalse:
- [fileName := self fileNameFromFormattedItem: (list at: anInteger)]. "open the file selected"
self
changed: #fileListIndex;
changed: #inputText!
Item was changed:
----- Method: FileSaverDialog>>inputText: (in category 'filename') -----
inputText: aText
+ "user has entered a potential filename in the text field. If it is a file in the current list, highlight it"
- "user has entered a potential filename in the text field. If it is a file in the currect list, highlight it"
fileName := aText asString.
+ listIndex := nameList findFirst:[: nm| nm = fileName].
- listIndex := list findFirst:[: nm| (self fileNameFromFormattedItem: nm) = fileName].
- listIndex = 0 ifFalse:
- [fileName := self fileNameFromFormattedItem: (list at: listIndex)].
self
changed: #fileListIndex;
+ changed: #inputText!
- changed: #inputFilename!
Item was removed:
- ----- Method: FileSaverDialog>>selectedPath (in category 'path and pattern') -----
- selectedPath
- | top here |
- top := FileDirectory root.
- here := directory.
- ^(Array streamContents:[:s| | next |
- s nextPut: here.
- [next := here containingDirectory.
- top pathName = next pathName] whileFalse:[
- s nextPut: next.
- here := next.
- ]]) reversed.!
Item was added:
+ ----- Method: FileSaverDialog>>userMessage (in category 'ui details') -----
+ userMessage
+ "return the string to present to the user in order to explain the purpose of this dialog appearing"
+
+ ^message ifNil:['Choose a file name; you can also edit the name below to create a new file name']!
tim Rowledge uploaded a new version of ToolBuilder-Morphic to project The Trunk:
http://source.squeak.org/trunk/ToolBuilder-Morphic-tpr.198.mcz
==================== Summary ====================
Name: ToolBuilder-Morphic-tpr.198
Author: tpr
Time: 10 November 2017, 4:29:04.518411 pm
UUID: fb6e05e6-8817-4dfc-b152-8fa5bfac720b
Ancestors: ToolBuilder-Morphic-mt.197
Morphic access to FileChooser/Saver Dialogs
=============== Diff against ToolBuilder-Morphic-mt.197 ===============
Item was added:
+ ----- Method: MorphicToolBuilder>>buildPluggableMultiColumnList: (in category 'widgets required') -----
+ buildPluggableMultiColumnList: aSpec
+ | widget listClass getIndex setIndex |
+ aSpec getSelected ifNil:[
+ listClass := self multiColumnListClass.
+ getIndex := aSpec getIndex.
+ setIndex := aSpec setIndex.
+ ] ifNotNil:[self error: 'not yet'.
+ listClass := self listByItemClass.
+ getIndex := aSpec getSelected.
+ setIndex := aSpec setSelected.
+ ].
+ widget := listClass on: aSpec model
+ list: aSpec list
+ selected: getIndex
+ changeSelected: setIndex
+ menu: aSpec menu
+ keystroke: aSpec keyPress.
+ self register: widget id: aSpec name.
+
+ " widget
+ dragItemSelector: aSpec dragItem;
+ dropItemSelector: aSpec dropItem;
+ wantsDropSelector: aSpec dropAccept;
+ dragStartedSelector: aSpec dragStarted."
+
+ self setListPropertiesFor: widget spec: aSpec.
+ ^widget!
Item was added:
+ ----- Method: MorphicToolBuilder>>multiColumnListClass (in category 'widget classes') -----
+ multiColumnListClass
+ ^ PluggableMultiColumnListMorph!
Item was added:
+ ----- Method: MorphicUIManager>>filenameSaverRequest:initialAnswer: (in category 'ui requests') -----
+ filenameSaverRequest: queryString initialAnswer: defaultAnswer
+ "Open a FileSaverDialog to ask for a place and filename to use for saving a file. The initial suggestion for the filename is defaultAnswer but the user may choose any existing file or type in a new name entirely"
+ "Note that this is a trulyawful message name but I can't think of a better one right now"
+ ^FileSaverDialog openOnInitialFilename: defaultAnswer
+ !
tim Rowledge uploaded a new version of ToolBuilder-MVC to project The Trunk:
http://source.squeak.org/trunk/ToolBuilder-MVC-tpr.49.mcz
==================== Summary ====================
Name: ToolBuilder-MVC-tpr.49
Author: tpr
Time: 10 November 2017, 4:27:04.88348 pm
UUID: 1b05c2bc-3134-4724-bf6c-2f96952c7d8a
Ancestors: ToolBuilder-MVC-nice.48
MVC handling of the FileChooser stuff; revert to boring old FillInTheBlanks until someone updates things
=============== Diff against ToolBuilder-MVC-nice.48 ===============
Item was added:
+ ----- Method: MVCUIManager>>filenameSaverRequest:initialAnswer: (in category 'ui requests') -----
+ filenameSaverRequest: queryString initialAnswer: defaultAnswer
+ "Open a FileSaverDialog to ask for a place and filename to use for saving a file. The initial suggestion for the filename is defaultAnswer but the user may choose any existing file or type in a new name entirely"
+ "Note that this is a trulyawful message name but I can't think of a better one right now"
+ "MVC has to stick with the boring way of doing it"
+ ^self request: queryString initialAnswer: defaultAnswer
+ !
tim Rowledge uploaded a new version of ToolBuilder-Kernel to project The Trunk:
http://source.squeak.org/trunk/ToolBuilder-Kernel-tpr.114.mcz
==================== Summary ====================
Name: ToolBuilder-Kernel-tpr.114
Author: tpr
Time: 10 November 2017, 4:15:30.142191 pm
UUID: 19c5cd07-e942-45d4-9529-30de44339db0
Ancestors: ToolBuilder-Kernel-eem.113
Add a multi-column list to the ToolBuider world.
=============== Diff against ToolBuilder-Kernel-eem.113 ===============
Item was added:
+ PluggableListSpec subclass: #PluggableMultiColumnListSpec
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'ToolBuilder-Kernel'!
Item was added:
+ ----- Method: PluggableMultiColumnListSpec>>buildWith: (in category 'building') -----
+ buildWith: builder
+ ^builder buildPluggableMultiColumnList: self!
Item was added:
+ ----- Method: ToolBuilder>>buildPluggableMultiColumnList: (in category 'widgets required') -----
+ buildPluggableMultiColumnList: aSpec
+ ^self subclassResponsibility!
Item was added:
+ ----- Method: ToolBuilder>>pluggableMultiColumnListSpec (in category 'defaults') -----
+ pluggableMultiColumnListSpec
+ ^PluggableMultiColumnListSpec!
Item was added:
+ ----- Method: UIManager>>filenameSaverRequest:initialAnswer: (in category 'ui requests') -----
+ filenameSaverRequest: queryString initialAnswer: defaultAnswer
+ "Open a FileSaverDialog to ask for a place and filename to use for saving a file. The initial suggestion for the filename is defaultAnswer but the user may choose any existing file or type in a new name entirely"
+ "Note that this is a trulyawful message name but I can't think of a better one right now"
+ ^self subclassResponsibility!