<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
</head>
<body>
<style type="text/css" style="display:none;"><!-- P {margin-top:0;margin-bottom:0;} --></style>
<div id="divtagdefaultwrapper" style="font-size:12pt;color:#000000;font-family:Calibri,Helvetica,sans-serif;" dir="ltr">
<p>Hi Marcel,</p>
<p><br>
</p>
<p>nice change, except for the large size of the menu IMHO. :-) Given the fact that workspace-to-file is just an edge of workspaces, it blows up the menu significantly with 4 items and the long width for the file name. What about a submenu for these items?</p>
<p><br>
</p>
<p>Best,</p>
<p>Christoph</p>
</div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>Von:</b> Squeak-dev <squeak-dev-bounces@lists.squeakfoundation.org> im Auftrag von Taeumel, Marcel<br>
<b>Gesendet:</b> Donnerstag, 25. November 2021 11:58:47<br>
<b>An:</b> squeak-dev<br>
<b>Betreff:</b> Re: [squeak-dev] The Trunk: Tools-mt.1075.mcz</font>
<div> </div>
</div>
<div>
<div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000;text-align: left" dir="ltr">
<img id="4c8eaa62-b0a6-4a55-b785-3fdb846c0280" width="506" height="222" src="cid:0dab9451-0169-45d5-91f3-93fb716479a9"><!-- </img> --><br>
<div class="mb_sig"></div>
<blockquote class="history_container" type="cite" style="border-left-style: solid;border-width: 1px;margin-top: 20px;margin-left: 0px;padding-left: 10px;min-width: 500px">
<p style="color: #AAAAAA; margin-top: 10px;">Am 25.11.2021 11:49:07 schrieb commits@source.squeak.org <commits@source.squeak.org>:</p>
<div style="font-family:Arial,Helvetica,sans-serif">Marcel Taeumel uploaded a new version of Tools to project The Trunk:<br>
http://source.squeak.org/trunk/Tools-mt.1075.mcz<br>
<br>
==================== Summary ====================<br>
<br>
Name: Tools-mt.1075<br>
Author: mt<br>
Time: 25 November 2021, 11:48:52.151354 am<br>
UUID: 089e5a95-68d9-1a44-957c-5ddbc897d40e<br>
Ancestors: Tools-mt.1074<br>
<br>
Refactors and extends "save contents to file" feature in workspaces and all kinds of tools that use PluggableTextMorph's (with their TextEditor). This also simplifies the rather recent "file-out workspace contents on accept" feature (see preferences).<br>
<br>
Thanks to Eliot (eem) for the idea and change set that served as a valuable template for this refactoring.<br>
<br>
Thanks to Christoph (ct) for pointing out the strange nature of that "file-out file path" preference.<br>
<br>
Note that all open workspaces should be updated automatically. See postscript.<br>
<br>
=============== Diff against Tools-mt.1074 ===============<br>
<br>
Item was changed:<br>
----- Method: FileList>>viewContentsInWorkspace (in category 'own services') -----<br>
viewContentsInWorkspace<br>
+ "View the contents of my selected file in a new workspace."<br>
- "View the contents of my selected file in a new workspace"<br>
<br>
+ | fileContents workspace lineConversion |<br>
+ fileContents := self directory<br>
+ readOnlyFileNamed: self fileName<br>
+ do: [:fileStream |<br>
+ fileStream<br>
+ setConverterForCode;<br>
+ wantsLineEndConversion: true.<br>
+ lineConversion := fileStream detectLineEndConvention.<br>
+ fileStream contents]. <br>
+ workspace := (Project uiManager edit: fileContents label: nil shouldStyle: Workspace shouldStyle) model.<br>
+ <br>
+ "Remember certain information to allow edits in the same file."<br>
+ workspace<br>
+ windowTitle: (self directory localNameFor: self fileName);<br>
+ fileDirectory: self directory;<br>
+ setProperty: #fileLineConversion toValue: lineConversion;<br>
+ saveContentsInFileOnAccept.<br>
+ !<br>
- | aFileStream aName lineConversion w | <br>
- aFileStream := (directory readOnlyFileNamed: self fullName) setConverterForCode.<br>
- aFileStream wantsLineEndConversion: true.<br>
- lineConversion := aFileStream detectLineEndConvention.<br>
- aName := aFileStream localName.<br>
- w := UIManager default<br>
- edit: ([aFileStream contentsOfEntireFile] ensure: [aFileStream close])<br>
- label: ((aName includesSubstring: 'Workspace')<br>
- ifTrue: [#('.text' '.txt') inject: aName into: [:name :ext| (name endsWith: ext) ifTrue: [name allButLast: ext size] ifFalse: [name]]]<br>
- ifFalse: ['Workspace from ', aName]).<br>
- w setProperty: #lineConversion toValue: lineConversion.<br>
- directory ~= FileDirectory default ifTrue: [w setProperty: #myDir toValue: directory]!<br>
<br>
Item was removed:<br>
- ----- Method: Workspace class>>fileOut: (in category 'support') -----<br>
- fileOut: contents<br>
- "Write the given contents into the workspace file-out file path."<br>
- <br>
- | filePath |<br>
- filePath := self fileOutFilePath.<br>
- (FileDirectory default on: filePath) containingDirectory assureExistence.<br>
- FileStream<br>
- fileNamed: filePath<br>
- do: [:stream |<br>
- stream<br>
- setToEnd;<br>
- nextPutAll: '"----ACCEPT----';<br>
- nextPutAll: DateAndTime now asString;<br>
- nextPutAll: '"';<br>
- cr; nextPutAll: contents; cr].<br>
- Transcript showln: 'Workspace contents successfully appended to: ', filePath printString.!<br>
<br>
Item was removed:<br>
- ----- Method: Workspace class>>fileOutFilePath (in category 'preferences') -----<br>
- fileOutFilePath<br>
- <preference: ?file-outfilepath="" for="" workspace?=""></preference:><br>
- categoryList: #('browsing' 'tools')<br>
- description: 'Set the file-out location for #fileOutOnAccept in workspaces.' <br>
- type: #String><br>
- ^ FileOutFilePath ifNil: [ 'workspace.st' ]!<br>
<br>
Item was removed:<br>
- ----- Method: Workspace class>>fileOutFilePath: (in category 'preferences') -----<br>
- fileOutFilePath: aString<br>
- <br>
- FileOutFilePath := aString.!<br>
<br>
Item was changed:<br>
----- Method: Workspace class>>fileOutOnAccept (in category 'preferences') -----<br>
fileOutOnAccept<br>
<preference: ?file-outworkspacecontentsonaccept?=""></preference:><br>
categoryList: #('browsing' 'tools')<br>
+ description: 'If true, accepting contents in a workspace will append them to a file on disk. The file name is derived from the workspace''s current window title and can thus be changed. The default name is thus ''Workspace.text''.'<br>
- description: 'If true, accepting contents in a workspace will append them to a known location in the file system. See #fileOutFilePath.'
<br>
type: #Boolean><br>
^ FileOutOnAccept ifNil: [ true ]!<br>
<br>
Item was changed:<br>
----- Method: Workspace class>>open (in category 'instance creation') -----<br>
open<br>
<br>
| workspace |<br>
workspace := self new.<br>
+ self fileOutOnAccept<br>
+ ifTrue: [workspace appendContentsToFileOnAccept].<br>
- self fileOutOnAccept ifTrue: [<br>
- workspace acceptAction: [:string | self fileOut: string]].<br>
^ self embedTranscript<br>
ifTrue: [workspace buildAndOpenWorkspaceTranscript]<br>
ifFalse: [workspace buildAndOpen]!<br>
<br>
Item was changed:<br>
----- Method: Workspace>>acceptContents: (in category 'accessing') -----<br>
acceptContents: aString<br>
+ <br>
+ ^ (acceptAction ifNotNil: [acceptAction value: aString]) ~~ false<br>
+ and: [super acceptContents: aString]!<br>
- acceptAction ifNotNil:[acceptAction value: aString].<br>
- ^super acceptContents: aString.!<br>
<br>
Item was changed:<br>
----- Method: Workspace>>addModelItemsToWindowMenu: (in category 'menu commands') -----<br>
addModelItemsToWindowMenu: aMenu <br>
<br>
aMenu addLine.<br>
aMenu<br>
+ add: 'change file name...'<br>
+ target: self<br>
+ action: #changeFileName.<br>
+ aMenu<br>
add: 'save contents to file...'<br>
target: self<br>
action: #saveContentsInFile.<br>
aMenu<br>
+ addUpdating: #saveContentsInFileOnAcceptWording<br>
+ target: self<br>
+ action: #saveContentsInFileOnAccept.<br>
+ aMenu<br>
+ addUpdating: #appendContentsToFileOnAcceptWording<br>
+ target: self<br>
+ action: #appendContentsToFileOnAccept.<br>
+ aMenu addLine.<br>
+ aMenu<br>
add: 'inspect variables'<br>
target: self<br>
action: #inspectBindings.<br>
aMenu<br>
add: 'reset variables'<br>
target: self<br>
+ action: #resetBindings.<br>
+ aMenu addLine.<br>
- action: #initializeBindings.<br>
aMenu<br>
addUpdating: #mustDeclareVariableWording<br>
target: self<br>
action: #toggleVariableDeclarationMode.<br>
aMenu<br>
addUpdating: #acceptDroppedMorphsWording<br>
target: self<br>
action: #toggleDroppingMorphForReference.<br>
<br>
self addToggleStylingMenuItemTo: aMenu.<br>
!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>appendContentsToFileOnAccept (in category 'menu commands') -----<br>
+ appendContentsToFileOnAccept<br>
+ "Arrange that the contents will be appended to a file when the user accepts."<br>
+ <br>
+ self saveContentsInFileOnAcceptEnabled<br>
+ ifTrue: [(Project uiManager confirm: 'Do you really want to change file access mode\from #update to #append?\\You might corrupt data when accepting changes.' withCRs translated)<br>
+ ifFalse: [^ self]].<br>
+ <br>
+ self acceptAction: (self appendContentsToFileOnAcceptEnabled ifTrue: [ "no action" ] ifFalse: [<br>
+ [:freshContents | | fileName stringToAppend |<br>
+ "Ensure to compute fileName as late as possible to consider recent changes of the #windowTitle."<br>
+ fileName := self suggestedFileNameForSave.<br>
+ <br>
+ stringToAppend := '"----ACCEPT----{1}"\{2}\' withCRs<br>
+ format: { DateAndTime now asString. freshContents }.<br>
+ <br>
+ ((FileDirectory forFileName: fileName) fileExists: fileName)<br>
+ ifFalse: [ "If the default file name, which is derived from the current window title, does not exist, ask the user once to confirm the location."
<br>
+ self<br>
+ saveContents: stringToAppend<br>
+ accessMode: #create]<br>
+ ifTrue: [ "Update/replace the contents in the existing file."<br>
+ self<br>
+ saveContents: stringToAppend<br>
+ onFileNamed: fileName<br>
+ accessMode: #append]] ]).!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>appendContentsToFileOnAcceptEnabled (in category 'menu commands') -----<br>
+ appendContentsToFileOnAcceptEnabled<br>
+ <br>
+ ^ (acceptAction notNil and: [acceptAction home selector = #appendContentsToFileOnAccept])!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>appendContentsToFileOnAcceptWording (in category 'menu commands') -----<br>
+ appendContentsToFileOnAcceptWording<br>
+ <br>
+ ^ (self appendContentsToFileOnAcceptEnabled<br>
+ ifTrue: ['<yes> append contents to {1} on accept']<br>
+ ifFalse: ['<no> append contents to {1} on accept']) translated<br>
+ format: { self suggestedFileNameForSave contractTo: 32 }!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>changeFileName (in category 'menu commands') -----<br>
+ changeFileName<br>
+ "Let the user specify a new file name (and path) for save-contents requests."<br>
+ <br>
+ (Project uiManager<br>
+ saveFilenameRequest: 'Save text contents in file...'<br>
+ initialAnswer: self suggestedFileNameForSave)<br>
+ ifNotNil: [:newFileName |<br>
+ newFileName ifNotEmpty: [self setFileName: newFileName]].!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>defaultFileNameForSave (in category 'user edits') -----<br>
+ defaultFileNameForSave<br>
+ "Overwritten to combine selected properties. Also see FileList >> #viewContentsInWorkspace to understand where different directories might originate."<br>
+ <br>
+ ^ self fileDirectory fullNameFor: self windowTitle!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>fileDirectory (in category 'accessing') -----<br>
+ fileDirectory<br>
+ "Answer the current directory for save-contents requests."<br>
+ <br>
+ ^ self valueOfProperty: #fileDirectory ifAbsent: [FileDirectory default]!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>fileDirectory: (in category 'accessing') -----<br>
+ fileDirectory: aDirectory<br>
+ "Do not save the default directory so that the image and its surrounding files can be moved across the disk."<br>
+ <br>
+ aDirectory = FileDirectory default<br>
+ ifTrue: [self removeProperty: #fileDirectory]<br>
+ ifFalse: [self setProperty: #fileDirectory toValue: aDirectory].!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>saveContents:onFileNamed:accessMode: (in category 'user edits') -----<br>
+ saveContents: stringContents onFileNamed: fileName accessMode: accessMode<br>
+ "Overwritten to set conversion rule of line-end character. See FileList >> #viewContentsInWorkspace."<br>
+ <br>
+ ^ self<br>
+ saveContents: stringContents<br>
+ onFileNamed: fileName<br>
+ accessMode: accessMode<br>
+ workBlock: [:fileStream |<br>
+ fileStream<br>
+ lineEndConvention: (self valueOfProperty: #fileLineConversion); "nil is fine here..."<br>
+ nextPutAll: stringContents]!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>saveContents:onFileNamed:accessMode:workBlock: (in category 'user edits') -----<br>
+ saveContents: stringContents onFileNamed: fileName accessMode: accessMode workBlock: workBlock<br>
+ "Overwritten to update #fileDirectory property and #windowTitle on success."<br>
+ <br>
+ ^ (super<br>
+ saveContents: stringContents<br>
+ onFileNamed: fileName<br>
+ accessMode: accessMode<br>
+ workBlock: workBlock)<br>
+ ifFalse: [false "no success"]<br>
+ ifTrue: [self setFileName: fileName. true "success"]!<br>
<br>
Item was changed:<br>
----- Method: Workspace>>saveContentsInFile (in category 'menu commands') -----<br>
saveContentsInFile<br>
+ "Save the view's current contents in a file. Dispatch goes through the view (or morph). See commentary in and senders of #saveContents:accessMode:."<br>
- "Pass along this message to the controller or morph. (Possibly this Workspace menu item could be deleted, since it's now in the text menu.)"<br>
<br>
+ self changed: #saveContents.!<br>
- self changed: #saveContents<br>
- !<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>saveContentsInFileOnAccept (in category 'menu commands') -----<br>
+ saveContentsInFileOnAccept<br>
+ "Arrange that the contents will be saved to a file on each save (or accept). Replace any existing file contents."<br>
+ <br>
+ self flag: #discuss. "mt: Is it 'onFile' or rather 'inFile'? Note that there are different access modes."<br>
+ <br>
+ self appendContentsToFileOnAcceptEnabled<br>
+ ifTrue: [(Project uiManager confirm: 'Do you really want to change file access mode\from #append to #update?\\You might lose data when accepting changes.' withCRs translated)<br>
+ ifFalse: [^ self]].<br>
+ <br>
+ self acceptAction: (self saveContentsInFileOnAcceptEnabled<br>
+ ifFalse: [ [:stringToSave | | fileName |<br>
+ "Ensure to compute fileName as late as possible to consider recent changes of the #windowTitle."<br>
+ fileName := self suggestedFileNameForSave.<br>
+ <br>
+ ((FileDirectory forFileName: fileName) fileExists: fileName)<br>
+ ifFalse: [ "If the default file name, which is derived from the current window title, does not exist, ask the user once to confirm the location."
<br>
+ self<br>
+ saveContents: stringToSave<br>
+ accessMode: #create]<br>
+ ifTrue: [ "Update/replace the contents in the existing file."<br>
+ self<br>
+ saveContents: stringToSave<br>
+ onFileNamed: fileName<br>
+ accessMode: #update]] ]).!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>saveContentsInFileOnAcceptEnabled (in category 'menu commands') -----<br>
+ saveContentsInFileOnAcceptEnabled<br>
+ <br>
+ ^ (acceptAction notNil and: [acceptAction home selector = #saveContentsInFileOnAccept])!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>saveContentsInFileOnAcceptWording (in category 'menu commands') -----<br>
+ saveContentsInFileOnAcceptWording<br>
+ <br>
+ ^ (self saveContentsInFileOnAcceptEnabled<br>
+ ifTrue: ['<yes> save contents to {1} on accept']<br>
+ ifFalse: ['<no> save contents to {1} on accept']) translated<br>
+ format: { self suggestedFileNameForSave contractTo: 32 }!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>setFileName: (in category 'initialize-release') -----<br>
+ setFileName: fileName<br>
+ <br>
+ | directory |<br>
+ directory := FileDirectory forFileName: fileName.<br>
+ self fileDirectory: directory.<br>
+ self windowTitle: (directory localNameFor: fileName).!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>windowReqNewLabel: (in category 'user edits') -----<br>
+ windowReqNewLabel: newLabel<br>
+ "The user has edited the window label. Remember for a later save-to-file request. See #defaultFileNameForSave."<br>
+ <br>
+ self setProperty: #windowTitle toValue: newLabel.<br>
+ ^ true!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>windowTitle (in category 'accessing') -----<br>
+ windowTitle<br>
+ <br>
+ ^ self valueOfProperty: #windowTitle ifAbsent: ['Workspace']!<br>
<br>
Item was added:<br>
+ ----- Method: Workspace>>windowTitle: (in category 'accessing') -----<br>
+ windowTitle: aString<br>
+ "Normalize window title to not expose file extension in regular workspaces."<br>
+ <br>
+ | normalizedTitle |<br>
+ normalizedTitle := ((aString includesSubstring: 'Workspace') and: [aString endsWithAnyOf: #('.text' '.txt')])<br>
+ ifTrue: [aString copyFrom: 1 to: (aString lastIndexOf: $.) - 1]<br>
+ ifFalse: [aString].<br>
+ <br>
+ self setProperty: #windowTitle toValue: normalizedTitle.<br>
+ self changed: #windowTitle.!<br>
<br>
Item was changed:<br>
(PackageInfo named: 'Tools') postscript: 'ChangeSorter allSubInstancesDo: [:sorter |<br>
(sorter instVarNamed: ''contentsAreStyleable'') ifNil: [<br>
+ sorter instVarNamed: ''contentsAreStyleable'' put: true]].<br>
+ <br>
+ "Convert existing properties from Morphic windows to the model. For MVC compatibility."<br>
+ Workspace allInstancesDo: [:workspace | <br>
+ workspace containingWindow ifNotNil: [:window |<br>
+ (window valueOfProperty: #myDir) ifNotNil: [:directory |<br>
+ workspace setProperty: #fileDirectory toValue: directory].<br>
+ (window valueOfProperty: #lineConversion) ifNotNil: [:symbol |<br>
+ workspace setProperty: #fileLineConversion toValue: symbol].<br>
+ workspace setProperty: #windowTitle toValue: window label].<br>
+ (workspace acceptAction notNil and: [workspace acceptAction home selector = #open])<br>
+ ifTrue: [workspace appendContentsToFileOnAccept]].'!<br>
- sorter instVarNamed: ''contentsAreStyleable'' put: true]].'!<br>
<br>
<br>
</no></yes></no></yes></div>
</blockquote>
</div>
</div>
</body>
</html>