From commits at source.squeak.org Tue Sep 1 00:07:17 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 1 Sep 2020 00:07:17 0000 Subject: [squeak-dev] The Inbox: Sound-ct.71.mcz Message-ID: A new version of Sound was added to project The Inbox: http://source.squeak.org/inbox/Sound-ct.71.mcz ==================== Summary ==================== Name: Sound-ct.71 Author: ct Time: 1 September 2020, 2:07:15.07999 am UUID: 0d91a1bf-41cb-834c-ab0c-fa2ad832e408 Ancestors: Sound-nice.69 Fixes wave sound streaming on non-filestream objects. The endianness was inverted because #int16: already uses Big Endian. This did not sound well - listen yourself in an unpatched image: :-) array := ByteArray streamContents: [:stream | PluckedSound bachFugue storeWAVSamplesOn: stream]. (FileStream fileNamed: 'bachFugue.wav') binary in: [:stream | [array do: [:ea | stream nextPut: ea]] ensure: [stream close]]. (SampledSound fromWaveFileNamed: 'bachFugue.wav') play. Please review in detail as this is one of my first contacts to the Sound system! =============== Diff against Sound-nice.69 =============== Item was changed: ----- Method: AbstractSound>>storeSampleCount:bigEndian:on: (in category 'file i/o') ----- storeSampleCount: samplesToStore bigEndian: bigEndianFlag on: aBinaryStream "Store my samples on the given stream at the current SoundPlayer sampling rate. If bigFlag is true, then each 16-bit sample is stored most-significant byte first (AIFF files), otherwise it is stored least-significant byte first (WAV files). If self isStereo is true, both channels are stored, creating a stereo file. Otherwise, only the left channel is stored, creating a mono file." + | bufSize stereoBuffer reverseBytes streamDirect | - | bufSize stereoBuffer reverseBytes | self reset. bufSize := (2 * self samplingRate rounded) min: samplesToStore. "two second buffer" stereoBuffer := SoundBuffer newStereoSampleCount: bufSize. + streamDirect := aBinaryStream isKindOf: StandardFileStream. + reverseBytes := (bigEndianFlag xor: Smalltalk isBigEndian) xor: streamDirect not. - reverseBytes := bigEndianFlag ~= (Smalltalk isBigEndian). 'Storing audio...' displayProgressFrom: 0 to: samplesToStore during: [:bar | | remaining out | remaining := samplesToStore. [remaining > 0] whileTrue: [ bar value: samplesToStore - remaining. stereoBuffer primFill: 0. "clear the buffer" self playSampleCount: (bufSize min: remaining) into: stereoBuffer startingAt: 1. out := self isStereo ifTrue: [stereoBuffer] ifFalse: [stereoBuffer extractLeftChannel]. reverseBytes ifTrue: [out reverseEndianness]. + streamDirect - (aBinaryStream isKindOf: StandardFileStream) ifTrue: [ "optimization for files: write sound buffer directly to file" aBinaryStream next: (out size // 2) putAll: out startingAt: 1] "size in words" ifFalse: [ "for non-file streams:" 1 to: out monoSampleCount do: [:i | aBinaryStream int16: (out at: i)]]. + remaining := remaining - bufSize]].! - remaining := remaining - bufSize]]. - ! From commits at source.squeak.org Tue Sep 1 00:10:44 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 1 Sep 2020 00:10:44 0000 Subject: [squeak-dev] The Inbox: Tests-ct.437.mcz Message-ID: Christoph Thiede uploaded a new version of Tests to project The Inbox: http://source.squeak.org/inbox/Tests-ct.437.mcz ==================== Summary ==================== Name: Tests-ct.437 Author: ct Time: 1 September 2020, 2:10:41.85599 am UUID: 88d1330f-334b-a14a-96ca-8ea4a6ab1b52 Ancestors: Tests-pre.436 Adds regression test for bugfix in Sound-ct.71. Should this go into the Tests-Sound or the SoundTests package (both not yet existing)? :-) =============== Diff against Tests-pre.436 =============== Item was changed: SystemOrganization addCategory: #'Tests-Bugs'! SystemOrganization addCategory: #'Tests-Compiler'! SystemOrganization addCategory: #'Tests-Dependencies'! SystemOrganization addCategory: #'Tests-Digital Signatures'! SystemOrganization addCategory: #'Tests-Environments'! SystemOrganization addCategory: #'Tests-Exceptions'! SystemOrganization addCategory: #'Tests-FilePackage'! SystemOrganization addCategory: #'Tests-Files'! SystemOrganization addCategory: #'Tests-Finalization'! SystemOrganization addCategory: #'Tests-Hex'! SystemOrganization addCategory: #'Tests-Installer-Core'! SystemOrganization addCategory: #'Tests-Localization'! SystemOrganization addCategory: #'Tests-Monticello'! SystemOrganization addCategory: #'Tests-Monticello-Mocks'! SystemOrganization addCategory: #'Tests-Monticello-Utils'! SystemOrganization addCategory: #'Tests-Object Events'! SystemOrganization addCategory: #'Tests-ObjectsAsMethods'! SystemOrganization addCategory: #'Tests-PrimCallController'! SystemOrganization addCategory: #'Tests-Release'! SystemOrganization addCategory: #'Tests-System-Applications'! SystemOrganization addCategory: #'Tests-System-Digital Signatures'! SystemOrganization addCategory: #'Tests-System-Object Storage'! SystemOrganization addCategory: #'Tests-System-Preferences'! SystemOrganization addCategory: #'Tests-System-Support'! SystemOrganization addCategory: #'Tests-Utilities'! SystemOrganization addCategory: #'Tests-VM'! SystemOrganization addCategory: #'Tests-MonticelloMocks'! + SystemOrganization addCategory: #'Tests-Sound'! Item was added: + TestCase subclass: #SoundTest + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Tests-Sound'! Item was added: + ----- Method: SoundTest>>testStoreSamples (in category 'tests') ----- + testStoreSamples + + | sound fileName arrayBytes fileBytes | + sound := PluckedSound default. + fileName := 'tempPluck.wav'. + + "Write to file" + fileBytes := [ + sound storeWAVOnFileNamed: fileName. + FileStream oldFileNamed: fileName do: [:stream | + stream binary contents]] ensure: [ + FileDirectory deleteFilePath: fileName]. + + "Write to array" + arrayBytes := ByteArray streamContents: [:stream | + sound storeWAVSamplesOn: stream]. + + "Compare" + self assert: fileBytes equals: arrayBytes.! From commits at source.squeak.org Tue Sep 1 02:00:51 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 1 Sep 2020 02:00:51 0000 Subject: [squeak-dev] The Trunk: Tests-ct.437.mcz Message-ID: David T. Lewis uploaded a new version of Tests to project The Trunk: http://source.squeak.org/trunk/Tests-ct.437.mcz ==================== Summary ==================== Name: Tests-ct.437 Author: ct Time: 1 September 2020, 2:10:41.85599 am UUID: 88d1330f-334b-a14a-96ca-8ea4a6ab1b52 Ancestors: Tests-pre.436 Adds regression test for bugfix in Sound-ct.71. Should this go into the Tests-Sound or the SoundTests package (both not yet existing)? :-) =============== Diff against Tests-pre.436 =============== Item was changed: SystemOrganization addCategory: #'Tests-Bugs'! SystemOrganization addCategory: #'Tests-Compiler'! SystemOrganization addCategory: #'Tests-Dependencies'! SystemOrganization addCategory: #'Tests-Digital Signatures'! SystemOrganization addCategory: #'Tests-Environments'! SystemOrganization addCategory: #'Tests-Exceptions'! SystemOrganization addCategory: #'Tests-FilePackage'! SystemOrganization addCategory: #'Tests-Files'! SystemOrganization addCategory: #'Tests-Finalization'! SystemOrganization addCategory: #'Tests-Hex'! SystemOrganization addCategory: #'Tests-Installer-Core'! SystemOrganization addCategory: #'Tests-Localization'! SystemOrganization addCategory: #'Tests-Monticello'! SystemOrganization addCategory: #'Tests-Monticello-Mocks'! SystemOrganization addCategory: #'Tests-Monticello-Utils'! SystemOrganization addCategory: #'Tests-Object Events'! SystemOrganization addCategory: #'Tests-ObjectsAsMethods'! SystemOrganization addCategory: #'Tests-PrimCallController'! SystemOrganization addCategory: #'Tests-Release'! SystemOrganization addCategory: #'Tests-System-Applications'! SystemOrganization addCategory: #'Tests-System-Digital Signatures'! SystemOrganization addCategory: #'Tests-System-Object Storage'! SystemOrganization addCategory: #'Tests-System-Preferences'! SystemOrganization addCategory: #'Tests-System-Support'! SystemOrganization addCategory: #'Tests-Utilities'! SystemOrganization addCategory: #'Tests-VM'! SystemOrganization addCategory: #'Tests-MonticelloMocks'! + SystemOrganization addCategory: #'Tests-Sound'! Item was added: + TestCase subclass: #SoundTest + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Tests-Sound'! Item was added: + ----- Method: SoundTest>>testStoreSamples (in category 'tests') ----- + testStoreSamples + + | sound fileName arrayBytes fileBytes | + sound := PluckedSound default. + fileName := 'tempPluck.wav'. + + "Write to file" + fileBytes := [ + sound storeWAVOnFileNamed: fileName. + FileStream oldFileNamed: fileName do: [:stream | + stream binary contents]] ensure: [ + FileDirectory deleteFilePath: fileName]. + + "Write to array" + arrayBytes := ByteArray streamContents: [:stream | + sound storeWAVSamplesOn: stream]. + + "Compare" + self assert: fileBytes equals: arrayBytes.! From commits at source.squeak.org Tue Sep 1 02:01:27 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 1 Sep 2020 02:01:27 0000 Subject: [squeak-dev] The Trunk: Sound-ct.71.mcz Message-ID: David T. Lewis uploaded a new version of Sound to project The Trunk: http://source.squeak.org/trunk/Sound-ct.71.mcz ==================== Summary ==================== Name: Sound-ct.71 Author: ct Time: 1 September 2020, 2:07:15.07999 am UUID: 0d91a1bf-41cb-834c-ab0c-fa2ad832e408 Ancestors: Sound-nice.69 Fixes wave sound streaming on non-filestream objects. The endianness was inverted because #int16: already uses Big Endian. This did not sound well - listen yourself in an unpatched image: :-) array := ByteArray streamContents: [:stream | PluckedSound bachFugue storeWAVSamplesOn: stream]. (FileStream fileNamed: 'bachFugue.wav') binary in: [:stream | [array do: [:ea | stream nextPut: ea]] ensure: [stream close]]. (SampledSound fromWaveFileNamed: 'bachFugue.wav') play. Please review in detail as this is one of my first contacts to the Sound system! =============== Diff against Sound-nice.69 =============== Item was changed: ----- Method: AbstractSound>>storeSampleCount:bigEndian:on: (in category 'file i/o') ----- storeSampleCount: samplesToStore bigEndian: bigEndianFlag on: aBinaryStream "Store my samples on the given stream at the current SoundPlayer sampling rate. If bigFlag is true, then each 16-bit sample is stored most-significant byte first (AIFF files), otherwise it is stored least-significant byte first (WAV files). If self isStereo is true, both channels are stored, creating a stereo file. Otherwise, only the left channel is stored, creating a mono file." + | bufSize stereoBuffer reverseBytes streamDirect | - | bufSize stereoBuffer reverseBytes | self reset. bufSize := (2 * self samplingRate rounded) min: samplesToStore. "two second buffer" stereoBuffer := SoundBuffer newStereoSampleCount: bufSize. + streamDirect := aBinaryStream isKindOf: StandardFileStream. + reverseBytes := (bigEndianFlag xor: Smalltalk isBigEndian) xor: streamDirect not. - reverseBytes := bigEndianFlag ~= (Smalltalk isBigEndian). 'Storing audio...' displayProgressFrom: 0 to: samplesToStore during: [:bar | | remaining out | remaining := samplesToStore. [remaining > 0] whileTrue: [ bar value: samplesToStore - remaining. stereoBuffer primFill: 0. "clear the buffer" self playSampleCount: (bufSize min: remaining) into: stereoBuffer startingAt: 1. out := self isStereo ifTrue: [stereoBuffer] ifFalse: [stereoBuffer extractLeftChannel]. reverseBytes ifTrue: [out reverseEndianness]. + streamDirect - (aBinaryStream isKindOf: StandardFileStream) ifTrue: [ "optimization for files: write sound buffer directly to file" aBinaryStream next: (out size // 2) putAll: out startingAt: 1] "size in words" ifFalse: [ "for non-file streams:" 1 to: out monoSampleCount do: [:i | aBinaryStream int16: (out at: i)]]. + remaining := remaining - bufSize]].! - remaining := remaining - bufSize]]. - ! From lewis at mail.msen.com Tue Sep 1 02:13:16 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 31 Aug 2020 22:13:16 -0400 Subject: [squeak-dev] The Trunk: Sound-ct.71.mcz In-Reply-To: References: Message-ID: <20200901021316.GA37550@shell.msen.com> Thanks Christoph, good catch and thanks for the fix. I moved this and the supporting test immediately to trunk because it is clearly a worthwhile change. All - Christoph requests detailed review, please do so and make any updates if appropriate. I'm really happy to see long-overlooked details like this being addressed :-) Dave On Tue, Sep 01, 2020 at 02:01:27AM +0000, commits at source.squeak.org wrote: > David T. Lewis uploaded a new version of Sound to project The Trunk: > http://source.squeak.org/trunk/Sound-ct.71.mcz > > ==================== Summary ==================== > > Name: Sound-ct.71 > Author: ct > Time: 1 September 2020, 2:07:15.07999 am > UUID: 0d91a1bf-41cb-834c-ab0c-fa2ad832e408 > Ancestors: Sound-nice.69 > > Fixes wave sound streaming on non-filestream objects. The endianness was inverted because #int16: already uses Big Endian. This did not sound well - listen yourself in an unpatched image: :-) > > array := ByteArray streamContents: [:stream | > PluckedSound bachFugue storeWAVSamplesOn: stream]. > (FileStream fileNamed: 'bachFugue.wav') binary in: [:stream | > [array do: [:ea | stream nextPut: ea]] > ensure: [stream close]]. > (SampledSound fromWaveFileNamed: 'bachFugue.wav') play. > > Please review in detail as this is one of my first contacts to the Sound system! > > =============== Diff against Sound-nice.69 =============== > > Item was changed: > ----- Method: AbstractSound>>storeSampleCount:bigEndian:on: (in category 'file i/o') ----- > storeSampleCount: samplesToStore bigEndian: bigEndianFlag on: aBinaryStream > "Store my samples on the given stream at the current SoundPlayer sampling rate. If bigFlag is true, then each 16-bit sample is stored most-significant byte first (AIFF files), otherwise it is stored least-significant byte first (WAV files). If self isStereo is true, both channels are stored, creating a stereo file. Otherwise, only the left channel is stored, creating a mono file." > > + | bufSize stereoBuffer reverseBytes streamDirect | > - | bufSize stereoBuffer reverseBytes | > self reset. > bufSize := (2 * self samplingRate rounded) min: samplesToStore. "two second buffer" > stereoBuffer := SoundBuffer newStereoSampleCount: bufSize. > + streamDirect := aBinaryStream isKindOf: StandardFileStream. > + reverseBytes := (bigEndianFlag xor: Smalltalk isBigEndian) xor: streamDirect not. > - reverseBytes := bigEndianFlag ~= (Smalltalk isBigEndian). > > 'Storing audio...' > displayProgressFrom: 0 to: samplesToStore during: [:bar | | remaining out | > remaining := samplesToStore. > [remaining > 0] whileTrue: [ > bar value: samplesToStore - remaining. > stereoBuffer primFill: 0. "clear the buffer" > self playSampleCount: (bufSize min: remaining) into: stereoBuffer startingAt: 1. > out := self isStereo > ifTrue: [stereoBuffer] > ifFalse: [stereoBuffer extractLeftChannel]. > reverseBytes ifTrue: [out reverseEndianness]. > + streamDirect > - (aBinaryStream isKindOf: StandardFileStream) > ifTrue: [ "optimization for files: write sound buffer directly to file" > aBinaryStream next: (out size // 2) putAll: out startingAt: 1] "size in words" > ifFalse: [ "for non-file streams:" > 1 to: out monoSampleCount do: [:i | aBinaryStream int16: (out at: i)]]. > + remaining := remaining - bufSize]].! > - remaining := remaining - bufSize]]. > - ! > > From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 1 06:03:09 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 1 Sep 2020 06:03:09 +0000 Subject: [squeak-dev] The Trunk: Sound-ct.71.mcz In-Reply-To: <20200901021316.GA37550@shell.msen.com> References: , <20200901021316.GA37550@shell.msen.com> Message-ID: <061af1fd17f944f186ba2c946e95eb1e@student.hpi.uni-potsdam.de> Hi David, hi all, thanks for merging! Do you know what Smalltalk endianness is good for? Is this maybe only relevant for FileStreams at all? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Dienstag, 1. September 2020 04:13:16 An: squeak-dev at lists.squeakfoundation.org Cc: packages at lists.squeakfoundation.org Betreff: Re: [squeak-dev] The Trunk: Sound-ct.71.mcz Thanks Christoph, good catch and thanks for the fix. I moved this and the supporting test immediately to trunk because it is clearly a worthwhile change. All - Christoph requests detailed review, please do so and make any updates if appropriate. I'm really happy to see long-overlooked details like this being addressed :-) Dave On Tue, Sep 01, 2020 at 02:01:27AM +0000, commits at source.squeak.org wrote: > David T. Lewis uploaded a new version of Sound to project The Trunk: > http://source.squeak.org/trunk/Sound-ct.71.mcz > > ==================== Summary ==================== > > Name: Sound-ct.71 > Author: ct > Time: 1 September 2020, 2:07:15.07999 am > UUID: 0d91a1bf-41cb-834c-ab0c-fa2ad832e408 > Ancestors: Sound-nice.69 > > Fixes wave sound streaming on non-filestream objects. The endianness was inverted because #int16: already uses Big Endian. This did not sound well - listen yourself in an unpatched image: :-) > > array := ByteArray streamContents: [:stream | > PluckedSound bachFugue storeWAVSamplesOn: stream]. > (FileStream fileNamed: 'bachFugue.wav') binary in: [:stream | > [array do: [:ea | stream nextPut: ea]] > ensure: [stream close]]. > (SampledSound fromWaveFileNamed: 'bachFugue.wav') play. > > Please review in detail as this is one of my first contacts to the Sound system! > > =============== Diff against Sound-nice.69 =============== > > Item was changed: > ----- Method: AbstractSound>>storeSampleCount:bigEndian:on: (in category 'file i/o') ----- > storeSampleCount: samplesToStore bigEndian: bigEndianFlag on: aBinaryStream > "Store my samples on the given stream at the current SoundPlayer sampling rate. If bigFlag is true, then each 16-bit sample is stored most-significant byte first (AIFF files), otherwise it is stored least-significant byte first (WAV files). If self isStereo is true, both channels are stored, creating a stereo file. Otherwise, only the left channel is stored, creating a mono file." > > + | bufSize stereoBuffer reverseBytes streamDirect | > - | bufSize stereoBuffer reverseBytes | > self reset. > bufSize := (2 * self samplingRate rounded) min: samplesToStore. "two second buffer" > stereoBuffer := SoundBuffer newStereoSampleCount: bufSize. > + streamDirect := aBinaryStream isKindOf: StandardFileStream. > + reverseBytes := (bigEndianFlag xor: Smalltalk isBigEndian) xor: streamDirect not. > - reverseBytes := bigEndianFlag ~= (Smalltalk isBigEndian). > > 'Storing audio...' > displayProgressFrom: 0 to: samplesToStore during: [:bar | | remaining out | > remaining := samplesToStore. > [remaining > 0] whileTrue: [ > bar value: samplesToStore - remaining. > stereoBuffer primFill: 0. "clear the buffer" > self playSampleCount: (bufSize min: remaining) into: stereoBuffer startingAt: 1. > out := self isStereo > ifTrue: [stereoBuffer] > ifFalse: [stereoBuffer extractLeftChannel]. > reverseBytes ifTrue: [out reverseEndianness]. > + streamDirect > - (aBinaryStream isKindOf: StandardFileStream) > ifTrue: [ "optimization for files: write sound buffer directly to file" > aBinaryStream next: (out size // 2) putAll: out startingAt: 1] "size in words" > ifFalse: [ "for non-file streams:" > 1 to: out monoSampleCount do: [:i | aBinaryStream int16: (out at: i)]]. > + remaining := remaining - bufSize]].! > - remaining := remaining - bufSize]]. > - ! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tonyg at leastfixedpoint.com Tue Sep 1 09:42:53 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Tue, 1 Sep 2020 11:42:53 +0200 Subject: [squeak-dev] TTCFont >> flushCachedValues In-Reply-To: References: <93602a53-09f8-b358-4436-b51b90d7d650@leastfixedpoint.com> Message-ID: <416e948e-5f8c-2893-22dc-8a047d3abe7d@leastfixedpoint.com> On 8/28/20 10:42 AM, Marcel Taeumel wrote: > It is possible to see the effects of your changes without closing all > your widgets. Just use #apply or #applyAfter:. Aha! #apply is what I was looking for without realising it :-) Thanks for the tip. Regards,   Tony -------------- next part -------------- An HTML attachment was scrubbed... URL: From tonyg at leastfixedpoint.com Tue Sep 1 09:44:09 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Tue, 1 Sep 2020 11:44:09 +0200 Subject: [squeak-dev] Thank Eliot Miranda! In-Reply-To: References: Message-ID: On 8/28/20 4:17 PM, ken.dickey at whidbey.com wrote: > That was 99.997% Eliot.  I just got the vm-display-fbdev up with > libevdev on Raspi3/4 & LePotato. > Great thanks to Eliot !!! Well, hear hear, many thanks indeed to Eliot for this and all the other great work he does (but thanks to you also for your part) :-) From tonyg at leastfixedpoint.com Tue Sep 1 09:46:53 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Tue, 1 Sep 2020 11:46:53 +0200 Subject: [squeak-dev] ARMv8 JIT (was Re: Thank Eliot Miranda!) In-Reply-To: References: Message-ID: On 8/28/20 7:08 PM, Eliot Miranda wrote: > Once we have the JIT fully working Oh, wow, is there no JIT on ARMv8? I can report that it's quite usable on my phone, so far at least. I'm still working on input support so I haven't had much of a chance to *use* it on the phone yet, but the display repainting, device detection, DPI-adjusting code I've written so far runs fast enough it hadn't occurred to me to wonder about JIT or not. Tony -------------- next part -------------- An HTML attachment was scrubbed... URL: From tonyg at leastfixedpoint.com Tue Sep 1 09:49:02 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Tue, 1 Sep 2020 11:49:02 +0200 Subject: [squeak-dev] Squeak on a PostmarketOS cellphone In-Reply-To: <06ac175a-acb9-7045-a77c-a46d65ca0936@gmail.com> References: <06ac175a-acb9-7045-a77c-a46d65ca0936@gmail.com> Message-ID: <26f48911-7d16-82eb-9faf-5b5a44704013@leastfixedpoint.com> On 8/30/20 2:07 AM, Douglas Brebner wrote: > Oh wow, I've been looking at PostmarketOS phones with Plasma Mobile > recently, but a Smalltalk based phone would be amazing. If you'd like to follow along, see https://eighty-twenty.org/2020/08/25/postmarketos-smalltalk and followup posts. And feel free to email me directly if you'd like to *actually* try out the software or help with development. It's not on squeaksource yet but it will be soonish. (I'll not spam the list with natter about the work unless there's enough interest from others!) Cheers,   Tony From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 1 09:56:10 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 1 Sep 2020 09:56:10 +0000 Subject: [squeak-dev] Metacello broken again Message-ID: <2c3359358004407498a8d3331c381203@student.hpi.uni-potsdam.de> Hi all, I did not follow the recent Metacello discussions again, but just now in my fresh Trunk image, Metacello new failed again multiple times saying 'retry with alternate repository failed: ''MessageNotUnderstood: UndefinedObject>>contentStreamFromEncoding:'''. Is this a known problem? How can I solve this without waiting for any server to be online again? After this command failed, you cannot even retry it, because #Metacello is no longer a registered class in the image. This is rather inconvenient ... Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 1 10:14:17 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 1 Sep 2020 10:14:17 +0000 Subject: [squeak-dev] Issues with SmartRefStream Message-ID: <77a76448f0fb4459bfbf592570820ce5@student.hpi.uni-potsdam.de> Hi all, I was just trying to file out a complex Object graph via #saveOnFile and found two problems with the SmartRefStream: First, uniclass instances cannot be written to the stream. In SmartRefStream >> #instVarInfo:, I get an #errorKeyNotFound because the uniclass name is looked up into the SmalltalkImage globals. Minimum example to reproduce: Object newSubclass new saveOnFile. Second, blocks met during the file out process cannot be filed in again. Just try [] saveOnFile to reproduce this. Filein raises newMethodViaNewError. Maybe CompiledBlock should overwrite some streaming method for creating an appropriate disk representation? Just wanted to report these issues. Maybe someone is more experienced with the SmartRefStream architecture and would like to fix these bugs. :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 1 10:16:20 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 1 Sep 2020 10:16:20 +0000 Subject: [squeak-dev] The Inbox: 60Deprecated-ct.80.mcz In-Reply-To: References: , Message-ID: <40d1deaa1ffd4feb824a4b5aa1d76dc5@student.hpi.uni-potsdam.de> Hi all, any other opinions on this question? If not, shall I proceed to upload the changeset as separate inbox versions or can someone with Trunk permissions do this with one click? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Freitag, 21. August 2020 21:29:10 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: 60Deprecated-ct.80.mcz On Thu, Aug 20, 2020 at 7:07 AM Levente Uzonyi > wrote: Hi Christoph, +1 from me to deprecate it. Even if the final decision will be to not deprecate/remove those methods, using #withIndex*: instead of #*WithIndex: in Trunk won't hurt. So, feel free to push those changes, as those should be in the Trunk IMO. +1 Levente On Thu, 20 Aug 2020, Thiede, Christoph wrote: > > I have stopped to commit the patches to all affected packages, which was a terrible idea as I recognize now. In case we can agree on deprecating #doWithIndex:, I am attaching the corresponding changeset. > > > FWIW, here is the snippet I used to rename all senders of the deprecated selectors: > > oldSelector := #collectWithIndex:. > newSelector := #withIndexCollect:. > > methods := self systemNavigation allCallsOn: oldSelector. > methods > do: [:method | > method actualClass compile: (method sourceCode > copyReplaceTokens: oldSelector > with: newSelector) ] > displayingProgress: ('Replacing {1} with {2}' format: {oldSelector. newSelector}) > [FORM] > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev > im Auftrag von Taeumel, Marcel > Gesendet: Donnerstag, 20. August 2020 14:55:14 > An: squeak-dev > Betreff: Re: [squeak-dev] The Inbox: 60Deprecated-ct.80.mcz > Thank you for this explanation. Would have made a great commit message ;-) > Also thanks for the highlighted example. Never thought of it that way. Makes sense. > > Best, > Marcel > > Am 20.08.2020 14:52:33 schrieb Thiede, Christoph >: > > Sorry for the overhasty commit storm. From what the old method comment in SequenceableCollection >> #doWithIndex: stated, I believed that the formal deprecation of this selector was already decided but never > realized: > > > "Use the new version with consistent naming" > > > In my image, #doWithIndex: has 89 senders opposed to #withIndexDo: which has 156 senders. #doWithIndex: could be a confusing name because usually, the latest phrase before the argument specifies the role or type > of the argument, but in this case, the argument is not an index, but a block. > > > Marcel said #withIndexDo: could be considered as confusing either because its name would not match the arguments in the expected block. However, I think it still matches the order because the element is already > represented by the receiver of the MessageSend: > > > classes withIndexDo: [:class :index | ...] > > > Open to hear your opinions! However we decide, I think it would improve the overall Smalltalk experience to have a single preferred name for protocols like this one to clean up the things. > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev > im Auftrag von Taeumel, Marcel > Gesendet: Donnerstag, 20. August 2020 14:39:58 > An: squeak-dev > Betreff: Re: [squeak-dev] The Inbox: 60Deprecated-ct.80.mcz > ... just why? > Best, > Marcel > > Am 20.08.2020 14:33:41 schrieb commits at source.squeak.org >: > > A new version of 60Deprecated was added to project The Inbox: > http://source.squeak.org/inbox/60Deprecated-ct.80.mcz > > ==================== Summary ==================== > > Name: 60Deprecated-ct.80 > Author: ct > Time: 20 August 2020, 2:33:32.63864 pm > UUID: 1295269c-62ee-5c45-9315-e66ff5eef57a > Ancestors: 60Deprecated-mt.79 > > Finnaly deprecate #doWithIndex: and #collectWithIndex:. Other packages will be patched right now. > > =============== Diff against 60Deprecated-mt.79 =============== > > Item was added: > + ----- Method: HashedCollection>>doWithIndex: (in category '*60Deprecated-enumerating') ----- > + doWithIndex: elementAndIndexBlock > + > + self deprecated: 'Use #withIndexDo: instead'. > + ^ self withIndexDo: elementAndIndexBlock! > > Item was added: > + ----- Method: SequenceableCollection>>collectWithIndex: (in category '*60Deprecated-enumerating') ----- > + collectWithIndex: elementAndIndexBlock > + > + self deprecated: 'Use #withIndexCollect: instead'. > + ^ self withIndexCollect: elementAndIndexBlock! > > Item was added: > + ----- Method: SequenceableCollection>>doWithIndex: (in category '*60Deprecated-enumerating') ----- > + doWithIndex: elementAndIndexBlock > + > + self deprecated: 'Use #withIndexDo: instead'. > + ^ self withIndexDo: elementAndIndexBlock! > > Item was changed: > ----- Method: StandardFileMenu>>menuSelectionsArray: (in category 'menu building') ----- > menuSelectionsArray: aDirectory > "Answer a menu selections object corresponding to aDirectory. The object is an array corresponding to each item, each element itself constituting a two-element array, the first element of which contains a > selector to operate on and the second element of which contains the parameters for that selector." > > |dirSize| > dirSize := aDirectory pathParts size. > ^Array streamContents: [:s | > canTypeFileName ifTrue: > [s nextPut: (StandardFileMenuResult > directory: aDirectory > name: nil)]. > s nextPut: (StandardFileMenuResult > directory: (FileDirectory root) > name: ''). > + aDirectory pathParts withIndexDo: > - aDirectory pathParts doWithIndex: > [:d :i | s nextPut: (StandardFileMenuResult > directory: (self > advance: dirSize - i > containingDirectoriesFrom: aDirectory) > name: '')]. > aDirectory directoryNames do: > [:dn | s nextPut: (StandardFileMenuResult > directory: (FileDirectory on: (aDirectory fullNameFor: dn)) > name: '')]. > aDirectory fileNames do: > [:fn | pattern do: [:pat | (pat match: fn) ifTrue: [ > s nextPut: (StandardFileMenuResult > directory: aDirectory > name: fn)]]]]! > > Item was changed: > ----- Method: StandardFileMenu>>pathPartsString: (in category 'menu building') ----- > pathPartsString: aDirectory > "Answer a string concatenating the path parts strings in aDirectory, each string followed by a cr." > > ^String streamContents: > [:s | > s nextPutAll: '[]'; cr. > + aDirectory pathParts asArray withIndexDo: > - aDirectory pathParts asArray doWithIndex: > [:part :i | > s next: i put: $ . > s nextPutAll: part withBlanksTrimmed; cr]]! > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Tom.Beckmann at student.hpi.uni-potsdam.de Tue Sep 1 10:23:39 2020 From: Tom.Beckmann at student.hpi.uni-potsdam.de (Beckmann, Tom) Date: Tue, 1 Sep 2020 10:23:39 +0000 Subject: [squeak-dev] Metacello broken again In-Reply-To: <2c3359358004407498a8d3331c381203@student.hpi.uni-potsdam.de> References: <2c3359358004407498a8d3331c381203@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, the smalltalkhub servers recently had some reconfigurations that are potentially the culprit [1] (just heard back, they are already on it :)). I attached a .sar for bootstrapping based on this issue's [2] suggestion for a new bootstrapping process that should allow you to get a working metacello in the meantime. Best, Tom [1] http://forum.world.st/VOMongoConnectionError-when-dowloading-mcz-from-smalltalkhub-com-tp5120870p5121135.html [2] https://github.com/Metacello/metacello/issues/524 ________________________________________ From: Squeak-dev on behalf of Thiede, Christoph Sent: Tuesday, September 1, 2020 11:56:10 AM To: Squeak Dev Subject: [squeak-dev] Metacello broken again Hi all, I did not follow the recent Metacello discussions again, but just now in my fresh Trunk image, Metacello new failed again multiple times saying 'retry with alternate repository failed: ''MessageNotUnderstood: UndefinedObject>>contentStreamFromEncoding:'''. Is this a known problem? How can I solve this without waiting for any server to be online again? After this command failed, you cannot even retry it, because #Metacello is no longer a registered class in the image. This is rather inconvenient ... Best, Christoph -------------- next part -------------- A non-text attachment was scrubbed... Name: metacello.sar Type: application/octet-stream Size: 539430 bytes Desc: metacello.sar URL: From Tom.Beckmann at student.hpi.uni-potsdam.de Tue Sep 1 10:32:05 2020 From: Tom.Beckmann at student.hpi.uni-potsdam.de (Beckmann, Tom) Date: Tue, 1 Sep 2020 10:32:05 +0000 Subject: [squeak-dev] Metacello broken again In-Reply-To: References: <2c3359358004407498a8d3331c381203@student.hpi.uni-potsdam.de>, Message-ID: ...and now the servers are fixed, too :) http://forum.world.st/VOMongoConnectionError-when-dowloading-mcz-from-smalltalkhub-com-tp5120870p5121140.html ________________________________________ From: Squeak-dev on behalf of Beckmann, Tom Sent: Tuesday, September 1, 2020 12:23:39 PM To: Squeak Dev Subject: Re: [squeak-dev] Metacello broken again Hi Christoph, the smalltalkhub servers recently had some reconfigurations that are potentially the culprit [1] (just heard back, they are already on it :)). I attached a .sar for bootstrapping based on this issue's [2] suggestion for a new bootstrapping process that should allow you to get a working metacello in the meantime. Best, Tom [1] http://forum.world.st/VOMongoConnectionError-when-dowloading-mcz-from-smalltalkhub-com-tp5120870p5121135.html [2] https://github.com/Metacello/metacello/issues/524 ________________________________________ From: Squeak-dev on behalf of Thiede, Christoph Sent: Tuesday, September 1, 2020 11:56:10 AM To: Squeak Dev Subject: [squeak-dev] Metacello broken again Hi all, I did not follow the recent Metacello discussions again, but just now in my fresh Trunk image, Metacello new failed again multiple times saying 'retry with alternate repository failed: ''MessageNotUnderstood: UndefinedObject>>contentStreamFromEncoding:'''. Is this a known problem? How can I solve this without waiting for any server to be online again? After this command failed, you cannot even retry it, because #Metacello is no longer a registered class in the image. This is rather inconvenient ... Best, Christoph From tonyg at leastfixedpoint.com Tue Sep 1 13:50:43 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Tue, 1 Sep 2020 15:50:43 +0200 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior Message-ID: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> It occurs to me that to get better power efficiency, Squeak may need to learn how to go to sleep more often. At present it's using about a quarter of a core on my phone when I'm not doing anything to it. JIT could help that, I guess; but more generally, it'd be nice just not to be awake when there's nothing pressing to do... Tony From marcel.taeumel at hpi.de Tue Sep 1 13:54:58 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 1 Sep 2020 15:54:58 +0200 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> References: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> Message-ID: Hi Tony. Can you improve the situation by tuning: ProcessorScheduler class >> #idleProcess WorldState >> #interCyclePause: Best, Marcel Am 01.09.2020 15:50:53 schrieb Tony Garnock-Jones : It occurs to me that to get better power efficiency, Squeak may need to learn how to go to sleep more often. At present it's using about a quarter of a core on my phone when I'm not doing anything to it. JIT could help that, I guess; but more generally, it'd be nice just not to be awake when there's nothing pressing to do... Tony -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Tue Sep 1 14:18:53 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Tue, 1 Sep 2020 16:18:53 +0200 (CEST) Subject: [squeak-dev] The Inbox: WebClient-Core-ct.124.mcz In-Reply-To: References: Message-ID: The change looks good to me. It would be better if we had a test case covering this method. Levente On Mon, 31 Aug 2020, Chris Muller wrote: > I'm not qualified to review the change but good dig! :) > > On Sun, Aug 30, 2020 at 6:54 PM wrote: >> >> Christoph Thiede uploaded a new version of WebClient-Core to project The Inbox: >> http://source.squeak.org/inbox/WebClient-Core-ct.124.mcz >> >> ==================== Summary ==================== >> >> Name: WebClient-Core-ct.124 >> Author: ct >> Time: 31 August 2020, 1:54:11.685896 am >> UUID: b0d7790c-c195-6e4d-ad0f-fce8cf8b3b00 >> Ancestors: WebClient-Core-ul.123 >> >> Fixes a syntax error in multipart/form-data encoding >> >> Phew! It costed me a few hours to track some higher-level application bug down to this low level code ... >> >> =============== Diff against WebClient-Core-ul.123 =============== >> >> Item was changed: >> ----- Method: WebUtils class>>encodeMultipartForm:boundary: (in category 'decoding') ----- >> encodeMultipartForm: fieldMap boundary: boundary >> "Encodes the fieldMap as multipart/form-data. >> >> The fieldMap may contain MIMEDocument instances to indicate the presence >> of a file to upload to the server. If the MIMEDocument is present, its >> content type and file name will be used for the upload. >> >> The fieldMap can be EITHER an array of associations OR a Dictionary of >> key value pairs (the former is useful for providing multiple fields and/or >> specifying the order of fields)." >> >> ^String streamContents:[:stream| >> (fieldMap as: Dictionary) keysAndValuesDo:[:fieldName :fieldValue | | fieldContent | >> "Write multipart boundary and common headers" >> stream nextPutAll: '--', boundary; crlf. >> stream nextPutAll: 'Content-Disposition: form-data; name="', fieldName, '"'. >> "Figure out if this is a file upload" >> (fieldValue isKindOf: MIMEDocument) ifTrue:[ >> + stream nextPutAll: '; filename="', fieldValue url pathForFile, '"'; crlf. >> - stream nextPutAll: ' filename="', fieldValue url pathForFile, '"'; crlf. >> stream nextPutAll: 'Content-Type: ', fieldValue contentType. >> fieldContent := (fieldValue content ifNil:[ >> (FileStream readOnlyFileNamed: fieldValue url pathForFile) contentsOfEntireFile. >> ]) asString. >> ] ifFalse: [fieldContent := fieldValue]. >> stream crlf; crlf. >> stream nextPutAll: fieldContent asString. >> stream crlf. >> ]. >> stream nextPutAll: '--', boundary, '--', String crlf. >> ]. >> ! >> >> From builds at travis-ci.org Tue Sep 1 15:07:35 2020 From: builds at travis-ci.org (Travis CI) Date: Tue, 01 Sep 2020 15:07:35 +0000 Subject: [squeak-dev] [CRON] Passed: squeak-smalltalk/squeak-app#1812 (squeak-trunk - 25ebaf1) In-Reply-To: Message-ID: <5f4e62627f969_13fbb785b8290239084@travis-tasks-544d989cdf-c6hzb.mail> Build Update for squeak-smalltalk/squeak-app ------------------------------------- Build: #1812 Status: Passed Duration: 19 mins and 55 secs Commit: 25ebaf1 (squeak-trunk) Author: Marcel Taeumel Message: Hi all! The project "squeak-app" is part of the continuous build infrastructure (short: CI) for Squeak's bundles, which you can download at https://files.squeak.org/, followed by the version tag you are looking for. All bundles are updated on a regular basis, which is daily for Trunk (i.e. the current alpha version) and monthly for release versions. Note that there won't be new bundles if Squeak's build number, which reflects in-image code updates, did not change between CI jobs. -- The Squeak Oversight Board [ci skip] View the changeset: https://github.com/squeak-smalltalk/squeak-app/compare/fb55517f583fe544f9225413a23fe82a680200c2...25ebaf18249322227da1f7c767384cb35082fab7 View the full build log and details: https://travis-ci.org/github/squeak-smalltalk/squeak-app/builds/723105553?utm_medium=notification&utm_source=email -- You can unsubscribe from build emails from the squeak-smalltalk/squeak-app repository going to https://travis-ci.org/account/preferences/unsubscribe?repository=8901856&utm_medium=notification&utm_source=email. Or unsubscribe from *all* email updating your settings at https://travis-ci.org/account/preferences/unsubscribe?utm_medium=notification&utm_source=email. Or configure specific recipients for build notifications in your .travis.yml file. See https://docs.travis-ci.com/user/notifications. -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 1 15:29:11 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 1 Sep 2020 08:29:11 -0700 Subject: [squeak-dev] Issues with SmartRefStream In-Reply-To: <77a76448f0fb4459bfbf592570820ce5@student.hpi.uni-potsdam.de> References: <77a76448f0fb4459bfbf592570820ce5@student.hpi.uni-potsdam.de> Message-ID: <2C4F6647-A44F-40C6-8F8A-14A5CB2F381B@gmail.com> Hi Christoph, > On Sep 1, 2020, at 3:14 AM, Thiede, Christoph wrote: > >  > Hi all, > > > > I was just trying to file out a complex Object graph via #saveOnFile and found two problems with the SmartRefStream: > > > > First, uniclass instances cannot be written to the stream. In SmartRefStream >> #instVarInfo:, I get an #errorKeyNotFound because the uniclass name is looked up into the SmalltalkImage globals. Minimum example to reproduce: > > > > Object newSubclass new saveOnFile. > > > > Second, blocks met during the file out process cannot be filed in again. Just try [] saveOnFile to reproduce this. Filein raises newMethodViaNewError. Maybe CompiledBlock should overwrite some streaming method for creating an appropriate disk representation? > My guess is that class side code in CompiledMethod should be moved up to CompiledCode, the common superclass of CompiledMethod and CompiledBlock. > Just wanted to report these issues. Maybe someone is more experienced with the SmartRefStream architecture and would like to fix these bugs. :-) > > Best, > Christoph > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 1 15:36:01 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 1 Sep 2020 08:36:01 -0700 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> References: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> Message-ID: Hi Tony, > On Sep 1, 2020, at 6:50 AM, Tony Garnock-Jones wrote: > > It occurs to me that to get better power efficiency, Squeak may need to > learn how to go to sleep more often. At present it's using about a > quarter of a core on my phone when I'm not doing anything to it. > > JIT could help that, I guess; but more generally, it'd be nice just not > to be awake when there's nothing pressing to do... The solution is a modified event architecture and jettisoning relinquishProcessorForMilliseconds:. Search the archives (squeak-dev & vm-dev) for “event driven vm”. This is work that should have been done a long time ago (I did this for the VisualWorks vm back at the turn of the century or thereabouts). But it isn’t going to get done unless someone steps up. My input queue is full to overflowing. > Tony _,,,^..^,,,_ (phone) From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 1 15:41:51 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 1 Sep 2020 15:41:51 +0000 Subject: [squeak-dev] The Inbox: WebClient-Core-ct.124.mcz In-Reply-To: References: , Message-ID: Hi Levente, thanks for the feedback. We already have WebClientServerTest >> #testMultipartFiles but it already passed before this patch because the decoding implementation in WebUtils (#decodeMultipartForm:boundary:do:) is very robust: Instead of parsing a specific header syntax, it just searches for patterns matching #=, just see yourself ... I did not want to touch this because I thought robust client/server implementations are a good thing in general (at least every web browser is able to display malformed HTML pages), but this hinders us from writing simple integration tests. Would you recommend to sharpen the decoding implementation or rather to write separate unit tests? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Dienstag, 1. September 2020 16:18:53 An: ma.chris.m at gmail.com; The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: WebClient-Core-ct.124.mcz The change looks good to me. It would be better if we had a test case covering this method. Levente On Mon, 31 Aug 2020, Chris Muller wrote: > I'm not qualified to review the change but good dig! :) > > On Sun, Aug 30, 2020 at 6:54 PM wrote: >> >> Christoph Thiede uploaded a new version of WebClient-Core to project The Inbox: >> http://source.squeak.org/inbox/WebClient-Core-ct.124.mcz >> >> ==================== Summary ==================== >> >> Name: WebClient-Core-ct.124 >> Author: ct >> Time: 31 August 2020, 1:54:11.685896 am >> UUID: b0d7790c-c195-6e4d-ad0f-fce8cf8b3b00 >> Ancestors: WebClient-Core-ul.123 >> >> Fixes a syntax error in multipart/form-data encoding >> >> Phew! It costed me a few hours to track some higher-level application bug down to this low level code ... >> >> =============== Diff against WebClient-Core-ul.123 =============== >> >> Item was changed: >> ----- Method: WebUtils class>>encodeMultipartForm:boundary: (in category 'decoding') ----- >> encodeMultipartForm: fieldMap boundary: boundary >> "Encodes the fieldMap as multipart/form-data. >> >> The fieldMap may contain MIMEDocument instances to indicate the presence >> of a file to upload to the server. If the MIMEDocument is present, its >> content type and file name will be used for the upload. >> >> The fieldMap can be EITHER an array of associations OR a Dictionary of >> key value pairs (the former is useful for providing multiple fields and/or >> specifying the order of fields)." >> >> ^String streamContents:[:stream| >> (fieldMap as: Dictionary) keysAndValuesDo:[:fieldName :fieldValue | | fieldContent | >> "Write multipart boundary and common headers" >> stream nextPutAll: '--', boundary; crlf. >> stream nextPutAll: 'Content-Disposition: form-data; name="', fieldName, '"'. >> "Figure out if this is a file upload" >> (fieldValue isKindOf: MIMEDocument) ifTrue:[ >> + stream nextPutAll: '; filename="', fieldValue url pathForFile, '"'; crlf. >> - stream nextPutAll: ' filename="', fieldValue url pathForFile, '"'; crlf. >> stream nextPutAll: 'Content-Type: ', fieldValue contentType. >> fieldContent := (fieldValue content ifNil:[ >> (FileStream readOnlyFileNamed: fieldValue url pathForFile) contentsOfEntireFile. >> ]) asString. >> ] ifFalse: [fieldContent := fieldValue]. >> stream crlf; crlf. >> stream nextPutAll: fieldContent asString. >> stream crlf. >> ]. >> stream nextPutAll: '--', boundary, '--', String crlf. >> ]. >> ! >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 1 15:42:29 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 1 Sep 2020 08:42:29 -0700 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: References: Message-ID: <013D89E7-A61E-4327-8CB1-F4FD5063579C@gmail.com> > On Sep 1, 2020, at 6:55 AM, Marcel Taeumel wrote: > >  > Hi Tony. > > Can you improve the situation by tuning: > > ProcessorScheduler class >> #idleProcess > WorldState >> #interCyclePause: This will always be a hack. The right solution is to have the vm scheduler enter a wait state when there are no runnable processes and to have no background process to needlessly consume cycles. The event architecture needs to allow input events to be one of the conditions (the other being i/o in sockets files et al) that the vm waits on. This is one of the key things that is meant by the “event driven vm”. The other thing that is meant by “event driven vm” is that the vm runs within the GUI event loop rather than the vm calling it. I’m not sure how important this is; input events seem to work just fine with what we have. So the key thing it seems to me is blocking on input (& pending output) when there is nothing to do rather than spinning a background process that calls a primitive that blocks. > > Best, > Marcel >> Am 01.09.2020 15:50:53 schrieb Tony Garnock-Jones : >> >> It occurs to me that to get better power efficiency, Squeak may need to >> learn how to go to sleep more often. At present it's using about a >> quarter of a core on my phone when I'm not doing anything to it. >> >> JIT could help that, I guess; but more generally, it'd be nice just not >> to be awake when there's nothing pressing to do... >> >> Tony >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 1 15:46:47 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 1 Sep 2020 15:46:47 +0000 Subject: [squeak-dev] Issues with SmartRefStream In-Reply-To: <2C4F6647-A44F-40C6-8F8A-14A5CB2F381B@gmail.com> References: <77a76448f0fb4459bfbf592570820ce5@student.hpi.uni-potsdam.de>, <2C4F6647-A44F-40C6-8F8A-14A5CB2F381B@gmail.com> Message-ID: <8e9d7d434739415a8ece2fee2449974b@student.hpi.uni-potsdam.de> Hi Eliot, generalization would have been my first idea, too, but I did not yet found the relevant piece of code ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Dienstag, 1. September 2020 17:29:11 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Issues with SmartRefStream Hi Christoph, On Sep 1, 2020, at 3:14 AM, Thiede, Christoph wrote:  Hi all, I was just trying to file out a complex Object graph via #saveOnFile and found two problems with the SmartRefStream: First, uniclass instances cannot be written to the stream. In SmartRefStream >> #instVarInfo:, I get an #errorKeyNotFound because the uniclass name is looked up into the SmalltalkImage globals. Minimum example to reproduce: Object newSubclass new saveOnFile. Second, blocks met during the file out process cannot be filed in again. Just try [] saveOnFile to reproduce this. Filein raises newMethodViaNewError. Maybe CompiledBlock should overwrite some streaming method for creating an appropriate disk representation? My guess is that class side code in CompiledMethod should be moved up to CompiledCode, the common superclass of CompiledMethod and CompiledBlock. Just wanted to report these issues. Maybe someone is more experienced with the SmartRefStream architecture and would like to fix these bugs. :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 1 15:53:06 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 1 Sep 2020 08:53:06 -0700 Subject: [squeak-dev] Issues with SmartRefStream In-Reply-To: <8e9d7d434739415a8ece2fee2449974b@student.hpi.uni-potsdam.de> References: <77a76448f0fb4459bfbf592570820ce5@student.hpi.uni-potsdam.de> <2C4F6647-A44F-40C6-8F8A-14A5CB2F381B@gmail.com> <8e9d7d434739415a8ece2fee2449974b@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, Hi Marcel (cuz you're in the timestamp) In NativeImageSegment>>rootsIncludingBlock[Method]s are two "anOut class == CompiledMethod" that should probably be anOut isCompiledCode. On Tue, Sep 1, 2020 at 8:46 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, > > > generalization would have been my first idea, too, but I did not yet found > the relevant piece of code ... > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Eliot Miranda > *Gesendet:* Dienstag, 1. September 2020 17:29:11 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] Issues with SmartRefStream > > Hi Christoph, > > On Sep 1, 2020, at 3:14 AM, Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >  > > Hi all, > > > I was just trying to file out a complex Object graph via #saveOnFile and > found two problems with the SmartRefStream: > > > First, uniclass instances cannot be written to the stream. In > SmartRefStream >> #instVarInfo:, I get an #errorKeyNotFound because the > uniclass name is looked up into the SmalltalkImage globals. Minimum example > to reproduce: > > > Object newSubclass new saveOnFile. > > > Second, blocks met during the file out process cannot be filed in again. > Just try [] saveOnFile to reproduce this. Filein raises newMethodViaNewError. > Maybe CompiledBlock should overwrite some streaming method for creating an > appropriate disk representation? > > My guess is that class side code in CompiledMethod should be moved up to > CompiledCode, the common superclass of CompiledMethod and CompiledBlock. > > > Just wanted to report these issues. Maybe someone is more experienced with > the SmartRefStream architecture and would like to fix these bugs. :-) > > Best, > Christoph > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 1 16:28:37 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 1 Sep 2020 16:28:37 +0000 Subject: [squeak-dev] Issues with SmartRefStream In-Reply-To: References: <77a76448f0fb4459bfbf592570820ce5@student.hpi.uni-potsdam.de> <2C4F6647-A44F-40C6-8F8A-14A5CB2F381B@gmail.com> <8e9d7d434739415a8ece2fee2449974b@student.hpi.uni-potsdam.de>, Message-ID: This does not help, unfortunately. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Dienstag, 1. September 2020 17:53:06 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Issues with SmartRefStream Hi Christoph, Hi Marcel (cuz you're in the timestamp) In NativeImageSegment>>rootsIncludingBlock[Method]s are two "anOut class == CompiledMethod" that should probably be anOut isCompiledCode. On Tue, Sep 1, 2020 at 8:46 AM Thiede, Christoph > wrote: Hi Eliot, generalization would have been my first idea, too, but I did not yet found the relevant piece of code ... Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Eliot Miranda > Gesendet: Dienstag, 1. September 2020 17:29:11 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Issues with SmartRefStream Hi Christoph, On Sep 1, 2020, at 3:14 AM, Thiede, Christoph > wrote:  Hi all, I was just trying to file out a complex Object graph via #saveOnFile and found two problems with the SmartRefStream: First, uniclass instances cannot be written to the stream. In SmartRefStream >> #instVarInfo:, I get an #errorKeyNotFound because the uniclass name is looked up into the SmalltalkImage globals. Minimum example to reproduce: Object newSubclass new saveOnFile. Second, blocks met during the file out process cannot be filed in again. Just try [] saveOnFile to reproduce this. Filein raises newMethodViaNewError. Maybe CompiledBlock should overwrite some streaming method for creating an appropriate disk representation? My guess is that class side code in CompiledMethod should be moved up to CompiledCode, the common superclass of CompiledMethod and CompiledBlock. Just wanted to report these issues. Maybe someone is more experienced with the SmartRefStream architecture and would like to fix these bugs. :-) Best, Christoph -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From stes at telenet.be Tue Sep 1 16:48:29 2020 From: stes at telenet.be (stes@PANDORA.BE) Date: Tue, 1 Sep 2020 18:48:29 +0200 (CEST) Subject: [squeak-dev] SQUEAK_SPY and -spy option In-Reply-To: <1040300227.101031863.1598857768961.JavaMail.zimbra@telenet.be> References: <1040300227.101031863.1598857768961.JavaMail.zimbra@telenet.be> Message-ID: <24329392.107613878.1598978909454.JavaMail.zimbra@telenet.be> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 If noone is using this variable (or switch), over time, I may submit a patch to remove it (not right away, but in the far future :-)). This is not urgent of course, but when running a tool on the Squeak VM ("link analysis of runtime interfaces") it flags the withSpy variable as interesting because it is declared and defined in two places. And if it is not really used (at all) then there's no point in doing that. David Stes -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAEBCAAGBQJfTnsNAAoJEAwpOKXMq1Mad8gH/0Ux6sr6jIPlNiFz4fi1WW4W FHNnHzRueXXklILZuuCBQFs0lEVxZIVhVJTQuC7LyLa+rjTofvZxQ8ZueR9N4o23 lAUvGxhqK2/rhMUp97Ccq5iH5tR6Py820r2bI4Gbl0B+rL3AdWBWLvtaUSXSU/0g 1TVtMhhuR4X73MysE3ikfN7fagFk2wUs0BGuO3FlZriQikc86hogGcbDTiLy8P4o dowUOGwRd1Tx1WZkRPgcDEDoeXV0V4Gpcy3gVvKBITrOOCTZOBr9Oa7HNq3JbnlL D8co9nYaO+CMu/U3cAJ4NQFOjPGO+z8f9aQfPFn5Bs2wNXXrnjP7O9eohJsmZd4= =SP1E -----END PGP SIGNATURE----- From lewis at mail.msen.com Tue Sep 1 18:48:55 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 1 Sep 2020 14:48:55 -0400 Subject: [squeak-dev] The Trunk: Sound-ct.71.mcz In-Reply-To: <061af1fd17f944f186ba2c946e95eb1e@student.hpi.uni-potsdam.de> References: <20200901021316.GA37550@shell.msen.com> <061af1fd17f944f186ba2c946e95eb1e@student.hpi.uni-potsdam.de> Message-ID: <20200901184855.GA7826@shell.msen.com> On Tue, Sep 01, 2020 at 06:03:09AM +0000, Thiede, Christoph wrote: > Hi David, hi all, thanks for merging! > > Do you know what Smalltalk endianness is good for? Is this maybe only relevant for FileStreams at all? Endianness is important when you need to know about the memory addressing conventions of the platform that Squeak is running on. For example, the elements of a ByteArray are always accessed consistenty in Smalltalk (e.g the first element is aByteArray at: 1). But the internal storage of the bytes in the object memory is done differently on little-endian versus big-endian platforms. If you look for senders of #endianness, #isBigEndian, and #isLittleEndian you will find lots of places where the endianness is important. It is worth noting also that the object memory gets fixed up for endianness when the image is loaded. Thus if you save an image on a little-endian machine, then load and run that image on a big-endian machine, then the byte ordering gets swapped at image loading time. Within Squeak, everything looks the same, but internally in the VM the bytes will be been swapped into the appropriate ordering. There is a good overview at https://en.wikipedia.org/wiki/Endianness Dave > > > Best, > Christoph > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Dienstag, 1. September 2020 04:13:16 > An: squeak-dev at lists.squeakfoundation.org > Cc: packages at lists.squeakfoundation.org > Betreff: Re: [squeak-dev] The Trunk: Sound-ct.71.mcz > > Thanks Christoph, good catch and thanks for the fix. > > I moved this and the supporting test immediately to trunk because it is > clearly a worthwhile change. > > All - Christoph requests detailed review, please do so and make any updates > if appropriate. > > I'm really happy to see long-overlooked details like this being addressed :-) > > Dave > > > On Tue, Sep 01, 2020 at 02:01:27AM +0000, commits at source.squeak.org wrote: > > David T. Lewis uploaded a new version of Sound to project The Trunk: > > http://source.squeak.org/trunk/Sound-ct.71.mcz > > > > ==================== Summary ==================== > > > > Name: Sound-ct.71 > > Author: ct > > Time: 1 September 2020, 2:07:15.07999 am > > UUID: 0d91a1bf-41cb-834c-ab0c-fa2ad832e408 > > Ancestors: Sound-nice.69 > > > > Fixes wave sound streaming on non-filestream objects. The endianness was inverted because #int16: already uses Big Endian. This did not sound well - listen yourself in an unpatched image: :-) > > > > array := ByteArray streamContents: [:stream | > > PluckedSound bachFugue storeWAVSamplesOn: stream]. > > (FileStream fileNamed: 'bachFugue.wav') binary in: [:stream | > > [array do: [:ea | stream nextPut: ea]] > > ensure: [stream close]]. > > (SampledSound fromWaveFileNamed: 'bachFugue.wav') play. > > > > Please review in detail as this is one of my first contacts to the Sound system! > > > > =============== Diff against Sound-nice.69 =============== > > > > Item was changed: > > ----- Method: AbstractSound>>storeSampleCount:bigEndian:on: (in category 'file i/o') ----- > > storeSampleCount: samplesToStore bigEndian: bigEndianFlag on: aBinaryStream > > "Store my samples on the given stream at the current SoundPlayer sampling rate. If bigFlag is true, then each 16-bit sample is stored most-significant byte first (AIFF files), otherwise it is stored least-significant byte first (WAV files). If self isStereo is true, both channels are stored, creating a stereo file. Otherwise, only the left channel is stored, creating a mono file." > > > > + | bufSize stereoBuffer reverseBytes streamDirect | > > - | bufSize stereoBuffer reverseBytes | > > self reset. > > bufSize := (2 * self samplingRate rounded) min: samplesToStore. "two second buffer" > > stereoBuffer := SoundBuffer newStereoSampleCount: bufSize. > > + streamDirect := aBinaryStream isKindOf: StandardFileStream. > > + reverseBytes := (bigEndianFlag xor: Smalltalk isBigEndian) xor: streamDirect not. > > - reverseBytes := bigEndianFlag ~= (Smalltalk isBigEndian). > > > > 'Storing audio...' > > displayProgressFrom: 0 to: samplesToStore during: [:bar | | remaining out | > > remaining := samplesToStore. > > [remaining > 0] whileTrue: [ > > bar value: samplesToStore - remaining. > > stereoBuffer primFill: 0. "clear the buffer" > > self playSampleCount: (bufSize min: remaining) into: stereoBuffer startingAt: 1. > > out := self isStereo > > ifTrue: [stereoBuffer] > > ifFalse: [stereoBuffer extractLeftChannel]. > > reverseBytes ifTrue: [out reverseEndianness]. > > + streamDirect > > - (aBinaryStream isKindOf: StandardFileStream) > > ifTrue: [ "optimization for files: write sound buffer directly to file" > > aBinaryStream next: (out size // 2) putAll: out startingAt: 1] "size in words" > > ifFalse: [ "for non-file streams:" > > 1 to: out monoSampleCount do: [:i | aBinaryStream int16: (out at: i)]]. > > + remaining := remaining - bufSize]].! > > - remaining := remaining - bufSize]]. > > - ! > > > > > > From eric.gade at gmail.com Tue Sep 1 22:10:35 2020 From: eric.gade at gmail.com (Eric Gade) Date: Tue, 1 Sep 2020 18:10:35 -0400 Subject: [squeak-dev] Inspector Custom Value Panes & Related Message-ID: Hi all, What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? For my RISCV work, I'm looking to show a morphic representation of the bitfields when a given instruction's `self` property is highlighted in the Inspector window. As a related question, what's the best way to make a composed Morph that has StringMorphs of different fonts, sizes, and alignments? It seems like not all Fonts are available to StringMorph (TrueType ones, for example) -- is that correct? Thanks! -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From christoph.thiede at student.hpi.uni-potsdam.de Tue Sep 1 23:32:40 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Tue, 1 Sep 2020 23:32:40 +0000 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: Message-ID: Hi Eric, for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text. Best, Christoph On Wed, Sep 2, 2020 at 12:10 AM +0200, "Eric Gade" wrote: Hi all, What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? For my RISCV work, I'm looking to show a morphic representation of the bitfields when a given instruction's `self` property is highlighted in the Inspector window. As a related question, what's the best way to make a composed Morph that has StringMorphs of different fonts, sizes, and alignments? It seems like not all Fonts are available to StringMorph (TrueType ones, for example) -- is that correct? Thanks! -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From vanessa at codefrau.net Wed Sep 2 01:27:23 2020 From: vanessa at codefrau.net (Vanessa Freudenberg) Date: Tue, 1 Sep 2020 18:27:23 -0700 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: References: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> Message-ID: On Tue, Sep 1, 2020 at 8:36 AM Eliot Miranda wrote: > Hi Tony, > > > > On Sep 1, 2020, at 6:50 AM, Tony Garnock-Jones < > tonyg at leastfixedpoint.com> wrote: > > > > It occurs to me that to get better power efficiency, Squeak may need to > > learn how to go to sleep more often. At present it's using about a > > quarter of a core on my phone when I'm not doing anything to it. > > > > JIT could help that, I guess; but more generally, it'd be nice just not > > to be awake when there's nothing pressing to do... > > The solution is a modified event architecture and jettisoning > relinquishProcessorForMilliseconds:. Search the archives (squeak-dev & > vm-dev) for “event driven vm”. This is work that should have been done a > long time ago (I did this for the VisualWorks vm back at the turn of the > century or thereabouts). But it isn’t going to get done unless someone > steps up. My input queue is full to overflowing. > Not just the VM, but we would also need to modify the image to be event-driven. A lot of Morphic code works by polling for changes in state (typically in a "step" method), rather than reacting to events. That is almost elegant in its simplicity, but rather wasteful. It was designed before power consumption became an issue we care about. Nowadays, Morphs should only be stepping if they are actively animating something without user input. Basically, the UI process should sleep until there is an event, which could be a user event, a timer event, or some asynchronous state change (like a network package arrived, etc). I guess Morphic can accommodate that, or it might need a larger re-design. E.g. Tweak was designed around every UI element running in a separate thread, and sleeping until there was actually something to do. - Vanessa - -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Wed Sep 2 08:52:20 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Wed, 2 Sep 2020 10:52:20 +0200 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: References: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> Message-ID: Hi all. If one does not depend on accurate timing for animations, it would be possible to re-design WorldState >> #doOneCycle to go to sleep after each cycle, only waking up if some (unknown) delay or event triggers from another Squeak process. We could design events for Morph >> #changed and also Morphic stepping, as Vanessa explained. Maybe the stepping logic could be handled in an extra process altogether. Not sure about the Tweak approach because not sure about the scheduling efficiency when facing loads (!) of Squeak processes. :-) User input is -- basically -- event-driven already. See EventSensor >> #eventTickler, #nextEvent and HandMorph >> #processEvent. There is just a "polling layer" on top of it to make it compatible with the Morphic main loop (i.e. #doOneCycle). If that event-tickler process would still consume too much CPU time, there is a rough sketch of an "inputSemaphore" in the image. See EventSensor >> #primSetInputSemaphore:. However, one would have to double-check the VM code whether that semaphore is signaled in all cases on all platforms. Then, even the event-tickler process could avoid the use of a fixed delay. Then, the VM would tell the image when it is time to call #primGetNextEvent:. No unnecessary polling. Best, Marcel Am 02.09.2020 03:28:11 schrieb Vanessa Freudenberg : On Tue, Sep 1, 2020 at 8:36 AM Eliot Miranda wrote: Hi Tony, > On Sep 1, 2020, at 6:50 AM, Tony Garnock-Jones wrote: > > It occurs to me that to get better power efficiency, Squeak may need to > learn how to go to sleep more often. At present it's using about a > quarter of a core on my phone when I'm not doing anything to it. > > JIT could help that, I guess; but more generally, it'd be nice just not > to be awake when there's nothing pressing to do... The solution is a modified event architecture and jettisoning relinquishProcessorForMilliseconds:.  Search the archives (squeak-dev & vm-dev) for “event driven vm”.  This is work that should have been done a long time ago (I did this for the VisualWorks vm back at the turn of the century or thereabouts).  But it isn’t going to get done unless someone steps up.  My input queue is full to overflowing. Not just the VM, but we would also need to modify the image to be event-driven. A lot of Morphic code works by polling for changes in state (typically in a "step" method), rather than reacting to events. That is almost elegant in its simplicity, but rather wasteful. It was designed before power consumption became an issue we care about. Nowadays, Morphs should only be stepping if they are actively animating something without user input.  Basically, the UI process should sleep until there is an event, which could be a user event, a timer event, or some asynchronous state change (like a network package arrived, etc). I guess Morphic can accommodate that, or it might need a larger re-design. E.g. Tweak was designed around every UI element running in a separate thread, and sleeping until there was actually something to do. - Vanessa -  -------------- next part -------------- An HTML attachment was scrubbed... URL: From lecteur at zogotounga.net Wed Sep 2 11:36:48 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Wed, 2 Sep 2020 13:36:48 +0200 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: References: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> Message-ID: <5fc32785-075d-087b-4405-892fbbdbb9af@zogotounga.net> > > If one does not depend on accurate timing for animations, That's a big 'if'. I certainly desire the most accurate timing for animations (and also for everything else...) Stef From commits at source.squeak.org Wed Sep 2 14:55:49 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 2 Sep 2020 14:55:49 0000 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz Message-ID: Christoph Thiede uploaded a new version of Tools to project The Inbox: http://source.squeak.org/inbox/Tools-ct.986.mcz ==================== Summary ==================== Name: Tools-ct.986 Author: ct Time: 2 September 2020, 4:55:38.538083 pm UUID: b4cdf611-f04b-0b40-8f61-34429f414cca Ancestors: Tools-ct.985 Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. =============== Diff against Tools-ct.985 =============== Item was changed: ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- addParentMethodsSending: selectorSymbol + ^ self systemNavigation + headingAndAutoselectForLiteral: selectorSymbol + do: [:label :autoSelect | + | methodsList | + methodsList := self systemNavigation allCallsOn: selectorSymbol. + methodsList ifEmpty: [ + ^ self inform: ('There are no {1}' translated format: {label})]. + self - | methodsList | - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty - ifTrue: - [ ^(PopUpMenu labels: ' OK ') - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] - ifFalse: - [ self addParentMessages: methodsList + autoSelectString: autoSelect] - autoSelectString: selectorSymbol ] ! From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 2 14:56:34 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 2 Sep 2020 14:56:34 +0000 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: Message-ID: Can we make SystemNavigation >> #headingAndAutoselectForLiteral:do: non-private? ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Mittwoch, 2. September 2020 16:55:49 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: Tools-ct.986.mcz Christoph Thiede uploaded a new version of Tools to project The Inbox: http://source.squeak.org/inbox/Tools-ct.986.mcz ==================== Summary ==================== Name: Tools-ct.986 Author: ct Time: 2 September 2020, 4:55:38.538083 pm UUID: b4cdf611-f04b-0b40-8f61-34429f414cca Ancestors: Tools-ct.985 Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. =============== Diff against Tools-ct.985 =============== Item was changed: ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- addParentMethodsSending: selectorSymbol + ^ self systemNavigation + headingAndAutoselectForLiteral: selectorSymbol + do: [:label :autoSelect | + | methodsList | + methodsList := self systemNavigation allCallsOn: selectorSymbol. + methodsList ifEmpty: [ + ^ self inform: ('There are no {1}' translated format: {label})]. + self - | methodsList | - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty - ifTrue: - [ ^(PopUpMenu labels: ' OK ') - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] - ifFalse: - [ self addParentMessages: methodsList + autoSelectString: autoSelect] - autoSelectString: selectorSymbol ] ! -------------- next part -------------- An HTML attachment was scrubbed... URL: From Yoshiki.Ohshima at acm.org Wed Sep 2 14:58:46 2020 From: Yoshiki.Ohshima at acm.org (Yoshiki Ohshima) Date: Wed, 2 Sep 2020 07:58:46 -0700 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: References: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> Message-ID: Besides Tweak, I have some recollection that Andreas was making the Android VM that was requires to be event-driven, and in turn making a Morphic image event-driven. Does anybody know how it went? On Tue, Sep 1, 2020 at 6:28 PM Vanessa Freudenberg wrote: > On Tue, Sep 1, 2020 at 8:36 AM Eliot Miranda > wrote: > >> Hi Tony, >> >> >> > On Sep 1, 2020, at 6:50 AM, Tony Garnock-Jones < >> tonyg at leastfixedpoint.com> wrote: >> > >> > It occurs to me that to get better power efficiency, Squeak may need to >> > learn how to go to sleep more often. At present it's using about a >> > quarter of a core on my phone when I'm not doing anything to it. >> > >> > JIT could help that, I guess; but more generally, it'd be nice just not >> > to be awake when there's nothing pressing to do... >> >> The solution is a modified event architecture and jettisoning >> relinquishProcessorForMilliseconds:. Search the archives (squeak-dev & >> vm-dev) for “event driven vm”. This is work that should have been done a >> long time ago (I did this for the VisualWorks vm back at the turn of the >> century or thereabouts). But it isn’t going to get done unless someone >> steps up. My input queue is full to overflowing. >> > > Not just the VM, but we would also need to modify the image to be > event-driven. > > A lot of Morphic code works by polling for changes in state (typically in > a "step" method), rather than reacting to events. That is almost elegant in > its simplicity, but rather wasteful. It was designed before power > consumption became an issue we care about. Nowadays, Morphs should only be > stepping if they are actively animating something without user input. > > Basically, the UI process should sleep until there is an event, which > could be a user event, a timer event, or some asynchronous state change > (like a network package arrived, etc). > > I guess Morphic can accommodate that, or it might need a larger re-design. > E.g. Tweak was designed around every UI element running in a separate > thread, and sleeping until there was actually something to do. > > - Vanessa - > > -- -- Yoshiki -------------- next part -------------- An HTML attachment was scrubbed... URL: From eric.gade at gmail.com Wed Sep 2 15:01:45 2020 From: eric.gade at gmail.com (Eric Gade) Date: Wed, 2 Sep 2020 11:01:45 -0400 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: Message-ID: Hi Christoph, On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede < christoph.thiede at student.hpi.uni-potsdam.de> wrote: for displaying graphics in an inspector, have a look at MorphInspector > (screenshot) or FormInspector (pixels). Custom interactive fields are not > (yet?) supported, but in theory, you could subclass Inspector and override > the relevant toolbuilder methods. > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > Regarding to your second question - why don't you use a TextMorphs? The > composition of different formatting styles applied to a string is just what > makes up a Text > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? Thanks again -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Wed Sep 2 15:49:20 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 2 Sep 2020 11:49:20 -0400 Subject: [squeak-dev] Squeak on a cellphone may need better blocking behavior In-Reply-To: References: <0f9fb44b-cd77-9563-9158-fc9593ee934c@leastfixedpoint.com> Message-ID: <20200902154920.GA20163@shell.msen.com> On Wed, Sep 02, 2020 at 07:58:46AM -0700, Yoshiki Ohshima wrote: > Besides Tweak, I have some recollection that Andreas was making the Android > VM that was requires to be event-driven, and in turn making a Morphic image > event-driven. Does anybody know how it went? > Anreas' event-driven Android VM was successful. I had it installed on my phone for several years. I think that it was downloaded from the Google Play Store, but is no longer available there. Ian provides a pointer to archived information and code http://squeakvm.org/ By "successful" I mean that the event-driven VM worked, and it was able to run an image on my phone. I do not recall any issues with CPU consumption, although I do not remember looking for that as an issue. In any case, it ran on my phone with no obvious problems that I could notice. It was clear that a good deal of work would be required in order make Squeak itself usable on a device with no real keyboard or mouse. Dave > On Tue, Sep 1, 2020 at 6:28 PM Vanessa Freudenberg > wrote: > > > On Tue, Sep 1, 2020 at 8:36 AM Eliot Miranda > > wrote: > > > >> Hi Tony, > >> > >> > >> > On Sep 1, 2020, at 6:50 AM, Tony Garnock-Jones < > >> tonyg at leastfixedpoint.com> wrote: > >> > > >> > ???It occurs to me that to get better power efficiency, Squeak may need to > >> > learn how to go to sleep more often. At present it's using about a > >> > quarter of a core on my phone when I'm not doing anything to it. > >> > > >> > JIT could help that, I guess; but more generally, it'd be nice just not > >> > to be awake when there's nothing pressing to do... > >> > >> The solution is a modified event architecture and jettisoning > >> relinquishProcessorForMilliseconds:. Search the archives (squeak-dev & > >> vm-dev) for ???event driven vm???. This is work that should have been done a > >> long time ago (I did this for the VisualWorks vm back at the turn of the > >> century or thereabouts). But it isn???t going to get done unless someone > >> steps up. My input queue is full to overflowing. > >> > > > > Not just the VM, but we would also need to modify the image to be > > event-driven. > > > > A lot of Morphic code works by polling for changes in state (typically in > > a "step" method), rather than reacting to events. That is almost elegant in > > its simplicity, but rather wasteful. It was designed before power > > consumption became an issue we care about. Nowadays, Morphs should only be > > stepping if they are actively animating something without user input. > > > > Basically, the UI process should sleep until there is an event, which > > could be a user event, a timer event, or some asynchronous state change > > (like a network package arrived, etc). > > > > I guess Morphic can accommodate that, or it might need a larger re-design. > > E.g. Tweak was designed around every UI element running in a separate > > thread, and sleeping until there was actually something to do. > > > > - Vanessa - > > > > > > -- > -- Yoshiki > From sean at clipperadams.com Wed Sep 2 16:20:01 2020 From: sean at clipperadams.com (Sean DeNigris) Date: Wed, 2 Sep 2020 12:20:01 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) Message-ID: I've been pontificating about unity between dialects, convinced more than ever that we need each other and have more of a simple (not easy) communication problem than anything else, and pestering the principals of various communities, so I decided to put some concrete action behind it - some "skin in the game" as we say in the U.S. After reading Trygve's frustration with Squeak image/VM management, and Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage and run Squeak (and GToolkit) VMs/images. The current limitation vs. launching standard Pharo images is that you have to download the VMs and images and manually install them due to differences in URL and other conventions. However once the files are in place, you can create templates allowing images to be created at will from different Squeak versions and automatically run with the correct VM. Auto-installation could probably be done if someone cares enough but it scratched my itch for the moment to manage GT images. I’m sure Cuis support could be added, but the hooks are now available and someone more familiar with its VM/image installation might have an easier time. Until my latest PR [1] is merged, brave souls can play with it and give feedback by loading my issue branch [2] into the latest Launcher release [3]. NB only tested on Mac. It seemed Trygve was at his wits' end, but maybe this and other small gestures will let him know how important he is to our community if not motivate him to resume his important work. I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what small action can you take today that unites us rather than divides us? We can be each others' supporters even if technical, vision and other differences keep our codebases somewhat distinct. I believe the seeds of wars are planted when I lack the grace to give those around me the benefit of the doubt that they mean well and are trying their best. There is more than enough of that already in the world. Let’s invent a *better* future… Your brother, Sean 1. https://github.com/pharo-project/pharo-launcher/pull/503 2. https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks 3. https://pharo.org/download From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 2 19:14:57 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 2 Sep 2020 19:14:57 +0000 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: , Message-ID: Hi Eric, > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! See http://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html and https://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? These are good questions others can probably answer better than I could do. This is all I can tell you: 1. Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. 2. To change the font in a Text, use something like: 'foo' asText addAttribute: (TextFontReference toFont: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 20)); openAsMorph > Also -- how can I disable editing of a TextMorph? See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eric Gade Gesendet: Mittwoch, 2. September 2020 17:01:45 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related Hi Christoph, On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede > wrote: for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? Thanks again -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Wed Sep 2 20:04:38 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Wed, 2 Sep 2020 22:04:38 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: Message-ID: Hi > On 02.09.2020, at 21:14, Thiede, Christoph wrote: > > Hi Eric, > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! See http://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html and https://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? > > These are good questions others can probably answer better than I could do. > This is all I can tell you: > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. > • To change the font in a Text, use something like: > 'foo' asText > addAttribute: (TextFontReference toFont: (StrikeFont > familyName: 'Darkmap DejaVu Sans' > pointSize: 20)); > openAsMorph I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: 'foo' asText addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); openAsMorph If you are just after changing the font used in a TextMorph (not the text itself), I'd use 'foo' asTextMorph beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); openInHand That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. Best regards -Tobias > > > Also -- how can I disable editing of a TextMorph? > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. > > Best, > Christoph > Von: Squeak-dev im Auftrag von Eric Gade > Gesendet: Mittwoch, 2. September 2020 17:01:45 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > Hi Christoph, > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? > > Thanks again From lewis at mail.msen.com Wed Sep 2 20:05:22 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 2 Sep 2020 16:05:22 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: <20200902200522.GA56319@shell.msen.com> On Wed, Sep 02, 2020 at 12:20:01PM -0400, Sean DeNigris wrote: > I've been pontificating about unity between dialects, convinced more than ever that we need each other and have more of a simple (not easy) communication problem than anything else, and pestering the principals of various communities, so I decided to put some concrete action behind it - some "skin in the game" as we say in the U.S. > > After reading Trygve's frustration with Squeak image/VM management, and Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage and run Squeak (and GToolkit) VMs/images. > > The current limitation vs. launching standard Pharo images is that you have to download the VMs and images and manually install them due to differences in URL and other conventions. However once the files are in place, you can create templates allowing images to be created at will from different Squeak versions and automatically run with the correct VM. Auto-installation could probably be done if someone cares enough but it scratched my itch for the moment to manage GT images. I???m sure Cuis support could be added, but the hooks are now available and someone more familiar with its VM/image installation might have an easier time. > That's very cool Sean, thank you :-) > Until my latest PR [1] is merged, brave souls can play with it and give feedback by loading my issue branch [2] into the latest Launcher release [3]. NB only tested on Mac. > > It seemed Trygve was at his wits' end, but maybe this and other small gestures will let him know how important he is to our community if not motivate him to resume his important work. > > I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what small action can you take today that unites us rather than divides us? We can be each others' supporters even if technical, vision and other differences keep our codebases somewhat distinct. I believe the seeds of wars are planted when I lack the grace to give those around me the benefit of the doubt that they mean well and are trying their best. There is more than enough of that already in the world. Let???s invent a *better* future??? > I can't take any more actions today, but just the other day I published a Pharo/Tonel version of one the the packages that I develop in Squeak, so maybe I get half a "brownie point" for that. Thanks for your efforts and good words, Dave > Your brother, > Sean > > 1. https://github.com/pharo-project/pharo-launcher/pull/503 > 2. https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks > 3. https://pharo.org/download > From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 2 21:29:22 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 2 Sep 2020 21:29:22 +0000 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: , Message-ID: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de> Hi Tobias, thanks for the tips! :-) > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. Would you expect such a Text>>#setFont:from:to: only to add a TextFontReference or also to remove all existing TextFontReferences from the interval? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Mittwoch, 2. September 2020 22:04:38 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related Hi > On 02.09.2020, at 21:14, Thiede, Christoph wrote: > > Hi Eric, > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! See http://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html and https://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? > > These are good questions others can probably answer better than I could do. > This is all I can tell you: > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. > • To change the font in a Text, use something like: > 'foo' asText > addAttribute: (TextFontReference toFont: (StrikeFont > familyName: 'Darkmap DejaVu Sans' > pointSize: 20)); > openAsMorph I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: 'foo' asText addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); openAsMorph If you are just after changing the font used in a TextMorph (not the text itself), I'd use 'foo' asTextMorph beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); openInHand That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. Best regards -Tobias > > > Also -- how can I disable editing of a TextMorph? > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. > > Best, > Christoph > Von: Squeak-dev im Auftrag von Eric Gade > Gesendet: Mittwoch, 2. September 2020 17:01:45 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > Hi Christoph, > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? > > Thanks again -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 2 21:35:50 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 2 Sep 2020 21:35:50 +0000 Subject: [squeak-dev] The Trunk: Sound-ct.71.mcz In-Reply-To: <20200901184855.GA7826@shell.msen.com> References: <20200901021316.GA37550@shell.msen.com> <061af1fd17f944f186ba2c946e95eb1e@student.hpi.uni-potsdam.de>, <20200901184855.GA7826@shell.msen.com> Message-ID: <0a37e7afcf764c55986e463480dc0a61@student.hpi.uni-potsdam.de> Hi David, thanks for the explanation! Given that explanation, the patched method will still not work correctly for ByteArray on a BigEndian system, is that correct? (I do not have a BigEndian system available.) Should we define reverseBytes as the following instead? reverseBytes := streamDirect ifTrue: [bigEndianFlag xor: Smalltalk isBigEndian] ifFalse: [bigEndianFlag]. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Dienstag, 1. September 2020 20:48:55 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Trunk: Sound-ct.71.mcz On Tue, Sep 01, 2020 at 06:03:09AM +0000, Thiede, Christoph wrote: > Hi David, hi all, thanks for merging! > > Do you know what Smalltalk endianness is good for? Is this maybe only relevant for FileStreams at all? Endianness is important when you need to know about the memory addressing conventions of the platform that Squeak is running on. For example, the elements of a ByteArray are always accessed consistenty in Smalltalk (e.g the first element is aByteArray at: 1). But the internal storage of the bytes in the object memory is done differently on little-endian versus big-endian platforms. If you look for senders of #endianness, #isBigEndian, and #isLittleEndian you will find lots of places where the endianness is important. It is worth noting also that the object memory gets fixed up for endianness when the image is loaded. Thus if you save an image on a little-endian machine, then load and run that image on a big-endian machine, then the byte ordering gets swapped at image loading time. Within Squeak, everything looks the same, but internally in the VM the bytes will be been swapped into the appropriate ordering. There is a good overview at https://en.wikipedia.org/wiki/Endianness Dave > > > Best, > Christoph > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Dienstag, 1. September 2020 04:13:16 > An: squeak-dev at lists.squeakfoundation.org > Cc: packages at lists.squeakfoundation.org > Betreff: Re: [squeak-dev] The Trunk: Sound-ct.71.mcz > > Thanks Christoph, good catch and thanks for the fix. > > I moved this and the supporting test immediately to trunk because it is > clearly a worthwhile change. > > All - Christoph requests detailed review, please do so and make any updates > if appropriate. > > I'm really happy to see long-overlooked details like this being addressed :-) > > Dave > > > On Tue, Sep 01, 2020 at 02:01:27AM +0000, commits at source.squeak.org wrote: > > David T. Lewis uploaded a new version of Sound to project The Trunk: > > http://source.squeak.org/trunk/Sound-ct.71.mcz > > > > ==================== Summary ==================== > > > > Name: Sound-ct.71 > > Author: ct > > Time: 1 September 2020, 2:07:15.07999 am > > UUID: 0d91a1bf-41cb-834c-ab0c-fa2ad832e408 > > Ancestors: Sound-nice.69 > > > > Fixes wave sound streaming on non-filestream objects. The endianness was inverted because #int16: already uses Big Endian. This did not sound well - listen yourself in an unpatched image: :-) > > > > array := ByteArray streamContents: [:stream | > > PluckedSound bachFugue storeWAVSamplesOn: stream]. > > (FileStream fileNamed: 'bachFugue.wav') binary in: [:stream | > > [array do: [:ea | stream nextPut: ea]] > > ensure: [stream close]]. > > (SampledSound fromWaveFileNamed: 'bachFugue.wav') play. > > > > Please review in detail as this is one of my first contacts to the Sound system! > > > > =============== Diff against Sound-nice.69 =============== > > > > Item was changed: > > ----- Method: AbstractSound>>storeSampleCount:bigEndian:on: (in category 'file i/o') ----- > > storeSampleCount: samplesToStore bigEndian: bigEndianFlag on: aBinaryStream > > "Store my samples on the given stream at the current SoundPlayer sampling rate. If bigFlag is true, then each 16-bit sample is stored most-significant byte first (AIFF files), otherwise it is stored least-significant byte first (WAV files). If self isStereo is true, both channels are stored, creating a stereo file. Otherwise, only the left channel is stored, creating a mono file." > > > > + | bufSize stereoBuffer reverseBytes streamDirect | > > - | bufSize stereoBuffer reverseBytes | > > self reset. > > bufSize := (2 * self samplingRate rounded) min: samplesToStore. "two second buffer" > > stereoBuffer := SoundBuffer newStereoSampleCount: bufSize. > > + streamDirect := aBinaryStream isKindOf: StandardFileStream. > > + reverseBytes := (bigEndianFlag xor: Smalltalk isBigEndian) xor: streamDirect not. > > - reverseBytes := bigEndianFlag ~= (Smalltalk isBigEndian). > > > > 'Storing audio...' > > displayProgressFrom: 0 to: samplesToStore during: [:bar | | remaining out | > > remaining := samplesToStore. > > [remaining > 0] whileTrue: [ > > bar value: samplesToStore - remaining. > > stereoBuffer primFill: 0. "clear the buffer" > > self playSampleCount: (bufSize min: remaining) into: stereoBuffer startingAt: 1. > > out := self isStereo > > ifTrue: [stereoBuffer] > > ifFalse: [stereoBuffer extractLeftChannel]. > > reverseBytes ifTrue: [out reverseEndianness]. > > + streamDirect > > - (aBinaryStream isKindOf: StandardFileStream) > > ifTrue: [ "optimization for files: write sound buffer directly to file" > > aBinaryStream next: (out size // 2) putAll: out startingAt: 1] "size in words" > > ifFalse: [ "for non-file streams:" > > 1 to: out monoSampleCount do: [:i | aBinaryStream int16: (out at: i)]]. > > + remaining := remaining - bufSize]].! > > - remaining := remaining - bufSize]]. > > - ! > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 2 21:37:06 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 2 Sep 2020 21:37:06 +0000 Subject: [squeak-dev] Metacello broken again In-Reply-To: References: <2c3359358004407498a8d3331c381203@student.hpi.uni-potsdam.de>, , Message-ID: <53086722e7f443a79e1aa74757004963@student.hpi.uni-potsdam.de> Thank you Tom :-) ________________________________ Von: Squeak-dev im Auftrag von Beckmann, Tom Gesendet: Dienstag, 1. September 2020 12:32:05 An: Squeak Dev Betreff: Re: [squeak-dev] Metacello broken again ...and now the servers are fixed, too :) http://forum.world.st/VOMongoConnectionError-when-dowloading-mcz-from-smalltalkhub-com-tp5120870p5121140.html ________________________________________ From: Squeak-dev on behalf of Beckmann, Tom Sent: Tuesday, September 1, 2020 12:23:39 PM To: Squeak Dev Subject: Re: [squeak-dev] Metacello broken again Hi Christoph, the smalltalkhub servers recently had some reconfigurations that are potentially the culprit [1] (just heard back, they are already on it :)). I attached a .sar for bootstrapping based on this issue's [2] suggestion for a new bootstrapping process that should allow you to get a working metacello in the meantime. Best, Tom [1] http://forum.world.st/VOMongoConnectionError-when-dowloading-mcz-from-smalltalkhub-com-tp5120870p5121135.html [2] https://github.com/Metacello/metacello/issues/524 ________________________________________ From: Squeak-dev on behalf of Thiede, Christoph Sent: Tuesday, September 1, 2020 11:56:10 AM To: Squeak Dev Subject: [squeak-dev] Metacello broken again Hi all, I did not follow the recent Metacello discussions again, but just now in my fresh Trunk image, Metacello new failed again multiple times saying 'retry with alternate repository failed: ''MessageNotUnderstood: UndefinedObject>>contentStreamFromEncoding:'''. Is this a known problem? How can I solve this without waiting for any server to be online again? After this command failed, you cannot even retry it, because #Metacello is no longer a registered class in the image. This is rather inconvenient ... Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Wed Sep 2 21:59:46 2020 From: asqueaker at gmail.com (Chris Muller) Date: Wed, 2 Sep 2020 16:59:46 -0500 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: Message-ID: Hi Christoph, For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Best, Chris On Wed, Sep 2, 2020 at 9:56 AM wrote: > > Christoph Thiede uploaded a new version of Tools to project The Inbox: > http://source.squeak.org/inbox/Tools-ct.986.mcz > > ==================== Summary ==================== > > Name: Tools-ct.986 > Author: ct > Time: 2 September 2020, 4:55:38.538083 pm > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca > Ancestors: Tools-ct.985 > > Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. > > =============== Diff against Tools-ct.985 =============== > > Item was changed: > ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- > addParentMethodsSending: selectorSymbol > > + ^ self systemNavigation > + headingAndAutoselectForLiteral: selectorSymbol > + do: [:label :autoSelect | > + | methodsList | > + methodsList := self systemNavigation allCallsOn: selectorSymbol. > + methodsList ifEmpty: [ > + ^ self inform: ('There are no {1}' translated format: {label})]. > + self > - | methodsList | > - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty > - ifTrue: > - [ ^(PopUpMenu labels: ' OK ') > - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] > - ifFalse: > - [ self > addParentMessages: methodsList > + autoSelectString: autoSelect] > - autoSelectString: selectorSymbol ] > ! > > -------------- next part -------------- A non-text attachment was scrubbed... Name: MessageTrace-addParentMethodsSending.st Type: application/octet-stream Size: 601 bytes Desc: not available URL: From Das.Linux at gmx.de Wed Sep 2 22:13:24 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Thu, 3 Sep 2020 00:13:24 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de> References: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de> Message-ID: <8A5F154E-0BA8-4D64-8D1C-E844CCB85690@gmx.de> > On 02.09.2020, at 23:29, Thiede, Christoph wrote: > > Hi Tobias, thanks for the tips! :-) > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > Would you expect such a Text>>#setFont:from:to: only to add a TextFontReference or also to remove all existing TextFontReferences from the interval? > I'd be wary of using #set... because exactly that expectation happens. Vocabulary is cumbersome, tho. see TextMorph's _be_AllFont, Text's _make_Bold and the attributes only ever add. Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? Coalescing the runs would probably take care of chopping up the different font runs, but that's beneath the cover. That said, I liked the Seaside/Magritte-wording with beSomething on their brushes or models respectively… Best regards -Tobias > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Mittwoch, 2. September 2020 22:04:38 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > Hi > > > On 02.09.2020, at 21:14, Thiede, Christoph wrote: > > > > Hi Eric, > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! Seehttp://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html andhttps://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > > > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? > > > > These are good questions others can probably answer better than I could do. > > This is all I can tell you: > > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. > > • To change the font in a Text, use something like: > > 'foo' asText > > addAttribute: (TextFontReference toFont: (StrikeFont > > familyName: 'Darkmap DejaVu Sans' > > pointSize: 20)); > > openAsMorph > > I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: > > 'foo' asText > addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); > openAsMorph > > > If you are just after changing the font used in a TextMorph (not the text itself), I'd use > > 'foo' asTextMorph > beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); > openInHand > > > That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > Best regards > -Tobias > > > > > > > Also -- how can I disable editing of a TextMorph? > > > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. > > > > Best, > > Christoph > > Von: Squeak-dev im Auftrag von Eric Gade > > Gesendet: Mittwoch, 2. September 2020 17:01:45 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > Hi Christoph, > > > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: > > > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text > > > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? > > > > Thanks again From marcel.taeumel at hpi.de Thu Sep 3 07:56:26 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 3 Sep 2020 09:56:26 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: Message-ID: Hi Eric. > What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? Unfortunately, the current inspector framework has no such extension point. You can, however, prepare a custom inspector for your domain objects. See MorphInspector as an example.  Best, Marcel Am 02.09.2020 00:10:56 schrieb Eric Gade : Hi all, What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? For my RISCV work, I'm looking to show a morphic representation of the bitfields when a given instruction's `self` property is highlighted in the Inspector window. As a related question, what's the best way to make a composed Morph that has StringMorphs of different fonts, sizes, and alignments? It seems like not all Fonts are available to StringMorph (TrueType ones, for example) -- is that correct? Thanks! -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 3 10:58:39 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 3 Sep 2020 10:58:39 0000 Subject: [squeak-dev] The Inbox: Morphic-LM.1678.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-LM.1678.mcz ==================== Summary ==================== Name: Morphic-LM.1678 Author: LM Time: 3 September 2020, 12:58:35.620287 pm UUID: d1262531-fd1c-43a1-ba41-9ff8b3728a3a Ancestors: Morphic-ct.1677 Prevent users from creating multiple SystemProgressMorphs by abusing uniqueInstance. Currently the UniqueInstance class variable is not set properly when it is nil, which allows the user to call it multiple times to generate many SystemProgressMorphs, defeating the purpose of having a *unique* instance. =============== Diff against Morphic-ct.1677 =============== Item was changed: ----- Method: SystemProgressMorph class>>uniqueInstance (in category 'instance creation') ----- uniqueInstance + + ^ UniqueInstance ifNil: [UniqueInstance := super new]! - ^UniqueInstance ifNil:[super new]! From commits at source.squeak.org Thu Sep 3 11:05:03 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 3 Sep 2020 11:05:03 0000 Subject: [squeak-dev] The Inbox: Network-ct.242.mcz Message-ID: Christoph Thiede uploaded a new version of Network to project The Inbox: http://source.squeak.org/inbox/Network-ct.242.mcz ==================== Summary ==================== Name: Network-ct.242 Author: ct Time: 3 September 2020, 1:05:00.729083 pm UUID: 8c331b27-3e86-aa41-bc42-910d53097713 Ancestors: Network-eem.241 Fixes two MNUs that can occur during connecting to an unavailable network resource. Bug #1 (stream>>>#timeout:) Steps to reproduce: 1. Start your image while an internet connection is available. Make any connection attempt (e.g. refresh a Monticello HTTP(s) repository) to ensure that the network is initialized (NetNameResolver initializeNetwork). 2. There are two alternative scenarios: 2.(i) Do it: WebClient httpGet: 'https://foo.bar'. 2.(ii) Turn off your internet connection (e.g. disable your WLAN adapter). Then make another connection attempt. In both scenarios, you will see the following error: MessageNotUnderstood: UndefinedObject>>timeout: (stream) WebClient>>connect WebClient>>sendRequest:contentBlock: ... This bug occurred because there was no check for the presence of any network socket for the request host/port. Bug #2 (#findNextHandlerContextStarting): Steps to reproduce: Do it: WebClient httpGet: 'https://'. Error: MessageNotUnderstood: UndefinedObject>>findNextHandlerContextStarting Context>>nextHandlerContext ConnectionRefused(Exception)>>pass ... This bug was related to the unorthodox way of exception handling in the unpatched method when trying to connect to the different available sockets. You cannot pass an exception if its context is dead, so I used recursion instead, which also has the advantage that every exception from every socket is stored on the stack and can be explored by users encountering an error. Also improves multilingual support. Please review! - Is SocketStream the right place to signal the NoNetworkError? (Because of its enumerative character, I found SocketAddressInformation>>#forHost:service:flags:addressFamily:socketType:protocol:) appropriate to return an empty collection. =============== Diff against Network-eem.241 =============== Item was changed: ----- Method: SocketStream class>>openConnectionToHostNamed:port: (in category 'instance creation') ----- openConnectionToHostNamed: hostName port: portNumber + | addressInformations | + NetNameResolver useOldNetwork ifTrue: [ + | hostIP | + hostIP := NetNameResolver addressForName: hostName timeout: 20. + hostIP ifNil: [NetworkError signal: ('Cannot resolve {1}.' format: {hostName})]. + ^ self openConnectionToHost: hostIP port: portNumber]. + + addressInformations := SocketAddressInformation + forHost: hostName + service: portNumber asString + flags: 0 + addressFamily: 0 + socketType: SocketAddressInformation socketTypeStream + protocol: SocketAddressInformation protocolTCP. + addressInformations ifEmpty: [ + NoNetworkError signal: ('Could not find a network for {1} on port {2}' translated format: {hostName. portNumber})]. + + addressInformations readStream in: [:stream | + | connectBlock | + connectBlock := [ + [^ self on: stream next connect] + on: NetworkError do: [:error | + stream atEnd + ifFalse: connectBlock "ignore and try next" + ifTrue: [error pass]]]. + ^ connectBlock value].! - NetNameResolver useOldNetwork - ifTrue: [ | hostIP | - hostIP := NetNameResolver addressForName: hostName timeout: 20. - hostIP ifNil: [NetworkError signal: ('Cannot resolve {1}.' format: {hostName})]. - ^self openConnectionToHost: hostIP port: portNumber] - ifFalse: [| addressInformations lastError | - addressInformations := SocketAddressInformation - forHost: hostName - service: portNumber asString - flags: 0 - addressFamily: 0 - socketType: SocketAddressInformation socketTypeStream - protocol: SocketAddressInformation protocolTCP. - addressInformations do: [:addressInformation | - [^ self on: addressInformation connect] on: NetworkError do: [:e | lastError := e]]. - ^ lastError ifNotNil: [:e | e pass]]! From commits at source.squeak.org Thu Sep 3 11:05:48 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 3 Sep 2020 11:05:48 0000 Subject: [squeak-dev] The Inbox: NetworkTests-ct.59.mcz Message-ID: Christoph Thiede uploaded a new version of NetworkTests to project The Inbox: http://source.squeak.org/inbox/NetworkTests-ct.59.mcz ==================== Summary ==================== Name: NetworkTests-ct.59 Author: ct Time: 3 September 2020, 1:05:46.401083 pm UUID: f29c9c9a-0f9d-2c49-9a78-2ff656b9c3a4 Ancestors: NetworkTests-pre.58 Regression tests for Network-ct.242. =============== Diff against NetworkTests-pre.58 =============== Item was added: + ----- Method: SocketStreamTest>>testOpenConnection (in category 'tests') ----- + testOpenConnection + + | stream | + [self + shouldnt: [stream := SocketStream openConnectionToHostNamed: 'google.de' port: 80] + raise: NetworkError. + self assert: stream isConnected] ensure: [ + stream close]. + + [self + should: [stream := SocketStream openConnectionToHostNamed: 'thisurlshouldbeinval.it' port: 80] + raise: NetworkError] ensure: [stream close]. + + [self + should: [stream := SocketStream openConnectionToHostNamed: '' port: 80] + raise: NetworkError] ensure: [stream close].! From commits at source.squeak.org Thu Sep 3 11:11:36 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 3 Sep 2020 11:11:36 0000 Subject: [squeak-dev] The Trunk: Morphic-LM.1678.mcz Message-ID: Fabio Niephaus uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-LM.1678.mcz ==================== Summary ==================== Name: Morphic-LM.1678 Author: LM Time: 3 September 2020, 12:58:35.620287 pm UUID: d1262531-fd1c-43a1-ba41-9ff8b3728a3a Ancestors: Morphic-ct.1677 Prevent users from creating multiple SystemProgressMorphs by abusing uniqueInstance. Currently the UniqueInstance class variable is not set properly when it is nil, which allows the user to call it multiple times to generate many SystemProgressMorphs, defeating the purpose of having a *unique* instance. =============== Diff against Morphic-mt.1674 =============== Item was changed: ----- Method: MenuItemMorph>>invokeWithEvent: (in category 'events') ----- invokeWithEvent: evt "Perform the action associated with the given menu item." - | w | self isEnabled ifFalse: [^ self]. + + (owner notNil and: [self isStayUpItem not]) ifTrue: [ - target class == HandMorph ifTrue: [(self notObsolete) ifFalse: [^ self]]. - owner ifNotNil:[self isStayUpItem ifFalse:[ self flag: #workAround. "The tile system invokes menus straightforwardly so the menu might not be in the world." + self world ifNotNil: [:world | - (w := self world) ifNotNil:[ owner deleteIfPopUp: evt. "Repair damage before invoking the action for better feedback" + world displayWorldSafely]]. + + selector ifNil: [^ self]. + + Cursor normal showWhile: [ + "show cursor in case item opens a new MVC window" + selector numArgs isZero + ifTrue: [target perform: selector] + ifFalse: [target perform: selector withArguments: ( + selector numArgs = arguments size + ifTrue: [arguments] + ifFalse: [arguments copyWith: evt] )] ].! - w displayWorldSafely]]]. - selector ifNil:[^self]. - Cursor normal showWhile: [ | selArgCount | "show cursor in case item opens a new MVC window" - (selArgCount := selector numArgs) = 0 - ifTrue: - [target perform: selector] - ifFalse: - [selArgCount = arguments size - ifTrue: [target perform: selector withArguments: arguments] - ifFalse: [target perform: selector withArguments: (arguments copyWith: evt)]]].! Item was removed: - ----- Method: MenuItemMorph>>notObsolete (in category 'private') ----- - notObsolete - "Provide backward compatibility with messages being sent to the Hand. Remove this when no projects made prior to 2.9 are likely to be used. If this method is removed early, the worst that can happen is a notifier when invoking an item in an obsolete menu." - - (HandMorph canUnderstand: (selector)) ifTrue: [^ true]. "a modern one" - - self inform: 'This world menu is obsolete. - Please dismiss the menu and open a new one.'. - ^ false - ! Item was changed: ----- Method: SystemProgressMorph class>>uniqueInstance (in category 'instance creation') ----- uniqueInstance + + ^ UniqueInstance ifNil: [UniqueInstance := super new]! - ^UniqueInstance ifNil:[super new]! From commits at source.squeak.org Thu Sep 3 11:27:40 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 3 Sep 2020 11:27:40 0000 Subject: [squeak-dev] The Trunk: Morphic-ct.1677.mcz Message-ID: Fabio Niephaus uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-ct.1677.mcz ==================== Summary ==================== Name: Morphic-ct.1677 Author: ct Time: 22 August 2020, 7:34:52.856555 pm UUID: 094ccc97-c3f6-364c-a10d-7418b3482703 Ancestors: Morphic-mt.1674 Nuke backward compatibility for Squeak 2.x menus. This should really not be relevant any longer I think. ;-) Also minor refactoring to MenuItemMorph >> #invokeWithEvent:. =============== Diff against Morphic-mt.1674 =============== Item was changed: ----- Method: MenuItemMorph>>invokeWithEvent: (in category 'events') ----- invokeWithEvent: evt "Perform the action associated with the given menu item." - | w | self isEnabled ifFalse: [^ self]. + + (owner notNil and: [self isStayUpItem not]) ifTrue: [ - target class == HandMorph ifTrue: [(self notObsolete) ifFalse: [^ self]]. - owner ifNotNil:[self isStayUpItem ifFalse:[ self flag: #workAround. "The tile system invokes menus straightforwardly so the menu might not be in the world." + self world ifNotNil: [:world | - (w := self world) ifNotNil:[ owner deleteIfPopUp: evt. "Repair damage before invoking the action for better feedback" + world displayWorldSafely]]. + + selector ifNil: [^ self]. + + Cursor normal showWhile: [ + "show cursor in case item opens a new MVC window" + selector numArgs isZero + ifTrue: [target perform: selector] + ifFalse: [target perform: selector withArguments: ( + selector numArgs = arguments size + ifTrue: [arguments] + ifFalse: [arguments copyWith: evt] )] ].! - w displayWorldSafely]]]. - selector ifNil:[^self]. - Cursor normal showWhile: [ | selArgCount | "show cursor in case item opens a new MVC window" - (selArgCount := selector numArgs) = 0 - ifTrue: - [target perform: selector] - ifFalse: - [selArgCount = arguments size - ifTrue: [target perform: selector withArguments: arguments] - ifFalse: [target perform: selector withArguments: (arguments copyWith: evt)]]].! Item was removed: - ----- Method: MenuItemMorph>>notObsolete (in category 'private') ----- - notObsolete - "Provide backward compatibility with messages being sent to the Hand. Remove this when no projects made prior to 2.9 are likely to be used. If this method is removed early, the worst that can happen is a notifier when invoking an item in an obsolete menu." - - (HandMorph canUnderstand: (selector)) ifTrue: [^ true]. "a modern one" - - self inform: 'This world menu is obsolete. - Please dismiss the menu and open a new one.'. - ^ false - ! From karlramberg at gmail.com Thu Sep 3 11:28:41 2020 From: karlramberg at gmail.com (karl ramberg) Date: Thu, 3 Sep 2020 13:28:41 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: Message-ID: Here is a change set that adds HaloHandle for font change to StringMorph Best, Karl On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel wrote: > Hi Eric. > > > What is the proper way to make a "custom value pane" for my objects > whenever they appear inside an Inspector? > > Unfortunately, the current inspector framework has no such extension > point. You can, however, prepare a custom inspector for your domain > objects. See MorphInspector as an example. > > Best, > Marcel > > Am 02.09.2020 00:10:56 schrieb Eric Gade : > Hi all, > > What is the proper way to make a "custom value pane" for my objects > whenever they appear inside an Inspector? For my RISCV work, I'm looking to > show a morphic representation of the bitfields when a given instruction's > `self` property is highlighted in the Inspector window. > > As a related question, what's the best way to make a composed Morph that > has StringMorphs of different fonts, sizes, and alignments? It seems like > not all Fonts are available to StringMorph (TrueType ones, for example) -- > is that correct? > > Thanks! > > -- > Eric > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: FontHandleForStringMorph.1.cs Type: application/octet-stream Size: 2602 bytes Desc: not available URL: From lists at fniephaus.com Thu Sep 3 11:30:48 2020 From: lists at fniephaus.com (Fabio Niephaus) Date: Thu, 3 Sep 2020 13:30:48 +0200 Subject: [squeak-dev] The Trunk: Morphic-LM.1678.mcz In-Reply-To: References: Message-ID: Hi all, I accidentally pushed this without realizing its ancestor is in the Inbox. Marcel is about to help me get this fixed...sorry! Fabio On Thu, Sep 3, 2020 at 1:11 PM wrote: > > Fabio Niephaus uploaded a new version of Morphic to project The Trunk: > http://source.squeak.org/trunk/Morphic-LM.1678.mcz > > ==================== Summary ==================== > > Name: Morphic-LM.1678 > Author: LM > Time: 3 September 2020, 12:58:35.620287 pm > UUID: d1262531-fd1c-43a1-ba41-9ff8b3728a3a > Ancestors: Morphic-ct.1677 > > Prevent users from creating multiple SystemProgressMorphs by abusing uniqueInstance. > > Currently the UniqueInstance class variable is not set properly when it is nil, which allows the user to call it multiple times to generate many SystemProgressMorphs, defeating the purpose of having a *unique* instance. > > =============== Diff against Morphic-mt.1674 =============== > > Item was changed: > ----- Method: MenuItemMorph>>invokeWithEvent: (in category 'events') ----- > invokeWithEvent: evt > "Perform the action associated with the given menu item." > > - | w | > self isEnabled ifFalse: [^ self]. > + > + (owner notNil and: [self isStayUpItem not]) ifTrue: [ > - target class == HandMorph ifTrue: [(self notObsolete) ifFalse: [^ self]]. > - owner ifNotNil:[self isStayUpItem ifFalse:[ > self flag: #workAround. "The tile system invokes menus straightforwardly so the menu might not be in the world." > + self world ifNotNil: [:world | > - (w := self world) ifNotNil:[ > owner deleteIfPopUp: evt. > "Repair damage before invoking the action for better feedback" > + world displayWorldSafely]]. > + > + selector ifNil: [^ self]. > + > + Cursor normal showWhile: [ > + "show cursor in case item opens a new MVC window" > + selector numArgs isZero > + ifTrue: [target perform: selector] > + ifFalse: [target perform: selector withArguments: ( > + selector numArgs = arguments size > + ifTrue: [arguments] > + ifFalse: [arguments copyWith: evt] )] ].! > - w displayWorldSafely]]]. > - selector ifNil:[^self]. > - Cursor normal showWhile: [ | selArgCount | "show cursor in case item opens a new MVC window" > - (selArgCount := selector numArgs) = 0 > - ifTrue: > - [target perform: selector] > - ifFalse: > - [selArgCount = arguments size > - ifTrue: [target perform: selector withArguments: arguments] > - ifFalse: [target perform: selector withArguments: (arguments copyWith: evt)]]].! > > Item was removed: > - ----- Method: MenuItemMorph>>notObsolete (in category 'private') ----- > - notObsolete > - "Provide backward compatibility with messages being sent to the Hand. Remove this when no projects made prior to 2.9 are likely to be used. If this method is removed early, the worst that can happen is a notifier when invoking an item in an obsolete menu." > - > - (HandMorph canUnderstand: (selector)) ifTrue: [^ true]. "a modern one" > - > - self inform: 'This world menu is obsolete. > - Please dismiss the menu and open a new one.'. > - ^ false > - ! > > Item was changed: > ----- Method: SystemProgressMorph class>>uniqueInstance (in category 'instance creation') ----- > uniqueInstance > + > + ^ UniqueInstance ifNil: [UniqueInstance := super new]! > - ^UniqueInstance ifNil:[super new]! > > From commits at source.squeak.org Thu Sep 3 11:31:29 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 3 Sep 2020 11:31:29 0000 Subject: [squeak-dev] The Trunk: Morphic-mt.1679.mcz Message-ID: Marcel Taeumel uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-mt.1679.mcz ==================== Summary ==================== Name: Morphic-mt.1679 Author: mt Time: 3 September 2020, 1:31:22.442904 pm UUID: 88c94469-c1c1-4781-8e75-6a59d3bd76ee Ancestors: Morphic-LM.1678, Morphic-mt.1675 Merge commits. =============== Diff against Morphic-LM.1678 =============== Item was changed: ----- Method: DialogWindow>>createMessage: (in category 'initialization') ----- createMessage: aString messageMorph := aString asText asMorph. messageMorph name: 'Message'; readOnly: true; setProperty: #indicateKeyboardFocus toValue: #never; + handlesKeyboardOnlyOnFocus: true. "If user presses enter while only hovering the text, we want to process the stroke to close the dialog." - setProperty: #needsClickToFocus toValue: true. self setMessageParameters. ^ messageMorph! Item was changed: ----- Method: Morph>>handleKeyDown: (in category 'events-processing') ----- handleKeyDown: anEvent "System level event handling." + anEvent wasHandled ifTrue: [^ self]. + (self handlesKeyboard: anEvent) ifFalse: [^ self]. + (anEvent hand keyboardFocus ~~ self + and: [self handlesKeyboardOnlyOnFocus]) + ifTrue: [^ self]. + - anEvent wasHandled ifTrue:[^self]. - (self handlesKeyboard: anEvent) ifFalse:[^self]. anEvent wasHandled: true. + ^ self keyDown: anEvent! - ^self keyDown: anEvent! Item was changed: ----- Method: Morph>>handleKeyUp: (in category 'events-processing') ----- handleKeyUp: anEvent "System level event handling." + anEvent wasHandled ifTrue: [^ self]. + (self handlesKeyboard: anEvent) ifFalse: [^ self]. + (anEvent hand keyboardFocus ~~ self + and: [self handlesKeyboardOnlyOnFocus]) + ifTrue: [^ self]. + - anEvent wasHandled ifTrue:[^self]. - (self handlesKeyboard: anEvent) ifFalse:[^self]. anEvent wasHandled: true. + ^ self keyUp: anEvent! - ^self keyUp: anEvent! Item was changed: ----- Method: Morph>>handleKeystroke: (in category 'events-processing') ----- handleKeystroke: anEvent "System level event handling. Has support for automatically grabbing the keyboard focus considering the keyboard focus delegate. See #newKeyboardFocus:" | handler | anEvent wasHandled ifTrue: [^ self]. (self handlesKeyboard: anEvent) ifFalse: [^ self]. + (anEvent hand keyboardFocus ~~ self + and: [self handlesKeyboardOnlyOnFocus]) + ifTrue: [^ self]. handler := self wantsKeyboardFocus ifFalse: [self] ifTrue: [(anEvent hand newKeyboardFocus: self) ifNil: [self]]. anEvent handler: handler. anEvent wasHandled: true. ^ handler keyStroke: anEvent! Item was added: + ----- Method: Morph>>handlesKeyboardOnlyOnFocus (in category 'event handling') ----- + handlesKeyboardOnlyOnFocus + "If set, reject every keyboard event until the receiver has received the keyboard focus in another way, i.e. a mouse click (see #mouseDown:) or programmatic focusing (see HandMorph >> #newKeyboardFocus:). This allows sending keyboard events to any owner of the receiver while the receiver is hovered by the hand. See senders. + A particular user is DialogWindow which looks for Enter and Escape presses and should not loose these events to the content morph unless it is explicitly focused. For the full discussion, see http://forum.world.st/The-Inbox-Morphic-cbc-1665-mcz-td5117905.html." + + ^ self valueOfProperty: #handlesKeyboardOnlyOnFocus ifAbsent: [false]! Item was added: + ----- Method: Morph>>handlesKeyboardOnlyOnFocus: (in category 'event handling') ----- + handlesKeyboardOnlyOnFocus: aBoolean + + ^ self setProperty: #handlesKeyboardOnlyOnFocus toValue: aBoolean! Item was changed: ----- Method: PluggableTextMorph>>mouseEnter: (in category 'event handling') ----- mouseEnter: event "Restore the selection in the text morph if there was a selection." super mouseEnter: event. selectionInterval ifNotNil: [:interval | textMorph editor selectInterval: selectionInterval; setEmphasisHere]. Preferences mouseOverForKeyboardFocus + ifTrue: [event hand newKeyboardFocus: self].! - ifTrue:[event hand newKeyboardFocus: self]! Item was changed: ----- Method: TextMorph>>handleKeystroke: (in category 'events-processing') ----- handleKeystroke: anEvent "Overwritten to support tab-among-fields preference." | pasteUp | anEvent wasHandled ifTrue:[^self]. (self handlesKeyboard: anEvent) ifFalse: [^ self]. + (anEvent hand keyboardFocus ~~ self + and: [self handlesKeyboardOnlyOnFocus]) + ifTrue: [^ self]. anEvent keyCharacter = Character tab ifTrue: [ "Allow passing through text morph inside pasteups" (self wouldAcceptKeyboardFocusUponTab and: [(pasteUp := self pasteUpMorphHandlingTabAmongFields) notNil]) ifTrue: [ anEvent wasHandled: true. ^ pasteUp tabHitWithEvent: anEvent]]. ^ super handleKeystroke: anEvent! Item was changed: ----- Method: TextMorph>>handlesKeyboard: (in category 'event handling') ----- + handlesKeyboard: evt + ^true! - handlesKeyboard: anEvent - - ^ ((self valueOfProperty: #needsClickToFocus ifAbsent: [false]) ==> [ - anEvent hand keyboardFocus = self])! From commits at source.squeak.org Thu Sep 3 12:45:39 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 3 Sep 2020 12:45:39 0000 Subject: [squeak-dev] The Inbox: Tools-ct.987.mcz Message-ID: Christoph Thiede uploaded a new version of Tools to project The Inbox: http://source.squeak.org/inbox/Tools-ct.987.mcz ==================== Summary ==================== Name: Tools-ct.987 Author: ct Time: 3 September 2020, 2:45:32.786083 pm UUID: e07c870e-186e-f54a-9dbc-b766b1cb2bbb Ancestors: Tools-ct.985 Fixes several issues with accepting source in debuggers. Applies overall refactoring to Debugger>>#contents:notifying:. With this version, two concrete regressions are fixed that were introduced with the SistaV1 bytecode set (see [1]): - When compiling a method from a block context and answering subsequent parser notifications, make sure the source code is not lost. A variant of this issue was filed by Eliot (emm) in [2]. - When removing a method from a block context, make sure the stack is unwinded correctly. Further adjustments: - Don't restart the current context if a different selector is changed. - Update contentsSelection correctly without morphic hack. Works now in MVC, again. [1] http://forum.world.st/The-Inbox-Kernel-dtl-1310-mcz-td5113032.html [2] http://forum.world.st/tedious-programming-in-the-debugger-error-needs-fixing-td5109568.html =============== Diff against Tools-ct.985 =============== Item was changed: ----- Method: Debugger>>contents:notifying: (in category 'accessing') ----- contents: aText notifying: aController + "Accept new method source of the selected context." - "The retrieved information has changed and its source must now be updated. - In this case, the retrieved information is the method of the selected context." - | result selector classOfMethod category h ctxt newMethod | - contextStackIndex = 0 ifTrue: - [^false]. - self selectedContext isExecutingBlock ifTrue: - [h := self selectedContext activeHome. - h ifNil: - [self inform: 'Method for block not found on stack, can''t edit and continue'. - ^false]. - (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs) ifFalse: - [^false]. - self resetContext: h changeContents: false. - "N.B. Only reset the contents if the compilation succeeds. If contents are reset - when compilation fails both compiler error message and modifications are lost." - (result := self contents: aText notifying: aController) ifTrue: - [self contentsChanged]. - ^result]. + | selector classOfMethod category ctxt newMethod | + contextStackIndex = 0 ifTrue: [^ false]. + + "First, handle some edge cases" + selector := self selectedClass newParser parseSelector: aText. + "selector isDoIt ifTrue: [ + currentCompiledMethod := self compileDoIt: aText]." + self flag: #todo. "ct: Recompile doIt method *without* creating method litters!! See Compiler>>#evaluateCue:ifFail:." + selector = self selectedMessageName ifFalse: [ + "Different message compiled, delegating to super" + ^ super contents: aText notifying: aController]. + + self selectedContext isExecutingBlock ifTrue: [ + "If we are in a block context, we need to rewind the stack before ." + | home | + home := self selectedContext activeHome. + home ifNil: [ + self inform: 'Method for block not found on stack, can''t edit and continue' translated. + ^ false]. + (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs translated) ifFalse: [ + ^ false]. + + self resetContext: home changeContents: false. + "N.B. Only reset the contents if the compilation succeeds. If contents would be reset when compilation fails, both compiler error message and modifications were lost." + ^ (self contents: aText notifying: aController) + ifTrue: [self contentsChanged]; + yourself]. + classOfMethod := self selectedClass. category := self selectedMessageCategoryName. + + "Do the actual compilation" - selector := self selectedClass newParser parseSelector: aText. - (selector == self selectedMessageName - or: [(self selectedMessageName beginsWith: 'DoIt') - and: [selector numArgs = self selectedMessageName numArgs]]) ifFalse: - [self inform: 'can''t change selector'. - ^false]. selector := classOfMethod + compile: aText + classified: category + notifying: aController. + selector ifNil: [^ false]. "compilation cancelled" + + "Update views" - compile: aText - classified: category - notifying: aController. - selector ifNil: [^false]. "compile cancelled" contents := aText. newMethod := classOfMethod compiledMethodAt: selector. + newMethod isQuick ifTrue: [ + self cutBackExecutionToSenderContext]. - newMethod isQuick ifTrue: - [self cutBackExecutionToSenderContext]. ctxt := interruptedProcess popTo: self selectedContext. ctxt == self selectedContext + ifFalse: [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs translated] + ifTrue: [ + newMethod isQuick ifFalse: [ + interruptedProcess restartTopWith: newMethod. + self stepToStatement]. - ifFalse: - [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs] - ifTrue: - [newMethod isQuick ifFalse: - [interruptedProcess - restartTopWith: newMethod; - stepToSendOrReturn]. contextVariablesInspector object: nil]. self resetContext: ctxt. + + Project current addDeferredUIMessage: [ + self changed: #contentsSelection]. + ^ true! - Smalltalk isMorphic ifTrue: - [Project current world - addAlarm: #changed: - withArguments: #(contentsSelection) - for: self - at: (Time millisecondClockValue + 200)]. - ^true! Item was changed: ----- Method: Debugger>>contextStackIndex:oldContextWas: (in category 'private') ----- contextStackIndex: anInteger oldContextWas: oldContext "Change the context stack index to anInteger, perhaps in response to user selection." | isNewMethod | self saveReceiverInspectorState. self saveContextVariablesInspectorState. contextStackIndex := anInteger. anInteger = 0 ifTrue: [currentCompiledMethod := contents := nil. self changed: #contextStackIndex. self decorateButtons. self contentsChanged. contextVariablesInspector object: nil. receiverInspector context: nil; inspect: self receiver. ^self]. isNewMethod := oldContext isNil + or: [oldContext home method ~= (currentCompiledMethod := self selectedContext home method)]. - or: [oldContext method ~~ (currentCompiledMethod := self selectedContext method)]. isNewMethod ifTrue: [contents := self selectedMessage. self contentsChanged. self pcRange]. self changed: #contextStackIndex. self decorateButtons. contextVariablesInspector object: self selectedContext. self restoreContextVariablesInspectorState. receiverInspector context: self selectedContext; inspect: self receiver. self restoreReceiverInspectorState. isNewMethod ifFalse: [self changed: #contentsSelection]! Item was changed: ----- Method: Debugger>>findCleanHomeBelow: (in category 'context stack (message list)') ----- findCleanHomeBelow: method | dirtyIndex | dirtyIndex := contextStack size + 1. contextStack reverse detect: [:context | dirtyIndex := dirtyIndex - 1. + context home method = method homeMethod]. - context method = method]. ^ dirtyIndex + 1! Item was added: + ----- Method: Debugger>>tallyMenu: (in category 'controls') ----- + tallyMenu: aMenu + + ^ aMenu + "title: 'Tally' translated;" flag: #todo; "ct: Implement on PluggableMenuSpec" + addTranslatedList: #( + ('Tally selection' tallyIt 'evaluate current selection and measure the time') + ('Record send' doRecord 'record next message send')); + yourself! From christoph.thiede at student.hpi.uni-potsdam.de Thu Sep 3 12:50:23 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Thu, 3 Sep 2020 07:50:23 -0500 (CDT) Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> Message-ID: <1599137423163-0.post@n4.nabble.com> Hi Eliot, when you reported this issue, I was not able to reproduce it. About two months later, your new Sista bytecode set has arrived in the Trunk. Today, I encountered a very similar issue, could reproduce it, and trace it back to a comparison issue of Context >> #method which was affected by the introduction of CompiledBlock instances. Is it possible that you already used Sista in your image when reporting this bug? In case you were able to reproduce the bug: Can you confirm whether Tools-ct.987 (inbox) fixes the issue for you? :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 3 13:01:54 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 3 Sep 2020 13:01:54 +0000 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: , Message-ID: Hi Chris, thanks for your feedback! > For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I think this is very much a question of favor - personally, I prefer guard clauses over the functional style unless both code paths have the same relevance for the whole method. In my opinion, an empty message list is clearly a secondary edge case only. :-) > I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Yes, this is not really an intuitive message name ... What do you think about calling it #readLiteral:withHeadingAndStringDo: instead (plus making it public)? Best, Christoph ________________________________ Von: Chris Muller Gesendet: Mittwoch, 2. September 2020 23:59:46 An: squeak dev; Thiede, Christoph Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz Hi Christoph, For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Best, Chris On Wed, Sep 2, 2020 at 9:56 AM wrote: > > Christoph Thiede uploaded a new version of Tools to project The Inbox: > http://source.squeak.org/inbox/Tools-ct.986.mcz > > ==================== Summary ==================== > > Name: Tools-ct.986 > Author: ct > Time: 2 September 2020, 4:55:38.538083 pm > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca > Ancestors: Tools-ct.985 > > Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. > > =============== Diff against Tools-ct.985 =============== > > Item was changed: > ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- > addParentMethodsSending: selectorSymbol > > + ^ self systemNavigation > + headingAndAutoselectForLiteral: selectorSymbol > + do: [:label :autoSelect | > + | methodsList | > + methodsList := self systemNavigation allCallsOn: selectorSymbol. > + methodsList ifEmpty: [ > + ^ self inform: ('There are no {1}' translated format: {label})]. > + self > - | methodsList | > - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty > - ifTrue: > - [ ^(PopUpMenu labels: ' OK ') > - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] > - ifFalse: > - [ self > addParentMessages: methodsList > + autoSelectString: autoSelect] > - autoSelectString: selectorSymbol ] > ! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 3 13:04:17 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 3 Sep 2020 13:04:17 +0000 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: <8A5F154E-0BA8-4D64-8D1C-E844CCB85690@gmx.de> References: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de>, <8A5F154E-0BA8-4D64-8D1C-E844CCB85690@gmx.de> Message-ID: <4cad345e0ba544d7816544ff864fb74d@student.hpi.uni-potsdam.de> Hi Tobias, > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? That was exactly my point. 'foo' asText beAllFont: font1; beAllFont: font2 does not make any sense, of course, but when a text is passed from somewhere else, you may not know which formatting has already been applied to it ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Donnerstag, 3. September 2020 00:13:24 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > On 02.09.2020, at 23:29, Thiede, Christoph wrote: > > Hi Tobias, thanks for the tips! :-) > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > Would you expect such a Text>>#setFont:from:to: only to add a TextFontReference or also to remove all existing TextFontReferences from the interval? > I'd be wary of using #set... because exactly that expectation happens. Vocabulary is cumbersome, tho. see TextMorph's _be_AllFont, Text's _make_Bold and the attributes only ever add. Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? Coalescing the runs would probably take care of chopping up the different font runs, but that's beneath the cover. That said, I liked the Seaside/Magritte-wording with beSomething on their brushes or models respectively… Best regards -Tobias > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Mittwoch, 2. September 2020 22:04:38 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > Hi > > > On 02.09.2020, at 21:14, Thiede, Christoph wrote: > > > > Hi Eric, > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! Seehttp://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html andhttps://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > > > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? > > > > These are good questions others can probably answer better than I could do. > > This is all I can tell you: > > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. > > • To change the font in a Text, use something like: > > 'foo' asText > > addAttribute: (TextFontReference toFont: (StrikeFont > > familyName: 'Darkmap DejaVu Sans' > > pointSize: 20)); > > openAsMorph > > I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: > > 'foo' asText > addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); > openAsMorph > > > If you are just after changing the font used in a TextMorph (not the text itself), I'd use > > 'foo' asTextMorph > beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); > openInHand > > > That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > Best regards > -Tobias > > > > > > > Also -- how can I disable editing of a TextMorph? > > > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. > > > > Best, > > Christoph > > Von: Squeak-dev im Auftrag von Eric Gade > > Gesendet: Mittwoch, 2. September 2020 17:01:45 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > Hi Christoph, > > > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: > > > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text > > > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? > > > > Thanks again -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Thu Sep 3 13:15:31 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 3 Sep 2020 15:15:31 +0200 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: <,> Message-ID: Hi -- #headingAndAutoselectForLiteral:do: is a private helper function to disguise a rather constant value, which is a prefix for a tool's label. It should not be used outside SystemNavigation. That would make things even worse. The basic issue here is that there is no specific object for the concerns "user" or "sender". So, it is tediously derived or forwarded in a functional style. We should find a way to get rid of #headingAndAutoselectForLiteral:do:. That very name indicates that there is something wrong with the overall invocation of SystemNavigation's public protocol. Best, Marcel Am 03.09.2020 15:02:06 schrieb Thiede, Christoph : Hi Chris, thanks for your feedback! > For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I think this is very much a question of favor - personally, I prefer guard clauses over the functional style unless both code paths have the same relevance for the whole method. In my opinion, an empty message list is clearly a secondary edge case only. :-) > I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Yes, this is not really an intuitive message name ... What do you think about calling it #readLiteral:withHeadingAndStringDo: instead (plus making it public)? Best, Christoph Von: Chris Muller Gesendet: Mittwoch, 2. September 2020 23:59:46 An: squeak dev; Thiede, Christoph Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz   Hi Christoph, For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Best,   Chris On Wed, Sep 2, 2020 at 9:56 AM wrote: > > Christoph Thiede uploaded a new version of Tools to project The Inbox: > http://source.squeak.org/inbox/Tools-ct.986.mcz [http://source.squeak.org/inbox/Tools-ct.986.mcz] > > ==================== Summary ==================== > > Name: Tools-ct.986 > Author: ct > Time: 2 September 2020, 4:55:38.538083 pm > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca > Ancestors: Tools-ct.985 > > Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. > > =============== Diff against Tools-ct.985 =============== > > Item was changed: >   ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- >   addParentMethodsSending: selectorSymbol > > +       ^ self systemNavigation > +               headingAndAutoselectForLiteral: selectorSymbol > +               do: [:label :autoSelect | > +                       | methodsList | > +                       methodsList := self systemNavigation allCallsOn: selectorSymbol. > +                       methodsList ifEmpty: [ > +                               ^ self inform: ('There are no {1}' translated format: {label})]. > +                       self > -       | methodsList | > -       (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty > -               ifTrue: > -                       [ ^(PopUpMenu labels: ' OK ') > -                               startUpWithCaption: 'There are no methods that send ', selectorSymbol ] > -               ifFalse: > -                       [ self >                                 addParentMessages: methodsList > +                               autoSelectString: autoSelect] > -                               autoSelectString: selectorSymbol ] >   ! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Thu Sep 3 13:53:26 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Thu, 3 Sep 2020 15:53:26 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: <4cad345e0ba544d7816544ff864fb74d@student.hpi.uni-potsdam.de> References: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de> <8A5F154E-0BA8-4D64-8D1C-E844CCB85690@gmx.de> <4cad345e0ba544d7816544ff864fb74d@student.hpi.uni-potsdam.de> Message-ID: <5662556D-AA81-46B1-850B-CD6E89BCAF42@gmx.de> Hi > On 03.09.2020, at 15:04, Thiede, Christoph wrote: > > Hi Tobias, > > > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? > > That was exactly my point. 'foo' asText beAllFont: font1; beAllFont: font2 does not make any sense, of course, but when a text is passed from somewhere else, you may not know which formatting has already been applied to it ... That is true. But if you want to change the font, there's only so much you can do. If you want just change an upright font to an italic one, the FontReference will not work in any case. The TextFontChange attribute is seemingly intended for that, but it only understands "Font array indices" which is most unhelpful :D Best regards -Tobias > > Best, > Christoph > > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Donnerstag, 3. September 2020 00:13:24 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > On 02.09.2020, at 23:29, Thiede, Christoph wrote: > > > > Hi Tobias, thanks for the tips! :-) > > > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > > Would you expect such a Text>>#setFont:from:to: only to add a TextFontReference or also to remove all existing TextFontReferences from the interval? > > > > I'd be wary of using #set... because exactly that expectation happens. > Vocabulary is cumbersome, tho. > see TextMorph's _be_AllFont, Text's _make_Bold and the attributes only ever add. > > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? > Coalescing the runs would probably take care of chopping up the different font runs, but that's beneath the cover. > > That said, I liked the Seaside/Magritte-wording with beSomething on their brushes or models respectively… > > Best regards > -Tobias > > > Best, > > Christoph > > Von: Squeak-dev im Auftrag von Tobias Pape > > Gesendet: Mittwoch, 2. September 2020 22:04:38 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > Hi > > > > > On 02.09.2020, at 21:14, Thiede, Christoph wrote: > > > > > > Hi Eric, > > > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! Seehttp://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html andhttps://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > > > > > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? > > > > > > These are good questions others can probably answer better than I could do. > > > This is all I can tell you: > > > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. > > > • To change the font in a Text, use something like: > > > 'foo' asText > > > addAttribute: (TextFontReference toFont: (StrikeFont > > > familyName: 'Darkmap DejaVu Sans' > > > pointSize: 20)); > > > openAsMorph > > > > I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: > > > > 'foo' asText > > addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); > > openAsMorph > > > > > > If you are just after changing the font used in a TextMorph (not the text itself), I'd use > > > > 'foo' asTextMorph > > beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); > > openInHand > > > > > > That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool > > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > > > > Best regards > > -Tobias > > > > > > > > > > > Also -- how can I disable editing of a TextMorph? > > > > > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. > > > > > > Best, > > > Christoph > > > Von: Squeak-dev im Auftrag von Eric Gade > > > Gesendet: Mittwoch, 2. September 2020 17:01:45 > > > An: The general-purpose Squeak developers list > > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > > > Hi Christoph, > > > > > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: > > > > > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. > > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text > > > > > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? > > > > > > Thanks again From marcel.taeumel at hpi.de Thu Sep 3 13:56:49 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 3 Sep 2020 15:56:49 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: <5662556D-AA81-46B1-850B-CD6E89BCAF42@gmx.de> References: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de> <8A5F154E-0BA8-4D64-8D1C-E844CCB85690@gmx.de> <4cad345e0ba544d7816544ff864fb74d@student.hpi.uni-potsdam.de> <5662556D-AA81-46B1-850B-CD6E89BCAF42@gmx.de> Message-ID: Plus, TextFontReference is a rather fixed pointer to a specific font and size, which breaks when using Squeak's notion of High-DPI with bigger fonts in general. TextFontChange works better with its index. And so does TextEmphasis for italic. Best, Marcel Am 03.09.2020 15:53:35 schrieb Tobias Pape : Hi > On 03.09.2020, at 15:04, Thiede, Christoph wrote: > > Hi Tobias, > > > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? > > That was exactly my point. 'foo' asText beAllFont: font1; beAllFont: font2 does not make any sense, of course, but when a text is passed from somewhere else, you may not know which formatting has already been applied to it ... That is true. But if you want to change the font, there's only so much you can do. If you want just change an upright font to an italic one, the FontReference will not work in any case. The TextFontChange attribute is seemingly intended for that, but it only understands "Font array indices" which is most unhelpful :D Best regards -Tobias > > Best, > Christoph > > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Donnerstag, 3. September 2020 00:13:24 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > On 02.09.2020, at 23:29, Thiede, Christoph wrote: > > > > Hi Tobias, thanks for the tips! :-) > > > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > > Would you expect such a Text>>#setFont:from:to: only to add a TextFontReference or also to remove all existing TextFontReferences from the interval? > > > > I'd be wary of using #set... because exactly that expectation happens. > Vocabulary is cumbersome, tho. > see TextMorph's _be_AllFont, Text's _make_Bold and the attributes only ever add. > > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? > Coalescing the runs would probably take care of chopping up the different font runs, but that's beneath the cover. > > That said, I liked the Seaside/Magritte-wording with beSomething on their brushes or models respectively… > > Best regards > -Tobias > > > Best, > > Christoph > > Von: Squeak-dev im Auftrag von Tobias Pape > > Gesendet: Mittwoch, 2. September 2020 22:04:38 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > Hi > > > > > On 02.09.2020, at 21:14, Thiede, Christoph wrote: > > > > > > Hi Eric, > > > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! Seehttp://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html andhttps://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > > > > > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? > > > > > > These are good questions others can probably answer better than I could do. > > > This is all I can tell you: > > > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. > > > • To change the font in a Text, use something like: > > > 'foo' asText > > > addAttribute: (TextFontReference toFont: (StrikeFont > > > familyName: 'Darkmap DejaVu Sans' > > > pointSize: 20)); > > > openAsMorph > > > > I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: > > > > 'foo' asText > > addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); > > openAsMorph > > > > > > If you are just after changing the font used in a TextMorph (not the text itself), I'd use > > > > 'foo' asTextMorph > > beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); > > openInHand > > > > > > That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool > > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > > > > Best regards > > -Tobias > > > > > > > > > > > Also -- how can I disable editing of a TextMorph? > > > > > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. > > > > > > Best, > > > Christoph > > > Von: Squeak-dev im Auftrag von Eric Gade > > > Gesendet: Mittwoch, 2. September 2020 17:01:45 > > > An: The general-purpose Squeak developers list > > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > > > Hi Christoph, > > > > > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: > > > > > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. > > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text > > > > > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? > > > > > > Thanks again -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Fri Sep 4 09:58:11 2020 From: gettimothy at zoho.com (gettimothy) Date: Fri, 04 Sep 2020 05:58:11 -0400 Subject: [squeak-dev] morhpic copy the max/min buttons to bottom of the browser. Message-ID: <174588d54fe.d863155914165.5390894482462793606@zoho.com> Hi all, In my work, I like to 'adhere to edge' on many open browsers. I have a browser adhered to the bottom right of the world. to explain further, the browser is minimized at bottom-right edge. the max /min buttons are aligned in the minimized browser along the bottom. I click the button, and the browser is un-minimized but now I have to move my mouse all the way up to where they moved to. (problems of the future, I know.....poor tty has to move that heavy mouse all of several inches, oh!  the humanity!) I thought it would be handy to copy the min-max buttons on the browser to the bottom of the browser so I could do a quick max/min without having to move my mouse after the operation. the copy works fine, but, after the first click, the buttons get hidden behind the browser and stuck to the world. any Morphic geniuses have a suggestion on how to get the copied buttons to adhere to the little status-bar-doohickey along the bottom of the browser? thanks in advance -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Fri Sep 4 11:29:27 2020 From: gettimothy at zoho.com (gettimothy) Date: Fri, 04 Sep 2020 07:29:27 -0400 Subject: [squeak-dev] I just purchased squeakbooks.com and .org Message-ID: <17458e0e4f9.106e12a8315071.1205817659484112188@zoho.com> Hi folks, I am going to try to recreate books.pharo.org using those domains, but for Squeak as part of my Doc/SeasideDoc project. I am NOT trying to make money for myself, I am just trying to solve the Squeak documentation problem. I will be building the site with Seaside on Squeak and I will be studying the Pharo model for their documentation creation to see if it is appropriate for squeak. Motivation:  http://books.pharo.org/booklet-Scraping/html/scrapingbook.html That is an example of the type of documentation I frequently use as a developer. How do I accomplish a specific task, where do I start, what are the conceptual "things" I have to think in. I will be using that documentation to get myself familiarized with the Squeak XML stuff as I will need it for processing xml documents of 10's of millions of records. I will be happy to give the domains to the squeak community when/if the project is solid. Once the stubbed website is up, I will attempt to create a live-round-robin model from the squeak image. Open a workspace, open a book (book content comes down from website) add/edit content within the workspace, save it live to the website (and concurrently to the image ?) Hopefully it will be useful and we can start crowdsourcing documentation. cheers, tty.   -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Fri Sep 4 13:24:06 2020 From: karlramberg at gmail.com (karl ramberg) Date: Fri, 4 Sep 2020 15:24:06 +0200 Subject: [squeak-dev] I just purchased squeakbooks.com and .org In-Reply-To: <17458e0e4f9.106e12a8315071.1205817659484112188@zoho.com> References: <17458e0e4f9.106e12a8315071.1205817659484112188@zoho.com> Message-ID: On Fri, Sep 4, 2020 at 1:29 PM gettimothy via Squeak-dev < squeak-dev at lists.squeakfoundation.org> wrote: > Hi folks, > > I am going to try to recreate books.pharo.org using those domains, but > for Squeak as part of my Doc/SeasideDoc project. > > I am NOT trying to make money for myself, I am just trying to solve the > Squeak documentation problem. > > I will be building the site with Seaside on Squeak and I will be studying > the Pharo model for their documentation creation to see if it is > appropriate for squeak. > > Motivation: > http://books.pharo.org/booklet-Scraping/html/scrapingbook.html > > That is an example of the type of documentation I frequently use as a > developer. How do I accomplish a specific task, where do I start, what are > the conceptual "things" I have to think in. > > I will be using that documentation to get myself familiarized with the > Squeak XML stuff as I will need it for processing xml documents of 10's of > millions of records. > > I will be happy to give the domains to the squeak community when/if the > project is solid. > > Once the stubbed website is up, I will attempt to create a > live-round-robin model from the squeak image. > > Open a workspace, open a book (book content comes down from website) > add/edit content within the workspace, save it live to the website (and > concurrently to the image ?) > It would be a really nice addition to be able to write "how to's" and documentation in an almost literal coding style within Squeak. The HelpBrowser has the ability to pull down content from the Squeak Wiki. And we have added inlining pictures in it: http://forum.world.st/The-Trunk-MonticelloConfigurations-mt-160-mcz-td5115112.html#a5116431 But the HelpBrowser is read only at the moment. (Some skeleton methods exist for writing documentation) Best, Karl > Hopefully it will be useful and we can start crowdsourcing documentation. > > > > cheers, > > tty. > > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 4 13:49:13 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 4 Sep 2020 13:49:13 +0000 Subject: [squeak-dev] I just purchased squeakbooks.com and .org In-Reply-To: References: <17458e0e4f9.106e12a8315071.1205817659484112188@zoho.com>, Message-ID: > But the HelpBrowser is read only at the moment. (Some skeleton methods exist for writing documentation) I uploaded a lot of stuff concerning this to the inbox last year. Unless you want to achieve something very fancy, you do not need to descend into the System Browser any longer. Creating, removing, renaming, and editing topics is all possible via the HelpBrowser. Moreover, with Morphic-ct.1587, you can even insert images without writing code. :-) The changes are still waiting for anyone who has some time to review and eventually merge them. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Freitag, 4. September 2020 15:24:06 An: gettimothy; The general-purpose Squeak developers list Betreff: Re: [squeak-dev] I just purchased squeakbooks.com and .org On Fri, Sep 4, 2020 at 1:29 PM gettimothy via Squeak-dev > wrote: Hi folks, I am going to try to recreate books.pharo.org using those domains, but for Squeak as part of my Doc/SeasideDoc project. I am NOT trying to make money for myself, I am just trying to solve the Squeak documentation problem. I will be building the site with Seaside on Squeak and I will be studying the Pharo model for their documentation creation to see if it is appropriate for squeak. Motivation: http://books.pharo.org/booklet-Scraping/html/scrapingbook.html That is an example of the type of documentation I frequently use as a developer. How do I accomplish a specific task, where do I start, what are the conceptual "things" I have to think in. I will be using that documentation to get myself familiarized with the Squeak XML stuff as I will need it for processing xml documents of 10's of millions of records. I will be happy to give the domains to the squeak community when/if the project is solid. Once the stubbed website is up, I will attempt to create a live-round-robin model from the squeak image. Open a workspace, open a book (book content comes down from website) add/edit content within the workspace, save it live to the website (and concurrently to the image ?) It would be a really nice addition to be able to write "how to's" and documentation in an almost literal coding style within Squeak. The HelpBrowser has the ability to pull down content from the Squeak Wiki. And we have added inlining pictures in it: http://forum.world.st/The-Trunk-MonticelloConfigurations-mt-160-mcz-td5115112.html#a5116431 But the HelpBrowser is read only at the moment. (Some skeleton methods exist for writing documentation) Best, Karl Hopefully it will be useful and we can start crowdsourcing documentation. cheers, tty. -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Fri Sep 4 14:54:38 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 4 Sep 2020 14:54:38 0000 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz Message-ID: A new version of Chronology-Core was added to project The Inbox: http://source.squeak.org/inbox/Chronology-Core-dtl.56.mcz ==================== Summary ==================== Name: Chronology-Core-dtl.56 Author: dtl Time: 4 September 2020, 10:54:37.509663 am UUID: a33e5fab-940e-41ed-b05c-76f8ff54f5ee Ancestors: Chronology-Core-ul.55 Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. =============== Diff against Chronology-Core-ul.55 =============== Item was removed: - SharedPool subclass: #ChronologyConstants - instanceVariableNames: '' - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' - poolDictionaries: '' - category: 'Chronology-Core'! - - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! Item was removed: - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- - initialize - "ChronologyConstants initialize" - - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" - SecondsInDay := 86400. - SecondsInHour := 3600. - SecondsInMinute := 60. - MicrosecondsInDay := 24 * 60 * 60 * 1000000. - NanosInSecond := 10 raisedTo: 9. - NanosInMillisecond := 10 raisedTo: 6. - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). - - MonthNames := #( January February March April May June - July August September October November December). - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! Item was changed: Timespan subclass: #Date instanceVariableNames: '' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! Item was changed: ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- fromDays: dayCount "Days since 1 January 1901" + ^ self julianDayNumber: dayCount + Time squeakEpoch! - ^ self julianDayNumber: dayCount + SqueakEpoch! Item was changed: ----- Method: Date class>>starting: (in category 'squeak protocol') ----- starting: aDateAndTime ^ self starting: aDateAndTime midnight + duration: 1 day! - duration: Duration oneDay! Item was changed: Magnitude subclass: #DateAndTime instanceVariableNames: 'utcMicroseconds localOffsetSeconds' classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! I represent a point in UTC time as defined by ISO 8601. I have zero duration. My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. ! Item was changed: ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- epochOffset "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! Item was changed: ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- posixEpochJulianDays + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! Item was changed: ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- ticks: ticks offset: utcOffset "ticks is {julianDayNumber. secondCount. nanoSeconds}" | jdn s nanos normalizedTicks | normalizedTicks := ticks copy. + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. jdn := normalizedTicks at: 1. s := normalizedTicks at: 2. nanos := normalizedTicks at: 3. localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. ! Item was changed: Magnitude subclass: #Duration instanceVariableNames: 'nanos seconds' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! I represent a duration of time. I have nanosecond precision! Item was changed: ----- Method: Duration class>>days: (in category 'squeak protocol') ----- days: aNumber + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! Item was changed: ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos ^self seconds: seconds + + (minutes * Time secondsInMinute) + + (hours * Time secondsInHour) + + (days * Time secondsInDay) - + (minutes * SecondsInMinute) - + (hours * SecondsInHour) - + (days * SecondsInDay) nanoSeconds: nanos ! Item was changed: ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- days: days seconds: seconds + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 - ! Item was changed: ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- hours: aNumber + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! Item was removed: - ----- Method: Duration class>>initialize (in category 'initialize-release') ----- - initialize - ChronologyConstants classPool - at: #Zero - put: - (self basicNew - seconds: 0 - nanoSeconds: 0) ; - at: #OneDay - put: 1 day! Item was changed: ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- milliSeconds: milliCount ^self seconds: (milliCount quo: 1000) + nanoSeconds: (milliCount rem: 1000) * 1000000! - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! Item was changed: ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- minutes: aNumber + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! Item was changed: ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- nanoSeconds: nanos "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." | quo | + quo _ nanos quo: Time nanosInSecond. - quo := nanos quo: NanosInSecond. ^ self basicNew seconds: quo + nanoSeconds: nanos - (quo * Time nanosInSecond)! - nanoSeconds: nanos - (quo * NanosInSecond) - ! Item was changed: ----- Method: Duration class>>oneDay (in category 'squeak protocol') ----- oneDay "Answer the canonicalized Duration representing length of 1 day. Used by Dates." + ^ 1 day! - ^ OneDay! Item was changed: ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- seconds: seconds nanoSeconds: nanos ^ self basicNew seconds: seconds truncated + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! Item was changed: ----- Method: Duration class>>zero (in category 'ansi protocol') ----- zero + + ^ self basicNew seconds: 0 nanoSeconds: 0 + ! - "Answer the canonicalized instance of Duration zero." - ^ Zero! Item was changed: ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- asNanoSeconds + ^seconds * Time nanosInSecond + nanos! - ^seconds * NanosInSecond + nanos! Item was changed: ----- Method: Duration>>days (in category 'ansi protocol') ----- days "Answer the number of days the receiver represents." + ^ seconds quo: Time secondsInDay! - ^ seconds quo: SecondsInDay - ! Item was changed: ----- Method: Duration>>hours (in category 'ansi protocol') ----- hours "Answer the number of hours the receiver represents." + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! Item was changed: ----- Method: Duration>>minutes (in category 'ansi protocol') ----- minutes - "Answer the number of minutes the receiver represents." + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! - - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! Item was changed: ----- Method: Duration>>seconds (in category 'ansi protocol') ----- seconds "Answer the number of seconds the receiver represents." + ^seconds rem: Time secondsInMinute! - ^seconds rem: SecondsInMinute! Item was changed: ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- seconds: secondCount nanoSeconds: nanoCount "Private - only used by Duration class" seconds := secondCount. nanos := nanoCount rounded. "normalize if signs do not match" [ nanos < 0 and: [ seconds > 0 ] ] whileTrue: [ seconds := seconds - 1. + nanos := nanos + Time nanosInSecond ]. - nanos := nanos + NanosInSecond ]. [ seconds < 0 and: [ nanos > 0 ] ] whileTrue: [ seconds := seconds + 1. + nanos := nanos - Time nanosInSecond ] - nanos := nanos - NanosInSecond ] ! Item was changed: ----- Method: Duration>>ticks (in category 'private') ----- ticks "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." | days | days := self days. ^ Array with: days + with: seconds - (days * Time secondsInDay) - with: seconds - (days * SecondsInDay) with: nanos ! Item was changed: Timespan subclass: #Month instanceVariableNames: '' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! I represent a month. For example, to get the number of days this month, you can evaluate the following expression: Month current daysInMonth! Item was added: + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- + daysInMonth + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! Item was changed: ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- daysInMonth: indexOrName forYear: yearInteger | index | index := indexOrName isInteger ifTrue: [indexOrName] ifFalse: [self indexOfMonth: indexOrName]. + ^ (self daysInMonth at: index) - ^ (DaysInMonth at: index) + ((index = 2 and: [Year isLeapYear: yearInteger]) ifTrue: [1] ifFalse: [0]) ! Item was changed: ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- indexOfMonth: aMonthName + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. + self error: aMonthName , ' is not a recognized month name'! - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. - self error: aMonthName , ' is not a recognized month name'.! Item was added: + ----- Method: Month class>>monthNames (in category 'inquiries') ----- + monthNames + ^#(January February March April May June July August September October November December)! Item was changed: ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- nameOfMonth: anIndex + ^ self monthNames at: anIndex! - ^ MonthNames at: anIndex.! Item was changed: Magnitude subclass: #Time instanceVariableNames: 'seconds nanos' classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! This represents a particular point in time during any given day. For example, '5:19:45 pm'. If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. ! Item was changed: ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- fromSeconds: secondCount "Answer an instance of me that is secondCount number of seconds since midnight." | integerSeconds nanos | integerSeconds := secondCount truncated. integerSeconds = secondCount ifTrue: [nanos := 0] + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. ^ self seconds: integerSeconds nanoSeconds: nanos ! Item was changed: ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- + hour: hour minute: minute second: second nanoSecond: nanoCount + "Answer a Time" - hour: hour minute: minute second: second nanoSecond: nanoCount - "Answer a Time - only second precision for now" ^ self + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second + nanoSeconds: nanoCount! - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second - nanoSeconds: nanoCount - ! Item was added: + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- + nanosInSecond + ^ 1000000000! Item was changed: ----- Method: Time class>>noon (in category 'squeak protocol') ----- noon + ^ self seconds: self secondsInDay / 2! - ^ self seconds: (SecondsInDay / 2) - ! Item was changed: ----- Method: Time class>>now (in category 'ansi protocol') ----- now "Answer a Time representing the time right now - this is a 24 hour clock." | localUsecs localUsecsToday | localUsecs := self localMicrosecondClock. + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" - localUsecsToday := localUsecs \\ MicrosecondsInDay. ^ self seconds: localUsecsToday // 1000000 nanoSeconds: localUsecsToday \\ 1000000 * 1000! Item was added: + ----- Method: Time class>>secondsInDay (in category 'constants') ----- + secondsInDay + ^86400! Item was added: + ----- Method: Time class>>secondsInHour (in category 'constants') ----- + secondsInHour + ^3600! Item was added: + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- + secondsInMinute + ^60! Item was added: + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- + squeakEpoch + ^ 2415386. "Julian day number of 1 Jan 1901"! Item was changed: ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" | h m s | h := self hour. m := self minute. s := self second. hr24 ifTrue: [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. h printOn: aStream ] ifFalse: [ h > 12 ifTrue: [h - 12 printOn: aStream] ifFalse: [h < 1 ifTrue: [ 12 printOn: aStream ] ifFalse: [ h printOn: aStream ]]]. aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). m printOn: aStream. showSeconds ifTrue: [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). (showSubseconds not or: [self nanoSecond = 0]) ifTrue: [s asInteger printOn: aStream] + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger + printOn: aStream asFixedPoint: Time nanosInSecond]]. - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger - printOn: aStream asFixedPoint: NanosInSecond]]. hr24 ifFalse: [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. ! Item was changed: Object subclass: #TimeZone instanceVariableNames: 'offset abbreviation name' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! TimeZone is a simple class to colect the information identifying a UTC time zone. offset - Duration - the time zone's offset from UTC abbreviation - String - the abbreviated name for the time zone. name - String - the name of the time zone. TimeZone class >> #timeZones returns an array of the known time zones TimeZone class >> #default returns the default time zone (Grenwich Mean Time) ! Item was changed: Timespan subclass: #Week instanceVariableNames: '' classVariableNames: 'StartDay' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! I represent a week. To find out what days of the week on which Squeak is fun, select the following expression, and print it: Week dayNames! Item was changed: + ----- Method: Week class>>dayNames (in category 'inquiries') ----- - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- dayNames + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! - ^ DayNames! Item was changed: ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- indexOfDay: aSymbol + ^ self dayNames indexOf: aSymbol! - ^ DayNames indexOf: aSymbol! Item was changed: ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- nameOfDay: anIndex + ^ self dayNames at: anIndex! - ^ DayNames at: anIndex! Item was changed: ----- Method: Week class>>startDay (in category 'squeak protocol') ----- startDay ^ StartDay ifNil: [ StartDay + := self dayNames first ] - := DayNames first ] ! Item was changed: ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- startDay: aSymbol + (self dayNames includes: aSymbol) - (DayNames includes: aSymbol) ifTrue: [ StartDay := aSymbol ] ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! Item was changed: ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- starting: aDateAndTime duration: aDuration "Override - the duration is always one week. Week will start from the Week class>>startDay" | midnight delta adjusted | midnight := aDateAndTime asDateAndTime midnight. + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. adjusted := midnight - (Duration days: delta seconds: 0). ^ super starting: adjusted duration: (Duration weeks: 1)! From lewis at mail.msen.com Fri Sep 4 15:00:58 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Fri, 4 Sep 2020 11:00:58 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz In-Reply-To: References: Message-ID: <20200904150058.GA11481@shell.msen.com> I have been looking at Chronology in Cuis, as I would like to be able to contribute Squeak's UTC style DateAndTime for consideration in Cuis. Juan has done some cleanups that simplify Chronology by getting rid of an unnecessary shared pool for constants. I think this is a good improvement so I put it in the inbox here for review. To me this seems cleaner and simpler. All tests still pass of course. Dave On Fri, Sep 04, 2020 at 02:54:38PM +0000, commits at source.squeak.org wrote: > A new version of Chronology-Core was added to project The Inbox: > http://source.squeak.org/inbox/Chronology-Core-dtl.56.mcz > > ==================== Summary ==================== > > Name: Chronology-Core-dtl.56 > Author: dtl > Time: 4 September 2020, 10:54:37.509663 am > UUID: a33e5fab-940e-41ed-b05c-76f8ff54f5ee > Ancestors: Chronology-Core-ul.55 > > Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. > > =============== Diff against Chronology-Core-ul.55 =============== > > Item was removed: > - SharedPool subclass: #ChronologyConstants > - instanceVariableNames: '' > - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' > - poolDictionaries: '' > - category: 'Chronology-Core'! > - > - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! > - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! > > Item was removed: > - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- > - initialize > - "ChronologyConstants initialize" > - > - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" > - SecondsInDay := 86400. > - SecondsInHour := 3600. > - SecondsInMinute := 60. > - MicrosecondsInDay := 24 * 60 * 60 * 1000000. > - NanosInSecond := 10 raisedTo: 9. > - NanosInMillisecond := 10 raisedTo: 6. > - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). > - > - MonthNames := #( January February March April May June > - July August September October November December). > - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! > > Item was changed: > Timespan subclass: #Date > instanceVariableNames: '' > classVariableNames: '' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! > Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. > > However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! > > Item was changed: > ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- > fromDays: dayCount > "Days since 1 January 1901" > > + ^ self julianDayNumber: dayCount + Time squeakEpoch! > - ^ self julianDayNumber: dayCount + SqueakEpoch! > > Item was changed: > ----- Method: Date class>>starting: (in category 'squeak protocol') ----- > starting: aDateAndTime > ^ self > starting: aDateAndTime midnight > + duration: 1 day! > - duration: Duration oneDay! > > Item was changed: > Magnitude subclass: #DateAndTime > instanceVariableNames: 'utcMicroseconds localOffsetSeconds' > classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! > I represent a point in UTC time as defined by ISO 8601. I have zero duration. > > My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. > > The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. > > DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. > ! > > Item was changed: > ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- > epochOffset > "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" > + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! > - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! > > Item was changed: > ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- > posixEpochJulianDays > > + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! > - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! > > Item was changed: > ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- > ticks: ticks offset: utcOffset > "ticks is {julianDayNumber. secondCount. nanoSeconds}" > > | jdn s nanos normalizedTicks | > normalizedTicks := ticks copy. > + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. > + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. > - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. > - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. > > jdn := normalizedTicks at: 1. > s := normalizedTicks at: 2. > nanos := normalizedTicks at: 3. > localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. > utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. > ! > > Item was changed: > Magnitude subclass: #Duration > instanceVariableNames: 'nanos seconds' > classVariableNames: '' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > I represent a duration of time. I have nanosecond precision! > > Item was changed: > ----- Method: Duration class>>days: (in category 'squeak protocol') ----- > days: aNumber > > + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! > - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! > > Item was changed: > ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- > days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos > > ^self > seconds: seconds > + + (minutes * Time secondsInMinute) > + + (hours * Time secondsInHour) > + + (days * Time secondsInDay) > - + (minutes * SecondsInMinute) > - + (hours * SecondsInHour) > - + (days * SecondsInDay) > nanoSeconds: nanos > ! > > Item was changed: > ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- > days: days seconds: seconds > > + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! > - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 > - ! > > Item was changed: > ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- > hours: aNumber > > + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! > - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! > > Item was removed: > - ----- Method: Duration class>>initialize (in category 'initialize-release') ----- > - initialize > - ChronologyConstants classPool > - at: #Zero > - put: > - (self basicNew > - seconds: 0 > - nanoSeconds: 0) ; > - at: #OneDay > - put: 1 day! > > Item was changed: > ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- > milliSeconds: milliCount > > ^self > seconds: (milliCount quo: 1000) > + nanoSeconds: (milliCount rem: 1000) * 1000000! > - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! > > Item was changed: > ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- > minutes: aNumber > > + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! > - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! > > Item was changed: > ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- > nanoSeconds: nanos > "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." > > | quo | > + quo _ nanos quo: Time nanosInSecond. > - quo := nanos quo: NanosInSecond. > ^ self basicNew > seconds: quo > + nanoSeconds: nanos - (quo * Time nanosInSecond)! > - nanoSeconds: nanos - (quo * NanosInSecond) > - ! > > Item was changed: > ----- Method: Duration class>>oneDay (in category 'squeak protocol') ----- > oneDay > "Answer the canonicalized Duration representing length of 1 day. Used by Dates." > + ^ 1 day! > - ^ OneDay! > > Item was changed: > ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- > seconds: seconds nanoSeconds: nanos > > ^ self basicNew > seconds: seconds truncated > + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! > - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! > > Item was changed: > ----- Method: Duration class>>zero (in category 'ansi protocol') ----- > zero > + > + ^ self basicNew seconds: 0 nanoSeconds: 0 > + ! > - "Answer the canonicalized instance of Duration zero." > - ^ Zero! > > Item was changed: > ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- > asNanoSeconds > > + ^seconds * Time nanosInSecond + nanos! > - ^seconds * NanosInSecond + nanos! > > Item was changed: > ----- Method: Duration>>days (in category 'ansi protocol') ----- > days > "Answer the number of days the receiver represents." > > + ^ seconds quo: Time secondsInDay! > - ^ seconds quo: SecondsInDay > - ! > > Item was changed: > ----- Method: Duration>>hours (in category 'ansi protocol') ----- > hours > "Answer the number of hours the receiver represents." > > > + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! > - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! > > Item was changed: > ----- Method: Duration>>minutes (in category 'ansi protocol') ----- > minutes > - > "Answer the number of minutes the receiver represents." > > + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! > - > - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! > > Item was changed: > ----- Method: Duration>>seconds (in category 'ansi protocol') ----- > seconds > "Answer the number of seconds the receiver represents." > > + ^seconds rem: Time secondsInMinute! > - ^seconds rem: SecondsInMinute! > > Item was changed: > ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- > seconds: secondCount nanoSeconds: nanoCount > "Private - only used by Duration class" > > seconds := secondCount. > nanos := nanoCount rounded. > "normalize if signs do not match" > [ nanos < 0 and: [ seconds > 0 ] ] > whileTrue: [ seconds := seconds - 1. > + nanos := nanos + Time nanosInSecond ]. > - nanos := nanos + NanosInSecond ]. > [ seconds < 0 and: [ nanos > 0 ] ] > whileTrue: [ seconds := seconds + 1. > + nanos := nanos - Time nanosInSecond ] > - nanos := nanos - NanosInSecond ] > > ! > > Item was changed: > ----- Method: Duration>>ticks (in category 'private') ----- > ticks > "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." > > | days | > days := self days. > ^ Array > with: days > + with: seconds - (days * Time secondsInDay) > - with: seconds - (days * SecondsInDay) > with: nanos > ! > > Item was changed: > Timespan subclass: #Month > instanceVariableNames: '' > classVariableNames: '' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > I represent a month. > > For example, to get the number of days this month, you can evaluate the following expression: > > Month current daysInMonth! > > Item was added: > + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- > + daysInMonth > + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! > > Item was changed: > ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- > daysInMonth: indexOrName forYear: yearInteger > > | index | > index := indexOrName isInteger > ifTrue: [indexOrName] > ifFalse: [self indexOfMonth: indexOrName]. > + ^ (self daysInMonth at: index) > - ^ (DaysInMonth at: index) > + ((index = 2 > and: [Year isLeapYear: yearInteger]) > ifTrue: [1] ifFalse: [0]) > ! > > Item was changed: > ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- > indexOfMonth: aMonthName > > > + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. > + self error: aMonthName , ' is not a recognized month name'! > - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. > - self error: aMonthName , ' is not a recognized month name'.! > > Item was added: > + ----- Method: Month class>>monthNames (in category 'inquiries') ----- > + monthNames > + ^#(January February March April May June July August September October November December)! > > Item was changed: > ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- > nameOfMonth: anIndex > > + ^ self monthNames at: anIndex! > - ^ MonthNames at: anIndex.! > > Item was changed: > Magnitude subclass: #Time > instanceVariableNames: 'seconds nanos' > classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! > This represents a particular point in time during any given day. For example, '5:19:45 pm'. > > If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. > ! > > Item was changed: > ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- > fromSeconds: secondCount > "Answer an instance of me that is secondCount number of seconds since midnight." > > | integerSeconds nanos | > integerSeconds := secondCount truncated. > integerSeconds = secondCount > ifTrue: [nanos := 0] > + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. > - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. > ^ self seconds: integerSeconds nanoSeconds: nanos > ! > > Item was changed: > ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- > + hour: hour minute: minute second: second nanoSecond: nanoCount > + "Answer a Time" > - hour: hour minute: minute second: second nanoSecond: nanoCount > - "Answer a Time - only second precision for now" > > ^ self > + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second > + nanoSeconds: nanoCount! > - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second > - nanoSeconds: nanoCount > - ! > > Item was added: > + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- > + nanosInSecond > + ^ 1000000000! > > Item was changed: > ----- Method: Time class>>noon (in category 'squeak protocol') ----- > noon > > + ^ self seconds: self secondsInDay / 2! > - ^ self seconds: (SecondsInDay / 2) > - ! > > Item was changed: > ----- Method: Time class>>now (in category 'ansi protocol') ----- > now > "Answer a Time representing the time right now - this is a 24 hour clock." > | localUsecs localUsecsToday | > localUsecs := self localMicrosecondClock. > + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" > - localUsecsToday := localUsecs \\ MicrosecondsInDay. > ^ self > seconds: localUsecsToday // 1000000 > nanoSeconds: localUsecsToday \\ 1000000 * 1000! > > Item was added: > + ----- Method: Time class>>secondsInDay (in category 'constants') ----- > + secondsInDay > + ^86400! > > Item was added: > + ----- Method: Time class>>secondsInHour (in category 'constants') ----- > + secondsInHour > + ^3600! > > Item was added: > + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- > + secondsInMinute > + ^60! > > Item was added: > + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- > + squeakEpoch > + ^ 2415386. "Julian day number of 1 Jan 1901"! > > Item was changed: > ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- > print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream > "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. > If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" > > | h m s | > h := self hour. m := self minute. s := self second. > hr24 > ifTrue: > [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. > h printOn: aStream ] > ifFalse: > [ h > 12 > ifTrue: [h - 12 printOn: aStream] > ifFalse: > [h < 1 > ifTrue: [ 12 printOn: aStream ] > ifFalse: [ h printOn: aStream ]]]. > > aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). > m printOn: aStream. > > showSeconds ifTrue: > [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). > (showSubseconds not or: [self nanoSecond = 0]) > ifTrue: [s asInteger printOn: aStream] > + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger > + printOn: aStream asFixedPoint: Time nanosInSecond]]. > - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger > - printOn: aStream asFixedPoint: NanosInSecond]]. > > hr24 ifFalse: > [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. > ! > > Item was changed: > Object subclass: #TimeZone > instanceVariableNames: 'offset abbreviation name' > classVariableNames: '' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > TimeZone is a simple class to colect the information identifying a UTC time zone. > > offset - Duration - the time zone's offset from UTC > abbreviation - String - the abbreviated name for the time zone. > name - String - the name of the time zone. > > TimeZone class >> #timeZones returns an array of the known time zones > TimeZone class >> #default returns the default time zone (Grenwich Mean Time) > ! > > Item was changed: > Timespan subclass: #Week > instanceVariableNames: '' > classVariableNames: 'StartDay' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > I represent a week. > > To find out what days of the week on which Squeak is fun, select the following expression, and print it: > > Week dayNames! > > Item was changed: > + ----- Method: Week class>>dayNames (in category 'inquiries') ----- > - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- > dayNames > > + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! > - ^ DayNames! > > Item was changed: > ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- > indexOfDay: aSymbol > > + ^ self dayNames indexOf: aSymbol! > - ^ DayNames indexOf: aSymbol! > > Item was changed: > ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- > nameOfDay: anIndex > > + ^ self dayNames at: anIndex! > - ^ DayNames at: anIndex! > > Item was changed: > ----- Method: Week class>>startDay (in category 'squeak protocol') ----- > startDay > > ^ StartDay ifNil: [ StartDay > + := self dayNames first ] > - := DayNames first ] > ! > > Item was changed: > ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- > startDay: aSymbol > > + (self dayNames includes: aSymbol) > - (DayNames includes: aSymbol) > ifTrue: [ StartDay := aSymbol ] > ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! > > Item was changed: > ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- > starting: aDateAndTime duration: aDuration > "Override - the duration is always one week. > Week will start from the Week class>>startDay" > > | midnight delta adjusted | > midnight := aDateAndTime asDateAndTime midnight. > + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. > - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. > adjusted := midnight - (Duration days: delta seconds: 0). > > ^ super starting: adjusted duration: (Duration weeks: 1)! > > From marcel.taeumel at hpi.de Fri Sep 4 16:29:25 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Fri, 4 Sep 2020 18:29:25 +0200 Subject: [squeak-dev] morhpic copy the max/min buttons to bottom of the browser. In-Reply-To: <174588d54fe.d863155914165.5390894482462793606@zoho.com> References: <174588d54fe.d863155914165.5390894482462793606@zoho.com> Message-ID: Hi Timothy! Hmm... SystemWindow >> #collapseOrExpand is rather ... special. :-) Even the "layout" of the grip morphs to resize the window is rather "hacked" on top of the proportional layout. See BorderGripMorph >> #layoutProportionallyInBounds:positioning:. Could you add a screenshot so that we can see what you want to achieve? Best, Marcel Am 04.09.2020 11:58:22 schrieb gettimothy via Squeak-dev : Hi all, In my work, I like to 'adhere to edge' on many open browsers. I have a browser adhered to the bottom right of the world. to explain further, the browser is minimized at bottom-right edge. the max /min buttons are aligned in the minimized browser along the bottom. I click the button, and the browser is un-minimized but now I have to move my mouse all the way up to where they moved to. (problems of the future, I know.....poor tty has to move that heavy mouse all of several inches, oh!  the humanity!) I thought it would be handy to copy the min-max buttons on the browser to the bottom of the browser so I could do a quick max/min without having to move my mouse after the operation. the copy works fine, but, after the first click, the buttons get hidden behind the browser and stuck to the world. any Morphic geniuses have a suggestion on how to get the copied buttons to adhere to the little status-bar-doohickey along the bottom of the browser? thanks in advance -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Fri Sep 4 18:48:33 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 4 Sep 2020 18:48:33 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1680.mcz Message-ID: Christoph Thiede uploaded a new version of Morphic to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1680.mcz ==================== Summary ==================== Name: Morphic-ct.1680 Author: ct Time: 4 September 2020, 8:48:24.528421 pm UUID: 8fb88541-6e15-cb41-82d0-7de2f7c3e00e Ancestors: Morphic-mt.1679 Fixes a bug in PluggableButtonMorph when passing nil as a label Instead of an empty string, 'nil' asText was diplayed. See also PluggableButtonMorph >> #label. =============== Diff against Morphic-mt.1679 =============== Item was changed: ----- Method: PluggableButtonMorph>>label: (in category 'accessing') ----- label: aStringOrTextOrMorph label = aStringOrTextOrMorph ifTrue: [^ self]. + label := aStringOrTextOrMorph ifNotNil: [ + aStringOrTextOrMorph isString + ifFalse: [aStringOrTextOrMorph asMorph] + ifTrue: [aStringOrTextOrMorph]]. - label := aStringOrTextOrMorph isString - ifFalse: [aStringOrTextOrMorph asMorph] - ifTrue: [aStringOrTextOrMorph]. self updateMinimumExtent. self changed.! From gettimothy at zoho.com Fri Sep 4 18:54:09 2020 From: gettimothy at zoho.com (gettimothy) Date: Fri, 04 Sep 2020 14:54:09 -0400 Subject: [squeak-dev] morhpic copy the max/min buttons to bottom of the        browser. In-Reply-To: References: <174588d54fe.d863155914165.5390894482462793606@zoho.com> Message-ID: <1745a780762.d700ecbf21202.7063585473691223219@zoho.com> Hi Marcel, I emailed your "home" email with the screen shots. thx. ---- On Fri, 04 Sep 2020 12:29:25 -0400 Marcel Taeumel wrote ---- Hi Timothy! Hmm... SystemWindow >> #collapseOrExpand is rather ... special. :-) Even the "layout" of the grip morphs to resize the window is rather "hacked" on top of the proportional layout. See BorderGripMorph >> #layoutProportionallyInBounds:positioning:. Could you add a screenshot so that we can see what you want to achieve? Best, Marcel Am 04.09.2020 11:58:22 schrieb gettimothy via Squeak-dev : Hi all, In my work, I like to 'adhere to edge' on many open browsers. I have a browser adhered to the bottom right of the world. to explain further, the browser is minimized at bottom-right edge. the max /min buttons are aligned in the minimized browser along the bottom. I click the button, and the browser is un-minimized but now I have to move my mouse all the way up to where they moved to. (problems of the future, I know.....poor tty has to move that heavy mouse all of several inches, oh!  the humanity!) I thought it would be handy to copy the min-max buttons on the browser to the bottom of the browser so I could do a quick max/min without having to move my mouse after the operation. the copy works fine, but, after the first click, the buttons get hidden behind the browser and stuck to the world. any Morphic geniuses have a suggestion on how to get the copied buttons to adhere to the little status-bar-doohickey along the bottom of the browser? thanks in advance -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Fri Sep 4 19:09:20 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 4 Sep 2020 19:09:20 0000 Subject: [squeak-dev] The Inbox: Kernel-ct.1339.mcz Message-ID: A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-ct.1339.mcz ==================== Summary ==================== Name: Kernel-ct.1339 Author: ct Time: 4 September 2020, 9:09:14.713421 pm UUID: edc35a82-ce03-014c-85de-13d68c7fc46f Ancestors: Kernel-ct.1338 Implement missing simulation of objects as methods. In the past, it was not possible to debug/simulate code that used objects as methods properly. (Thanks to Marcel for the hint!) This very simple commit adds support of the OaM protocol [1] to the simulation machinery. Now you can debug all tests in TestObjectsAsMethods as you would expect, instead of crashing your image! Update: Uploaded again, this time with additional documentation comment, reformatted code, and multilingual support/fix of typ�s. Replaces Kernel-ct.1306, which can be moved to the treated inbox. [1] "The [Objects as Methods] contract is that, when the VM encounters an ordinary object (rather than a compiled method) in the method dictionary during lookup, it sends it the special selector #run:with:in: providing the original selector, arguments, and receiver.". DOI: 10.1145/2991041.2991062. =============== Diff against Kernel-ct.1338 =============== Item was changed: ----- Method: Context>>send:to:with:lookupIn: (in category 'controlling') ----- send: selector to: rcvr with: arguments lookupIn: lookupClass + "Simulate the action of sending a message with selector and arguments to rcvr. The argument, lookupClass, is the class in which to lookup the message. This is the receiver's class for normal messages, but for super messages it will be some specific class related to the source method." - "Simulate the action of sending a message with selector and arguments - to rcvr. The argument, lookupClass, is the class in which to lookup the - message. This is the receiver's class for normal messages, but for super - messages it will be some specific class related to the source method." | meth primIndex val ctxt | + (meth := lookupClass lookupSelector: selector) ifNil: [ + ^ self + send: #doesNotUnderstand: + to: rcvr + with: {(Message selector: selector arguments: arguments) lookupClass: lookupClass} + lookupIn: lookupClass]. + + meth isCompiledMethod ifFalse: [ + "Object as Methods (OaM) protocol: 'The contract is that, when the VM encounters an ordinary object (rather than a compiled method) in the method dictionary during lookup, it sends it the special selector #run:with:in: providing the original selector, arguments, and receiver.'. DOI: 10.1145/2991041.2991062." + ^ self send: #run:with:in: + to: meth + with: {selector. arguments. rcvr}]. + + meth numArgs ~= arguments size ifTrue: [ + ^ self error: ('Wrong number of arguments in simulated message {1}' translated format: {selector})]. + (primIndex := meth primitive) > 0 ifTrue: [ + val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments. + (self isPrimFailToken: val) ifFalse: [^ val]]. + + (selector == #doesNotUnderstand: and: [lookupClass == ProtoObject]) ifTrue: [ + ^ self error: ('Simulated message {1} not understood' translated format: {arguments first selector})]. + - (meth := lookupClass lookupSelector: selector) ifNil: - [^self send: #doesNotUnderstand: - to: rcvr - with: {(Message selector: selector arguments: arguments) lookupClass: lookupClass} - lookupIn: lookupClass]. - meth numArgs ~= arguments size ifTrue: - [^self error: 'Wrong number of arguments in simulated message ', selector printString]. - (primIndex := meth primitive) > 0 ifTrue: - [val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments. - (self isPrimFailToken: val) ifFalse: - [^val]]. - (selector == #doesNotUnderstand: and: [lookupClass == ProtoObject]) ifTrue: - [^self error: 'Simulated message ', arguments first selector, ' not understood']. ctxt := Context sender: self receiver: rcvr method: meth arguments: arguments. + (primIndex notNil and: [primIndex > 0]) ifTrue: [ + ctxt failPrimitiveWith: val]. + + ^ ctxt! - primIndex > 0 ifTrue: - [ctxt failPrimitiveWith: val]. - ^ctxt! From leves at caesar.elte.hu Fri Sep 4 22:01:21 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sat, 5 Sep 2020 00:01:21 +0200 (CEST) Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz In-Reply-To: <20200904150058.GA11481@shell.msen.com> References: <20200904150058.GA11481@shell.msen.com> Message-ID: Hi Dave, I skimmed through the diff and my impression was that the goal of these changes was to remove the ChronologyConstants shared pool. What's the motivation to do that? Does Cuis not have shared pools? And the criticism: 1) Variable references are replaced with message sends. Even of those are quick sends, they'll still be slightly slower than using the variables (with the current VMs). 2) Some replacement methods are not quick and create new objects. For example, OneDay, a single Duration of one day is replaced with #oneDay, a method returning the expression 1 day. Each invocation of the method creates a new object. In my image, there are 19821 Date instances. If all of those had their own custom one day long Duration objects, which is a side effect of these changes in the long run, my image would be ~620 kiB larger (64-bit image, object header + 2 variables => 32 bytes each). There's a solution: cache the shared instance in a variable. :) 3) A new role for the Time class: provide the variables ChronologyConstants used to. I think Time is already overloaded with roles; it doesn't need a new one. Even if the shared pool goes away, there should be a separate class holding these constants. For example, we could call it ChronologyConstants. :) Levente On Fri, 4 Sep 2020, David T. Lewis wrote: > I have been looking at Chronology in Cuis, as I would like to be able to > contribute Squeak's UTC style DateAndTime for consideration in Cuis. > > Juan has done some cleanups that simplify Chronology by getting rid of > an unnecessary shared pool for constants. I think this is a good improvement > so I put it in the inbox here for review. > > To me this seems cleaner and simpler. All tests still pass of course. > > Dave > > > On Fri, Sep 04, 2020 at 02:54:38PM +0000, commits at source.squeak.org wrote: >> A new version of Chronology-Core was added to project The Inbox: >> http://source.squeak.org/inbox/Chronology-Core-dtl.56.mcz >> >> ==================== Summary ==================== >> >> Name: Chronology-Core-dtl.56 >> Author: dtl >> Time: 4 September 2020, 10:54:37.509663 am >> UUID: a33e5fab-940e-41ed-b05c-76f8ff54f5ee >> Ancestors: Chronology-Core-ul.55 >> >> Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. >> >> =============== Diff against Chronology-Core-ul.55 =============== >> >> Item was removed: >> - SharedPool subclass: #ChronologyConstants >> - instanceVariableNames: '' >> - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' >> - poolDictionaries: '' >> - category: 'Chronology-Core'! >> - >> - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! >> - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! >> >> Item was removed: >> - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- >> - initialize >> - "ChronologyConstants initialize" >> - >> - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" >> - SecondsInDay := 86400. >> - SecondsInHour := 3600. >> - SecondsInMinute := 60. >> - MicrosecondsInDay := 24 * 60 * 60 * 1000000. >> - NanosInSecond := 10 raisedTo: 9. >> - NanosInMillisecond := 10 raisedTo: 6. >> - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). >> - >> - MonthNames := #( January February March April May June >> - July August September October November December). >> - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! >> >> Item was changed: >> Timespan subclass: #Date >> instanceVariableNames: '' >> classVariableNames: '' >> + poolDictionaries: '' >> - poolDictionaries: 'ChronologyConstants' >> category: 'Chronology-Core'! >> >> !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! >> Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. >> >> However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! >> >> Item was changed: >> ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- >> fromDays: dayCount >> "Days since 1 January 1901" >> >> + ^ self julianDayNumber: dayCount + Time squeakEpoch! >> - ^ self julianDayNumber: dayCount + SqueakEpoch! >> >> Item was changed: >> ----- Method: Date class>>starting: (in category 'squeak protocol') ----- >> starting: aDateAndTime >> ^ self >> starting: aDateAndTime midnight >> + duration: 1 day! >> - duration: Duration oneDay! >> >> Item was changed: >> Magnitude subclass: #DateAndTime >> instanceVariableNames: 'utcMicroseconds localOffsetSeconds' >> classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' >> + poolDictionaries: '' >> - poolDictionaries: 'ChronologyConstants' >> category: 'Chronology-Core'! >> >> !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! >> I represent a point in UTC time as defined by ISO 8601. I have zero duration. >> >> My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. >> >> The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. >> >> DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. >> ! >> >> Item was changed: >> ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- >> epochOffset >> "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" >> + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! >> - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! >> >> Item was changed: >> ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- >> posixEpochJulianDays >> >> + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! >> - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! >> >> Item was changed: >> ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- >> ticks: ticks offset: utcOffset >> "ticks is {julianDayNumber. secondCount. nanoSeconds}" >> >> | jdn s nanos normalizedTicks | >> normalizedTicks := ticks copy. >> + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. >> + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. >> - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. >> - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. >> >> jdn := normalizedTicks at: 1. >> s := normalizedTicks at: 2. >> nanos := normalizedTicks at: 3. >> localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. >> utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. >> ! >> >> Item was changed: >> Magnitude subclass: #Duration >> instanceVariableNames: 'nanos seconds' >> classVariableNames: '' >> + poolDictionaries: '' >> - poolDictionaries: 'ChronologyConstants' >> category: 'Chronology-Core'! >> >> !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! >> I represent a duration of time. I have nanosecond precision! >> >> Item was changed: >> ----- Method: Duration class>>days: (in category 'squeak protocol') ----- >> days: aNumber >> >> + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! >> - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! >> >> Item was changed: >> ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- >> days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos >> >> ^self >> seconds: seconds >> + + (minutes * Time secondsInMinute) >> + + (hours * Time secondsInHour) >> + + (days * Time secondsInDay) >> - + (minutes * SecondsInMinute) >> - + (hours * SecondsInHour) >> - + (days * SecondsInDay) >> nanoSeconds: nanos >> ! >> >> Item was changed: >> ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- >> days: days seconds: seconds >> >> + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! >> - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 >> - ! >> >> Item was changed: >> ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- >> hours: aNumber >> >> + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! >> - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! >> >> Item was removed: >> - ----- Method: Duration class>>initialize (in category 'initialize-release') ----- >> - initialize >> - ChronologyConstants classPool >> - at: #Zero >> - put: >> - (self basicNew >> - seconds: 0 >> - nanoSeconds: 0) ; >> - at: #OneDay >> - put: 1 day! >> >> Item was changed: >> ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- >> milliSeconds: milliCount >> >> ^self >> seconds: (milliCount quo: 1000) >> + nanoSeconds: (milliCount rem: 1000) * 1000000! >> - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! >> >> Item was changed: >> ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- >> minutes: aNumber >> >> + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! >> - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! >> >> Item was changed: >> ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- >> nanoSeconds: nanos >> "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." >> >> | quo | >> + quo _ nanos quo: Time nanosInSecond. >> - quo := nanos quo: NanosInSecond. >> ^ self basicNew >> seconds: quo >> + nanoSeconds: nanos - (quo * Time nanosInSecond)! >> - nanoSeconds: nanos - (quo * NanosInSecond) >> - ! >> >> Item was changed: >> ----- Method: Duration class>>oneDay (in category 'squeak protocol') ----- >> oneDay >> "Answer the canonicalized Duration representing length of 1 day. Used by Dates." >> + ^ 1 day! >> - ^ OneDay! >> >> Item was changed: >> ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- >> seconds: seconds nanoSeconds: nanos >> >> ^ self basicNew >> seconds: seconds truncated >> + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! >> - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! >> >> Item was changed: >> ----- Method: Duration class>>zero (in category 'ansi protocol') ----- >> zero >> + >> + ^ self basicNew seconds: 0 nanoSeconds: 0 >> + ! >> - "Answer the canonicalized instance of Duration zero." >> - ^ Zero! >> >> Item was changed: >> ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- >> asNanoSeconds >> >> + ^seconds * Time nanosInSecond + nanos! >> - ^seconds * NanosInSecond + nanos! >> >> Item was changed: >> ----- Method: Duration>>days (in category 'ansi protocol') ----- >> days >> "Answer the number of days the receiver represents." >> >> + ^ seconds quo: Time secondsInDay! >> - ^ seconds quo: SecondsInDay >> - ! >> >> Item was changed: >> ----- Method: Duration>>hours (in category 'ansi protocol') ----- >> hours >> "Answer the number of hours the receiver represents." >> >> >> + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! >> - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! >> >> Item was changed: >> ----- Method: Duration>>minutes (in category 'ansi protocol') ----- >> minutes >> - >> "Answer the number of minutes the receiver represents." >> >> + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! >> - >> - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! >> >> Item was changed: >> ----- Method: Duration>>seconds (in category 'ansi protocol') ----- >> seconds >> "Answer the number of seconds the receiver represents." >> >> + ^seconds rem: Time secondsInMinute! >> - ^seconds rem: SecondsInMinute! >> >> Item was changed: >> ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- >> seconds: secondCount nanoSeconds: nanoCount >> "Private - only used by Duration class" >> >> seconds := secondCount. >> nanos := nanoCount rounded. >> "normalize if signs do not match" >> [ nanos < 0 and: [ seconds > 0 ] ] >> whileTrue: [ seconds := seconds - 1. >> + nanos := nanos + Time nanosInSecond ]. >> - nanos := nanos + NanosInSecond ]. >> [ seconds < 0 and: [ nanos > 0 ] ] >> whileTrue: [ seconds := seconds + 1. >> + nanos := nanos - Time nanosInSecond ] >> - nanos := nanos - NanosInSecond ] >> >> ! >> >> Item was changed: >> ----- Method: Duration>>ticks (in category 'private') ----- >> ticks >> "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." >> >> | days | >> days := self days. >> ^ Array >> with: days >> + with: seconds - (days * Time secondsInDay) >> - with: seconds - (days * SecondsInDay) >> with: nanos >> ! >> >> Item was changed: >> Timespan subclass: #Month >> instanceVariableNames: '' >> classVariableNames: '' >> + poolDictionaries: '' >> - poolDictionaries: 'ChronologyConstants' >> category: 'Chronology-Core'! >> >> !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! >> I represent a month. >> >> For example, to get the number of days this month, you can evaluate the following expression: >> >> Month current daysInMonth! >> >> Item was added: >> + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- >> + daysInMonth >> + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! >> >> Item was changed: >> ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- >> daysInMonth: indexOrName forYear: yearInteger >> >> | index | >> index := indexOrName isInteger >> ifTrue: [indexOrName] >> ifFalse: [self indexOfMonth: indexOrName]. >> + ^ (self daysInMonth at: index) >> - ^ (DaysInMonth at: index) >> + ((index = 2 >> and: [Year isLeapYear: yearInteger]) >> ifTrue: [1] ifFalse: [0]) >> ! >> >> Item was changed: >> ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- >> indexOfMonth: aMonthName >> >> >> + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. >> + self error: aMonthName , ' is not a recognized month name'! >> - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. >> - self error: aMonthName , ' is not a recognized month name'.! >> >> Item was added: >> + ----- Method: Month class>>monthNames (in category 'inquiries') ----- >> + monthNames >> + ^#(January February March April May June July August September October November December)! >> >> Item was changed: >> ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- >> nameOfMonth: anIndex >> >> + ^ self monthNames at: anIndex! >> - ^ MonthNames at: anIndex.! >> >> Item was changed: >> Magnitude subclass: #Time >> instanceVariableNames: 'seconds nanos' >> classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' >> + poolDictionaries: '' >> - poolDictionaries: 'ChronologyConstants' >> category: 'Chronology-Core'! >> >> !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! >> This represents a particular point in time during any given day. For example, '5:19:45 pm'. >> >> If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. >> ! >> >> Item was changed: >> ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- >> fromSeconds: secondCount >> "Answer an instance of me that is secondCount number of seconds since midnight." >> >> | integerSeconds nanos | >> integerSeconds := secondCount truncated. >> integerSeconds = secondCount >> ifTrue: [nanos := 0] >> + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. >> - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. >> ^ self seconds: integerSeconds nanoSeconds: nanos >> ! >> >> Item was changed: >> ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- >> + hour: hour minute: minute second: second nanoSecond: nanoCount >> + "Answer a Time" >> - hour: hour minute: minute second: second nanoSecond: nanoCount >> - "Answer a Time - only second precision for now" >> >> ^ self >> + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second >> + nanoSeconds: nanoCount! >> - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second >> - nanoSeconds: nanoCount >> - ! >> >> Item was added: >> + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- >> + nanosInSecond >> + ^ 1000000000! >> >> Item was changed: >> ----- Method: Time class>>noon (in category 'squeak protocol') ----- >> noon >> >> + ^ self seconds: self secondsInDay / 2! >> - ^ self seconds: (SecondsInDay / 2) >> - ! >> >> Item was changed: >> ----- Method: Time class>>now (in category 'ansi protocol') ----- >> now >> "Answer a Time representing the time right now - this is a 24 hour clock." >> | localUsecs localUsecsToday | >> localUsecs := self localMicrosecondClock. >> + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" >> - localUsecsToday := localUsecs \\ MicrosecondsInDay. >> ^ self >> seconds: localUsecsToday // 1000000 >> nanoSeconds: localUsecsToday \\ 1000000 * 1000! >> >> Item was added: >> + ----- Method: Time class>>secondsInDay (in category 'constants') ----- >> + secondsInDay >> + ^86400! >> >> Item was added: >> + ----- Method: Time class>>secondsInHour (in category 'constants') ----- >> + secondsInHour >> + ^3600! >> >> Item was added: >> + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- >> + secondsInMinute >> + ^60! >> >> Item was added: >> + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- >> + squeakEpoch >> + ^ 2415386. "Julian day number of 1 Jan 1901"! >> >> Item was changed: >> ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- >> print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream >> "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. >> If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" >> >> | h m s | >> h := self hour. m := self minute. s := self second. >> hr24 >> ifTrue: >> [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. >> h printOn: aStream ] >> ifFalse: >> [ h > 12 >> ifTrue: [h - 12 printOn: aStream] >> ifFalse: >> [h < 1 >> ifTrue: [ 12 printOn: aStream ] >> ifFalse: [ h printOn: aStream ]]]. >> >> aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). >> m printOn: aStream. >> >> showSeconds ifTrue: >> [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). >> (showSubseconds not or: [self nanoSecond = 0]) >> ifTrue: [s asInteger printOn: aStream] >> + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger >> + printOn: aStream asFixedPoint: Time nanosInSecond]]. >> - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger >> - printOn: aStream asFixedPoint: NanosInSecond]]. >> >> hr24 ifFalse: >> [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. >> ! >> >> Item was changed: >> Object subclass: #TimeZone >> instanceVariableNames: 'offset abbreviation name' >> classVariableNames: '' >> + poolDictionaries: '' >> - poolDictionaries: 'ChronologyConstants' >> category: 'Chronology-Core'! >> >> !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! >> TimeZone is a simple class to colect the information identifying a UTC time zone. >> >> offset - Duration - the time zone's offset from UTC >> abbreviation - String - the abbreviated name for the time zone. >> name - String - the name of the time zone. >> >> TimeZone class >> #timeZones returns an array of the known time zones >> TimeZone class >> #default returns the default time zone (Grenwich Mean Time) >> ! >> >> Item was changed: >> Timespan subclass: #Week >> instanceVariableNames: '' >> classVariableNames: 'StartDay' >> + poolDictionaries: '' >> - poolDictionaries: 'ChronologyConstants' >> category: 'Chronology-Core'! >> >> !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! >> I represent a week. >> >> To find out what days of the week on which Squeak is fun, select the following expression, and print it: >> >> Week dayNames! >> >> Item was changed: >> + ----- Method: Week class>>dayNames (in category 'inquiries') ----- >> - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- >> dayNames >> >> + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! >> - ^ DayNames! >> >> Item was changed: >> ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- >> indexOfDay: aSymbol >> >> + ^ self dayNames indexOf: aSymbol! >> - ^ DayNames indexOf: aSymbol! >> >> Item was changed: >> ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- >> nameOfDay: anIndex >> >> + ^ self dayNames at: anIndex! >> - ^ DayNames at: anIndex! >> >> Item was changed: >> ----- Method: Week class>>startDay (in category 'squeak protocol') ----- >> startDay >> >> ^ StartDay ifNil: [ StartDay >> + := self dayNames first ] >> - := DayNames first ] >> ! >> >> Item was changed: >> ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- >> startDay: aSymbol >> >> + (self dayNames includes: aSymbol) >> - (DayNames includes: aSymbol) >> ifTrue: [ StartDay := aSymbol ] >> ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! >> >> Item was changed: >> ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- >> starting: aDateAndTime duration: aDuration >> "Override - the duration is always one week. >> Week will start from the Week class>>startDay" >> >> | midnight delta adjusted | >> midnight := aDateAndTime asDateAndTime midnight. >> + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. >> - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. >> adjusted := midnight - (Duration days: delta seconds: 0). >> >> ^ super starting: adjusted duration: (Duration weeks: 1)! >> >> From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 4 23:13:57 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 4 Sep 2020 23:13:57 +0000 Subject: [squeak-dev] optionMenu in nested MVC buttons not working Message-ID: <10db1d802c2d4f91ad0251a15eb70fb0@student.hpi.uni-potsdam.de> Hi all, I experimented a bit with the backward compatibility of MVC buttons and found out that menus were broken in them. The attached changeset fixes this. However, you cannot invoke a menu on a button that is nested into another view. Please find the attached example. I already found out that in PluggableButtonController >> #isControlWanted, sensor anyButtonPressed returns false when it should return true. Crazily, the bug does not occur when having a transcript open and logging things to it directly before the sensor call. As a side effect, if you enable both buttons in the debug code parallelly, regular clicks on the unnested button won't work at all. I have no idea why is this. Any help and tips would be great! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: CTDebugModel.st Type: application/octet-stream Size: 1273 bytes Desc: CTDebugModel.st URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: PluggableButtonController-optionalMenu-fixes.1.cs URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 4 23:18:46 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 4 Sep 2020 23:18:46 +0000 Subject: [squeak-dev] optionMenu in nested MVC buttons not working In-Reply-To: <10db1d802c2d4f91ad0251a15eb70fb0@student.hpi.uni-potsdam.de> References: <10db1d802c2d4f91ad0251a15eb70fb0@student.hpi.uni-potsdam.de> Message-ID: I just updated the debugging code for understandability. You can build it by doing: ToolBuilder open: CTDebugModel. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Samstag, 5. September 2020 01:13:57 An: Squeak Dev Betreff: [squeak-dev] optionMenu in nested MVC buttons not working Hi all, I experimented a bit with the backward compatibility of MVC buttons and found out that menus were broken in them. The attached changeset fixes this. However, you cannot invoke a menu on a button that is nested into another view. Please find the attached example. I already found out that in PluggableButtonController >> #isControlWanted, sensor anyButtonPressed returns false when it should return true. Crazily, the bug does not occur when having a transcript open and logging things to it directly before the sensor call. As a side effect, if you enable both buttons in the debug code parallelly, regular clicks on the unnested button won't work at all. I have no idea why is this. Any help and tips would be great! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: CTDebugModel.st Type: application/octet-stream Size: 1280 bytes Desc: CTDebugModel.st URL: From commits at source.squeak.org Fri Sep 4 23:20:57 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 4 Sep 2020 23:20:57 0000 Subject: [squeak-dev] The Inbox: ST80-ct.258.mcz Message-ID: Christoph Thiede uploaded a new version of ST80 to project The Inbox: http://source.squeak.org/inbox/ST80-ct.258.mcz ==================== Summary ==================== Name: ST80-ct.258 Author: ct Time: 5 September 2020, 1:20:54.682421 am UUID: db2a2f67-e818-ea46-b2f6-f4a9950577db Ancestors: ST80-mt.257 Fixes home and end keys in MVC editors if the text is empty =============== Diff against ST80-mt.257 =============== Item was changed: ----- Method: ParagraphEditor>>cursorEnd: (in category 'nonediting/nontyping keys') ----- cursorEnd: characterStream "Private - Move cursor end of current line." | string | self closeTypeIn: characterStream. string := paragraph text string. self moveCursor: [:position | Preferences wordStyleCursorMovement + ifTrue: [ + paragraph lines + at: (paragraph lineIndexOfCharacterIndex: position) + ifPresent: [:targetLine | targetLine last + (targetLine last = string size) asBit] + ifAbsent: [position]] + ifFalse: [ - ifTrue:[| targetLine | - targetLine := paragraph lines at:(paragraph lineIndexOfCharacterIndex: position). - targetLine = paragraph lines last - ifTrue:[targetLine last + 1] - ifFalse:[targetLine last]] - ifFalse:[ string indexOfAnyOf: CharacterSet crlf startingAt: position ifAbsent:[string size + 1]]] forward: true specialBlock:[:dummy | string size + 1]. ^true! Item was changed: ----- Method: ParagraphEditor>>cursorHome: (in category 'nonediting/nontyping keys') ----- cursorHome: characterStream "Private - Move cursor from position in current line to beginning of current line. If control key is pressed put cursor at beginning of text" | string | string := paragraph text string. self + moveCursor: [:position | Preferences wordStyleCursorMovement + ifTrue: [ + paragraph lines + at: (paragraph lineIndexOfCharacterIndex: position) + ifPresent: [:targetLine | targetLine first] ifAbsent: [position]] - moveCursor: [ :position | Preferences wordStyleCursorMovement - ifTrue:[ - (paragraph lines at:(paragraph lineIndexOfCharacterIndex: position)) first] ifFalse:[ (string lastIndexOfAnyOf: CharacterSet crlf startingAt: position - 1) + 1]] forward: false specialBlock: [:dummy | 1]. ^true! From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 4 23:22:07 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 4 Sep 2020 23:22:07 +0000 Subject: [squeak-dev] The Inbox: ST80-ct.258.mcz In-Reply-To: References: Message-ID: Still, pressing End in the latest line skips one character, but this is not a regression ... ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Samstag, 5. September 2020 01:20:57 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: ST80-ct.258.mcz Christoph Thiede uploaded a new version of ST80 to project The Inbox: http://source.squeak.org/inbox/ST80-ct.258.mcz ==================== Summary ==================== Name: ST80-ct.258 Author: ct Time: 5 September 2020, 1:20:54.682421 am UUID: db2a2f67-e818-ea46-b2f6-f4a9950577db Ancestors: ST80-mt.257 Fixes home and end keys in MVC editors if the text is empty =============== Diff against ST80-mt.257 =============== Item was changed: ----- Method: ParagraphEditor>>cursorEnd: (in category 'nonediting/nontyping keys') ----- cursorEnd: characterStream "Private - Move cursor end of current line." | string | self closeTypeIn: characterStream. string := paragraph text string. self moveCursor: [:position | Preferences wordStyleCursorMovement + ifTrue: [ + paragraph lines + at: (paragraph lineIndexOfCharacterIndex: position) + ifPresent: [:targetLine | targetLine last + (targetLine last = string size) asBit] + ifAbsent: [position]] + ifFalse: [ - ifTrue:[| targetLine | - targetLine := paragraph lines at:(paragraph lineIndexOfCharacterIndex: position). - targetLine = paragraph lines last - ifTrue:[targetLine last + 1] - ifFalse:[targetLine last]] - ifFalse:[ string indexOfAnyOf: CharacterSet crlf startingAt: position ifAbsent:[string size + 1]]] forward: true specialBlock:[:dummy | string size + 1]. ^true! Item was changed: ----- Method: ParagraphEditor>>cursorHome: (in category 'nonediting/nontyping keys') ----- cursorHome: characterStream "Private - Move cursor from position in current line to beginning of current line. If control key is pressed put cursor at beginning of text" | string | string := paragraph text string. self + moveCursor: [:position | Preferences wordStyleCursorMovement + ifTrue: [ + paragraph lines + at: (paragraph lineIndexOfCharacterIndex: position) + ifPresent: [:targetLine | targetLine first] ifAbsent: [position]] - moveCursor: [ :position | Preferences wordStyleCursorMovement - ifTrue:[ - (paragraph lines at:(paragraph lineIndexOfCharacterIndex: position)) first] ifFalse:[ (string lastIndexOfAnyOf: CharacterSet crlf startingAt: position - 1) + 1]] forward: false specialBlock: [:dummy | 1]. ^true! -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Sat Sep 5 01:04:08 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Fri, 4 Sep 2020 21:04:08 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz In-Reply-To: References: <20200904150058.GA11481@shell.msen.com> Message-ID: <20200905010408.GA3671@shell.msen.com> Hi Levente, On Sat, Sep 05, 2020 at 12:01:21AM +0200, Levente Uzonyi wrote: > Hi Dave, > > I skimmed through the diff and my impression was that the goal of these > changes was to remove the ChronologyConstants shared pool. > What's the motivation to do that? Does Cuis not have shared pools? I can't speak for Juan, but my assumption is that the original motivation was that of Cuis overall - make things as simple and approachable as possible. My personal motivation in suggesting it for Squeak is that I'm interested in making the Squeak UTC DateAndTime available for Cuis, which is actually a rather challenging thing to do. It's easier for me if I don't have to re-implement Juan's improvments after (hypothetically) porting the code from Squeak to Cuis. So if they are worthwhile changes, and I think that they are, then it's easier to just do the update once in Squeak so that we can just be rid of the incompatibilities. > > And the criticism: > 1) Variable references are replaced with message sends. Even of those are > quick sends, they'll still be slightly slower than using the variables > (with the current VMs). As far as I can tell, this has no practical impact, except for the #oneDay case that you identified. But I don't think that it is actually a problem, see below. > 2) Some replacement methods are not quick and create new objects. For > example, OneDay, a single Duration of one day is replaced with #oneDay, a > method returning the expression 1 day. Each invocation of the method > creates a new object. > In my image, there are 19821 Date instances. If all of those had their own > custom one day long Duration objects, which is a side effect of these > changes in the long run, my image would be ~620 kiB larger (64-bit image, > object header + 2 variables => 32 bytes each). > There's a solution: cache the shared instance in a variable. :) If it is a problem, then this is an error on my part (not Juan's). After adopting the main part of the Cuis changes, #oneDay was one of a couple of remaining cases that I addressed with trivial replacements. But there are no senders of #oneDay in the image other than a unit test. So I think that it is not likely to be an issue. Dave > 3) A new role for the Time class: provide the variables > ChronologyConstants used to. I think Time is already overloaded with > roles; it doesn't need a new one. Even if the shared pool goes away, there > should be a separate class holding these constants. For example, we could > call it ChronologyConstants. :) > I would not be in favor of that :-) Thanks very much for reviewing. Dave > > Levente > > On Fri, 4 Sep 2020, David T. Lewis wrote: > > >I have been looking at Chronology in Cuis, as I would like to be able to > >contribute Squeak's UTC style DateAndTime for consideration in Cuis. > > > >Juan has done some cleanups that simplify Chronology by getting rid of > >an unnecessary shared pool for constants. I think this is a good > >improvement > >so I put it in the inbox here for review. > > > >To me this seems cleaner and simpler. All tests still pass of course. > > > >Dave > > > > > >On Fri, Sep 04, 2020 at 02:54:38PM +0000, commits at source.squeak.org wrote: > >>A new version of Chronology-Core was added to project The Inbox: > >>http://source.squeak.org/inbox/Chronology-Core-dtl.56.mcz > >> > >>==================== Summary ==================== > >> > >>Name: Chronology-Core-dtl.56 > >>Author: dtl > >>Time: 4 September 2020, 10:54:37.509663 am > >>UUID: a33e5fab-940e-41ed-b05c-76f8ff54f5ee > >>Ancestors: Chronology-Core-ul.55 > >> > >>Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv > >>author initials where possible. The shared pool is not required, it is > >>simpler to use methods in the responsible classes. > >> > >>=============== Diff against Chronology-Core-ul.55 =============== > >> > >>Item was removed: > >>- SharedPool subclass: #ChronologyConstants > >>- instanceVariableNames: '' > >>- classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay > >>MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay > >>SecondsInHour SecondsInMinute SqueakEpoch Zero' > >>- poolDictionaries: '' > >>- category: 'Chronology-Core'! > >>- > >>- !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! > >>- ChronologyConstants is a SharedPool for the constants used by the > >>Kernel-Chronology classes.! > >> > >>Item was removed: > >>- ----- Method: ChronologyConstants class>>initialize (in category 'class > >>initialization') ----- > >>- initialize > >>- "ChronologyConstants initialize" > >>- > >>- SqueakEpoch := 2415386. "Julian day number of 1 Jan > >>1901" - SecondsInDay := 86400. > >>- SecondsInHour := 3600. > >>- SecondsInMinute := 60. > >>- MicrosecondsInDay := 24 * 60 * 60 * 1000000. > >>- NanosInSecond := 10 raisedTo: 9. > >>- NanosInMillisecond := 10 raisedTo: 6. > >>- DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday > >>Saturday). > >>- > >>- MonthNames := #( January February March April May June > >>- July August September > >>October November December). > >>- DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! > >> > >>Item was changed: > >> Timespan subclass: #Date > >> instanceVariableNames: '' > >> classVariableNames: '' > >>+ poolDictionaries: '' > >>- poolDictionaries: 'ChronologyConstants' > >> category: 'Chronology-Core'! > >> > >> !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! > >> Instances of Date are Timespans with duration of 1 day. As with all > >> Chronology Timespan sub-instances, Dates can be instantiated as > >> position values which compare equally to any other instance of the same > >> Date, irregardless of the timezone in which either is created. > >> > >> However, like the other Timespan subInstances, there are rare cases > >> where it may be desirable to use instances of Date to represent a > >> particular 1-day span of time at a particular locality on the globe. > >> All Timespans, including Dates, may specify a particular timezone > >> offset for this purpose.! > >> > >>Item was changed: > >> ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- > >> fromDays: dayCount > >> "Days since 1 January 1901" > >> > >>+ ^ self julianDayNumber: dayCount + Time squeakEpoch! > >>- ^ self julianDayNumber: dayCount + SqueakEpoch! > >> > >>Item was changed: > >> ----- Method: Date class>>starting: (in category 'squeak protocol') > >> ----- > >> starting: aDateAndTime > >> ^ self > >> starting: aDateAndTime midnight > >>+ duration: 1 day! > >>- duration: Duration oneDay! > >> > >>Item was changed: > >> Magnitude subclass: #DateAndTime > >> instanceVariableNames: 'utcMicroseconds localOffsetSeconds' > >> classVariableNames: 'AutomaticTimezone ClockProvider > >> InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' > >>+ poolDictionaries: '' > >>- poolDictionaries: 'ChronologyConstants' > >> category: 'Chronology-Core'! > >> > >> !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! > >> I represent a point in UTC time as defined by ISO 8601. I have zero > >> duration. > >> > >> My implementation uses variables utcMicroseconds and > >> localOffsetSeconds. This represents time magnitude as elapsed > >> microseconds since the Posix epoch, with localOffsetSeconds > >> representing local offset from UTC. The magnitude is used for > >> comparison and duration calculations, and the local offset is used for > >> displaying this magnitude in the context of a local time zone. > >> > >> The implementation ignores leap seconds, which are adjustments made to > >> maintain earth rotational clock time in synchronization with elapsed > >> seconds. > >> > >> DateAndTime class>>now will use #primitiveUtcWithOffset to obtain > >> current time in UTC microseconds with current local offset in seconds. > >> The primitive provides an atomic query for UTC time and local offset as > >> measured by the OS platform. If primitiveUtcWithOffset is not > >> available, the traditional implementation is used, which relies on a > >> primitive for microseconds in the local time zone and derives UTC based > >> on the TimeZone setting. > >> ! > >> > >>Item was changed: > >> ----- Method: DateAndTime class>>epochOffset (in category 'private') > >> ----- > >> epochOffset > >> "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" > >>+ ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! > >>- ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! > >> > >>Item was changed: > >> ----- Method: DateAndTime>>posixEpochJulianDays (in category > >> 'initialize-release') ----- > >> posixEpochJulianDays > >> > >>+ ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! > >>- ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! > >> > >>Item was changed: > >> ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- > >> ticks: ticks offset: utcOffset > >> "ticks is {julianDayNumber. secondCount. nanoSeconds}" > >> > >> | jdn s nanos normalizedTicks | > >> normalizedTicks := ticks copy. > >>+ self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. > >>+ self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. > >>- self normalize: 3 ticks: normalizedTicks base: NanosInSecond. > >>- self normalize: 2 ticks: normalizedTicks base: SecondsInDay. > >> > >> jdn := normalizedTicks at: 1. > >> s := normalizedTicks at: 2. > >> nanos := normalizedTicks at: 3. > >> localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset > >> asSeconds]. > >> utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: > >> nanos offset: localOffsetSeconds. > >> ! > >> > >>Item was changed: > >> Magnitude subclass: #Duration > >> instanceVariableNames: 'nanos seconds' > >> classVariableNames: '' > >>+ poolDictionaries: '' > >>- poolDictionaries: 'ChronologyConstants' > >> category: 'Chronology-Core'! > >> > >> !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > >> I represent a duration of time. I have nanosecond precision! > >> > >>Item was changed: > >> ----- Method: Duration class>>days: (in category 'squeak protocol') > >> ----- > >> days: aNumber > >> > >>+ ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! > >>- ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! > >> > >>Item was changed: > >> ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: > >> (in category 'squeak protocol') ----- > >> days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: > >> nanos > >> > >> ^self > >> seconds: seconds > >>+ + (minutes * Time secondsInMinute) > >>+ + (hours * Time secondsInHour) > >>+ + (days * Time secondsInDay) > >>- + (minutes * SecondsInMinute) > >>- + (hours * SecondsInHour) > >>- + (days * SecondsInDay) > >> nanoSeconds: nanos > >> ! > >> > >>Item was changed: > >> ----- Method: Duration class>>days:seconds: (in category 'ansi > >> protocol') ----- > >> days: days seconds: seconds > >> > >>+ ^ self basicNew seconds: days * Time secondsInDay + seconds > >>nanoSeconds: 0! > >>- ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 > >>- ! > >> > >>Item was changed: > >> ----- Method: Duration class>>hours: (in category 'squeak protocol') > >> ----- > >> hours: aNumber > >> > >>+ ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! > >>- ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! > >> > >>Item was removed: > >>- ----- Method: Duration class>>initialize (in category > >>'initialize-release') ----- > >>- initialize > >>- ChronologyConstants classPool > >>- at: #Zero > >>- put: > >>- (self basicNew > >>- seconds: 0 > >>- nanoSeconds: 0) ; > >>- at: #OneDay > >>- put: 1 day! > >> > >>Item was changed: > >> ----- Method: Duration class>>milliSeconds: (in category 'squeak > >> protocol') ----- > >> milliSeconds: milliCount > >> > >> ^self > >> seconds: (milliCount quo: 1000) > >>+ nanoSeconds: (milliCount rem: 1000) * 1000000! > >>- nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! > >> > >>Item was changed: > >> ----- Method: Duration class>>minutes: (in category 'squeak protocol') > >> ----- > >> minutes: aNumber > >> > >>+ ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! > >>- ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! > >> > >>Item was changed: > >> ----- Method: Duration class>>nanoSeconds: (in category 'squeak > >> protocol') ----- > >> nanoSeconds: nanos > >> "This method is slow. If you have nanos less than 10^6 you should > >> use #seconds:nanoSeconds: instead." > >> > >> | quo | > >>+ quo _ nanos quo: Time nanosInSecond. > >>- quo := nanos quo: NanosInSecond. > >> ^ self basicNew > >> seconds: quo > >>+ nanoSeconds: nanos - (quo * Time nanosInSecond)! > >>- nanoSeconds: nanos - (quo * NanosInSecond) > >>- ! > >> > >>Item was changed: > >> ----- Method: Duration class>>oneDay (in category 'squeak protocol') > >> ----- > >> oneDay > >> "Answer the canonicalized Duration representing length of 1 day. > >> Used by Dates." > >>+ ^ 1 day! > >>- ^ OneDay! > >> > >>Item was changed: > >> ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak > >> protocol') ----- > >> seconds: seconds nanoSeconds: nanos > >> > >> ^ self basicNew > >> seconds: seconds truncated > >>+ nanoSeconds: seconds fractionPart * Time nanosInSecond + > >>nanos! > >>- nanoSeconds: seconds fractionPart * NanosInSecond + nanos! > >> > >>Item was changed: > >> ----- Method: Duration class>>zero (in category 'ansi protocol') ----- > >> zero > >>+ > >>+ ^ self basicNew seconds: 0 nanoSeconds: 0 > >>+ ! > >>- "Answer the canonicalized instance of Duration zero." > >>- ^ Zero! > >> > >>Item was changed: > >> ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') > >> ----- > >> asNanoSeconds > >> > >>+ ^seconds * Time nanosInSecond + nanos! > >>- ^seconds * NanosInSecond + nanos! > >> > >>Item was changed: > >> ----- Method: Duration>>days (in category 'ansi protocol') ----- > >> days > >> "Answer the number of days the receiver represents." > >> > >>+ ^ seconds quo: Time secondsInDay! > >>- ^ seconds quo: SecondsInDay > >>- ! > >> > >>Item was changed: > >> ----- Method: Duration>>hours (in category 'ansi protocol') ----- > >> hours > >> "Answer the number of hours the receiver represents." > >> > >> > >>+ ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! > >>- ^ (seconds rem: SecondsInDay) quo: SecondsInHour! > >> > >>Item was changed: > >> ----- Method: Duration>>minutes (in category 'ansi protocol') ----- > >> minutes > >>- > >> "Answer the number of minutes the receiver represents." > >> > >>+ ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! > >>- > >>- ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! > >> > >>Item was changed: > >> ----- Method: Duration>>seconds (in category 'ansi protocol') ----- > >> seconds > >> "Answer the number of seconds the receiver represents." > >> > >>+ ^seconds rem: Time secondsInMinute! > >>- ^seconds rem: SecondsInMinute! > >> > >>Item was changed: > >> ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') > >> ----- > >> seconds: secondCount nanoSeconds: nanoCount > >> "Private - only used by Duration class" > >> > >> seconds := secondCount. > >> nanos := nanoCount rounded. > >> "normalize if signs do not match" > >> [ nanos < 0 and: [ seconds > 0 ] ] > >> whileTrue: [ seconds := seconds - 1. > >>+ nanos := nanos + Time nanosInSecond ]. > >>- nanos := nanos + NanosInSecond ]. > >> [ seconds < 0 and: [ nanos > 0 ] ] > >> whileTrue: [ seconds := seconds + 1. > >>+ nanos := nanos - Time nanosInSecond ] > >>- nanos := nanos - NanosInSecond ] > >> > >> ! > >> > >>Item was changed: > >> ----- Method: Duration>>ticks (in category 'private') ----- > >> ticks > >> "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime > >> and Time." > >> > >> | days | > >> days := self days. > >> ^ Array > >> with: days > >>+ with: seconds - (days * Time secondsInDay) > >>- with: seconds - (days * SecondsInDay) > >> with: nanos > >> ! > >> > >>Item was changed: > >> Timespan subclass: #Month > >> instanceVariableNames: '' > >> classVariableNames: '' > >>+ poolDictionaries: '' > >>- poolDictionaries: 'ChronologyConstants' > >> category: 'Chronology-Core'! > >> > >> !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > >> I represent a month. > >> > >> For example, to get the number of days this month, you can evaluate the > >> following expression: > >> > >> Month current daysInMonth! > >> > >>Item was added: > >>+ ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- > >>+ daysInMonth > >>+ ^#(31 28 31 30 31 30 31 31 30 31 30 31)! > >> > >>Item was changed: > >> ----- Method: Month class>>daysInMonth:forYear: (in category > >> 'smalltalk-80') ----- > >> daysInMonth: indexOrName forYear: yearInteger > >> > >> | index | > >> index := indexOrName isInteger > >> ifTrue: [indexOrName] > >> ifFalse: [self indexOfMonth: indexOrName]. > >>+ ^ (self daysInMonth at: index) > >>- ^ (DaysInMonth at: index) > >> + ((index = 2 > >> and: [Year isLeapYear: yearInteger]) > >> ifTrue: [1] ifFalse: [0]) > >> ! > >> > >>Item was changed: > >> ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') > >> ----- > >> indexOfMonth: aMonthName > >> > >> > >>+ 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: > >>i)) ifTrue: [^i] ]. > >>+ self error: aMonthName , ' is not a recognized month name'! > >>- 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) > >>ifTrue: [^i] ]. > >>- self error: aMonthName , ' is not a recognized month name'.! > >> > >>Item was added: > >>+ ----- Method: Month class>>monthNames (in category 'inquiries') ----- > >>+ monthNames > >>+ ^#(January February March April May June July August September > >>October November December)! > >> > >>Item was changed: > >> ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') > >> ----- > >> nameOfMonth: anIndex > >> > >>+ ^ self monthNames at: anIndex! > >>- ^ MonthNames at: anIndex.! > >> > >>Item was changed: > >> Magnitude subclass: #Time > >> instanceVariableNames: 'seconds nanos' > >> classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond > >> LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' > >>+ poolDictionaries: '' > >>- poolDictionaries: 'ChronologyConstants' > >> category: 'Chronology-Core'! > >> > >> !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! > >> This represents a particular point in time during any given day. For > >> example, '5:19:45 pm'. > >> > >> If you need a point in time on a particular day, use DateAndTime. If > >> you need a duration of time, use Duration. > >> ! > >> > >>Item was changed: > >> ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') > >> ----- > >> fromSeconds: secondCount > >> "Answer an instance of me that is secondCount number of seconds > >> since midnight." > >> > >> | integerSeconds nanos | > >> integerSeconds := secondCount truncated. > >> integerSeconds = secondCount > >> ifTrue: [nanos := 0] > >>+ ifFalse: [nanos := (secondCount - integerSeconds * self > >>nanosInSecond) asInteger]. > >>- ifFalse: [nanos := (secondCount - integerSeconds * > >>NanosInSecond) asInteger]. > >> ^ self seconds: integerSeconds nanoSeconds: nanos > >> ! > >> > >>Item was changed: > >> ----- Method: Time class>>hour:minute:second:nanoSecond: (in category > >> 'squeak protocol') ----- > >>+ hour: hour minute: minute second: second nanoSecond: nanoCount > >>+ "Answer a Time" > >>- hour: hour minute: minute second: second nanoSecond: nanoCount > >>- "Answer a Time - only second precision for now" > >> > >> ^ self > >>+ seconds: (hour * self secondsInHour) + (minute * self > >>secondsInMinute) + second + nanoSeconds: nanoCount! > >>- seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) > >>+ second - nanoSeconds: nanoCount > >>- ! > >> > >>Item was added: > >>+ ----- Method: Time class>>nanosInSecond (in category 'constants') ----- > >>+ nanosInSecond > >>+ ^ 1000000000! > >> > >>Item was changed: > >> ----- Method: Time class>>noon (in category 'squeak protocol') ----- > >> noon > >> > >>+ ^ self seconds: self secondsInDay / 2! > >>- ^ self seconds: (SecondsInDay / 2) > >>- ! > >> > >>Item was changed: > >> ----- Method: Time class>>now (in category 'ansi protocol') ----- > >> now > >> "Answer a Time representing the time right now - this is a 24 hour > >> clock." > >> | localUsecs localUsecsToday | > >> localUsecs := self localMicrosecondClock. > >>+ localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * > >>1000000" > >>- localUsecsToday := localUsecs \\ MicrosecondsInDay. > >> ^ self > >> seconds: localUsecsToday // 1000000 > >> nanoSeconds: localUsecsToday \\ 1000000 * 1000! > >> > >>Item was added: > >>+ ----- Method: Time class>>secondsInDay (in category 'constants') ----- > >>+ secondsInDay > >>+ ^86400! > >> > >>Item was added: > >>+ ----- Method: Time class>>secondsInHour (in category 'constants') ----- > >>+ secondsInHour > >>+ ^3600! > >> > >>Item was added: > >>+ ----- Method: Time class>>secondsInMinute (in category 'constants') > >>----- > >>+ secondsInMinute > >>+ ^60! > >> > >>Item was added: > >>+ ----- Method: Time class>>squeakEpoch (in category 'constants') ----- > >>+ squeakEpoch > >>+ ^ 2415386. "Julian day number of 1 Jan 1901"! > >> > >>Item was changed: > >> ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category > >> 'printing') ----- > >> print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds > >> on: aStream > >> "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, > >> 'hh:mm' or 'h:mm am'. > >> If showSubseconds is true and our nanoSeconds are not zero, a > >> decimal point and subseconds are added" > >> > >> | h m s | > >> h := self hour. m := self minute. s := self second. > >> hr24 > >> ifTrue: > >> [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. > >> h printOn: aStream ] > >> ifFalse: > >> [ h > 12 > >> ifTrue: [h - 12 printOn: aStream] > >> ifFalse: > >> [h < 1 > >> ifTrue: [ 12 printOn: > >> aStream ] > >> ifFalse: [ h printOn: > >> aStream ]]]. > >> > >> aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). > >> m printOn: aStream. > >> > >> showSeconds ifTrue: > >> [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). > >> (showSubseconds not or: [self nanoSecond = 0]) > >> ifTrue: [s asInteger printOn: aStream] > >>+ ifFalse: [s asInteger * Time nanosInSecond + self > >>nanoSecond asInteger + printOn: aStream > >>asFixedPoint: Time nanosInSecond]]. > >>- ifFalse: [s asInteger * NanosInSecond + self > >>nanoSecond asInteger - printOn: aStream > >>asFixedPoint: NanosInSecond]]. > >> > >> hr24 ifFalse: > >> [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' > >> pm']) ]. > >> ! > >> > >>Item was changed: > >> Object subclass: #TimeZone > >> instanceVariableNames: 'offset abbreviation name' > >> classVariableNames: '' > >>+ poolDictionaries: '' > >>- poolDictionaries: 'ChronologyConstants' > >> category: 'Chronology-Core'! > >> > >> !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > >> TimeZone is a simple class to colect the information identifying a UTC > >> time zone. > >> > >> offset - Duration - the time zone's > >> offset from UTC > >> abbreviation - String - the abbreviated name for > >> the time zone. > >> name - String - the name of the > >> time zone. > >> > >> TimeZone class >> #timeZones returns an array of the known time zones > >> TimeZone class >> #default returns the default time zone (Grenwich Mean > >> Time) > >> ! > >> > >>Item was changed: > >> Timespan subclass: #Week > >> instanceVariableNames: '' > >> classVariableNames: 'StartDay' > >>+ poolDictionaries: '' > >>- poolDictionaries: 'ChronologyConstants' > >> category: 'Chronology-Core'! > >> > >> !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > >> I represent a week. > >> > >> To find out what days of the week on which Squeak is fun, select the > >> following expression, and print it: > >> > >> Week dayNames! > >> > >>Item was changed: > >>+ ----- Method: Week class>>dayNames (in category 'inquiries') ----- > >>- ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- > >> dayNames > >> > >>+ ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! > >>- ^ DayNames! > >> > >>Item was changed: > >> ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') > >> ----- > >> indexOfDay: aSymbol > >> > >>+ ^ self dayNames indexOf: aSymbol! > >>- ^ DayNames indexOf: aSymbol! > >> > >>Item was changed: > >> ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- > >> nameOfDay: anIndex > >> > >>+ ^ self dayNames at: anIndex! > >>- ^ DayNames at: anIndex! > >> > >>Item was changed: > >> ----- Method: Week class>>startDay (in category 'squeak protocol') ----- > >> startDay > >> > >> ^ StartDay ifNil: [ StartDay > >>+ := self dayNames first ] > >>- := DayNames first ] > >> ! > >> > >>Item was changed: > >> ----- Method: Week class>>startDay: (in category 'squeak protocol') > >> ----- > >> startDay: aSymbol > >> > >>+ (self dayNames includes: aSymbol) > >>- (DayNames includes: aSymbol) > >> ifTrue: [ StartDay := aSymbol ] > >> ifFalse: [ self error: aSymbol, ' is not a recognised day > >> name' ]! > >> > >>Item was changed: > >> ----- Method: Week class>>starting:duration: (in category 'squeak > >> protocol') ----- > >> starting: aDateAndTime duration: aDuration > >> "Override - the duration is always one week. > >> Week will start from the Week class>>startDay" > >> > >> | midnight delta adjusted | > >> midnight := aDateAndTime asDateAndTime midnight. > >>+ delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self > >>startDay)) rem: 7) abs. > >>- delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self > >>startDay)) rem: 7) abs. > >> adjusted := midnight - (Duration days: delta seconds: 0). > >> > >> ^ super starting: adjusted duration: (Duration weeks: 1)! > >> > >> > From lewis at mail.msen.com Sat Sep 5 02:10:21 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Fri, 4 Sep 2020 22:10:21 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz In-Reply-To: <20200905010408.GA3671@shell.msen.com> References: <20200904150058.GA11481@shell.msen.com> <20200905010408.GA3671@shell.msen.com> Message-ID: <20200905021021.GA12864@shell.msen.com> On Fri, Sep 04, 2020 at 09:04:08PM -0400, David T. Lewis wrote: > > On Sat, Sep 05, 2020 at 12:01:21AM +0200, Levente Uzonyi wrote: > > > 2) Some replacement methods are not quick and create new objects. For > > example, OneDay, a single Duration of one day is replaced with #oneDay, a > > method returning the expression 1 day. Each invocation of the method > > creates a new object. > > In my image, there are 19821 Date instances. If all of those had their own > > custom one day long Duration objects, which is a side effect of these > > changes in the long run, my image would be ~620 kiB larger (64-bit image, > > object header + 2 variables => 32 bytes each). > > There's a solution: cache the shared instance in a variable. :) > > If it is a problem, then this is an error on my part (not Juan's). After > adopting the main part of the Cuis changes, #oneDay was one of a couple > of remaining cases that I addressed with trivial replacements. > > But there are no senders of #oneDay in the image other than a unit test. > So I think that it is not likely to be an issue. > Levente, My apologies, I was careless and you are right. Duration class>>oneDay is sent by Date class>>starting: and therefore it does create redundant new objects exactly as you said. This is an error on my part and would need to be fixed before the changes could be considered for trunk. Dave From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 11:10:29 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 11:10:29 +0000 Subject: [squeak-dev] Two new questions about VM machinery :-) Message-ID: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> Hi all, hi Eliot, just for curiosity, I have two new questions about the VM machinery (which are far away from praxis but close to my interests): 1. super on ProtoObject What would you expect the following to return? x := Compiler evaluate: 'super identityHash' for: ProtoObject new. thisContext objectClass: x. I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. If you debug the call instead (Context class >> #runSimulated:), Context >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense but unfortunately is not consistent with the original VM behavior. Maybe the Compiler should forbid any call to super from ProtoObject instances at all (more formally: if the receiver's class's superclass is nil)? Or should we adjust the simulation code? (By the way: If you do the same in Squeak.js, you'll get an infinite recursion :D) 2. Mirror primitives What is the reason for primitives such as 75 (#identityHash) or 78/139 (#nextInstance/#nextObject) not being mirrored in Context? Was this simply forgotten, or don't we have the claim to mirror any "essential" primitive without it actually being needed by the simulation machinery? As always, looking forward to your interesting explanations! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 11:17:38 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 11:17:38 +0000 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> Message-ID: <2ffdd60468dd434ea886eba44e8a2f95@student.hpi.uni-potsdam.de> > x := Compiler evaluate: 'super identityHash' for: ProtoObject new. > thisContext objectClass: x. Also interesting: 'super bar' returns a DNU where the receiver is a SmallInteger (sic!). In a second image, the call is skipped, again ... ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Samstag, 5. September 2020 13:10:29 An: Squeak Dev Betreff: [squeak-dev] Two new questions about VM machinery :-) Hi all, hi Eliot, just for curiosity, I have two new questions about the VM machinery (which are far away from praxis but close to my interests): 1. super on ProtoObject What would you expect the following to return? x := Compiler evaluate: 'super identityHash' for: ProtoObject new. thisContext objectClass: x. I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. If you debug the call instead (Context class >> #runSimulated:), Context >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense but unfortunately is not consistent with the original VM behavior. Maybe the Compiler should forbid any call to super from ProtoObject instances at all (more formally: if the receiver's class's superclass is nil)? Or should we adjust the simulation code? (By the way: If you do the same in Squeak.js, you'll get an infinite recursion :D) 2. Mirror primitives What is the reason for primitives such as 75 (#identityHash) or 78/139 (#nextInstance/#nextObject) not being mirrored in Context? Was this simply forgotten, or don't we have the claim to mirror any "essential" primitive without it actually being needed by the simulation machinery? As always, looking forward to your interesting explanations! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Sat Sep 5 11:25:26 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sat, 5 Sep 2020 13:25:26 +0200 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> Message-ID: <9FF45C95-BA33-4EAD-9E2A-823C9A2BB563@gmx.de> > On 05.09.2020, at 13:10, Thiede, Christoph wrote: > > What would you expect the following to return? > x := Compiler evaluate: 'super identityHash' for: ProtoObject new. > thisContext objectClass: x. > I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. > But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. No. you're just missing the return, that's why it answers self… -t From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 11:34:33 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 11:34:33 +0000 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: <9FF45C95-BA33-4EAD-9E2A-823C9A2BB563@gmx.de> References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de>, <9FF45C95-BA33-4EAD-9E2A-823C9A2BB563@gmx.de> Message-ID: <0e3c7a47cc924e01896f6e2cbba645af@student.hpi.uni-potsdam.de> Hi Tobias, are you sure about that? [Compiler evaluate: 'self identityHash' for: ProtoObject new] answers a me a SmallInteger. Compiler >> #evaluate:[...] returns the last stack value automatically, just a like a printIt. See senders of #returnSelfIfNoOther: in Parser. :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Samstag, 5. September 2020 13:25:26 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) > On 05.09.2020, at 13:10, Thiede, Christoph wrote: > > What would you expect the following to return? > x := Compiler evaluate: 'super identityHash' for: ProtoObject new. > thisContext objectClass: x. > I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. > But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. No. you're just missing the return, that's why it answers self… -t -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 15:01:41 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 15:01:41 +0000 Subject: [squeak-dev] Are we missing some string encoding in SqueakSSL/WebClient? Message-ID: <151190a158c9417aa22b3ac64e263ae0@student.hpi.uni-potsdam.de> Hi all, while doing some experiments with WebClient & WebUtils in order to send textual data to a server via HTTP(S), I found out that posting a request containing non-ASCII characters in a multipart/form-data yields a primitive failure from SqueakSSL/primitiveEncrypt (occurs on both Win32 + emulated Linux/Ubuntu). When I converted the text manually using #squeakToUtf8 before putting it into the contents data, everything worked fine and the server receives the correct text without any encoding problems. So for my application, I can manually #squeakToUtf8-convert the request string before posting it, but I wonder whether this should really be the responsibility of every Squeak developer and not rather one of the WebClient? For illustration: If you use normal #htmlSubmit:fields: instead of #httpPost:multipartFields:, WebClient does all necessary conversion (#encodeUrlEncodedForm:, String >> #encodeForHTTP) itself. Anyway, #encodeMultipartForm:boundary: does not care about conversion. I am only a bloody newbie to all this web stuff, so maybe I am missing something important. What do you think? Should we add #squeakToUtf8 conversion in WebUtils class >> #encodeMultipartForm:boundary:, and in the decode message vice versa? Or would this rather be a responsibility of the SqueakSSL protocol? I am looking forward to your help! Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sat Sep 5 15:37:50 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 5 Sep 2020 15:37:50 0000 Subject: [squeak-dev] The Trunk: Chronology-Core-dtl.57.mcz Message-ID: David T. Lewis uploaded a new version of Chronology-Core to project The Trunk: http://source.squeak.org/trunk/Chronology-Core-dtl.57.mcz ==================== Summary ==================== Name: Chronology-Core-dtl.57 Author: dtl Time: 5 September 2020, 11:37:48.320554 am UUID: a2e27812-3d4b-4db0-b3ce-e1371c1c895b Ancestors: Chronology-Core-ul.55 Clean up a leftover FIXME in DateAndTime>>asTimeStamp =============== Diff against Chronology-Core-ul.55 =============== Item was changed: ----- Method: DateAndTime>>asTimeStamp (in category 'squeak protocol') ----- asTimeStamp + ^ self as: TimeStamp! - ^ self - asDateAndTime "FIXME LX hack for test support" - as: TimeStamp! From christoph.thiede at student.hpi.uni-potsdam.de Sat Sep 5 16:12:55 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Sat, 5 Sep 2020 11:12:55 -0500 (CDT) Subject: [squeak-dev] Unicode In-Reply-To: References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> <8DBCEF4B-055E-4F97-9D42-B55A1D5384C5@gmail.com> Message-ID: <1599322375667-0.post@n4.nabble.com> Hi all, I would still be interested in resuming this project and supporting the latest Unicode codepoints in Squeak. Is there really no one who could find some minutes to review my proposed design change? Or putting it another way: If I will upload these changes into the inbox, will anyone merge it? :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From karlramberg at gmail.com Sat Sep 5 16:35:12 2020 From: karlramberg at gmail.com (karl ramberg) Date: Sat, 5 Sep 2020 18:35:12 +0200 Subject: [squeak-dev] I just purchased squeakbooks.com and .org In-Reply-To: References: <17458e0e4f9.106e12a8315071.1205817659484112188@zoho.com> Message-ID: On Fri, Sep 4, 2020 at 3:49 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > But the HelpBrowser is read only at the moment. (Some skeleton methods > exist for writing documentation) > > I uploaded a lot of stuff concerning this to the inbox last year. Unless > you want to achieve something very fancy, you do not need to descend into > the System Browser any longer. Creating, removing, renaming, and editing > topics is all possible via the HelpBrowser. Moreover, with Morphic-ct.1587, > you can even insert images without writing code. :-) > The changes are still waiting for anyone who has some time to review and > eventually merge them. > Wow, lots of stuff here. Not sure I understand all of it. Do you have a short description for creating a new help topic after filing in all the changes ? :-D Best, Karl > > Best, > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von karl ramberg > *Gesendet:* Freitag, 4. September 2020 15:24:06 > *An:* gettimothy; The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] I just purchased squeakbooks.com and .org > > > > On Fri, Sep 4, 2020 at 1:29 PM gettimothy via Squeak-dev < > squeak-dev at lists.squeakfoundation.org> wrote: > >> Hi folks, >> >> I am going to try to recreate books.pharo.org using those domains, but >> for Squeak as part of my Doc/SeasideDoc project. >> >> I am NOT trying to make money for myself, I am just trying to solve the >> Squeak documentation problem. >> >> I will be building the site with Seaside on Squeak and I will be studying >> the Pharo model for their documentation creation to see if it is >> appropriate for squeak. >> >> Motivation: >> http://books.pharo.org/booklet-Scraping/html/scrapingbook.html >> >> That is an example of the type of documentation I frequently use as a >> developer. How do I accomplish a specific task, where do I start, what are >> the conceptual "things" I have to think in. >> >> I will be using that documentation to get myself familiarized with the >> Squeak XML stuff as I will need it for processing xml documents of 10's of >> millions of records. >> >> I will be happy to give the domains to the squeak community when/if the >> project is solid. >> >> Once the stubbed website is up, I will attempt to create a >> live-round-robin model from the squeak image. >> >> Open a workspace, open a book (book content comes down from website) >> add/edit content within the workspace, save it live to the website (and >> concurrently to the image ?) >> > > It would be a really nice addition to be able to write "how to's" and > documentation in an almost literal coding style within Squeak. > > The HelpBrowser has the ability to pull down content from the Squeak Wiki. > And we have added inlining pictures in it: > > http://forum.world.st/The-Trunk-MonticelloConfigurations-mt-160-mcz-td5115112.html#a5116431 > > But the HelpBrowser is read only at the moment. (Some skeleton methods > exist for writing documentation) > > Best, > Karl > > >> Hopefully it will be useful and we can start crowdsourcing documentation. >> >> >> >> cheers, >> >> tty. >> >> >> >> >> >> >> >> >> >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Sat Sep 5 16:59:06 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sat, 5 Sep 2020 18:59:06 +0200 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: <0e3c7a47cc924e01896f6e2cbba645af@student.hpi.uni-potsdam.de> References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> <9FF45C95-BA33-4EAD-9E2A-823C9A2BB563@gmx.de> <0e3c7a47cc924e01896f6e2cbba645af@student.hpi.uni-potsdam.de> Message-ID: <3BBE9B1A-3274-4499-8C0E-0E0B037B62DC@gmx.de> > On 05.09.2020, at 13:34, Thiede, Christoph wrote: > > Hi Tobias, > > are you sure about that? > [Compiler evaluate: 'self identityHash' for: ProtoObject new] answers a me a SmallInteger. Compiler >> #evaluate:[...] returns the last stack value automatically, just a like a printIt. See senders of #returnSelfIfNoOther: in Parser. :-) Ah, I thought it be just a method, I misread that. That said, 'super' needs a lexical scope, can you do the whole thing in a proper method? -t > > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Samstag, 5. September 2020 13:25:26 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) > > > > On 05.09.2020, at 13:10, Thiede, Christoph wrote: > > > > What would you expect the following to return? > > x := Compiler evaluate: 'super identityHash' for: ProtoObject new. > > thisContext objectClass: x. > > I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. > > But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. > > No. you're just missing the return, that's why it answers self… > -t From commits at source.squeak.org Sat Sep 5 17:35:43 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 5 Sep 2020 17:35:43 0000 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.58.mcz Message-ID: A new version of Chronology-Core was added to project The Inbox: http://source.squeak.org/inbox/Chronology-Core-dtl.58.mcz ==================== Summary ==================== Name: Chronology-Core-dtl.58 Author: dtl Time: 5 September 2020, 1:35:43.337716 pm UUID: c6ada6e9-d4a0-4f20-a71b-53ea65a114b6 Ancestors: Chronology-Core-dtl.57 Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. For Squeak, OneDay is now a class variable in order to continue to allow Date instances to share a Duration. Cuis creates a new Duration for each Date. =============== Diff against Chronology-Core-dtl.57 =============== Item was removed: - SharedPool subclass: #ChronologyConstants - instanceVariableNames: '' - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' - poolDictionaries: '' - category: 'Chronology-Core'! - - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! Item was removed: - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- - initialize - "ChronologyConstants initialize" - - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" - SecondsInDay := 86400. - SecondsInHour := 3600. - SecondsInMinute := 60. - MicrosecondsInDay := 24 * 60 * 60 * 1000000. - NanosInSecond := 10 raisedTo: 9. - NanosInMillisecond := 10 raisedTo: 6. - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). - - MonthNames := #( January February March April May June - July August September October November December). - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! Item was changed: Timespan subclass: #Date instanceVariableNames: '' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! Item was changed: ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- fromDays: dayCount "Days since 1 January 1901" + ^ self julianDayNumber: dayCount + Time squeakEpoch! - ^ self julianDayNumber: dayCount + SqueakEpoch! Item was changed: Magnitude subclass: #DateAndTime instanceVariableNames: 'utcMicroseconds localOffsetSeconds' classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! I represent a point in UTC time as defined by ISO 8601. I have zero duration. My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. ! Item was changed: ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- epochOffset "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! Item was changed: ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- posixEpochJulianDays + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! Item was changed: ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- ticks: ticks offset: utcOffset "ticks is {julianDayNumber. secondCount. nanoSeconds}" | jdn s nanos normalizedTicks | normalizedTicks := ticks copy. + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. jdn := normalizedTicks at: 1. s := normalizedTicks at: 2. nanos := normalizedTicks at: 3. localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. ! Item was changed: Magnitude subclass: #Duration instanceVariableNames: 'nanos seconds' + classVariableNames: 'OneDay' + poolDictionaries: '' - classVariableNames: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! I represent a duration of time. I have nanosecond precision! Item was changed: ----- Method: Duration class>>days: (in category 'squeak protocol') ----- days: aNumber + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! Item was changed: ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos ^self seconds: seconds + + (minutes * Time secondsInMinute) + + (hours * Time secondsInHour) + + (days * Time secondsInDay) - + (minutes * SecondsInMinute) - + (hours * SecondsInHour) - + (days * SecondsInDay) nanoSeconds: nanos ! Item was changed: ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- days: days seconds: seconds + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 - ! Item was changed: ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- hours: aNumber + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! Item was changed: ----- Method: Duration class>>initialize (in category 'initialize-release') ----- initialize + "Duration oneDay is used in Date creation, and is cached to allow + sharing the instance." + OneDay := 1 day. + self class recompile: #oneDay.! - ChronologyConstants classPool - at: #Zero - put: - (self basicNew - seconds: 0 - nanoSeconds: 0) ; - at: #OneDay - put: 1 day! Item was changed: ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- milliSeconds: milliCount ^self seconds: (milliCount quo: 1000) + nanoSeconds: (milliCount rem: 1000) * 1000000! - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! Item was changed: ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- minutes: aNumber + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! Item was changed: ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- nanoSeconds: nanos "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." | quo | + quo _ nanos quo: Time nanosInSecond. - quo := nanos quo: NanosInSecond. ^ self basicNew seconds: quo + nanoSeconds: nanos - (quo * Time nanosInSecond)! - nanoSeconds: nanos - (quo * NanosInSecond) - ! Item was changed: ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- seconds: seconds nanoSeconds: nanos ^ self basicNew seconds: seconds truncated + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! Item was changed: ----- Method: Duration class>>zero (in category 'ansi protocol') ----- zero + + ^ self basicNew seconds: 0 nanoSeconds: 0 + ! - "Answer the canonicalized instance of Duration zero." - ^ Zero! Item was changed: ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- asNanoSeconds + ^seconds * Time nanosInSecond + nanos! - ^seconds * NanosInSecond + nanos! Item was changed: ----- Method: Duration>>days (in category 'ansi protocol') ----- days "Answer the number of days the receiver represents." + ^ seconds quo: Time secondsInDay! - ^ seconds quo: SecondsInDay - ! Item was changed: ----- Method: Duration>>hours (in category 'ansi protocol') ----- hours "Answer the number of hours the receiver represents." + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! Item was changed: ----- Method: Duration>>minutes (in category 'ansi protocol') ----- minutes - "Answer the number of minutes the receiver represents." + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! - - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! Item was changed: ----- Method: Duration>>seconds (in category 'ansi protocol') ----- seconds "Answer the number of seconds the receiver represents." + ^seconds rem: Time secondsInMinute! - ^seconds rem: SecondsInMinute! Item was changed: ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- seconds: secondCount nanoSeconds: nanoCount "Private - only used by Duration class" seconds := secondCount. nanos := nanoCount rounded. "normalize if signs do not match" [ nanos < 0 and: [ seconds > 0 ] ] whileTrue: [ seconds := seconds - 1. + nanos := nanos + Time nanosInSecond ]. - nanos := nanos + NanosInSecond ]. [ seconds < 0 and: [ nanos > 0 ] ] whileTrue: [ seconds := seconds + 1. + nanos := nanos - Time nanosInSecond ] - nanos := nanos - NanosInSecond ] ! Item was changed: ----- Method: Duration>>ticks (in category 'private') ----- ticks "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." | days | days := self days. ^ Array with: days + with: seconds - (days * Time secondsInDay) - with: seconds - (days * SecondsInDay) with: nanos ! Item was changed: Timespan subclass: #Month instanceVariableNames: '' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! I represent a month. For example, to get the number of days this month, you can evaluate the following expression: Month current daysInMonth! Item was added: + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- + daysInMonth + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! Item was changed: ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- daysInMonth: indexOrName forYear: yearInteger | index | index := indexOrName isInteger ifTrue: [indexOrName] ifFalse: [self indexOfMonth: indexOrName]. + ^ (self daysInMonth at: index) - ^ (DaysInMonth at: index) + ((index = 2 and: [Year isLeapYear: yearInteger]) ifTrue: [1] ifFalse: [0]) ! Item was changed: ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- indexOfMonth: aMonthName + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. + self error: aMonthName , ' is not a recognized month name'! - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. - self error: aMonthName , ' is not a recognized month name'.! Item was added: + ----- Method: Month class>>monthNames (in category 'inquiries') ----- + monthNames + ^#(January February March April May June July August September October November December)! Item was changed: ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- nameOfMonth: anIndex + ^ self monthNames at: anIndex! - ^ MonthNames at: anIndex.! Item was changed: Magnitude subclass: #Time instanceVariableNames: 'seconds nanos' classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! This represents a particular point in time during any given day. For example, '5:19:45 pm'. If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. ! Item was changed: ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- fromSeconds: secondCount "Answer an instance of me that is secondCount number of seconds since midnight." | integerSeconds nanos | integerSeconds := secondCount truncated. integerSeconds = secondCount ifTrue: [nanos := 0] + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. ^ self seconds: integerSeconds nanoSeconds: nanos ! Item was changed: ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- + hour: hour minute: minute second: second nanoSecond: nanoCount + "Answer a Time" - hour: hour minute: minute second: second nanoSecond: nanoCount - "Answer a Time - only second precision for now" ^ self + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second + nanoSeconds: nanoCount! - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second - nanoSeconds: nanoCount - ! Item was added: + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- + nanosInSecond + ^ 1000000000! Item was changed: ----- Method: Time class>>noon (in category 'squeak protocol') ----- noon + ^ self seconds: self secondsInDay / 2! - ^ self seconds: (SecondsInDay / 2) - ! Item was changed: ----- Method: Time class>>now (in category 'ansi protocol') ----- now "Answer a Time representing the time right now - this is a 24 hour clock." | localUsecs localUsecsToday | localUsecs := self localMicrosecondClock. + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" - localUsecsToday := localUsecs \\ MicrosecondsInDay. ^ self seconds: localUsecsToday // 1000000 nanoSeconds: localUsecsToday \\ 1000000 * 1000! Item was added: + ----- Method: Time class>>secondsInDay (in category 'constants') ----- + secondsInDay + ^86400! Item was added: + ----- Method: Time class>>secondsInHour (in category 'constants') ----- + secondsInHour + ^3600! Item was added: + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- + secondsInMinute + ^60! Item was added: + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- + squeakEpoch + ^ 2415386. "Julian day number of 1 Jan 1901"! Item was changed: ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" | h m s | h := self hour. m := self minute. s := self second. hr24 ifTrue: [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. h printOn: aStream ] ifFalse: [ h > 12 ifTrue: [h - 12 printOn: aStream] ifFalse: [h < 1 ifTrue: [ 12 printOn: aStream ] ifFalse: [ h printOn: aStream ]]]. aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). m printOn: aStream. showSeconds ifTrue: [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). (showSubseconds not or: [self nanoSecond = 0]) ifTrue: [s asInteger printOn: aStream] + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger + printOn: aStream asFixedPoint: Time nanosInSecond]]. - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger - printOn: aStream asFixedPoint: NanosInSecond]]. hr24 ifFalse: [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. ! Item was changed: Object subclass: #TimeZone instanceVariableNames: 'offset abbreviation name' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! TimeZone is a simple class to colect the information identifying a UTC time zone. offset - Duration - the time zone's offset from UTC abbreviation - String - the abbreviated name for the time zone. name - String - the name of the time zone. TimeZone class >> #timeZones returns an array of the known time zones TimeZone class >> #default returns the default time zone (Grenwich Mean Time) ! Item was changed: Timespan subclass: #Week instanceVariableNames: '' classVariableNames: 'StartDay' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! I represent a week. To find out what days of the week on which Squeak is fun, select the following expression, and print it: Week dayNames! Item was changed: + ----- Method: Week class>>dayNames (in category 'inquiries') ----- - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- dayNames + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! - ^ DayNames! Item was changed: ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- indexOfDay: aSymbol + ^ self dayNames indexOf: aSymbol! - ^ DayNames indexOf: aSymbol! Item was changed: ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- nameOfDay: anIndex + ^ self dayNames at: anIndex! - ^ DayNames at: anIndex! Item was changed: ----- Method: Week class>>startDay (in category 'squeak protocol') ----- startDay ^ StartDay ifNil: [ StartDay + := self dayNames first ] - := DayNames first ] ! Item was changed: ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- startDay: aSymbol + (self dayNames includes: aSymbol) - (DayNames includes: aSymbol) ifTrue: [ StartDay := aSymbol ] ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! Item was changed: ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- starting: aDateAndTime duration: aDuration "Override - the duration is always one week. Week will start from the Week class>>startDay" | midnight delta adjusted | midnight := aDateAndTime asDateAndTime midnight. + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. adjusted := midnight - (Duration days: delta seconds: 0). ^ super starting: adjusted duration: (Duration weeks: 1)! From lewis at mail.msen.com Sat Sep 5 17:39:21 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 5 Sep 2020 13:39:21 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.58.mcz In-Reply-To: References: Message-ID: <20200905173921.GA76329@shell.msen.com> This replaces Chronology-Core-dtl.56.mcz, which I moved to the treated inbox. I am not lobbying strongly for this, just submitting it for consideration. Dave On Sat, Sep 05, 2020 at 05:35:43PM +0000, commits at source.squeak.org wrote: > A new version of Chronology-Core was added to project The Inbox: > http://source.squeak.org/inbox/Chronology-Core-dtl.58.mcz > > ==================== Summary ==================== > > Name: Chronology-Core-dtl.58 > Author: dtl > Time: 5 September 2020, 1:35:43.337716 pm > UUID: c6ada6e9-d4a0-4f20-a71b-53ea65a114b6 > Ancestors: Chronology-Core-dtl.57 > > Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. > > For Squeak, OneDay is now a class variable in order to continue to allow Date instances to share a Duration. Cuis creates a new Duration for each Date. > > =============== Diff against Chronology-Core-dtl.57 =============== > > Item was removed: > - SharedPool subclass: #ChronologyConstants > - instanceVariableNames: '' > - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' > - poolDictionaries: '' > - category: 'Chronology-Core'! > - > - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! > - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! > > Item was removed: > - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- > - initialize > - "ChronologyConstants initialize" > - > - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" > - SecondsInDay := 86400. > - SecondsInHour := 3600. > - SecondsInMinute := 60. > - MicrosecondsInDay := 24 * 60 * 60 * 1000000. > - NanosInSecond := 10 raisedTo: 9. > - NanosInMillisecond := 10 raisedTo: 6. > - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). > - > - MonthNames := #( January February March April May June > - July August September October November December). > - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! > > Item was changed: > Timespan subclass: #Date > instanceVariableNames: '' > classVariableNames: '' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! > Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. > > However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! > > Item was changed: > ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- > fromDays: dayCount > "Days since 1 January 1901" > > + ^ self julianDayNumber: dayCount + Time squeakEpoch! > - ^ self julianDayNumber: dayCount + SqueakEpoch! > > Item was changed: > Magnitude subclass: #DateAndTime > instanceVariableNames: 'utcMicroseconds localOffsetSeconds' > classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! > I represent a point in UTC time as defined by ISO 8601. I have zero duration. > > My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. > > The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. > > DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. > ! > > Item was changed: > ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- > epochOffset > "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" > + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! > - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! > > Item was changed: > ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- > posixEpochJulianDays > > + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! > - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! > > Item was changed: > ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- > ticks: ticks offset: utcOffset > "ticks is {julianDayNumber. secondCount. nanoSeconds}" > > | jdn s nanos normalizedTicks | > normalizedTicks := ticks copy. > + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. > + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. > - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. > - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. > > jdn := normalizedTicks at: 1. > s := normalizedTicks at: 2. > nanos := normalizedTicks at: 3. > localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. > utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. > ! > > Item was changed: > Magnitude subclass: #Duration > instanceVariableNames: 'nanos seconds' > + classVariableNames: 'OneDay' > + poolDictionaries: '' > - classVariableNames: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > I represent a duration of time. I have nanosecond precision! > > Item was changed: > ----- Method: Duration class>>days: (in category 'squeak protocol') ----- > days: aNumber > > + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! > - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! > > Item was changed: > ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- > days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos > > ^self > seconds: seconds > + + (minutes * Time secondsInMinute) > + + (hours * Time secondsInHour) > + + (days * Time secondsInDay) > - + (minutes * SecondsInMinute) > - + (hours * SecondsInHour) > - + (days * SecondsInDay) > nanoSeconds: nanos > ! > > Item was changed: > ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- > days: days seconds: seconds > > + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! > - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 > - ! > > Item was changed: > ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- > hours: aNumber > > + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! > - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! > > Item was changed: > ----- Method: Duration class>>initialize (in category 'initialize-release') ----- > initialize > + "Duration oneDay is used in Date creation, and is cached to allow > + sharing the instance." > + OneDay := 1 day. > + self class recompile: #oneDay.! > - ChronologyConstants classPool > - at: #Zero > - put: > - (self basicNew > - seconds: 0 > - nanoSeconds: 0) ; > - at: #OneDay > - put: 1 day! > > Item was changed: > ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- > milliSeconds: milliCount > > ^self > seconds: (milliCount quo: 1000) > + nanoSeconds: (milliCount rem: 1000) * 1000000! > - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! > > Item was changed: > ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- > minutes: aNumber > > + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! > - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! > > Item was changed: > ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- > nanoSeconds: nanos > "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." > > | quo | > + quo _ nanos quo: Time nanosInSecond. > - quo := nanos quo: NanosInSecond. > ^ self basicNew > seconds: quo > + nanoSeconds: nanos - (quo * Time nanosInSecond)! > - nanoSeconds: nanos - (quo * NanosInSecond) > - ! > > Item was changed: > ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- > seconds: seconds nanoSeconds: nanos > > ^ self basicNew > seconds: seconds truncated > + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! > - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! > > Item was changed: > ----- Method: Duration class>>zero (in category 'ansi protocol') ----- > zero > + > + ^ self basicNew seconds: 0 nanoSeconds: 0 > + ! > - "Answer the canonicalized instance of Duration zero." > - ^ Zero! > > Item was changed: > ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- > asNanoSeconds > > + ^seconds * Time nanosInSecond + nanos! > - ^seconds * NanosInSecond + nanos! > > Item was changed: > ----- Method: Duration>>days (in category 'ansi protocol') ----- > days > "Answer the number of days the receiver represents." > > + ^ seconds quo: Time secondsInDay! > - ^ seconds quo: SecondsInDay > - ! > > Item was changed: > ----- Method: Duration>>hours (in category 'ansi protocol') ----- > hours > "Answer the number of hours the receiver represents." > > > + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! > - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! > > Item was changed: > ----- Method: Duration>>minutes (in category 'ansi protocol') ----- > minutes > - > "Answer the number of minutes the receiver represents." > > + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! > - > - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! > > Item was changed: > ----- Method: Duration>>seconds (in category 'ansi protocol') ----- > seconds > "Answer the number of seconds the receiver represents." > > + ^seconds rem: Time secondsInMinute! > - ^seconds rem: SecondsInMinute! > > Item was changed: > ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- > seconds: secondCount nanoSeconds: nanoCount > "Private - only used by Duration class" > > seconds := secondCount. > nanos := nanoCount rounded. > "normalize if signs do not match" > [ nanos < 0 and: [ seconds > 0 ] ] > whileTrue: [ seconds := seconds - 1. > + nanos := nanos + Time nanosInSecond ]. > - nanos := nanos + NanosInSecond ]. > [ seconds < 0 and: [ nanos > 0 ] ] > whileTrue: [ seconds := seconds + 1. > + nanos := nanos - Time nanosInSecond ] > - nanos := nanos - NanosInSecond ] > > ! > > Item was changed: > ----- Method: Duration>>ticks (in category 'private') ----- > ticks > "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." > > | days | > days := self days. > ^ Array > with: days > + with: seconds - (days * Time secondsInDay) > - with: seconds - (days * SecondsInDay) > with: nanos > ! > > Item was changed: > Timespan subclass: #Month > instanceVariableNames: '' > classVariableNames: '' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > I represent a month. > > For example, to get the number of days this month, you can evaluate the following expression: > > Month current daysInMonth! > > Item was added: > + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- > + daysInMonth > + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! > > Item was changed: > ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- > daysInMonth: indexOrName forYear: yearInteger > > | index | > index := indexOrName isInteger > ifTrue: [indexOrName] > ifFalse: [self indexOfMonth: indexOrName]. > + ^ (self daysInMonth at: index) > - ^ (DaysInMonth at: index) > + ((index = 2 > and: [Year isLeapYear: yearInteger]) > ifTrue: [1] ifFalse: [0]) > ! > > Item was changed: > ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- > indexOfMonth: aMonthName > > > + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. > + self error: aMonthName , ' is not a recognized month name'! > - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. > - self error: aMonthName , ' is not a recognized month name'.! > > Item was added: > + ----- Method: Month class>>monthNames (in category 'inquiries') ----- > + monthNames > + ^#(January February March April May June July August September October November December)! > > Item was changed: > ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- > nameOfMonth: anIndex > > + ^ self monthNames at: anIndex! > - ^ MonthNames at: anIndex.! > > Item was changed: > Magnitude subclass: #Time > instanceVariableNames: 'seconds nanos' > classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! > This represents a particular point in time during any given day. For example, '5:19:45 pm'. > > If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. > ! > > Item was changed: > ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- > fromSeconds: secondCount > "Answer an instance of me that is secondCount number of seconds since midnight." > > | integerSeconds nanos | > integerSeconds := secondCount truncated. > integerSeconds = secondCount > ifTrue: [nanos := 0] > + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. > - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. > ^ self seconds: integerSeconds nanoSeconds: nanos > ! > > Item was changed: > ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- > + hour: hour minute: minute second: second nanoSecond: nanoCount > + "Answer a Time" > - hour: hour minute: minute second: second nanoSecond: nanoCount > - "Answer a Time - only second precision for now" > > ^ self > + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second > + nanoSeconds: nanoCount! > - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second > - nanoSeconds: nanoCount > - ! > > Item was added: > + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- > + nanosInSecond > + ^ 1000000000! > > Item was changed: > ----- Method: Time class>>noon (in category 'squeak protocol') ----- > noon > > + ^ self seconds: self secondsInDay / 2! > - ^ self seconds: (SecondsInDay / 2) > - ! > > Item was changed: > ----- Method: Time class>>now (in category 'ansi protocol') ----- > now > "Answer a Time representing the time right now - this is a 24 hour clock." > | localUsecs localUsecsToday | > localUsecs := self localMicrosecondClock. > + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" > - localUsecsToday := localUsecs \\ MicrosecondsInDay. > ^ self > seconds: localUsecsToday // 1000000 > nanoSeconds: localUsecsToday \\ 1000000 * 1000! > > Item was added: > + ----- Method: Time class>>secondsInDay (in category 'constants') ----- > + secondsInDay > + ^86400! > > Item was added: > + ----- Method: Time class>>secondsInHour (in category 'constants') ----- > + secondsInHour > + ^3600! > > Item was added: > + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- > + secondsInMinute > + ^60! > > Item was added: > + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- > + squeakEpoch > + ^ 2415386. "Julian day number of 1 Jan 1901"! > > Item was changed: > ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- > print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream > "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. > If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" > > | h m s | > h := self hour. m := self minute. s := self second. > hr24 > ifTrue: > [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. > h printOn: aStream ] > ifFalse: > [ h > 12 > ifTrue: [h - 12 printOn: aStream] > ifFalse: > [h < 1 > ifTrue: [ 12 printOn: aStream ] > ifFalse: [ h printOn: aStream ]]]. > > aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). > m printOn: aStream. > > showSeconds ifTrue: > [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). > (showSubseconds not or: [self nanoSecond = 0]) > ifTrue: [s asInteger printOn: aStream] > + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger > + printOn: aStream asFixedPoint: Time nanosInSecond]]. > - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger > - printOn: aStream asFixedPoint: NanosInSecond]]. > > hr24 ifFalse: > [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. > ! > > Item was changed: > Object subclass: #TimeZone > instanceVariableNames: 'offset abbreviation name' > classVariableNames: '' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > TimeZone is a simple class to colect the information identifying a UTC time zone. > > offset - Duration - the time zone's offset from UTC > abbreviation - String - the abbreviated name for the time zone. > name - String - the name of the time zone. > > TimeZone class >> #timeZones returns an array of the known time zones > TimeZone class >> #default returns the default time zone (Grenwich Mean Time) > ! > > Item was changed: > Timespan subclass: #Week > instanceVariableNames: '' > classVariableNames: 'StartDay' > + poolDictionaries: '' > - poolDictionaries: 'ChronologyConstants' > category: 'Chronology-Core'! > > !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > I represent a week. > > To find out what days of the week on which Squeak is fun, select the following expression, and print it: > > Week dayNames! > > Item was changed: > + ----- Method: Week class>>dayNames (in category 'inquiries') ----- > - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- > dayNames > > + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! > - ^ DayNames! > > Item was changed: > ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- > indexOfDay: aSymbol > > + ^ self dayNames indexOf: aSymbol! > - ^ DayNames indexOf: aSymbol! > > Item was changed: > ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- > nameOfDay: anIndex > > + ^ self dayNames at: anIndex! > - ^ DayNames at: anIndex! > > Item was changed: > ----- Method: Week class>>startDay (in category 'squeak protocol') ----- > startDay > > ^ StartDay ifNil: [ StartDay > + := self dayNames first ] > - := DayNames first ] > ! > > Item was changed: > ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- > startDay: aSymbol > > + (self dayNames includes: aSymbol) > - (DayNames includes: aSymbol) > ifTrue: [ StartDay := aSymbol ] > ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! > > Item was changed: > ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- > starting: aDateAndTime duration: aDuration > "Override - the duration is always one week. > Week will start from the Week class>>startDay" > > | midnight delta adjusted | > midnight := aDateAndTime asDateAndTime midnight. > + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. > - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. > adjusted := midnight - (Duration days: delta seconds: 0). > > ^ super starting: adjusted duration: (Duration weeks: 1)! > > From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 17:49:43 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 17:49:43 +0000 Subject: [squeak-dev] I just purchased squeakbooks.com and .org In-Reply-To: References: <17458e0e4f9.106e12a8315071.1205817659484112188@zoho.com> , Message-ID: Hi Karl, basically, you can reach all new operations by opening a context menu on a help topic in the tree pane on the left of a help browser. Then choose 'Add topic...', 'Browse topic' etc. Does this help? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Samstag, 5. September 2020 18:35:12 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] I just purchased squeakbooks.com and .org On Fri, Sep 4, 2020 at 3:49 PM Thiede, Christoph > wrote: > But the HelpBrowser is read only at the moment. (Some skeleton methods exist for writing documentation) I uploaded a lot of stuff concerning this to the inbox last year. Unless you want to achieve something very fancy, you do not need to descend into the System Browser any longer. Creating, removing, renaming, and editing topics is all possible via the HelpBrowser. Moreover, with Morphic-ct.1587, you can even insert images without writing code. :-) The changes are still waiting for anyone who has some time to review and eventually merge them. Wow, lots of stuff here. Not sure I understand all of it. Do you have a short description for creating a new help topic after filing in all the changes ? :-D Best, Karl Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von karl ramberg > Gesendet: Freitag, 4. September 2020 15:24:06 An: gettimothy; The general-purpose Squeak developers list Betreff: Re: [squeak-dev] I just purchased squeakbooks.com and .org On Fri, Sep 4, 2020 at 1:29 PM gettimothy via Squeak-dev > wrote: Hi folks, I am going to try to recreate books.pharo.org using those domains, but for Squeak as part of my Doc/SeasideDoc project. I am NOT trying to make money for myself, I am just trying to solve the Squeak documentation problem. I will be building the site with Seaside on Squeak and I will be studying the Pharo model for their documentation creation to see if it is appropriate for squeak. Motivation: http://books.pharo.org/booklet-Scraping/html/scrapingbook.html That is an example of the type of documentation I frequently use as a developer. How do I accomplish a specific task, where do I start, what are the conceptual "things" I have to think in. I will be using that documentation to get myself familiarized with the Squeak XML stuff as I will need it for processing xml documents of 10's of millions of records. I will be happy to give the domains to the squeak community when/if the project is solid. Once the stubbed website is up, I will attempt to create a live-round-robin model from the squeak image. Open a workspace, open a book (book content comes down from website) add/edit content within the workspace, save it live to the website (and concurrently to the image ?) It would be a really nice addition to be able to write "how to's" and documentation in an almost literal coding style within Squeak. The HelpBrowser has the ability to pull down content from the Squeak Wiki. And we have added inlining pictures in it: http://forum.world.st/The-Trunk-MonticelloConfigurations-mt-160-mcz-td5115112.html#a5116431 But the HelpBrowser is read only at the moment. (Some skeleton methods exist for writing documentation) Best, Karl Hopefully it will be useful and we can start crowdsourcing documentation. cheers, tty. -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 18:39:32 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 18:39:32 +0000 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: <3BBE9B1A-3274-4499-8C0E-0E0B037B62DC@gmx.de> References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> <9FF45C95-BA33-4EAD-9E2A-823C9A2BB563@gmx.de> <0e3c7a47cc924e01896f6e2cbba645af@student.hpi.uni-potsdam.de>, <3BBE9B1A-3274-4499-8C0E-0E0B037B62DC@gmx.de> Message-ID: <96c1108729f54a318456a16f4c86eb3b@student.hpi.uni-potsdam.de> Hi Tobias, > That said, 'super' needs a lexical scope, Not sure about that either, because [Compiler evaluate: 'super yourself' for: Object new] gives me a MNU (MessageNotUnderstood: ProtoObject>>yourself). A do it block is nothing else than a normal method that is parsed with noPattern (with a default do it selector), isn't it? But yes, of course: > can you do the whole thing in a proper method? ProtoObject compile: 'superCallingMethod ^ super foo'. ProtoObject new superCallingMethod. Context runSimulated: [ProtoObject new superCallingMethod] Same error pattern as described below. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Samstag, 5. September 2020 18:59 Uhr An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) > On 05.09.2020, at 13:34, Thiede, Christoph wrote: > > Hi Tobias, > > are you sure about that? > [Compiler evaluate: 'self identityHash' for: ProtoObject new] answers a me a SmallInteger. Compiler >> #evaluate:[...] returns the last stack value automatically, just a like a printIt. See senders of #returnSelfIfNoOther: in Parser. :-) Ah, I thought it be just a method, I misread that. That said, 'super' needs a lexical scope, can you do the whole thing in a proper method? -t > > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Samstag, 5. September 2020 13:25:26 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) > > > > On 05.09.2020, at 13:10, Thiede, Christoph wrote: > > > > What would you expect the following to return? > > x := Compiler evaluate: 'super identityHash' for: ProtoObject new. > > thisContext objectClass: x. > > I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. > > But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. > > No. you're just missing the return, that's why it answers self… > -t -------------- next part -------------- An HTML attachment was scrubbed... URL: From pbpublist at gmail.com Sat Sep 5 19:44:45 2020 From: pbpublist at gmail.com (Phil B) Date: Sat, 5 Sep 2020 15:44:45 -0400 Subject: [squeak-dev] SqueakMap down? Message-ID: It doesn't appear to be responding to requests (gateway time-out) -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 19:59:33 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 19:59:33 +0000 Subject: [squeak-dev] I'd like to contribute to the JSON project Message-ID: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> Hi all, I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into the repository? :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: JSON-ct.40.mcz Type: application/octet-stream Size: 16239 bytes Desc: JSON-ct.40.mcz URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 5 20:08:05 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 5 Sep 2020 20:08:05 +0000 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> Message-ID: Here is a second small but, as I hope, useful patch. Name: JSON-ct.41 Author: ct Time: 5 September 2020, 10:04:48.328336 pm UUID: 15667ebd-25e5-2f45-a758-7ab8df09a3e1 Ancestors: JSON-ct.40 Implements #respondsTo: on JsonObject. Usage: | anObject | anObject := Json readFrom: '{"foo": 42}' readStream. (anObject respondsTo: #foo) ifTrue: [anObject foo] ifFalse: ["fallback code"]. This is especially helpful when using JsonObjects anywhere in the system as polymorphic substitution for other classes. Have a nice weekend! Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Samstag, 5. September 2020 21:59 Uhr An: Squeak Dev; Tony Garnock-Jones; Niephaus, Fabio Betreff: [squeak-dev] I'd like to contribute to the JSON project Hi all, I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into the repository? :-) Name: JSON-ct.40 Author: ct Time: 5 September 2020, 9:48:49.742336 pm UUID: 85bcdb2a-a65e-b14f-90a6-eb6ad8f82eba Ancestors: JSON-tonyg.39 Adds support and tests for UTF16 unicode strings. Unicode sequences should not be parsed as two separate characters but rather as a single one with 4 bytes. For more information, see https://tools.ietf.org/id/draft-ietf-json-rfc4627bis-09.html#rfc.section.7. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: JSON-ct.41.mcz Type: application/octet-stream Size: 16615 bytes Desc: JSON-ct.41.mcz URL: From leves at caesar.elte.hu Sun Sep 6 00:13:56 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 6 Sep 2020 02:13:56 +0200 (CEST) Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, Your code looks correct to me, though it's a bit too slow for my taste. I've fixed it in my fork of JSON[1] as well. Levente [1] http://squeaksource.com/PostgresV3/JSON-ul.55.mcz On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into the > repository? :-) > > > Best, > > Christoph > > > From lewis at mail.msen.com Sun Sep 6 00:44:39 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 5 Sep 2020 20:44:39 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz In-Reply-To: <20200905021021.GA12864@shell.msen.com> References: <20200904150058.GA11481@shell.msen.com> <20200905010408.GA3671@shell.msen.com> <20200905021021.GA12864@shell.msen.com> Message-ID: <20200906004439.GA43410@shell.msen.com> On Fri, Sep 04, 2020 at 10:10:21PM -0400, David T. Lewis wrote: > On Fri, Sep 04, 2020 at 09:04:08PM -0400, David T. Lewis wrote: > > > > On Sat, Sep 05, 2020 at 12:01:21AM +0200, Levente Uzonyi wrote: > > > > > 2) Some replacement methods are not quick and create new objects. For > > > example, OneDay, a single Duration of one day is replaced with #oneDay, a > > > method returning the expression 1 day. Each invocation of the method > > > creates a new object. > > > In my image, there are 19821 Date instances. If all of those had their own > > > custom one day long Duration objects, which is a side effect of these > > > changes in the long run, my image would be ~620 kiB larger (64-bit image, > > > object header + 2 variables => 32 bytes each). > > > There's a solution: cache the shared instance in a variable. :) > > > > If it is a problem, then this is an error on my part (not Juan's). After > > adopting the main part of the Cuis changes, #oneDay was one of a couple > > of remaining cases that I addressed with trivial replacements. > > > > But there are no senders of #oneDay in the image other than a unit test. > > So I think that it is not likely to be an issue. > > > > Levente, > > My apologies, I was careless and you are right. Duration class>>oneDay is > sent by Date class>>starting: and therefore it does create redundant new > objects exactly as you said. > > This is an error on my part and would need to be fixed before the changes > could be considered for trunk. > I moved Chronology-Core-dtl.56 to the treated inbox, and put Chronology-Core-dtl.58 in the inbox to address the #oneDay issue. Dave From eliot.miranda at gmail.com Sun Sep 6 02:49:54 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 5 Sep 2020 19:49:54 -0700 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> Message-ID: Hi Christophe, Hi Tobias, On Sat, Sep 5, 2020 at 4:10 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi all, hi Eliot, > > > just for curiosity, I have two new questions about the VM machinery (which > are far away from praxis but close to my interests): > > > *1.** super on ProtoObject* > > What would you expect the following to return? > > x := Compiler evaluate: 'super identityHash' for: ProtoObject new. > > thisContext objectClass: x. > > I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: > is called. > My guess before looking at the code was that the send provoked a doesNotUnderstand: and hence the expression returned the result of raising a MessageNotUnderstood exception. But looking at the code I see that at least in the JIT there is undefined behavior in looking up a message in a nil class (the superclass of ProtoObject is nil). So thank you for this. I'll have to firm up the behaviour to ensure a doesNotUnderstand: is the result. But actually, it answers you the ProtoObject instance, which I find very > interesting because it means that if a message cannot be looked up, it is > simply and silently skipped. > Which is a bug. The only reasonable thing to do here (IMO) is for the VM to send doesNotUnderstnnd:. > > If you debug the call instead (Context class >> #runSimulated:), Context > >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense > but unfortunately is not consistent with the original VM behavior. > Maybe the Compiler should forbid any call to super from ProtoObject > instances at all (more formally: if the receiver's class's superclass is > nil)? Or should we adjust the simulation code? > (By the way: If you do the same in Squeak.js, you'll get an infinite > recursion :D) > > *2. Mirror primitives* > What is the reason for primitives such as 75 (#identityHash) or 78/139 > (#nextInstance/#nextObject) not being mirrored in Context? > Was this simply forgotten, or don't we have the claim to mirror any > "essential" primitive without it actually being needed by the simulation > machinery? > They're not needed by the execution simulation machinery. > As always, looking forward to your interesting explanations! :-) > > > Best, > Christoph > Cheers _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From sean at clipperadams.com Sun Sep 6 03:51:16 2020 From: sean at clipperadams.com (Sean DeNigris) Date: Sat, 5 Sep 2020 23:51:16 -0400 Subject: [squeak-dev] OpenSmalltalk Cooperation Weekly Roundup Message-ID: <1B43C6A2-F068-4BA0-8203-24A6F0D08248@clipperadams.com> A few highlights of cooperation between Cuis, Pharo, and Squeak this week... Squeak (and GToolkit) support has been officially added to PharoLauncher. Thanks to Christophe Demarey for merging my extensions so quickly! It is included in the [latest CI build](https://ci.inria.fr/pharo-ci-jenkins2/job/PharoLauncher-Pipeline/job/dev/lastSuccessfulBuild/artifact/) by default. If you'd rather play with it in the [latest released version](https://github.com/pharo-project/pharo-launcher/releases/tag/2.2), you can load the `enh_vm-image-custom-hooks` issue branch from [my fork](https://github.com/seandenigris/pharo-launcher) and make sure the `PharoLauncher-Squeak` package gets loaded. [RISCV](https://github.com/darth-cheney/safe-bet ) is now available in Pharo, as well as Squeak, thanks to Eric Gade (aka darth-cheney) [OSProcess-Tonel](https://github.com/dtlewis290/OSProcess-Tonel ), which allows OSP to be run in Pharo, has received an update, thanks to David Lewis. -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Sun Sep 6 12:19:21 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 6 Sep 2020 14:19:21 +0200 (CEST) Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz In-Reply-To: <20200906004439.GA43410@shell.msen.com> References: <20200904150058.GA11481@shell.msen.com> <20200905010408.GA3671@shell.msen.com> <20200905021021.GA12864@shell.msen.com> <20200906004439.GA43410@shell.msen.com> Message-ID: Hi Dave, On Sat, 5 Sep 2020, David T. Lewis wrote: > > I moved Chronology-Core-dtl.56 to the treated inbox, and put > Chronology-Core-dtl.58 in the inbox to address the #oneDay issue. Duration class >> #zero is similar to #oneDay in the sense that it's a single instance in Squeak. It would be worth keeping it that way, as existing users use it quite liberally. Now that we support read-only objects, I think these shared objects should be made read-only as well (#beReadOnlyObject). Something like this: Duration class >> #initialize "Duration oneDay is used in Date creation, and is cached to allow sharing the instance." (OneDay := self days: 1) beReadOnlyObject. "Duration zero is used in various comparisons and computations, and is cached to allow reusing the instance." (Zero := self seconds: 0 nanoSeconds: 0) beReadOnlyObject. "The following recompilation is only needed during the transition from pool variables to class variables." self class recompile: #oneDay; recompile: #zero Levente > > Dave From commits at source.squeak.org Sun Sep 6 12:29:23 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 6 Sep 2020 12:29:23 0000 Subject: [squeak-dev] The Trunk: Chronology-Core-ul.58.mcz Message-ID: Levente Uzonyi uploaded a new version of Chronology-Core to project The Trunk: http://source.squeak.org/trunk/Chronology-Core-ul.58.mcz ==================== Summary ==================== Name: Chronology-Core-ul.58 Author: ul Time: 6 September 2020, 2:28:59.102076 pm UUID: 67bb8acc-c81b-4e0c-a6f2-e9603c284236 Ancestors: Chronology-Core-dtl.57 Simplify DateAndTime class>>unixEpoch =============== Diff against Chronology-Core-dtl.57 =============== Item was changed: ----- Method: DateAndTime class>>unixEpoch (in category 'squeak protocol') ----- unixEpoch "Answer a DateAndTime representing the Unix epoch (1 January 1970, midnight UTC)" + ^self utcMicroseconds: 0 offset: 0! - ^ self basicNew - ticks: #(2440588 0 0) offset: Duration zero; - yourself. - ! From gettimothy at zoho.com Sun Sep 6 15:50:54 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 06 Sep 2020 11:50:54 -0400 Subject: [squeak-dev] Live coding documentation at squeakbooks.com Message-ID: <174641cf912.c7e57eb910335.6900327554577308621@zoho.com> Hi folks, A quick FYI and some brainstorms and a request for brainstorms. I have a stub website up at squeakbooks.org  / squeakbooks.com My next goal is  for anybody to be able to code the  'Live Document Coding Hacking'  book from within their Squeak environment; then make the content available in several formats: HelpBrowser Book format, PDF, text... 1. Hacking the HelpBrowser is an obvious choice; unfortunately somebody else will have to do that as I find the thing painful to use. Nothing personal, its just reality for me. 2. Make squeakbooks.com/org a Monticello Repo and make Books a package that can be edited. Upon submit to the repo, the backend software extracts that and makes it live on the website and also creates HelpBrowser "books" that they can import and other stuff..  3. REST from within a workspace. 4. Stephan at the Pharo discord suggested using Glaborous Tookit--which sounds like fun..he says I can even connect to Squeak image with it...but until GT is on Squeak, I am going to hold off there. 5. Stephan also mentioned uploading zips to some commercial wiki named 'confluence'. 6. Leverage Doc  http://www.squeaksource.com/Doc/  as the CLI interface 7. Subclass WAComponent...hmmm that will require a Seaside install...nocando. 8. Connect directly to the running squeakbooks.com image via some interface and have the new instances/classes update on the remove image At first glance, I am leaning 2 and 6 as monticello is something we all use and are familiar with. 8 really intrigues me....how do I commit live changes to a remote image? Thank you for your time. cheers, tty -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Sun Sep 6 16:19:33 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 6 Sep 2020 18:19:33 +0200 (CEST) Subject: [squeak-dev] Unicode In-Reply-To: <1599322375667-0.post@n4.nabble.com> References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> <8DBCEF4B-055E-4F97-9D42-B55A1D5384C5@gmail.com> <1599322375667-0.post@n4.nabble.com> Message-ID: Hi Christoph, On Sat, 5 Sep 2020, Christoph Thiede wrote: > Hi all, > > I would still be interested in resuming this project and supporting the > latest Unicode codepoints in Squeak. Is there really no one who could find > some minutes to review my proposed design change? Your words suggest that it has already been published, but I can't find it anywhere. > Or putting it another way: If I will upload these changes into the inbox, > will anyone merge it? :-) I will review it and I'm sure others will do as well. Though I can't promise to merge it without having a look. :) Levente > > Best, > Christoph > > > > -- > Sent from: http://forum.world.st/Squeak-Dev-f45488.html From leves at caesar.elte.hu Sun Sep 6 16:22:54 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 6 Sep 2020 18:22:54 +0200 (CEST) Subject: [squeak-dev] Are we missing some string encoding in SqueakSSL/WebClient? In-Reply-To: <151190a158c9417aa22b3ac64e263ae0@student.hpi.uni-potsdam.de> References: <151190a158c9417aa22b3ac64e263ae0@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > while doing some experiments with WebClient & WebUtils in order to send textual data to a server via HTTP(S), I found out that posting a request containing non-ASCII characters in a multipart/form-data yields a primitive > failure from SqueakSSL/primitiveEncrypt (occurs on both Win32 + emulated Linux/Ubuntu). When I converted the text manually using #squeakToUtf8 before putting it into the contents data, everything worked fine and the server > receives the correct text without any encoding problems. > > > So for my application, I can manually #squeakToUtf8-convert the request string before posting it, but I wonder whether this should really be the responsibility of every Squeak developer and not rather one of the WebClient? > For illustration: If you use normal #htmlSubmit:fields: instead of #httpPost:multipartFields:, WebClient does all necessary conversion (#encodeUrlEncodedForm:, String >> #encodeForHTTP) itself. Anyway, > #encodeMultipartForm:boundary: does not care about conversion. > > > I am only a bloody newbie to all this web stuff, so maybe I am missing something important. What do you think? Should we add #squeakToUtf8 conversion in WebUtils class >> #encodeMultipartForm:boundary:, and in the decode > message vice versa? Or would this rather be a responsibility of the SqueakSSL protocol? I am looking forward to your help! Encoding is definitely missing there. multipart/form-data is a mess [1] though, so we should be careful to generate what presumably everything supports but be able to parse what other sources can generate. Levente [1] https://tools.ietf.org/html/rfc7578 > > > Best, > > Christoph > > > From eliot.miranda at gmail.com Sun Sep 6 17:15:12 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 6 Sep 2020 10:15:12 -0700 Subject: [squeak-dev] Unicode In-Reply-To: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, Hi All, > On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: > > Hi all! :-) > > After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, but so long, I have one general question for you: > And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values). In the 32-bit variant Characters are 30-bit unsigned integers. In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant? It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure that initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. Q2, how many bits should the 64-bit variant VM support for immediate Characters? Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to LargePositiveInteger beyond SmallInteger maxVal. This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. It has implications in a few parts of the system: - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation - 32-bit <=> 64-bit image conversion All this is easily doable (because we have models of doing it for Float and Integer general instances). But we need good specifications so we can implement the right thing from the get-go. > At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. > > Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't know whether this will ever happen). > > > Examples: > Unicode generalTagOf: $a asUnicode. "#Ll" > > Unicode class >> isLetterCode: charCode > ^ (self generalTagOf: charCode) first = $L > > Unicode class >> isAlphaNumericCode: charCode > | tag| > ^ (tag := self generalCategoryOf: charCode) first = $L > or: [tag = #Nd] > > How do you think about this proposal? Please let me know and I will go ahead! :D > > Best, > Christoph Best, Eliot _,,,^..^,,,_ (phone) -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Sun Sep 6 17:37:22 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sun, 6 Sep 2020 19:37:22 +0200 Subject: [squeak-dev] Unicode In-Reply-To: References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> Message-ID: > On 06.09.2020, at 19:15, Eliot Miranda wrote: > > Hi Christoph, Hi All, > >> On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: >> >> Hi all! :-) >> >> After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, but so long, I have one general question for you: >> > > And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values). In the 32-bit variant Characters are 30-bit unsigned integers. In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. > > Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant? It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure that initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. > > Q2, how many bits should the 64-bit variant VM support for immediate Characters? Unicode has a max value of 0x10FFFF. That makes 21 bit. So no worries there. We should just not forget the leading-char stuff (Yoshiki, Andreas,...) BEst regards -Tobias > > Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to LargePositiveInteger beyond SmallInteger maxVal. This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. > > It has implications in a few parts of the system: > - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances > - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation > - 32-bit <=> 64-bit image conversion > > All this is easily doable (because we have models of doing it for Float and Integer general instances). But we need good specifications so we can implement the right thing from the get-go. > >> At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. >> >> Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't know whether this will ever happen). >> >> >> Examples: >> Unicode generalTagOf: $a asUnicode. "#Ll" >> >> Unicode class >> isLetterCode: charCode >> ^ (self generalTagOf: charCode) first = $L >> >> Unicode class >> isAlphaNumericCode: charCode >> | tag| >> ^ (tag := self generalCategoryOf: charCode) first = $L >> or: [tag = #Nd] >> >> How do you think about this proposal? Please let me know and I will go ahead! :D >> >> Best, >> Christoph > > Best, Eliot > _,,,^..^,,,_ (phone) From commits at source.squeak.org Sun Sep 6 18:05:02 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 6 Sep 2020 18:05:02 0000 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.59.mcz Message-ID: A new version of Chronology-Core was added to project The Inbox: http://source.squeak.org/inbox/Chronology-Core-dtl.59.mcz ==================== Summary ==================== Name: Chronology-Core-dtl.59 Author: dtl Time: 6 September 2020, 2:05:00.620796 pm UUID: 99c99e87-ef4a-49ee-99eb-699c6c3dd65d Ancestors: Chronology-Core-ul.58 Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. For Squeak, OneDay is now a class variable in order to continue to allow Date instances to share a Duration. Cuis creates a new Duration for each Date. Class variable Zero is similarly defined. =============== Diff against Chronology-Core-ul.58 =============== Item was removed: - SharedPool subclass: #ChronologyConstants - instanceVariableNames: '' - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' - poolDictionaries: '' - category: 'Chronology-Core'! - - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! Item was removed: - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- - initialize - "ChronologyConstants initialize" - - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" - SecondsInDay := 86400. - SecondsInHour := 3600. - SecondsInMinute := 60. - MicrosecondsInDay := 24 * 60 * 60 * 1000000. - NanosInSecond := 10 raisedTo: 9. - NanosInMillisecond := 10 raisedTo: 6. - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). - - MonthNames := #( January February March April May June - July August September October November December). - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! Item was changed: Timespan subclass: #Date instanceVariableNames: '' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! Item was changed: ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- fromDays: dayCount "Days since 1 January 1901" + ^ self julianDayNumber: dayCount + Time squeakEpoch! - ^ self julianDayNumber: dayCount + SqueakEpoch! Item was changed: Magnitude subclass: #DateAndTime instanceVariableNames: 'utcMicroseconds localOffsetSeconds' classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! I represent a point in UTC time as defined by ISO 8601. I have zero duration. My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. ! Item was changed: ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- epochOffset "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! Item was changed: ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- posixEpochJulianDays + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! Item was changed: ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- ticks: ticks offset: utcOffset "ticks is {julianDayNumber. secondCount. nanoSeconds}" | jdn s nanos normalizedTicks | normalizedTicks := ticks copy. + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. jdn := normalizedTicks at: 1. s := normalizedTicks at: 2. nanos := normalizedTicks at: 3. localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. ! Item was changed: Magnitude subclass: #Duration instanceVariableNames: 'nanos seconds' + classVariableNames: 'OneDay Zero' + poolDictionaries: '' - classVariableNames: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! I represent a duration of time. I have nanosecond precision! Item was changed: ----- Method: Duration class>>days: (in category 'squeak protocol') ----- days: aNumber + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! Item was changed: ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos ^self seconds: seconds + + (minutes * Time secondsInMinute) + + (hours * Time secondsInHour) + + (days * Time secondsInDay) - + (minutes * SecondsInMinute) - + (hours * SecondsInHour) - + (days * SecondsInDay) nanoSeconds: nanos ! Item was changed: ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- days: days seconds: seconds + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 - ! Item was changed: ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- hours: aNumber + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! Item was changed: ----- Method: Duration class>>initialize (in category 'initialize-release') ----- initialize + "Duration oneDay is used in Date creation, and is cached to allow + sharing the instance." + (OneDay := self days: 1) beReadOnlyObject. + "Duration zero is used in various comparisons and computations, and + is cached to allow reusing the instance." + (Zero := self seconds: 0 nanoSeconds: 0) beReadOnlyObject. + "The following recompilation is only needed during the transition + from pool variables to class variables." + self class + recompile: #oneDay; + recompile: #zero + ! - ChronologyConstants classPool - at: #Zero - put: - (self basicNew - seconds: 0 - nanoSeconds: 0) ; - at: #OneDay - put: 1 day! Item was changed: ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- milliSeconds: milliCount ^self seconds: (milliCount quo: 1000) + nanoSeconds: (milliCount rem: 1000) * 1000000! - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! Item was changed: ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- minutes: aNumber + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! Item was changed: ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- nanoSeconds: nanos "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." | quo | + quo _ nanos quo: Time nanosInSecond. - quo := nanos quo: NanosInSecond. ^ self basicNew seconds: quo + nanoSeconds: nanos - (quo * Time nanosInSecond)! - nanoSeconds: nanos - (quo * NanosInSecond) - ! Item was changed: ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- seconds: seconds nanoSeconds: nanos ^ self basicNew seconds: seconds truncated + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! Item was changed: ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- asNanoSeconds + ^seconds * Time nanosInSecond + nanos! - ^seconds * NanosInSecond + nanos! Item was changed: ----- Method: Duration>>days (in category 'ansi protocol') ----- days "Answer the number of days the receiver represents." + ^ seconds quo: Time secondsInDay! - ^ seconds quo: SecondsInDay - ! Item was changed: ----- Method: Duration>>hours (in category 'ansi protocol') ----- hours "Answer the number of hours the receiver represents." + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! Item was changed: ----- Method: Duration>>minutes (in category 'ansi protocol') ----- minutes - "Answer the number of minutes the receiver represents." + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! - - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! Item was changed: ----- Method: Duration>>seconds (in category 'ansi protocol') ----- seconds "Answer the number of seconds the receiver represents." + ^seconds rem: Time secondsInMinute! - ^seconds rem: SecondsInMinute! Item was changed: ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- seconds: secondCount nanoSeconds: nanoCount "Private - only used by Duration class" seconds := secondCount. nanos := nanoCount rounded. "normalize if signs do not match" [ nanos < 0 and: [ seconds > 0 ] ] whileTrue: [ seconds := seconds - 1. + nanos := nanos + Time nanosInSecond ]. - nanos := nanos + NanosInSecond ]. [ seconds < 0 and: [ nanos > 0 ] ] whileTrue: [ seconds := seconds + 1. + nanos := nanos - Time nanosInSecond ] - nanos := nanos - NanosInSecond ] ! Item was changed: ----- Method: Duration>>ticks (in category 'private') ----- ticks "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." | days | days := self days. ^ Array with: days + with: seconds - (days * Time secondsInDay) - with: seconds - (days * SecondsInDay) with: nanos ! Item was changed: Timespan subclass: #Month instanceVariableNames: '' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! I represent a month. For example, to get the number of days this month, you can evaluate the following expression: Month current daysInMonth! Item was added: + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- + daysInMonth + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! Item was changed: ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- daysInMonth: indexOrName forYear: yearInteger | index | index := indexOrName isInteger ifTrue: [indexOrName] ifFalse: [self indexOfMonth: indexOrName]. + ^ (self daysInMonth at: index) - ^ (DaysInMonth at: index) + ((index = 2 and: [Year isLeapYear: yearInteger]) ifTrue: [1] ifFalse: [0]) ! Item was changed: ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- indexOfMonth: aMonthName + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. + self error: aMonthName , ' is not a recognized month name'! - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. - self error: aMonthName , ' is not a recognized month name'.! Item was added: + ----- Method: Month class>>monthNames (in category 'inquiries') ----- + monthNames + ^#(January February March April May June July August September October November December)! Item was changed: ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- nameOfMonth: anIndex + ^ self monthNames at: anIndex! - ^ MonthNames at: anIndex.! Item was changed: Magnitude subclass: #Time instanceVariableNames: 'seconds nanos' classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! This represents a particular point in time during any given day. For example, '5:19:45 pm'. If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. ! Item was changed: ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- fromSeconds: secondCount "Answer an instance of me that is secondCount number of seconds since midnight." | integerSeconds nanos | integerSeconds := secondCount truncated. integerSeconds = secondCount ifTrue: [nanos := 0] + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. ^ self seconds: integerSeconds nanoSeconds: nanos ! Item was changed: ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- + hour: hour minute: minute second: second nanoSecond: nanoCount + "Answer a Time" - hour: hour minute: minute second: second nanoSecond: nanoCount - "Answer a Time - only second precision for now" ^ self + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second + nanoSeconds: nanoCount! - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second - nanoSeconds: nanoCount - ! Item was added: + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- + nanosInSecond + ^ 1000000000! Item was changed: ----- Method: Time class>>noon (in category 'squeak protocol') ----- noon + ^ self seconds: self secondsInDay / 2! - ^ self seconds: (SecondsInDay / 2) - ! Item was changed: ----- Method: Time class>>now (in category 'ansi protocol') ----- now "Answer a Time representing the time right now - this is a 24 hour clock." | localUsecs localUsecsToday | localUsecs := self localMicrosecondClock. + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" - localUsecsToday := localUsecs \\ MicrosecondsInDay. ^ self seconds: localUsecsToday // 1000000 nanoSeconds: localUsecsToday \\ 1000000 * 1000! Item was added: + ----- Method: Time class>>secondsInDay (in category 'constants') ----- + secondsInDay + ^86400! Item was added: + ----- Method: Time class>>secondsInHour (in category 'constants') ----- + secondsInHour + ^3600! Item was added: + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- + secondsInMinute + ^60! Item was added: + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- + squeakEpoch + ^ 2415386. "Julian day number of 1 Jan 1901"! Item was changed: ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" | h m s | h := self hour. m := self minute. s := self second. hr24 ifTrue: [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. h printOn: aStream ] ifFalse: [ h > 12 ifTrue: [h - 12 printOn: aStream] ifFalse: [h < 1 ifTrue: [ 12 printOn: aStream ] ifFalse: [ h printOn: aStream ]]]. aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). m printOn: aStream. showSeconds ifTrue: [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). (showSubseconds not or: [self nanoSecond = 0]) ifTrue: [s asInteger printOn: aStream] + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger + printOn: aStream asFixedPoint: Time nanosInSecond]]. - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger - printOn: aStream asFixedPoint: NanosInSecond]]. hr24 ifFalse: [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. ! Item was changed: Object subclass: #TimeZone instanceVariableNames: 'offset abbreviation name' classVariableNames: '' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! TimeZone is a simple class to colect the information identifying a UTC time zone. offset - Duration - the time zone's offset from UTC abbreviation - String - the abbreviated name for the time zone. name - String - the name of the time zone. TimeZone class >> #timeZones returns an array of the known time zones TimeZone class >> #default returns the default time zone (Grenwich Mean Time) ! Item was changed: Timespan subclass: #Week instanceVariableNames: '' classVariableNames: 'StartDay' + poolDictionaries: '' - poolDictionaries: 'ChronologyConstants' category: 'Chronology-Core'! !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! I represent a week. To find out what days of the week on which Squeak is fun, select the following expression, and print it: Week dayNames! Item was changed: + ----- Method: Week class>>dayNames (in category 'inquiries') ----- - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- dayNames + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! - ^ DayNames! Item was changed: ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- indexOfDay: aSymbol + ^ self dayNames indexOf: aSymbol! - ^ DayNames indexOf: aSymbol! Item was changed: ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- nameOfDay: anIndex + ^ self dayNames at: anIndex! - ^ DayNames at: anIndex! Item was changed: ----- Method: Week class>>startDay (in category 'squeak protocol') ----- startDay ^ StartDay ifNil: [ StartDay + := self dayNames first ] - := DayNames first ] ! Item was changed: ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- startDay: aSymbol + (self dayNames includes: aSymbol) - (DayNames includes: aSymbol) ifTrue: [ StartDay := aSymbol ] ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! Item was changed: ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- starting: aDateAndTime duration: aDuration "Override - the duration is always one week. Week will start from the Week class>>startDay" | midnight delta adjusted | midnight := aDateAndTime asDateAndTime midnight. + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. adjusted := midnight - (Duration days: delta seconds: 0). ^ super starting: adjusted duration: (Duration weeks: 1)! From lewis at mail.msen.com Sun Sep 6 18:15:58 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 6 Sep 2020 14:15:58 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.56.mcz In-Reply-To: References: <20200904150058.GA11481@shell.msen.com> <20200905010408.GA3671@shell.msen.com> <20200905021021.GA12864@shell.msen.com> <20200906004439.GA43410@shell.msen.com> Message-ID: <20200906181558.GA23660@shell.msen.com> Hi Levente, On Sun, Sep 06, 2020 at 02:19:21PM +0200, Levente Uzonyi wrote: > Hi Dave, > > On Sat, 5 Sep 2020, David T. Lewis wrote: > > > > >I moved Chronology-Core-dtl.56 to the treated inbox, and put > >Chronology-Core-dtl.58 in the inbox to address the #oneDay issue. > > Duration class >> #zero is similar to #oneDay in the sense that it's a > single instance in Squeak. It would be worth keeping it that way, as > existing users use it quite liberally. > > Now that we support read-only objects, I think these shared objects should > be made read-only as well (#beReadOnlyObject). Something like this: > > Duration class >> #initialize > > "Duration oneDay is used in Date creation, and is cached to allow > sharing the instance." > (OneDay := self days: 1) beReadOnlyObject. > "Duration zero is used in various comparisons and computations, and > is cached to allow reusing the instance." > (Zero := self seconds: 0 nanoSeconds: 0) beReadOnlyObject. > "The following recompilation is only needed during the transition > from pool variables to class variables." > self class > recompile: #oneDay; > recompile: #zero > Yes, that is better. I put Chronology-Core-dtl.59 in the inbox to add this, and moved Chronology-Core-dtl.58 to treated. To your earlier point about Time having too many responsibilities already, the secondsInDay, secondsInHour, and secondsInMinute methods are trivial and have no functional value other than to document intent in methods that send them. I wonder if it might be better to just get rid of them and add a few comments in the appropriate methods. Of course that contradicts my stated interest in having better compatibility with Cuis, so maybe I am just running in circles here (as you previously suggested with a smiley). Dave From lewis at mail.msen.com Sun Sep 6 18:22:36 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 6 Sep 2020 14:22:36 -0400 Subject: [squeak-dev] The Trunk: Chronology-Core-ul.58.mcz In-Reply-To: References: Message-ID: <20200906182236.GB23660@shell.msen.com> Good catch, thank you. The only remaining use of DateAndTime>>ticks is in storeDataOn: for serializing instances in the known earlier format. That is a good standard to use for compatibility, and works well as an external storage format anyway. Dave On Sun, Sep 06, 2020 at 12:29:23PM +0000, commits at source.squeak.org wrote: > Levente Uzonyi uploaded a new version of Chronology-Core to project The Trunk: > http://source.squeak.org/trunk/Chronology-Core-ul.58.mcz > > ==================== Summary ==================== > > Name: Chronology-Core-ul.58 > Author: ul > Time: 6 September 2020, 2:28:59.102076 pm > UUID: 67bb8acc-c81b-4e0c-a6f2-e9603c284236 > Ancestors: Chronology-Core-dtl.57 > > Simplify DateAndTime class>>unixEpoch > > =============== Diff against Chronology-Core-dtl.57 =============== > > Item was changed: > ----- Method: DateAndTime class>>unixEpoch (in category 'squeak protocol') ----- > unixEpoch > "Answer a DateAndTime representing the Unix epoch (1 January 1970, midnight UTC)" > > + ^self utcMicroseconds: 0 offset: 0! > - ^ self basicNew > - ticks: #(2440588 0 0) offset: Duration zero; > - yourself. > - ! > > From leves at caesar.elte.hu Sun Sep 6 18:40:02 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 6 Sep 2020 20:40:02 +0200 (CEST) Subject: [squeak-dev] Unicode In-Reply-To: References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> Message-ID: On Sun, 6 Sep 2020, Tobias Pape wrote: > >> On 06.09.2020, at 19:15, Eliot Miranda wrote: >> >> Hi Christoph, Hi All, >> >>> On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: >>> >>> Hi all! :-) >>> >>> After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, but so long, I have one general question for you: >>> >> >> And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values). In the 32-bit variant Characters are 30-bit unsigned integers. In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. >> >> Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant? It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure that initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. >> >> Q2, how many bits should the 64-bit variant VM support for immediate Characters? > > Unicode has a max value of 0x10FFFF. > That makes 21 bit. > So no worries there. > > We should just not forget the leading-char stuff (Yoshiki, Andreas,...) AFAIU the leading char only makes sense when you have multiple CJK(V?) languages in use at the same time. In other cases Unicode (leadingChar = 0) is perfectly fine. IIRC there are 22 bits available for the codePoint and 8 for the leadingChar, so we're still good: all unicode characters fit. Levente > > > BEst regards > -Tobias > >> >> Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to LargePositiveInteger beyond SmallInteger maxVal. This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. >> >> It has implications in a few parts of the system: >> - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances >> - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation >> - 32-bit <=> 64-bit image conversion >> >> All this is easily doable (because we have models of doing it for Float and Integer general instances). But we need good specifications so we can implement the right thing from the get-go. >> >>> At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. >>> >>> Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't know whether this will ever happen). >>> >>> >>> Examples: >>> Unicode generalTagOf: $a asUnicode. "#Ll" >>> >>> Unicode class >> isLetterCode: charCode >>> ^ (self generalTagOf: charCode) first = $L >>> >>> Unicode class >> isAlphaNumericCode: charCode >>> | tag| >>> ^ (tag := self generalCategoryOf: charCode) first = $L >>> or: [tag = #Nd] >>> >>> How do you think about this proposal? Please let me know and I will go ahead! :D >>> >>> Best, >>> Christoph >> >> Best, Eliot >> _,,,^..^,,,_ (phone) From Das.Linux at gmx.de Sun Sep 6 19:00:14 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sun, 6 Sep 2020 21:00:14 +0200 Subject: [squeak-dev] Unicode In-Reply-To: References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> Message-ID: <8DCD60E5-9959-4B5D-9C84-D0AC59405A4C@gmx.de> > On 06.09.2020, at 20:40, Levente Uzonyi wrote: > > On Sun, 6 Sep 2020, Tobias Pape wrote: > >> >>> On 06.09.2020, at 19:15, Eliot Miranda wrote: >>> Hi Christoph, Hi All, >>>> On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: >>>> Hi all! :-) >>>> After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, but so long, I have one general question for you: >>> And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values). In the 32-bit variant Characters are 30-bit unsigned integers. In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. >>> Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant? It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure that initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. >>> Q2, how many bits should the 64-bit variant VM support for immediate Characters? >> >> Unicode has a max value of 0x10FFFF. That makes 21 bit. So no worries there. >> >> We should just not forget the leading-char stuff (Yoshiki, Andreas,...) > > AFAIU the leading char only makes sense when you have multiple CJK(V?) languages in use at the same time. In other cases Unicode (leadingChar = 0) is perfectly fine. > IIRC there are 22 bits available for the codePoint and 8 for the leadingChar, so we're still good: all unicode characters fit. > > \o/ hooray! > Levente > >> >> >> BEst regards >> -Tobias >> >>> Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to LargePositiveInteger beyond SmallInteger maxVal. This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. >>> It has implications in a few parts of the system: >>> - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances >>> - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation >>> - 32-bit <=> 64-bit image conversion All this is easily doable (because we have models of doing it for Float and Integer general instances). But we need good specifications so we can implement the right thing from the get-go. >>>> At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. >>>> Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't know whether this will ever happen). >>>> Examples: >>>> Unicode generalTagOf: $a asUnicode. "#Ll" >>>> Unicode class >> isLetterCode: charCode >>>> ^ (self generalTagOf: charCode) first = $L >>>> Unicode class >> isAlphaNumericCode: charCode >>>> | tag| >>>> ^ (tag := self generalCategoryOf: charCode) first = $L >>>> or: [tag = #Nd] >>>> How do you think about this proposal? Please let me know and I will go ahead! :D >>>> Best, >>>> Christoph >>> Best, Eliot >>> _,,,^..^,,,_ (phone) From asqueaker at gmail.com Sun Sep 6 19:14:50 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sun, 6 Sep 2020 14:14:50 -0500 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.58.mcz In-Reply-To: <20200905173921.GA76329@shell.msen.com> References: <20200905173921.GA76329@shell.msen.com> Message-ID: Why? These are true constants. It's not only more words in the code, but an extra message send = slower. I see no "plus" side to this at all. If this is not a place to utilize a PoolDictionary, what is? -1 On Sat, Sep 5, 2020 at 12:39 PM David T. Lewis wrote: > > This replaces Chronology-Core-dtl.56.mcz, which I moved to the treated inbox. > > I am not lobbying strongly for this, just submitting it for consideration. > > Dave > > On Sat, Sep 05, 2020 at 05:35:43PM +0000, commits at source.squeak.org wrote: > > A new version of Chronology-Core was added to project The Inbox: > > http://source.squeak.org/inbox/Chronology-Core-dtl.58.mcz > > > > ==================== Summary ==================== > > > > Name: Chronology-Core-dtl.58 > > Author: dtl > > Time: 5 September 2020, 1:35:43.337716 pm > > UUID: c6ada6e9-d4a0-4f20-a71b-53ea65a114b6 > > Ancestors: Chronology-Core-dtl.57 > > > > Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. > > > > For Squeak, OneDay is now a class variable in order to continue to allow Date instances to share a Duration. Cuis creates a new Duration for each Date. > > > > =============== Diff against Chronology-Core-dtl.57 =============== > > > > Item was removed: > > - SharedPool subclass: #ChronologyConstants > > - instanceVariableNames: '' > > - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' > > - poolDictionaries: '' > > - category: 'Chronology-Core'! > > - > > - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! > > - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! > > > > Item was removed: > > - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- > > - initialize > > - "ChronologyConstants initialize" > > - > > - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" > > - SecondsInDay := 86400. > > - SecondsInHour := 3600. > > - SecondsInMinute := 60. > > - MicrosecondsInDay := 24 * 60 * 60 * 1000000. > > - NanosInSecond := 10 raisedTo: 9. > > - NanosInMillisecond := 10 raisedTo: 6. > > - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). > > - > > - MonthNames := #( January February March April May June > > - July August September October November December). > > - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! > > > > Item was changed: > > Timespan subclass: #Date > > instanceVariableNames: '' > > classVariableNames: '' > > + poolDictionaries: '' > > - poolDictionaries: 'ChronologyConstants' > > category: 'Chronology-Core'! > > > > !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! > > Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. > > > > However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! > > > > Item was changed: > > ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- > > fromDays: dayCount > > "Days since 1 January 1901" > > > > + ^ self julianDayNumber: dayCount + Time squeakEpoch! > > - ^ self julianDayNumber: dayCount + SqueakEpoch! > > > > Item was changed: > > Magnitude subclass: #DateAndTime > > instanceVariableNames: 'utcMicroseconds localOffsetSeconds' > > classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' > > + poolDictionaries: '' > > - poolDictionaries: 'ChronologyConstants' > > category: 'Chronology-Core'! > > > > !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! > > I represent a point in UTC time as defined by ISO 8601. I have zero duration. > > > > My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. > > > > The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. > > > > DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. > > ! > > > > Item was changed: > > ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- > > epochOffset > > "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" > > + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! > > - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! > > > > Item was changed: > > ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- > > posixEpochJulianDays > > > > + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! > > - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! > > > > Item was changed: > > ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- > > ticks: ticks offset: utcOffset > > "ticks is {julianDayNumber. secondCount. nanoSeconds}" > > > > | jdn s nanos normalizedTicks | > > normalizedTicks := ticks copy. > > + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. > > + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. > > - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. > > - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. > > > > jdn := normalizedTicks at: 1. > > s := normalizedTicks at: 2. > > nanos := normalizedTicks at: 3. > > localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. > > utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. > > ! > > > > Item was changed: > > Magnitude subclass: #Duration > > instanceVariableNames: 'nanos seconds' > > + classVariableNames: 'OneDay' > > + poolDictionaries: '' > > - classVariableNames: '' > > - poolDictionaries: 'ChronologyConstants' > > category: 'Chronology-Core'! > > > > !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > > I represent a duration of time. I have nanosecond precision! > > > > Item was changed: > > ----- Method: Duration class>>days: (in category 'squeak protocol') ----- > > days: aNumber > > > > + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! > > - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! > > > > Item was changed: > > ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- > > days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos > > > > ^self > > seconds: seconds > > + + (minutes * Time secondsInMinute) > > + + (hours * Time secondsInHour) > > + + (days * Time secondsInDay) > > - + (minutes * SecondsInMinute) > > - + (hours * SecondsInHour) > > - + (days * SecondsInDay) > > nanoSeconds: nanos > > ! > > > > Item was changed: > > ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- > > days: days seconds: seconds > > > > + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! > > - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 > > - ! > > > > Item was changed: > > ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- > > hours: aNumber > > > > + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! > > - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! > > > > Item was changed: > > ----- Method: Duration class>>initialize (in category 'initialize-release') ----- > > initialize > > + "Duration oneDay is used in Date creation, and is cached to allow > > + sharing the instance." > > + OneDay := 1 day. > > + self class recompile: #oneDay.! > > - ChronologyConstants classPool > > - at: #Zero > > - put: > > - (self basicNew > > - seconds: 0 > > - nanoSeconds: 0) ; > > - at: #OneDay > > - put: 1 day! > > > > Item was changed: > > ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- > > milliSeconds: milliCount > > > > ^self > > seconds: (milliCount quo: 1000) > > + nanoSeconds: (milliCount rem: 1000) * 1000000! > > - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! > > > > Item was changed: > > ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- > > minutes: aNumber > > > > + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! > > - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! > > > > Item was changed: > > ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- > > nanoSeconds: nanos > > "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." > > > > | quo | > > + quo _ nanos quo: Time nanosInSecond. > > - quo := nanos quo: NanosInSecond. > > ^ self basicNew > > seconds: quo > > + nanoSeconds: nanos - (quo * Time nanosInSecond)! > > - nanoSeconds: nanos - (quo * NanosInSecond) > > - ! > > > > Item was changed: > > ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- > > seconds: seconds nanoSeconds: nanos > > > > ^ self basicNew > > seconds: seconds truncated > > + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! > > - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! > > > > Item was changed: > > ----- Method: Duration class>>zero (in category 'ansi protocol') ----- > > zero > > + > > + ^ self basicNew seconds: 0 nanoSeconds: 0 > > + ! > > - "Answer the canonicalized instance of Duration zero." > > - ^ Zero! > > > > Item was changed: > > ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- > > asNanoSeconds > > > > + ^seconds * Time nanosInSecond + nanos! > > - ^seconds * NanosInSecond + nanos! > > > > Item was changed: > > ----- Method: Duration>>days (in category 'ansi protocol') ----- > > days > > "Answer the number of days the receiver represents." > > > > + ^ seconds quo: Time secondsInDay! > > - ^ seconds quo: SecondsInDay > > - ! > > > > Item was changed: > > ----- Method: Duration>>hours (in category 'ansi protocol') ----- > > hours > > "Answer the number of hours the receiver represents." > > > > > > + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! > > - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! > > > > Item was changed: > > ----- Method: Duration>>minutes (in category 'ansi protocol') ----- > > minutes > > - > > "Answer the number of minutes the receiver represents." > > > > + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! > > - > > - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! > > > > Item was changed: > > ----- Method: Duration>>seconds (in category 'ansi protocol') ----- > > seconds > > "Answer the number of seconds the receiver represents." > > > > + ^seconds rem: Time secondsInMinute! > > - ^seconds rem: SecondsInMinute! > > > > Item was changed: > > ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- > > seconds: secondCount nanoSeconds: nanoCount > > "Private - only used by Duration class" > > > > seconds := secondCount. > > nanos := nanoCount rounded. > > "normalize if signs do not match" > > [ nanos < 0 and: [ seconds > 0 ] ] > > whileTrue: [ seconds := seconds - 1. > > + nanos := nanos + Time nanosInSecond ]. > > - nanos := nanos + NanosInSecond ]. > > [ seconds < 0 and: [ nanos > 0 ] ] > > whileTrue: [ seconds := seconds + 1. > > + nanos := nanos - Time nanosInSecond ] > > - nanos := nanos - NanosInSecond ] > > > > ! > > > > Item was changed: > > ----- Method: Duration>>ticks (in category 'private') ----- > > ticks > > "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." > > > > | days | > > days := self days. > > ^ Array > > with: days > > + with: seconds - (days * Time secondsInDay) > > - with: seconds - (days * SecondsInDay) > > with: nanos > > ! > > > > Item was changed: > > Timespan subclass: #Month > > instanceVariableNames: '' > > classVariableNames: '' > > + poolDictionaries: '' > > - poolDictionaries: 'ChronologyConstants' > > category: 'Chronology-Core'! > > > > !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > > I represent a month. > > > > For example, to get the number of days this month, you can evaluate the following expression: > > > > Month current daysInMonth! > > > > Item was added: > > + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- > > + daysInMonth > > + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! > > > > Item was changed: > > ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- > > daysInMonth: indexOrName forYear: yearInteger > > > > | index | > > index := indexOrName isInteger > > ifTrue: [indexOrName] > > ifFalse: [self indexOfMonth: indexOrName]. > > + ^ (self daysInMonth at: index) > > - ^ (DaysInMonth at: index) > > + ((index = 2 > > and: [Year isLeapYear: yearInteger]) > > ifTrue: [1] ifFalse: [0]) > > ! > > > > Item was changed: > > ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- > > indexOfMonth: aMonthName > > > > > > + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. > > + self error: aMonthName , ' is not a recognized month name'! > > - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. > > - self error: aMonthName , ' is not a recognized month name'.! > > > > Item was added: > > + ----- Method: Month class>>monthNames (in category 'inquiries') ----- > > + monthNames > > + ^#(January February March April May June July August September October November December)! > > > > Item was changed: > > ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- > > nameOfMonth: anIndex > > > > + ^ self monthNames at: anIndex! > > - ^ MonthNames at: anIndex.! > > > > Item was changed: > > Magnitude subclass: #Time > > instanceVariableNames: 'seconds nanos' > > classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' > > + poolDictionaries: '' > > - poolDictionaries: 'ChronologyConstants' > > category: 'Chronology-Core'! > > > > !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! > > This represents a particular point in time during any given day. For example, '5:19:45 pm'. > > > > If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. > > ! > > > > Item was changed: > > ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- > > fromSeconds: secondCount > > "Answer an instance of me that is secondCount number of seconds since midnight." > > > > | integerSeconds nanos | > > integerSeconds := secondCount truncated. > > integerSeconds = secondCount > > ifTrue: [nanos := 0] > > + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. > > - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. > > ^ self seconds: integerSeconds nanoSeconds: nanos > > ! > > > > Item was changed: > > ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- > > + hour: hour minute: minute second: second nanoSecond: nanoCount > > + "Answer a Time" > > - hour: hour minute: minute second: second nanoSecond: nanoCount > > - "Answer a Time - only second precision for now" > > > > ^ self > > + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second > > + nanoSeconds: nanoCount! > > - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second > > - nanoSeconds: nanoCount > > - ! > > > > Item was added: > > + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- > > + nanosInSecond > > + ^ 1000000000! > > > > Item was changed: > > ----- Method: Time class>>noon (in category 'squeak protocol') ----- > > noon > > > > + ^ self seconds: self secondsInDay / 2! > > - ^ self seconds: (SecondsInDay / 2) > > - ! > > > > Item was changed: > > ----- Method: Time class>>now (in category 'ansi protocol') ----- > > now > > "Answer a Time representing the time right now - this is a 24 hour clock." > > | localUsecs localUsecsToday | > > localUsecs := self localMicrosecondClock. > > + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" > > - localUsecsToday := localUsecs \\ MicrosecondsInDay. > > ^ self > > seconds: localUsecsToday // 1000000 > > nanoSeconds: localUsecsToday \\ 1000000 * 1000! > > > > Item was added: > > + ----- Method: Time class>>secondsInDay (in category 'constants') ----- > > + secondsInDay > > + ^86400! > > > > Item was added: > > + ----- Method: Time class>>secondsInHour (in category 'constants') ----- > > + secondsInHour > > + ^3600! > > > > Item was added: > > + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- > > + secondsInMinute > > + ^60! > > > > Item was added: > > + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- > > + squeakEpoch > > + ^ 2415386. "Julian day number of 1 Jan 1901"! > > > > Item was changed: > > ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- > > print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream > > "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. > > If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" > > > > | h m s | > > h := self hour. m := self minute. s := self second. > > hr24 > > ifTrue: > > [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. > > h printOn: aStream ] > > ifFalse: > > [ h > 12 > > ifTrue: [h - 12 printOn: aStream] > > ifFalse: > > [h < 1 > > ifTrue: [ 12 printOn: aStream ] > > ifFalse: [ h printOn: aStream ]]]. > > > > aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). > > m printOn: aStream. > > > > showSeconds ifTrue: > > [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). > > (showSubseconds not or: [self nanoSecond = 0]) > > ifTrue: [s asInteger printOn: aStream] > > + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger > > + printOn: aStream asFixedPoint: Time nanosInSecond]]. > > - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger > > - printOn: aStream asFixedPoint: NanosInSecond]]. > > > > hr24 ifFalse: > > [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. > > ! > > > > Item was changed: > > Object subclass: #TimeZone > > instanceVariableNames: 'offset abbreviation name' > > classVariableNames: '' > > + poolDictionaries: '' > > - poolDictionaries: 'ChronologyConstants' > > category: 'Chronology-Core'! > > > > !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > > TimeZone is a simple class to colect the information identifying a UTC time zone. > > > > offset - Duration - the time zone's offset from UTC > > abbreviation - String - the abbreviated name for the time zone. > > name - String - the name of the time zone. > > > > TimeZone class >> #timeZones returns an array of the known time zones > > TimeZone class >> #default returns the default time zone (Grenwich Mean Time) > > ! > > > > Item was changed: > > Timespan subclass: #Week > > instanceVariableNames: '' > > classVariableNames: 'StartDay' > > + poolDictionaries: '' > > - poolDictionaries: 'ChronologyConstants' > > category: 'Chronology-Core'! > > > > !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > > I represent a week. > > > > To find out what days of the week on which Squeak is fun, select the following expression, and print it: > > > > Week dayNames! > > > > Item was changed: > > + ----- Method: Week class>>dayNames (in category 'inquiries') ----- > > - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- > > dayNames > > > > + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! > > - ^ DayNames! > > > > Item was changed: > > ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- > > indexOfDay: aSymbol > > > > + ^ self dayNames indexOf: aSymbol! > > - ^ DayNames indexOf: aSymbol! > > > > Item was changed: > > ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- > > nameOfDay: anIndex > > > > + ^ self dayNames at: anIndex! > > - ^ DayNames at: anIndex! > > > > Item was changed: > > ----- Method: Week class>>startDay (in category 'squeak protocol') ----- > > startDay > > > > ^ StartDay ifNil: [ StartDay > > + := self dayNames first ] > > - := DayNames first ] > > ! > > > > Item was changed: > > ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- > > startDay: aSymbol > > > > + (self dayNames includes: aSymbol) > > - (DayNames includes: aSymbol) > > ifTrue: [ StartDay := aSymbol ] > > ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! > > > > Item was changed: > > ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- > > starting: aDateAndTime duration: aDuration > > "Override - the duration is always one week. > > Week will start from the Week class>>startDay" > > > > | midnight delta adjusted | > > midnight := aDateAndTime asDateAndTime midnight. > > + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. > > - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. > > adjusted := midnight - (Duration days: delta seconds: 0). > > > > ^ super starting: adjusted duration: (Duration weeks: 1)! > > > > > From lewis at mail.msen.com Sun Sep 6 19:37:08 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 6 Sep 2020 15:37:08 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.58.mcz In-Reply-To: References: <20200905173921.GA76329@shell.msen.com> Message-ID: <20200906193708.GA35202@shell.msen.com> On Sun, Sep 06, 2020 at 02:14:50PM -0500, Chris Muller wrote: > Why? These are true constants. It's not only more words in the code, > but an extra message send = slower. > > I see no "plus" side to this at all. If this is not a place to > utilize a PoolDictionary, what is? > > -1 > Levente also asked but the motiviation for the change, and my reply was: I can't speak for Juan, but my assumption is that the original motivation was that of Cuis overall - make things as simple and approachable as possible. My personal motivation in suggesting it for Squeak is that I'm interested in making the Squeak UTC DateAndTime available for Cuis, which is actually a rather challenging thing to do. It's easier for me if I don't have to re-implement Juan's improvments after (hypothetically) porting the code from Squeak to Cuis. So if they are worthwhile changes, and I think that they are, then it's easier to just do the update once in Squeak so that we can just be rid of the incompatibilities. That's all. I'll move it to treated if it's not a good idea. Note, Chronology-Core-dtl.58 is already moved to treated, so the inbox currently has Chronology-Core-dtl.59, which contains an update from Levente that may or may not make the overall idea any more agreeable. I'll leave it in inbox for a day or two and move it to treated if there are no further comments. Dave > On Sat, Sep 5, 2020 at 12:39 PM David T. Lewis wrote: > > > > This replaces Chronology-Core-dtl.56.mcz, which I moved to the treated inbox. > > > > I am not lobbying strongly for this, just submitting it for consideration. > > > > Dave > > > > On Sat, Sep 05, 2020 at 05:35:43PM +0000, commits at source.squeak.org wrote: > > > A new version of Chronology-Core was added to project The Inbox: > > > http://source.squeak.org/inbox/Chronology-Core-dtl.58.mcz > > > > > > ==================== Summary ==================== > > > > > > Name: Chronology-Core-dtl.58 > > > Author: dtl > > > Time: 5 September 2020, 1:35:43.337716 pm > > > UUID: c6ada6e9-d4a0-4f20-a71b-53ea65a114b6 > > > Ancestors: Chronology-Core-dtl.57 > > > > > > Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. > > > > > > For Squeak, OneDay is now a class variable in order to continue to allow Date instances to share a Duration. Cuis creates a new Duration for each Date. > > > > > > =============== Diff against Chronology-Core-dtl.57 =============== > > > > > > Item was removed: > > > - SharedPool subclass: #ChronologyConstants > > > - instanceVariableNames: '' > > > - classVariableNames: 'DayNames DaysInMonth MicrosecondsInDay MonthNames NanosInMillisecond NanosInSecond OneDay SecondsInDay SecondsInHour SecondsInMinute SqueakEpoch Zero' > > > - poolDictionaries: '' > > > - category: 'Chronology-Core'! > > > - > > > - !ChronologyConstants commentStamp: 'brp 3/12/2004 14:34' prior: 0! > > > - ChronologyConstants is a SharedPool for the constants used by the Kernel-Chronology classes.! > > > > > > Item was removed: > > > - ----- Method: ChronologyConstants class>>initialize (in category 'class initialization') ----- > > > - initialize > > > - "ChronologyConstants initialize" > > > - > > > - SqueakEpoch := 2415386. "Julian day number of 1 Jan 1901" > > > - SecondsInDay := 86400. > > > - SecondsInHour := 3600. > > > - SecondsInMinute := 60. > > > - MicrosecondsInDay := 24 * 60 * 60 * 1000000. > > > - NanosInSecond := 10 raisedTo: 9. > > > - NanosInMillisecond := 10 raisedTo: 6. > > > - DayNames := #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday). > > > - > > > - MonthNames := #( January February March April May June > > > - July August September October November December). > > > - DaysInMonth := #(31 28 31 30 31 30 31 31 30 31 30 31)! > > > > > > Item was changed: > > > Timespan subclass: #Date > > > instanceVariableNames: '' > > > classVariableNames: '' > > > + poolDictionaries: '' > > > - poolDictionaries: 'ChronologyConstants' > > > category: 'Chronology-Core'! > > > > > > !Date commentStamp: 'cmm 6/28/2016 21:36' prior: 0! > > > Instances of Date are Timespans with duration of 1 day. As with all Chronology Timespan sub-instances, Dates can be instantiated as position values which compare equally to any other instance of the same Date, irregardless of the timezone in which either is created. > > > > > > However, like the other Timespan subInstances, there are rare cases where it may be desirable to use instances of Date to represent a particular 1-day span of time at a particular locality on the globe. All Timespans, including Dates, may specify a particular timezone offset for this purpose.! > > > > > > Item was changed: > > > ----- Method: Date class>>fromDays: (in category 'smalltalk-80') ----- > > > fromDays: dayCount > > > "Days since 1 January 1901" > > > > > > + ^ self julianDayNumber: dayCount + Time squeakEpoch! > > > - ^ self julianDayNumber: dayCount + SqueakEpoch! > > > > > > Item was changed: > > > Magnitude subclass: #DateAndTime > > > instanceVariableNames: 'utcMicroseconds localOffsetSeconds' > > > classVariableNames: 'AutomaticTimezone ClockProvider InitializeFromPrimitive LocalTimeZone PosixEpochJulianDays' > > > + poolDictionaries: '' > > > - poolDictionaries: 'ChronologyConstants' > > > category: 'Chronology-Core'! > > > > > > !DateAndTime commentStamp: 'dtl 3/12/2016 10:32' prior: 0! > > > I represent a point in UTC time as defined by ISO 8601. I have zero duration. > > > > > > My implementation uses variables utcMicroseconds and localOffsetSeconds. This represents time magnitude as elapsed microseconds since the Posix epoch, with localOffsetSeconds representing local offset from UTC. The magnitude is used for comparison and duration calculations, and the local offset is used for displaying this magnitude in the context of a local time zone. > > > > > > The implementation ignores leap seconds, which are adjustments made to maintain earth rotational clock time in synchronization with elapsed seconds. > > > > > > DateAndTime class>>now will use #primitiveUtcWithOffset to obtain current time in UTC microseconds with current local offset in seconds. The primitive provides an atomic query for UTC time and local offset as measured by the OS platform. If primitiveUtcWithOffset is not available, the traditional implementation is used, which relies on a primitive for microseconds in the local time zone and derives UTC based on the TimeZone setting. > > > ! > > > > > > Item was changed: > > > ----- Method: DateAndTime class>>epochOffset (in category 'private') ----- > > > epochOffset > > > "Elaspsed seconds from the Smalltalk epoch to the Posix epoch" > > > + ^self daysFromSmalltalkEpochToPosixEpoch * Time secondsInDay! > > > - ^self daysFromSmalltalkEpochToPosixEpoch * SecondsInDay! > > > > > > Item was changed: > > > ----- Method: DateAndTime>>posixEpochJulianDays (in category 'initialize-release') ----- > > > posixEpochJulianDays > > > > > > + ^self class daysFromSmalltalkEpochToPosixEpoch + Time squeakEpoch! > > > - ^self class daysFromSmalltalkEpochToPosixEpoch + SqueakEpoch! > > > > > > Item was changed: > > > ----- Method: DateAndTime>>ticks:offset: (in category 'private') ----- > > > ticks: ticks offset: utcOffset > > > "ticks is {julianDayNumber. secondCount. nanoSeconds}" > > > > > > | jdn s nanos normalizedTicks | > > > normalizedTicks := ticks copy. > > > + self normalize: 3 ticks: normalizedTicks base: Time nanosInSecond. > > > + self normalize: 2 ticks: normalizedTicks base: Time secondsInDay. > > > - self normalize: 3 ticks: normalizedTicks base: NanosInSecond. > > > - self normalize: 2 ticks: normalizedTicks base: SecondsInDay. > > > > > > jdn := normalizedTicks at: 1. > > > s := normalizedTicks at: 2. > > > nanos := normalizedTicks at: 3. > > > localOffsetSeconds := utcOffset ifNil: [0] ifNotNil: [utcOffset asSeconds]. > > > utcMicroseconds := self microsecondsFromDay: jdn seconds: s nanos: nanos offset: localOffsetSeconds. > > > ! > > > > > > Item was changed: > > > Magnitude subclass: #Duration > > > instanceVariableNames: 'nanos seconds' > > > + classVariableNames: 'OneDay' > > > + poolDictionaries: '' > > > - classVariableNames: '' > > > - poolDictionaries: 'ChronologyConstants' > > > category: 'Chronology-Core'! > > > > > > !Duration commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > > > I represent a duration of time. I have nanosecond precision! > > > > > > Item was changed: > > > ----- Method: Duration class>>days: (in category 'squeak protocol') ----- > > > days: aNumber > > > > > > + ^ self seconds: aNumber * Time secondsInDay nanoSeconds: 0! > > > - ^ self seconds: aNumber * SecondsInDay nanoSeconds: 0! > > > > > > Item was changed: > > > ----- Method: Duration class>>days:hours:minutes:seconds:nanoSeconds: (in category 'squeak protocol') ----- > > > days: days hours: hours minutes: minutes seconds: seconds nanoSeconds: nanos > > > > > > ^self > > > seconds: seconds > > > + + (minutes * Time secondsInMinute) > > > + + (hours * Time secondsInHour) > > > + + (days * Time secondsInDay) > > > - + (minutes * SecondsInMinute) > > > - + (hours * SecondsInHour) > > > - + (days * SecondsInDay) > > > nanoSeconds: nanos > > > ! > > > > > > Item was changed: > > > ----- Method: Duration class>>days:seconds: (in category 'ansi protocol') ----- > > > days: days seconds: seconds > > > > > > + ^ self basicNew seconds: days * Time secondsInDay + seconds nanoSeconds: 0! > > > - ^ self basicNew seconds: days * SecondsInDay + seconds nanoSeconds: 0 > > > - ! > > > > > > Item was changed: > > > ----- Method: Duration class>>hours: (in category 'squeak protocol') ----- > > > hours: aNumber > > > > > > + ^ self seconds: aNumber * Time secondsInHour nanoSeconds: 0! > > > - ^ self seconds: aNumber * SecondsInHour nanoSeconds: 0! > > > > > > Item was changed: > > > ----- Method: Duration class>>initialize (in category 'initialize-release') ----- > > > initialize > > > + "Duration oneDay is used in Date creation, and is cached to allow > > > + sharing the instance." > > > + OneDay := 1 day. > > > + self class recompile: #oneDay.! > > > - ChronologyConstants classPool > > > - at: #Zero > > > - put: > > > - (self basicNew > > > - seconds: 0 > > > - nanoSeconds: 0) ; > > > - at: #OneDay > > > - put: 1 day! > > > > > > Item was changed: > > > ----- Method: Duration class>>milliSeconds: (in category 'squeak protocol') ----- > > > milliSeconds: milliCount > > > > > > ^self > > > seconds: (milliCount quo: 1000) > > > + nanoSeconds: (milliCount rem: 1000) * 1000000! > > > - nanoSeconds: (milliCount rem: 1000) * NanosInMillisecond! > > > > > > Item was changed: > > > ----- Method: Duration class>>minutes: (in category 'squeak protocol') ----- > > > minutes: aNumber > > > > > > + ^ self seconds: aNumber * Time secondsInMinute nanoSeconds: 0! > > > - ^ self seconds: aNumber * SecondsInMinute nanoSeconds: 0! > > > > > > Item was changed: > > > ----- Method: Duration class>>nanoSeconds: (in category 'squeak protocol') ----- > > > nanoSeconds: nanos > > > "This method is slow. If you have nanos less than 10^6 you should use #seconds:nanoSeconds: instead." > > > > > > | quo | > > > + quo _ nanos quo: Time nanosInSecond. > > > - quo := nanos quo: NanosInSecond. > > > ^ self basicNew > > > seconds: quo > > > + nanoSeconds: nanos - (quo * Time nanosInSecond)! > > > - nanoSeconds: nanos - (quo * NanosInSecond) > > > - ! > > > > > > Item was changed: > > > ----- Method: Duration class>>seconds:nanoSeconds: (in category 'squeak protocol') ----- > > > seconds: seconds nanoSeconds: nanos > > > > > > ^ self basicNew > > > seconds: seconds truncated > > > + nanoSeconds: seconds fractionPart * Time nanosInSecond + nanos! > > > - nanoSeconds: seconds fractionPart * NanosInSecond + nanos! > > > > > > Item was changed: > > > ----- Method: Duration class>>zero (in category 'ansi protocol') ----- > > > zero > > > + > > > + ^ self basicNew seconds: 0 nanoSeconds: 0 > > > + ! > > > - "Answer the canonicalized instance of Duration zero." > > > - ^ Zero! > > > > > > Item was changed: > > > ----- Method: Duration>>asNanoSeconds (in category 'squeak protocol') ----- > > > asNanoSeconds > > > > > > + ^seconds * Time nanosInSecond + nanos! > > > - ^seconds * NanosInSecond + nanos! > > > > > > Item was changed: > > > ----- Method: Duration>>days (in category 'ansi protocol') ----- > > > days > > > "Answer the number of days the receiver represents." > > > > > > + ^ seconds quo: Time secondsInDay! > > > - ^ seconds quo: SecondsInDay > > > - ! > > > > > > Item was changed: > > > ----- Method: Duration>>hours (in category 'ansi protocol') ----- > > > hours > > > "Answer the number of hours the receiver represents." > > > > > > > > > + ^ (seconds rem: Time secondsInDay) quo: Time secondsInHour! > > > - ^ (seconds rem: SecondsInDay) quo: SecondsInHour! > > > > > > Item was changed: > > > ----- Method: Duration>>minutes (in category 'ansi protocol') ----- > > > minutes > > > - > > > "Answer the number of minutes the receiver represents." > > > > > > + ^ (seconds rem: Time secondsInHour) quo: Time secondsInMinute! > > > - > > > - ^ (seconds rem: SecondsInHour) quo: SecondsInMinute! > > > > > > Item was changed: > > > ----- Method: Duration>>seconds (in category 'ansi protocol') ----- > > > seconds > > > "Answer the number of seconds the receiver represents." > > > > > > + ^seconds rem: Time secondsInMinute! > > > - ^seconds rem: SecondsInMinute! > > > > > > Item was changed: > > > ----- Method: Duration>>seconds:nanoSeconds: (in category 'private') ----- > > > seconds: secondCount nanoSeconds: nanoCount > > > "Private - only used by Duration class" > > > > > > seconds := secondCount. > > > nanos := nanoCount rounded. > > > "normalize if signs do not match" > > > [ nanos < 0 and: [ seconds > 0 ] ] > > > whileTrue: [ seconds := seconds - 1. > > > + nanos := nanos + Time nanosInSecond ]. > > > - nanos := nanos + NanosInSecond ]. > > > [ seconds < 0 and: [ nanos > 0 ] ] > > > whileTrue: [ seconds := seconds + 1. > > > + nanos := nanos - Time nanosInSecond ] > > > - nanos := nanos - NanosInSecond ] > > > > > > ! > > > > > > Item was changed: > > > ----- Method: Duration>>ticks (in category 'private') ----- > > > ticks > > > "Answer an array {days. seconds. nanoSeconds}. Used by DateAndTime and Time." > > > > > > | days | > > > days := self days. > > > ^ Array > > > with: days > > > + with: seconds - (days * Time secondsInDay) > > > - with: seconds - (days * SecondsInDay) > > > with: nanos > > > ! > > > > > > Item was changed: > > > Timespan subclass: #Month > > > instanceVariableNames: '' > > > classVariableNames: '' > > > + poolDictionaries: '' > > > - poolDictionaries: 'ChronologyConstants' > > > category: 'Chronology-Core'! > > > > > > !Month commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > > > I represent a month. > > > > > > For example, to get the number of days this month, you can evaluate the following expression: > > > > > > Month current daysInMonth! > > > > > > Item was added: > > > + ----- Method: Month class>>daysInMonth (in category 'inquiries') ----- > > > + daysInMonth > > > + ^#(31 28 31 30 31 30 31 31 30 31 30 31)! > > > > > > Item was changed: > > > ----- Method: Month class>>daysInMonth:forYear: (in category 'smalltalk-80') ----- > > > daysInMonth: indexOrName forYear: yearInteger > > > > > > | index | > > > index := indexOrName isInteger > > > ifTrue: [indexOrName] > > > ifFalse: [self indexOfMonth: indexOrName]. > > > + ^ (self daysInMonth at: index) > > > - ^ (DaysInMonth at: index) > > > + ((index = 2 > > > and: [Year isLeapYear: yearInteger]) > > > ifTrue: [1] ifFalse: [0]) > > > ! > > > > > > Item was changed: > > > ----- Method: Month class>>indexOfMonth: (in category 'smalltalk-80') ----- > > > indexOfMonth: aMonthName > > > > > > > > > + 1 to: 12 do: [ :i | (aMonthName, '*' match: (self monthNames at: i)) ifTrue: [^i] ]. > > > + self error: aMonthName , ' is not a recognized month name'! > > > - 1 to: 12 do: [ :i | (aMonthName, '*' match: (MonthNames at: i)) ifTrue: [^i] ]. > > > - self error: aMonthName , ' is not a recognized month name'.! > > > > > > Item was added: > > > + ----- Method: Month class>>monthNames (in category 'inquiries') ----- > > > + monthNames > > > + ^#(January February March April May June July August September October November December)! > > > > > > Item was changed: > > > ----- Method: Month class>>nameOfMonth: (in category 'smalltalk-80') ----- > > > nameOfMonth: anIndex > > > > > > + ^ self monthNames at: anIndex! > > > - ^ MonthNames at: anIndex.! > > > > > > Item was changed: > > > Magnitude subclass: #Time > > > instanceVariableNames: 'seconds nanos' > > > classVariableNames: 'ClockPolicy HighResClockTicksPerMillisecond LastClockTick UpdateVMTimeZoneCacheAt UseHighResClockForTiming' > > > + poolDictionaries: '' > > > - poolDictionaries: 'ChronologyConstants' > > > category: 'Chronology-Core'! > > > > > > !Time commentStamp: 'dew 10/23/2004 17:58' prior: 0! > > > This represents a particular point in time during any given day. For example, '5:19:45 pm'. > > > > > > If you need a point in time on a particular day, use DateAndTime. If you need a duration of time, use Duration. > > > ! > > > > > > Item was changed: > > > ----- Method: Time class>>fromSeconds: (in category 'smalltalk-80') ----- > > > fromSeconds: secondCount > > > "Answer an instance of me that is secondCount number of seconds since midnight." > > > > > > | integerSeconds nanos | > > > integerSeconds := secondCount truncated. > > > integerSeconds = secondCount > > > ifTrue: [nanos := 0] > > > + ifFalse: [nanos := (secondCount - integerSeconds * self nanosInSecond) asInteger]. > > > - ifFalse: [nanos := (secondCount - integerSeconds * NanosInSecond) asInteger]. > > > ^ self seconds: integerSeconds nanoSeconds: nanos > > > ! > > > > > > Item was changed: > > > ----- Method: Time class>>hour:minute:second:nanoSecond: (in category 'squeak protocol') ----- > > > + hour: hour minute: minute second: second nanoSecond: nanoCount > > > + "Answer a Time" > > > - hour: hour minute: minute second: second nanoSecond: nanoCount > > > - "Answer a Time - only second precision for now" > > > > > > ^ self > > > + seconds: (hour * self secondsInHour) + (minute * self secondsInMinute) + second > > > + nanoSeconds: nanoCount! > > > - seconds: (hour * SecondsInHour) + (minute * SecondsInMinute) + second > > > - nanoSeconds: nanoCount > > > - ! > > > > > > Item was added: > > > + ----- Method: Time class>>nanosInSecond (in category 'constants') ----- > > > + nanosInSecond > > > + ^ 1000000000! > > > > > > Item was changed: > > > ----- Method: Time class>>noon (in category 'squeak protocol') ----- > > > noon > > > > > > + ^ self seconds: self secondsInDay / 2! > > > - ^ self seconds: (SecondsInDay / 2) > > > - ! > > > > > > Item was changed: > > > ----- Method: Time class>>now (in category 'ansi protocol') ----- > > > now > > > "Answer a Time representing the time right now - this is a 24 hour clock." > > > | localUsecs localUsecsToday | > > > localUsecs := self localMicrosecondClock. > > > + localUsecsToday := localUsecs \\ 86400000000. "24 * 60 * 60 * 1000000" > > > - localUsecsToday := localUsecs \\ MicrosecondsInDay. > > > ^ self > > > seconds: localUsecsToday // 1000000 > > > nanoSeconds: localUsecsToday \\ 1000000 * 1000! > > > > > > Item was added: > > > + ----- Method: Time class>>secondsInDay (in category 'constants') ----- > > > + secondsInDay > > > + ^86400! > > > > > > Item was added: > > > + ----- Method: Time class>>secondsInHour (in category 'constants') ----- > > > + secondsInHour > > > + ^3600! > > > > > > Item was added: > > > + ----- Method: Time class>>secondsInMinute (in category 'constants') ----- > > > + secondsInMinute > > > + ^60! > > > > > > Item was added: > > > + ----- Method: Time class>>squeakEpoch (in category 'constants') ----- > > > + squeakEpoch > > > + ^ 2415386. "Julian day number of 1 Jan 1901"! > > > > > > Item was changed: > > > ----- Method: Time>>print24:showSeconds:showSubseconds:on: (in category 'printing') ----- > > > print24: hr24 showSeconds: showSeconds showSubseconds: showSubseconds on: aStream > > > "Format is 'hh:mm:ss' or 'h:mm:ss am' or, if showSeconds is false, 'hh:mm' or 'h:mm am'. > > > If showSubseconds is true and our nanoSeconds are not zero, a decimal point and subseconds are added" > > > > > > | h m s | > > > h := self hour. m := self minute. s := self second. > > > hr24 > > > ifTrue: > > > [ h < 10 ifTrue: [ aStream nextPutAll: '0' ]. > > > h printOn: aStream ] > > > ifFalse: > > > [ h > 12 > > > ifTrue: [h - 12 printOn: aStream] > > > ifFalse: > > > [h < 1 > > > ifTrue: [ 12 printOn: aStream ] > > > ifFalse: [ h printOn: aStream ]]]. > > > > > > aStream nextPutAll: (m < 10 ifTrue: [':0'] ifFalse: [':']). > > > m printOn: aStream. > > > > > > showSeconds ifTrue: > > > [ aStream nextPutAll: (s < 10 ifTrue: [':0'] ifFalse: [':']). > > > (showSubseconds not or: [self nanoSecond = 0]) > > > ifTrue: [s asInteger printOn: aStream] > > > + ifFalse: [s asInteger * Time nanosInSecond + self nanoSecond asInteger > > > + printOn: aStream asFixedPoint: Time nanosInSecond]]. > > > - ifFalse: [s asInteger * NanosInSecond + self nanoSecond asInteger > > > - printOn: aStream asFixedPoint: NanosInSecond]]. > > > > > > hr24 ifFalse: > > > [ aStream nextPutAll: (h < 12 ifTrue: [' am'] ifFalse: [' pm']) ]. > > > ! > > > > > > Item was changed: > > > Object subclass: #TimeZone > > > instanceVariableNames: 'offset abbreviation name' > > > classVariableNames: '' > > > + poolDictionaries: '' > > > - poolDictionaries: 'ChronologyConstants' > > > category: 'Chronology-Core'! > > > > > > !TimeZone commentStamp: 'dtl 7/11/2009 15:03' prior: 0! > > > TimeZone is a simple class to colect the information identifying a UTC time zone. > > > > > > offset - Duration - the time zone's offset from UTC > > > abbreviation - String - the abbreviated name for the time zone. > > > name - String - the name of the time zone. > > > > > > TimeZone class >> #timeZones returns an array of the known time zones > > > TimeZone class >> #default returns the default time zone (Grenwich Mean Time) > > > ! > > > > > > Item was changed: > > > Timespan subclass: #Week > > > instanceVariableNames: '' > > > classVariableNames: 'StartDay' > > > + poolDictionaries: '' > > > - poolDictionaries: 'ChronologyConstants' > > > category: 'Chronology-Core'! > > > > > > !Week commentStamp: 'cbr 7/28/2010 18:11' prior: 0! > > > I represent a week. > > > > > > To find out what days of the week on which Squeak is fun, select the following expression, and print it: > > > > > > Week dayNames! > > > > > > Item was changed: > > > + ----- Method: Week class>>dayNames (in category 'inquiries') ----- > > > - ----- Method: Week class>>dayNames (in category 'squeak protocol') ----- > > > dayNames > > > > > > + ^ #(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)! > > > - ^ DayNames! > > > > > > Item was changed: > > > ----- Method: Week class>>indexOfDay: (in category 'squeak protocol') ----- > > > indexOfDay: aSymbol > > > > > > + ^ self dayNames indexOf: aSymbol! > > > - ^ DayNames indexOf: aSymbol! > > > > > > Item was changed: > > > ----- Method: Week class>>nameOfDay: (in category 'smalltalk-80') ----- > > > nameOfDay: anIndex > > > > > > + ^ self dayNames at: anIndex! > > > - ^ DayNames at: anIndex! > > > > > > Item was changed: > > > ----- Method: Week class>>startDay (in category 'squeak protocol') ----- > > > startDay > > > > > > ^ StartDay ifNil: [ StartDay > > > + := self dayNames first ] > > > - := DayNames first ] > > > ! > > > > > > Item was changed: > > > ----- Method: Week class>>startDay: (in category 'squeak protocol') ----- > > > startDay: aSymbol > > > > > > + (self dayNames includes: aSymbol) > > > - (DayNames includes: aSymbol) > > > ifTrue: [ StartDay := aSymbol ] > > > ifFalse: [ self error: aSymbol, ' is not a recognised day name' ]! > > > > > > Item was changed: > > > ----- Method: Week class>>starting:duration: (in category 'squeak protocol') ----- > > > starting: aDateAndTime duration: aDuration > > > "Override - the duration is always one week. > > > Week will start from the Week class>>startDay" > > > > > > | midnight delta adjusted | > > > midnight := aDateAndTime asDateAndTime midnight. > > > + delta := ((midnight dayOfWeek + 7 - (self dayNames indexOf: self startDay)) rem: 7) abs. > > > - delta := ((midnight dayOfWeek + 7 - (DayNames indexOf: self startDay)) rem: 7) abs. > > > adjusted := midnight - (Duration days: delta seconds: 0). > > > > > > ^ super starting: adjusted duration: (Duration weeks: 1)! > > > > > > > > > From lewis at mail.msen.com Sun Sep 6 22:08:40 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 6 Sep 2020 18:08:40 -0400 Subject: [squeak-dev] Time>addSeconds: ignores nanos ivar value In-Reply-To: References: Message-ID: <20200906220840.GA59299@shell.msen.com> On Mon, Jul 20, 2020 at 01:08:57PM -0700, tim Rowledge wrote: > Time >addSeconds: completely ignores the nanos ivar value and so > Time now -> (say) 12:51:47.418205 pm > send that #addSeconds: 2 -> 12:51:49 pm > > Which is incorrect. Actually *fixing* it seems to be a bit more complex > than just "add the nanoseconds stuff". > > Should #asSeconds be truncating the seconds and ignoring the nanoseconds? > That's also an issue for #addTime: and #subtractTime: (wait, no #subtractSeconds: ?) > > Chronology-afficonados assemble! > I'm a bit late following up on this, but I'm working on some unit tests and a fix to be submitted to inbox soon. I took a look at Squeak 3.6 (prior to the introduction of the Chronology package). I was expecting that Time instances had originally assumed the use of whole seconds, but to my pleasant surprise, it is not so. Here is what I see in Squeak 3.6, which looks entirely right to me (note that I am intentionally using a completely inappropriate Float value to illustrate the point): t1 := Time fromSeconds: Float pi. "==> 12:00:03.141592653589793 am" t2 := t1 addSeconds: Float pi. "==> 12:00:06.283185307179586 am" t1 seconds. "==> 3.141592653589793" t1 asSeconds. "==> 3.141592653589793" t2 seconds. "==> 6.283185307179586" t2 asSeconds. "==> .283185307179586" t2 asSeconds - t1 asSeconds. "==> 3.141592653589793" And here is what I see in trunk today, which is clearly broken: t1 := Time fromSeconds: Float pi. "==> 12:00:03.141592653 am" t2 := t1 addSeconds: Float pi. "==> 12:00:06.141592654 am" t1 seconds. "==> 3" t1 nanoSecond. "==> 141592653" t1 asSeconds. "==> 3" t1 asNanoSeconds. "==> 3141592653" t2 seconds. "==> 6" t2 nanoSecond. "==> 141592654" t2 asSeconds. "==> 6" t2 asNanoSeconds. "==> 6141592654" t2 asSeconds - t1 asSeconds. "==> 3" t2 asNanoSeconds - t1 asNanoSeconds. "==> 3000000001" By fixing #addSeconds to respect the nanos ivar, we will have something more like this: t1 := Time fromSeconds: Float pi. "==> 12:00:03.141592653 am" t2 := t1 addSeconds: Float pi. "==> 12:00:06.283185307 am" t1 seconds. "==> 3" t1 nanoSecond. "==> 141592653" t1 asSeconds. "==> 3" t1 asNanoSeconds. "==> 3141592653" t2 seconds. "==> 6" t2 nanoSecond. "==> 283185307" t2 asSeconds. "==> 6" t2 asNanoSeconds. "==> 6283185307" t2 asSeconds - t1 asSeconds. "==> 3" t2 asNanoSeconds - t1 asNanoSeconds. "==> 3141592654" That will still leave us with inconsistent behavior with respect to #asSeconds, but that is probably a separate topic, and best checked against the behavior of other Smalltalk dialects for compatibility. Dave From eliot.miranda at gmail.com Mon Sep 7 00:00:43 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 6 Sep 2020 17:00:43 -0700 Subject: [squeak-dev] Recent Monticello Timestamps Message-ID: Name: VMMaker.oscog-eem.2800 Author: eem Time: 6 September 2020, 4:58:*47.598839* pm UUID: a6116113-df13-435d-968d-e9b111676754 Ancestors: VMMaker.oscog-eem.2799 Seriously ?!??! ;-) _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Mon Sep 7 01:56:19 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Mon, 7 Sep 2020 03:56:19 +0200 (CEST) Subject: [squeak-dev] Recent Monticello Timestamps In-Reply-To: References: Message-ID: Hi Eliot, The Monticello history of the Chronology package says that those microseconds were introduced by Kernel-eem.970: > Changes to Time and Delay prior to changing over to the utc microsecond clock and its use in delay scheduling. The "offending" change is probably the following: Item was changed: ----- Method: Time class>>now (in category 'ansi protocol') ----- now "Answer a Time representing the time right now - this is a 24 hour clock." + | localUsecs localUsecsToday | + localUsecs := self localMicrosecondClock. + localUsecsToday := localUsecs \\ MicrosecondsInDay. + ^ self + seconds: localUsecsToday // 1000000 + nanoSeconds: localUsecsToday \\ 1000000 * 1000! - - | ms | - - ms := self milliSecondsSinceMidnight. - - ^ self seconds: (ms // 1000) nanoSeconds: (ms \\ 1000) * 1000000 - - - ! And that happened more than four and a half years ago. Levente On Sun, 6 Sep 2020, Eliot Miranda wrote: > Name: VMMaker.oscog-eem.2800 > Author: eem > Time: 6 September 2020, 4:58:47.598839 pm > UUID: a6116113-df13-435d-968d-e9b111676754 > Ancestors: VMMaker.oscog-eem.2799 > > Seriously ?!??! ;-) > _,,,^..^,,,_ > best, Eliot > > From commits at source.squeak.org Mon Sep 7 23:48:10 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 7 Sep 2020 23:48:10 0000 Subject: [squeak-dev] The Inbox: Monticello-dtl.727.mcz Message-ID: A new version of Monticello was added to project The Inbox: http://source.squeak.org/inbox/Monticello-dtl.727.mcz ==================== Summary ==================== Name: Monticello-dtl.727 Author: dtl Time: 7 September 2020, 7:48:09.973319 pm UUID: 88de6167-2724-45e3-ae7a-c0e692903f98 Ancestors: Monticello-cmm.726 For MC commit notices, truncate time stamps to whole seconds when printing summaryHeader =============== Diff against Monticello-cmm.726 =============== Item was changed: ----- Method: MCVersionInfo>>summaryHeader (in category 'accessing') ----- summaryHeader ^ String streamContents: [:s | s nextPutAll: 'Name: '; nextPutAll: self name; cr. date ifNotNil: [s nextPutAll: 'Author: '; nextPutAll: author; cr; + nextPutAll: 'Time: '; nextPutAll: date asString, ', '; + nextPutAll: (time ifNotNil: [ :tm | Time fromSeconds: tm asSeconds asInteger]) asString; cr]. - nextPutAll: 'Time: '; nextPutAll: date asString, ', ', time asString; cr]. id ifNotNil: [s nextPutAll: 'UUID: '; nextPutAll: id asString; cr]. s nextPutAll: 'Ancestors: '; nextPutAll: self ancestorString. self stepChildren isEmpty ifFalse: [s cr; nextPutAll: 'Backported From: '; nextPutAll: self stepChildrenString]. ]! From lewis at mail.msen.com Mon Sep 7 23:52:02 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 7 Sep 2020 19:52:02 -0400 Subject: [squeak-dev] Recent Monticello Timestamps In-Reply-To: References: Message-ID: <20200907235202.GA8757@shell.msen.com> A crude but effective fix is in the inbox in Monticello-dtl.727 Dave On Mon, Sep 07, 2020 at 03:56:19AM +0200, Levente Uzonyi wrote: > Hi Eliot, > > > The Monticello history of the Chronology package says that > those microseconds were introduced by Kernel-eem.970: > > >Changes to Time and Delay prior to changing over to the utc microsecond > >clock and its use in delay scheduling. > > The "offending" change is probably the following: > > Item was changed: > ----- Method: Time class>>now (in category 'ansi protocol') ----- > now > "Answer a Time representing the time right now - this is a 24 hour > clock." > + | localUsecs localUsecsToday | > + localUsecs := self localMicrosecondClock. > + localUsecsToday := localUsecs \\ MicrosecondsInDay. > + ^ self > + seconds: localUsecsToday // 1000000 > + nanoSeconds: localUsecsToday \\ 1000000 * 1000! > - > - | ms | > - > - ms := self milliSecondsSinceMidnight. > - > - ^ self seconds: (ms // 1000) nanoSeconds: (ms \\ 1000) * 1000000 > - > - > - ! > > And that happened more than four and a half years ago. > > > Levente > > On Sun, 6 Sep 2020, Eliot Miranda wrote: > > >Name: VMMaker.oscog-eem.2800 > >Author: eem > >Time: 6 September 2020, 4:58:47.598839 pm > >UUID: a6116113-df13-435d-968d-e9b111676754 > >Ancestors: VMMaker.oscog-eem.2799 > > > >Seriously ?!??! ;-) > >_,,,^..^,,,_ > >best,??Eliot > > > > > From lewis at mail.msen.com Mon Sep 7 23:59:00 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 7 Sep 2020 19:59:00 -0400 Subject: [squeak-dev] Recent Monticello Timestamps In-Reply-To: <20200907235202.GA8757@shell.msen.com> References: <20200907235202.GA8757@shell.msen.com> Message-ID: <20200907235900.GA10645@shell.msen.com> Well, maybe not so effective after all, given that the commit notice for Monticello-dtl.727 on the mailing list still exhibits the same problem. Dave On Mon, Sep 07, 2020 at 07:52:02PM -0400, David T. Lewis wrote: > A crude but effective fix is in the inbox in Monticello-dtl.727 > > Dave > > On Mon, Sep 07, 2020 at 03:56:19AM +0200, Levente Uzonyi wrote: > > Hi Eliot, > > > > > > The Monticello history of the Chronology package says that > > those microseconds were introduced by Kernel-eem.970: > > > > >Changes to Time and Delay prior to changing over to the utc microsecond > > >clock and its use in delay scheduling. > > > > The "offending" change is probably the following: > > > > Item was changed: > > ----- Method: Time class>>now (in category 'ansi protocol') ----- > > now > > "Answer a Time representing the time right now - this is a 24 hour > > clock." > > + | localUsecs localUsecsToday | > > + localUsecs := self localMicrosecondClock. > > + localUsecsToday := localUsecs \\ MicrosecondsInDay. > > + ^ self > > + seconds: localUsecsToday // 1000000 > > + nanoSeconds: localUsecsToday \\ 1000000 * 1000! > > - > > - | ms | > > - > > - ms := self milliSecondsSinceMidnight. > > - > > - ^ self seconds: (ms // 1000) nanoSeconds: (ms \\ 1000) * 1000000 > > - > > - > > - ! > > > > And that happened more than four and a half years ago. > > > > > > Levente > > > > On Sun, 6 Sep 2020, Eliot Miranda wrote: > > > > >Name: VMMaker.oscog-eem.2800 > > >Author: eem > > >Time: 6 September 2020, 4:58:47.598839 pm > > >UUID: a6116113-df13-435d-968d-e9b111676754 > > >Ancestors: VMMaker.oscog-eem.2799 > > > > > >Seriously ?!??! ;-) > > >_,,,^..^,,,_ > > >best,??Eliot > > > > > > > > > > > From asqueaker at gmail.com Tue Sep 8 01:57:09 2020 From: asqueaker at gmail.com (Chris Muller) Date: Mon, 7 Sep 2020 20:57:09 -0500 Subject: [squeak-dev] Time>addSeconds: ignores nanos ivar value In-Reply-To: <20200906220840.GA59299@shell.msen.com> References: <20200906220840.GA59299@shell.msen.com> Message-ID: Out of the dozen or so lines of code listed, #asSeconds it looks like the only one that's broken, it should be the number of seconds since Time's epoch. It must've been broken recently, it's still correct in 5.3. The other accessors without "as", like #seconds, are supposed to be the "clock readout" of those values, and _should_ never return a fractional value. Nothing in Chronology was ever intended to use or return fractional (e.g., Float or Fraction) values, only integers. If we still have #exactSeconds, we should probably delete it and introduce #fractionalSeconds if you feel you want that. Subseconds access was intended to be accessed via the explicit accessors (#nanoSecond and #asNanoSeconds). Introducing a new separate mechanism for precision that utilizes Fractions sounds like it would subvert the efficiency of your UTC upgrade, and be confusing to have in conjunction with the API that already provides access to the subsecond values. Chronology needs to be fast and efficient. To me, Tim's original example of #addSeconds: dropping the nanos seems like the only other bug, so far. - Chris On Sun, Sep 6, 2020 at 5:08 PM David T. Lewis wrote: > > On Mon, Jul 20, 2020 at 01:08:57PM -0700, tim Rowledge wrote: > > Time >addSeconds: completely ignores the nanos ivar value and so > > Time now -> (say) 12:51:47.418205 pm > > send that #addSeconds: 2 -> 12:51:49 pm > > > > Which is incorrect. Actually *fixing* it seems to be a bit more complex > > than just "add the nanoseconds stuff". > > > > Should #asSeconds be truncating the seconds and ignoring the nanoseconds? > > That's also an issue for #addTime: and #subtractTime: (wait, no #subtractSeconds: ?) > > > > Chronology-afficonados assemble! > > > > I'm a bit late following up on this, but I'm working on some unit tests > and a fix to be submitted to inbox soon. > > I took a look at Squeak 3.6 (prior to the introduction of the Chronology > package). I was expecting that Time instances had originally assumed the > use of whole seconds, but to my pleasant surprise, it is not so. > > Here is what I see in Squeak 3.6, which looks entirely right to me (note > that I am intentionally using a completely inappropriate Float value to > illustrate the point): > > t1 := Time fromSeconds: Float pi. "==> 12:00:03.141592653589793 am" > t2 := t1 addSeconds: Float pi. "==> 12:00:06.283185307179586 am" > t1 seconds. "==> 3.141592653589793" > t1 asSeconds. "==> 3.141592653589793" > t2 seconds. "==> 6.283185307179586" > t2 asSeconds. "==> .283185307179586" > t2 asSeconds - t1 asSeconds. "==> 3.141592653589793" > > And here is what I see in trunk today, which is clearly broken: > > t1 := Time fromSeconds: Float pi. "==> 12:00:03.141592653 am" > t2 := t1 addSeconds: Float pi. "==> 12:00:06.141592654 am" > t1 seconds. "==> 3" > t1 nanoSecond. "==> 141592653" > t1 asSeconds. "==> 3" > t1 asNanoSeconds. "==> 3141592653" > t2 seconds. "==> 6" > t2 nanoSecond. "==> 141592654" > t2 asSeconds. "==> 6" > t2 asNanoSeconds. "==> 6141592654" > t2 asSeconds - t1 asSeconds. "==> 3" > t2 asNanoSeconds - t1 asNanoSeconds. "==> 3000000001" > > By fixing #addSeconds to respect the nanos ivar, we will have > something more like this: > > t1 := Time fromSeconds: Float pi. "==> 12:00:03.141592653 am" > t2 := t1 addSeconds: Float pi. "==> 12:00:06.283185307 am" > t1 seconds. "==> 3" > t1 nanoSecond. "==> 141592653" > t1 asSeconds. "==> 3" > t1 asNanoSeconds. "==> 3141592653" > t2 seconds. "==> 6" > t2 nanoSecond. "==> 283185307" > t2 asSeconds. "==> 6" > t2 asNanoSeconds. "==> 6283185307" > t2 asSeconds - t1 asSeconds. "==> 3" > t2 asNanoSeconds - t1 asNanoSeconds. "==> 3141592654" > > That will still leave us with inconsistent behavior with respect to > #asSeconds, but that is probably a separate topic, and best checked > against the behavior of other Smalltalk dialects for compatibility. > > Dave > > From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 05:57:21 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 05:57:21 +0000 Subject: [squeak-dev] Recent Monticello Timestamps In-Reply-To: <20200907235900.GA10645@shell.msen.com> References: <20200907235202.GA8757@shell.msen.com>, <20200907235900.GA10645@shell.msen.com> Message-ID: > Well, maybe not so effective after all, given that the commit notice for Monticello-dtl.727 on the mailing list still exhibits the same problem. Isn't that notice generated by some servers that depend on a Trunk image rather than the uploaded inbox version itself? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Dienstag, 8. September 2020 01:59:00 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Recent Monticello Timestamps Well, maybe not so effective after all, given that the commit notice for Monticello-dtl.727 on the mailing list still exhibits the same problem. Dave On Mon, Sep 07, 2020 at 07:52:02PM -0400, David T. Lewis wrote: > A crude but effective fix is in the inbox in Monticello-dtl.727 > > Dave > > On Mon, Sep 07, 2020 at 03:56:19AM +0200, Levente Uzonyi wrote: > > Hi Eliot, > > > > > > The Monticello history of the Chronology package says that > > those microseconds were introduced by Kernel-eem.970: > > > > >Changes to Time and Delay prior to changing over to the utc microsecond > > >clock and its use in delay scheduling. > > > > The "offending" change is probably the following: > > > > Item was changed: > > ----- Method: Time class>>now (in category 'ansi protocol') ----- > > now > > "Answer a Time representing the time right now - this is a 24 hour > > clock." > > + | localUsecs localUsecsToday | > > + localUsecs := self localMicrosecondClock. > > + localUsecsToday := localUsecs \\ MicrosecondsInDay. > > + ^ self > > + seconds: localUsecsToday // 1000000 > > + nanoSeconds: localUsecsToday \\ 1000000 * 1000! > > - > > - | ms | > > - > > - ms := self milliSecondsSinceMidnight. > > - > > - ^ self seconds: (ms // 1000) nanoSeconds: (ms \\ 1000) * 1000000 > > - > > - > > - ! > > > > And that happened more than four and a half years ago. > > > > > > Levente > > > > On Sun, 6 Sep 2020, Eliot Miranda wrote: > > > > >Name: VMMaker.oscog-eem.2800 > > >Author: eem > > >Time: 6 September 2020, 4:58:47.598839 pm > > >UUID: a6116113-df13-435d-968d-e9b111676754 > > >Ancestors: VMMaker.oscog-eem.2799 > > > > > >Seriously ?!??! ;-) > > >_,,,^..^,,,_ > > >best,??Eliot > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 06:09:44 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 06:09:44 +0000 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de> <8A5F154E-0BA8-4D64-8D1C-E844CCB85690@gmx.de> <4cad345e0ba544d7816544ff864fb74d@student.hpi.uni-potsdam.de> <5662556D-AA81-46B1-850B-CD6E89BCAF42@gmx.de>, Message-ID: Hi all, Hi Tobias, does the attached changeset match your expectations? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 3. September 2020 15:56:49 An: squeak-dev Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related Plus, TextFontReference is a rather fixed pointer to a specific font and size, which breaks when using Squeak's notion of High-DPI with bigger fonts in general. TextFontChange works better with its index. And so does TextEmphasis for italic. Best, Marcel Am 03.09.2020 15:53:35 schrieb Tobias Pape : Hi > On 03.09.2020, at 15:04, Thiede, Christoph wrote: > > Hi Tobias, > > > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? > > That was exactly my point. 'foo' asText beAllFont: font1; beAllFont: font2 does not make any sense, of course, but when a text is passed from somewhere else, you may not know which formatting has already been applied to it ... That is true. But if you want to change the font, there's only so much you can do. If you want just change an upright font to an italic one, the FontReference will not work in any case. The TextFontChange attribute is seemingly intended for that, but it only understands "Font array indices" which is most unhelpful :D Best regards -Tobias > > Best, > Christoph > > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Donnerstag, 3. September 2020 00:13:24 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > On 02.09.2020, at 23:29, Thiede, Christoph wrote: > > > > Hi Tobias, thanks for the tips! :-) > > > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > > Would you expect such a Text>>#setFont:from:to: only to add a TextFontReference or also to remove all existing TextFontReferences from the interval? > > > > I'd be wary of using #set... because exactly that expectation happens. > Vocabulary is cumbersome, tho. > see TextMorph's _be_AllFont, Text's _make_Bold and the attributes only ever add. > > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? > Coalescing the runs would probably take care of chopping up the different font runs, but that's beneath the cover. > > That said, I liked the Seaside/Magritte-wording with beSomething on their brushes or models respectively… > > Best regards > -Tobias > > > Best, > > Christoph > > Von: Squeak-dev im Auftrag von Tobias Pape > > Gesendet: Mittwoch, 2. September 2020 22:04:38 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > Hi > > > > > On 02.09.2020, at 21:14, Thiede, Christoph wrote: > > > > > > Hi Eric, > > > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! Seehttp://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html andhttps://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) > > > > > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? > > > > > > These are good questions others can probably answer better than I could do. > > > This is all I can tell you: > > > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. > > > • To change the font in a Text, use something like: > > > 'foo' asText > > > addAttribute: (TextFontReference toFont: (StrikeFont > > > familyName: 'Darkmap DejaVu Sans' > > > pointSize: 20)); > > > openAsMorph > > > > I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: > > > > 'foo' asText > > addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); > > openAsMorph > > > > > > If you are just after changing the font used in a TextMorph (not the text itself), I'd use > > > > 'foo' asTextMorph > > beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); > > openInHand > > > > > > That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool > > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. > > > > > > Best regards > > -Tobias > > > > > > > > > > > Also -- how can I disable editing of a TextMorph? > > > > > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. > > > > > > Best, > > > Christoph > > > Von: Squeak-dev im Auftrag von Eric Gade > > > Gesendet: Mittwoch, 2. September 2020 17:01:45 > > > An: The general-purpose Squeak developers list > > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > > > Hi Christoph, > > > > > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: > > > > > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. > > > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? > > > > > > > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text > > > > > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? > > > > > > Thanks again -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: Text-emphasis.1.cs URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 06:16:42 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 06:16:42 +0000 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: , Message-ID: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de> Hi Karl, I'm sorry your proposal is lost a bit - I think it's a nice addition! It does not look exactly like the TextMorph dialog, but I actually prefer "one dialog that fits it all" over the three separate handles for a TextMorph (that do not even all appear to work properly at the moment.) I think we should get this merged! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Donnerstag, 3. September 2020 13:28:41 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related Here is a change set that adds HaloHandle for font change to StringMorph Best, Karl On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel > wrote: Hi Eric. > What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? Unfortunately, the current inspector framework has no such extension point. You can, however, prepare a custom inspector for your domain objects. See MorphInspector as an example. Best, Marcel Am 02.09.2020 00:10:56 schrieb Eric Gade >: Hi all, What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? For my RISCV work, I'm looking to show a morphic representation of the bitfields when a given instruction's `self` property is highlighted in the Inspector window. As a related question, what's the best way to make a composed Morph that has StringMorphs of different fonts, sizes, and alignments? It seems like not all Fonts are available to StringMorph (TrueType ones, for example) -- is that correct? Thanks! -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 06:31:15 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 06:31:15 +0000 Subject: [squeak-dev] Unicode In-Reply-To: <8DCD60E5-9959-4B5D-9C84-D0AC59405A4C@gmx.de> References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> , <8DCD60E5-9959-4B5D-9C84-D0AC59405A4C@gmx.de> Message-ID: <37fa8427b38e40fc8c4a2cc61027b2bd@student.hpi.uni-potsdam.de> Hi all, > Your words suggest that it has already been published, but I can't find it anywhere. Then I must have expressed myself wrong. I did not yet publish any code changes, but in my original post from March, you can find a short description of the design changes I'd like to implement. Essentially, I would like to replace the separate class variables for every known character class in favor of greater flexibility. Eliot, are there any remaining questions regarding the VM size? Character size should be sufficient as discussed below, and of course, I can test any changes in a 32-bit image, too. :-) WideString withAll: (#(42r2l 16r1F497 16r1F388 33) collect: #asCharacter) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Sonntag, 6. September 2020 21:00:14 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Unicode > On 06.09.2020, at 20:40, Levente Uzonyi wrote: > > On Sun, 6 Sep 2020, Tobias Pape wrote: > >> >>> On 06.09.2020, at 19:15, Eliot Miranda wrote: >>> Hi Christoph, Hi All, >>>> On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: >>>> Hi all! :-) >>>> After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, but so long, I have one general question for you: >>> And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values). In the 32-bit variant Characters are 30-bit unsigned integers. In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. >>> Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant? It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure that initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. >>> Q2, how many bits should the 64-bit variant VM support for immediate Characters? >> >> Unicode has a max value of 0x10FFFF. That makes 21 bit. So no worries there. >> >> We should just not forget the leading-char stuff (Yoshiki, Andreas,...) > > AFAIU the leading char only makes sense when you have multiple CJK(V?) languages in use at the same time. In other cases Unicode (leadingChar = 0) is perfectly fine. > IIRC there are 22 bits available for the codePoint and 8 for the leadingChar, so we're still good: all unicode characters fit. > > \o/ hooray! > Levente > >> >> >> BEst regards >> -Tobias >> >>> Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to LargePositiveInteger beyond SmallInteger maxVal. This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. >>> It has implications in a few parts of the system: >>> - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances >>> - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation >>> - 32-bit <=> 64-bit image conversion All this is easily doable (because we have models of doing it for Float and Integer general instances). But we need good specifications so we can implement the right thing from the get-go. >>>> At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. >>>> Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't know whether this will ever happen). >>>> Examples: >>>> Unicode generalTagOf: $a asUnicode. "#Ll" >>>> Unicode class >> isLetterCode: charCode >>>> ^ (self generalTagOf: charCode) first = $L >>>> Unicode class >> isAlphaNumericCode: charCode >>>> | tag| >>>> ^ (tag := self generalCategoryOf: charCode) first = $L >>>> or: [tag = #Nd] >>>> How do you think about this proposal? Please let me know and I will go ahead! :D >>>> Best, >>>> Christoph >>> Best, Eliot >>> _,,,^..^,,,_ (phone) -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 06:37:24 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 06:37:24 +0000 Subject: [squeak-dev] Are we missing some string encoding in SqueakSSL/WebClient? In-Reply-To: References: <151190a158c9417aa22b3ac64e263ae0@student.hpi.uni-potsdam.de>, Message-ID: Hi Levente, unfortunately, I cannot see what component are you referring to: WebUtils or SqueakSSL? However, I recently found out myself that it is not a solution to change the encoding of the entire post stream, it breaks other things, for example, base64-encoded data. So we probably want some kind of encoding/decoding logic in the WebUtils part. I will upload a relevant inbox version as soon as possible. :-) How can we be sure not to break any existing implementations (backward compatibility)? In a nutshell, I think we can't (without doing any kind of error-prone "encoding guess"), but the next release will be a new major version, so I think this will allow us to make a breaking change? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Sonntag, 6. September 2020 18:22:54 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Are we missing some string encoding in SqueakSSL/WebClient? Hi Christoph, On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > while doing some experiments with WebClient & WebUtils in order to send textual data to a server via HTTP(S), I found out that posting a request containing non-ASCII characters in a multipart/form-data yields a primitive > failure from SqueakSSL/primitiveEncrypt (occurs on both Win32 + emulated Linux/Ubuntu). When I converted the text manually using #squeakToUtf8 before putting it into the contents data, everything worked fine and the server > receives the correct text without any encoding problems. > > > So for my application, I can manually #squeakToUtf8-convert the request string before posting it, but I wonder whether this should really be the responsibility of every Squeak developer and not rather one of the WebClient? > For illustration: If you use normal #htmlSubmit:fields: instead of #httpPost:multipartFields:, WebClient does all necessary conversion (#encodeUrlEncodedForm:, String >> #encodeForHTTP) itself. Anyway, > #encodeMultipartForm:boundary: does not care about conversion. > > > I am only a bloody newbie to all this web stuff, so maybe I am missing something important. What do you think? Should we add #squeakToUtf8 conversion in WebUtils class >> #encodeMultipartForm:boundary:, and in the decode > message vice versa? Or would this rather be a responsibility of the SqueakSSL protocol? I am looking forward to your help! Encoding is definitely missing there. multipart/form-data is a mess [1] though, so we should be careful to generate what presumably everything supports but be able to parse what other sources can generate. Levente [1] https://tools.ietf.org/html/rfc7578 > > > Best, > > Christoph > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 8 08:09:05 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 8 Sep 2020 08:09:05 0000 Subject: [squeak-dev] The Inbox: WebClient-Core-ct.125.mcz Message-ID: Christoph Thiede uploaded a new version of WebClient-Core to project The Inbox: http://source.squeak.org/inbox/WebClient-Core-ct.125.mcz ==================== Summary ==================== Name: WebClient-Core-ct.125 Author: ct Time: 8 September 2020, 10:09:01.217336 am UUID: b5a5321e-904d-ca40-aa6a-8ae3236fe629 Ancestors: WebClient-Core-ct.124 Adds UTF-8 conversion to multipart/form-data encoding & decoding in WebUtils. Also includes overall refactoring of the relevant methods. Last but not least, makes the filename parameter optional as specified in RFC7578 Sec. 4.2. NOTE that this is a breaking change for any users of the multipart/form-data protocol provided by the WebClient package! If you manually converted (file)names or plain-text fields to UTF-8 via #squeakToUtf8 before passing them to the WebClient, you should stop doing so now. Vice versa, as a user of WebRequest >> #fields or #multipartFields, you should no longer call #utf8ToSqueak on the output. For more information, please refer to [2]. Thanks to Levente (ul) for the help! Depends indeed on WebClient-Core-ct.124. [1] https://tools.ietf.org/html/rfc7578#section-4.2 [2] http://forum.world.st/Are-we-missing-some-string-encoding-in-SqueakSSL-WebClient-td5121341.html =============== Diff against WebClient-Core-ct.124 =============== Item was changed: ----- Method: WebUtils class>>decodeMultipartForm:boundary:do: (in category 'decoding') ----- decodeMultipartForm: aStream boundary: boundary do: aBlock "Parse the contents of a multipart/form-data submission. + + Evaluate aBlock with three parts: The headers, the (parsed) form-data arguments and the (undecoded) contents of the part. The sender is expected to take care of other issues such as content-transfer-encoding and similar headers." - Evaluate aBlock with three parts: The headers, the (parsed) form-data - arguments and the (undecoded) contents of the part. The sender is - expected to take care of other issues such as content-transfer-encoding - and similar headers." - | skip headers content disposition index params | aStream upToAll: '--', boundary. + [aStream atEnd or: [(skip := aStream next: 2) = '--']] whileFalse: [ + self assert: skip = String crlf description: 'Error decoding multipart/form-data fields'. + + headers := Dictionary newFrom: (WebUtils readHeadersFrom: aStream). - [aStream atEnd or:[(skip := aStream next: 2) = '--']] whileFalse:[ - skip = String crlf ifFalse:[self error: 'Error decoding multipart/form-data fields']. - headers := Dictionary new. - (WebUtils readHeadersFrom: aStream) do:[:hdr| headers add: hdr]. - content := aStream upToAll: String crlf, '--', boundary. params := Dictionary new. + content := aStream upToAll: String crlf, '--', boundary. + + disposition := (headers at: 'content-disposition' ifAbsent: ['']) utf8ToSqueak. + self flag: #todo. "Support content-type and charset as described in RFC7578." + #(name filename) do: [:arg | + | len | - disposition := headers at: 'content-disposition' ifAbsent:['']. - #(name filename) do:[:arg| | len val | len := arg size + 2. + index := disposition findString: arg, '='. + index > 0 ifTrue: [ + params at: arg put: (disposition + copyFrom: index + len + to: (disposition indexOf: $" startingAt: index + len) - 1) utf8ToSqueak]]. + aBlock value: headers value: params value: content].! - index := disposition findString: arg,'='. - index > 0 ifTrue:[ - val := disposition copyFrom: index + len to: (disposition indexOf: $" startingAt: index+len) - 1. - params at: arg put: val. - ]. - ]. - aBlock value: headers value: params value: content. - ].! Item was changed: ----- Method: WebUtils class>>encodeMultipartForm:boundary: (in category 'decoding') ----- encodeMultipartForm: fieldMap boundary: boundary + "Encode the fieldMap as multipart/form-data. + + The fieldMap may contain MIMEDocument instances to indicate the presence of a file to upload to the server. If the MIMEDocument is present, its content type and file name will be used for the upload. + + The fieldMap can be EITHER an array of associations OR a Dictionary of key value pairs (the former is useful for providing multiple fields and/or specifying the order of fields)." - "Encodes the fieldMap as multipart/form-data. + ^ String streamContents: [:stream | + (fieldMap as: OrderedDictionary) keysAndValuesDo: [:fieldName :fieldValue | + | fieldContent | - The fieldMap may contain MIMEDocument instances to indicate the presence - of a file to upload to the server. If the MIMEDocument is present, its - content type and file name will be used for the upload. - - The fieldMap can be EITHER an array of associations OR a Dictionary of - key value pairs (the former is useful for providing multiple fields and/or - specifying the order of fields)." - - ^String streamContents:[:stream| - (fieldMap as: Dictionary) keysAndValuesDo:[:fieldName :fieldValue | | fieldContent | "Write multipart boundary and common headers" stream nextPutAll: '--', boundary; crlf. + stream nextPutAll: 'Content-Disposition: form-data; name="' + , fieldName squeakToUtf8 + , '"'. + self flag: #todo. "Support Content-Type and charset as described in RFC7578." + + "Write field content - either a file or a string" + fieldContent := (fieldValue isKindOf: MIMEDocument) + ifTrue: [ + fieldValue url ifNotNil: [:url | + stream nextPutAll: '; filename="', url pathForFile squeakToUtf8, '"']. + stream crlf. + stream + nextPutAll: 'Content-Type: '; + nextPutAll: fieldValue contentType squeakToUtf8. + (fieldValue content ifNil: [ + (FileStream readOnlyFileNamed: fieldValue url pathForFile) contentsOfEntireFile]) asString] + ifFalse: [fieldValue asString squeakToUtf8]. - stream nextPutAll: 'Content-Disposition: form-data; name="', fieldName, '"'. - "Figure out if this is a file upload" - (fieldValue isKindOf: MIMEDocument) ifTrue:[ - stream nextPutAll: '; filename="', fieldValue url pathForFile, '"'; crlf. - stream nextPutAll: 'Content-Type: ', fieldValue contentType. - fieldContent := (fieldValue content ifNil:[ - (FileStream readOnlyFileNamed: fieldValue url pathForFile) contentsOfEntireFile. - ]) asString. - ] ifFalse: [fieldContent := fieldValue]. stream crlf; crlf. + stream nextPutAll: fieldContent. + + stream crlf]. + stream nextPutAll: '--', boundary, '--', String crlf]! - stream nextPutAll: fieldContent asString. - stream crlf. - ]. - stream nextPutAll: '--', boundary, '--', String crlf. - ]. - ! From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 08:10:50 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 08:10:50 +0000 Subject: [squeak-dev] Are we missing some string encoding in SqueakSSL/WebClient? In-Reply-To: References: <151190a158c9417aa22b3ac64e263ae0@student.hpi.uni-potsdam.de>, , Message-ID: Hi Levente, hi all, please see WebClient-Core-ct.125. I am looking forward to your review! :-) In my image, WebClient-Tests keeps failing with 9 failures and 1 error, but I get the same results in a fresh Trunk image, so I guess there is no obvious regression. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Dienstag, 8. September 2020 08:37:24 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Are we missing some string encoding in SqueakSSL/WebClient? Hi Levente, unfortunately, I cannot see what component are you referring to: WebUtils or SqueakSSL? However, I recently found out myself that it is not a solution to change the encoding of the entire post stream, it breaks other things, for example, base64-encoded data. So we probably want some kind of encoding/decoding logic in the WebUtils part. I will upload a relevant inbox version as soon as possible. :-) How can we be sure not to break any existing implementations (backward compatibility)? In a nutshell, I think we can't (without doing any kind of error-prone "encoding guess"), but the next release will be a new major version, so I think this will allow us to make a breaking change? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Sonntag, 6. September 2020 18:22:54 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Are we missing some string encoding in SqueakSSL/WebClient? Hi Christoph, On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > while doing some experiments with WebClient & WebUtils in order to send textual data to a server via HTTP(S), I found out that posting a request containing non-ASCII characters in a multipart/form-data yields a primitive > failure from SqueakSSL/primitiveEncrypt (occurs on both Win32 + emulated Linux/Ubuntu). When I converted the text manually using #squeakToUtf8 before putting it into the contents data, everything worked fine and the server > receives the correct text without any encoding problems. > > > So for my application, I can manually #squeakToUtf8-convert the request string before posting it, but I wonder whether this should really be the responsibility of every Squeak developer and not rather one of the WebClient? > For illustration: If you use normal #htmlSubmit:fields: instead of #httpPost:multipartFields:, WebClient does all necessary conversion (#encodeUrlEncodedForm:, String >> #encodeForHTTP) itself. Anyway, > #encodeMultipartForm:boundary: does not care about conversion. > > > I am only a bloody newbie to all this web stuff, so maybe I am missing something important. What do you think? Should we add #squeakToUtf8 conversion in WebUtils class >> #encodeMultipartForm:boundary:, and in the decode > message vice versa? Or would this rather be a responsibility of the SqueakSSL protocol? I am looking forward to your help! Encoding is definitely missing there. multipart/form-data is a mess [1] though, so we should be careful to generate what presumably everything supports but be able to parse what other sources can generate. Levente [1] https://tools.ietf.org/html/rfc7578 > > > Best, > > Christoph > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 08:34:44 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 08:34:44 +0000 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de>, Message-ID: <432f7ec3796847cca1751c771dc2c98c@student.hpi.uni-potsdam.de> Hi Eliot, thanks for your answer! Just to be sure: What class do you intend to lookup #doesNotUnderstand: for if the message not understood's lookupClass is ProtoObject superclass (i.e. nil)? Are you thinking of ProtoObject again? This would break the existing rule "If a message is sent to super but not understood, send #doesNotUnderstand: to super as well" (*), or am I misinterpreting this? Shouldn't you rather signal some special selector in this case? (*) I'm not sure about this, but I think this results from the Bluebook, page 589. According to the implementation given there, the VM had to crash with a recursion error if a nil class is reached while looking up #doesNotUnderstand:. Because the Bluebook states that the existence of a #doesNotUnderstand: implementation is guaranteed, I would also find it reasonable to have the VM crashed if you actually make a call to super from a method compiled on ProtoObject. What do you think? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Sonntag, 6. September 2020 04:49:54 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) Hi Christophe, Hi Tobias, On Sat, Sep 5, 2020 at 4:10 AM Thiede, Christoph > wrote: Hi all, hi Eliot, just for curiosity, I have two new questions about the VM machinery (which are far away from praxis but close to my interests): 1. super on ProtoObject What would you expect the following to return? x := Compiler evaluate: 'super identityHash' for: ProtoObject new. thisContext objectClass: x. I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. My guess before looking at the code was that the send provoked a doesNotUnderstand: and hence the expression returned the result of raising a MessageNotUnderstood exception. But looking at the code I see that at least in the JIT there is undefined behavior in looking up a message in a nil class (the superclass of ProtoObject is nil). So thank you for this. I'll have to firm up the behaviour to ensure a doesNotUnderstand: is the result. But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. Which is a bug. The only reasonable thing to do here (IMO) is for the VM to send doesNotUnderstnnd:. If you debug the call instead (Context class >> #runSimulated:), Context >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense but unfortunately is not consistent with the original VM behavior. Maybe the Compiler should forbid any call to super from ProtoObject instances at all (more formally: if the receiver's class's superclass is nil)? Or should we adjust the simulation code? (By the way: If you do the same in Squeak.js, you'll get an infinite recursion :D) 2. Mirror primitives What is the reason for primitives such as 75 (#identityHash) or 78/139 (#nextInstance/#nextObject) not being mirrored in Context? Was this simply forgotten, or don't we have the claim to mirror any "essential" primitive without it actually being needed by the simulation machinery? They're not needed by the execution simulation machinery. As always, looking forward to your interesting explanations! :-) Best, Christoph Cheers _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 08:42:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 08:42:19 +0000 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de>, Message-ID: <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> Hi Levente, is there no active development happening on http://www.squeaksource.com/JSON any longer? Is your fork specialized for optimized code? I was rather trying to reuse existing functionality such as #nextHexDigit. Just for interest, how much faster did you get the code by optimizing my changes? ;-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Sonntag, 6. September 2020 02:13:56 An: The general-purpose Squeak developers list Cc: Niephaus, Fabio Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project Hi Christoph, Your code looks correct to me, though it's a bit too slow for my taste. I've fixed it in my fork of JSON[1] as well. Levente [1] http://squeaksource.com/PostgresV3/JSON-ul.55.mcz On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into the > repository? :-) > > > Best, > > Christoph > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 08:49:21 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 08:49:21 +0000 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: <,> , Message-ID: <00a5e68205c142d8898ad1924025a8ff@student.hpi.uni-potsdam.de> Hi all, Hi Marcel, > We should find a way to get rid of #headingAndAutoselectForLiteral:do:. That very name indicates that there is something wrong with the overall invocation of SystemNavigation's public protocol. Hm, I guess the simplest way would be splitting up #headingAndAutoselectForLiteral:do: into #headingForLiteral: and #stringForLiteral: or something like this. Looking at the latter, this should be equivalent to [literal name], which is returns a valid string each for Symbol, String, LookupKey, Integer, and Boolean, so we would only need to implemented the former. What do you think? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 3. September 2020 15:15:31 An: squeak-dev Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz Hi -- #headingAndAutoselectForLiteral:do: is a private helper function to disguise a rather constant value, which is a prefix for a tool's label. It should not be used outside SystemNavigation. That would make things even worse. The basic issue here is that there is no specific object for the concerns "user" or "sender". So, it is tediously derived or forwarded in a functional style. We should find a way to get rid of #headingAndAutoselectForLiteral:do:. That very name indicates that there is something wrong with the overall invocation of SystemNavigation's public protocol. Best, Marcel Am 03.09.2020 15:02:06 schrieb Thiede, Christoph : Hi Chris, thanks for your feedback! > For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I think this is very much a question of favor - personally, I prefer guard clauses over the functional style unless both code paths have the same relevance for the whole method. In my opinion, an empty message list is clearly a secondary edge case only. :-) > I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Yes, this is not really an intuitive message name ... What do you think about calling it #readLiteral:withHeadingAndStringDo: instead (plus making it public)? Best, Christoph ________________________________ Von: Chris Muller Gesendet: Mittwoch, 2. September 2020 23:59:46 An: squeak dev; Thiede, Christoph Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz Hi Christoph, For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Best, Chris On Wed, Sep 2, 2020 at 9:56 AM wrote: > > Christoph Thiede uploaded a new version of Tools to project The Inbox: > http://source.squeak.org/inbox/Tools-ct.986.mcz > > ==================== Summary ==================== > > Name: Tools-ct.986 > Author: ct > Time: 2 September 2020, 4:55:38.538083 pm > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca > Ancestors: Tools-ct.985 > > Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. > > =============== Diff against Tools-ct.985 =============== > > Item was changed: > ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- > addParentMethodsSending: selectorSymbol > > + ^ self systemNavigation > + headingAndAutoselectForLiteral: selectorSymbol > + do: [:label :autoSelect | > + | methodsList | > + methodsList := self systemNavigation allCallsOn: selectorSymbol. > + methodsList ifEmpty: [ > + ^ self inform: ('There are no {1}' translated format: {label})]. > + self > - | methodsList | > - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty > - ifTrue: > - [ ^(PopUpMenu labels: ' OK ') > - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] > - ifFalse: > - [ self > addParentMessages: methodsList > + autoSelectString: autoSelect] > - autoSelectString: selectorSymbol ] > ! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 08:58:44 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 08:58:44 +0000 Subject: [squeak-dev] Starting an image from SqueakConsole manipulates pwd In-Reply-To: <04633BB1-DB6A-4529-801D-B9114A268FEB@rowledge.org> References: <431f0386497e4a33814235a1be2d1234@student.hpi.uni-potsdam.de> <4566481ba9a94419b4631211db66b5dd@student.hpi.uni-potsdam.de> <2deb51e8-76b7-abd0-19a0-9ae99c5e8183@gmail.com> <13020CE2-9699-4C9B-B0CC-F19430EEDA05@gmx.de>, <04633BB1-DB6A-4529-801D-B9114A268FEB@rowledge.org> Message-ID: Hi all, (yep, I'm cleaning up my old inbox messages today. :D) What is the current state of this issue? IMO it is still an undesired behavior and if I understand it correctly, it should be fixed on the VM side. Will it be appropriate to open a ticket in the OSVM repo before we all forget this? ;-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von tim Rowledge Gesendet: Sonntag, 14. Juni 2020 17:38:45 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Starting an image from SqueakConsole manipulates pwd >> On 12.06.2020, at 20:03, K K Subbu wrote: >> >> On 12/06/20 7:24 pm, Thiede, Christoph wrote: >>> I just rechecked this under Ubuntu. Same issue here. Do you agree >>> that it is a bug that the pwd is changed when starting the VM? >> >> On all platforms, the default directory is the current directory, but FileDirectory>>startUp is setting the default directory to that of the image instead of current directory. The attached fix will cure the fault. I think we've established that this is not necessarily the case, certainly not for all platforms. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: IXM: Initiate X-rated error Messages -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 10:22:04 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 10:22:04 +0000 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de>, Message-ID: <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> Hi Marcel, hi all, phew, this turned out to be a bit more complicated than I had assumed. The attached script should patch every in the image required to upgrade the update URLs to HTTPS. Eventually, I would like to ship it via a postscript in a regular trunk version. Still, the server code must be updated, because unfortunately, it returns URLs with hard-coded HTTP, even if the request is made via HTTPS: ( name 'update-eem.477' repository ('http://source.squeak.org/trunk') dependency ('Squeak-Version' 'Squeak-Version-mt.5252' 'b9a3cd5b-b708-8646-a99f-5f3ae294ceb1') ... I don't have access to the server code, but apparently the protocol should be checked there. And isn't it a general convention to favor relative over absolute URLs? Why doesn't the server simply return '/trunk' here? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Freitag, 12. Juni 2020 16:17 Uhr An: squeak-dev Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS? I suppose it is because the update map has "http" in it for each package version. And maybe also because your repositories in Monticello are configured as "http". So, the mechanism does not find any previous versions? Seems like bug to me. Best, Marcel Am 12.06.2020 15:51:51 schrieb Thiede, Christoph : Hi all, I spent a number of attempts during the latest months on changing my image's update URL to use HTTPS, but unfortunately, it gets reset each time. Shouldn't I be able to simply change the update URL in the preferences by adding a small "s" behind the "http"? But if I do so, when I press the update button the next time, Squeak starts loading all packages from their initial version (Tools-xyz.1, Morphic-xyz.1, ...). Is there a way to easily change the URL? Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: patch-source.squeak.org-repositories.st Type: application/octet-stream Size: 1210 bytes Desc: patch-source.squeak.org-repositories.st URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 10:26:48 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 10:26:48 +0000 Subject: [squeak-dev] The Inbox: System-ct.1161.mcz In-Reply-To: References: , , Message-ID: <6f9d85690c964737bf1735c4e283177b@student.hpi.uni-potsdam.de> Hi Marcel, this issue is still up to date and I would like to make the discussed method more robust. Will you accept a patch that uses "error return: error defaultAction" to handle any errors occurring during the operation? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Montag, 8. Juni 2020 09:48:20 An: squeak-dev Betreff: Re: [squeak-dev] The Inbox: System-ct.1161.mcz Hi Marcel, should GetTextTranslator expose these errors anyway? At the moment, [GetTextTranslator availableLanguageLocaleIDs] ifError: [self error] would regularly fail. What about "error return: error defaultAction"? I know this is not identical, but it should have an equivalent effect in this situation. > But maybe we can find an even better way to collect errors during image startup and present them only after at least the GUI system was initialized, which happens through Project class >> #startUp: at the moment. Also take a look at ToolSet class >> #debugException:. Interesting idea! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 8. Juni 2020 08:40:26 An: squeak-dev Betreff: Re: [squeak-dev] The Inbox: System-ct.1161.mcz Hi Christoph, look at the senders of #resumeUnchecked:. ;-) This message is rather inappropriate to be used in GetTextTranslator. If you want to fix image startup, then I suggest to put such exception handlers in NaturalLanguageTranslator class >> #startUp:. But maybe we can find an even better way to collect errors during image startup and present them only after at least the GUI system was initialized, which happens through Project class >> #startUp: at the moment. Also take a look at ToolSet class >> #debugException:. Best, Marcel Am 06.06.2020 20:55:04 schrieb Jakob Reschke : Errors are not resumable, although these errors with defaultActions will resume with something if they do not signal another exception and *if they are not handled*. If you turned this into a Notification, you could resume it with another directory listing, but how much sense would that make? Am Sa., 6. Juni 2020 um 20:22 Uhr schrieb Thiede, Christoph : > > I wonder anyway why InvalidDirectoryError & Co. override #defaultAction ... > > If they are serious errors, they should show up a debugger via UndefinedError but not silently fix that anyhow by returning a default value, shouldn't they? > > And if they are no serious errors, they should be Notifications IMO. > > > What do you think? :-) > > > Best, > > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von commits at source.squeak.org > Gesendet: Samstag, 6. Juni 2020 20:19:27 > An: squeak-dev at lists.squeakfoundation.org > Betreff: [squeak-dev] The Inbox: System-ct.1161.mcz > > Christoph Thiede uploaded a new version of System to project The Inbox: > http://source.squeak.org/inbox/System-ct.1161.mcz > > ==================== Summary ==================== > > Name: System-ct.1161 > Author: ct > Time: 6 June 2020, 8:19:13.480392 pm > UUID: 173a90f3-4bb8-fc46-b5a6-6e4ab74e63ca > Ancestors: System-mt.1160 > > Catch all InvalidDirectoryErrors in GetTextTranslator availableLanguageLocaleIDs. Those exceptions are an implementation detail and may not confuse the caller. Such a confusion happened in connection with Morphic-ct.1638. > > =============== Diff against System-mt.1160 =============== > > Item was changed: > ----- Method: GetTextTranslator class>>availableLanguageLocaleIDs (in category 'accessing') ----- > availableLanguageLocaleIDs > "GetTextTranslator availableLanguageLocaleIDs" > | ids dirs localeDirForLang directoryNames | > ids := Set new. > dirs := Set new. > dirs addAll: LocaleDirsForDomain values. > dirs addAll: self defaultLocaleDirs. > + [dirs do: [:dir | > - dirs do: [:dir | > | localesDir | > localesDir := FileDirectory on: dir. > + directoryNames := localesDir directoryNames. > - directoryNames := [localesDir directoryNames] on: InvalidDirectoryError do: [:e | #()]. > directoryNames > do: [:langDirName | > | localeID | > localeID := LocaleID posixName: langDirName. > localeDirForLang := localesDir directoryNamed: (self langDirNameForLocaleID: localeID). > localeDirForLang ifNotNil: [ > (localeDirForLang fileNamesMatching: '*.mo') ifNotEmpty: [ids add: localeID]]. > localeID hasParent ifTrue: [ > localeDirForLang := localesDir directoryNamed: (self langDirNameForLocaleID: localeID parent). > localeDirForLang ifNotNil: [ > (localeDirForLang fileNamesMatching: '*.mo') ifNotEmpty: [ids add: localeID parent]]]. > ]. > + ]] on: InvalidDirectoryError do: [:error | error resumeUnchecked: error defaultAction]. > - ]. > ^ids! > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 10:43:31 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 10:43:31 +0000 Subject: [squeak-dev] TextMorphs support display of text with emphasis. They also support reasonable text-editing capabilities, as well as embedded HOT LINKS In-Reply-To: <340526677f8f4b98940def83daafcb69@student.hpi.uni-potsdam.de> References: <1723c58e69a.1299d284031193.9038219790930242543@zoho.com> <1724c4cf2cb.cade0b8a61452.8974473611806138340@zoho.com> , , <340526677f8f4b98940def83daafcb69@student.hpi.uni-potsdam.de> Message-ID: <82fa5064df404b3999aa82365ac8a294@student.hpi.uni-potsdam.de> Hi all, please have a look at the attached changeset. As proposed earlier, it allows invoking the "change emphasis" dialog via a menu, without requiring the user to be aware of any magic shortcuts. Shortcuts are convenient, of course, but a menu item supports exploration for new users. [cid:1c4c8432-d60d-4c4b-b82f-494e35476a32] Please merge or discuss! :D (PS: Am I the only one to find this connection between model and morph to be a bit smelling?) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Freitag, 5. Juni 2020 13:19:09 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] TextMorphs support display of text with emphasis. They also support reasonable text-editing capabilities, as well as embedded HOT LINKS Hi all, I think Karl made an important observation here: You cannot invoke the #changeEmphasis: dialog via mouse. This is a bit pity because in general, Squeak emphasizes (that was flat) intuitive UIs whereas keyboard shortcuts cannot be learned without propaganda or reading the documentation. I think all functionality should be available via mouse, too. How would you think about inserting a link to #changeEmphasis: into the TextMorph menu, just below (or above) "Set font..."? Best, Christoph PS: Thanks, Tim, cacao is in the air :D ________________________________ Von: Squeak-dev im Auftrag von tim Rowledge Gesendet: Dienstag, 26. Mai 2020 18:57:38 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] TextMorphs support display of text with emphasis. They also support reasonable text-editing capabilities, as well as embedded HOT LINKS > On 2020-05-26, at 5:04 AM, Marcel Taeumel wrote: > > I've never noticed that 'debug invocation' thing before. I *love* it. May the Great Chocolate Fairy reward the creator. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: XYZZY: Branch and Play Adventure -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 24408 bytes Desc: pastedImage.png URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: changeEmphasisMenu.2.cs URL: From commits at source.squeak.org Tue Sep 8 11:48:19 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 8 Sep 2020 11:48:19 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1681.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1681.mcz ==================== Summary ==================== Name: Morphic-ct.1681 Author: ct Time: 8 September 2020, 1:48:12.211798 pm UUID: 9e91205a-93e1-c143-ae8f-4aa3428bf197 Ancestors: Morphic-mt.1679 Fixes use of custom attributes in DialogWindows Things like the following did NOT work properly before this patch: self inform: 'normal ' asText , ('red ' asText addAttribute: TextColor red; yourself) , ('font ' asText addAttribute: (TextFontReference toFont: ((TextStyle named: 'BitstreamVeraSansMono') fontOfSize: 20)); yourself) , ('url ' asText addAttribute: TextURL new; yourself) This was because DialogWindow added its own styling attributes on top of all existing attributes. For color, this is not necessary because #textColor: is sufficient. For font size, in theory, TextFontChange should be sufficient, but currently it does not support relative changes to the font size. =============== Diff against Morphic-mt.1679 =============== Item was changed: ----- Method: DialogWindow>>setMessageParameters (in category 'initialization') ----- setMessageParameters + + messageMorph ifNil: [^ self]. + messageMorph + hResizing: #shrinkWrap; + vResizing: #shrinkWrap. + + self userInterfaceTheme textColor ifNotNil: [:color | + messageMorph textColor: color]. + self userInterfaceTheme font ifNotNil: [:font | + self flag: #todo. "ct: Develop a relative text font change attribute. At the moment, all prior font sizes will be overwritten." + messageMorph contents addAttribute: ( + TextFontChange fontNumber: (font textStyle fontIndexOf: font))].! - messageMorph ifNotNil: [ - | fontToUse colorToUse | - fontToUse := self userInterfaceTheme font ifNil: [TextStyle defaultFont]. - colorToUse := self userInterfaceTheme textColor ifNil: [Color black]. - - messageMorph - hResizing: #shrinkWrap; - vResizing: #shrinkWrap. - - messageMorph contents - addAttribute: (TextFontReference toFont: fontToUse); - addAttribute: (TextColor color: colorToUse). - messageMorph textColor: colorToUse].! From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 11:50:32 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 11:50:32 +0000 Subject: [squeak-dev] The Trunk: MonticelloConfigurations-mt.160.mcz In-Reply-To: References: <,> <20200417184909.GA23884@shell.msen.com> <6275cea29b1942ff85f8c1c6466555df@student.hpi.uni-potsdam.de> <,> <9bf8a5f46406489e89befe417ae79b20@student.hpi.uni-potsdam.de> <,> <529e35859b3c441d95a63f697bef6c7b@student.hpi.uni-potsdam.de> <,> <5154c1af4030491c9e6d7507d17260a6@student.hpi.uni-potsdam.de>, Message-ID: <2e3d969f784f470e9da9003ff85b4a1f@student.hpi.uni-potsdam.de> Hi Marcel, I think it is absolutely expectable behavior that a TextColor laid over a TextURL suppresses the color of the link. But the TextColor was the unexpected part here, at least for me. Please see Morphic-ct.1681 for a fix. :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 11. Mai 2020 09:07:58 An: gettimothy via Squeak-dev Betreff: Re: [squeak-dev] The Trunk: MonticelloConfigurations-mt.160.mcz > And btw, since we can now handle events in dialog windows, the URL could also be made clickable :-) It already is. It is just not blue-ish because of some quirks in TextAction and #dominates: ... Best, Marcel Am 10.05.2020 13:08:02 schrieb Thiede, Christoph : Hm, just another, probably simpler idea: On almost all GitHub/GitLab repos, I see the badges arranged in their own line, without any regular text before or behind them. Can't we do this, too? Then we will also get rid of any problems with the inconsistent text adjustments: [cid:46835f79-caea-455a-8a6f-277c8e262557] Something like this: [cid:e0f46d3f-c014-42a7-ba6d-90cc09cf58c6] Alternatively, just leave out the "TravisCI status:" text. And btw, since we can now handle events in dialog windows, the URL could also be made clickable :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 27. April 2020 10:37:16 An: Robert via Squeak-dev Betreff: Re: [squeak-dev] The Trunk: MonticelloConfigurations-mt.160.mcz Hi Tom, thanks for the explanation! :-) This is the current dialog in hi-dpi mode: [cid:dc361a7f-7266-4e21-89a0-68a90f74ee58] Best, Marcel Am 27.04.2020 09:35:15 schrieb Beckmann, Tom : Hi Christoph, hi Marcel, looking at the picture, it appears to have a height of 20 pixels, which is just above what is commonly used as the default body text size in the Web (and for example in a GitHub README) of 16px. In the web, travis will by default suggest linking to an SVG file that has a viewport height of 20px at the default resolution. For 2x HiDPI, this would then get doubled to be 40 rendered pixels high. If you were to place a PNG file with a height of 20px in your website, it would just get scaled up and appear blurry next to the crisp text, which is why rasterized icons are often provided at multiple resolutions[1]. So in this case, I would argue that simply scaling by whatever scale factor the RealEstateAgent suggests is the "correct" behavior. What exactly the rules concerning alignment of the image in the text are in the web, I would also have to look up. I would imagine they do quite a bit of trickery to get baselines to match prettily. Best, Tom [1] https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images ________________________________________ From: Squeak-dev on behalf of Taeumel, Marcel Sent: Monday, April 27, 2020 8:50:27 AM To: Robert via Squeak-dev Subject: Re: [squeak-dev] The Trunk: MonticelloConfigurations-mt.160.mcz Hi Christoph, I wonder what the conventions in the Web-programming world are in this regard. Using Firefox, the PNG from TravisCI happens to be "just of the right size" on a low-dpi display. After zooming in, that picture gets blurry. I wouldn't bother 1-2 pixels off for the sake of not being blurry. Or maybe our text anchors need some option to be on the baseline but not considered when computing the line height? Best, Marcel Am 26.04.2020 20:21:10 schrieb Thiede, Christoph : Hi Marcel, > While we cannot call #scaledToHeight: depending on the text's current font properties, Why not? Is there any HTML style attribute we could use for this purpose, or would this be a too major change? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 20. April 2020 10:06:45 An: gettimothy via Squeak-dev Betreff: Re: [squeak-dev] The Trunk: MonticelloConfigurations-mt.160.mcz > > 1. Can we automatically use #scaledToHeight: for the badge to avoid the inconsistent row heights? > Oh, definitely. I forgot to call #scaleIconToDisplay ... Sorry, I mixed that up. While we cannot call #scaledToHeight: depending on the text's current font properties, we should call #scaledToSize: and honor RealEstateAgent >> scaleFactor. It is the best we have at the moment. --- #scaleIconToDisplay would also not work because that scales down bigger images, which we do not want in HTML content. Best, Marcel Am 20.04.2020 08:49:49 schrieb Marcel Taeumel : > 1. Can we automatically use #scaledToHeight: for the badge to avoid the inconsistent row heights? Oh, definitely. I forgot to call #scaleIconToDisplay. I would rather not make a assumptions about the surrounding text but maybe add support width/height to the [X] tag. > 2. TextURL and TextReference seem not to work well together: In my image, only the left half of the banner is actually clickable. You can open a new discussion for that. :-) And maybe isolate the issue first in a compact example for everybody to try out. Best, Marcel Am 18.04.2020 15:36:05 schrieb Thiede, Christoph : I love this idea! :D Two comments: 1. Can we automatically use #scaledToHeight: for the badge to avoid the inconsistent row heights? 2. TextURL and TextReference seem not to work well together: In my image, only the left half of the banner is actually clickable. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Freitag, 17. April 2020 20:49 Uhr An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Trunk: MonticelloConfigurations-mt.160.mcz That's brilliant :-) Dave On Fri, Apr 17, 2020 at 07:04:17PM +0200, karl ramberg wrote: > Cool > > Best, > Karl > > On Fri, Apr 17, 2020 at 5:25 PM Marcel Taeumel > wrote: > > > > > Am 17.04.2020 17:17:37 schrieb commits at source.squeak.org > > commits at source.squeak.org>: > > Marcel Taeumel uploaded a new version of MonticelloConfigurations to > > project The Trunk: > > http://source.squeak.org/trunk/MonticelloConfigurations-mt.160.mcz > > > > ==================== Summary ==================== > > > > Name: MonticelloConfigurations-mt.160 > > Author: mt > > Time: 17 April 2020, 5:17:27.82286 pm > > UUID: 6d32501e-f519-3b4a-ae1a-58b52360a937 > > Ancestors: MonticelloConfigurations-mt.159 > > > > Shows the current CI status in the update dialog. > > > > =============== Diff against MonticelloConfigurations-mt.159 > > =============== > > > > Item was changed: > > ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') ----- > > doUpdate: interactive > > "Update the image by loading all pending updates from the server. If this > > is > > the default updater for the system, update the system version when > > complete. > > If interteractive use a modal notifier, otherwise only update the > > transcript. > > Flush all caches. If a previous download failed this is often helpful" > > > > | config previousUpdateLevel ensureTranscriptSetting | > > previousUpdateLevel := SystemVersion current highestUpdate. > > MCFileBasedRepository flushAllCaches. > > ensureTranscriptSetting := MCConfiguration ensureOpenTranscript. > > [ MCConfiguration ensureOpenTranscript: interactive. > > config := self updateFromRepository. > > config ifNil: [ > > interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from > > remote repository.' translated ]. > > Transcript cr; show: '========== Unable to retrieve updates from remote > > repository. ==========' translated; cr. > > ^ self ]. > > MCMcmUpdater default == self > > ifTrue: [ > > config setSystemVersion. > > + interactive ifTrue: [ > > + self inform: (self updateMessageFor: previousUpdateLevel)]. > > - interactive ifTrue: [ > > - self inform: ('Update completed.\\Version: {1}\Update: {3}{2}\\Url: > > {4}\Map: ''{5}''{6}' translated withCRs format: { > > - SystemVersion current version. > > - SystemVersion current highestUpdate. > > - previousUpdateLevel = SystemVersion current highestUpdate > > - ifTrue: [''] > > - ifFalse: [previousUpdateLevel asString, ' -> ']. > > - self repository. > > - MCMcmUpdater updateMapName. > > - SystemVersion current description ifEmpty: [''] ifNotEmpty: [:d | String > > cr, String cr, d]})]. > > Transcript cr; > > show: '========== Update completed: ' translated; > > show: previousUpdateLevel; > > show: ' -> ' ; > > show: SystemVersion current highestUpdate; > > show: ' =========='; cr ] > > ifFalse: [ > > interactive > > ifTrue: [ self inform: 'Update completed.' ]. > > Transcript cr; show: '========== Update completed. ==========' translated; > > cr ] ] > > ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting]. > > > > ! > > > > Item was changed: > > ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') ----- > > doUpdate: interactive upTo: versionNumber > > "Update the image by loading all pending updates from the server. If this > > is > > the default updater for the system, update the system version when > > complete. > > If interteractive use a modal notifier, otherwise only update the > > transcript. > > Flush all caches. If a previous download failed this is often helpful" > > > > | config previousUpdateLevel | > > previousUpdateLevel := SystemVersion current highestUpdate. > > MCFileBasedRepository flushAllCaches. > > config := self updateFromRepositories: { self repository } upTo: > > versionNumber. > > config ifNil: [ > > interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from > > remote repository.' translated ]. > > Transcript cr; show: '========== Unable to retrieve updates from remote > > repository. ==========' translated; cr. > > ^ self ]. > > MCMcmUpdater default == self > > ifTrue: [ > > config setSystemVersion. > > + interactive ifTrue: [ > > + self inform: (self updateMessageFor: previousUpdateLevel)]. > > - interactive ifTrue: [ > > - self inform: ('Update completed.\\Version: {1}\Update: {3}{2}\\Url: > > {4}\Map: ''{5}''{6}' translated withCRs format: { > > - SystemVersion current version. > > - SystemVersion current highestUpdate. > > - previousUpdateLevel = SystemVersion current highestUpdate > > - ifTrue: [''] > > - ifFalse: [previousUpdateLevel asString, ' -> ']. > > - self repository. > > - MCMcmUpdater updateMapName. > > - SystemVersion current description ifEmpty: [''] ifNotEmpty: [:d | String > > cr, String cr, d]})]. > > Transcript cr; > > show: '========== Update completed: ' translated; > > show: previousUpdateLevel; > > show: ' -> ' ; > > show: SystemVersion current highestUpdate; > > show: ' =========='; cr ] > > ifFalse: [ > > interactive > > ifTrue: [ self inform: 'Update completed.' ]. > > Transcript cr; show: '========== Update completed. ==========' translated; > > cr ] > > ! > > > > Item was added: > > + ----- Method: MCMcmUpdater>>updateMessageFor: (in category 'private') > > ----- > > + updateMessageFor: previousUpdateLevel > > + > > + ^ ('Update completed. > > > > Version: {1} > > Update: {3}*{2}* > > > > Url: {4} > > Map: ''{5}'' > > TravisCI status: {6}' translated format: { > > + SystemVersion current version. > > + SystemVersion current highestUpdate. > > + previousUpdateLevel = SystemVersion current highestUpdate > > + ifTrue: [''] > > + ifFalse: [previousUpdateLevel asString, ' -> ']. > > + self repository. > > + MCMcmUpdater updateMapName. > > + SystemVersion current description ifEmpty: [''] ifNotEmpty: [:d | > > + ' > > > > ', (d copyReplaceAll: String cr with: ' > > ')]. > > + SystemVersion current ciStatusBadgeUrl. > > + SystemVersion current ciStatusPageUrl. > > + }) asTextFromHtml! > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 37101 bytes Desc: image.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 43277 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 44636 bytes Desc: pastedImage.png URL: From commits at source.squeak.org Tue Sep 8 15:06:24 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 8 Sep 2020 15:06:24 0000 Subject: [squeak-dev] The Inbox: Regex-Tests-Core-ct.11.mcz Message-ID: Christoph Thiede uploaded a new version of Regex-Tests-Core to project The Inbox: http://source.squeak.org/inbox/Regex-Tests-Core-ct.11.mcz ==================== Summary ==================== Name: Regex-Tests-Core-ct.11 Author: ct Time: 8 September 2020, 5:06:20.887336 pm UUID: 6d0bb586-18f2-1045-ba9c-6b288515c123 Ancestors: Regex-Tests-Core-nice.10 Adds regression test for capturing-like behavior of lookaround expressions See also: http://forum.world.st/The-Inbox-Regex-Core-ct-56-mcz-tp5113011p5114971.html =============== Diff against Regex-Tests-Core-nice.10 =============== Item was added: + ----- Method: RxParserTest>>testLookaroundNullable (in category 'tests') ----- + testLookaroundNullable + + self should: ['(?<=a)?b' asRegex] raise: RegexSyntaxError.! From commits at source.squeak.org Tue Sep 8 15:07:35 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 8 Sep 2020 15:07:35 0000 Subject: [squeak-dev] The Inbox: Regex-Core-ct.58.mcz Message-ID: Christoph Thiede uploaded a new version of Regex-Core to project The Inbox: http://source.squeak.org/inbox/Regex-Core-ct.58.mcz ==================== Summary ==================== Name: Regex-Core-ct.58 Author: ct Time: 8 September 2020, 5:07:32.229336 pm UUID: 66a3d373-fd7a-fc41-ba4f-7233d60a5ec4 Ancestors: Regex-Core-ul.57 Fixes capturing-like behavior of lookaround expressions. Complements Regex-Tests-Core-ct.11. Thanks to Levente (ul) for the bug report! [1] [1] http://forum.world.st/The-Inbox-Regex-Core-ct-56-mcz-tp5113011p5114971.html =============== Diff against Regex-Core-ul.57 =============== Item was added: + ----- Method: RxsLookaround>>isNullable (in category 'testing') ----- + isNullable + + ^ true! From lewis at mail.msen.com Tue Sep 8 15:52:16 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 8 Sep 2020 11:52:16 -0400 Subject: [squeak-dev] Recent Monticello Timestamps In-Reply-To: References: <20200907235900.GA10645@shell.msen.com> Message-ID: <20200908155216.GA73589@shell.msen.com> On Tue, Sep 08, 2020 at 05:57:21AM +0000, Thiede, Christoph wrote: > > Well, maybe not so effective after all, given that the commit notice for Monticello-dtl.727 on the mailing list still exhibits the same problem. > > Isn't that notice generated by some servers that depend on a Trunk image rather than the uploaded inbox version itself? > Yes, that is the reason. Dave > Best, > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Dienstag, 8. September 2020 01:59:00 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Recent Monticello Timestamps > > Well, maybe not so effective after all, given that the commit notice > for Monticello-dtl.727 on the mailing list still exhibits the same problem. > > Dave > > On Mon, Sep 07, 2020 at 07:52:02PM -0400, David T. Lewis wrote: > > A crude but effective fix is in the inbox in Monticello-dtl.727 > > > > Dave > > > > On Mon, Sep 07, 2020 at 03:56:19AM +0200, Levente Uzonyi wrote: > > > Hi Eliot, > > > > > > > > > The Monticello history of the Chronology package says that > > > those microseconds were introduced by Kernel-eem.970: > > > > > > >Changes to Time and Delay prior to changing over to the utc microsecond > > > >clock and its use in delay scheduling. > > > > > > The "offending" change is probably the following: > > > > > > Item was changed: > > > ----- Method: Time class>>now (in category 'ansi protocol') ----- > > > now > > > "Answer a Time representing the time right now - this is a 24 hour > > > clock." > > > + | localUsecs localUsecsToday | > > > + localUsecs := self localMicrosecondClock. > > > + localUsecsToday := localUsecs \\ MicrosecondsInDay. > > > + ^ self > > > + seconds: localUsecsToday // 1000000 > > > + nanoSeconds: localUsecsToday \\ 1000000 * 1000! > > > - > > > - | ms | > > > - > > > - ms := self milliSecondsSinceMidnight. > > > - > > > - ^ self seconds: (ms // 1000) nanoSeconds: (ms \\ 1000) * 1000000 > > > - > > > - > > > - ! > > > > > > And that happened more than four and a half years ago. > > > > > > > > > Levente > > > > > > On Sun, 6 Sep 2020, Eliot Miranda wrote: > > > > > > >Name: VMMaker.oscog-eem.2800 > > > >Author: eem > > > >Time: 6 September 2020, 4:58:47.598839 pm > > > >UUID: a6116113-df13-435d-968d-e9b111676754 > > > >Ancestors: VMMaker.oscog-eem.2799 > > > > > > > >Seriously ?!??! ;-) > > > >_,,,^..^,,,_ > > > >best,??Eliot > > > > > > > > > > > > > > > > > > > From Das.Linux at gmx.de Tue Sep 8 16:30:42 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Tue, 8 Sep 2020 18:30:42 +0200 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> Message-ID: <8AFA56FD-38C7-4D1A-9895-D11C8960DD3A@gmx.de> Hi Tony could add you .. Best regards -Tobias > On 08.09.2020, at 10:42, Thiede, Christoph wrote: > > Hi Levente, > > is there no active development happening on http://www.squeaksource.com/JSON any longer? Is your fork specialized for optimized code? I was rather trying to reuse existing functionality such as #nextHexDigit. Just for interest, how much faster did you get the code by optimizing my changes? ;-) > > Best, > Christoph > Von: Squeak-dev im Auftrag von Levente Uzonyi > Gesendet: Sonntag, 6. September 2020 02:13:56 > An: The general-purpose Squeak developers list > Cc: Niephaus, Fabio > Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project > > Hi Christoph, > > Your code looks correct to me, though it's a bit too slow for my taste. > I've fixed it in my fork of JSON[1] as well. > > Levente > [1] http://squeaksource.com/PostgresV3/JSON-ul.55.mcz > > On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > > > > Hi all, > > > > > > I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into the > > repository? :-) > > > > > > Best, > > > > Christoph > > > > > > From tim at rowledge.org Tue Sep 8 19:00:03 2020 From: tim at rowledge.org (tim Rowledge) Date: Tue, 8 Sep 2020 12:00:03 -0700 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: <8AFA56FD-38C7-4D1A-9895-D11C8960DD3A@gmx.de> References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> <8AFA56FD-38C7-4D1A-9895-D11C8960DD3A@gmx.de> Message-ID: > On 2020-09-08, at 9:30 AM, Tobias Pape wrote: > > Hi > > Tony could add you .. For a fairly widely used facility like this it would be nice to gather all the best bits into one well maintained place. I use the 'raw' version with my Weatherstation and Levente's version in the postgres package for a work thing... nice to have just one. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim All new: The software is not compatible with previous versions. From leves at caesar.elte.hu Tue Sep 8 19:43:56 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Tue, 8 Sep 2020 21:43:56 +0200 (CEST) Subject: [squeak-dev] Unicode In-Reply-To: <37fa8427b38e40fc8c4a2cc61027b2bd@student.hpi.uni-potsdam.de> References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> , <8DCD60E5-9959-4B5D-9C84-D0AC59405A4C@gmx.de> <37fa8427b38e40fc8c4a2cc61027b2bd@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > > Your words suggest that it has already been published, but I can't find it anywhere. > > > Then I must have expressed myself wrong. I did not yet publish any code changes, but in my original post from March, you can find a short description of the design changes I'd like to implement. Essentially, I would like to > replace the separate class variables for every known character class in favor of greater flexibility. How would your changes affect GeneralCategory? Would it still be a SpareLargeTable with ByteArray as arrayClass? If you just replace those integers with symbols, the size of the table will be at least 4 or 8 times larger in 32 or 64 bit images, respectively. Levente. > > Eliot, are there any remaining questions regarding the VM size? Character size should be sufficient as discussed below, and of course, I can test any changes in a 32-bit image, too. :-) > > WideString withAll: (#(42r2l 16r1F497 16r1F388 33) collect: #asCharacter) > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Sonntag, 6. September 2020 21:00:14 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Unicode   > > > On 06.09.2020, at 20:40, Levente Uzonyi wrote: > > > > On Sun, 6 Sep 2020, Tobias Pape wrote: > > > >> > >>> On 06.09.2020, at 19:15, Eliot Miranda wrote: > >>> Hi Christoph, Hi All, > >>>> On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: > >>>> Hi all! :-) > >>>> After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 > are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, > but so long, I have one general question for you: > >>> And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values).  In the 32-bit variant Characters > are 30-bit unsigned integers.  In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. > >>> Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant?  It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure that > initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. > >>> Q2, how many bits should the 64-bit variant VM support for immediate Characters? > >> > >> Unicode has a max value of 0x10FFFF. That makes 21 bit. So no worries there. > >> > >> We should just not forget the leading-char stuff (Yoshiki, Andreas,...) > > > > AFAIU the leading char only makes sense when you have multiple CJK(V?) languages in use at the same time. In other cases Unicode (leadingChar = 0) is perfectly fine. > > IIRC there are 22 bits available for the codePoint and 8 for the leadingChar, so we're still good: all unicode characters fit. > > > > > > \o/ hooray! > > > Levente > > > >> > >> > >> BEst regards > >>       -Tobias > >> > >>> Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to > LargePositiveInteger beyond SmallInteger maxVal.  This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. > >>> It has implications in a few parts of the system: > >>> - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances > >>> - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation > >>> - 32-bit <=> 64-bit image conversion All this is easily doable (because we have models of doing it for Float and Integer general instances).  But we need good specifications so we can implement the right thing from the > get-go. > >>>> At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different > purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. > >>>> Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, > #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't know > whether this will ever happen). > >>>> Examples: > >>>> Unicode generalTagOf: $a asUnicode. "#Ll" > >>>> Unicode class >> isLetterCode: charCode > >>>>  ^ (self generalTagOf: charCode) first = $L > >>>> Unicode class >> isAlphaNumericCode: charCode > >>>>  | tag| > >>>>  ^ (tag := self generalCategoryOf: charCode) first = $L > >>>>        or: [tag = #Nd] > >>>> How do you think about this proposal? Please let me know and I will go ahead! :D > >>>> Best, > >>>> Christoph > >>> Best, Eliot > >>> _,,,^..^,,,_ (phone) > > > > > From karlramberg at gmail.com Tue Sep 8 19:49:23 2020 From: karlramberg at gmail.com (karl ramberg) Date: Tue, 8 Sep 2020 21:49:23 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de> References: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de> Message-ID: On Tue, Sep 8, 2020 at 8:16 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Karl, > > > I'm sorry your proposal is lost a bit - I think it's a nice addition! It > does not look exactly like the TextMorph dialog, but I actually prefer "one > dialog that fits it all" over the three separate handles for a TextMorph > (that do not even all appear to work properly at the moment.) I think we > should get this merged! :-) > > > My change just adds handle support to the already existing StringMorph>>changeFont The three TextMorph handles are not optimal and could be refactored into one handle. I made another change to FontChooserTool where I added a button to apply text changes to the partition of text selected or a bulk change to all text in the pane. With this change I also enabled the emphasis pane to FontChooserTool. [image: bild.png] Best, Karl Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von karl ramberg > *Gesendet:* Donnerstag, 3. September 2020 13:28:41 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] Inspector Custom Value Panes & Related > > Here is a change set that adds HaloHandle for font change to StringMorph > > Best, > Karl > > On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel > wrote: > >> Hi Eric. >> >> > What is the proper way to make a "custom value pane" for my objects >> whenever they appear inside an Inspector? >> >> Unfortunately, the current inspector framework has no such extension >> point. You can, however, prepare a custom inspector for your domain >> objects. See MorphInspector as an example. >> >> Best, >> Marcel >> >> Am 02.09.2020 00:10:56 schrieb Eric Gade : >> Hi all, >> >> What is the proper way to make a "custom value pane" for my objects >> whenever they appear inside an Inspector? For my RISCV work, I'm looking to >> show a morphic representation of the bitfields when a given instruction's >> `self` property is highlighted in the Inspector window. >> >> As a related question, what's the best way to make a composed Morph that >> has StringMorphs of different fonts, sizes, and alignments? It seems like >> not all Fonts are available to StringMorph (TrueType ones, for example) -- >> is that correct? >> >> Thanks! >> >> -- >> Eric >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 21823 bytes Desc: not available URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 19:52:55 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 19:52:55 +0000 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> <8AFA56FD-38C7-4D1A-9895-D11C8960DD3A@gmx.de>, Message-ID: Without wanting to step on anyone's toes, +1 for centralizing packages like this. If Levente hadn't explained in his email that his repository is a fork, I would not even have known which version to develop against. Such things are confusing and impede open source contributions. Are you talking about integrating it into the Trunk or would this be another step? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von tim Rowledge Gesendet: Dienstag, 8. September 2020 21:00:03 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project > On 2020-09-08, at 9:30 AM, Tobias Pape wrote: > > Hi > > Tony could add you .. For a fairly widely used facility like this it would be nice to gather all the best bits into one well maintained place. I use the 'raw' version with my Weatherstation and Levente's version in the postgres package for a work thing... nice to have just one. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim All new: The software is not compatible with previous versions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 8 19:57:50 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 8 Sep 2020 19:57:50 +0000 Subject: [squeak-dev] Font Handles (was: Inspector Custom Value Panes & Related) In-Reply-To: References: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de>, Message-ID: Great ideas! I never get the right handle on the first try, merging them sounds very reasonable. :-) Hm ... doesn't the existence of such an "Apply" button impede Morphic's liveness? Why not just apply all changes as soon as a font/style has been selected? You could replace the buttons by a checkbox "selection only". Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Dienstag, 8. September 2020 21:49 Uhr An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related On Tue, Sep 8, 2020 at 8:16 AM Thiede, Christoph > wrote: Hi Karl, I'm sorry your proposal is lost a bit - I think it's a nice addition! It does not look exactly like the TextMorph dialog, but I actually prefer "one dialog that fits it all" over the three separate handles for a TextMorph (that do not even all appear to work properly at the moment.) I think we should get this merged! :-) My change just adds handle support to the already existing StringMorph>>changeFont The three TextMorph handles are not optimal and could be refactored into one handle. I made another change to FontChooserTool where I added a button to apply text changes to the partition of text selected or a bulk change to all text in the pane. With this change I also enabled the emphasis pane to FontChooserTool. [bild.png] Best, Karl Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von karl ramberg > Gesendet: Donnerstag, 3. September 2020 13:28:41 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related Here is a change set that adds HaloHandle for font change to StringMorph Best, Karl On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel > wrote: Hi Eric. > What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? Unfortunately, the current inspector framework has no such extension point. You can, however, prepare a custom inspector for your domain objects. See MorphInspector as an example. Best, Marcel Am 02.09.2020 00:10:56 schrieb Eric Gade >: Hi all, What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? For my RISCV work, I'm looking to show a morphic representation of the bitfields when a given instruction's `self` property is highlighted in the Inspector window. As a related question, what's the best way to make a composed Morph that has StringMorphs of different fonts, sizes, and alignments? It seems like not all Fonts are available to StringMorph (TrueType ones, for example) -- is that correct? Thanks! -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 21823 bytes Desc: bild.png URL: From leves at caesar.elte.hu Tue Sep 8 20:22:10 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Tue, 8 Sep 2020 22:22:10 +0200 (CEST) Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de>, <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > Hi Levente, > > > is there no active development happening on http://www.squeaksource.com/JSON any longer? Last commit is from 2016. I wanted to have our changes added to the main repository[1]. > Is your fork specialized for optimized code? It has got performance improvements and new features as well. There are quite a few changes, so I suggest you go through the MC history if you want to know what has been changed. >I was rather trying to reuse existing functionality such as #nextHexDigit. Just for > interest, how much faster did you get the code by optimizing my changes? ;-) I haven't measured it. The parser has been rewritten, so the code is quite different. But I expect my version to be at least a magnitude faster. Levente [1] http://forum.world.st/Another-version-of-JSON-in-the-Inbox-td5066614.html > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Levente Uzonyi > Gesendet: Sonntag, 6. September 2020 02:13:56 > An: The general-purpose Squeak developers list > Cc: Niephaus, Fabio > Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project   > Hi Christoph, > > Your code looks correct to me, though it's a bit too slow for my taste. > I've fixed it in my fork of JSON[1] as well. > > Levente > [1] http://squeaksource.com/PostgresV3/JSON-ul.55.mcz > > On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > > > > Hi all, > > > > > > I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into > the > > repository? :-) > > > > > > Best, > > > > Christoph > > > > > > > > From vanessa at codefrau.net Tue Sep 8 23:15:29 2020 From: vanessa at codefrau.net (Vanessa Freudenberg) Date: Tue, 8 Sep 2020 16:15:29 -0700 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de> <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> Message-ID: The source server does not rewrite config maps. It returns exactly what was uploaded when that map was published. Including the trunk url. - Vanessa - On Tue, Sep 8, 2020 at 3:22 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Marcel, hi all, > > > phew, this turned out to be a bit more complicated than I had assumed. > > > The attached script should patch every in the image required to upgrade > the update URLs to HTTPS. Eventually, I would like to ship it via a > postscript in a regular trunk version. > > Still, the server code must be updated, because unfortunately, it returns > URLs with hard-coded HTTP, even if the request is made via HTTPS: > > > ( > name 'update-eem.477' > repository ('http://source.squeak.org/trunk') > dependency ('Squeak-Version' 'Squeak-Version-mt.5252' > 'b9a3cd5b-b708-8646-a99f-5f3ae294ceb1') > ... > > > > I don't have access to the server code, but apparently the protocol should > be checked there. And isn't it a general convention to favor relative over > absolute URLs? Why doesn't the server simply return '/trunk' here? > > Best, > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Taeumel, Marcel > *Gesendet:* Freitag, 12. Juni 2020 16:17 Uhr > *An:* squeak-dev > *Betreff:* Re: [squeak-dev] How to change the update URL for Trunk HTTPS? > > I suppose it is because the update map has "http" in it for each package > version. And maybe also because your repositories in Monticello are > configured as "http". So, the mechanism does not find any previous > versions? > > Seems like bug to me. > > Best, > Marcel > > Am 12.06.2020 15:51:51 schrieb Thiede, Christoph < > christoph.thiede at student.hpi.uni-potsdam.de>: > > Hi all, > > > I spent a number of attempts during the latest months on changing my > image's update URL to use HTTPS, but unfortunately, it gets reset each time. > > > Shouldn't I be able to simply change the update URL in the preferences by > adding a small "s" behind the "http"? But if I do so, when I press the > update button the next time, Squeak starts loading all packages from their > initial version (Tools-xyz.1, Morphic-xyz.1, ...). Is there a way to > easily change the URL? > > > Best, > > Christoph > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 8 23:41:59 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 8 Sep 2020 23:41:59 0000 Subject: [squeak-dev] The Inbox: Chronology-Tests-dtl.24.mcz Message-ID: A new version of Chronology-Tests was added to project The Inbox: http://source.squeak.org/inbox/Chronology-Tests-dtl.24.mcz ==================== Summary ==================== Name: Chronology-Tests-dtl.24 Author: dtl Time: 8 September 2020, 7:41:58.606243 pm UUID: b399c1e6-4dfe-4240-b44e-19ec5abf3a74 Ancestors: Chronology-Tests-mt.23 Add tests for Time>>addSeconds: to ensure that nanosecond values are preserved. =============== Diff against Chronology-Tests-mt.23 =============== Item was added: + ----- Method: TimeTest>>testAddSecondsNanoSecondRollover (in category 'testing') ----- + testAddSecondsNanoSecondRollover + + | time1 time2 time3 time4 time5 time6 time7 time8 | + time1 := Time fromSeconds: 1.2s. + time2 := time1 addSeconds: 1.3s. + "n.b. use ticks second to access seconds instvar because #asSeconds is + questionable and possibly subject to future change -dtl" + self assert: 2 equals: time2 ticks second. + self assert: 500000000 equals: time2 nanoSecond. + time3 := Time fromSeconds: 1.9s. + time4 := time3 addSeconds: 1.2s. + self assert: 3 equals: time4 ticks second. + self assert: 100000000 equals: time4 nanoSecond. + time5 := Time fromSeconds: 0.9s. + time6 := time5 addSeconds: 0.2s. + self assert: 1 equals: time6 ticks second. + self assert: 100000000 equals: time6 nanoSecond. + time7 := time5 addSeconds: 1. + self assert: 1 equals: time7 ticks second. + self assert: 900000000 equals: time7 nanoSecond. + "midnight rollover" + time8 := '11:59:59.505 pm' asTime addSeconds: 1.0101. + self assert: 0 equals: time8 ticks second. + self assert: 515100000 equals: time8 nanoSecond. + ! Item was added: + ----- Method: TimeTest>>testAddSecondsWithNanos (in category 'testing') ----- + testAddSecondsWithNanos + self assert: (timeWithNanos addSeconds: 1) = (Time readFromString: '4:02:48.42 am'). + self assert: (timeWithNanos addSeconds: 60) = (Time readFromString: '4:03:47.42 am'). + self assert: (timeWithNanos addSeconds: 3600) = (Time readFromString: '5:02:47.42 am'). + self assert: (timeWithNanos addSeconds: 24*60*60) = (Time readFromString: '4:02:47.42 am'). + "rollover after midnight" + self assert: (timeWithNanos addSeconds: 71832) = (Time readFromString: '11:59:59.42 pm'). + self assert: (timeWithNanos addSeconds: 71833) = (Time readFromString: '00:00:00.42 am'). + ! From commits at source.squeak.org Tue Sep 8 23:42:45 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 8 Sep 2020 23:42:45 0000 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.60.mcz Message-ID: A new version of Chronology-Core was added to project The Inbox: http://source.squeak.org/inbox/Chronology-Core-dtl.60.mcz ==================== Summary ==================== Name: Chronology-Core-dtl.60 Author: dtl Time: 8 September 2020, 7:42:44.062725 pm UUID: afcc86c3-7fb2-4ecd-b9ae-9840f100393c Ancestors: Chronology-Core-ul.58 Fix Time>>addSeconds: to maintain nanoseconds. =============== Diff against Chronology-Core-ul.58 =============== Item was changed: ----- Method: Time>>addSeconds: (in category 'smalltalk-80') ----- addSeconds: nSeconds "Answer a Time that is nSeconds after the receiver." + | secondsToAdd newNanos | + (secondsToAdd := nSeconds truncated) = nSeconds + ifTrue: [newNanos := nanos] + ifFalse: [(newNanos := nanos + (nSeconds - secondsToAdd * NanosInSecond)) > NanosInSecond + ifTrue: [secondsToAdd := secondsToAdd + 1. + newNanos := newNanos - NanosInSecond]]. + ^ self class seconds: seconds + secondsToAdd nanoSeconds: newNanos! - ^ self class seconds: self asSeconds + nSeconds! From lewis at mail.msen.com Tue Sep 8 23:57:18 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 8 Sep 2020 19:57:18 -0400 Subject: [squeak-dev] Time>addSeconds: ignores nanos ivar value In-Reply-To: References: Message-ID: <20200908235718.GA51256@shell.msen.com> On Mon, Jul 20, 2020 at 01:08:57PM -0700, tim Rowledge wrote: > Time >addSeconds: completely ignores the nanos ivar value and so > Time now -> (say) 12:51:47.418205 pm > send that #addSeconds: 2 -> 12:51:49 pm > > Which is incorrect. Actually *fixing* it seems to be a bit more complex > than just "add the nanoseconds stuff". > > Should #asSeconds be truncating the seconds and ignoring the nanoseconds? > That's also an issue for #addTime: and #subtractTime: (wait, no #subtractSeconds: ?) > > Chronology-afficonados assemble! > > tim In the inbox now, test coverage for the addSeconds: issue is in Chronology-Tests-dtl.24 and a fix is in Chronology-Core-dtl.60. I have not looked at addTime: yet but it may require similar treatment. Dave From lewis at mail.msen.com Wed Sep 9 02:06:40 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 8 Sep 2020 22:06:40 -0400 Subject: [squeak-dev] The Inbox: Chronology-Core-dtl.59.mcz In-Reply-To: References: Message-ID: <20200909020640.GA71596@shell.msen.com> I moved this to treated inbox. Thanks Levente for the reviews and suggestions. Dave On Sun, Sep 06, 2020 at 06:05:02PM +0000, commits at source.squeak.org wrote: > A new version of Chronology-Core was added to project The Inbox: > http://source.squeak.org/inbox/Chronology-Core-dtl.59.mcz > > ==================== Summary ==================== > > Name: Chronology-Core-dtl.59 > Author: dtl > Time: 6 September 2020, 2:05:00.620796 pm > UUID: 99c99e87-ef4a-49ee-99eb-699c6c3dd65d > Ancestors: Chronology-Core-ul.58 > > Adopt simplifications from Cuis. Remove ChronologyConstants. Retain jmv author initials where possible. The shared pool is not required, it is simpler to use methods in the responsible classes. > > For Squeak, OneDay is now a class variable in order to continue to allow Date instances to share a Duration. Cuis creates a new Duration for each Date. Class variable Zero is similarly defined. > > =============== Diff against Chronology-Core-ul.58 =============== > From Das.Linux at gmx.de Wed Sep 9 06:16:16 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Wed, 9 Sep 2020 08:16:16 +0200 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de> <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> Message-ID: <20FF6FC6-1278-4F4F-9572-82EE140BC55F@gmx.de> Hi > On 09.09.2020, at 01:15, Vanessa Freudenberg wrote: > > The source server does not rewrite config maps. It returns exactly what was uploaded when that map was published. Including the trunk url. To add, there has never been "official" HTTPS support in Monticello. We just happen to have: - Replaced HTTPSocket with WebClient - SqueakSSL - A server that incidentally serves https:// for the monticello paths I switched over to https for our HPI squeaksource by (reasonable) demand of the students. We then tested for some time and found no problems. That said, - We have no extensive test - https is (in our variant) still slower than plain http and subject to all SqueakSSL problems that we might have. - certificate checking is still a big mess. - http2 is still missing from SqueakSSL/WebClient. Fortunately, the SqueakSource server does not need to speak SSL itself, the reverse proxy (nginx) takes care of that. So, should we consider an "official" switch, then I think, that can work, if we're fine with the caveats. That would mean, changing the update maps. Best regards -Tobias PS: HTML support something like "protocol-relative urls", that is, you omit the protocl in a link. so instead of hardcoding http://source.squeak.org/trunk or https://source.squeak.org/trunk, you'd say //source.squeak.org/trunk. This was popular for some time, but at the time of writing, the general recommendation for the web is: go https:// > > - Vanessa - > > On Tue, Sep 8, 2020 at 3:22 AM Thiede, Christoph wrote: > Hi Marcel, hi all, > > > phew, this turned out to be a bit more complicated than I had assumed. > > > The attached script should patch every in the image required to upgrade the update URLs to HTTPS. Eventually, I would like to ship it via a postscript in a regular trunk version. > > Still, the server code must be updated, because unfortunately, it returns URLs with hard-coded HTTP, even if the request is made via HTTPS: > > > > ( > name 'update-eem.477' > repository ('http://source.squeak.org/trunk') > dependency ('Squeak-Version' 'Squeak-Version-mt.5252' 'b9a3cd5b-b708-8646-a99f-5f3ae294ceb1') > ... > > > I don't have access to the server code, but apparently the protocol should be checked there. And isn't it a general convention to favor relative over absolute URLs? Why doesn't the server simply return '/trunk' here? > > Best, > Christoph > > Von: Squeak-dev im Auftrag von Taeumel, Marcel > Gesendet: Freitag, 12. Juni 2020 16:17 Uhr > An: squeak-dev > Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS? > > I suppose it is because the update map has "http" in it for each package version. And maybe also because your repositories in Monticello are configured as "http". So, the mechanism does not find any previous versions? > > Seems like bug to me. > > Best, > Marcel >> Am 12.06.2020 15:51:51 schrieb Thiede, Christoph : >> >> Hi all, >> >> >> I spent a number of attempts during the latest months on changing my image's update URL to use HTTPS, but unfortunately, it gets reset each time. >> >> >> Shouldn't I be able to simply change the update URL in the preferences by adding a small "s" behind the "http"? But if I do so, when I press the update button the next time, Squeak starts loading all packages from their initial version (Tools-xyz.1, Morphic-xyz.1, ...). Is there a way to easily change the URL? >> >> >> Best, >> >> Christoph >> > > From Das.Linux at gmx.de Wed Sep 9 06:19:03 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Wed, 9 Sep 2020 08:19:03 +0200 Subject: [squeak-dev] Inspector Custom Value Panes & Related In-Reply-To: References: <1e5509177e4a4c8e9f831dbdf6c15e21@student.hpi.uni-potsdam.de> <8A5F154E-0BA8-4D64-8D1C-E844CCB85690@gmx.de> <4cad345e0ba544d7816544ff864fb74d@student.hpi.uni-potsdam.de> <5662556D-AA81-46B1-850B-CD6E89BCAF42@gmx.de> Message-ID: <43037785-C47E-4FC9-A599-501B483CA417@gmx.de> Hi > On 08.09.2020, at 08:09, Thiede, Christoph wrote: > > Hi all, Hi Tobias, > > does the attached changeset match your expectations? :-) Looks promising. I'd like some input from others wether the #be*-API on Text works, as it currently lives on TextMorph. Other than that, go ahead. Best regards -Tobias > > Best, > Christoph > Von: Squeak-dev im Auftrag von Taeumel, Marcel > Gesendet: Donnerstag, 3. September 2020 15:56:49 > An: squeak-dev > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related > > Plus, TextFontReference is a rather fixed pointer to a specific font and size, which breaks when using Squeak's notion of High-DPI with bigger fonts in general. TextFontChange works better with its index. And so does TextEmphasis for italic. > > Best, > Marcel >> Am 03.09.2020 15:53:35 schrieb Tobias Pape : >> Hi >> > On 03.09.2020, at 15:04, Thiede, Christoph wrote: >> > >> > Hi Tobias, >> > >> > > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? >> > >> > That was exactly my point. 'foo' asText beAllFont: font1; beAllFont: font2 does not make any sense, of course, but when a text is passed from somewhere else, you may not know which formatting has already been applied to it ... >> >> That is true. But if you want to change the font, there's only so much you can do. >> If you want just change an upright font to an italic one, the FontReference will not work in any case. >> The TextFontChange attribute is seemingly intended for that, but it only understands "Font array indices" which is most unhelpful :D >> >> Best regards >> -Tobias >> >> >> > >> > Best, >> > Christoph >> > >> > Von: Squeak-dev im Auftrag von Tobias Pape >> > Gesendet: Donnerstag, 3. September 2020 00:13:24 >> > An: The general-purpose Squeak developers list >> > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related >> > >> > >> > > On 02.09.2020, at 23:29, Thiede, Christoph wrote: >> > > >> > > Hi Tobias, thanks for the tips! :-) >> > > >> > > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. >> > > >> > > Would you expect such a Text>>#setFont:from:to: only to add a TextFontReference or also to remove all existing TextFontReferences from the interval? >> > > >> > >> > I'd be wary of using #set... because exactly that expectation happens. >> > Vocabulary is cumbersome, tho. >> > see TextMorph's _be_AllFont, Text's _make_Bold and the attributes only ever add. >> > >> > Also, I don't see how two font refs can ever be stacked, it makes not much sense, no? >> > Coalescing the runs would probably take care of chopping up the different font runs, but that's beneath the cover. >> > >> > That said, I liked the Seaside/Magritte-wording with beSomething on their brushes or models respectively… >> > >> > Best regards >> > -Tobias >> > >> > > Best, >> > > Christoph >> > > Von: Squeak-dev im Auftrag von Tobias Pape >> > > Gesendet: Mittwoch, 2. September 2020 22:04:38 >> > > An: The general-purpose Squeak developers list >> > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related >> > > >> > > Hi >> > > >> > > > On 02.09.2020, at 21:14, Thiede, Christoph wrote: >> > > > >> > > > Hi Eric, >> > > > >> > > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? >> > > > >> > > > MorphInspector, FormInspector, and a bunch of other small new improvements to the inspector framework have arrived in the Squeak Trunk just a few months ago! Seehttp://forum.world.st/Please-try-out-Inspector-Refactoring-td5114974.html andhttps://squeak.org/downloads/#current-trunk-image-2:~:text=Current-,Trunk,-Image :-) >> > > > >> > > > > I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? >> > > > >> > > > These are good questions others can probably answer better than I could do. >> > > > This is all I can tell you: >> > > > • Font rendering in Squeak is difficult and the default font (DejaVu Sans) appears to be the only one that looks kind of nice in all sizes. I almost never choose a different font. >> > > > • To change the font in a Text, use something like: >> > > > 'foo' asText >> > > > addAttribute: (TextFontReference toFont: (StrikeFont >> > > > familyName: 'Darkmap DejaVu Sans' >> > > > pointSize: 20)); >> > > > openAsMorph >> > > >> > > I'd suggest to use the TextStyle indirection, so you don't have to guess wether the font is a Strike font or a TTFont or whatever: >> > > >> > > 'foo' asText >> > > addAttribute: (TextFontReference toFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20)); >> > > openAsMorph >> > > >> > > >> > > If you are just after changing the font used in a TextMorph (not the text itself), I'd use >> > > >> > > 'foo' asTextMorph >> > > beAllFont: ((TextStyle named: 'Darkmap DejaVu Sans') fontOfSize: 20); >> > > openInHand >> > > >> > > >> > > That way, the name string can be easily exchanged, such as 'BitstreamVeraSans', or anything imported with the FontImporterTool >> > > >> > > I think the "TextFontReference toFont:" and explicit "addAttribute:" are a bit involved; maybe something akin to Text>>#makeBoldFrom:to:/Text>>#allBold would be nice. >> > > >> > > >> > > Best regards >> > > -Tobias >> > > >> > > >> > > > >> > > > > Also -- how can I disable editing of a TextMorph? >> > > > >> > > > See TextMorph >> #readOnly: or also Morph >> #lock to disable any interaction. >> > > > >> > > > Best, >> > > > Christoph >> > > > Von: Squeak-dev im Auftrag von Eric Gade >> > > > Gesendet: Mittwoch, 2. September 2020 17:01:45 >> > > > An: The general-purpose Squeak developers list >> > > > Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related >> > > > >> > > > Hi Christoph, >> > > > >> > > > On Tue, Sep 1, 2020 at 7:32 PM Christoph Thiede wrote: >> > > > >> > > > for displaying graphics in an inspector, have a look at MorphInspector (screenshot) or FormInspector (pixels). Custom interactive fields are not (yet?) supported, but in theory, you could subclass Inspector and override the relevant toolbuilder methods. >> > > > >> > > > I can't seem to find MorphInspector in a current (5.3 here) image. Is this a separate package? >> > > > >> > > > >> > > > Regarding to your second question - why don't you use a TextMorphs? The composition of different formatting styles applied to a string is just what makes up a Text >> > > > >> > > > Aha, yes, I think I've looked into this before. I find the whole relationship between a Text and TextStyle a bit confusing, especially where it comes to setting Fonts. It appears that there are lots of different "kinds" of Fonts, and there's a good amount of indirection so it's difficult to determine which kind of fonts (StrikeFont? TTFont?) "live" and how to see what's available and pick programmatically. Is there a good primer somewhere on Text/TextStyle? Also -- how can I disable editing of a TextMorph? >> > > > >> > > > Thanks again >> >> >> > From leves at caesar.elte.hu Wed Sep 9 08:57:08 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Wed, 9 Sep 2020 10:57:08 +0200 (CEST) Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de>, <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > Hi Marcel, hi all, > > > phew, this turned out to be a bit more complicated than I had assumed. > > > The attached script should patch every in the image required to upgrade the update URLs to HTTPS. Eventually, I would like to ship it via a postscript in a regular trunk version. I just checked your script. It's behavior seems to match what I wrote a while ago. My variant changes all source.squeak.org urls to https: MCMcmUpdater allInstances select: [ :each | each repository beginsWith: 'http://source.squeak.org' ] thenDo: [ :httpUpdater | | httpsUrl newUpdater | httpsUrl := 'https', (httpUpdater repository allButFirst: 4). newUpdater := MCMcmUpdater repository: httpsUrl updateMap: httpUpdater updateMapName lastUpdateMap: (Dictionary new at: httpsUrl put: (httpUpdater lastUpdateMap at: httpUpdater repository); yourself). newUpdater register. httpUpdater unregister ]. MCRepositoryGroup default repositories select: [ :each | (each isKindOf: MCHttpRepository) and: [ each description beginsWith: 'http://source.squeak.org' ]] thenDo: [ :repository | repository location: 'https', (repository description allButFirst: 4) ]. MCMcmUpdater defaultUpdateURL in: [ :updateUrl | (updateUrl beginsWith: 'http://source.squeak.org') ifTrue: [ MCMcmUpdater defaultUpdateURL: 'https', (updateUrl allButFirst: 4) ] ] However, such scripts on their own are not enough. Besides the update maps, the image has several references to the http urls. Most of those need to be updated as well. Levente > > Still, the server code must be updated, because unfortunately, it returns URLs with hard-coded HTTP, even if the request is made via HTTPS: > > > ( > name 'update-eem.477' > repository ('http://source.squeak.org/trunk') > dependency ('Squeak-Version' 'Squeak-Version-mt.5252' 'b9a3cd5b-b708-8646-a99f-5f3ae294ceb1') > ... > > > I don't have access to the server code, but apparently the protocol should be checked there. And isn't it a general convention to favor relative over absolute URLs? Why doesn't the server simply return '/trunk' here? > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Taeumel, Marcel > Gesendet: Freitag, 12. Juni 2020 16:17 Uhr > An: squeak-dev > Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS?   > I suppose it is because the update map has "http" in it for each package version. And maybe also because your repositories in Monticello are configured as "http". So, the mechanism does not find any previous versions? > Seems like bug to me. > > Best, > Marcel > > Am 12.06.2020 15:51:51 schrieb Thiede, Christoph : > > Hi all, > > > I spent a number of attempts during the latest months on changing my image's update URL to use HTTPS, but unfortunately, it gets reset each time. > > > Shouldn't I be able to simply change the update URL in the preferences by adding a small "s" behind the "http"? But if I do so, when I press the update button the next time, Squeak starts loading all packages from > their initial version (Tools-xyz.1, Morphic-xyz.1, ...). Is there a way to easily change the URL? > > > Best, > > Christoph > > > From tonyg at leastfixedpoint.com Wed Sep 9 14:14:59 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Wed, 9 Sep 2020 16:14:59 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: Message-ID: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> Hi all, I've been hacking on Squeak-on-a-cellphone a bit more. Now the image can scan /proc/bus/input/devices and /dev/input/event* (using FFI, AioPlugin and OSProcess), and is able to open touchscreen-like devices and set up additional World HandMorphs for them. There's a short (2 minute) demo video linked here: https://eighty-twenty.org/2020/09/09/squeak-postmarketos-touchscreen I think next I might get Squeak to boot and configure the cellular modem, and get it making calls. Alternatively, perhaps an on-screen keyboard? Does anyone know if anything like that already exists for Squeak? Technical details: it uses my Actors library for Squeak, plus some (not yet released, but only because of laziness) code for the Linux-specific stuff on the image side. It uses a stock Cog VM. Cheers, Tony On 8/27/20 8:42 PM, Tony Garnock-Jones wrote: > Hi all, > > I've recently been playing with Squeak running on a PostmarketOS phone > (Samsung Galaxy S7). > > Many thanks to those who recently landed the ARMv8 build of Cog. (Per > the commit history I think the main person responsible was Ken Dickey? > Thanks Ken!) > > I wrote about progress so far here: > https://eighty-twenty.org/2020/08/25/postmarketos-smalltalk > > Today I posted an update with a saner DPI setting for the phone's > display: https://eighty-twenty.org/2020/08/27/squeak-postmarketos-update > > Next on the list is to read touchscreen input from /dev/input/event1! > > Cheers, > Tony > From marcel.taeumel at hpi.de Wed Sep 9 14:28:00 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Wed, 9 Sep 2020 16:28:00 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> Message-ID: Hi Tony, there are still traces of a rather ancient #ImmPlugin that was meant to tell the OS where to put that virtual keyboard: Best, Marcel Am 09.09.2020 16:15:10 schrieb Tony Garnock-Jones : Hi all, I've been hacking on Squeak-on-a-cellphone a bit more. Now the image can scan /proc/bus/input/devices and /dev/input/event* (using FFI, AioPlugin and OSProcess), and is able to open touchscreen-like devices and set up additional World HandMorphs for them. There's a short (2 minute) demo video linked here: https://eighty-twenty.org/2020/09/09/squeak-postmarketos-touchscreen I think next I might get Squeak to boot and configure the cellular modem, and get it making calls. Alternatively, perhaps an on-screen keyboard? Does anyone know if anything like that already exists for Squeak? Technical details: it uses my Actors library for Squeak, plus some (not yet released, but only because of laziness) code for the Linux-specific stuff on the image side. It uses a stock Cog VM. Cheers, Tony On 8/27/20 8:42 PM, Tony Garnock-Jones wrote: > Hi all, > > I've recently been playing with Squeak running on a PostmarketOS phone > (Samsung Galaxy S7). > > Many thanks to those who recently landed the ARMv8 build of Cog. (Per > the commit history I think the main person responsible was Ken Dickey? > Thanks Ken!) > > I wrote about progress so far here: > https://eighty-twenty.org/2020/08/25/postmarketos-smalltalk > > Today I posted an update with a saner DPI setting for the phone's > display: https://eighty-twenty.org/2020/08/27/squeak-postmarketos-update > > Next on the list is to read touchscreen input from /dev/input/event1! > > Cheers, > Tony > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 138627 bytes Desc: not available URL: From Das.Linux at gmx.de Wed Sep 9 14:32:53 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Wed, 9 Sep 2020 16:32:53 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> Message-ID: <1523E318-DB94-4E37-8D4B-0F12E56CF392@gmx.de> > On 09.09.2020, at 16:28, Marcel Taeumel wrote: > > Hi Tony, > > there are still traces of a rather ancient #ImmPlugin that was meant to tell the OS where to put that virtual keyboard: I dont know wether anciet cuts it. It's regularly shipped with the Linux VM and IIRC necessary for Japanese input… Best regards -Tobias > > > > Best, > Marcel >> Am 09.09.2020 16:15:10 schrieb Tony Garnock-Jones : >> >> Hi all, >> >> I've been hacking on Squeak-on-a-cellphone a bit more. Now the image can >> scan /proc/bus/input/devices and /dev/input/event* (using FFI, AioPlugin >> and OSProcess), and is able to open touchscreen-like devices and set up >> additional World HandMorphs for them. >> >> There's a short (2 minute) demo video linked here: >> https://eighty-twenty.org/2020/09/09/squeak-postmarketos-touchscreen >> >> I think next I might get Squeak to boot and configure the cellular >> modem, and get it making calls. >> >> Alternatively, perhaps an on-screen keyboard? Does anyone know if >> anything like that already exists for Squeak? >> >> Technical details: it uses my Actors library for Squeak, plus some (not >> yet released, but only because of laziness) code for the Linux-specific >> stuff on the image side. >> >> It uses a stock Cog VM. >> >> Cheers, >> Tony >> >> >> >> >> On 8/27/20 8:42 PM, Tony Garnock-Jones wrote: >> > Hi all, >> > >> > I've recently been playing with Squeak running on a PostmarketOS phone >> > (Samsung Galaxy S7). >> > >> > Many thanks to those who recently landed the ARMv8 build of Cog. (Per >> > the commit history I think the main person responsible was Ken Dickey? >> > Thanks Ken!) >> > >> > I wrote about progress so far here: >> > https://eighty-twenty.org/2020/08/25/postmarketos-smalltalk >> > >> > Today I posted an update with a saner DPI setting for the phone's >> > display: https://eighty-twenty.org/2020/08/27/squeak-postmarketos-update >> > >> > Next on the list is to read touchscreen input from /dev/input/event1! >> > >> > Cheers, >> > Tony >> > >> > From lewis at mail.msen.com Wed Sep 9 15:25:17 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 9 Sep 2020 11:25:17 -0400 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> Message-ID: <20200909152517.GA13080@shell.msen.com> That looks very promising indeed. Thanks for the video. Dave On Wed, Sep 09, 2020 at 04:14:59PM +0200, Tony Garnock-Jones wrote: > Hi all, > > I've been hacking on Squeak-on-a-cellphone a bit more. Now the image can > scan /proc/bus/input/devices and /dev/input/event* (using FFI, AioPlugin > and OSProcess), and is able to open touchscreen-like devices and set up > additional World HandMorphs for them. > > There's a short (2 minute) demo video linked here: > https://eighty-twenty.org/2020/09/09/squeak-postmarketos-touchscreen > > I think next I might get Squeak to boot and configure the cellular > modem, and get it making calls. > > Alternatively, perhaps an on-screen keyboard? Does anyone know if > anything like that already exists for Squeak? > > Technical details: it uses my Actors library for Squeak, plus some (not > yet released, but only because of laziness) code for the Linux-specific > stuff on the image side. > > It uses a stock Cog VM. > > Cheers, > Tony > > > > > On 8/27/20 8:42 PM, Tony Garnock-Jones wrote: > > Hi all, > > > > I've recently been playing with Squeak running on a PostmarketOS phone > > (Samsung Galaxy S7). > > > > Many thanks to those who recently landed the ARMv8 build of Cog. (Per > > the commit history I think the main person responsible was Ken Dickey? > > Thanks Ken!) > > > > I wrote about progress so far here: > > https://eighty-twenty.org/2020/08/25/postmarketos-smalltalk > > > > Today I posted an update with a saner DPI setting for the phone's > > display: https://eighty-twenty.org/2020/08/27/squeak-postmarketos-update > > > > Next on the list is to read touchscreen input from /dev/input/event1! > > > > Cheers, > > Tony > > > From marcel.taeumel at hpi.de Wed Sep 9 17:10:55 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Wed, 9 Sep 2020 19:10:55 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <1523E318-DB94-4E37-8D4B-0F12E56CF392@gmx.de> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <1523E318-DB94-4E37-8D4B-0F12E56CF392@gmx.de> Message-ID: > ... and IIRC necessary for Japanese input… Because you need a virtual keyboard for that case? Hmm....  Best. Marcel Am 09.09.2020 16:32:58 schrieb Tobias Pape : > On 09.09.2020, at 16:28, Marcel Taeumel wrote: > > Hi Tony, > > there are still traces of a rather ancient #ImmPlugin that was meant to tell the OS where to put that virtual keyboard: I dont know wether anciet cuts it. It's regularly shipped with the Linux VM and IIRC necessary for Japanese input… Best regards -Tobias > > > > Best, > Marcel >> Am 09.09.2020 16:15:10 schrieb Tony Garnock-Jones : >> >> Hi all, >> >> I've been hacking on Squeak-on-a-cellphone a bit more. Now the image can >> scan /proc/bus/input/devices and /dev/input/event* (using FFI, AioPlugin >> and OSProcess), and is able to open touchscreen-like devices and set up >> additional World HandMorphs for them. >> >> There's a short (2 minute) demo video linked here: >> https://eighty-twenty.org/2020/09/09/squeak-postmarketos-touchscreen >> >> I think next I might get Squeak to boot and configure the cellular >> modem, and get it making calls. >> >> Alternatively, perhaps an on-screen keyboard? Does anyone know if >> anything like that already exists for Squeak? >> >> Technical details: it uses my Actors library for Squeak, plus some (not >> yet released, but only because of laziness) code for the Linux-specific >> stuff on the image side. >> >> It uses a stock Cog VM. >> >> Cheers, >> Tony >> >> >> >> >> On 8/27/20 8:42 PM, Tony Garnock-Jones wrote: >> > Hi all, >> > >> > I've recently been playing with Squeak running on a PostmarketOS phone >> > (Samsung Galaxy S7). >> > >> > Many thanks to those who recently landed the ARMv8 build of Cog. (Per >> > the commit history I think the main person responsible was Ken Dickey? >> > Thanks Ken!) >> > >> > I wrote about progress so far here: >> > https://eighty-twenty.org/2020/08/25/postmarketos-smalltalk >> > >> > Today I posted an update with a saner DPI setting for the phone's >> > display: https://eighty-twenty.org/2020/08/27/squeak-postmarketos-update >> > >> > Next on the list is to read touchscreen input from /dev/input/event1! >> > >> > Cheers, >> > Tony >> > >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tim at rowledge.org Wed Sep 9 18:39:06 2020 From: tim at rowledge.org (tim Rowledge) Date: Wed, 9 Sep 2020 11:39:06 -0700 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <1523E318-DB94-4E37-8D4B-0F12E56CF392@gmx.de> Message-ID: <21E704DA-96B1-4188-B0E8-D0420A53607E@rowledge.org> > On 2020-09-09, at 10:10 AM, Marcel Taeumel wrote: > > > ... and IIRC necessary for Japanese input… > > Because you need a virtual keyboard for that case? Hmm.... Kinda; I did some faffing with it for the Scratch/Pi stuff several years ago. I faintly recall there being some really irritating clash between the settings/installs required and... something or other. Sometihng to do with the `-compositionInput` and clashing with unix key handling and 'anthy' and iBus? I don't recall managing to fully solve it. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim To iterate is human; to recurse, divine. From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 9 20:56:00 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 9 Sep 2020 20:56:00 +0000 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de>, <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de>, Message-ID: Hi all, Levente, thanks for the improvements! I attempted to only patch as much as required for updating the image via the docking bar menu or the Monticello Browser. Why aren't you using 'MCHttpRepository allInstancesDo:' in your version? And I used the Compiler in my version in order not to change the DefaultUpdateURL class variable if it was not set before. > That would mean, changing the update maps. +1 for doing so. Could there be any possible problems? Environments that do not support SSL today? Possible performance problems? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Mittwoch, 9. September 2020 10:57:08 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS? Hi Christoph, On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > Hi Marcel, hi all, > > > phew, this turned out to be a bit more complicated than I had assumed. > > > The attached script should patch every in the image required to upgrade the update URLs to HTTPS. Eventually, I would like to ship it via a postscript in a regular trunk version. I just checked your script. It's behavior seems to match what I wrote a while ago. My variant changes all source.squeak.org urls to https: MCMcmUpdater allInstances select: [ :each | each repository beginsWith: 'http://source.squeak.org' ] thenDo: [ :httpUpdater | | httpsUrl newUpdater | httpsUrl := 'https', (httpUpdater repository allButFirst: 4). newUpdater := MCMcmUpdater repository: httpsUrl updateMap: httpUpdater updateMapName lastUpdateMap: (Dictionary new at: httpsUrl put: (httpUpdater lastUpdateMap at: httpUpdater repository); yourself). newUpdater register. httpUpdater unregister ]. MCRepositoryGroup default repositories select: [ :each | (each isKindOf: MCHttpRepository) and: [ each description beginsWith: 'http://source.squeak.org' ]] thenDo: [ :repository | repository location: 'https', (repository description allButFirst: 4) ]. MCMcmUpdater defaultUpdateURL in: [ :updateUrl | (updateUrl beginsWith: 'http://source.squeak.org') ifTrue: [ MCMcmUpdater defaultUpdateURL: 'https', (updateUrl allButFirst: 4) ] ] However, such scripts on their own are not enough. Besides the update maps, the image has several references to the http urls. Most of those need to be updated as well. Levente > > Still, the server code must be updated, because unfortunately, it returns URLs with hard-coded HTTP, even if the request is made via HTTPS: > > > ( > name 'update-eem.477' > repository ('http://source.squeak.org/trunk') > dependency ('Squeak-Version' 'Squeak-Version-mt.5252' 'b9a3cd5b-b708-8646-a99f-5f3ae294ceb1') > ... > > > I don't have access to the server code, but apparently the protocol should be checked there. And isn't it a general convention to favor relative over absolute URLs? Why doesn't the server simply return '/trunk' here? > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Taeumel, Marcel > Gesendet: Freitag, 12. Juni 2020 16:17 Uhr > An: squeak-dev > Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS? > I suppose it is because the update map has "http" in it for each package version. And maybe also because your repositories in Monticello are configured as "http". So, the mechanism does not find any previous versions? > Seems like bug to me. > > Best, > Marcel > > Am 12.06.2020 15:51:51 schrieb Thiede, Christoph : > > Hi all, > > > I spent a number of attempts during the latest months on changing my image's update URL to use HTTPS, but unfortunately, it gets reset each time. > > > Shouldn't I be able to simply change the update URL in the preferences by adding a small "s" behind the "http"? But if I do so, when I press the update button the next time, Squeak starts loading all packages from > their initial version (Tools-xyz.1, Morphic-xyz.1, ...). Is there a way to easily change the URL? > > > Best, > > Christoph > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 9 21:16:17 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 9 Sep 2020 21:16:17 +0000 Subject: [squeak-dev] Unicode In-Reply-To: References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> , <8DCD60E5-9959-4B5D-9C84-D0AC59405A4C@gmx.de> <37fa8427b38e40fc8c4a2cc61027b2bd@student.hpi.uni-potsdam.de>, Message-ID: <135fcf9b7c4b4e0f83118cd6f1acd8e8@student.hpi.uni-potsdam.de> Hi Levente, basically, I only would like to get rid of the class variables for every single Unicode category because they provide low explorability (it's hard to work with the numeric output of #generalCategoryOf:) and extensibility (you need to recompile the class definition for adding UTF-16 support). If you are critical of increasing the size of the SparseLargeTable, I think we would also just make one or two extra dictionaries to map every category symbol to a number and vice versa. What do you think? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Dienstag, 8. September 2020 21:43:56 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Unicode Hi Christoph, On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > > Your words suggest that it has already been published, but I can't find it anywhere. > > > Then I must have expressed myself wrong. I did not yet publish any code changes, but in my original post from March, you can find a short description of the design changes I'd like to implement. Essentially, I would like to > replace the separate class variables for every known character class in favor of greater flexibility. How would your changes affect GeneralCategory? Would it still be a SpareLargeTable with ByteArray as arrayClass? If you just replace those integers with symbols, the size of the table will be at least 4 or 8 times larger in 32 or 64 bit images, respectively. Levente. > > Eliot, are there any remaining questions regarding the VM size? Character size should be sufficient as discussed below, and of course, I can test any changes in a 32-bit image, too. :-) > > WideString withAll: (#(42r2l 16r1F497 16r1F388 33) collect: #asCharacter) > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Sonntag, 6. September 2020 21:00:14 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Unicode > > > On 06.09.2020, at 20:40, Levente Uzonyi wrote: > > > > On Sun, 6 Sep 2020, Tobias Pape wrote: > > > >> > >>> On 06.09.2020, at 19:15, Eliot Miranda wrote: > >>> Hi Christoph, Hi All, > >>>> On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: > >>>> Hi all! :-) > >>>> After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 > are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, > but so long, I have one general question for you: > >>> And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values). In the 32-bit variant Characters > are 30-bit unsigned integers. In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. > >>> Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant? It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure that > initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. > >>> Q2, how many bits should the 64-bit variant VM support for immediate Characters? > >> > >> Unicode has a max value of 0x10FFFF. That makes 21 bit. So no worries there. > >> > >> We should just not forget the leading-char stuff (Yoshiki, Andreas,...) > > > > AFAIU the leading char only makes sense when you have multiple CJK(V?) languages in use at the same time. In other cases Unicode (leadingChar = 0) is perfectly fine. > > IIRC there are 22 bits available for the codePoint and 8 for the leadingChar, so we're still good: all unicode characters fit. > > > > > > \o/ hooray! > > > Levente > > > >> > >> > >> BEst regards > >> -Tobias > >> > >>> Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to > LargePositiveInteger beyond SmallInteger maxVal. This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. > >>> It has implications in a few parts of the system: > >>> - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances > >>> - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation > >>> - 32-bit <=> 64-bit image conversion All this is easily doable (because we have models of doing it for Float and Integer general instances). But we need good specifications so we can implement the right thing from the > get-go. > >>>> At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different > purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. > >>>> Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, > #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't know > whether this will ever happen). > >>>> Examples: > >>>> Unicode generalTagOf: $a asUnicode. "#Ll" > >>>> Unicode class >> isLetterCode: charCode > >>>> ^ (self generalTagOf: charCode) first = $L > >>>> Unicode class >> isAlphaNumericCode: charCode > >>>> | tag| > >>>> ^ (tag := self generalCategoryOf: charCode) first = $L > >>>> or: [tag = #Nd] > >>>> How do you think about this proposal? Please let me know and I will go ahead! :D > >>>> Best, > >>>> Christoph > >>> Best, Eliot > >>> _,,,^..^,,,_ (phone) > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 9 21:24:03 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 9 Sep 2020 21:24:03 +0000 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <20200909152517.GA13080@shell.msen.com> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com>, <20200909152517.GA13080@shell.msen.com> Message-ID: <1bf3189d9d254d39b0afb0b45511acf8@student.hpi.uni-potsdam.de> Very interesting project! By the way, I am suffering from similar DPI problems on my Windows laptop (the font just does not get large enough). If you can port some of your changes made for PostmarketOS back to the Trunk, this would be great. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Mittwoch, 9. September 2020 17:25:17 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) That looks very promising indeed. Thanks for the video. Dave On Wed, Sep 09, 2020 at 04:14:59PM +0200, Tony Garnock-Jones wrote: > Hi all, > > I've been hacking on Squeak-on-a-cellphone a bit more. Now the image can > scan /proc/bus/input/devices and /dev/input/event* (using FFI, AioPlugin > and OSProcess), and is able to open touchscreen-like devices and set up > additional World HandMorphs for them. > > There's a short (2 minute) demo video linked here: > https://eighty-twenty.org/2020/09/09/squeak-postmarketos-touchscreen > > I think next I might get Squeak to boot and configure the cellular > modem, and get it making calls. > > Alternatively, perhaps an on-screen keyboard? Does anyone know if > anything like that already exists for Squeak? > > Technical details: it uses my Actors library for Squeak, plus some (not > yet released, but only because of laziness) code for the Linux-specific > stuff on the image side. > > It uses a stock Cog VM. > > Cheers, > Tony > > > > > On 8/27/20 8:42 PM, Tony Garnock-Jones wrote: > > Hi all, > > > > I've recently been playing with Squeak running on a PostmarketOS phone > > (Samsung Galaxy S7). > > > > Many thanks to those who recently landed the ARMv8 build of Cog. (Per > > the commit history I think the main person responsible was Ken Dickey? > > Thanks Ken!) > > > > I wrote about progress so far here: > > https://eighty-twenty.org/2020/08/25/postmarketos-smalltalk > > > > Today I posted an update with a saner DPI setting for the phone's > > display: https://eighty-twenty.org/2020/08/27/squeak-postmarketos-update > > > > Next on the list is to read touchscreen input from /dev/input/event1! > > > > Cheers, > > Tony > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 9 21:30:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 9 Sep 2020 21:30:19 +0000 Subject: [squeak-dev] ImmPlugin (was: Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone)) In-Reply-To: <21E704DA-96B1-4188-B0E8-D0420A53607E@rowledge.org> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <1523E318-DB94-4E37-8D4B-0F12E56CF392@gmx.de> , <21E704DA-96B1-4188-B0E8-D0420A53607E@rowledge.org> Message-ID: <1d15c4408cf440bf8183bdc6e0beaff8@student.hpi.uni-potsdam.de> > > there are still traces of a rather ancient #ImmPlugin that was meant to tell the OS where to put that virtual keyboard: > I dont know wether anciet cuts it. It's regularly shipped with the Linux VM and IIRC necessary for Japanese input… The ImmPlugin does not work on Windows 10. This would be helpful for different scenarios: Ultrabooks with a touch keyboard, but also pop-up dialogs for clipboard history or character table, which usually pop up at the caret, just not in Squeak. Oh yes, another cleanup project! :-) Does the current ImmPluginWin32 work for anyone? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von tim Rowledge Gesendet: Mittwoch, 9. September 2020 20:39 Uhr An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) > On 2020-09-09, at 10:10 AM, Marcel Taeumel wrote: > > > ... and IIRC necessary for Japanese input… > > Because you need a virtual keyboard for that case? Hmm.... Kinda; I did some faffing with it for the Scratch/Pi stuff several years ago. I faintly recall there being some really irritating clash between the settings/installs required and... something or other. Sometihng to do with the `-compositionInput` and clashing with unix key handling and 'anthy' and iBus? I don't recall managing to fully solve it. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim To iterate is human; to recurse, divine. -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 9 21:50:31 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 9 Sep 2020 21:50:31 +0000 Subject: [squeak-dev] SyntaxError while loading FFI Message-ID: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> Hi all, I fear this could be a frequently discussed topic, but I did not know where to start else. In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: Metacello new configuration: 'FFI'; load. If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: ffiPrintString: aString "FFITestLibrary ffiPrintString: 'Hello'" "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> ^self externalCallFailed This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) I just wanted to inform you about that. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Thu Sep 10 05:16:30 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 9 Sep 2020 22:16:30 -0700 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: <432f7ec3796847cca1751c771dc2c98c@student.hpi.uni-potsdam.de> References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> <432f7ec3796847cca1751c771dc2c98c@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Tue, Sep 8, 2020 at 1:34 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, > > > thanks for your answer! > > > Just to be sure: What class do you intend to lookup #doesNotUnderstand: for > if the message not understood's lookupClass is ProtoObject superclass > (i.e. nil)? Are you thinking of ProtoObject again? This would break the > existing rule "If a message is sent to super but not understood, send > #doesNotUnderstand: to super as well" (*), or am I misinterpreting this? > Shouldn't you rather signal some special selector in this case? > Right, it seems asymmetric, but one *has* to lookup in the class of the receiver when doing doesNotUndersatnd:. For example, imagine one has a special doesNotUnderstand: method. If a super send causes doesNotUnderstand: but doesn't invoke your special doesNotUnderstand: the system is effectively broken, preventing you from intercepting certain messages. > (*) I'm not sure about this, but I think this results from the Bluebook, > page 589. According to the implementation given there, the VM had to crash > with a recursion error if a nil class is reached while looking up > #doesNotUnderstand:. Because the Bluebook states that the existence of a > #doesNotUnderstand: implementation is guaranteed, I would also find it > reasonable to have the VM crashed if you actually make a call to super from > a method compiled on ProtoObject. > That's still the case today. If one removes the doesNotUnderstand: method, or one creates a detached class hierarchy which doesn't contain a doesNotUnderstand: method then the VM will still abort with a recursive doesNotUnderstand: error. One could add a fall-back, say send a message to thisContext such as object:doesNotUnderstand: . But what's to say one hasn't removed the object:doesNotUnderstand:. Analogous to Gödel's theorem, if the system is powerful enough to modify itself then it is able to destroy itself. Dan Ingalls gave a lovely talk at Xerox PARC a few years ago and Donald Knuth was in the audience. His question to Dan was "With the power to modify the system on the fly, how do you avoid shooting yourself in the foot?". Dan's answer was the same as the Doctor who, when the patient says "Dr, it hurts when I do X", replies "Don't do X". Dan simply said "we tend not to, and we have crash recovery tools when we do." > What do you think? :-) > I think that the major difference between the Blue Book's doesNotUndersatnd: and modern doesNotUnderstand: is that if a Message instance has three inst vars then the VM fills in the third inst var with the class in which the message lookup started, so that now in a doesNotUnderstand: method one can find out if the doesNotUnderstand: was the result of a normal send or a super send, and hence a doesNotUnderstand: method has the necessary info if it in fact wanted to do super doesNotUnderstand: for MNUs resulting from super sends. > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Eliot Miranda > *Gesendet:* Sonntag, 6. September 2020 04:49:54 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] Two new questions about VM machinery :-) > > Hi Christophe, Hi Tobias, > > On Sat, Sep 5, 2020 at 4:10 AM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Hi all, hi Eliot, >> >> >> just for curiosity, I have two new questions about the VM machinery (which >> are far away from praxis but close to my interests): >> >> >> *1.** super on ProtoObject* >> >> What would you expect the following to return? >> >> x := Compiler evaluate: 'super identityHash' for: ProtoObject new. >> >> thisContext objectClass: x. >> >> I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: >> is called. >> > > My guess before looking at the code was that the send provoked a > doesNotUnderstand: and hence the expression returned the result of raising > a MessageNotUnderstood exception. But looking at the code I see that at > least in the JIT there is undefined behavior in looking up a message in a > nil class (the superclass of ProtoObject is nil). So thank you for this. > I'll have to firm up the behaviour to ensure a doesNotUnderstand: is the > result. > > But actually, it answers you the ProtoObject instance, which I find very >> interesting because it means that if a message cannot be looked up, it is >> simply and silently skipped. >> > > Which is a bug. The only reasonable thing to do here (IMO) is for the VM > to send doesNotUnderstnnd:. > > >> >> If you debug the call instead (Context class >> #runSimulated:), Context >> >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense >> but unfortunately is not consistent with the original VM behavior. >> Maybe the Compiler should forbid any call to super from ProtoObject >> instances at all (more formally: if the receiver's class's superclass is >> nil)? Or should we adjust the simulation code? >> (By the way: If you do the same in Squeak.js, you'll get an infinite >> recursion :D) >> >> *2. Mirror primitives* >> What is the reason for primitives such as 75 (#identityHash) or 78/139 >> (#nextInstance/#nextObject) not being mirrored in Context? >> Was this simply forgotten, or don't we have the claim to mirror any >> "essential" primitive without it actually being needed by the simulation >> machinery? >> > > They're not needed by the execution simulation machinery. > > >> As always, looking forward to your interesting explanations! :-) >> > >> >> Best, >> Christoph >> > > Cheers > > _,,,^..^,,,_ > best, Eliot > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From forums.jakob at resfarm.de Thu Sep 10 06:04:51 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Thu, 10 Sep 2020 08:04:51 +0200 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph : > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > From commits at source.squeak.org Thu Sep 10 11:34:18 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 10 Sep 2020 11:34:18 0000 Subject: [squeak-dev] The Inbox: Morphic-stlu.1682.mcz Message-ID: Stephan Lutz uploaded a new version of Morphic to project The Inbox: http://source.squeak.org/inbox/Morphic-stlu.1682.mcz ==================== Summary ==================== Name: Morphic-stlu.1682 Author: stlu Time: 10 September 2020, 1:34:13.030274 pm UUID: c81abdd7-0400-4fe2-bc43-61704bff7fe7 Ancestors: Morphic-mt.1679 Fixes FormCanvas's fast path for pure translations when using MatrixTransform2x3. --- FormCanvas's current fast path for DisplayTransforms that are pure translations calls #offset, a method that is not (formally) part of the DisplayTransform protocol. While all transform types currently in trunk implement it, these implementations differ in their meaning: A) MatrixTransform2x3 >> #offset describes the translation applied to objects transformed by it on the grid B) MorphicTransform >> #offset describes the translation applied to the grid which objects transformed by it are placed on All this ultimately amounts to is a simple negation. However, FormCanvas >> #transformBy:clippingTo:during:smoothing: assumes all display transforms to behave like MorphicTransform, causing MatrixTransform2x3 to translate in the exact opposite direction. Here an example: | formCanvas transform | transform := MatrixTransform2x3 withOffset: -100@ -100. "uncomment line below for not pure translation which should look (almost) identical" "transform := transform composedWithLocal: (MatrixTransform2x3 withScale: 0.99)." formCanvas := FormCanvas extent: 400 at 400. formCanvas fillColor: Color white. formCanvas translateBy: 200 at 200 during: [:canvas | canvas transformBy: transform clippingTo: (-1000@ -1000 corner: 1000 at 1000) during: [:c | c fillRectangle: (0 at 0 extent: 100 at 100) color: Color red]]. formCanvas form asMorph openInHand. BUT since #offset has an entirely different meaning for the two transform types, it is IMO not a good idea to adapt either of its implementations to the other. I would not expect widely accepted matrix terminology to have a different meaning in Squeak. As a result, I currently only see 2 options: 1. Introduce a new selector (e.g. #canvasOffset or #canvasTranslation) 2. Use #localPointToGlobal: I decided to go with the second option, since it reuses the existing interface and is hence automatically compatible with all existing display transforms. Also, while my imagination is failing to think of an example, assuming all transforms can answer to #offset might not be desirable? As for performance impact, the previous implementation is obviously faster, since it only required a quick method accessor. On my machine I benched the following: | transform | transform := MorphicTransform offset: 123@ -456. [transform offset] bench. " '152,000,000 per second. 6.59 nanoseconds per run. 0 % GC time.'" [transform localPointToGlobal: 0 at 0] bench. " '19,500,000 per second. 51.2 nanoseconds per run. 4.79808 % GC time.'" I focused here on the MorphicTransform, since it is the only one actively used in Morphic (to my knowledge). To me this still seems like a rather tame slowdown, especially considering there only a couple hundred Morphs (at worst) calling #transformBy* in a typical Squeak image. =============== Diff against Morphic-mt.1679 =============== Item was changed: ----- Method: FormCanvas>>transformBy:clippingTo:during:smoothing: (in category 'drawing-support') ----- + transformBy: aDisplayTransform clippingTo: aClipRect during: aBlock smoothing: cellSize - transformBy: aDisplayTransform clippingTo: aClipRect during: aBlock smoothing: cellSize "Note: This method has been originally copied from TransformationMorph." | innerRect patchRect sourceQuad warp start subCanvas | + aDisplayTransform isPureTranslation ifTrue: [ + ^ self + translateBy: (aDisplayTransform localPointToGlobal: 0 at 0) truncated + clippingTo: aClipRect + during: aBlock]. - (aDisplayTransform isPureTranslation) ifTrue:[ - ^aBlock value: (self copyOffset: aDisplayTransform offset negated truncated - clipRect: aClipRect) - ]. "Prepare an appropriate warp from patch to innerRect" innerRect := aClipRect. patchRect := (aDisplayTransform globalBoundsToLocal: innerRect) truncated. sourceQuad := (aDisplayTransform sourceQuadFor: innerRect) collect: [:p | p - patchRect topLeft]. warp := self warpFrom: sourceQuad toRect: innerRect. warp cellSize: cellSize. "Render the submorphs visible in the clipping rectangle, as patchForm" start := (self depth = 1 and: [self isShadowDrawing not]) "If this is true B&W, then we need a first pass for erasure." ifTrue: [1] ifFalse: [2]. start to: 2 do: [:i | "If i=1 we first make a shadow and erase it for opaque whites in B&W" subCanvas := self class extent: patchRect extent depth: self depth. i=1 ifTrue: [subCanvas shadowColor: Color black. warp combinationRule: Form erase] ifFalse: [self isShadowDrawing ifTrue: [subCanvas shadowColor: self shadowColor]. warp combinationRule: (self depth = 32 ifTrue: [Form blendAlphaScaled] ifFalse: [Form paint])]. subCanvas translateBy: patchRect topLeft negated during: aBlock. warp sourceForm: subCanvas form; warpBits. warp sourceForm: nil. subCanvas := nil "release space for next loop"] ! From tonyg at leastfixedpoint.com Thu Sep 10 11:41:34 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Thu, 10 Sep 2020 13:41:34 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> Message-ID: <086f1904-d9df-a262-6445-f867a105e63c@leastfixedpoint.com> On 9/9/20 4:28 PM, Marcel Taeumel wrote: > there are still traces of a rather ancient #ImmPlugin that was meant to > tell the OS where to put that virtual keyboard: Thanks! I'll look into that. Of course, here it'd be Squeak providing a keyboard for (itself and) external programs (if any), not the other way around :) Tony From tonyg at leastfixedpoint.com Thu Sep 10 11:44:13 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Thu, 10 Sep 2020 13:44:13 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <1bf3189d9d254d39b0afb0b45511acf8@student.hpi.uni-potsdam.de> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <20200909152517.GA13080@shell.msen.com> <1bf3189d9d254d39b0afb0b45511acf8@student.hpi.uni-potsdam.de> Message-ID: On 9/9/20 11:24 PM, Thiede, Christoph wrote: > By the way, I am suffering from similar DPI problems on my Windows > laptop (the font just does not get large enough). If you can port some > of your changes made for PostmarketOS back to the Trunk, this would be > great. Most of it is already done: the main thing to change image-side is from bitmapped to truetype fonts. I have noticed font rendering with truetype is *much* slower than bitmapped fonts. So perhaps that's why this hasn't already been done? (Regarding DPI detection: my hacks are linux-specific, and involve parsing EDID and reading special files in /sys etc. But once I *have* an estimate of DPI, applying it within the image is straightforward, and I've already committed the small changes I needed back to the trunk.) Tony From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 10 16:33:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 10 Sep 2020 16:33:19 +0000 Subject: [squeak-dev] Bug in TableLayout for #spaceFill + #minExtent? Message-ID: <9c068aee6541479b9ee36bd4479a9510@student.hpi.uni-potsdam.de> Hi all, I think I found a bug in the Morphic TableLayout logic when combining #spaceFill with #minExtent. Please watch the following "minimum failing example": m := Morph new. m height: 100. m changeTableLayout. m addMorph: (Morph new color: Color red; vResizing: #spaceFill). m addMorph: (Morph new minimumHeight: 40; color: Color yellow; vResizing: #spaceFill). m fullBounds. m submorphs collect: #height. Expected output: #(50 50). Actual output: #(69 31). Here is also a pic for increasing the reach of this message: [cid:83a2c9e7-9397-45ad-8749-5df7f2c3b342] Could you please soon confirm to me that this is a bug? In my opinion, this is quite obvious because "min extent" does not make any indications like "Please do not regard my extent while laying me out". Its very only effect should be, as stated by the name, "don't shrink me smaller than this value". This bug gets even more important if you consider that #minExtent cannot only be hard-coded via #minimumExtent but also be computed from a nested table layout that consists of rigid morphs. This way, I became aware of that behavior at all. I already found the origin of the presumable error: TableLayout >> #layoutTopToBottom:in: or #layoutLeftToRight:in: for the other dimension, respectively. But the same behavior also occurs if a #wrapDirection is specified and we don't get into the "fast line" as described #layout:in:. So I wonder whether this might be a conscious design decision rather than a problem by design. Is it possible at all to reach the desired behavior in linear time? I need to check this, but before doing so, your confirmation or objection would be greatly appreciated. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 547 bytes Desc: pastedImage.png URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 10 16:44:49 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 10 Sep 2020 16:44:49 +0000 Subject: [squeak-dev] Bug in TableLayout for #spaceFill + #minExtent? In-Reply-To: <9c068aee6541479b9ee36bd4479a9510@student.hpi.uni-potsdam.de> References: <9c068aee6541479b9ee36bd4479a9510@student.hpi.uni-potsdam.de> Message-ID: In case you agree to call this a bug, I'm attaching a test method to detect the issue. ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Donnerstag, 10. September 2020 18:33:19 An: Squeak Dev Betreff: [squeak-dev] Bug in TableLayout for #spaceFill + #minExtent? Hi all, I think I found a bug in the Morphic TableLayout logic when combining #spaceFill with #minExtent. Please watch the following "minimum failing example": m := Morph new. m height: 100. m changeTableLayout. m addMorph: (Morph new color: Color red; vResizing: #spaceFill). m addMorph: (Morph new minimumHeight: 40; color: Color yellow; vResizing: #spaceFill). m fullBounds. m submorphs collect: #height. Expected output: #(50 50). Actual output: #(69 31). Here is also a pic for increasing the reach of this message: [cid:83a2c9e7-9397-45ad-8749-5df7f2c3b342] Could you please soon confirm to me that this is a bug? In my opinion, this is quite obvious because "min extent" does not make any indications like "Please do not regard my extent while laying me out". Its very only effect should be, as stated by the name, "don't shrink me smaller than this value". This bug gets even more important if you consider that #minExtent cannot only be hard-coded via #minimumExtent but also be computed from a nested table layout that consists of rigid morphs. This way, I became aware of that behavior at all. I already found the origin of the presumable error: TableLayout >> #layoutTopToBottom:in: or #layoutLeftToRight:in: for the other dimension, respectively. But the same behavior also occurs if a #wrapDirection is specified and we don't get into the "fast line" as described #layout:in:. So I wonder whether this might be a conscious design decision rather than a problem by design. Is it possible at all to reach the desired behavior in linear time? I need to check this, but before doing so, your confirmation or objection would be greatly appreciated. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 547 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: TableLayoutTest-testMinimumExtent.st Type: application/octet-stream Size: 1060 bytes Desc: TableLayoutTest-testMinimumExtent.st URL: From Das.Linux at gmx.de Thu Sep 10 17:14:12 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Thu, 10 Sep 2020 19:14:12 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <20200909152517.GA13080@shell.msen.com> <1bf3189d9d254d39b0afb0b45511acf8@student.hpi.uni-potsdam.de> Message-ID: <9DBE82D3-84B8-499C-B25D-2F0A355A073E@gmx.de> > On 10.09.2020, at 13:44, Tony Garnock-Jones wrote: > > > (Regarding DPI detection: my hacks are linux-specific, and involve > parsing EDID and reading special files in /sys etc. But once I *have* an > estimate of DPI, applying it within the image is straightforward, and > I've already committed the small changes I needed back to the trunk.) Can you share them? I am preparing an update of my highdpi-VM branch and would really like to include the Linux stuff (which are, atm, a considerable mess…) Best regards -Tobias From eliot.miranda at gmail.com Thu Sep 10 17:28:55 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Thu, 10 Sep 2020 10:28:55 -0700 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <9DBE82D3-84B8-499C-B25D-2F0A355A073E@gmx.de> References: <9DBE82D3-84B8-499C-B25D-2F0A355A073E@gmx.de> Message-ID: <5F3475A4-2F81-4A5C-883F-5EFFFFD0EE2D@gmail.com> Hi Tobias, > On Sep 10, 2020, at 10:14 AM, Tobias Pape wrote: > >  >> On 10.09.2020, at 13:44, Tony Garnock-Jones wrote: >> >> >> (Regarding DPI detection: my hacks are linux-specific, and involve >> parsing EDID and reading special files in /sys etc. But once I *have* an >> estimate of DPI, applying it within the image is straightforward, and >> I've already committed the small changes I needed back to the trunk.) > > Can you share them? I am preparing an update of my highdpi-VM branch and would really like to include the Linux stuff (which are, atm, a considerable mess…) As part of this will you be looking at the configure script? If so, please reach out to Ken Dickey and interact with him over getting fbdev support detected by the script. > > Best regards > -Tobias > > > From Das.Linux at gmx.de Thu Sep 10 17:49:32 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Thu, 10 Sep 2020 19:49:32 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <5F3475A4-2F81-4A5C-883F-5EFFFFD0EE2D@gmail.com> References: <9DBE82D3-84B8-499C-B25D-2F0A355A073E@gmx.de> <5F3475A4-2F81-4A5C-883F-5EFFFFD0EE2D@gmail.com> Message-ID: > On 10.09.2020, at 19:28, Eliot Miranda wrote: > > Hi Tobias, > >> On Sep 10, 2020, at 10:14 AM, Tobias Pape wrote: >> >>  >>> On 10.09.2020, at 13:44, Tony Garnock-Jones wrote: >>> >>> >>> (Regarding DPI detection: my hacks are linux-specific, and involve >>> parsing EDID and reading special files in /sys etc. But once I *have* an >>> estimate of DPI, applying it within the image is straightforward, and >>> I've already committed the small changes I needed back to the trunk.) >> >> Can you share them? I am preparing an update of my highdpi-VM branch and would really like to include the Linux stuff (which are, atm, a considerable mess…) > > As part of this will you be looking at the configure script? If so, please reach out to Ken Dickey and interact with him over getting fbdev support detected by the script. maaaaaaybe :D I also thought about that. Can't promise a thing but we'll see -T > >> >> Best regards >> -Tobias >> >> >> > From tonyg at leastfixedpoint.com Thu Sep 10 17:53:36 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Thu, 10 Sep 2020 19:53:36 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <9DBE82D3-84B8-499C-B25D-2F0A355A073E@gmx.de> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <20200909152517.GA13080@shell.msen.com> <1bf3189d9d254d39b0afb0b45511acf8@student.hpi.uni-potsdam.de> <9DBE82D3-84B8-499C-B25D-2F0A355A073E@gmx.de> Message-ID: <71127afc-f634-8260-bd18-cdec65e8a5b0@leastfixedpoint.com> Hi Tobias, On 9/10/20 7:14 PM, Tobias Pape wrote: >> (Regarding DPI detection: my hacks are linux-specific, and involve >> parsing EDID and reading special files in /sys etc. But once I *have* an >> estimate of DPI, applying it within the image is straightforward, and >> I've already committed the small changes I needed back to the trunk.) > > Can you share them? I am preparing an update of my highdpi-VM branch and would really like to include the Linux stuff (which are, atm, a considerable mess…) Sure. Here's the current work-in-progress. https://steam.eighty-twenty.org/~tonyg/Monticello/LinuxIO-tonyg.5.mcz For the DPI stuff, look at (the class side of) class LinuxDisplayInfo. Cheers, Tony From marcel.taeumel at hpi.de Thu Sep 10 18:13:55 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 10 Sep 2020 20:13:55 +0200 Subject: [squeak-dev] Bug in TableLayout for #spaceFill + #minExtent? In-Reply-To: References: <9c068aee6541479b9ee36bd4479a9510@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, thanks for this report. Overall, the support for minimum extent in Squeak's layouts is rather ... hmpf. For TableLayout, however, it should work. I will take a look into it. Best, Marcel Am 10.09.2020 18:44:57 schrieb Thiede, Christoph : In case you agree to call this a bug, I'm attaching a test method to detect the issue. [http://www.hpi.de/] Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Donnerstag, 10. September 2020 18:33:19 An: Squeak Dev Betreff: [squeak-dev] Bug in TableLayout for #spaceFill + #minExtent?   Hi all, I think I found a bug in the Morphic TableLayout logic when combining #spaceFill with #minExtent. Please watch the following "minimum failing example": m := Morph new. m height: 100. m changeTableLayout. m addMorph: (Morph new     color: Color red;     vResizing: #spaceFill). m addMorph: (Morph new     minimumHeight: 40;     color: Color yellow;     vResizing: #spaceFill). m fullBounds. m submorphs collect: #height. Expected output: #(50 50). Actual output: #(69 31). Here is also a pic for increasing the reach of this message: Could you please soon confirm to me that this is a bug? In my opinion, this is quite obvious because "min extent" does not make any indications like "Please do not regard my extent while laying me out". Its very only effect should be, as stated by the name, "don't shrink me smaller than this value". This bug gets even more important if you consider that #minExtent cannot only be hard-coded via #minimumExtent but also be computed from a nested table layout that consists of rigid morphs. This way, I became aware of that behavior at all. I already found the origin of the presumable error: TableLayout >> #layoutTopToBottom:in: or #layoutLeftToRight:in: for the other dimension, respectively. But the same behavior also occurs if a #wrapDirection is specified and we don't get into the "fast line" as described #layout:in:. So I wonder whether this might be a conscious design decision rather than a problem by design. Is it possible at all to reach the desired behavior in linear time? I need to check this, but before doing so, your confirmation or objection would be greatly appreciated. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 547 bytes Desc: not available URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 10 18:32:27 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 10 Sep 2020 18:32:27 +0000 Subject: [squeak-dev] FormInspector, or also: Text>>#= and its consequences Message-ID: <10d552ed992347e0ba801c1423dada6b@student.hpi.uni-potsdam.de> Hi all, is there any old thread about the design discussion of how Text>>#= works? (It does not consider attributes for quality.) Has this decision ever been questioned? Naively and without an overview of any existing components that could rely on this implementation, I would like to question it. Why should 'foo' asText allBold be equal to 'foo' asText addAttribute: TextURL new? With the same logic, we could also say that two dictionaries are equal iff they have got the same keys ... There is even a concrete client in the Trunk suffering from this design decision: Marcel's new FormInspector (and analogously, MorphInspector). It uses TextFontReference with a FormSetFont to display a screenshot right in the inspector pane. Unfortunately, the pane is never updated automatically because even if the screenshot changes, the text morph thinks the old text would equal the new one. I'd like to fix that without hacking any workaround into the inspectors. Even though this inspector implementation is a bit unusual, in my opinion, it shows that the current Text >> #= implementation might not be a perfect solution. I'm looking forward to your opinions. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From vanessa at codefrau.net Thu Sep 10 20:23:37 2020 From: vanessa at codefrau.net (Vanessa Freudenberg) Date: Thu, 10 Sep 2020 13:23:37 -0700 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> Message-ID: On Wed, Sep 9, 2020 at 7:15 AM Tony Garnock-Jones wrote: > Hi all, > > I've been hacking on Squeak-on-a-cellphone a bit more. Now the image can > scan /proc/bus/input/devices and /dev/input/event* (using FFI, AioPlugin > and OSProcess), and is able to open touchscreen-like devices and set up > additional World HandMorphs for them. > Oh, exciting! Please record a multi-hand Etoys demo. Maybe like this: https://youtu.be/gYrp31fH-Jk?t=56 - Vanessa - -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Thu Sep 10 22:28:57 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Fri, 11 Sep 2020 00:28:57 +0200 (CEST) Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de>, <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de>, Message-ID: Hi Christoph, On Wed, 9 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > Levente, thanks for the improvements! I attempted to only patch as much as required for updating the image via the docking bar menu or the Monticello Browser. They are not necessarily improvements, just what I came up with when I tried to tackle the problem. > > Why aren't you using 'MCHttpRepository allInstancesDo:' in your version? That's probably better if you have repositories outside of the default group. > > And I used the Compiler in my version in order not to change the DefaultUpdateURL class variable if it was not set before. There's no need for that. #defaultUpdateURL will not change DefaultUpdateURL's value. > > > > That would mean, changing the update maps. > +1 for doing so. Could there be any possible problems? Environments that do not support SSL today? Possible performance problems? If you apply your patch before the new update map is in place, the http repositires will come back during the next update. If the update map with the https repositories is in place before the patch is applied, the repositories will duplicate: the update map will create new https repositores on update, then the patch will try to convert the http repositories to https. (AFAIU) Levente > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Levente Uzonyi > Gesendet: Mittwoch, 9. September 2020 10:57:08 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS?   > Hi Christoph, > > On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > > > > Hi Marcel, hi all, > > > > > > phew, this turned out to be a bit more complicated than I had assumed. > > > > > > The attached script should patch every in the image required to upgrade the update URLs to HTTPS. Eventually, I would like to ship it via a postscript in a regular trunk version. > > I just checked your script. It's behavior seems to match what I wrote a > while ago. My variant changes all source.squeak.org urls to https: > > MCMcmUpdater allInstances >          select: [ :each | each repository beginsWith: 'http://source.squeak.org' ] >          thenDo: [ :httpUpdater | >                  | httpsUrl newUpdater | >                  httpsUrl := 'https', (httpUpdater repository allButFirst: 4). >                  newUpdater := MCMcmUpdater >                          repository: httpsUrl >                          updateMap: httpUpdater updateMapName >                          lastUpdateMap: (Dictionary new >                                  at: httpsUrl >                                          put: (httpUpdater lastUpdateMap at: httpUpdater repository); >                                  yourself). >                  newUpdater register. >                  httpUpdater unregister ]. > MCRepositoryGroup default repositories >          select: [ :each | >                  (each isKindOf: MCHttpRepository) >                          and: [ each description beginsWith: 'http://source.squeak.org' ]] >          thenDo: [ :repository | >                  repository location: 'https', (repository description allButFirst: 4) ]. > MCMcmUpdater defaultUpdateURL in: [ :updateUrl | >          (updateUrl beginsWith: 'http://source.squeak.org') ifTrue: [ >                  MCMcmUpdater defaultUpdateURL: 'https', (updateUrl allButFirst: 4) ] ] > > > However, such scripts on their own are not enough. Besides the update > maps, the image has several references to the http urls. Most of those > need to be updated as well. > > > Levente > > > > > Still, the server code must be updated, because unfortunately, it returns URLs with hard-coded HTTP, even if the request is made via HTTPS: > > > > > >       ( > > name 'update-eem.477' > > repository ('http://source.squeak.org/trunk') > > dependency ('Squeak-Version' 'Squeak-Version-mt.5252' 'b9a3cd5b-b708-8646-a99f-5f3ae294ceb1') > > ... > > > > > > I don't have access to the server code, but apparently the protocol should be checked there. And isn't it a general convention to favor relative over absolute URLs? Why doesn't the server simply return '/trunk' here? > > > > Best, > > Christoph > > > >________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > _ > > Von: Squeak-dev im Auftrag von Taeumel, Marcel > > Gesendet: Freitag, 12. Juni 2020 16:17 Uhr > > An: squeak-dev > > Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS?   > > I suppose it is because the update map has "http" in it for each package version. And maybe also because your repositories in Monticello are configured as "http". So, the mechanism does not find any previous versions? > > Seems like bug to me. > > > > Best, > > Marcel > > > >       Am 12.06.2020 15:51:51 schrieb Thiede, Christoph : > > > >       Hi all, > > > > > >       I spent a number of attempts during the latest months on changing my image's update URL to use HTTPS, but unfortunately, it gets reset each time. > > > > > >       Shouldn't I be able to simply change the update URL in the preferences by adding a small "s" behind the "http"? But if I do so, when I press the update button the next time, Squeak starts loading all packages from > >       their initial version (Tools-xyz.1, Morphic-xyz.1, ...). Is there a way to easily change the URL? > > > > > >       Best, > > > >       Christoph > > > > > > > > From leves at caesar.elte.hu Thu Sep 10 22:49:24 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Fri, 11 Sep 2020 00:49:24 +0200 (CEST) Subject: [squeak-dev] Unicode In-Reply-To: <135fcf9b7c4b4e0f83118cd6f1acd8e8@student.hpi.uni-potsdam.de> References: <192180aab2aa4ca3bdf7728d9481f2ac@student.hpi.uni-potsdam.de> , <8DCD60E5-9959-4B5D-9C84-D0AC59405A4C@gmx.de> <37fa8427b38e40fc8c4a2cc61027b2bd@student.hpi.uni-potsdam.de>, <135fcf9b7c4b4e0f83118cd6f1acd8e8@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Wed, 9 Sep 2020, Thiede, Christoph wrote: > > Hi Levente, > > > basically, I only would like to get rid of the class variables for every single Unicode category because they provide low explorability (it's hard to work with the numeric output of #generalCategoryOf:) and extensibility (you > need to recompile the class definition for adding UTF-16 support). If you are critical of increasing the size of the SparseLargeTable, I think we would also just make one or two extra dictionaries to map every category symbol > to a number and vice versa. What do you think? You mean an array to map the integers to symbols, right? :) Anyway, I don't think it's worth using symbols internally. For example, #isLetterCode: is 8-10% slower with the extra array lookup and checking the category symbol's first letter than the current method of integer comparisons. Do you expect these constants to appear outside the Unicode class? If yes, then using symbols for those cases is probably a good solution. But for internal use, the integers are better. Levente > > > Best, > > Christoph > > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Levente Uzonyi > Gesendet: Dienstag, 8. September 2020 21:43:56 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Unicode   > Hi Christoph, > > On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > > > > Hi all, > > > > > > > Your words suggest that it has already been published, but I can't find it anywhere. > > > > > > Then I must have expressed myself wrong. I did not yet publish any code changes, but in my original post from March, you can find a short description of the design changes I'd like to implement. Essentially, I would like to > > replace the separate class variables for every known character class in favor of greater flexibility. > > How would your changes affect GeneralCategory? Would it still be a > SpareLargeTable with ByteArray as arrayClass? > If you just replace those integers with symbols, the size of the table > will be at least 4 or 8 times larger in 32 or 64 bit images, > respectively. > > > Levente. > > > > > Eliot, are there any remaining questions regarding the VM size? Character size should be sufficient as discussed below, and of course, I can test any changes in a 32-bit image, too. :-) > > > > WideString withAll: (#(42r2l 16r1F497 16r1F388 33) collect: #asCharacter) > > > > Best, > > Christoph > > > >________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > _ > > Von: Squeak-dev im Auftrag von Tobias Pape > > Gesendet: Sonntag, 6. September 2020 21:00:14 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Unicode   > > > > > On 06.09.2020, at 20:40, Levente Uzonyi wrote: > > > > > > On Sun, 6 Sep 2020, Tobias Pape wrote: > > > > > >> > > >>> On 06.09.2020, at 19:15, Eliot Miranda wrote: > > >>> Hi Christoph, Hi All, > > >>>> On Mar 17, 2020, at 3:51 PM, Thiede, Christoph wrote: > > >>>> Hi all! :-) > > >>>> After some recent fun with the Unicode class, I found out that its data is quite out of date (for example, the comments do not even "know" that code points can be longer than 4 bytes. Younger characters such as 😺❤🤓 > > are not categorized correctly, etc. ...). Luckily, there is already some logic to fetch the latest data from www.unicode.org. I'm currently reworking this logic because it's not completely automated yet and has some slips, > > but so long, I have one general question for you: > > >>> And consequently I have a couple of questions for you. In the Spur VM Characters are immediate (they are like SmallInteger and exist in oops (object-oriented pointers) as tagged values).  In the 32-bit variant > Characters > > are 30-bit unsigned integers.  In the 64-bit variant they are also 30-bit unsigned integers, but could easily be extended to be up to 61-bit unsigned integers. > > >>> Q1, can you arrange that the Unicode support does not break in initialization on the 32-bit variant?  It may be that the 32-bit variant cannot represent code points beyond 30 bits in size, but we should try to ensure > that > > initialization still runs to completion even if it fails to initialize information relating to code points beyond 30 bits in size. > > >>> Q2, how many bits should the 64-bit variant VM support for immediate Characters? > > >> > > >> Unicode has a max value of 0x10FFFF. That makes 21 bit. So no worries there. > > >> > > >> We should just not forget the leading-char stuff (Yoshiki, Andreas,...) > > > > > > AFAIU the leading char only makes sense when you have multiple CJK(V?) languages in use at the same time. In other cases Unicode (leadingChar = 0) is perfectly fine. > > > IIRC there are 22 bits available for the codePoint and 8 for the leadingChar, so we're still good: all unicode characters fit. > > > > > > > > > > \o/ hooray! > > > > > Levente > > > > > >> > > >> > > >> BEst regards > > >>       -Tobias > > >> > > >>> Then something to consider is that it is conceptually possible to support something like WideCharacter, which would represent code points outside of the immediate Character range on the 32-bit variant, analogous to > > LargePositiveInteger beyond SmallInteger maxVal.  This can be made to work seamlessly, just as it does currently with integers, and with Floats where SmallFloat64 is only used on 64-bits. > > >>> It has implications in a few parts of the system: > > >>> - failure code for WideString (VeryWideString?) at:[put:] primitives that would have to manage overflow into/access from WideCharacter instances > > >>> - ImageSegment and other (un)pickling systems that need to convert to/from a bit-specific “wire” protocol/representation > > >>> - 32-bit <=> 64-bit image conversion All this is easily doable (because we have models of doing it for Float and Integer general instances).  But we need good specifications so we can implement the right thing from the > > get-go. > > >>>> At the moment, we have 30 class variables each for one Unicode category number. These class vars map in alphabetical order to the integers from 0 to: 29. Is this tedious structure really necessary? For different > > purposes, I would like to get the category name of a specific code point from a client. The current design makes this impossible without writing additional mappings. > > >>>> Tl;dr: I would like to propose to drop these class variables and use Symbols instead. They are comparable like integers, and as they are flyweights, this should not be a performance issue either. Of course, > > #generalCategoryOf: will have to keep returning numbers, but we could deprecate it and use a new #generalTagOf: in the future. Furthermore, this would also allow us to deal with later added category names (though I don't > know > > whether this will ever happen). > > >>>> Examples: > > >>>> Unicode generalTagOf: $a asUnicode. "#Ll" > > >>>> Unicode class >> isLetterCode: charCode > > >>>>  ^ (self generalTagOf: charCode) first = $L > > >>>> Unicode class >> isAlphaNumericCode: charCode > > >>>>  | tag| > > >>>>  ^ (tag := self generalCategoryOf: charCode) first = $L > > >>>>        or: [tag = #Nd] > > >>>> How do you think about this proposal? Please let me know and I will go ahead! :D > > >>>> Best, > > >>>> Christoph > > >>> Best, Eliot > > >>> _,,,^..^,,,_ (phone) > > > > > > > > > > > > From Yoshiki.Ohshima at acm.org Thu Sep 10 22:53:55 2020 From: Yoshiki.Ohshima at acm.org (Yoshiki Ohshima) Date: Thu, 10 Sep 2020 15:53:55 -0700 Subject: [squeak-dev] ImmPlugin (was: Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone)) In-Reply-To: <1d15c4408cf440bf8183bdc6e0beaff8@student.hpi.uni-potsdam.de> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <1523E318-DB94-4E37-8D4B-0F12E56CF392@gmx.de> <21E704DA-96B1-4188-B0E8-D0420A53607E@rowledge.org> <1d15c4408cf440bf8183bdc6e0beaff8@student.hpi.uni-potsdam.de> Message-ID: On Wed, Sep 9, 2020 at 2:30 PM Thiede, Christoph wrote: > > > > there are still traces of a rather ancient #ImmPlugin that was meant to tell the OS where to put that virtual keyboard: > > I dont know wether anciet cuts it. It's regularly shipped with the Linux VM and IIRC necessary for Japanese input… > > The ImmPlugin does not work on Windows 10. This would be helpful for different scenarios: Ultrabooks with a touch keyboard, but also pop-up dialogs for clipboard history or character table, which usually pop up at the caret, just not in Squeak. > Oh yes, another cleanup project! :-) Does the current ImmPluginWin32 work for anyone? If I remember correctly, The plugin for Windows was written first and then X11 version. The internet probably has the source code of it somewhere, I would guess... > > Best, > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von tim Rowledge > Gesendet: Mittwoch, 9. September 2020 20:39 Uhr > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) > > > > > On 2020-09-09, at 10:10 AM, Marcel Taeumel wrote: > > > > > ... and IIRC necessary for Japanese input… > > > > Because you need a virtual keyboard for that case? Hmm.... > > Kinda; I did some faffing with it for the Scratch/Pi stuff several years ago. I faintly recall there being some really irritating clash between the settings/installs required and... something or other. Sometihng to do with the `-compositionInput` and clashing with unix key handling and 'anthy' and iBus? > > I don't recall managing to fully solve it. > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > To iterate is human; to recurse, divine. > > > > -- -- Yoshiki From commits at source.squeak.org Fri Sep 11 02:30:05 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 11 Sep 2020 02:30:05 0000 Subject: [squeak-dev] The Trunk: Chronology-Tests-dtl.24.mcz Message-ID: David T. Lewis uploaded a new version of Chronology-Tests to project The Trunk: http://source.squeak.org/trunk/Chronology-Tests-dtl.24.mcz ==================== Summary ==================== Name: Chronology-Tests-dtl.24 Author: dtl Time: 8 September 2020, 7:41:58.606243 pm UUID: b399c1e6-4dfe-4240-b44e-19ec5abf3a74 Ancestors: Chronology-Tests-mt.23 Add tests for Time>>addSeconds: to ensure that nanosecond values are preserved. =============== Diff against Chronology-Tests-mt.23 =============== Item was added: + ----- Method: TimeTest>>testAddSecondsNanoSecondRollover (in category 'testing') ----- + testAddSecondsNanoSecondRollover + + | time1 time2 time3 time4 time5 time6 time7 time8 | + time1 := Time fromSeconds: 1.2s. + time2 := time1 addSeconds: 1.3s. + "n.b. use ticks second to access seconds instvar because #asSeconds is + questionable and possibly subject to future change -dtl" + self assert: 2 equals: time2 ticks second. + self assert: 500000000 equals: time2 nanoSecond. + time3 := Time fromSeconds: 1.9s. + time4 := time3 addSeconds: 1.2s. + self assert: 3 equals: time4 ticks second. + self assert: 100000000 equals: time4 nanoSecond. + time5 := Time fromSeconds: 0.9s. + time6 := time5 addSeconds: 0.2s. + self assert: 1 equals: time6 ticks second. + self assert: 100000000 equals: time6 nanoSecond. + time7 := time5 addSeconds: 1. + self assert: 1 equals: time7 ticks second. + self assert: 900000000 equals: time7 nanoSecond. + "midnight rollover" + time8 := '11:59:59.505 pm' asTime addSeconds: 1.0101. + self assert: 0 equals: time8 ticks second. + self assert: 515100000 equals: time8 nanoSecond. + ! Item was added: + ----- Method: TimeTest>>testAddSecondsWithNanos (in category 'testing') ----- + testAddSecondsWithNanos + self assert: (timeWithNanos addSeconds: 1) = (Time readFromString: '4:02:48.42 am'). + self assert: (timeWithNanos addSeconds: 60) = (Time readFromString: '4:03:47.42 am'). + self assert: (timeWithNanos addSeconds: 3600) = (Time readFromString: '5:02:47.42 am'). + self assert: (timeWithNanos addSeconds: 24*60*60) = (Time readFromString: '4:02:47.42 am'). + "rollover after midnight" + self assert: (timeWithNanos addSeconds: 71832) = (Time readFromString: '11:59:59.42 pm'). + self assert: (timeWithNanos addSeconds: 71833) = (Time readFromString: '00:00:00.42 am'). + ! From commits at source.squeak.org Fri Sep 11 02:30:12 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 11 Sep 2020 02:30:12 0000 Subject: [squeak-dev] The Trunk: Chronology-Core-dtl.60.mcz Message-ID: David T. Lewis uploaded a new version of Chronology-Core to project The Trunk: http://source.squeak.org/trunk/Chronology-Core-dtl.60.mcz ==================== Summary ==================== Name: Chronology-Core-dtl.60 Author: dtl Time: 8 September 2020, 7:42:44.062725 pm UUID: afcc86c3-7fb2-4ecd-b9ae-9840f100393c Ancestors: Chronology-Core-ul.58 Fix Time>>addSeconds: to maintain nanoseconds. =============== Diff against Chronology-Core-ul.58 =============== Item was changed: ----- Method: Time>>addSeconds: (in category 'smalltalk-80') ----- addSeconds: nSeconds "Answer a Time that is nSeconds after the receiver." + | secondsToAdd newNanos | + (secondsToAdd := nSeconds truncated) = nSeconds + ifTrue: [newNanos := nanos] + ifFalse: [(newNanos := nanos + (nSeconds - secondsToAdd * NanosInSecond)) > NanosInSecond + ifTrue: [secondsToAdd := secondsToAdd + 1. + newNanos := newNanos - NanosInSecond]]. + ^ self class seconds: seconds + secondsToAdd nanoSeconds: newNanos! - ^ self class seconds: self asSeconds + nSeconds! From Das.Linux at gmx.de Fri Sep 11 06:18:17 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Fri, 11 Sep 2020 08:18:17 +0200 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de> <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> Message-ID: <40068F67-68F6-468A-B80E-13C9DA475E70@gmx.de> > On 09.09.2020, at 22:56, Thiede, Christoph wrote: > > Hi all, > > Levente, thanks for the improvements! I attempted to only patch as much as required for updating the image via the docking bar menu or the Monticello Browser. > Why aren't you using 'MCHttpRepository allInstancesDo:' in your version? > And I used the Compiler in my version in order not to change the DefaultUpdateURL class variable if it was not set before. > > > That would mean, changing the update maps. > > +1 for doing so. Could there be any possible problems? Environments that do not support SSL today? Possible performance problems? Our SSL support is lacking. No actual Cert checking on Unix. Nobody outside HPI seriously tested the environment. Thats about it. -t From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 11 10:23:57 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 11 Sep 2020 10:23:57 +0000 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: <40068F67-68F6-468A-B80E-13C9DA475E70@gmx.de> References: <0955d0f3023041349cf219702472e210@student.hpi.uni-potsdam.de> <371d0321d3ae4603909851eb4d88c4d4@student.hpi.uni-potsdam.de> , <40068F67-68F6-468A-B80E-13C9DA475E70@gmx.de> Message-ID: <299151de165e404c89a250abcdf36f27@student.hpi.uni-potsdam.de> Hi all, sounds as if the Squeak world would not yet be ready to migrate completely to HTTPS? Maybe a hybrid solution (i.e. relative URLs in the update map) could indeed be a better solution? > There's no need for that. #defaultUpdateURL will not change DefaultUpdateURL's value. Yes, it won't, but #defaultUpdateURL: will do so. > Nobody outside HPI seriously tested the environment. How could such a test look like? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Freitag, 11. September 2020 08:18:17 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS? > On 09.09.2020, at 22:56, Thiede, Christoph wrote: > > Hi all, > > Levente, thanks for the improvements! I attempted to only patch as much as required for updating the image via the docking bar menu or the Monticello Browser. > Why aren't you using 'MCHttpRepository allInstancesDo:' in your version? > And I used the Compiler in my version in order not to change the DefaultUpdateURL class variable if it was not set before. > > > That would mean, changing the update maps. > > +1 for doing so. Could there be any possible problems? Environments that do not support SSL today? Possible performance problems? Our SSL support is lacking. No actual Cert checking on Unix. Nobody outside HPI seriously tested the environment. Thats about it. -t -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 11 10:25:54 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 11 Sep 2020 10:25:54 +0000 Subject: [squeak-dev] ImmPlugin (was: Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone)) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <1523E318-DB94-4E37-8D4B-0F12E56CF392@gmx.de> <21E704DA-96B1-4188-B0E8-D0420A53607E@rowledge.org> <1d15c4408cf440bf8183bdc6e0beaff8@student.hpi.uni-potsdam.de>, Message-ID: I would also not exclude that Microsoft has established a new API for this since XP? Vista? 7?. ________________________________ Von: Squeak-dev im Auftrag von Yoshiki Ohshima Gesendet: Freitag, 11. September 2020 00:53:55 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] ImmPlugin (was: Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone)) On Wed, Sep 9, 2020 at 2:30 PM Thiede, Christoph wrote: > > > > there are still traces of a rather ancient #ImmPlugin that was meant to tell the OS where to put that virtual keyboard: > > I dont know wether anciet cuts it. It's regularly shipped with the Linux VM and IIRC necessary for Japanese input… > > The ImmPlugin does not work on Windows 10. This would be helpful for different scenarios: Ultrabooks with a touch keyboard, but also pop-up dialogs for clipboard history or character table, which usually pop up at the caret, just not in Squeak. > Oh yes, another cleanup project! :-) Does the current ImmPluginWin32 work for anyone? If I remember correctly, The plugin for Windows was written first and then X11 version. The internet probably has the source code of it somewhere, I would guess... > > Best, > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von tim Rowledge > Gesendet: Mittwoch, 9. September 2020 20:39 Uhr > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) > > > > > On 2020-09-09, at 10:10 AM, Marcel Taeumel wrote: > > > > > ... and IIRC necessary for Japanese input… > > > > Because you need a virtual keyboard for that case? Hmm.... > > Kinda; I did some faffing with it for the Scratch/Pi stuff several years ago. I faintly recall there being some really irritating clash between the settings/installs required and... something or other. Sometihng to do with the `-compositionInput` and clashing with unix key handling and 'anthy' and iBus? > > I don't recall managing to fully solve it. > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > To iterate is human; to recurse, divine. > > > > -- -- Yoshiki -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 11 10:31:37 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 11 Sep 2020 10:31:37 +0000 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> <432f7ec3796847cca1751c771dc2c98c@student.hpi.uni-potsdam.de>, Message-ID: Hi Eliot, this sounds very convincing. If you have patched this in the VM, it would be great if you could send me a link to the relevant patch. :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Donnerstag, 10. September 2020 07:16:30 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) Hi Christoph, On Tue, Sep 8, 2020 at 1:34 AM Thiede, Christoph > wrote: Hi Eliot, thanks for your answer! Just to be sure: What class do you intend to lookup #doesNotUnderstand: for if the message not understood's lookupClass is ProtoObject superclass (i.e. nil)? Are you thinking of ProtoObject again? This would break the existing rule "If a message is sent to super but not understood, send #doesNotUnderstand: to super as well" (*), or am I misinterpreting this? Shouldn't you rather signal some special selector in this case? Right, it seems asymmetric, but one *has* to lookup in the class of the receiver when doing doesNotUndersatnd:. For example, imagine one has a special doesNotUnderstand: method. If a super send causes doesNotUnderstand: but doesn't invoke your special doesNotUnderstand: the system is effectively broken, preventing you from intercepting certain messages. (*) I'm not sure about this, but I think this results from the Bluebook, page 589. According to the implementation given there, the VM had to crash with a recursion error if a nil class is reached while looking up #doesNotUnderstand:. Because the Bluebook states that the existence of a #doesNotUnderstand: implementation is guaranteed, I would also find it reasonable to have the VM crashed if you actually make a call to super from a method compiled on ProtoObject. That's still the case today. If one removes the doesNotUnderstand: method, or one creates a detached class hierarchy which doesn't contain a doesNotUnderstand: method then the VM will still abort with a recursive doesNotUnderstand: error. One could add a fall-back, say send a message to thisContext such as object:doesNotUnderstand: . But what's to say one hasn't removed the object:doesNotUnderstand:. Analogous to Gödel's theorem, if the system is powerful enough to modify itself then it is able to destroy itself. Dan Ingalls gave a lovely talk at Xerox PARC a few years ago and Donald Knuth was in the audience. His question to Dan was "With the power to modify the system on the fly, how do you avoid shooting yourself in the foot?". Dan's answer was the same as the Doctor who, when the patient says "Dr, it hurts when I do X", replies "Don't do X". Dan simply said "we tend not to, and we have crash recovery tools when we do." What do you think? :-) I think that the major difference between the Blue Book's doesNotUndersatnd: and modern doesNotUnderstand: is that if a Message instance has three inst vars then the VM fills in the third inst var with the class in which the message lookup started, so that now in a doesNotUnderstand: method one can find out if the doesNotUnderstand: was the result of a normal send or a super send, and hence a doesNotUnderstand: method has the necessary info if it in fact wanted to do super doesNotUnderstand: for MNUs resulting from super sends. Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Eliot Miranda > Gesendet: Sonntag, 6. September 2020 04:49:54 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) Hi Christophe, Hi Tobias, On Sat, Sep 5, 2020 at 4:10 AM Thiede, Christoph > wrote: Hi all, hi Eliot, just for curiosity, I have two new questions about the VM machinery (which are far away from praxis but close to my interests): 1. super on ProtoObject What would you expect the following to return? x := Compiler evaluate: 'super identityHash' for: ProtoObject new. thisContext objectClass: x. I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. My guess before looking at the code was that the send provoked a doesNotUnderstand: and hence the expression returned the result of raising a MessageNotUnderstood exception. But looking at the code I see that at least in the JIT there is undefined behavior in looking up a message in a nil class (the superclass of ProtoObject is nil). So thank you for this. I'll have to firm up the behaviour to ensure a doesNotUnderstand: is the result. But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. Which is a bug. The only reasonable thing to do here (IMO) is for the VM to send doesNotUnderstnnd:. If you debug the call instead (Context class >> #runSimulated:), Context >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense but unfortunately is not consistent with the original VM behavior. Maybe the Compiler should forbid any call to super from ProtoObject instances at all (more formally: if the receiver's class's superclass is nil)? Or should we adjust the simulation code? (By the way: If you do the same in Squeak.js, you'll get an infinite recursion :D) 2. Mirror primitives What is the reason for primitives such as 75 (#identityHash) or 78/139 (#nextInstance/#nextObject) not being mirrored in Context? Was this simply forgotten, or don't we have the claim to mirror any "essential" primitive without it actually being needed by the simulation machinery? They're not needed by the execution simulation machinery. As always, looking forward to your interesting explanations! :-) Best, Christoph Cheers _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Fri Sep 11 13:42:36 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Fri, 11 Sep 2020 06:42:36 -0700 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: <299151de165e404c89a250abcdf36f27@student.hpi.uni-potsdam.de> References: <299151de165e404c89a250abcdf36f27@student.hpi.uni-potsdam.de> Message-ID: Hi Levente, Hi Tobias, Hi Vanessa, would it be possible to layer in a hack to avoid having to edit the URLs? At some point in Monticello update a http connection is made. If at that point there is a url translation dictionary (a map) then a http url could be mapped selectively to an https one. If there is no entry in the map no translation occurs. Or does that break things upstream? _,,,^..^,,,_ (phone) > On Sep 11, 2020, at 3:24 AM, Thiede, Christoph wrote: > >  > Hi all, > > > > sounds as if the Squeak world would not yet be ready to migrate completely to HTTPS? Maybe a hybrid solution (i.e. relative URLs in the update map) could indeed be a better solution? > > > > > There's no need for that. #defaultUpdateURL will not change DefaultUpdateURL's value. > > > > Yes, it won't, but #defaultUpdateURL: will do so. > > > > > Nobody outside HPI seriously tested the environment. > > > How could such a test look like? > > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Freitag, 11. September 2020 08:18:17 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS? > > > > On 09.09.2020, at 22:56, Thiede, Christoph wrote: > > > > Hi all, > > > > Levente, thanks for the improvements! I attempted to only patch as much as required for updating the image via the docking bar menu or the Monticello Browser. > > Why aren't you using 'MCHttpRepository allInstancesDo:' in your version? > > And I used the Compiler in my version in order not to change the DefaultUpdateURL class variable if it was not set before. > > > > > That would mean, changing the update maps. > > > > +1 for doing so. Could there be any possible problems? Environments that do not support SSL today? Possible performance problems? > > Our SSL support is lacking. > No actual Cert checking on Unix. > Nobody outside HPI seriously tested the environment. > > Thats about it. > -t > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Fri Sep 11 13:46:35 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Fri, 11 Sep 2020 06:46:35 -0700 Subject: [squeak-dev] How to change the update URL for Trunk HTTPS? In-Reply-To: References: Message-ID: > On Sep 11, 2020, at 6:42 AM, Eliot Miranda wrote: > > Hi Levente, Hi Tobias, Hi Vanessa, > > would it be possible to layer in a hack to avoid having to edit the URLs? At some point in Monticello update a http connection is made. If at that point there is a url translation dictionary (a map) then a http url could be mapped selectively to an https one. If there is no entry in the map no translation occurs. Or does that break things upstream? One big advantage of a map scheme is that it can be left unused on platforms where SSL support isn’t working properly. > > _,,,^..^,,,_ (phone) > >>> On Sep 11, 2020, at 3:24 AM, Thiede, Christoph wrote: >>> >>  >> Hi all, >> >> >> >> sounds as if the Squeak world would not yet be ready to migrate completely to HTTPS? Maybe a hybrid solution (i.e. relative URLs in the update map) could indeed be a better solution? >> >> >> >> > There's no need for that. #defaultUpdateURL will not change DefaultUpdateURL's value. >> >> >> >> Yes, it won't, but #defaultUpdateURL: will do so. >> >> >> >> > Nobody outside HPI seriously tested the environment. >> >> >> How could such a test look like? >> >> Best, >> Christoph >> Von: Squeak-dev im Auftrag von Tobias Pape >> Gesendet: Freitag, 11. September 2020 08:18:17 >> An: The general-purpose Squeak developers list >> Betreff: Re: [squeak-dev] How to change the update URL for Trunk HTTPS? >> >> >> > On 09.09.2020, at 22:56, Thiede, Christoph wrote: >> > >> > Hi all, >> > >> > Levente, thanks for the improvements! I attempted to only patch as much as required for updating the image via the docking bar menu or the Monticello Browser. >> > Why aren't you using 'MCHttpRepository allInstancesDo:' in your version? >> > And I used the Compiler in my version in order not to change the DefaultUpdateURL class variable if it was not set before. >> > >> > > That would mean, changing the update maps. >> > >> > +1 for doing so. Could there be any possible problems? Environments that do not support SSL today? Possible performance problems? >> >> Our SSL support is lacking. >> No actual Cert checking on Unix. >> Nobody outside HPI seriously tested the environment. >> >> Thats about it. >> -t >> >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From tonyg at leastfixedpoint.com Fri Sep 11 15:04:43 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Fri, 11 Sep 2020 17:04:43 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> Message-ID: <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> Hi Vanessa, On 9/10/20 10:23 PM, Vanessa Freudenberg wrote: > Oh, exciting! Please record a multi-hand Etoys demo. Maybe like this: > https://youtu.be/gYrp31fH-Jk?t=56 Whoa, awesome! I didn't know this existed! I have questions! - How much of that shift-to-get-other-colour-buttons support is still in the image? So far I haven't touched the default world menu bar, which is mildly inappropriate for small and/or multitouch devices. - What other ideas about mapping multitouch to Morphic interaction are out there I have missed and am in danger of reinventing? Also, I am intrigued by the idea of simply having each touch be mapped to a Hand. It seems like the obvious way to do things, on one level, but doesn't quite fit my fuzzy intuitions for how (multi-finger) gesture recognition might work on another level. So: - When you were working on this, did you have any thoughts about deficiencies of the Morphic model wrt multitouch/gestures/etc., and then did you have any ideas about what to change and how to change it? I'll try to cook up a little demo like that sometime soon. Tony From eliot.miranda at gmail.com Fri Sep 11 17:12:36 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Fri, 11 Sep 2020 10:12:36 -0700 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> <432f7ec3796847cca1751c771dc2c98c@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Fri, Sep 11, 2020 at 3:31 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, > > this sounds very convincing. If you have patched this in the VM, it would > be great if you could send me a link to the relevant patch. :-) > Once I fix the issue? Or where the extra info in the Message object is filled in? Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Eliot Miranda > *Gesendet:* Donnerstag, 10. September 2020 07:16:30 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] Two new questions about VM machinery :-) > > Hi Christoph, > > On Tue, Sep 8, 2020 at 1:34 AM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Hi Eliot, >> >> >> thanks for your answer! >> >> >> Just to be sure: What class do you intend to lookup #doesNotUnderstand: for >> if the message not understood's lookupClass is ProtoObject superclass >> (i.e. nil)? Are you thinking of ProtoObject again? This would break the >> existing rule "If a message is sent to super but not understood, send >> #doesNotUnderstand: to super as well" (*), or am I misinterpreting this? >> Shouldn't you rather signal some special selector in this case? >> > > Right, it seems asymmetric, but one *has* to lookup in the class of the > receiver when doing doesNotUndersatnd:. For example, imagine one has a > special doesNotUnderstand: method. If a super send causes > doesNotUnderstand: but doesn't invoke your special doesNotUnderstand: the > system is effectively broken, preventing you from intercepting certain > messages. > > >> (*) I'm not sure about this, but I think this results from the Bluebook, >> page 589. According to the implementation given there, the VM had to crash >> with a recursion error if a nil class is reached while looking up >> #doesNotUnderstand:. Because the Bluebook states that the existence of a >> #doesNotUnderstand: implementation is guaranteed, I would also find it >> reasonable to have the VM crashed if you actually make a call to super from >> a method compiled on ProtoObject. >> > > That's still the case today. If one removes the doesNotUnderstand: method, > or one creates a detached class hierarchy which doesn't contain a > doesNotUnderstand: method then the VM will still abort with a recursive > doesNotUnderstand: error. One could add a fall-back, say send a message to > thisContext such as object:doesNotUnderstand: . But what's to say one > hasn't removed the object:doesNotUnderstand:. Analogous to Gödel's > theorem, if the system is powerful enough to modify itself then it is able > to destroy itself. Dan Ingalls gave a lovely talk at Xerox PARC a few > years ago and Donald Knuth was in the audience. His question to Dan was > "With the power to modify the system on the fly, how do you avoid shooting > yourself in the foot?". Dan's answer was the same as the Doctor who, when > the patient says "Dr, it hurts when I do X", replies "Don't do X". Dan > simply said "we tend not to, and we have crash recovery tools when we do." > >> What do you think? :-) >> > > I think that the major difference between the Blue Book's > doesNotUndersatnd: and modern doesNotUnderstand: is that if a Message > instance has three inst vars then the VM fills in the third inst var with > the class in which the message lookup started, so that now in a > doesNotUnderstand: method one can find out if the doesNotUnderstand: was > the result of a normal send or a super send, and hence a doesNotUnderstand: > method has the necessary info if it in fact wanted to do super > doesNotUnderstand: for MNUs resulting from super sends. > >> >> >> Best, >> >> Christoph >> >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von Eliot Miranda >> *Gesendet:* Sonntag, 6. September 2020 04:49:54 >> *An:* The general-purpose Squeak developers list >> *Betreff:* Re: [squeak-dev] Two new questions about VM machinery :-) >> >> Hi Christophe, Hi Tobias, >> >> On Sat, Sep 5, 2020 at 4:10 AM Thiede, Christoph < >> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >> >>> Hi all, hi Eliot, >>> >>> >>> just for curiosity, I have two new questions about the VM machinery (which >>> are far away from praxis but close to my interests): >>> >>> >>> *1.** super on ProtoObject* >>> >>> What would you expect the following to return? >>> >>> x := Compiler evaluate: 'super identityHash' for: ProtoObject new. >>> >>> thisContext objectClass: x. >>> >>> I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: >>> is called. >>> >> >> My guess before looking at the code was that the send provoked a >> doesNotUnderstand: and hence the expression returned the result of raising >> a MessageNotUnderstood exception. But looking at the code I see that at >> least in the JIT there is undefined behavior in looking up a message in a >> nil class (the superclass of ProtoObject is nil). So thank you for this. >> I'll have to firm up the behaviour to ensure a doesNotUnderstand: is the >> result. >> >> But actually, it answers you the ProtoObject instance, which I find very >>> interesting because it means that if a message cannot be looked up, it is >>> simply and silently skipped. >>> >> >> Which is a bug. The only reasonable thing to do here (IMO) is for the VM >> to send doesNotUnderstnnd:. >> >> >>> >>> If you debug the call instead (Context class >> #runSimulated:), Context >>> >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense >>> but unfortunately is not consistent with the original VM behavior. >>> Maybe the Compiler should forbid any call to super from ProtoObject >>> instances at all (more formally: if the receiver's class's superclass is >>> nil)? Or should we adjust the simulation code? >>> (By the way: If you do the same in Squeak.js, you'll get an infinite >>> recursion :D) >>> >>> *2. Mirror primitives* >>> What is the reason for primitives such as 75 (#identityHash) or 78/139 >>> (#nextInstance/#nextObject) not being mirrored in Context? >>> Was this simply forgotten, or don't we have the claim to mirror any >>> "essential" primitive without it actually being needed by the simulation >>> machinery? >>> >> >> They're not needed by the execution simulation machinery. >> >> >>> As always, looking forward to your interesting explanations! :-) >>> >> >>> >>> Best, >>> Christoph >>> >> >> Cheers >> >> _,,,^..^,,,_ >> best, Eliot >> >> > > -- > _,,,^..^,,,_ > best, Eliot > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Fri Sep 11 17:27:10 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Fri, 11 Sep 2020 10:27:10 -0700 Subject: [squeak-dev] a few small quit issues I want to note Message-ID: Hi All, there are a handful of issues on quitting, especially quitting without saving, that bother me. I'm not suggesting we fix these right away but I do want to put them on the list to be fixed. Fast system startup and exit are important, especially in a scripting context. The first is that I *hate* the QUIT/NO SAVE notice being written to the changes file. This seems entirely unnecessary. Why if one enters the image, doesn't do anything and then quits, must the changes file be written to? This causes minor pain when one has an image/changes under git and one has to revert the changes occasionally (but not the image). Another issue is that we waste time shutting down the SoundPlugin via primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or not. So what happens is that the Vm loads the SoundPlugin just to turn it off. A waste of effort. We could check whether the plugin has been loaded first. Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do unnecessary processing if the system is just exiting, and not snapshotting and then exiting. _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 11 18:04:15 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 11 Sep 2020 18:04:15 +0000 Subject: [squeak-dev] Can we deprecate PasteUpMorph>>#findA in favor of "SystemWindow reuseWindows"? Message-ID: <3abafbab9b274591bdcc42b314708e54@student.hpi.uni-potsdam.de> Hi all, I think the entire question is in the title. PasteUpMorph is a very overloaded class and I do not really see the added value of the selectors #findAFileList:, #findATranscript:, etc.. The only one I could figure out is performance, but we could also speed up SystemWindow >> #openInWorld:[extent:] (ouch, duplication!) instead. Wdyt? :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 11 18:49:04 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 11 Sep 2020 18:49:04 +0000 Subject: [squeak-dev] Two new questions about VM machinery :-) In-Reply-To: References: <675dc1fcb4da48e4b6bb4861392164cb@student.hpi.uni-potsdam.de> <432f7ec3796847cca1751c771dc2c98c@student.hpi.uni-potsdam.de> , Message-ID: Once you fixed the issue :-) ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Freitag, 11. September 2020 19:12:36 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) Hi Christoph, On Fri, Sep 11, 2020 at 3:31 AM Thiede, Christoph > wrote: Hi Eliot, this sounds very convincing. If you have patched this in the VM, it would be great if you could send me a link to the relevant patch. :-) Once I fix the issue? Or where the extra info in the Message object is filled in? Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Eliot Miranda > Gesendet: Donnerstag, 10. September 2020 07:16:30 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) Hi Christoph, On Tue, Sep 8, 2020 at 1:34 AM Thiede, Christoph > wrote: Hi Eliot, thanks for your answer! Just to be sure: What class do you intend to lookup #doesNotUnderstand: for if the message not understood's lookupClass is ProtoObject superclass (i.e. nil)? Are you thinking of ProtoObject again? This would break the existing rule "If a message is sent to super but not understood, send #doesNotUnderstand: to super as well" (*), or am I misinterpreting this? Shouldn't you rather signal some special selector in this case? Right, it seems asymmetric, but one *has* to lookup in the class of the receiver when doing doesNotUndersatnd:. For example, imagine one has a special doesNotUnderstand: method. If a super send causes doesNotUnderstand: but doesn't invoke your special doesNotUnderstand: the system is effectively broken, preventing you from intercepting certain messages. (*) I'm not sure about this, but I think this results from the Bluebook, page 589. According to the implementation given there, the VM had to crash with a recursion error if a nil class is reached while looking up #doesNotUnderstand:. Because the Bluebook states that the existence of a #doesNotUnderstand: implementation is guaranteed, I would also find it reasonable to have the VM crashed if you actually make a call to super from a method compiled on ProtoObject. That's still the case today. If one removes the doesNotUnderstand: method, or one creates a detached class hierarchy which doesn't contain a doesNotUnderstand: method then the VM will still abort with a recursive doesNotUnderstand: error. One could add a fall-back, say send a message to thisContext such as object:doesNotUnderstand: . But what's to say one hasn't removed the object:doesNotUnderstand:. Analogous to Gödel's theorem, if the system is powerful enough to modify itself then it is able to destroy itself. Dan Ingalls gave a lovely talk at Xerox PARC a few years ago and Donald Knuth was in the audience. His question to Dan was "With the power to modify the system on the fly, how do you avoid shooting yourself in the foot?". Dan's answer was the same as the Doctor who, when the patient says "Dr, it hurts when I do X", replies "Don't do X". Dan simply said "we tend not to, and we have crash recovery tools when we do." What do you think? :-) I think that the major difference between the Blue Book's doesNotUndersatnd: and modern doesNotUnderstand: is that if a Message instance has three inst vars then the VM fills in the third inst var with the class in which the message lookup started, so that now in a doesNotUnderstand: method one can find out if the doesNotUnderstand: was the result of a normal send or a super send, and hence a doesNotUnderstand: method has the necessary info if it in fact wanted to do super doesNotUnderstand: for MNUs resulting from super sends. Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Eliot Miranda > Gesendet: Sonntag, 6. September 2020 04:49:54 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Two new questions about VM machinery :-) Hi Christophe, Hi Tobias, On Sat, Sep 5, 2020 at 4:10 AM Thiede, Christoph > wrote: Hi all, hi Eliot, just for curiosity, I have two new questions about the VM machinery (which are far away from praxis but close to my interests): 1. super on ProtoObject What would you expect the following to return? x := Compiler evaluate: 'super identityHash' for: ProtoObject new. thisContext objectClass: x. I would have guessed that either the VM crashes or ProtoObject >> #cannotInterpret: is called. My guess before looking at the code was that the send provoked a doesNotUnderstand: and hence the expression returned the result of raising a MessageNotUnderstood exception. But looking at the code I see that at least in the JIT there is undefined behavior in looking up a message in a nil class (the superclass of ProtoObject is nil). So thank you for this. I'll have to firm up the behaviour to ensure a doesNotUnderstand: is the result. But actually, it answers you the ProtoObject instance, which I find very interesting because it means that if a message cannot be looked up, it is simply and silently skipped. Which is a bug. The only reasonable thing to do here (IMO) is for the VM to send doesNotUnderstnnd:. If you debug the call instead (Context class >> #runSimulated:), Context >> #send:to:with:lookupIn: raises a nil DNU error, which makes more sense but unfortunately is not consistent with the original VM behavior. Maybe the Compiler should forbid any call to super from ProtoObject instances at all (more formally: if the receiver's class's superclass is nil)? Or should we adjust the simulation code? (By the way: If you do the same in Squeak.js, you'll get an infinite recursion :D) 2. Mirror primitives What is the reason for primitives such as 75 (#identityHash) or 78/139 (#nextInstance/#nextObject) not being mirrored in Context? Was this simply forgotten, or don't we have the claim to mirror any "essential" primitive without it actually being needed by the simulation machinery? They're not needed by the execution simulation machinery. As always, looking forward to your interesting explanations! :-) Best, Christoph Cheers _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From tim at rowledge.org Fri Sep 11 20:00:55 2020 From: tim at rowledge.org (tim Rowledge) Date: Fri, 11 Sep 2020 13:00:55 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: Message-ID: <52057FBF-A842-43B5-8143-AE07F35EC951@rowledge.org> I kind of agree here. If nothing has been written to the changelog, then quit/nosave ought not be added - nothing has happened, it doesn't need recording. For the sound plugin pointless-shutdown it is at least not causing an error should the plugin fail to load, so that's one small blessing. Maybe in SoundPlayer class>>#stopPlayerProcess we should simply do nothing if the PlayerProcess is nil? I think that would work ok with SoundPlayer class>>#playLoop. > On 2020-09-11, at 10:27 AM, Eliot Miranda wrote: > > Hi All, > > there are a handful of issues on quitting, especially quitting without saving, that bother me. I'm not suggesting we fix these right away but I do want to put them on the list to be fixed. Fast system startup and exit are important, especially in a scripting context. > > The first is that I *hate* the QUIT/NO SAVE notice being written to the changes file. This seems entirely unnecessary. Why if one enters the image, doesn't do anything and then quits, must the changes file be written to? This causes minor pain when one has an image/changes under git and one has to revert the changes occasionally (but not the image). > > Another issue is that we waste time shutting down the SoundPlugin via primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or not. So what happens is that the Vm loads the SoundPlugin just to turn it off. A waste of effort. We could check whether the plugin has been loaded first. > > Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do unnecessary processing if the system is just exiting, and not snapshotting and then exiting. > _,,,^..^,,,_ > best, Eliot > tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: PDH: Page to Disk for the Hell of it From lewis at mail.msen.com Fri Sep 11 20:07:16 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Fri, 11 Sep 2020 16:07:16 -0400 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <52057FBF-A842-43B5-8143-AE07F35EC951@rowledge.org> References: <52057FBF-A842-43B5-8143-AE07F35EC951@rowledge.org> Message-ID: <20200911200716.GA27574@shell.msen.com> +1 from me too. Although I would also note that "Smalltalk quitPrimitive" does the job very effectively, at least on Unix systems. File handles are closed automatically, and I have never noticed any problems. I'm not sure if it is safe on Windows though. Dave On Fri, Sep 11, 2020 at 01:00:55PM -0700, tim Rowledge wrote: > I kind of agree here. > > If nothing has been written to the changelog, then quit/nosave ought not be added - nothing has happened, it doesn't need recording. > > For the sound plugin pointless-shutdown it is at least not causing an error should the plugin fail to load, so that's one small blessing. Maybe in SoundPlayer class>>#stopPlayerProcess we should simply do nothing if the PlayerProcess is nil? I think that would work ok with SoundPlayer class>>#playLoop. > > > On 2020-09-11, at 10:27 AM, Eliot Miranda wrote: > > > > Hi All, > > > > there are a handful of issues on quitting, especially quitting without saving, that bother me. I'm not suggesting we fix these right away but I do want to put them on the list to be fixed. Fast system startup and exit are important, especially in a scripting context. > > > > The first is that I *hate* the QUIT/NO SAVE notice being written to the changes file. This seems entirely unnecessary. Why if one enters the image, doesn't do anything and then quits, must the changes file be written to? This causes minor pain when one has an image/changes under git and one has to revert the changes occasionally (but not the image). > > > > Another issue is that we waste time shutting down the SoundPlugin via primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or not. So what happens is that the Vm loads the SoundPlugin just to turn it off. A waste of effort. We could check whether the plugin has been loaded first. > > > > Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do unnecessary processing if the system is just exiting, and not snapshotting and then exiting. > > _,,,^..^,,,_ > > best, Eliot > > > > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > Strange OpCodes: PDH: Page to Disk for the Hell of it > > > From smalltalker2 at mac.com Fri Sep 11 21:05:55 2020 From: smalltalker2 at mac.com (John Pfersich) Date: Fri, 11 Sep 2020 14:05:55 -0700 Subject: [squeak-dev] Squeak on Ubuntu 18.04 desktop In-Reply-To: <469351F1-7AC6-4852-B188-12F3992E30B3@rowledge.org> References: <469351F1-7AC6-4852-B188-12F3992E30B3@rowledge.org> Message-ID: <1C845ECF-024D-401D-9840-6E7A1F1FE64A@mac.com> Sorry for the late reply, but in Ubuntu 16.04, the default Window mgr is Unity and in 18.04 they switched to a more standard Gnome based WM. Yeah, the upgrade caused many problems, not the least of which was the WM. /*—————————————————-*/ Sent from my iPhone https://boincstats.com/signature/-1/user/51616339056/sig.png See https://objectnets.net and https://objectnets.org https://datascilv.com and https://datascilv.org > On Jul 2, 2020, at 22:42, tim Rowledge wrote: > >  > >> On 2020-07-02, at 8:42 PM, Chris Muller wrote: >> >> Has anyone figured out how to get Squeak's behavior to behave in 18.04 like it did in 16.04 and before? > > This is with the stock window manager for ubuntu 18? I got away from that horror story as quickly as I could work out. Not that xfce4 is looking much less annoying TBH. What is it with the unix world wanting to make life as miserable as possible? > > It may just be from long experience but the least awful unix/gui pair I know is Raspbian - or more correctly since they recently changed the name - Raspberry Pi OS. It's Debian based, the UI is tolerably consistent and best of all the system is being cared for and developed by some people that seem to have a decent clue. It wouldn't be worth mentioning this if they didn't provide an X86 version but they do so it is. > > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > The less time planning, the more time programming. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Fri Sep 11 21:54:16 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Fri, 11 Sep 2020 17:54:16 -0400 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps Message-ID: <20200911215416.GA42161@shell.msen.com> Christoph has fixed a bug in event timestamps on the Windows VM. Background is at https://github.com/OpenSmalltalk/opensmalltalk-vm/pull/518. This led us to a question about Time>>eventMillisecondClock. To summarize: Events are delivered to the image by the VM, and processed in the image as e.g. MorphicEvent. Each event has a timeStamp that is an integer number of milliseconds since some arbitrary starting point. The actual time basis (starting point) of those timestamps is not important, just as long as they are consistent. In the Windows VM, the event timeStamps are derived from the Windows event system, and are in milliseconds relative to the time that the Windows system was started. Other VMs obtain their event time stamps differently, but normally through ioMSecs() in the VM. Thus, for any VM, the events must have internally consistent time stamps, but the timestamps may or may not be aligned with ioMSecs() in the VM. All good so far, but then Christoph pointed out this in the image: Time>>eventMillisecondClock "In order to make certain event handling code work (cf MouseEvent>asMouseMove) we need access to the tick kept by ioMSecs() " "Time eventMillisecondClock" ^0 This is being used in multiple places in the image, and it explicitly assumes that event timestamps are relative to ioMSecs() in the VM which is not the case on Windows. This leads me to ask three questions: 1) Does #eventMillisecondClock need to exist? It looks like it might be a kludge, if so could we make it go away? 2) If the eventMillisecondClock method does need to exist, should it be changed to retrieve a millisecond value that is aligned with that of the events? 3) If not 2) then do we need to change the Windows VM to somehow use event timestamps that match eventMillisecondClock? Dave From tim at rowledge.org Fri Sep 11 22:45:35 2020 From: tim at rowledge.org (tim Rowledge) Date: Fri, 11 Sep 2020 15:45:35 -0700 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: <20200911215416.GA42161@shell.msen.com> References: <20200911215416.GA42161@shell.msen.com> Message-ID: I think the key thing is that the answers from the primitive 135 must be aligned with the tick values obtained from primitive 94. Of course if prim 94 is properly functional (all elements of the event array are correctly filled in) then prim 135 should never be needed; it was created for those machines that waaaaay back when I added the input events still couldn't provide such data. I'm not entirely sure there actually were any.... The only usage of prim 135 not related directly to events seems to be SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods that looks a bit iffy. EventSensor>>#createMouseEvent is only used in a couple of places to do with rectangle transforming interactively, and probably ought to be improved. EventSensor>>#nextEventSynthesized is only used if the EventSensor has no event queue and that is ... a bit complex but probably should never happen since we removed the old InputSensor years and years ago. EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe we should drop the fake-it code and really insist on the prim being there? MorphicEvent>>#timeStamp looks like a probably redundant backup? MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to copy the time stamp from the receiver? tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim There are no stupid questions. But, there are a lot of inquisitive idiots. From asqueaker at gmail.com Sat Sep 12 00:18:42 2020 From: asqueaker at gmail.com (Chris Muller) Date: Fri, 11 Sep 2020 19:18:42 -0500 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: Message-ID: Hi Eliot, may I recommend the "flat-line your image" hot key? It's really useful for that and other use cases like debugging shutdown code, or when Morphic gets slammed with events due to a bug that disrupts the UI. It calls Smalltalk quitPrimitive, so skips all shutdown processing and logging. It's one of the global command keys, Cmd+Shift+_. Another tip to avoid git fake dirty .changes file is, upon launching the image, immediately Save as... a "_test" image that'll be excluded from your repository. With those two easily-accessible paths to a non-logging exit for the user, I hope we won't rush into changing the logging of exit-without-save. That's a pretty legacy feature that I do use occasionally to help me keep track of which images I'm looking at, without wanting to change their .image file timestamp. That SoundPlugin one does sound like one to optimize, but for the Form, I think you want that processing because it's about what bits you want to record in the image file, regardless whether continuing the session. - Chris On Fri, Sep 11, 2020 at 12:27 PM Eliot Miranda wrote: > > Hi All, > > there are a handful of issues on quitting, especially quitting without saving, that bother me. I'm not suggesting we fix these right away but I do want to put them on the list to be fixed. Fast system startup and exit are important, especially in a scripting context. > > The first is that I *hate* the QUIT/NO SAVE notice being written to the changes file. This seems entirely unnecessary. Why if one enters the image, doesn't do anything and then quits, must the changes file be written to? This causes minor pain when one has an image/changes under git and one has to revert the changes occasionally (but not the image). > > Another issue is that we waste time shutting down the SoundPlugin via primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or not. So what happens is that the Vm loads the SoundPlugin just to turn it off. A waste of effort. We could check whether the plugin has been loaded first. > > Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do unnecessary processing if the system is just exiting, and not snapshotting and then exiting. > _,,,^..^,,,_ > best, Eliot > From asqueaker at gmail.com Sat Sep 12 00:24:55 2020 From: asqueaker at gmail.com (Chris Muller) Date: Fri, 11 Sep 2020 19:24:55 -0500 Subject: [squeak-dev] Squeak on Ubuntu 18.04 desktop In-Reply-To: <1C845ECF-024D-401D-9840-6E7A1F1FE64A@mac.com> References: <469351F1-7AC6-4852-B188-12F3992E30B3@rowledge.org> <1C845ECF-024D-401D-9840-6E7A1F1FE64A@mac.com> Message-ID: You'll like to install the "Unite" extension that fixes the top bar problem when running Squeak maximized. Let's you reclaim that real estate lost in 18.04... On Fri, Sep 11, 2020 at 4:06 PM John Pfersich via Squeak-dev wrote: > > Sorry for the late reply, but in Ubuntu 16.04, the default Window mgr is Unity and in 18.04 they switched to a more standard Gnome based WM. Yeah, the upgrade caused many problems, not the least of which was the WM. > > /*—————————————————-*/ > Sent from my iPhone > https://boincstats.com/signature/-1/user/51616339056/sig.png > See https://objectnets.net and https://objectnets.org > https://datascilv.com and https://datascilv.org > > On Jul 2, 2020, at 22:42, tim Rowledge wrote: > >  > > On 2020-07-02, at 8:42 PM, Chris Muller wrote: > > > Has anyone figured out how to get Squeak's behavior to behave in 18.04 like it did in 16.04 and before? > > > This is with the stock window manager for ubuntu 18? I got away from that horror story as quickly as I could work out. Not that xfce4 is looking much less annoying TBH. What is it with the unix world wanting to make life as miserable as possible? > > It may just be from long experience but the least awful unix/gui pair I know is Raspbian - or more correctly since they recently changed the name - Raspberry Pi OS. It's Debian based, the UI is tolerably consistent and best of all the system is being cared for and developed by some people that seem to have a decent clue. It wouldn't be worth mentioning this if they didn't provide an X86 version but they do so it is. > > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > The less time planning, the more time programming. > > > > From asqueaker at gmail.com Sat Sep 12 00:35:44 2020 From: asqueaker at gmail.com (Chris Muller) Date: Fri, 11 Sep 2020 19:35:44 -0500 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: <20200911215416.GA42161@shell.msen.com> References: <20200911215416.GA42161@shell.msen.com> Message-ID: I don't know the answer to those questions, but in checking its senders in my image, I did notice a couple by RFB-Server, which is a pretty important external package. Whatever answer is arrived at would be nice if it involves keeping RFB in a functioning state for the community (and hopefully eventually included in the base image). Best, Chris On Fri, Sep 11, 2020 at 4:54 PM David T. Lewis wrote: > > Christoph has fixed a bug in event timestamps on the Windows VM. Background > is at https://github.com/OpenSmalltalk/opensmalltalk-vm/pull/518. > > This led us to a question about Time>>eventMillisecondClock. To summarize: > > Events are delivered to the image by the VM, and processed in the image > as e.g. MorphicEvent. Each event has a timeStamp that is an integer number > of milliseconds since some arbitrary starting point. The actual time basis > (starting point) of those timestamps is not important, just as long as they > are consistent. > > In the Windows VM, the event timeStamps are derived from the Windows event > system, and are in milliseconds relative to the time that the Windows system > was started. Other VMs obtain their event time stamps differently, but > normally through ioMSecs() in the VM. > > Thus, for any VM, the events must have internally consistent time stamps, > but the timestamps may or may not be aligned with ioMSecs() in the VM. > > All good so far, but then Christoph pointed out this in the image: > > Time>>eventMillisecondClock > "In order to make certain event handling code work (cf MouseEvent>asMouseMove) we need access > to the tick kept by ioMSecs() " > "Time eventMillisecondClock" > > ^0 > > This is being used in multiple places in the image, and it explicitly > assumes that event timestamps are relative to ioMSecs() in the VM which > is not the case on Windows. > > This leads me to ask three questions: > > 1) Does #eventMillisecondClock need to exist? It looks like it might be > a kludge, if so could we make it go away? > > 2) If the eventMillisecondClock method does need to exist, should it be changed > to retrieve a millisecond value that is aligned with that of the events? > > 3) If not 2) then do we need to change the Windows VM to somehow use event > timestamps that match eventMillisecondClock? > > Dave > > > From eliot.miranda at gmail.com Sat Sep 12 01:02:15 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Fri, 11 Sep 2020 18:02:15 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: Message-ID: On Fri, Sep 11, 2020 at 5:19 PM Chris Muller wrote: > Hi Eliot, may I recommend the "flat-line your image" hot key? It's > really useful for that and other use cases like debugging shutdown > code, or when Morphic gets slammed with events due to a bug that > disrupts the UI. It calls Smalltalk quitPrimitive, so skips all > shutdown processing and logging. It's one of the global command keys, > Cmd+Shift+_. > Thanks. However quitting via quitPrimitive is a bad idea. Soon enough we will have a system with ephemerons around files and could expect file vuffers etc to be flushed before exit. So we will want to run pending finalization actions, etc. exiting via the quit primitive without running these activities could well break some applications. > > Another tip to avoid git fake dirty .changes file is, upon launching > the image, immediately Save as... a "_test" image that'll be excluded > from your repository. > Not the use case I'm thinking of. > With those two easily-accessible paths to a non-logging exit for the > user, I hope we won't rush into changing the logging of > exit-without-save. That's a pretty legacy feature that I do use > occasionally to help me keep track of which images I'm looking at, > without wanting to change their .image file timestamp. > OK, I abandon the idea. Forget I said anything. > That SoundPlugin one does sound like one to optimize, but for the > Form, I think you want that processing because it's about what bits > you want to record in the image file, regardless whether continuing > the session. > No, the Form thing is compressing forms so they take less space in the image file. A complete waste of time if one is merely quitting. > > > > > > - Chris > > On Fri, Sep 11, 2020 at 12:27 PM Eliot Miranda > wrote: > > > > Hi All, > > > > there are a handful of issues on quitting, especially quitting > without saving, that bother me. I'm not suggesting we fix these right away > but I do want to put them on the list to be fixed. Fast system startup and > exit are important, especially in a scripting context. > > > > The first is that I *hate* the QUIT/NO SAVE notice being written to the > changes file. This seems entirely unnecessary. Why if one enters the > image, doesn't do anything and then quits, must the changes file be written > to? This causes minor pain when one has an image/changes under git and one > has to revert the changes occasionally (but not the image). > > > > Another issue is that we waste time shutting down the SoundPlugin via > primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or > not. So what happens is that the Vm loads the SoundPlugin just to turn it > off. A waste of effort. We could check whether the plugin has been loaded > first. > > > > Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do > unnecessary processing if the system is just exiting, and not snapshotting > and then exiting. > > _,,,^..^,,,_ > > best, Eliot > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sat Sep 12 11:53:17 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 12 Sep 2020 11:53:17 0000 Subject: [squeak-dev] The Trunk: KernelTests-ul.386.mcz Message-ID: Levente Uzonyi uploaded a new version of KernelTests to project The Trunk: http://source.squeak.org/trunk/KernelTests-ul.386.mcz ==================== Summary ==================== Name: KernelTests-ul.386 Author: ul Time: 12 September 2020, 1:49:16.062715 pm UUID: 7c7b8abb-7226-47e6-929e-b220b49cd714 Ancestors: KernelTests-ct.385 - added a test for Context >> #copyTo: =============== Diff against KernelTests-ct.385 =============== Item was added: + ----- Method: MethodContextTest>>testCopyTo (in category 'tests') ----- + testCopyTo + + | context depth targetSender | + context := thisContext. + depth := 1. + targetSender := context. + [ (targetSender := targetSender sender) isNil ] whileFalse: [ + | original copy | + original := context. + copy := context copyTo: targetSender. + 1 to: depth do: [ :index | + index = 1 ifFalse: [ + "Since we're copying thisContext, the pc and stackPtr may be different for the current frame." + self + assert: original pc equals: copy pc; + assert: original stackPtr equals: copy stackPtr ]. + self + deny: original == copy; + assert: original method equals: copy method; + assert: original closure equals: copy closure; + assert: original receiver equals: copy receiver. + original := original sender. + copy := copy sender ]. + self + assert: copy isNil; + assert: original == targetSender. + depth := depth + 1 ]! From commits at source.squeak.org Sat Sep 12 11:53:40 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 12 Sep 2020 11:53:40 0000 Subject: [squeak-dev] The Trunk: Kernel-ul.1339.mcz Message-ID: Levente Uzonyi uploaded a new version of Kernel to project The Trunk: http://source.squeak.org/trunk/Kernel-ul.1339.mcz ==================== Summary ==================== Name: Kernel-ul.1339 Author: ul Time: 12 September 2020, 1:52:36.827997 pm UUID: 11d71615-1cc2-4e25-9ac8-d637679f3abd Ancestors: Kernel-ct.1338 - fixed Context >> #copyTo: which did not stop copying when aContext was reached - fixed a typo in SmallInteger >> #scaledIdentityHash's comment =============== Diff against Kernel-ct.1338 =============== Item was changed: ----- Method: Context>>copyTo: (in category 'query') ----- copyTo: aContext "Copy self and my sender chain down to, but not including, aContext. End of copied chain will have nil sender. Assume that there is no loop in the context chain." | currentContext senderContext copy | self == aContext ifTrue: [ ^nil ]. currentContext := copy := self copy. [ + senderContext := currentContext sender ifNil: [ ^copy ]. + senderContext == aContext ifTrue: [ + currentContext privSender: nil. + ^copy ]. + senderContext := senderContext copy. - senderContext := (currentContext sender ifNil: [ ^copy ]) copy. currentContext privSender: senderContext. currentContext := senderContext ] repeat! Item was changed: ----- Method: SmallInteger>>scaledIdentityHash (in category 'comparing') ----- scaledIdentityHash "For identityHash values returned by primitive 75, answer + such values times 2^8. Otherwise, match the existing - such values times 2^18. Otherwise, match the existing identityHash implementation" ^self! From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 11:55:21 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 11:55:21 +0000 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: <20200911215416.GA42161@shell.msen.com>, Message-ID: <34188386e4b94010878712ff29b63b4a@student.hpi.uni-potsdam.de> Hi all, aside from the current senders of #eventMillisecondClock in the Trunk image, I wonder whether we would like to keep this possibility open in general. For instance, you could use this value to compare the time of events from different VM- and other image-based sources such as incoming network requests ... This is just a vague guess, what do you think? With regard to existing dependencies, if we don't really need this selector, we could surely deprecate it and write a fallback to use primitive 240 instead, couldn't we? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Chris Muller Gesendet: Samstag, 12. September 2020 02:35:44 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Time>>eventMillisecondClock and event timestamps I don't know the answer to those questions, but in checking its senders in my image, I did notice a couple by RFB-Server, which is a pretty important external package. Whatever answer is arrived at would be nice if it involves keeping RFB in a functioning state for the community (and hopefully eventually included in the base image). Best, Chris On Fri, Sep 11, 2020 at 4:54 PM David T. Lewis wrote: > > Christoph has fixed a bug in event timestamps on the Windows VM. Background > is at https://github.com/OpenSmalltalk/opensmalltalk-vm/pull/518. > > This led us to a question about Time>>eventMillisecondClock. To summarize: > > Events are delivered to the image by the VM, and processed in the image > as e.g. MorphicEvent. Each event has a timeStamp that is an integer number > of milliseconds since some arbitrary starting point. The actual time basis > (starting point) of those timestamps is not important, just as long as they > are consistent. > > In the Windows VM, the event timeStamps are derived from the Windows event > system, and are in milliseconds relative to the time that the Windows system > was started. Other VMs obtain their event time stamps differently, but > normally through ioMSecs() in the VM. > > Thus, for any VM, the events must have internally consistent time stamps, > but the timestamps may or may not be aligned with ioMSecs() in the VM. > > All good so far, but then Christoph pointed out this in the image: > > Time>>eventMillisecondClock > "In order to make certain event handling code work (cf MouseEvent>asMouseMove) we need access > to the tick kept by ioMSecs() " > "Time eventMillisecondClock" > > ^0 > > This is being used in multiple places in the image, and it explicitly > assumes that event timestamps are relative to ioMSecs() in the VM which > is not the case on Windows. > > This leads me to ask three questions: > > 1) Does #eventMillisecondClock need to exist? It looks like it might be > a kludge, if so could we make it go away? > > 2) If the eventMillisecondClock method does need to exist, should it be changed > to retrieve a millisecond value that is aligned with that of the events? > > 3) If not 2) then do we need to change the Windows VM to somehow use event > timestamps that match eventMillisecondClock? > > Dave > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 12:04:55 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 12:04:55 +0000 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: , Message-ID: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> Hi, which entries in the changes file are we talking about in detail? Allow me to describe one certain use case of the changes file I frequently apply to one of my images: I have a FreshTrunk.image file that I only utilize in order to reproduce bugs or similar stuff but never save changes made to it. However, sometimes I make some experiments in it, i. e. compile a few methods, and at some time I also need to revert these changes later. Because I never overwrite the image file itself, the first entry in the "browse far as back as ..." dialog already lies several months in past. In this particular situation, it is really helpful for me to filter-type '...' into the changes browser, select the latest item, and press Enter to quickly jump to the latest changes made but not saved to the image. It would be great if this option would not be lost. Apart from that, accelerating image startup + shutdown sounds reasonable of course! Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Samstag, 12. September 2020 03:02:15 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] a few small quit issues I want to note On Fri, Sep 11, 2020 at 5:19 PM Chris Muller > wrote: Hi Eliot, may I recommend the "flat-line your image" hot key? It's really useful for that and other use cases like debugging shutdown code, or when Morphic gets slammed with events due to a bug that disrupts the UI. It calls Smalltalk quitPrimitive, so skips all shutdown processing and logging. It's one of the global command keys, Cmd+Shift+_. Thanks. However quitting via quitPrimitive is a bad idea. Soon enough we will have a system with ephemerons around files and could expect file vuffers etc to be flushed before exit. So we will want to run pending finalization actions, etc. exiting via the quit primitive without running these activities could well break some applications. Another tip to avoid git fake dirty .changes file is, upon launching the image, immediately Save as... a "_test" image that'll be excluded from your repository. Not the use case I'm thinking of. With those two easily-accessible paths to a non-logging exit for the user, I hope we won't rush into changing the logging of exit-without-save. That's a pretty legacy feature that I do use occasionally to help me keep track of which images I'm looking at, without wanting to change their .image file timestamp. OK, I abandon the idea. Forget I said anything. That SoundPlugin one does sound like one to optimize, but for the Form, I think you want that processing because it's about what bits you want to record in the image file, regardless whether continuing the session. No, the Form thing is compressing forms so they take less space in the image file. A complete waste of time if one is merely quitting. - Chris On Fri, Sep 11, 2020 at 12:27 PM Eliot Miranda > wrote: > > Hi All, > > there are a handful of issues on quitting, especially quitting without saving, that bother me. I'm not suggesting we fix these right away but I do want to put them on the list to be fixed. Fast system startup and exit are important, especially in a scripting context. > > The first is that I *hate* the QUIT/NO SAVE notice being written to the changes file. This seems entirely unnecessary. Why if one enters the image, doesn't do anything and then quits, must the changes file be written to? This causes minor pain when one has an image/changes under git and one has to revert the changes occasionally (but not the image). > > Another issue is that we waste time shutting down the SoundPlugin via primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or not. So what happens is that the Vm loads the SoundPlugin just to turn it off. A waste of effort. We could check whether the plugin has been loaded first. > > Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do unnecessary processing if the system is just exiting, and not snapshotting and then exiting. > _,,,^..^,,,_ > best, Eliot > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Sat Sep 12 12:41:56 2020 From: karlramberg at gmail.com (karl ramberg) Date: Sat, 12 Sep 2020 14:41:56 +0200 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> Message-ID: There is also the Frank stuff which probably have some of the same multi cursor funktionality: http://tinlizzie.org/~bert/frank4ipad/ One can download the Frank.ipa and unzip to look at the image. Best, Karl On Fri, Sep 11, 2020 at 5:04 PM Tony Garnock-Jones < tonyg at leastfixedpoint.com> wrote: > Hi Vanessa, > > On 9/10/20 10:23 PM, Vanessa Freudenberg wrote: > > Oh, exciting! Please record a multi-hand Etoys demo. Maybe like this: > > https://youtu.be/gYrp31fH-Jk?t=56 > > Whoa, awesome! I didn't know this existed! > > I have questions! > > - How much of that shift-to-get-other-colour-buttons support is still > in the image? So far I haven't touched the default world menu bar, which > is mildly inappropriate for small and/or multitouch devices. > > - What other ideas about mapping multitouch to Morphic interaction are > out there I have missed and am in danger of reinventing? > > Also, I am intrigued by the idea of simply having each touch be mapped > to a Hand. It seems like the obvious way to do things, on one level, but > doesn't quite fit my fuzzy intuitions for how (multi-finger) gesture > recognition might work on another level. So: > > - When you were working on this, did you have any thoughts about > deficiencies of the Morphic model wrt multitouch/gestures/etc., and then > did you have any ideas about what to change and how to change it? > > I'll try to cook up a little demo like that sometime soon. > > Tony > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sat Sep 12 14:18:02 2020 From: gettimothy at zoho.com (gettimothy) Date: Sat, 12 Sep 2020 10:18:02 -0400 Subject: [squeak-dev] Setting class instance variable nil...that's interesting Message-ID: <17482ae1a55.10a8dca6214879.2773607687036794288@zoho.com> Ignore if spam... It is interesting that I can reset a class instance variable inside one of the methods of a class with a doit. However, I cannot do it from a Workspace . TemplateTitleWords := nil  from within the method area works (surprising, but very handy). WikitextTemplates TemplateTitleWords :=  nil.  does not (as expected) I expect the latter, I am surprised by the former. Don't get me wrong, it is a handy feature , but if it is a bug, here is a heads up. cheers, tty -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 12 14:31:27 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 12 Sep 2020 07:31:27 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> Message-ID: <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> Hi Christoph, > On Sep 12, 2020, at 5:05 AM, Thiede, Christoph wrote: > >  > Hi, > > > > which entries in the changes file are we talking about in detail? > > Allow me to describe one certain use case of the changes file I frequently apply to one of my images: I have a FreshTrunk.image file that I only utilize in order to reproduce bugs or similar stuff but never save changes made to it. However, sometimes I make some experiments in it, i. e. compile a few methods, and at some time I also need to revert these changes later. Because I never overwrite the image file itself, the first entry in the "browse far as back as ..." dialog already lies several months in past. In this particular situation, it is really helpful for me to filter-type '...' into the changes browser, select the latest item, and press Enter to quickly jump to the latest changes made but not saved to the image. > That makes sense. So we do want the QUIT/NO SAVE indication written to the changes file if anything is written to the changes file because we know that when doing crash recovery we want to ignore everything before QUIT/NO SAVE. So, Chris Muller’s objections aside, I would argue that if the changes file is undisturbed (nothing written to it in the current session) when the system is about to quit without saving, it should do so without writing the QUIT/NO SAVE stamp. Chris, does this work for you or do you still see benefit in the QUIT/NO SAVE stamp when nothing has been logged? Why? > It would be great if this option would not be lost. > I’m most interested in not disturbing the changes file unnecessarily for the REPL image used in vm development and in the packaged app I’m working on (Terf), in our git repository. With the REPL image if it starts up and evaluates an expression of two (which are not logged) and then quits nothing has changed but the QUIT/NO SAVE has been written, and now my reproducible image/changes pair is no longer in exactly the same state; the changes file has grown. If I run the app image, launch the app, quit the app and then quit the image nothing has changed except that the QUIT/NO SAVE stamp has been written to the changes. And gif tells me I have a modified file. But I have modified nothing. So Chris, can you live with having the system not write the QUIT/NO SAVE stamp if the changes file is the same size it was at startup? > Apart from that, accelerating image startup + shutdown sounds reasonable of course! > > > > Best, > > Christoph > > Von: Squeak-dev im Auftrag von Eliot Miranda > Gesendet: Samstag, 12. September 2020 03:02:15 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] a few small quit issues I want to note > > > >> On Fri, Sep 11, 2020 at 5:19 PM Chris Muller wrote: >> Hi Eliot, may I recommend the "flat-line your image" hot key? It's >> really useful for that and other use cases like debugging shutdown >> code, or when Morphic gets slammed with events due to a bug that >> disrupts the UI. It calls Smalltalk quitPrimitive, so skips all >> shutdown processing and logging. It's one of the global command keys, >> Cmd+Shift+_. > > Thanks. However quitting via quitPrimitive is a bad idea. Soon enough we will have a system with ephemerons around files and could expect file vuffers etc to be flushed before exit. So we will want to run pending finalization actions, etc. exiting via the quit primitive without running these activities could well break some applications. > >> >> Another tip to avoid git fake dirty .changes file is, upon launching >> the image, immediately Save as... a "_test" image that'll be excluded >> from your repository. > > Not the use case I'm thinking of. > >> With those two easily-accessible paths to a non-logging exit for the >> user, I hope we won't rush into changing the logging of >> exit-without-save. That's a pretty legacy feature that I do use >> occasionally to help me keep track of which images I'm looking at, >> without wanting to change their .image file timestamp. > > OK, I abandon the idea. Forget I said anything. > >> That SoundPlugin one does sound like one to optimize, but for the >> Form, I think you want that processing because it's about what bits >> you want to record in the image file, regardless whether continuing >> the session. > > No, the Form thing is compressing forms so they take less space in the image file. A complete waste of time if one is merely quitting. > >> >> >> >> >> >> - Chris >> >> On Fri, Sep 11, 2020 at 12:27 PM Eliot Miranda wrote: >> > >> > Hi All, >> > >> > there are a handful of issues on quitting, especially quitting without saving, that bother me. I'm not suggesting we fix these right away but I do want to put them on the list to be fixed. Fast system startup and exit are important, especially in a scripting context. >> > >> > The first is that I *hate* the QUIT/NO SAVE notice being written to the changes file. This seems entirely unnecessary. Why if one enters the image, doesn't do anything and then quits, must the changes file be written to? This causes minor pain when one has an image/changes under git and one has to revert the changes occasionally (but not the image). >> > >> > Another issue is that we waste time shutting down the SoundPlugin via primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or not. So what happens is that the Vm loads the SoundPlugin just to turn it off. A waste of effort. We could check whether the plugin has been loaded first. >> > >> > Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do unnecessary processing if the system is just exiting, and not snapshotting and then exiting. >> > _,,,^..^,,,_ >> > best, Eliot >> > >> > > > -- > _,,,^..^,,,_ > best, Eliot > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 14:37:59 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 14:37:59 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic Message-ID: <953da6739f364d0388e5659c97299396@student.hpi.uni-potsdam.de> Hi all, recent discussions have shown just another time that in spite of its overall modular and object-oriented design, the Morphic System still incorporates a number of global state variables that impede modular processes in some situations. For instance, running or even debugging any form of UI simulation code in a background process was likely to cause problems because, via the global state variables, two planned-to-be-independent projects undesirably shared their events, hands, and worlds. Concrete systems suffering from this global state include various UI tests executed using AutoTDD [1], or the screenshot generation framework for Squeak by Example [2] which I had the joy to co-develop. The attached changeset tackles these issues for all packages in the Trunk by wrapping the following three globals into process-local accessors: ActiveEvent, ActiveHand, and ActiveWorld. As the changeset contains patches of over 300 selectors in more than 100 classes every single line of which you probably will not feel like reading in detail, here is a summary of all changes I applied: * Added #activeEvent[:], #activeHand[:], and #activeWorld[:] process-local accessors on Object as Morphic-Kernel extensions. The actual values are stored directly on the active process in the manner of a ProcessSpecificVariable. For backward compatibility, the global variables are still kept up to date here. * Added #activateHand:during: and #activateWorld:during: as dynamic scope setters on Object as Morphic-Kernel extensions. * Replaced all references to ActiveEvent, ActiveHand, and ActiveWorld by "self activeEvent", "self activeHand", and "self activeWorld" accordingly. I also spent some time reflecting in which cases you actually would like to receive a possible nil value and ended up with changing the most senders that are not involved into the critical event processing logic into their "#current*" equivalents (#currentEvent, #currentHand, and #currentWorld) which already guarantee to return non-nil values. In the case of #currentWorld, I also replaced many senders with "Project current world" that were not invoked in an event-related context. * While skimming over all the implementations, I also applied a number of really minor refactorings: improve multilingual support by adding some "#translated"s to user strings, remove nil checks that could never be reached, and reformat some of the very hardest to read methods I came across. Please review! I'm looking forward to eliminating these unnecessary artifacts of global state and making Squeak an even more purely object-oriented and modular system by merging these changes into the Trunk. Best, Christoph [1] https://github.com/hpi-swa-teaching/AutoTDD [2] https://github.com/codeZeilen/SqueakByExample-english/ -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: Hide activeVariables.2.cs URL: From eliot.miranda at gmail.com Sat Sep 12 14:40:20 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 12 Sep 2020 07:40:20 -0700 Subject: [squeak-dev] Setting class instance variable nil...that's interesting In-Reply-To: <17482ae1a55.10a8dca6214879.2773607687036794288@zoho.com> References: <17482ae1a55.10a8dca6214879.2773607687036794288@zoho.com> Message-ID: <45EF9C15-AE12-4D6C-9290-657A9BC3FAEF@gmail.com> Hi tty, > On Sep 12, 2020, at 7:18 AM, gettimothy via Squeak-dev wrote: > >  > Ignore if spam... It’s not spam. > > > It is interesting that I can reset a class instance variable inside one of the methods of a class with a doit. > However, I cannot do it from a Workspace . The creasing is that doits always happen in the context of a specific receiver and different contexts have different receivers. Trace Compiler class>>#evaluate: down and you’ll see where the receiver is supplied before the Compiler instance is created. In a Workspace the receiver is nil. In an Inspector the receiver is the object being inspected In an explorer the receiver is the currently selected object (IIRC) In a browser the receiver is the currently selected class (or nil if none selected, or the current method’s class in a method list browser) And within a doit (naturally since a doit is simply an anonymous method) all the inst cars if the receiver are in scope. Since nil has no inst vars there are no inst vars in scope in a workspace. > > > TemplateTitleWords := nil from within the method area works (surprising, but very handy). > WikitextTemplates TemplateTitleWords := nil. does not (as expected) > > I expect the latter, I am surprised by the former. > > Don't get me wrong, it is a handy feature , but if it is a bug, here is a heads up. > > cheers, > > tty > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 14:51:04 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 14:51:04 +0000 Subject: [squeak-dev] Setting class instance variable nil...that's interesting In-Reply-To: <17482ae1a55.10a8dca6214879.2773607687036794288@zoho.com> References: <17482ae1a55.10a8dca6214879.2773607687036794288@zoho.com> Message-ID: Hi Timothy, Please ignore if I chose the wrong level of explanation, this is always hard for me. :-) All code executed in an inspector is executed against the inspected object as a receiver. That means that you can do absolutely everything with the object (including its class' state) you could do from a method compiled on its class as well. > TemplateTitleWords := nil This is of course valid syntax when executing the assignment against a class that contains a class variable (!= class instance variable!) named like this. > WikitextTemplates TemplateTitleWords := nil. does not (as expected) This is no valid Smalltalk syntax (and has never been). Variables (except globals) can only be accessed when compiling against the defining class. This guarantees real encapsulation of an object's state, which is great to preserve modularity. If you want to access state from a foreign class, you have to define an accessor message by implementing an accessor method returning the state variable, because only messages can be used to communicate with foreign objects. In your example, navigate to the class side of WikitextTemplates and write a method #templateTitleWords: that directly assigns the parameter to the TemplateTitleWords variable. WikitextTemplates >> templateTitleWords: aCollection TemplateTitleWords := aCollection. Be cautious not to expose too many implementation details of a class by defining accessors. If you need a meta way to do this, have a look at #classPool and its senders. :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von gettimothy via Squeak-dev Gesendet: Samstag, 12. September 2020 16:18:02 An: squeak-dev Betreff: [squeak-dev] Setting class instance variable nil...that's interesting Ignore if spam... It is interesting that I can reset a class instance variable inside one of the methods of a class with a doit. However, I cannot do it from a Workspace . TemplateTitleWords := nil from within the method area works (surprising, but very handy). WikitextTemplates TemplateTitleWords := nil. does not (as expected) I expect the latter, I am surprised by the former. Don't get me wrong, it is a handy feature , but if it is a bug, here is a heads up. cheers, tty -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 14:59:07 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 14:59:07 +0000 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de>, <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> Message-ID: Hi Eliot, I think I see your point, but I'm not sure whether we really want to restrict the logging features of Squeak by defining an edge case like this. Without being aware of all the background, one could wonder "why quit 1 has been logged but quit 2 has not been" until you discover that you did not compile any code during the second session. However, that's just a loose thought trying to keep the complexity in the image as low as possible. As an alternative, couldn't you disable the whole changes file in your REPL process via a script, or maybe even via a VM parameter? Another option would be making the changes file read-only if you do not need to manipulate it anyway. Squeak displays a warning in this situation, but you can still do everything that does not involve compiling afaik. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Samstag, 12. September 2020 16:31:27 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] a few small quit issues I want to note Hi Christoph, On Sep 12, 2020, at 5:05 AM, Thiede, Christoph wrote:  Hi, which entries in the changes file are we talking about in detail? Allow me to describe one certain use case of the changes file I frequently apply to one of my images: I have a FreshTrunk.image file that I only utilize in order to reproduce bugs or similar stuff but never save changes made to it. However, sometimes I make some experiments in it, i. e. compile a few methods, and at some time I also need to revert these changes later. Because I never overwrite the image file itself, the first entry in the "browse far as back as ..." dialog already lies several months in past. In this particular situation, it is really helpful for me to filter-type '...' into the changes browser, select the latest item, and press Enter to quickly jump to the latest changes made but not saved to the image. That makes sense. So we do want the QUIT/NO SAVE indication written to the changes file if anything is written to the changes file because we know that when doing crash recovery we want to ignore everything before QUIT/NO SAVE. So, Chris Muller’s objections aside, I would argue that if the changes file is undisturbed (nothing written to it in the current session) when the system is about to quit without saving, it should do so without writing the QUIT/NO SAVE stamp. Chris, does this work for you or do you still see benefit in the QUIT/NO SAVE stamp when nothing has been logged? Why? It would be great if this option would not be lost. I’m most interested in not disturbing the changes file unnecessarily for the REPL image used in vm development and in the packaged app I’m working on (Terf), in our git repository. With the REPL image if it starts up and evaluates an expression of two (which are not logged) and then quits nothing has changed but the QUIT/NO SAVE has been written, and now my reproducible image/changes pair is no longer in exactly the same state; the changes file has grown. If I run the app image, launch the app, quit the app and then quit the image nothing has changed except that the QUIT/NO SAVE stamp has been written to the changes. And gif tells me I have a modified file. But I have modified nothing. So Chris, can you live with having the system not write the QUIT/NO SAVE stamp if the changes file is the same size it was at startup? Apart from that, accelerating image startup + shutdown sounds reasonable of course! Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Samstag, 12. September 2020 03:02:15 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] a few small quit issues I want to note On Fri, Sep 11, 2020 at 5:19 PM Chris Muller > wrote: Hi Eliot, may I recommend the "flat-line your image" hot key? It's really useful for that and other use cases like debugging shutdown code, or when Morphic gets slammed with events due to a bug that disrupts the UI. It calls Smalltalk quitPrimitive, so skips all shutdown processing and logging. It's one of the global command keys, Cmd+Shift+_. Thanks. However quitting via quitPrimitive is a bad idea. Soon enough we will have a system with ephemerons around files and could expect file vuffers etc to be flushed before exit. So we will want to run pending finalization actions, etc. exiting via the quit primitive without running these activities could well break some applications. Another tip to avoid git fake dirty .changes file is, upon launching the image, immediately Save as... a "_test" image that'll be excluded from your repository. Not the use case I'm thinking of. With those two easily-accessible paths to a non-logging exit for the user, I hope we won't rush into changing the logging of exit-without-save. That's a pretty legacy feature that I do use occasionally to help me keep track of which images I'm looking at, without wanting to change their .image file timestamp. OK, I abandon the idea. Forget I said anything. That SoundPlugin one does sound like one to optimize, but for the Form, I think you want that processing because it's about what bits you want to record in the image file, regardless whether continuing the session. No, the Form thing is compressing forms so they take less space in the image file. A complete waste of time if one is merely quitting. - Chris On Fri, Sep 11, 2020 at 12:27 PM Eliot Miranda > wrote: > > Hi All, > > there are a handful of issues on quitting, especially quitting without saving, that bother me. I'm not suggesting we fix these right away but I do want to put them on the list to be fixed. Fast system startup and exit are important, especially in a scripting context. > > The first is that I *hate* the QUIT/NO SAVE notice being written to the changes file. This seems entirely unnecessary. Why if one enters the image, doesn't do anything and then quits, must the changes file be written to? This causes minor pain when one has an image/changes under git and one has to revert the changes occasionally (but not the image). > > Another issue is that we waste time shutting down the SoundPlugin via primSoundStop in stopPlayerProcess, whether the SOundPlugin is loaded or not. So what happens is that the Vm loads the SoundPlugin just to turn it off. A waste of effort. We could check whether the plugin has been loaded first. > > Similarly, Symbol class>>shutDown: anf Form class>>shutDown: do unnecessary processing if the system is just exiting, and not snapshotting and then exiting. > _,,,^..^,,,_ > best, Eliot > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sat Sep 12 16:47:15 2020 From: gettimothy at zoho.com (gettimothy) Date: Sat, 12 Sep 2020 12:47:15 -0400 Subject: [squeak-dev] Setting class instance variable nil...that's interesting In-Reply-To: <45EF9C15-AE12-4D6C-9290-657A9BC3FAEF@gmail.com> References: <17482ae1a55.10a8dca6214879.2773607687036794288@zoho.com> <45EF9C15-AE12-4D6C-9290-657A9BC3FAEF@gmail.com> Message-ID: <1748336b963.11fda773615982.6612723230633969371@zoho.com> Christoph and Eliot, Thank you both. ---- On Sat, 12 Sep 2020 10:40:20 -0400 Eliot Miranda wrote ---- Hi tty, On Sep 12, 2020, at 7:18 AM, gettimothy via Squeak-dev wrote: Ignore if spam... It’s not spam. It is interesting that I can reset a class instance variable inside one of the methods of a class with a doit. However, I cannot do it from a Workspace . The creasing is that doits always happen in the context of a specific receiver and different contexts have different receivers.  Trace Compiler class>>#evaluate: down and you’ll see where the receiver is supplied before the Compiler instance is created. In a Workspace the receiver is nil. In an Inspector the receiver is the object being inspected  In an explorer the receiver is the currently selected object (IIRC) In a browser the receiver is the currently selected class (or nil if none selected, or the current method’s class in a method list browser) And within a doit (naturally since a doit is simply an anonymous method) all the inst cars if the receiver are in scope.  Since nil has no inst vars there are no inst vars in scope in a workspace. TemplateTitleWords := nil  from within the method area works (surprising, but very handy). WikitextTemplates TemplateTitleWords :=  nil.  does not (as expected) I expect the latter, I am surprised by the former. Don't get me wrong, it is a handy feature , but if it is a bug, here is a heads up. cheers, tty -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Sat Sep 12 16:57:03 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sat, 12 Sep 2020 18:57:03 +0200 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> Message-ID: > On 12.09.2020, at 16:31, Eliot Miranda wrote: > > With the REPL image if it starts up and evaluates an expression of two (which are not logged) and then quits nothing has changed but the QUIT/NO SAVE has been written, and now my reproducible image/changes pair is no longer in exactly the same state; the changes file has grown. > In that particular case, wouldn't it suffice to just make the Changes file read-only? Best regards -Tobias From lewis at mail.msen.com Sat Sep 12 17:39:50 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 12 Sep 2020 13:39:50 -0400 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <953da6739f364d0388e5659c97299396@student.hpi.uni-potsdam.de> References: <953da6739f364d0388e5659c97299396@student.hpi.uni-potsdam.de> Message-ID: <20200912173950.GA35925@shell.msen.com> Wow Christoph, this is great work :-) I cannot review in detail now but I loaded your changes into my image and all is good so far. I am going to strongly urge that we get this into trunk as soon as we can, but I also want to challenge us all to see if we can eliminate the bindings entirely from the Environment. In the case of the global World variable, we were able to make it an instance variable in Project. It is possible that we could do something similar with the other global bindings. I is really GREAT to see this, thank you for taking it on :-) Dave On Sat, Sep 12, 2020 at 02:37:59PM +0000, Thiede, Christoph wrote: > Hi all, > > > recent discussions have shown just another time that in spite of its overall modular and object-oriented design, the Morphic System still incorporates a number of global state variables that impede modular processes in some situations. For instance, running or even debugging any form of UI simulation code in a background process was likely to cause problems because, via the global state variables, two planned-to-be-independent projects undesirably shared their events, hands, and worlds. Concrete systems suffering from this global state include various UI tests executed using AutoTDD [1], or the screenshot generation framework for Squeak by Example [2] which I had the joy to co-develop. > > > The attached changeset tackles these issues for all packages in the Trunk by wrapping the following three globals into process-local accessors: ActiveEvent, ActiveHand, and ActiveWorld. > > As the changeset contains patches of over 300 selectors in more than 100 classes every single line of which you probably will not feel like reading in detail, here is a summary of all changes I applied: > > * Added #activeEvent[:], #activeHand[:], and #activeWorld[:] process-local accessors on Object as Morphic-Kernel extensions. The actual values are stored directly on the active process in the manner of a ProcessSpecificVariable. For backward compatibility, the global variables are still kept up to date here. > * Added #activateHand:during: and #activateWorld:during: as dynamic scope setters on Object as Morphic-Kernel extensions. > * Replaced all references to ActiveEvent, ActiveHand, and ActiveWorld by "self activeEvent", "self activeHand", and "self activeWorld" accordingly. I also spent some time reflecting in which cases you actually would like to receive a possible nil value and ended up with changing the most senders that are not involved into the critical event processing logic into their "#current*" equivalents (#currentEvent, #currentHand, and #currentWorld) which already guarantee to return non-nil values. In the case of #currentWorld, I also replaced many senders with "Project current world" that were not invoked in an event-related context. > * While skimming over all the implementations, I also applied a number of really minor refactorings: improve multilingual support by adding some "#translated"s to user strings, remove nil checks that could never be reached, and reformat some of the very hardest to read methods I came across. > > > Please review! I'm looking forward to eliminating these unnecessary artifacts of global state and making Squeak an even more purely object-oriented and modular system by merging these changes into the Trunk. > > > Best, > > Christoph > > > [1] https://github.com/hpi-swa-teaching/AutoTDD > > [2] https://github.com/codeZeilen/SqueakByExample-english/ Content-Description: Hide activeVariables.2.cs > 'From Squeak6.0alpha of 6 September 2020 [latest update: #19838] on 12 September 2020 at 4:29:53 pm'! !Object methodsFor: 'user interface' stamp: 'ct 9/12/2020 14:13'! launchPartVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins" | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph openInHand.! ! !Object methodsFor: '*Protocols' stamp: 'ct 9/12/2020 14:14'! haveFullProtocolBrowsedShowingSelector: aSelector "Open up a Lexicon on the receiver, having it open up showing aSelector, which may be nil" "(2 at 3) haveFullProtocolBrowsed" | aBrowser | aBrowser := (Smalltalk at: #InstanceBrowser ifAbsent: [^ nil]) new useVocabulary: Vocabulary fullVocabulary. aBrowser openOnObject: self inWorld: Project current world showingSelector: aSelector! ! !Object methodsFor: '*Etoys-Squeakland-user interface' stamp: 'ct 9/12/2020 14:13'! launchPartOffsetVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins. This variant makes the morph offset from the hand position by an amount suitable for tile-scripting in some circumstances." | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph setProperty: #offsetForAttachingToHand toValue: 10@ -10. aMorph fullBounds. aMorph openInHand! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:45'! activateHand: aHand during: aBlock | priorHand | priorHand := self activeHand. self activeHand: aHand. ^ aBlock ensure: [ self activeHand: priorHand]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 16:12'! activateWorld: aWorld during: aBlock | priorWorld | priorWorld := self activeWorld. self activeWorld: aWorld. ^ aBlock ensure: [ self activeWorld: priorWorld]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:53'! activeEvent "Answer the active morphic event for the current process, or nil if no event is active. Private!! Usually, you will want to send #currentEvent instead." ^ Processor activeProcess environmentAt: #ActiveEvent ifAbsent: [nil]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:53'! activeEvent: anEvent "Set the active morphic event for the current process. Can be nil." Processor activeProcess environmentAt: #ActiveEvent put: anEvent. "for backword compatibility <6.0" ActiveEvent := anEvent.! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:52'! activeHand "Answer the active HandMorph for the current process, or nil if no hand is active. Private!! Usually, you will want to send #currentHand instead." ^ Processor activeProcess environmentAt: #ActiveHand ifAbsent: [nil]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:51'! activeHand: aHand "Set the active HandMorph for the current process. Can be nil." Processor activeProcess environmentAt: #ActiveHand put: aHand. "for backword compatibility <6.0" ActiveHand := aHand.! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:52'! activeWorld "Answer the active morphic world for the current process, or nil if no world is active. Private!! Usually, you will want to send #currentWorld instead." ^ Processor activeProcess environmentAt: #ActiveWorld ifAbsent: [nil]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:52'! activeWorld: aWorld "Set the active morphic world for the current process. Can be nil." Processor activeProcess environmentAt: #ActiveWorld put: aWorld. "for backword compatibility <6.0" ActiveWorld := aWorld.! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:33'! currentEvent "Answer the current Morphic event. This method never returns nil." ^ self activeEvent ifNil: [self currentHand lastEvent]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:17'! currentHand "Return a usable HandMorph -- the one associated with the object's current environment. This method will always return a hand, even if it has to conjure one up as a last resort. If a particular hand is actually handling events at the moment (such as a remote hand or a ghost hand), it will be returned." ^ self activeHand ifNil: [self currentWorld primaryHand]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:01'! currentWorld "Answer a morphic world that is the current UI focus." ^ self activeWorld ifNil: [Project current world]! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:05'! currentBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: Browser]]) asSet! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:04'! currentHierarchyBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: HierarchyBrowser]]) asSet! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'Your name?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint onCancelReturn: cancelResponse! ! !Flaps class methodsFor: 'construction support' stamp: 'ct 9/11/2020 20:09'! possiblyReplaceEToyFlaps "If in eToyFriendly mode, and if it's ok to reinitialize flaps, replace the existing flaps with up-too-date etoy flaps. Caution: this is destructive of existing flaps. If preserving the contents of existing flaps is important, set the preference 'okToReinitializeFlaps' to true" PartsBin thumbnailForPartsDescription: StickyPadMorph descriptionForPartsBin. "Puts StickyPadMorph's custom icon back in the cache which typically will have been called" (Preferences eToyFriendly and: [Preferences okToReinitializeFlaps]) ifTrue: [Flaps disableGlobalFlaps: false. Flaps addAndEnableEToyFlaps. Smalltalk isMorphic ifTrue: [Project current world enableGlobalFlaps]]. "PartsBin clearThumbnailCache" "Flaps possiblyReplaceEToyFlaps"! ! !Flaps class methodsFor: 'menu commands' stamp: 'ct 9/11/2020 19:49'! disableGlobalFlaps: interactive "Clobber all the shared flaps structures. First read the user her Miranda rights." interactive ifTrue: [(self confirm: 'CAUTION!! This will destroy all the shared flaps, so that they will not be present in *any* project. If, later, you want them back, you will have to reenable them, from this same menu, whereupon the standard default set of shared flaps will be created. Do you really want to go ahead and clobber all shared flaps at this time?' translated) ifFalse: [^ self]]. self globalFlapTabsIfAny do: [:aFlapTab | self removeFlapTab: aFlapTab keepInList: false. aFlapTab isInWorld ifTrue: [self error: 'Flap problem' translated]]. self clobberFlapTabList. self initializeFlapsQuads. SharedFlapsAllowed := false. Smalltalk isMorphic ifTrue: [ Project current world restoreMorphicDisplay; reformulateUpdatingMenus]. "The following reduces the risk that flaps will be created with variant IDs such as 'Stack Tools2', potentially causing some shared flap logic to fail." "Smalltalk garbageCollect." "-- see if we are OK without this"! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:08'! enableGlobalFlaps "Start using global flaps, given that they were not present." Cursor wait showWhile: [ SharedFlapsAllowed := true. self globalFlapTabs. "This will create them" Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. FlapTab allInstancesDo: [:tab | tab computeEdgeFraction]. Project current world reformulateUpdatingMenus]]! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:09'! setUpSuppliesFlapOnly "Set up the Supplies flap as the only shared flap. A special version formulated for this stand-alone use is used, defined in #newLoneSuppliesFlap" | supplies | SharedFlapTabs isEmptyOrNil ifFalse: "get rid of pre-existing guys if any" [SharedFlapTabs do: [:t | t referent delete. t delete]]. SharedFlapsAllowed := true. SharedFlapTabs := OrderedCollection new. SharedFlapTabs add: (supplies := self newLoneSuppliesFlap). self enableGlobalFlapWithID: 'Supplies' translated. supplies setToPopOutOnMouseOver: false. Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps; reformulateUpdatingMenus].! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:08'! enableClassicNavigatorChanged "The #classicNavigatorEnabled preference has changed. No senders in easily traceable in the image, but this is really sent by a Preference object!!" Preferences classicNavigatorEnabled ifTrue: [Flaps disableGlobalFlapWithID: 'Navigator' translated. Preferences enable: #showProjectNavigator. self disableGlobalFlapWithID: 'Navigator' translated.] ifFalse: [self enableGlobalFlapWithID: 'Navigator' translated. Project current world addGlobalFlaps]. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. Project current world reformulateUpdatingMenus.! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:09'! makeNavigatorFlapResembleGoldenBar "At explicit request, make the flap-based navigator resemble the golden bar. No senders in the image, but sendable from a doit" "Flaps makeNavigatorFlapResembleGoldenBar" Preferences setPreference: #classicNavigatorEnabled toValue: false. Preferences setPreference: #showProjectNavigator toValue: false. (self globalFlapTabWithID: 'Navigator' translated) ifNil: [SharedFlapTabs add: self newNavigatorFlap delete]. self enableGlobalFlapWithID: 'Navigator' translated. Preferences setPreference: #navigatorOnLeftEdge toValue: true. (self globalFlapTabWithID: 'Navigator' translated) arrangeToPopOutOnMouseOver: true. Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. ! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:58'! addLocalFlap ^ self addLocalFlap: self currentEvent! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent "Menu command -- let the user add a new project-local flap. Once the new flap is born, the user can tell it to become a shared flap. Obtain an initial name and edge for the flap, launch the flap, and also launch a menu governing the flap, so that the user can get started right away with customizing it." | title edge | edge := self askForEdgeOfNewFlap. edge ifNil: [^ self]. title := UIManager default request: 'Wording for this flap:' translated initialAnswer: 'Flap' translated. title isEmptyOrNil ifTrue: [^ self]. ^ self addLocalFlap: anEvent titled: title onEdge: edge! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent titled: title onEdge: edge | flapTab menu world | flapTab := self newFlapTitled: title onEdge: edge. (world := anEvent hand world) addMorphFront: flapTab. flapTab adaptToWorld: world. menu := flapTab buildHandleMenu: anEvent hand. flapTab addTitleForHaloMenu: menu. flapTab computeEdgeFraction. menu popUpEvent: anEvent in: world.! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! enableOnlyGlobalFlapsWithIDs: survivorList "In the current project, suppress all global flaps other than those with ids in the survivorList" self globalFlapTabsIfAny do: [:flapTab | (survivorList includes: flapTab flapID) ifTrue: [self enableGlobalFlapWithID: flapTab flapID] ifFalse: [self disableGlobalFlapWithID: flapTab flapID]]. Project current world addGlobalFlaps "Flaps enableOnlyGlobalFlapsWithIDs: #('Supplies')"! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! positionVisibleFlapsRightToLeftOnEdge: edgeSymbol butPlaceAtLeftFlapsWithIDs: idList "Lay out flaps along the designated edge right-to-left, while laying left-to-right any flaps found in the exception list Flaps positionVisibleFlapsRightToLeftOnEdge: #bottom butPlaceAtLeftFlapWithIDs: {'Navigator' translated. 'Supplies' translated} Flaps sharedFlapsAlongBottom" | leftX flapList flapsOnRight flapsOnLeft | flapList := self globalFlapTabsIfAny select: [:aFlapTab | aFlapTab isInWorld and: [aFlapTab edgeToAdhereTo == edgeSymbol]]. flapsOnLeft := OrderedCollection new. flapsOnRight := OrderedCollection new. flapList do: [:fl | (idList includes: fl flapID) ifTrue: [ flapsOnLeft addLast: fl ] ifFalse: [ flapsOnRight addLast: fl ] ]. leftX := Project current world width - 15. flapsOnRight sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab right: leftX - 3. leftX := aFlapTab left]. leftX := Project current world left. flapsOnLeft sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab left: leftX + 3. leftX := aFlapTab right]. flapList do: [:ft | ft computeEdgeFraction. ft flapID = 'Navigator' translated ifTrue: [ft referent left: (ft center x - (ft referent width//2) max: 0)]]! ! !Flaps class methodsFor: '*Etoys-Squeakland-predefined flaps' stamp: 'ct 9/12/2020 14:29'! newSuppliesFlapFromQuads: quads positioning: positionSymbol withPreviousEntries: aCollection "Answer a fully-instantiated flap named 'Supplies' to be placed at the bottom of the screen. Use #center as the positionSymbol to have it centered at the bottom of the screen, or #right to have it placed off near the right edge." | aFlapTab aStrip aWidth sugarNavigator | sugarNavigator := SugarNavigatorBar showSugarNavigator. aStrip := PartsBin newPartsBinWithOrientation: #leftToRight andColor: Color gray muchLighter from: quads withPreviousEntries: aCollection. self twiddleSuppliesButtonsIn: aStrip. aFlapTab := (sugarNavigator ifTrue: [SolidSugarSuppliesTab] ifFalse: [FlapTab]) new referent: aStrip beSticky. aFlapTab setName: 'Supplies' translated edge: (sugarNavigator ifTrue: [#top] ifFalse: [#bottom]) color: Color red lighter. aFlapTab position: (0 @ Project current world sugarAllowance). aFlapTab setBalloonText: aFlapTab balloonTextForFlapsMenu. aFlapTab applyThickness: 20. aWidth := self currentWorld width. aStrip extent: aWidth @ (76 * (1 + (1350 // aWidth))). aStrip beFlap: true. aStrip autoLineLayout: true. aStrip vResizeToFit: true. sugarNavigator ifTrue: [ aFlapTab useSolidTab. aFlapTab height: 20; color: (Color r: 0.804 g: 0.804 b: 0.804)] ifFalse: [ aFlapTab color: Color red lighter]. ^ aFlapTab "Flaps replaceGlobalFlapwithID: 'Supplies' translated"! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleColorSees "Form exampleColorSees" "First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area - where red touches blue - superimposed on the original scene. Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" | formA formB maskA offset tally map intersection left top dCanvas sensitiveColor soughtColor index | formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. sensitiveColor := Color red. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: sensitiveColor. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map at: (index := sensitiveColor indexInMap: map) put: 1. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map at: index put: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((sensitiveColor pixelValueForDepth: formA depth) ) to: ((soughtColor pixelValueForDepth: formB depth) ) test: (Form compareMatchColor bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchTest "Form exampleTouchTest" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a non-transparent pixel of the background upon which it is displayed. First column shows a form with a red block in the midst of transparent area sneaking up on a form with a transparent LHS and blue RHS. The green frame shows the intersection area. Second column shows in grey the part of the red that is within the intersection. Third column shows in black the blue that is within the intersection. Fourth column shows just the A touching B area. Fifth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA maskB offset tally map intersection left top dCanvas| formA := formB := maskA := maskB := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := Form extent: 100 at 50 depth: 32. formB := Form extent: 100 at 50 depth: 16. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color yellow. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color red. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: Color blue. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 2. formA displayOn: maskA at: offset - intersection origin rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskB := Form extent: intersection extent depth: 2. formB displayOn: maskB at: intersection origin negated rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. map := Bitmap new: 4 withAll: 1. map at: 1 put: 0. "transparent" maskA copyBits: maskA boundingBox from: maskA at: 0 at 0 colorMap: map. "maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB copyBits: maskB boundingBox from: maskB at: 0 at 0 colorMap: map. "maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB displayOn: maskA at: 0 at 0 rule: Form and. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA boundingBox area -( maskA tallyPixelValues at: 1)) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((Color transparent pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((Color transparent pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorANotColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchingColor "Form exampleTouchingColor" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a particular color pixel of the background upon which it is displayed. First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area (black) superimposed on the original scene Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA offset tally map intersection left top dCanvas ignoreColor soughtColor| formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. ignoreColor := Color transparent. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color red. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map atAllPut: 1. map at: ( ignoreColor indexInMap: map) put: 0. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map atAllPut: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((ignoreColor pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((soughtColor pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorAMatchColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !HandBugs methodsFor: 'tests' stamp: 'ct 9/12/2020 14:41'! testTargetPoint "self new testTargetPoint" "self run: #testTargetPoint" "This should not throw an exception." self currentHand targetPoint ! ! !Lexicon methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:16'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'Lexicon' translated. aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var assignments (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !InstanceBrowser methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:12'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: ('Messages of {1}' translated format: {objectViewed nameForViewer}). aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var defs (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('viewer on me' viewViewee) ('inspector on me' inspectViewee) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !ListChooser methodsFor: 'actions' stamp: 'ct 9/12/2020 14:42'! accept "if the user submits with no valid entry, make them start over" | choice | self canAccept ifFalse: [ self canAdd ifTrue: [^ self add]. ^ self changed: #textSelection]. choice := self selectedItem. self canAdd ifTrue: [ "Ask the user whether to add the new item or choose the list selection." (UserDialogBoxMorph confirm: 'You can either choose an existing item or add a new one.\What do you want?' translated withCRs title: 'Choose or Add' translated trueChoice: choice asString falseChoice: self searchText asString at: self currentHand position) ifNil: ["Cancelled" self result: nil. ^ self] ifNotNil: [:answer | answer ifTrue: [self result: choice] ifFalse: [self result: self searchText asString]] ] ifFalse: [self result: choice]. self changed: #close.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! setUp previousID := Locale current localeID. previousKeyboardInterpreter := self currentHand instVarNamed: 'keyboardInterpreter'. previousClipboardInterpreter := Clipboard default instVarNamed: 'interpreter'. self currentHand clearKeyboardInterpreter. Clipboard default clearInterpreter.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! tearDown self currentHand instVarNamed: 'keyboardInterpreter' put: previousKeyboardInterpreter. Clipboard default instVarNamed: 'interpreter' put: previousClipboardInterpreter. Locale switchToID: (LocaleID isoLanguage: previousID).! ! !LocaleTest methodsFor: 'tests' stamp: 'ct 9/12/2020 14:43'! testLocaleChanged "self debug: #testLocaleChanged" "LanguageEnvironment >> startUp is called from Prject >> localeChanged" "takes quite a while" Project current updateLocaleDependents. self assert: (self currentHand instVarNamed: 'keyboardInterpreter') isNil description: 'non-nil keyboardInterpreter'. self assert: (Clipboard default instVarNamed: 'interpreter') isNil description: 'non-nil interpreter'. Locale switchToID: (LocaleID isoLanguage: 'ja'). self assert: 'ja' equals: Locale current localeID isoLanguage. Locale switchToID: (LocaleID isoLanguage: 'en'). self assert: 'en' equals: Locale current localeID isoLanguage.! ! !MCCodeTool methodsFor: 'menus' stamp: 'ct 9/11/2020 20:17'! browseFullProtocol "Open up a protocol-category browser on the value of the receiver's current selection. If in mvc, an old-style protocol browser is opened instead. Someone who still uses mvc might wish to make the protocol-category-browser work there too, thanks." (Smalltalk isMorphic and: [Smalltalk hasClassNamed: #Lexicon]) ifFalse: [^ self spawnFullProtocol]. self selectedClassOrMetaClass ifNotNil: [:class | ^ (Smalltalk at: #Lexicon) new openOnClass: class inWorld: self currentWorld showingSelector: self selectedMessageName]. ^ nil! ! !Morph methodsFor: 'copying' stamp: 'ct 9/12/2020 14:20'! duplicate "Make and return a duplicate of the receiver" | newMorph aName w aPlayer topRend | ((topRend := self topRendererOrSelf) ~~ self) ifTrue: [^ topRend duplicate]. self okayToDuplicate ifFalse: [^ self]. aName := (w := self world) ifNotNil: [w nameForCopyIfAlreadyNamed: self]. newMorph := self veryDeepCopy. aName ifNotNil: [newMorph setNameTo: aName]. newMorph arrangeToStartStepping. newMorph privateOwner: nil. "no longer in world" newMorph isPartsDonor: false. "no longer parts donor" (aPlayer := newMorph player) belongsToUniClass ifTrue: [aPlayer class bringScriptsUpToDate]. aPlayer ifNotNil: [self currentWorld presenter flushPlayerListCache]. ^ newMorph! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:20'! justDroppedInto: aMorph event: anEvent "This message is sent to a dropped morph after it has been dropped on -- and been accepted by -- a drop-sensitive morph" | partsBinCase cmd | (self formerOwner notNil and: [self formerOwner ~~ aMorph]) ifTrue: [self removeHalo]. self formerOwner: nil. self formerPosition: nil. cmd := self valueOfProperty: #undoGrabCommand. cmd ifNotNil:[aMorph rememberCommand: cmd. self removeProperty: #undoGrabCommand]. (partsBinCase := aMorph isPartsBin) ifFalse: [self isPartsDonor: false]. (self isInWorld and: [partsBinCase not]) ifTrue: [self world startSteppingSubmorphsOf: self]. "Note an unhappy inefficiency here: the startStepping... call will often have already been called in the sequence leading up to entry to this method, but unfortunately the isPartsDonor: call often will not have already happened, with the result that the startStepping... call will not have resulted in the startage of the steppage." "An object launched by certain parts-launcher mechanisms should end up fully visible..." (self hasProperty: #beFullyVisibleAfterDrop) ifTrue: [aMorph == self currentWorld ifTrue: [self goHome]. self removeProperty: #beFullyVisibleAfterDrop].! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:19'! slideToTrash: evt "Perhaps slide the receiver across the screen to a trash can and make it disappear into it. In any case, remove the receiver from the screen." | aForm trash startPoint endPoint morphToSlide | ((self renderedMorph == ScrapBook default scrapBook) or: [self renderedMorph isKindOf: TrashCanMorph]) ifTrue: [self dismissMorph. ^ self]. TrashCanMorph slideDismissalsToTrash ifTrue: [morphToSlide := self representativeNoTallerThan: 200 norWiderThan: 200 thumbnailHeight: 100. aForm := morphToSlide imageForm offset: (0 at 0). trash := self currentWorld findDeepSubmorphThat: [:aMorph | (aMorph isKindOf: TrashCanMorph) and: [aMorph topRendererOrSelf owner == self currentWorld]] ifAbsent: [trash := TrashCanMorph new. trash position: self currentWorld bottomLeft - (0 @ (trash extent y + 26)). trash openInWorld. trash]. endPoint := trash fullBoundsInWorld center. startPoint := self topRendererOrSelf fullBoundsInWorld center - (aForm extent // 2)]. self dismissMorph. self currentWorld displayWorld. TrashCanMorph slideDismissalsToTrash ifTrue: [aForm slideFrom: startPoint to: endPoint nSteps: 12 delay: 15]. ScrapBook default addToTrash: self! ! !Morph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:45'! yellowButtonActivity: shiftState "Find me or my outermost owner that has items to add to a yellow button menu. shiftState is true if the shift was pressed. Otherwise, build a menu that contains the contributions from myself and my interested submorphs, and present it to the user." | menu | self isWorldMorph ifFalse: [| outerOwner | outerOwner := self outermostOwnerWithYellowButtonMenu. outerOwner ifNil: [^ self]. outerOwner == self ifFalse: [^ outerOwner yellowButtonActivity: shiftState]]. menu := self buildYellowButtonMenu: self currentHand. menu addTitle: self externalName icon: (self iconOrThumbnailOfSize: (Preferences tinyDisplay ifTrue: [16] ifFalse: [28])). menu popUpInWorld: self currentWorld! ! !Morph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:00'! buildYellowButtonMenu: aHand "Build the morph menu for the yellow button." | menu | menu := MenuMorph new defaultTarget: self. self addNestedYellowButtonItemsTo: menu event: self currentEvent. MenuIcons decorateMenu: menu. ^ menu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:44'! addMiscExtrasTo: aMenu "Add a submenu of miscellaneous extra items to the menu." | realOwner realMorph subMenu | subMenu := MenuMorph new defaultTarget: self. (self isWorldMorph not and: [(self renderedMorph isSystemWindow) not]) ifTrue: [subMenu add: 'put in a window' translated action: #embedInWindow]. self isWorldMorph ifFalse: [subMenu add: 'adhere to edge...' translated action: #adhereToEdge. subMenu addLine]. realOwner := (realMorph := self topRendererOrSelf) owner. (realOwner isKindOf: TextPlusPasteUpMorph) ifTrue: [subMenu add: 'GeeMail stuff...' translated subMenu: (realOwner textPlusMenuFor: realMorph)]. subMenu add: 'add mouse up action' translated action: #addMouseUpAction; add: 'remove mouse up action' translated action: #removeMouseUpAction; add: 'hand me tiles to fire this button' translated action: #handMeTilesToFire. subMenu addLine. subMenu add: 'arrowheads on pen trails...' translated action: #setArrowheads. subMenu addLine. subMenu defaultTarget: self topRendererOrSelf. subMenu add: 'draw new path' translated action: #definePath. subMenu add: 'follow existing path' translated action: #followPath. subMenu add: 'delete existing path' translated action: #deletePath. subMenu addLine. self addGestureMenuItems: subMenu hand: self currentHand. aMenu add: 'extras...' translated subMenu: subMenu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:21'! chooseNewGraphicCoexisting: aBoolean "Allow the user to choose a different form for her form-based morph" | replacee aGraphicalMenu | self isInWorld ifFalse: "menu must have persisted for a not-in-world object." [aGraphicalMenu := Project current world submorphThat: [:m | (m isKindOf: GraphicalMenu) and: [m target == self]] ifNone: [^ self]. ^ aGraphicalMenu show; flashBounds]. aGraphicalMenu := GraphicalMenu new initializeFor: self withForms: self reasonableForms coexist: aBoolean. aBoolean ifTrue: [self primaryHand attachMorph: aGraphicalMenu] ifFalse: [replacee := self topRendererOrSelf. replacee owner replaceSubmorph: replacee by: aGraphicalMenu]! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/12/2020 14:20'! indicateAllSiblings "Indicate all the receiver and all its siblings by flashing momentarily." | aPlayer allBoxes | (aPlayer := self topRendererOrSelf player) belongsToUniClass ifFalse: [^ self "error: 'not uniclass'"]. allBoxes := aPlayer class allInstances select: [:m | m costume world == self currentWorld] thenCollect: [:m | m costume boundsInWorld]. 5 timesRepeat: [Display flashAll: allBoxes andWait: 120].! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/11/2020 18:00'! resizeFromMenu "Commence an interaction that will resize the receiver" ^ self resizeMorph: self currentEvent! ! !Morph methodsFor: 'structure' stamp: 'ct 9/12/2020 15:17'! activeHand ^ super activeHand ifNil: [ self isInWorld ifTrue: [self world activeHand] ifFalse: [nil]]! ! !Morph methodsFor: 'structure' stamp: 'ct 9/12/2020 14:40'! primaryHand | outer | outer := self outermostWorldMorph ifNil: [^ nil]. ^ outer activeHand ifNil: [outer firstHand]! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:45'! deleteUnlessHasFocus "Runs on a step timer because we cannot be guaranteed to get focus change events." (self currentHand keyboardFocus ~= self and: [ self isInWorld ]) ifTrue: [ self stopSteppingSelector: #deleteUnlessHasFocus ; delete ]! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:20'! dismissViaHalo "The user has clicked in the delete halo-handle. This provides a hook in case some concomitant action should be taken, or if the particular morph is not one which should be put in the trash can, for example." | cmd | self setProperty: #lastPosition toValue: self positionInWorld. self dismissMorph. TrashCanMorph preserveTrash ifTrue: [ TrashCanMorph slideDismissalsToTrash ifTrue:[self slideToTrash: nil] ifFalse:[TrashCanMorph moveToTrash: self]. ]. cmd := Command new cmdWording: 'dismiss ' translated, self externalName. cmd undoTarget: Project current world selector: #reintroduceIntoWorld: argument: self. cmd redoTarget: Project current world selector: #onceAgainDismiss: argument: self. Project current world rememberCommand: cmd.! ! !Morph methodsFor: 'e-toy support' stamp: 'ct 9/12/2020 14:19'! referencePlayfield "Answer the PasteUpMorph to be used for cartesian-coordinate reference" | former | owner ifNotNil: [(self topRendererOrSelf owner isHandMorph and: [(former := self formerOwner) notNil]) ifTrue: [former := former renderedMorph. ^ former isPlayfieldLike ifTrue: [former] ifFalse: [former referencePlayfield]]]. self allOwnersDo: [:o | o isPlayfieldLike ifTrue: [^ o]]. ^ Project current world! ! !Morph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:45'! handMeTilesToFire "Construct a phrase of tiles comprising a line of code that will 'fire' this object, and hand it to the user" self currentHand attachMorph: (self assuredPlayer tilesToCall: MethodInterface firingInterface)! ! !Morph methodsFor: '*Etoys-Squeakland-geometry' stamp: 'ct 9/12/2020 14:19'! stagingArea "Answer a containing Worldlet, or the World if none." ^ (self ownerThatIsA: Worldlet) ifNil: [self currentWorld]! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:22'! changeColorTarget: anObject selector: aSymbol originalColor: aColor hand: aHand showPalette: showPalette "Put up a color picker for changing some kind of color. May be modal or modeless, depending on #modalColorPickers setting" | c aRectangle | self flag: #arNote. "Simplify this due to anObject == self for almost all cases" c := ColorPickerMorph new. c choseModalityFromPreference; sourceHand: aHand; target: anObject; selector: aSymbol; originalColor: aColor. showPalette ifFalse: [c initializeForJustCursor]. aRectangle := (anObject == self currentWorld) ifTrue: [self currentHand position extent: (20 at 20)] ifFalse: [anObject isMorph ifFalse: [Rectangle center: self position extent: (20 at 20)] ifTrue: [anObject fullBoundsInWorld]]. c putUpFor: anObject near: aRectangle.! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:45'! showEmbedMenu "Put up a menu offering embed targets. Emphasize the current position. Theoretically this method will only be called when there are at least two alternatives." | aMenu | aMenu := self addEmbeddingMenuItemsTo: nil hand: self currentHand. aMenu title: ('embed {1} in...' translated format: {self externalName }). aMenu popUpInWorld! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:20'! hideWillingnessToAcceptDropFeedback "Make the receiver stop looking ready to show some welcoming feedback" self currentWorld removeHighlightFeedback ! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:19'! showWillingnessToAcceptDropFeedback "Make the receiver look ready to show show some welcoming feedback" | aMorph | aMorph := RectangleMorph new bounds: self bounds.. aMorph beTransparent; borderWidth: 4; borderColor: (Color green); lock. aMorph setProperty: #affilliatedPad toValue: (self ownerThatIsA: TilePadMorph). self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !Morph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 15:17'! openInWorldOrWorldlet "Open in the world-like creature affiliated with the active Hand." | aRecorder aWorldlet | (self currentHand isKindOf: HandMorphForReplay) ifTrue: [((aRecorder := self currentHand recorder) isKindOf: MentoringEventRecorder) ifTrue: [aWorldlet := aRecorder contentArea. self center: aWorldlet center. aWorldlet addMorphFront: self. ^ self]]. self openInWorld.! ! !AllPlayersTool methodsFor: 'reinvigoration' stamp: 'ct 9/12/2020 14:25'! reinvigorate "Referesh the contents of the receiver" (submorphs copyFrom: 3 to: submorphs size) do: [:m | m delete]. self currentWorld doOneCycleNow. self playSoundNamed: 'scritch'. (Delay forMilliseconds: 700) wait. self currentWorld presenter reinvigoratePlayersTool: self. self playSoundNamed: 'scratch'.! ! !AllScriptsTool methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:26'! addSecondLineOfControls "Add the second line of controls" | aRow outerButton aButton worldToUse | aRow := AlignmentMorph newRow listCentering: #center; color: Color transparent. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingOnlyActiveScripts; getSelector: #showingOnlyActiveScripts. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'tickers only' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then only scripts that are paused or ticking will be shown' translated. aRow addMorphBack: outerButton. aRow addTransparentSpacerOfSize: 20 at 0. aRow addMorphBack: self helpButton. aRow addTransparentSpacerOfSize: 20 at 0. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingAllInstances; getSelector: #showingAllInstances. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'all instances' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then entries for all instances will be shown, but if not checked, scripts for only one representative of each different kind of object will be shown. Consult the help available by clicking on the purple ? for more information.' translated. aRow addMorphBack: outerButton. self addMorphBack: aRow. worldToUse := self isInWorld ifTrue: [self world] ifFalse: [self currentWorld]. worldToUse presenter reinvigorateAllScriptsTool: self. self layoutChanged.! ! !BookMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:39'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer pageNum: pageNum "Call once to search a page of the book. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container wasIn strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [(pages at: pageNum) isInMemory ifFalse: [rawStrings] ifTrue: [(pages at: pageNum) allStringsAfter: oldContainer]]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findString: searchString startingAt: start caseSensitive: false) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" wasIn := (pages at: pageNum) isInMemory. self goToPage: pageNum. wasIn ifFalse: ["search again, on the real current text. Know page is in." ^self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) startAt: startIndex container: oldContainer pageNum: pageNum "recompute"]]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !BookMorph methodsFor: 'navigation' stamp: 'ct 9/12/2020 14:26'! goToPageMorph: newPage transitionSpec: transitionSpec "Go to a page, which is assumed to be an element of my pages array (if it is not, this method returns quickly. Apply the transitionSpec provided." | pageIndex aWorld oldPageIndex ascending tSpec readIn | pages isEmpty ifTrue: [^self]. self setProperty: #searchContainer toValue: nil. "forget previous search" self setProperty: #searchOffset toValue: nil. self setProperty: #searchKey toValue: nil. pageIndex := pages identityIndexOf: newPage ifAbsent: [^self "abort"]. readIn := newPage isInMemory not. oldPageIndex := pages identityIndexOf: currentPage ifAbsent: [nil]. ascending := (oldPageIndex isNil or: [newPage == currentPage]) ifTrue: [nil] ifFalse: [oldPageIndex < pageIndex]. tSpec := transitionSpec ifNil: ["If transition not specified by requestor..." newPage valueOfProperty: #transitionSpec ifAbsent: [" ... then consult new page" self transitionSpecFor: self " ... otherwise this is the default"]]. self flag: #arNote. "Probably unnecessary" (aWorld := self world) ifNotNil: [self primaryHand releaseKeyboardFocus]. currentPage ifNotNil: [currentPage updateCachedThumbnail]. self currentPage notNil ifTrue: [(((pages at: pageIndex) owner isKindOf: TransitionMorph) and: [(pages at: pageIndex) isInWorld]) ifTrue: [^self "In the process of a prior pageTurn"]. self currentPlayerDo: [:aPlayer | aPlayer runAllClosingScripts]. self removeViewersOnSubsIn: self currentWorld presenter. ascending ifNotNil: ["Show appropriate page transition and start new page when done" currentPage stopStepping. (pages at: pageIndex) position: currentPage position. ^(TransitionMorph effect: tSpec second direction: tSpec third inverse: (ascending or: [transitionSpec notNil]) not) showTransitionFrom: currentPage to: (pages at: pageIndex) in: self whenStart: [self playPageFlipSound: tSpec first] whenDone: [currentPage delete; fullReleaseCachedState. self insertPageMorphInCorrectSpot: (pages at: pageIndex). self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrlInBook: self url. currentPage sqkPage computeThumbnail "just store it"]]]. "No transition, but at least decommission current page" currentPage delete; fullReleaseCachedState]. self insertPageMorphInCorrectSpot: (pages at: pageIndex). "sets currentPage" self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrl. currentPage sqkPage computeThumbnail "just store it"]. self currentWorld presenter flushPlayerListCache.! ! !BookMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:38'! addAdvancedItemsTo: aMenu "Add advanced items to a menu which allow the user to affect all the pages of the book. NB balloon help msgs still pending." | subMenu | subMenu := MenuMorph new defaultTarget: self. subMenu addTranslatedList: #( ('make all pages the same size as this page' makeUniformPageSize 'Make all the pages of this book be the same size as the page currently showing.') ('set background color for all pages' #setPageColor 'Choose a color to assign as the background color for all of this book''s pages') - ('uncache page sorter' uncachePageSorter) ('make a thread of projects in this book' buildThreadOfProjects) - ('make this the template for new pages' setNewPagePrototype)) translatedNoop. "NB The following 2 items do not get auto-updated in a persistent menu." newPagePrototype ifNotNil: [ subMenu add: 'clear new-page template' translated action: #clearNewPagePrototype]. self isInFullScreenMode ifTrue: [ subMenu add: 'exit full screen' translated action: #exitFullScreen] ifFalse: [ subMenu add: 'show full screen' translated action: #goFullScreen]. (self currentHand pasteBuffer isKindOf: PasteUpMorph) ifTrue: [ subMenu addLine. subMenu add: 'paste book page' translated action: #pasteBookPage]. aMenu add: 'advanced...' translated subMenu: subMenu.! ! !CategoryViewer methodsFor: 'get/set slots' stamp: 'ct 9/12/2020 14:39'! makeUniversalTilesGetter: aMethodInterface event: evt from: aMorph "Button in viewer performs this to make a universal-tiles getter and attach it to hand." | newTiles | newTiles := self newGetterTilesFor: scriptedPlayer methodInterface: aMethodInterface. newTiles setProperty: #beScript toValue: true. owner ifNil: [^ newTiles]. self currentHand attachMorph: newTiles. newTiles align: newTiles topLeft with: evt hand position + (7 at 14).! ! !CategoryViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 19:42'! currentVocabulary "Answer the vocabulary currently installed in the viewer. The outer StandardViewer object holds this information." ^ self outerViewer ifNotNil: [:viewer | viewer currentVocabulary] ifNil: [(self world ifNil: [self currentWorld]) currentVocabularyFor: scriptedPlayer]! ! !CategoryViewer methodsFor: '*Etoys-Squeakland-categories' stamp: 'ct 9/11/2020 19:42'! assureCategoryFullyVisible "Keep deleting categoryviewers other than the receiver until the receiver is fully visible." | ready toDelete | ready := false. [(self bounds bottom > self world bottom) and: [ready not]] whileTrue: [ owner submorphs size > 2 ifTrue: [ toDelete := owner submorphs allButFirst reversed detect: [:cv | cv ~~ self] ifNone: [^ self]. toDelete delete. self world doOneCycleNow] ifFalse: [ ready := true]].! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: self bounds; beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock; yourself. self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! removeHighlightFeedback "Remove any existing highlight feedback" self world removeHighlightFeedback. ! ! !DialogWindow methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:44'! initialize super initialize. self changeTableLayout; listDirection: #topToBottom; hResizing: #shrinkWrap; vResizing: #shrinkWrap; rubberBandCells: true; setProperty: #indicateKeyboardFocus toValue: #never. self createTitle: 'Dialog'. self createBody. self setDefaultParameters. keyMap := Dictionary new. exclusive := true. autoCancel := false. preferredPosition := self currentWorld center.! ! !DockingBarMorph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:40'! delete self currentHand removeKeyboardListener: self. activeSubMenu ifNotNil: [ activeSubMenu delete]. ^ super delete! ! !EtoyDAVLoginMorph methodsFor: 'private' stamp: 'ct 9/11/2020 19:45'! loginAndDo: aBlock ifCanceled: cb "EtoyDAVLoginMorph loginAndDo:[:n :p | true] ifCanceled:[]" self name: '' actionBlock: aBlock cancelBlock: cb; fullBounds; position: Display extent - self extent // 2. self position: self position + (0 at 40). self currentWorld addMorphInLayer: self.! ! !EtoyDAVLoginMorph methodsFor: 'actions' stamp: 'ct 9/11/2020 19:45'! launchBrowser self currentWorld addMorph: self buildPanel centeredNear: Sensor cursorPoint. (Smalltalk classNamed: #ScratchPlugin) ifNotNil: [:sp | sp primOpenURL: self url].! ! !EventMorph methodsFor: 'drag and drop' stamp: 'ct 9/11/2020 19:45'! brownDragConcluded "After the user has manually repositioned the receiver via brown-halo-drag, this is invoked." self currentWorld abandonAllHalos. self eventRoll ifNotNil: [:evtRoll | evtRoll pushChangesBackToEventTheatre]! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! abandonReplayHandsAndHalos "Cleanup after playback." self currentWorld abandonReplayHandsAndHalosFor: eventRecorder! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! dismantlePaintBoxArtifacts "Cleanup after playback -- if a paint-box has been left up, take it down." (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/12/2020 14:28'! makeHorizontalRoll "Create a horizontal roll viewer for this recording space" state = #readyToRecord ifTrue: [ ^ self inform: 'Nothing recorded yet' translated]. "self convertToCanonicalForm." "Would prefer to do this but there are still issues." eventRoll ifNil: [ eventRoll := EventRollMorph new. eventRoll eventTheatre: self]. eventRoll formulate. eventRoll isInWorld ifFalse: [eventRoll openInWorld; setExtentFromHalo: (self currentWorld width - 10) @ eventRoll height; top: self bottom; bottom: (eventRoll bottom min: self currentWorld bottom); left: self currentWorld left + 2] "presumably zero" ifTrue: [eventRoll comeToFront].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! pausePlayback "Pause the playback. Sender responsible for setting state to #suspendedPlayback" eventRecorder pausePlayback. (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting. ^ self rewind]. self borderColor: Color orange. self setProperty: #suspendedContentArea toValue: contentArea veryDeepCopy. self populateControlsPanel! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! record "Commence event recording..." self currentWorld abandonAllHalos. self comeToFront. initialContentArea := contentArea veryDeepCopy. self forgetPriorPaintBoxSettings. initialPicture := contentArea imageForm. self state: #recording. self borderColor: Color red. self populateControlsPanel. self currentWorld doOneCycleNow. eventRecorder record! ! !EventRecordingSpace methodsFor: 'processing' stamp: 'ct 9/11/2020 19:45'! assureContentAreaStaysAt: aPoint "selbst-verst??ndlich" self currentWorld doOneCycleNow. self topLeft: ((self topLeft - contentArea topLeft ) + aPoint)! ! !EventRecordingSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:46'! initializeFromPlaybackButton: anEventPlaybackButton "Initialize my content area, caption, and tape from a playback button." | soundEvent | initialContentArea := anEventPlaybackButton contentArea veryDeepCopy. eventRecorder tape: anEventPlaybackButton tape veryDeepCopy. eventRecorder caption: anEventPlaybackButton caption. soundEvent := eventRecorder tape detect: [:evt | evt type = #startSound] ifNone: [nil]. soundEvent ifNotNil: "For benefit of possible re-record of voiceover" [eventRecorder startSoundEvent: soundEvent]. initialPicture := anEventPlaybackButton initialPicture veryDeepCopy ifNil: [self inform: 'caution - old playback; button lacks vital data.' translated. ^ nil]. finalPicture := anEventPlaybackButton finalPicture veryDeepCopy. eventRecorder saved: true. self rewind. self center: self currentWorld center.! ! !EventPlaybackSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:45'! launchFrom: aButton "Initialize the receiver from an invoker button, and launch it." | where | self setProperty: #originatingButton toValue: aButton. self contentArea: aButton contentArea veryDeepCopy tape: aButton tape veryDeepCopy. self captionString: aButton caption. self rewind. autoStart := aButton autoStart. autoDismiss := aButton autoDismiss. "showChrome := aButton showChrome." where := aButton whereToAppear. self openInWorld. where = #screenCenter ifTrue: [self center: self currentWorld center]. where = #buttonPosition ifTrue: [self position: aButton position]. where = #containerOrigin ifTrue: [self position: aButton owner position]. self goHome. self addStopper. autoStart ifTrue: [self play]! ! !FlapTab methodsFor: 'globalness' stamp: 'ct 9/11/2020 19:47'! toggleIsGlobalFlap "Toggle whether the receiver is currently a global flap or not" | oldWorld | self hideFlap. oldWorld := self currentWorld. self isGlobalFlap ifTrue: [Flaps removeFromGlobalFlapTabList: self. oldWorld addMorphFront: self] ifFalse: [self delete. Flaps addGlobalFlap: self. self currentWorld addGlobalFlaps]. self currentWorld reformulateUpdatingMenus.! ! !GoldBoxMenu methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:41'! initializeFor: aScriptor "Answer a graphical menu to be put up in conjunction with the Gold Box" | aButton goldBox aReceiver boxBounds example toScale | scriptor := aScriptor. lastItemMousedOver := nil. self removeAllMorphs. self setProperty: #goldBox toValue: true. self listDirection: #topToBottom; hResizing: #spaceFill; extent: 1 at 1; vResizing: #spaceFill. "standard #newColumn stuff" self setNameTo: 'Gold Box' translated. self useRoundedCorners. self color: Color white. self borderColor: (Color r: 1.0 g: 0.839 b: 0.065). self hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4. { {ScriptingSystem. #yesNoComplexOfTiles. 'test' translated. 'Test/Yes/No panes for testing a condition.' translated}. {ScriptingSystem. #timesRepeatComplexOfTiles. 'repeat' translated. 'TimesRepeat panes for running a section of code repeatedly.' translated}. { ScriptingSystem. #randomNumberTile. 'random' translated. 'A tile that will produce a random number in a given range.' translated}. { ScriptingSystem. #seminalFunctionTile. 'function' translated. 'A tile representing a function call. Click on the function name or the arrows to change functions.' translated}. {ScriptingSystem. #buttonUpTile. 'button up?' translated. 'Reports whether the mouse button is up' translated}. {ScriptingSystem. #buttonDownTile. 'button down?' translated. 'Reports whether the mouse button is down' translated}. {ScriptingSystem. #randomColorTile. 'random color' translated. 'A tile returning a random color' translated}. {scriptor playerScripted. #tileToRefer. 'tile for me' translated. 'A tile representing the object being scripted' translated}. {self. #numericConstantTile. 'number' translated. 'A tile holding a plain number' translated}. } do: [:tuple | aReceiver := tuple first. example := aReceiver perform: tuple second. aButton := IconicButton new target: aReceiver. aButton borderWidth: 0; color: Color transparent. toScale := tuple size >= 5 ifTrue: [tuple first perform: tuple fifth] "bail-out for intractable images." ifFalse: [example imageForm]. aButton labelGraphic: (toScale copy scaledToHeight: 40). aButton actionSelector: #launchPartOffsetVia:label:. aButton arguments: {tuple second. tuple third}. (tuple size > 3 and: [tuple fourth isEmptyOrNil not]) ifTrue: [aButton setBalloonText: tuple fourth]. aButton actWhen: #buttonDown. aButton on: #mouseEnter send: #mousedOverEvent:button: to: self. aButton on: #click send: #delete to: self. self addMorphBack: aButton]. goldBox := aScriptor submorphs first submorphThat: [:m | (m isKindOf: SimpleButtonMorph) and: [m actionSelector == #offerGoldBoxMenu]] ifNone: [nil]. goldBox ifNil: [self position: self currentHand position] ifNotNil: [boxBounds := goldBox boundsInWorld. self center: boxBounds center. self left: (boxBounds center x - (self width // 2)). self top: boxBounds bottom]. lastItemMousedOver := nil. self on: #mouseLeave send: #mouseLeftMenuWithEvent: to: self. self on: #mouseLeaveDragging send: #delete to: self.! ! !GrabPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:41'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair.! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! doDirection: anEvent with: directionHandle "The mouse went down on the forward-direction halo handle; respond appropriately." anEvent hand obtainHalo: self. anEvent shiftPressed ifTrue: [directionArrowAnchor := (target point: target referencePosition in: self world) rounded. self positionDirectionShaft: directionHandle. self removeAllHandlesBut: directionHandle. directionHandle setProperty: #trackDirectionArrow toValue: true] ifFalse: [self currentHand spawnBalloonFor: directionHandle]! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:10'! maybeDismiss: evt with: dismissHandle "Ask hand to dismiss my target if mouse comes up in it." evt hand obtainHalo: self. (dismissHandle containsPoint: evt cursorPoint) ifFalse: [ self delete. target addHalo: evt] ifTrue: [ target resistsRemoval ifTrue: [(UIManager default chooseFrom: { 'Yes' translated. 'Um, no, let me reconsider' translated. } title: 'Really throw this away?' translated) = 1 ifFalse: [^ self]]. evt hand removeHalo. self delete. target dismissViaHalo. self currentWorld presenter flushPlayerListCache].! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! prepareToTrackCenterOfRotation: evt with: rotationHandle "The mouse went down on the center of rotation." evt hand obtainHalo: self. evt shiftPressed ifTrue: [self removeAllHandlesBut: rotationHandle. rotationHandle setProperty: #trackCenterOfRotation toValue: true. evt hand showTemporaryCursor: Cursor blank] ifFalse: [self currentHand spawnBalloonFor: rotationHandle]! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:30'! cursorPoint "Implemented for allowing embedded worlds in an event cycle to query a hand's position and get it in its coordinates. The same can be achieved by #point:from: but this is simply much more convenient since it will look as if the hand is in the lower world." | pos world | pos := self position. world := self activeWorld. (world isNil or: [world == owner]) ifTrue: [^pos]. ^world point: pos from: owner! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:59'! processEvents "Process user input events from the local input devices." | evt evtBuf type hadAny | self activeEvent ifNotNil: [ "Meaning that we were invoked from within an event response. Make sure z-order is up to date." self mouseOverHandler processMouseOver: lastMouseEvent]. hadAny := false. [(evtBuf := Sensor nextEvent) isNil] whileFalse: [evt := nil. "for unknown event types" type := evtBuf first. type = EventTypeMouse ifTrue: [evt := self generateMouseEvent: evtBuf]. type = EventTypeMouseWheel ifTrue: [evt := self generateMouseWheelEvent: evtBuf]. type = EventTypeKeyboard ifTrue: [evt := self generateKeyboardEvent: evtBuf]. type = EventTypeDragDropFiles ifTrue: [evt := self generateDropFilesEvent: evtBuf]. type = EventTypeWindow ifTrue:[evt := self generateWindowEvent: evtBuf]. "All other events are ignored" (type ~= EventTypeDragDropFiles and: [evt isNil]) ifTrue: [^self]. evt ifNotNil: ["Finally, handle it." self handleEvent: evt. hadAny := true. "For better user feedback, return immediately after a mouse event has been processed." evt isMouse ifTrue: [^ self]]]. "note: if we come here we didn't have any mouse events" mouseClickState ifNotNil: [ "No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: [ "No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent].! ! !HandMorph methodsFor: 'initialization' stamp: 'ct 9/12/2020 15:33'! becomeActiveDuring: aBlock "Make the receiver the activeHand during the evaluation of aBlock." | priorHand | priorHand := self activeHand. self activeHand: self. ^ aBlock ensure: [ "check to support project switching." self activeHand == self ifTrue: [self activeHand: priorHand]].! ! !HandMorphForReplay methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:30'! processEvents "Play back the next event" | evt hadMouse hadAny tracker | suspended == true ifTrue: [^ self]. hadMouse := hadAny := false. tracker := recorder objectTrackingEvents. [(evt := recorder nextEventToPlay) isNil] whileFalse: [ ((evt isMemberOf: MouseMoveEvent) and: [evt trail isNil]) ifTrue: [^ self]. tracker ifNotNil: [tracker currentEventTimeStamp: evt timeStamp]. evt type == #EOF ifTrue: [recorder pauseIn: self currentWorld. ^ self]. evt type == #startSound ifTrue: [recorder perhapsPlaySound: evt argument. recorder synchronize. ^ self]. evt type == #startEventPlayback ifTrue: [evt argument launchPlayback. recorder synchronize. ^ self]. evt type == #noteTheatreBounds ifTrue: ["The argument holds the content rect --for now we don't make any use of that info in this form." ^ self]. evt isMouse ifTrue: [hadMouse := true]. (evt isMouse or: [evt isKeyboard]) ifTrue: [self handleEvent: (evt setHand: self) resetHandlerFields. hadAny := true]]. (mouseClickState notNil and: [hadMouse not]) ifTrue: ["No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: ["No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent]! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:12'! destroyThread "Manually destroy the thread" (self confirm: ('Destroy thread <{1}> ?' translated format:{threadName})) ifFalse: [^ self]. self class knownThreads removeKey: threadName ifAbsent: []. self setProperty: #moribund toValue: true. "In case pointed to in some other project" self currentWorld keyboardNavigationHandler == self ifTrue: [self stopKeyboardNavigation]. self delete.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:13'! moreCommands "Put up a menu of options" | allThreads aMenu others target | allThreads := self class knownThreads. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'navigation' translated. Preferences noviceMode ifFalse:[ self flag: #deferred. "Probably don't want that stay-up item, not least because the navigation-keystroke stuff is not dynamically handled" aMenu addStayUpItem ]. others := (allThreads keys reject: [ :each | each = threadName]) asArray sort. others do: [ :each | aMenu add: ('switch to <{1}>' translated format:{each}) selector: #switchToThread: argument: each ]. aMenu addList: { {'switch to recent projects' translated. #getRecentThread}. #-. {'create a new thread' translated. #threadOfNoProjects}. {'edit this thread' translated. #editThisThread}. {'create thread of all projects' translated. #threadOfAllProjects}. #-. {'First project in thread' translated. #firstPage}. {'Last project in thread' translated. #lastPage} }. (target := self currentIndex + 2) > listOfPages size ifFalse: [ aMenu add: ('skip over next project ({1})' translated format:{(listOfPages at: target - 1) first}) action: #skipOverNext ]. aMenu addList: { {'jump within this thread' translated. #jumpWithinThread}. {'insert new project' translated. #insertNewProject}. #-. {'simply close this navigator' translated. #delete}. {'destroy this thread' translated. #destroyThread}. #- }. (self currentWorld keyboardNavigationHandler == self) ifFalse:[ aMenu add: 'start keyboard navigation with this thread' translated action: #startKeyboardNavigation ] ifTrue: [ aMenu add: 'stop keyboard navigation with this thread' translated action: #stopKeyboardNavigation ]. aMenu popUpInWorld.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! positionAppropriately | others world otherRects overlaps bottomRight | (self ownerThatIsA: HandMorph) ifNotNil: [^self]. others := (world := Project currentWorld) submorphs select: [ :each | each ~~ self and: [each isKindOf: self class]]. otherRects := others collect: [ :each | each bounds]. bottomRight := (world hasProperty: #threadNavigatorPosition) ifTrue: [world valueOfProperty: #threadNavigatorPosition] ifFalse: [world bottomRight]. self align: self fullBounds bottomRight with: bottomRight. self setProperty: #previousWorldBounds toValue: self world bounds. [ overlaps := false. otherRects do: [ :r | (r intersects: bounds) ifTrue: [overlaps := true. self bottom: r top]. ]. self top < self world top ifTrue: [ self bottom: bottomRight y. self right: self left - 1. ]. overlaps ] whileTrue.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! startKeyboardNavigation "Tell the active world to starting navigating via desktop keyboard navigation via me" self currentWorld keyboardNavigationHandler: self! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:15'! stopKeyboardNavigation "Cease navigating via the receiver in response to desktop keystrokes" self currentWorld removeProperty: #keyboardNavigationHandler! ! !InternalThreadNavigationMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:13'! loadPageWithProgress "Load the desired page, showing a progress indicator as we go" | projectInfo projectName beSpaceHandler | projectInfo := listOfPages at: currentIndex. projectName := projectInfo first. loadedProject := Project named: projectName. self class know: listOfPages as: threadName. beSpaceHandler := (Project current world keyboardNavigationHandler == self). self currentWorld addDeferredUIMessage: [InternalThreadNavigationMorph openThreadNamed: threadName atIndex: currentIndex beKeyboardHandler: beSpaceHandler]. loadedProject ifNil: [ ComplexProgressIndicator new targetMorph: self; historyCategory: 'project loading' translated; withProgressDo: [ [ loadedProject := Project current fromMyServerLoad: projectName ] on: ProjectViewOpenNotification do: [ :ex | ex resume: false] "we probably don't want a project view morph in this case" ]. ]. loadedProject ifNil: [ ^self inform: 'I cannot find that project' translated ]. self delete. loadedProject enter.! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! resetBottomRightPosition self currentWorld removeProperty: #threadNavigatorPosition. ! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! setBottomRightPosition self currentWorld setProperty: #threadNavigatorPosition toValue: self bottomRight. ! ! !LassoPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:42'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! play "Play the movie, as it were." tape ifNil: [^ self]. tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! record "Commence recording or re-recording." tapeStream := WriteStream on: (Array new: 10000). self resumeRecordIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! resumePlayingWithoutPassingStop "Like play, but avoids the stop step that does more than we'd like." tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/12/2020 14:24'! stop "Stop recording or playing." tapeStream ifNotNil: [(#(recording recordingWithSound) includes: self state) ifTrue: [tape := tapeStream contents. saved := false]]. self terminateVoiceRecording. "In case doing" journalFile ifNotNil: [journalFile close]. self pauseIn: self currentWorld. tapeStream := nil. self state: #atEndOfPlayback. recordingSpace abandonReplayHandsAndHalos. recordMeter ifNotNil: [recordMeter width: 1].! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:43'! popUpEvent: evt in: aWorld "Present this menu in response to the given event." | aHand aPosition | aHand := evt ifNotNil: [evt hand] ifNil: [self currentHand]. aPosition := aHand position truncated. ^ self popUpAt: aPosition forHand: aHand in: aWorld! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:23'! popUpNoKeyboard "Present this menu in the current World, *not* allowing keyboard input into the menu" ^ self popUpAt: self currentHand position forHand: self currentHand in: self currentWorld allowKeyboard: false! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title world | title := self allMorphs detect: [ :ea | ea hasProperty: #titleString ]. title := title submorphs first. self visible: false. world := self currentWorld. aBlock value: [:string| self visible ifFalse:[ world addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: self currentHand cursorPoint hangOut: false. self changed. world displayWorld "show myself"]. self delete. world displayWorld.! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! invokeModal: allowKeyboardControl "Invoke this menu and don't return until the user has chosen a value. If the allowKeyboarControl boolean is true, permit keyboard control of the menu" ^ self invokeModalAt: self currentHand position in: self currentWorld allowKeyboard: allowKeyboardControl! ! !MenuMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:43'! positionAt: aPoint relativeTo: aMenuItem inWorld: aWorld "Note: items may not be laid out yet (I found them all to be at 0 at 0), so we have to add up heights of items above the selected item." | i yOffset sub delta | self fullBounds. "force layout" i := 0. yOffset := 0. [(sub := self submorphs at: (i := i + 1)) == aMenuItem] whileFalse: [yOffset := yOffset + sub height]. self position: aPoint - (2 @ (yOffset + 8)). "If it doesn't fit, show it to the left, not to the right of the hand." self right > aWorld worldBounds right ifTrue: [self right: aPoint x + 1]. "Make sure that the menu fits in the world." delta := self bounds amountToTranslateWithin: (aWorld worldBounds withHeight: ((aWorld worldBounds height - 18) max: (self currentHand position y) + 1)). delta isZero ifFalse: [self position: self position + delta].! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:17'! displayAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." Smalltalk isMorphic ifFalse: [^ self]. [self currentWorld addMorph: self centeredNear: aPoint. self world displayWorld. "show myself" aBlock value] ensure: [self delete]! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:18'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title w | Smalltalk isMorphic ifFalse: [^ self]. title := self allMorphs detect: [:ea | ea hasProperty: #titleString]. title := title submorphs first. self visible: false. w := self currentWorld. aBlock value: [:string| self visible ifFalse: [ w addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: Sensor cursorPoint hangOut: false. self changed. w displayWorld "show myself" ]. self delete. w displayWorld.! ! !Morph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:18'! fromFileName: fullName "Reconstitute a Morph from the file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | aFileStream morphOrList | aFileStream := (MultiByteBinaryOrTextStream with: ((FileStream readOnlyFileNamed: fullName) binary contentsOfEntireFile)) binary reset. morphOrList := aFileStream fileInObjectAndCode. (morphOrList isKindOf: SqueakPage) ifTrue: [morphOrList := morphOrList contentsMorph]. Smalltalk isMorphic ifTrue: [Project current world addMorphsAndModel: morphOrList] ifFalse: [morphOrList isMorph ifFalse: [self inform: 'Can only load a single morph into an mvc project via this mechanism.' translated]. morphOrList openInWorld]! ! !AllPlayersTool class methodsFor: '*Etoys-Squeakland-parts bin' stamp: 'ct 9/12/2020 14:26'! allPlayersToolForActiveWorld "Launch an AllPlayersTool to view the scripted objects of the active world" | aTool | aTool := self newStandAlone. aTool center: self currentWorld center. ^ aTool " AllPlayersTool allPlayersToolForActiveWorld "! ! !AllScriptsTool class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:26'! allScriptsToolForActiveWorld "Launch an AllScriptsTool to view scripts of the active world" | aTool | aTool := self newColumn. aTool initializeFor: self currentWorld presenter. ^ aTool! ! !AnonymousSoundMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:26'! fromFileName: fullName "Create an instance of the receiver from the given file path." | newPlayer aSound ext aName | newPlayer := self new initialize. ('*aif*' match: fullName) ifTrue: [aSound := SampledSound fromAIFFfileNamed: fullName]. ('*wav' match: fullName) ifTrue: [aSound := SampledSound fromWaveFileNamed: fullName]. newPlayer := self new. ext := FileDirectory extensionFor: fullName. aName := (FileDirectory on: fullName) pathParts last. ext size > 0 ifTrue: [aName := aName copyFrom: 1 to: (aName size - (ext size + 1))]. newPlayer sound: aSound interimName: aName. newPlayer openInWorld; position: self currentWorld center.! ! !BookMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:27'! openFromFile: fullName "Reconstitute a Morph from the selected file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | book aFileStream | Smalltalk verifyMorphicAvailability ifFalse: [^ self]. aFileStream := FileStream readOnlyFileNamed: fullName. book := BookMorph new. book setProperty: #url toValue: aFileStream url. book fromRemoteStream: aFileStream. aFileStream close. Smalltalk isMorphic ifTrue: [self currentWorld addMorphsAndModel: book] ifFalse: [book isMorph ifFalse: [^self inform: 'Can only load a single morph\into an mvc project via this mechanism.' withCRs translated]. book openInWorld]. book goToPage: 1! ! !EventRecordingSpace class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:46'! openFromPlaybackButton: aButton "Open an EventRecordingSpace derived from a playback button. The primary reason for doing this would be to re-record voiceover." | aSpace | aSpace := EventRecordingSpace new. aSpace initializeFromPlaybackButton: aButton. aSpace center: self currentWorld center. aSpace openInWorld! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer centerAt: aPoint "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels. This variant is only for calling from within a Morphic project." "FillInTheBlankMorph request: 'Type something, then type CR.' initialAnswer: 'yo ho ho!!' centerAt: Display center" ^ self request: queryString initialAnswer: defaultAnswer centerAt: aPoint inWorld: self currentWorld! ! !FillInTheBlankMorph class methodsFor: '*Etoys-Squeakland-instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: self currentHand cursorPoint inWorld: self currentWorld onCancelReturn: cancelResponse! ! !HandMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:11'! showEvents: aBool "HandMorph showEvents: true" "HandMorph showEvents: false" ShowEvents := aBool. aBool ifFalse: [ Project current world invalidRect: (0 at 0 extent: 250 at 120)].! ! !InternalThreadNavigationMorph class methodsFor: 'known threads' stamp: 'ct 9/11/2020 20:15'! openThreadNamed: nameOfThread atIndex: anInteger beKeyboardHandler: aBoolean "Activate the thread of the given name, from the given index; set it up to be navigated via desktop keys if indicated" | coll nav | coll := self knownThreads at: nameOfThread ifAbsent: [^self]. nav := Project current world submorphThat: [ :each | (each isKindOf: self) and: [each threadName = nameOfThread]] ifNone: [nav := self basicNew. nav listOfPages: coll; threadName: nameOfThread index: anInteger; initialize; openInWorld; positionAppropriately. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav]. ^ self]. nav listOfPages: coll; threadName: nameOfThread index: anInteger; removeAllMorphs; addButtons. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav].! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! chooseFrom: aList lines: linesArray title: queryString "Choose an item from the given list. Answer the index of the selected item." | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString. 1 to: aList size do: [:i| menu add: (aList at: i) asString target: aBlock selector: #value: argument: i. (linesArray includes: i) ifTrue:[menu addLine]]. MenuIcons decorateMenu: menu. result := 0. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! confirm: queryString trueChoice: trueChoice falseChoice: falseChoice "Put up a yes/no menu with caption queryString. The actual wording for the two choices will be as provided in the trueChoice and falseChoice parameters. Answer true if the response is the true-choice, false if it's the false-choice. This is a modal question -- the user must respond one way or the other." "MenuMorph confirm: 'Are you hungry?' trueChoice: 'yes, I''m famished' falseChoice: 'no, I just ate'" | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: trueChoice target: aBlock selector: #value: argument: true. menu add: falseChoice target: aBlock selector: #value: argument: false. MenuIcons decorateMenu: menu. [menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. result == nil] whileTrue. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:22'! inform: queryString "MenuMorph inform: 'I like Squeak'" | menu | menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: 'OK' translated target: self selector: #yourself. MenuIcons decorateMenu: menu. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true.! ! !MorphHierarchy class methodsFor: 'opening' stamp: 'ct 9/12/2020 14:45'! openOrDelete | oldMorph | oldMorph := Project current world submorphs detect: [:each | each hasProperty: #morphHierarchy] ifNone: [| newMorph | newMorph := self new asMorph. newMorph bottomLeft: self currentHand position. newMorph openInWorld. newMorph isFullOnScreen ifFalse: [newMorph goHome]. ^ self]. "" oldMorph delete! ! !MorphWorldController methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 15:15'! controlTerminate "This window is becoming inactive; restore the normal cursor." Cursor normal show. self activeWorld: nil; activeHand: nil; activeEvent: nil.! ! !MorphicEvent methodsFor: 'initialize' stamp: 'ct 9/12/2020 15:22'! becomeActiveDuring: aBlock "Make the receiver the activeEvent during the evaluation of aBlock." | priorEvent | priorEvent := self activeEvent. self activeEvent: self. ^ aBlock ensure: [ "check to support project switching." self activeEvent == self ifTrue: [self activeEvent: priorEvent]]! ! !MultiWindowLabelButtonMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:16'! performAction "Override to interpret the actionSelector as a menu accessor and to activate that menu." actionSelector ifNil: [^ self]- (model perform: actionSelector) ifNotNil: [:menu | menu invokeModalAt: self position - (0 at 5) in: self currentWorld allowKeyboard: Preferences menuKeyboardControl].! ! !NativeImageSegment methodsFor: 'read/write segment' stamp: 'ct 9/12/2020 14:15'! smartFillRoots: dummy | refs known ours ww blockers | "Put all traced objects into my arrayOfRoots. Remove some that want to be in outPointers. Return blockers, an IdentityDictionary of objects to replace in outPointers." blockers := dummy blockers. known := (refs := dummy references) size. refs keys do: [:obj | "copy keys to be OK with removing items" (obj isSymbol) ifTrue: [refs removeKey: obj. known := known-1]. (obj class == PasteUpMorph) ifTrue: [ obj isWorldMorph & (obj owner == nil) ifTrue: [ (dummy project ~~ nil and: [obj == dummy project world]) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: 'The worldMorph of a different world')]]]. "Make a ProjectViewMorph here" "obj class == Project ifTrue: [Transcript show: obj; cr]." (blockers includesKey: obj) ifTrue: [ refs removeKey: obj ifAbsent: [known := known+1]. known := known-1]. ]. ours := (dummy project ifNil: [Project current]) world. refs keysDo: [:obj | obj isMorph ifTrue: [ ww := obj world. (ww == ours) | (ww == nil) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: obj printString, ' from another world')]]]. "keep original roots on the front of the list" dummy rootObject do: [:rr | refs removeKey: rr ifAbsent: []]. (self respondsTo: #classOrganizersBeRoots:) ifTrue: "an EToys extension" [self classOrganizersBeRoots: dummy]. ^dummy rootObject, refs keys asArray! ! !NebraskaSenderMorph methodsFor: 'parts bin' stamp: 'ct 9/12/2020 14:14'! initializeToStandAlone super initializeToStandAlone. self installModelIn: Project current world.! ! !NebraskaServerMorph class methodsFor: 'as yet unclassified' stamp: 'ct 9/12/2020 14:14'! serveWorld ^ self serveWorld: self currentWorld ! ! !NewVariableDialogMorph methodsFor: 'build' stamp: 'ct 9/12/2020 14:14'! rebuild | buttonColor itsName enableDecimalPlaces | self removeAllMorphs. self addAColumn: { self lockedString: self title. }. self addSeparator. self addARow: { self inAColumn: { (self addARow: { self lockedString: 'Name:' translated. self spacer. varNameText := self newTextMorph contentsWrapped: self varName; selectAll; crAction: (MessageSend receiver: self selector: #doAccept); yourself }) cellPositioning: #center. self inAColumn: { (self addARow: { self lockedString: 'Type:' translated. self spacer. varTypeButton := self buildVarTypeButton }) cellPositioning: #center. } named: #varType. } }. self currentHand newKeyboardFocus: varNameText. self addSeparator. self addDecimalPlaces. enableDecimalPlaces := false. (#(#Number #Point) includes: self varType) ifTrue: [ enableDecimalPlaces := true]. self allMorphsDo: [ :each | itsName := each knownName. (#(decimalPlaces) includes: itsName) ifTrue: [self enable: each when: enableDecimalPlaces]]. buttonColor := self color lighter. self addARow: { self inAColumn: { (self addARow: { self buttonNamed: 'Accept' translated action: #doAccept color: buttonColor help: 'keep changes made and close panel' translated. self buttonNamed: 'Cancel' translated action: #doCancel color: buttonColor help: 'cancel changes made and close panel' translated. }) listCentering: #center } }! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! step "Let all views know that some of my objects need to be updated." self monitorList do: [ :object | object ifNotNil: [self changed: #objectChanged with: object]]. self monitorList ifEmpty: [ self world stopStepping: self selector: #step ].! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! world ^ Project current world! ! !ObjectExplorer methodsFor: 'accessing - view' stamp: 'ct 9/12/2020 14:12'! views ^ self findDeepSubmorphsIn: self world that: [:morph | morph modelOrNil = self]! ! !ObjectsTool methodsFor: 'search' stamp: 'ct 9/12/2020 14:45'! showSearchPane "Set the receiver up so that it shows the search pane" | tabsPane aPane | modeSymbol == #search ifTrue: [ ^self ]. self partsBin removeAllMorphs. tabsPane := self tabsPane. aPane := self newSearchPane. self replaceSubmorph: tabsPane by: aPane. self modeSymbol: #search. self showMorphsMatchingSearchString. self currentHand newKeyboardFocus: aPane! ! !ParagraphEditor methodsFor: 'nonediting/nontyping keys' stamp: 'ct 9/12/2020 14:12'! escapeToDesktop: characterStream "Pop up a morph to field keyboard input in the context of the desktop" Smalltalk isMorphic ifTrue: [ Project current world putUpWorldMenuFromEscapeKey]. ^ true! ! !ParagraphEditor methodsFor: '*Etoys-Squeakland-editing keys' stamp: 'ct 9/12/2020 14:07'! shiftEnclose: characterStream "Insert or remove bracket characters around the current selection. Flushes typeahead." | char left right startIndex stopIndex oldSelection which text | char := sensor keyboard. char = $9 ifTrue: [ char := $( ]. char = $, ifTrue: "[ char := $< ]" [self closeTypeIn. Project current world showSourceKeyHit. ^ true]. char = $[ ifTrue: [ char := ${ ]. char = $' ifTrue: [ char := $" ]. char asciiValue = 27 ifTrue: [ char := ${ ]. "ctrl-[" self closeTypeIn. startIndex := self startIndex. stopIndex := self stopIndex. oldSelection := self selection. which := '([<{"''' indexOf: char ifAbsent: [1]. left := '([<{"''' at: which. right := ')]>}"''' at: which. text := paragraph text. ((startIndex > 1 and: [stopIndex <= text size]) and: [(text at: startIndex-1) = left and: [(text at: stopIndex) = right]]) ifTrue: ["already enclosed; strip off brackets" self selectFrom: startIndex-1 to: stopIndex. self replaceSelectionWith: oldSelection] ifFalse: ["not enclosed; enclose by matching brackets" self replaceSelectionWith: (Text string: (String with: left), oldSelection string ,(String with: right) emphasis: emphasisHere). self selectFrom: startIndex+1 to: stopIndex]. ^true! ! !PasteUpMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:10'! flapTab "Answer the tab affilitated with the receiver. Normally every flap tab is expected to have a PasteUpMorph which serves as its 'referent.'" | ww | self isFlap ifFalse: [^ nil]. ww := self presenter associatedMorph ifNil: [self]. ^ ww flapTabs detect: [:any| any referent == self] ifNone: [nil]! ! !PasteUpMorph methodsFor: 'events-processing' stamp: 'ct 9/12/2020 15:11'! processEvent: anEvent using: defaultDispatcher "Reimplemented to install the receiver as the new ActiveWorld if it is one" self isWorldMorph ifFalse: [ ^ super processEvent: anEvent using: defaultDispatcher]. ^ self activateWorld: self during: [ super processEvent: anEvent using: defaultDispatcher]! ! !PasteUpMorph methodsFor: 'flaps' stamp: 'ct 9/12/2020 14:11'! correspondingFlapTab "If there is a flap tab whose referent is me, return it, else return nil. Will also work for flaps on the edge of embedded subareas such as within scripting-areas, but more slowly." self currentWorld flapTabs do: [:aTab | aTab referent == self ifTrue: [^ aTab]]. "Catch guys in embedded worldlets" self currentWorld allMorphs do: [:aTab | ((aTab isKindOf: FlapTab) and: [aTab referent == self]) ifTrue: [^ aTab]]. ^ nil! ! !PasteUpMorph methodsFor: 'initialization' stamp: 'ct 9/12/2020 15:33'! becomeActiveDuring: aBlock "Make the receiver the activeWorld during the evaluation of aBlock." | priorWorld | priorWorld := self activeWorld. self activeWorld: self. ^ aBlock ensure: [ "check to support project switching." self activeWorld == self ifTrue: [self activeWorld: priorWorld]]! ! !PasteUpMorph methodsFor: 'menu & halo' stamp: 'ct 9/12/2020 14:07'! putUpPenTrailsSubmenu "Put up the pen trails menu" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'pen trails' translated. aMenu addStayUpItem. self addPenTrailsMenuItemsTo: aMenu. ^ aMenu popUpInWorld: self! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/12/2020 14:45'! extractScreenRegion: poly andPutSketchInHand: hand "The user has specified a polygonal area of the Display. Now capture the pixels from that region, and put in the hand as a Sketch." | screenForm outline topLeft innerForm exterior | outline := poly shadowForm. topLeft := outline offset. exterior := (outline offset: 0 at 0) anyShapeFill reverse. screenForm := Form fromDisplay: (topLeft extent: outline extent). screenForm eraseShape: exterior. innerForm := screenForm trimBordersOfColor: Color transparent. self currentHand showTemporaryCursor: nil. innerForm isAllWhite ifFalse: [hand attachMorph: (self drawingClass withForm: innerForm)]! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 20:07'! initializeDesktopCommandKeySelectors "Provide the starting settings for desktop command key selectors. Answer the dictionary." "ActiveWorld initializeDesktopCommandKeySelectors" | dict | dict := IdentityDictionary new. self defaultDesktopCommandKeyTriplets do: [:trip | | messageSend | messageSend := MessageSend receiver: trip second selector: trip third. dict at: trip first put: messageSend]. self setProperty: #commandKeySelectors toValue: dict. ^ dict! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 18:00'! putUpWorldMenuFromEscapeKey Preferences noviceMode ifFalse: [self putUpWorldMenu: self currentEvent]! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/12/2020 15:11'! install owner := nil. "since we may have been inside another world previously" self activeWorld: self. self activeHand: self hands first. "default" self activeEvent: nil. submorphs do: [:ss | ss owner isNil ifTrue: [ss privateOwner: self]]. "Transcript that was in outPointers and then got deleted." self viewBox: Display boundingBox. EventSensor default flushEvents. worldState handsDo: [:h | h initForEvents]. self installFlaps. self borderWidth: 0. "default" (Preferences showSecurityStatus and: [SecurityManager default isInRestrictedMode]) ifTrue: [self borderWidth: 2; borderColor: Color red]. self presenter allExtantPlayers do: [:player | player prepareToBeRunning]. SystemWindow noteTopWindowIn: self.! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/12/2020 14:06'! repositionFlapsAfterScreenSizeChange "Reposition flaps after screen size change" (Flaps globalFlapTabsIfAny, self localFlapTabs) do: [:aFlapTab | aFlapTab applyEdgeFractionWithin: self bounds]. Flaps doAutomaticLayoutOfFlapsIfAppropriate! ! !PasteUpMorph methodsFor: '*Tools' stamp: 'ct 9/11/2020 20:07'! defaultDesktopCommandKeyTriplets "Answer a list of triplets of the form [+ optional fourth element, a for use in desktop-command-key-help] that will provide the default desktop command key handlers. If the selector takes an argument, that argument will be the command-key event" "World initializeDesktopCommandKeySelectors" | noviceKeys expertKeys | noviceKeys := { {$o. self. #activateObjectsTool. 'Activate the "Objects Tool"' translated}. {$r. self. #restoreMorphicDisplay. 'Redraw the screen' translated}. {$z. self. #undoOrRedoCommand. 'Undo or redo the last undoable command' translated}. {$F. Project current. #toggleFlapsSuppressed. 'Toggle the display of flaps' translated}. {$N. self. #toggleClassicNavigatorIfAppropriate. 'Show/Hide the classic Navigator, if appropriate' translated}. {$M. self. #toggleShowWorldMainDockingBar. 'Show/Hide the Main Docking Bar' translated}. {$]. Smalltalk. #saveSession. 'Save the image.' translated}. }. Preferences noviceMode ifTrue: [^ noviceKeys]. expertKeys := { {$b. SystemBrowser. #defaultOpenBrowser. 'Open a new System Browser' translated}. {$k. Workspace. #open. 'Open a new Workspace' translated}. {$m. self. #putUpNewMorphMenu. 'Put up the "New Morph" menu' translated}. {$O. self. #findAMonticelloBrowser. 'Bring a Monticello window into focus.' translated}. {$t. self. #findATranscript:. 'Make a System Transcript visible' translated}. {$w. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {Character escape. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {$C. self. #findAChangeSorter:. 'Make a Change Sorter visible' translated}. {$L. self. #findAFileList:. 'Make a File List visible' translated}. {$P. self. #findAPreferencesPanel:. 'Activate the Preferences tool' translated}. {$R. Utilities. #browseRecentSubmissions. 'Make a Recent Submissions browser visible' translated}. {$W. self. #findAMessageNamesWindow:. 'Make a MessageNames tool visible' translated}. {$Z. ChangeList. #browseRecentLog. 'Browse recently-logged changes' translated}. {$\. SystemWindow. #sendTopWindowToBack. 'Send the top window to the back' translated}. {$_. Smalltalk. #quitPrimitive. 'Quit the image immediately.' translated}. {$-. Preferences. #decreaseFontSize. 'Decrease all font sizes' translated}. {$+. Preferences. #increaseFontSize. 'Increase all font sizes' translated}. }. ^ noviceKeys, expertKeys! ! !PasteUpMorph methodsFor: '*Etoys-playfield' stamp: 'ct 9/12/2020 14:10'! galleryOfPlayers "Put up a tool showing all the players in the project" (Project current world findA: AllPlayersTool) ifNotNil: [:aTool | ^ aTool comeToFront]. AllPlayersTool newStandAlone openInHand "ActiveWorld galleryOfPlayers"! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:36'! attemptCleanupReporting: whetherToReport "Try to fix up some bad things that are known to occur in some etoy projects we've seen. If the whetherToReport parameter is true, an informer is presented after the cleanups" | fixes faultyStatusControls | fixes := 0. self world ifNotNil: [:world | world submorphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m submorphs isEmpty]] thenDo: [:m | m delete. fixes := fixes + 1]]. TransformationMorph allSubInstancesDo: [:m | (m player notNil and: [m renderedMorph ~~ m]) ifTrue: [m renderedMorph visible ifFalse: [m renderedMorph visible: true. fixes := fixes + 1]]]. (Player class allSubInstances select: [:cl | cl isUniClass and: [cl instanceCount > 0]]) do: [:aUniclass | fixes := fixes + aUniclass cleanseScripts]. self presenter flushPlayerListCache; allExtantPlayers. faultyStatusControls := ScriptStatusControl allInstances select: [:m |m fixUpScriptInstantiation]. fixes := fixes + faultyStatusControls size. ScriptNameTile allInstancesDo: [:aTile | aTile submorphs isEmpty ifTrue: [aTile setLiteral: aTile literal. fixes := fixes + 1]]. whetherToReport ifTrue: [self inform: ('{1} [or more] repair(s) made' translated format: {fixes printString})] ifFalse: [fixes > 0 ifTrue: [Transcript cr; show: fixes printString, ' repairs made to existing content.']] " ActiveWorld attemptCleanupReporting: true. ActiveWorld attemptCleanupReporting: false. "! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:09'! hideAllPlayers "Remove all Viewers belonging to scripted players associated with the receiver or any of its subjects from the screen." | a | a := OrderedCollection new. self allMorphsDo: [ :x | (self presenter currentlyViewing: x player) ifTrue: [a add: x player viewerFlapTab]]. a do: [ :each | each dismissViaHalo].! ! !PasteUpMorph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:08'! modernizeBJProject "Prepare a kids' project from the BJ fork of September 2000 -- a once-off thing for converting such projects forward to a modern 3.1a image, in July 2001. Except for the #enableOnlyGlobalFlapsWithIDs: call, this could conceivably be called upon reloading *any* project, just for safety." "ActiveWorld modernizeBJProject" self flag: #deprecate "ct: No senders". ScriptEditorMorph allInstancesDo: [:m | m userScriptObject]. Flaps enableOnlyGlobalFlapsWithIDs: {'Supplies' translated}. self abandonOldReferenceScheme. self relaunchAllViewers.! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:11'! abandonUnsituatedPlayers "If any objects in the project have references, in player-valued variables, to other objects otherwise not present in the project, abandon them and replace former references to them by references to Dot" | aList dot slotInfo varName ref allPlayers count | count := 0. allPlayers := self presenter reallyAllExtantPlayersNoSort. aList := allPlayers select: [:m | m belongsToUniClass]. dot := self presenter standardPlayer. aList do: [:p | p class slotInfo associationsDo: [:assoc | slotInfo := assoc value. varName := assoc key. (slotInfo type = #Player) ifTrue: [ref := p instVarNamed: varName. (allPlayers includes: ref) ifFalse: [p instVarNamed: varName put: dot. count := count + 1. Transcript cr; show: ('Variable named "{1}" in player named "{2}" changed to point to Dot' translated format: {varName. ref externalName})]]]]. aList := nil. "Increases chance of the next line having desired effect." self inform: ('{1} item(s) fixed up' translated format: {count}). WorldState addDeferredUIMessage: [Smalltalk garbageCollect]! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/12/2020 14:07'! putUpShowSourceMenu: evt title: aTitle "Put up a menu in response to the show-source button being hit" | menu | self bringTopmostsToFront. "put up the show-source menu" menu := (TheWorldMenu new adaptToWorld: self) buildShowSourceMenu. menu addTitle: aTitle. menu popUpEvent: evt in: self. ^ menu! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/11/2020 18:01'! showSourceKeyHit "The user hit the 'show source' key on the XO. Our current take on this is simply to put up the world menu..." ^ self putUpShowSourceMenu: self currentEvent title: 'etoys source' translated! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menus' stamp: 'ct 9/12/2020 14:46'! presentDesktopColorMenu "Present the menu that governs the fill style of the squeak desktop." | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'desktop color' translated. self fillStyle addFillStyleMenuItems: aMenu hand: self currentHand from: self. aMenu addLine. aMenu add: 'solid fill' translated action: #useSolidFill. aMenu add: 'gradient fill' translated action: #useGradientFill. aMenu add: 'bitmap fill' translated action: #useBitmapFill. aMenu add: 'default fill' translated action: #useDefaultFill. ^ aMenu popUpInWorld! ! !EventTimeline methodsFor: 'dropping/grabbing' stamp: 'ct 9/11/2020 19:47'! acceptDroppingMorph: aMorph event: evt "Accept the drop of a morph." | aRect anEventRoll itsDuration itsWidthAfterDrop | self flag: #deferred. "This is a possible place for discovering whether the drop would have damaging effects on the mouse track..." (aMorph isKindOf: MouseEventSequenceMorph) ifTrue: [itsDuration := aMorph durationInMilliseconds. itsWidthAfterDrop := itsDuration // self eventRoll millisecondsPerPixel. super acceptDroppingMorph: aMorph event: evt. aMorph bounds: ((aMorph left @ 6) extent: (itsWidthAfterDrop @ aMorph height)). submorphs do: [:m | ((m ~~ aMorph) and: [m isKindOf: MouseEventSequenceMorph]) ifTrue: [(m bounds intersects: aMorph bounds) ifTrue: ["Eureka" aMorph delete. aMorph position: 100 at 100. aMorph openInWorld. aMorph flash. ^ self]]]] ifFalse: [super acceptDroppingMorph: aMorph event: evt] . aRect := (((aMorph left + 10) max: 10) @ 0) extent: 100@ 10. (anEventRoll := self eventRoll) pushChangesBackToEventTheatre. "Note that will ultimately result in replacement of the receiver by a new timeline" aMorph delete. self currentWorld abandonAllHalos. anEventRoll scrollPaneForRoll scrollHorizontallyToShow: aRect! ! !PasteUpMorph class methodsFor: '*Etoys-Squeakland-eToys-scripting' stamp: 'ct 9/12/2020 14:09'! putativeAdditionsToViewerCategoryPlayfieldOptions "Answer playfield options additions. Some of these are not yet underpinned by code in the current image; these will follow in due course." self flag: #deprecate. "ct: No senders" ^ #(#'playfield options' ( (command roundUpStrays 'Bring back all objects whose current coordinates keep them from being visible, so that at least a portion of each of my interior objects can be seen.') (command makeFitContents 'Adjust my bounds so that I fit precisely around all the objects within me') (command showAllPlayers 'Make visible the viewers for all players which have user-written scripts in this playfield.') (command hideAllPlayers 'Make invisible the viewers for all players in this playfield. This will save space before you publish this project') (command shuffleSubmorphs 'Rearranges my contents in random order') (command showAllObjectNames 'show names beneath all the objects currently in my interior, except for those for which showing such names is inappropriate.') (command hideAllObjectNames 'stop showing names beneath all the objects of my interior, If any of them is marked to "always show name", remove that designation')))! ! !PartsBin class methodsFor: '*Etoys-Squeakland-thumbnail cache' stamp: 'ct 9/12/2020 14:11'! rebuildIconsWithProgress "Put up an eye-catching progress morph while doing a complete rebuild of all the parts icons in the system." | fixBlock | fixBlock := Project current displayProgressWithJump: 'Building icons' translated. self clearThumbnailCache. self cacheAllThumbnails. fixBlock value. Project current world fullRepaintNeeded.! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: ((submorphs at: (2 max: submorphs size)) bottomRight + (2 at 1))). "inHotZone := evt ifNil: [true] ifNotNil: [rect containsPoint: evt cursorPoint]." aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/11/2020 20:47'! createMultipleTestScripts: aCount "Simulate the action of dropping a copy of the receiver to launch a new script -- for performance testing. To use: Open an Inspector on some tile command in a Viewer, e.g. on 'Car forward 5'. In the trash pane of that Inspector, then, evaluate expressions like: [self createMultipleTestScripts: 10] timeToRun. and MessageTally spyOn: [self createMultipleTestScripts: 4] " | aPosition | aPosition := 10 at 10. 1 to: aCount do: [:i | self forceScriptCreationAt: aPosition. aPosition := aPosition + (0 @ 50). "avoid dropping into existing scriptor" Project current world doOneCycle] "refresh viewer"! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/12/2020 14:47'! forceScriptCreationAt: aPosition "For performance testing." | dup | dup := self duplicate. dup eventHandler: nil. "Remove viewer-related evt mouseover feedback" dup formerPosition: self currentHand position. self currentHand attachMorph: dup; simulateMorphDropAt: aPosition.! ! !PhraseTileForTest methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTest methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := CompoundTileMorph new. guyToTake setNamePropertyTo: 'TestTile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !PhraseTileForTimesRepeat methodsFor: 'hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTimesRepeat methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := TimesRepeatTile new. guyToTake setNamePropertyTo: 'Repeat Tile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! adoptScriptsFrom "Let the user click on another object form which the receiver should obtain scripts and code" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ Beeper beep]. (aMorph renderedMorph isSketchMorph and: [aMorph player belongsToUniClass] and: [self belongsToUniClass not]) ifTrue: [costume acquirePlayerSimilarTo: aMorph player] ifFalse: [Beeper beep].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! beRevealedInActiveWorld "Reveal my corresponding morph in the active world" self revealPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! grabPlayerInActiveWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" ^ self grabPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/12/2020 14:47'! grabPlayerIn: aWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" | aMorph newPosition | self costume == aWorld ifTrue: [^ self]. self currentHand releaseMouseFocus. (aMorph := self costume) visible: true. newPosition := self currentHand position - (aMorph extent // 2). aMorph isInWorld ifTrue: [aMorph goHome. aMorph formerPosition: aMorph positionInWorld] ifFalse: [aMorph formerPosition: aWorld center]. aMorph formerOwner: Project current world. aMorph position: newPosition. self currentHand targetOffset: aMorph position - self currentHand position; addMorphBack: aMorph.! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! impartSketchScripts "Let the user designate another object to which my scripts and code should be imparted" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ self]. (aMorph renderedMorph isSketchMorph) ifTrue: [ aMorph acquirePlayerSimilarTo: self].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:44'! offerAlternateViewerMenuFor: aViewer event: evt "Put up an alternate Viewer menu on behalf of the receiver." | menu world | world := aViewer world. menu := MenuMorph new defaultTarget: self. (costumes notNil and: [ (costumes size > 1 or: [costumes size == 1 and: [costumes first ~~ costume renderedMorph]])]) ifTrue: [menu add: 'forget other costumes' translated target: self selector: #forgetOtherCostumes]. menu add: 'expunge empty scripts' translated target: self action: #expungeEmptyScripts. menu addLine. menu add: 'choose vocabulary...' translated target: aViewer action: #chooseVocabulary; balloonTextForLastItem: 'Choose a different vocabulary for this Viewer.' translated. menu add: 'choose limit class...' translated target: aViewer action: #chooseLimitClass; balloonTextForLastItem: 'Specify what the limitClass should be for this Viewer -- i.e., the most generic class whose methods and categories should be considered here.' translated. menu add: 'open standard lexicon' translated target: aViewer action: #openLexicon; balloonTextForLastItem: 'open a window that shows the code for this object in traditional programmer format' translated. menu add: 'open lexicon with search pane' translated target: aViewer action: #openSearchingProtocolBrowser; balloonTextForLastItem: 'open a lexicon that has a type-in pane for search (not recommended!!)' translated. menu addLine. menu add: 'inspect morph' translated target: costume selector: #inspect. menu add: 'inspect player' translated target: self selector: #inspect. self belongsToUniClass ifTrue: [ menu add: 'browse class' translated target: self action: #browsePlayerClass. menu add: 'inspect class' translated target: self class action: #inspect]. menu add: 'inspect this Viewer' translated target: aViewer selector: #inspect. menu add: 'inspect this Vocabulary' translated target: aViewer currentVocabulary selector: #inspect. menu addLine. menu add: 'relaunch this Viewer' translated target: aViewer action: #relaunchViewer. menu add: 'attempt repairs' translated target: Project current world action: #attemptCleanup. menu add: 'destroy all this object''s scripts' translated target: self action: #destroyAllScripts. menu add: 'view morph directly' translated target: aViewer action: #viewMorphDirectly. menu balloonTextForLastItem: 'opens a Viewer directly on the rendered morph.' translated. costume renderedMorph isSketchMorph ifTrue: [ menu addLine. menu add: 'impart scripts to...' translated target: self action: #impartSketchScripts]. ^ menu popUpEvent: evt in: world! ! !Player methodsFor: 'scripts-standard' stamp: 'ct 9/12/2020 14:48'! hide "Make the object be hidden, as opposed to visible" self currentHand ifNotNil: [:hand | (hand keyboardFocus == self costume renderedMorph) ifTrue: [ hand releaseKeyboardFocus]]. self costume hide.! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:45'! getLastKeystroke "Answer the last keystroke fielded" ^ Project current world lastKeystroke! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:41'! setLastKeystroke: aString "Set the last keystroke fielded" ^ self currentWorld lastKeystroke: aString! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/12/2020 14:49'! setSecondColor: aColor "Setter for costume's second color, if it's using gradient fill; if not, does nothing" | morph fillStyle colorToUse | morph := costume renderedMorph. fillStyle := morph fillStyle. fillStyle isGradientFill ifFalse: [^ self]. colorToUse := (costume isWorldMorph and: [aColor isColor]) ifTrue: [aColor alpha: 1.0] "reject any translucency" ifFalse: [aColor]. fillStyle lastColor: colorToUse forMorph: morph hand: self currentHand.! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:47'! addInstanceVariable "Offer the user the opportunity to add an instance variable, and if he goes through with it, actually add it." Project current world addMorphInLayer: (NewVariableDialogMorph on: self costume) centeredNear: (self currentHand ifNil:[Sensor]) cursorPoint! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:46'! allPossibleWatchersFromWorld "Answer a list of all UpdatingStringMorphs, PlayerReferenceReadouts, ThumbnailMorphs, and UpdatingReferenceMorphs in the Active world and its hidden book pages, etc., which have me or any of my siblings as targets" | a | a := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: a. ^ a select: [:e | e isEtoyReadout and: [e target class == self class]]! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/12/2020 14:48'! offerGetterTiles: slotName "For a player-type slot, offer to build convenient compound tiles that otherwise would be hard to get" | typeChoices typeChosen thePlayerThereNow slotChoices slotChosen getterTiles aCategoryViewer playerGetter | typeChoices := Vocabulary typeChoices. typeChosen := UIManager default chooseFrom: (typeChoices collect: [:t | t translated]) values: typeChoices title: ('Choose the TYPE of data to get from {1}''s {2}' translated format: {self externalName. slotName translated}). typeChosen isEmptyOrNil ifTrue: [^self]. thePlayerThereNow := self perform: slotName asGetterSelector. thePlayerThereNow ifNil: [thePlayerThereNow := self presenter standardPlayer]. slotChoices := thePlayerThereNow slotNamesOfType: typeChosen. slotChoices isEmpty ifTrue: [^self inform: 'sorry -- no slots of that type' translated]. slotChoices sort. slotChosen := UIManager default chooseFrom: (slotChoices collect: [:t | t translated]) values: slotChoices title: ('Choose the datum you want to extract from {1}''s {2}' translated format: {self externalName. slotName translated}). slotChosen isEmptyOrNil ifTrue: [^self]. "Now we want to tear off tiles of the form holder's valueAtCursor's foo" getterTiles := nil. aCategoryViewer := CategoryViewer new initializeFor: thePlayerThereNow categoryChoice: 'basic'. getterTiles := aCategoryViewer getterTilesFor: slotChosen asGetterSelector type: typeChosen. aCategoryViewer := CategoryViewer new initializeFor: self categoryChoice: 'basic'. playerGetter := aCategoryViewer getterTilesFor: slotName asGetterSelector type: #Player. getterTiles submorphs first acceptDroppingMorph: playerGetter event: nil. "the pad" "simulate a drop" getterTiles makeAllTilesGreen. getterTiles justGrabbedFromViewer: false. (getterTiles firstSubmorph) changeTableLayout; hResizing: #shrinkWrap; vResizing: #spaceFill. self currentHand attachMorph: getterTiles.! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:46'! addPatchVarNamed: nameSymbol | f | f := KedamaPatchMorph newExtent: self costume dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). self addInstanceVariable2Named: nameSymbol type: #Patch value: f player. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatch | f usedNames newName | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f kedamaWorld: self costume renderedMorph. usedNames := Project current world allKnownNames, self class instVarNames. newName := Utilities keyLike: f innocuousName satisfying: [:aName | (usedNames includes: aName) not]. f setNameTo: newName. self createSlotForPatch: f. self addToPatchDisplayList: f assuredPlayer. self costume world primaryHand attachMorph: f. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtle | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). self costume world primaryHand attachMorph: m. ^ m! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleSilently | m | m := KedamaTurtleMorph new openInWorld. self useTurtle: m player. m turtleCount: 0. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !Player methodsFor: '*Etoys-Squeakland-scripts-standard' stamp: 'ct 9/11/2020 20:41'! printInTranscript "Print a line representing the receiver in the Transcript" Project current world findATranscript: nil. Transcript cr; show: (Time now printString copyWithoutAll: '()'); space; show: self costume printString.! ! !Player methodsFor: '*Etoys-Squeakland-slots-user' stamp: 'ct 9/12/2020 14:47'! changeSlotInfo: aSymbol Project current world addMorphInLayer: (ModifyVariableDialogMorph on: self costume slot: aSymbol) centeredNear: (self currentHand ifNil: [Sensor]) cursorPoint.! ! !Player methodsFor: '*Etoys-Squeakland-slot getters/setters' stamp: 'ct 9/12/2020 14:47'! handUserPictureOfPenTrail "Called from the user-interface: hand the user a picture of the pen trail" self getHasPenTrails ifFalse: [ ^ self inform: 'no pen trails present' translated]. self currentHand attachMorph: (SketchMorph new form: self getPenTrailGraphic).! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! kedamaWorld ^ Project current world findDeeplyA: KedamaMorph ! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatchForSet | f | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). f kedamaWorld: self costume renderedMorph. self createSlotForPatch: f. ^ f! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleForSet | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !PlayerSurrogate methodsFor: 'menu' stamp: 'ct 9/11/2020 20:37'! revealThisObject "Reveal the object I represent" playerRepresented revealPlayerIn: Project current world! ! !PlayerSurrogate methodsFor: '*Etoys-Squeakland-as yet unclassified' stamp: 'ct 9/11/2020 20:40'! forciblyRenamePlayer "Allow the receiver to seize a name already nominally in use in the project." | current reply currentlyBearingName newNameForHim binding | current := playerRepresented knownName. reply := FillInTheBlank request: 'Type the name you insist upon' translated initialAnswer: current. reply isEmptyOrNil ifTrue: [^ self]. Preferences uniquePlayerNames ifFalse: [^ self costume renameTo: reply]. reply := (reply asIdentifier: true) asSymbol. reply = current ifTrue: [^ self inform: 'no change' translated]. binding := Project current world referencePool hasBindingOf: reply. binding ifNotNil: [ currentlyBearingName := binding value. newNameForHim := Utilities keyLike: reply satisfying: [:name | (Project current world referencePool includesKey: name) not]. currentlyBearingName renameTo: newNameForHim]. playerRepresented renameTo: reply. self inform: (binding ifNil: [('There was no conflict; this object is now named {1}' translated format: {reply})] ifNotNil: ['Okay, this object is now named\{1}\and the object formerly known by this name is now called\{2}' translated format: {reply. newNameForHim}]).! ! !PlayerType methodsFor: 'tiles' stamp: 'ct 9/11/2020 20:37'! defaultArgumentTile "Answer a tile to represent the type" ^ Project current world presenter standardPlayer tileToRefer! ! !KedamaPatchType methodsFor: 'tile protocol' stamp: 'ct 9/11/2020 20:15'! defaultArgumentTile "Answer a tile to represent the type" | patch ks k p | patch := KedamaPatchTile new typeColor: self typeColor. ks := self world allMorphs select: [:e | e isKindOf: KedamaMorph]. ks isEmpty ifFalse: [ k := ks first. p := k player getPatch. ] ifTrue: [ k := KedamaPatchMorph new. k assuredPlayer. p := k player. ]. patch usePatch: p. ^ patch! ! !PluggableFileList methodsFor: 'StandardFileMenu' stamp: 'ct 9/12/2020 14:50'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PluggableListMorph methodsFor: 'model access - keystroke' stamp: 'ct 9/11/2020 18:01'! specialKeyPressed: asciiValue "A special key with the given ascii-value was pressed; dispatch it" | oldSelection nextSelection max howManyItemsShowing | (#(8 13) includes: asciiValue) ifTrue: [ "backspace key - clear the filter, restore the list with the selection" model okToChange ifFalse: [^ self]. self removeFilter. priorSelection ifNotNil: [ | prior | prior := priorSelection. priorSelection := self getCurrentSelectionIndex. asciiValue = 8 ifTrue: [ self changeModelSelection: prior ] ]. ^ self ]. asciiValue = 27 ifTrue: [" escape key" ^ self currentEvent shiftPressed ifTrue: [self currentEvent putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]]. max := self maximumSelection. max > 0 ifFalse: [^ self]. nextSelection := oldSelection := self selectionIndex. asciiValue = 31 ifTrue: [" down arrow" nextSelection := oldSelection + 1. nextSelection > max ifTrue: [nextSelection := 1]]. asciiValue = 30 ifTrue: [" up arrow" nextSelection := oldSelection - 1. nextSelection < 1 ifTrue: [nextSelection := max]]. asciiValue = 1 ifTrue: [" home" nextSelection := 1]. asciiValue = 4 ifTrue: [" end" nextSelection := max]. howManyItemsShowing := self numSelectionsInView. asciiValue = 11 ifTrue: [" page up" nextSelection := 1 max: oldSelection - howManyItemsShowing]. asciiValue = 12 ifTrue: [" page down" nextSelection := oldSelection + howManyItemsShowing min: max]. model okToChange ifFalse: [^ self]. "No change if model is locked" oldSelection = nextSelection ifTrue: [^ self flash]. ^ self changeModelSelection: (self modelIndexFor: nextSelection)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:50'! startUpCenteredWithCaption: captionOrNil "Differs from startUpWithCaption: by appearing with cursor in the menu, and thus ready to act on mouseUp, without requiring user tweak to confirm" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint - (20 @ 0)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." self flag: #fix. "mt: Could we manage to open pop-up menus in Morphic without accessing self currentHand?" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil icon: aForm "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil icon: aForm at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithoutKeyboard "Display and make a selection from the receiver as long as the button is pressed. Answer the current selection. Do not allow keyboard input into the menu" ^ self startUpWithCaption: nil at: ((self currentHand ifNil: [Sensor]) cursorPoint) allowKeyboard: false! ! !PopUpMenu methodsFor: '*Morphic-Menus' stamp: 'ct 9/11/2020 20:37'! morphicStartUpWithCaption: captionOrNil icon: aForm at: location allowKeyboard: aBoolean "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard." selection := Cursor normal showWhile: [| menuMorph | menuMorph := MVCMenuMorph from: self title: nil. (captionOrNil notNil or: [aForm notNil]) ifTrue: [menuMorph addTitle: captionOrNil icon: aForm]. MenuIcons decorateMenu: menuMorph. menuMorph invokeAt: location in: self currentWorld allowKeyboard: aBoolean]. ^ selection! ! !PopUpMenu methodsFor: '*Etoys-Squeakland-basic control sequence' stamp: 'ct 9/11/2020 20:37'! startUpWithCaption: captionOrNil at: location allowKeyboard: allowKeyboard centered: centered "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard If centered is true, the menu items are displayed centered.." | maxHeight aMenu | (ProvideAnswerNotification signal: captionOrNil) ifNotNil: [:answer | ^ selection := answer ifTrue: [1] ifFalse: [2]]. maxHeight := Display height*3//4. self frameHeight > maxHeight ifTrue: [^ self startUpSegmented: maxHeight withCaption: captionOrNil at: location allowKeyboard: allowKeyboard]. Smalltalk isMorphic ifTrue:[ selection := Cursor normal showWhile: [aMenu := MVCMenuMorph from: self title: captionOrNil. centered ifTrue: [aMenu submorphs allButFirst do: [:m | m setProperty: #centered toValue: true]]. aMenu invokeAt: location in: self currentWorld allowKeyboard: allowKeyboard]. ^ selection]. frame ifNil: [self computeForm]. Cursor normal showWhile: [self displayAt: location withCaption: captionOrNil during: [self controlActivity]]. ^ selection! ! !PopUpMenu class methodsFor: '*Etoys-Squeakland-dialogs' stamp: 'ct 9/12/2020 14:52'! informCenteredAboveCursor: aString "Put up an informer showing the given string in a box, with the OK button for dismissing the informer having the cursor at its center." "PopUpMenu informCenteredAboveCursor: 'I like Squeak\how about you?' withCRs" | lines maxWid xCoor | lines := Array streamContents: [:aStream | aString linesDo: [:l | aStream nextPut: l]]. maxWid := (lines collect: [:l | Preferences standardMenuFont widthOfString: l]) max. xCoor := self currentHand cursorPoint x - (maxWid // 2). ((xCoor + maxWid) > self currentWorld right) ifTrue: [xCoor := self currentWorld right]. "Caters to problematic PopUpMenu boundary behavior" (PopUpMenu labels: 'OK' translated) startUpWithCaption: aString at: (xCoor @ self currentHand cursorPoint y) allowKeyboard: true centered: true.! ! !PreferenceWizardMorph methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:35'! initializePreviewWorld | w1 w2 w3 | previewWorld := PasteUpMorph new hResizing: #spaceFill; vResizing: #spaceFill; viewBox: (0 at 0 corner: 500 at 500); layoutFrame: (LayoutFrame fractions: (0.3 @ 0 corner: 1.0 @ 1.0) offsets: (0@ titleMorph height corner: 0 @ buttonRowMorph height negated)); fillStyle: Project current world fillStyle; borderWidth: 2; borderColor: Color white; cornerStyle: (self hasLowPerformance ifTrue: [#square] ifFalse: [#rounded]); yourself. w1 := (ToolSet browse: Morph selector: #drawOn:) dependents detect: [:ea | ea isSystemWindow]. w2 := ToolSet browseMessageSet: (SystemNavigation default allCallsOn: #negated) name: 'Senders' translated autoSelect: 'negated'. w3 := (Workspace new contents: '3+4 "Select and hit [CMD]+[P]."') openLabel: 'Workspace'. {w1. w2. w3} do: [:ea | ea makeUnclosable. previewWorld addMorph: ea]. self updateWindowBounds.! ! !PreferenceWizardMorph methodsFor: 'support' stamp: 'ct 9/12/2020 14:36'! adjustSettingsForLowPerformance self updateLowPerformanceLabel: 'Please wait, optimizing performance...' translated. self refreshWorld. self stateGradients "flat look" ifFalse: [self toggleGradients]. self stateBlinkingCursor ifTrue: [self toggleBlinkingCursor]. self stateFastDrag ifFalse: [self toggleFastDrag]. self stateSoftShadows ifTrue: [self toggleSoftShadows]. self stateHardShadows ifTrue: [self toggleHardShadows]. self stateRoundedWindowLook ifTrue: [self toggleRoundedWindowLook]. self stateRoundedButtonLook ifTrue: [self toggleRoundedButtonLook]. self stateAttachToolsToMouse ifTrue: [self toggleAttachToolsToMouse]. self stateToolAndMenuIcons ifTrue: [self toggleToolAndMenuIcons]. self stateSmartHorizontalSplitters ifTrue: [self toggleSmartHorizontalSplitters]. self stateSmartVerticalSplitters ifTrue: [self toggleSmartVerticalSplitters]. PluggableListMorph highlightHoveredRow: false; filterableLists: false; highlightPreSelection: true; "Feedback is important!!" flashOnErrors: false. TheWorldMainDockingBar showSecondsInClock: false. Preferences disable: #balloonHelpInMessageLists. "Set simple background." Project current world setAsBackground: MorphicProject defaultFill. previewWorld fillStyle: Project current world fillStyle. "Done." self updateLowPerformanceLabel: 'Settings were adjusted for optimal performance.' translated.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! roundedWindowCornersChanged "The user changed the value of the roundedWindowCorners preference. React" Project current world fullRepaintNeeded.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! vectorVocabularySettingChanged "The current value of the useVectorVocabulary flag has changed; now react. No senders, but invoked by the Preference object associated with the #useVectorVocabulary preference." Smalltalk isMorphic ifFalse: [^ self]. Project current world makeVectorUseConformToPreference.! ! !Project methodsFor: '*Etoys-Squeakland-file in/out' stamp: 'ct 9/12/2020 15:10'! storeOnServerWithNoInteractionInnards "Save to disk as an Export Segment. Then put that file on the server I came from, as a new version. Version is literal piece of file name. Mime encoded and http encoded." | newName primaryServerDirectory serverVersionPair localDirectory localVersionPair myVersionNumber warning maxNumber myDepth | self assureIntegerVersion. "Find out what version" primaryServerDirectory := self defaultFolderForAutoSaving ifNil: [^self]. localDirectory := self squeakletDirectory. serverVersionPair := self class mostRecent: self name onServer: primaryServerDirectory. localVersionPair := self class mostRecent: self name onServer: localDirectory. maxNumber := myVersionNumber := self currentVersionNumber. ProgressNotification signal: '2:versionsDetected'. warning := ''. myVersionNumber < serverVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) on the server' translated. maxNumber := maxNumber max: serverVersionPair second. ]. myVersionNumber < localVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) in the local directory' translated. maxNumber := maxNumber max: localVersionPair second. ]. version := self bumpVersion: maxNumber. "write locally - now zipped automatically" Display isVirtualScreen ifTrue: [ myDepth := displayDepth. displayDepth := OLPCVirtualScreen preferredScreenDepth.. ]. newName := self versionedFileName. lastSavedAtSeconds := Time totalSeconds. self activateWorld: self activeWorld during: [ self flag: #suspicious. "ct: Are there any world side effects in the export logic?" self exportSegmentFileName: newName directory: localDirectory withoutInteraction: true]. (localDirectory readOnlyFileNamed: newName) setFileTypeToObject; close. Display isVirtualScreen ifTrue: [ displayDepth := myDepth. ]. ProgressNotification signal: '4:localSaveComplete'. "3 is deep in export logic" primaryServerDirectory ifNotNil: [ [ primaryServerDirectory writeProject: self inFileNamed: newName asFileName fromDirectory: localDirectory. ] on: ProjectPasswordNotification do: [ :ex | ex resume: '' ]. ]. ProgressNotification signal: '9999 save complete'.! ! !Project methodsFor: '*Etoys-Squeakland-language' stamp: 'ct 9/11/2020 20:34'! updateLocaleDependentsWithPreviousSupplies: aCollection gently: gentlyFlag "Set the project's natural language as indicated" | morphs scriptEditors | gentlyFlag ifTrue: [ LanguageEnvironment localeChangedGently. ] ifFalse: [ LanguageEnvironment localeChanged. ]. morphs := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: morphs. scriptEditors := morphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m topEditor == m]]. (morphs copyWithoutAll: scriptEditors) do: [:morph | morph localeChanged]. scriptEditors do: [:m | m localeChanged]. Flaps disableGlobalFlaps: false. SugarNavigatorBar showSugarNavigator ifTrue: [Flaps addAndEnableEToyFlapsWithPreviousEntries: aCollection. Project current world addGlobalFlaps] ifFalse: [Preferences eToyFriendly ifTrue: [Flaps addAndEnableEToyFlaps. Project current world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]]. (Project current isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ParagraphEditor initializeTextEditorMenus. MenuIcons initializeTranslations. #(PartsBin ParagraphEditor BitEditor FormEditor StandardSystemController) do: [ :key | Smalltalk at: key ifPresent: [ :class | class initialize ]]. Project current world reformulateUpdatingMenus. "self setFlaps. self setPaletteFor: aLanguageSymbol." ! ! !MorphicProject methodsFor: 'utilities' stamp: 'ct 9/12/2020 15:13'! createViewIfAppropriate "Create a project view for the receiver and place it appropriately on the screen." | aMorph requiredWidth existing proposedV proposedH despair | ProjectViewOpenNotification signal ifTrue: [Preferences projectViewsInWindows ifTrue: [(ProjectViewMorph newProjectViewInAWindowFor: self) openInWorld] ifFalse: [aMorph := ProjectViewMorph on: self. requiredWidth := aMorph width + 10. existing := self currentWorld submorphs select: [:m | m isKindOf: ProjectViewMorph] thenCollect: [:m | m fullBoundsInWorld]. proposedV := 85. proposedH := 10. despair := false. [despair not and: [((proposedH @ proposedV) extent: requiredWidth) intersectsAny: existing]] whileTrue: [proposedH := proposedH + requiredWidth. proposedH + requiredWidth > self currentWorld right ifTrue: [proposedH := 10. proposedV := proposedV + 90. proposedV > (self currentWorld bottom - 90) ifTrue: [proposedH := self currentWorld center x - 45. proposedV := self currentWorld center y - 30. despair := true]]]. aMorph position: (proposedH @ proposedV). aMorph openInWorld]]! ! !MorphicProject methodsFor: 'flaps support' stamp: 'ct 9/12/2020 14:34'! setFlaps | flapTabs flapIDs sharedFlapTabs navigationMorph | self flag: #toRemove. "check if this method still used by Etoys" flapTabs := self world flapTabs. flapIDs := flapTabs collect: [:tab | tab knownName]. flapTabs do: [:tab | (tab isMemberOf: ViewerFlapTab) ifFalse: [tab isGlobalFlap ifTrue: [Flaps removeFlapTab: tab keepInList: false. tab currentWorld reformulateUpdatingMenus] ifFalse: [| referent | referent := tab referent. referent isInWorld ifTrue: [referent delete]. tab delete]]]. sharedFlapTabs := Flaps classPool at: #SharedFlapTabs. flapIDs do: [:id | id = 'Navigator' translated ifTrue: [sharedFlapTabs add: Flaps newNavigatorFlap]. id = 'Widgets' translated ifTrue: [sharedFlapTabs add: Flaps newWidgetsFlap]. id = 'Tools' translated ifTrue: [sharedFlapTabs add: Flaps newToolsFlap]. id = 'Squeak' translated ifTrue: [sharedFlapTabs add: Flaps newSqueakFlap]. id = 'Supplies' translated ifTrue: [sharedFlapTabs add: Flaps newSuppliesFlap]. id = 'Stack Tools' translated ifTrue: [sharedFlapTabs add: Flaps newStackToolsFlap]. id = 'Painting' translated ifTrue: [sharedFlapTabs add: Flaps newPaintingFlap]. id = 'Objects' translated ifTrue: [sharedFlapTabs add: Flaps newObjectsFlap ]]. 2 timesRepeat: [flapIDs do: [:id | Flaps enableDisableGlobalFlapWithID: id]]. self world flapTabs do: [:flapTab | flapTab isCurrentlyTextual ifTrue: [flapTab changeTabText: flapTab knownName]]. Flaps positionNavigatorAndOtherFlapsAccordingToPreference. navigationMorph := self currentWorld findDeeplyA: ProjectNavigationMorph preferredNavigator. navigationMorph isNil ifTrue: [^ self]. navigationMorph allMorphs do: [:morph | morph class == SimpleButtonDelayedMenuMorph ifTrue: [(morph findA: ImageMorph) isNil ifTrue: [| label | label := morph label. label isNil ifFalse: [| name | name := morph knownName. name isNil ifTrue: [morph name: label. name := label]. morph label: name translated]]]]! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/12/2020 15:15'! clearGlobalState "Clean up global state. The global variables World, ActiveWorld, ActiveHand and ActiveEvent provide convenient access to the state of the active project in Morphic. Clear their prior values when leaving an active project. This method may be removed if the use of global state variables is eliminated." "If global World is defined, clear it now. The value is expected to be set again as a new project is entered." Smalltalk globals at: #World ifPresent: [:w | Smalltalk globals at: #World put: nil]. self activeWorld: nil; activeHand: nil; activeEvent: nil.! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/12/2020 14:45'! wakeUpTopWindow "Image has been restarted, and the startUp list has been processed. Perform any additional actions needed to restart the user interface." SystemWindow wakeUpTopWindowUponStartup. Preferences mouseOverForKeyboardFocus ifTrue: [ "Allow global command keys to work upon re-entry without having to cause a focus change first." self currentHand releaseKeyboardFocus ]! ! !MorphicProject methodsFor: 'language' stamp: 'ct 9/12/2020 14:17'! updateLocaleDependents "Set the project's natural language as indicated" (self world respondsTo: #isTileScriptingElement) ifTrue: "Etoys present" [ self world allTileScriptingElements do: [:viewerOrScriptor | viewerOrScriptor localeChanged]]. Flaps disableGlobalFlaps: false. (Preferences eToyFriendly or: [ (Smalltalk classNamed: #SugarNavigatorBar) ifNotNil: [:c | c showSugarNavigator] ifNil: [false]]) ifTrue: [ Flaps addAndEnableEToyFlaps. self world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]. (self isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ScrapBook default emptyScrapBook. MenuIcons initializeTranslations. super updateLocaleDependents. "self setFlaps. self setPaletteFor: aLanguageSymbol."! ! !MorphicProject methodsFor: 'protocols' stamp: 'ct 9/12/2020 14:18'! currentVocabulary ^ self world currentVocabulary! ! !MorphicProject methodsFor: 'scheduling & debugging' stamp: 'ct 9/12/2020 15:13'! interruptCleanUpFor: interruptedProcess "Clean up things in case of a process interrupt." super interruptCleanUpFor: interruptedProcess. self uiProcess == interruptedProcess ifTrue: [ self activeHand ifNotNil: [:hand | hand interrupted]. self activeWorld: world. "reinstall active globals" self activeHand: world primaryHand. self activeHand interrupted. "make sure this one's interrupted too" self activeEvent: nil. Preferences eToyFriendly ifTrue: [ Project current world stopRunningAll]].! ! !MorphicProject class methodsFor: 'shrinking' stamp: 'ct 9/12/2020 15:45'! unloadMorphic "MorphicProject unloadMorphic" Project current isMorphic ifTrue: [ ^ Error signal: 'You can only unload Morphic from within another kind of project.' translated]. MorphicProject removeProjectsFromSystem. #(ActiveEvent ActiveHand ActiveWorld World) do: [:ea | Smalltalk globals removeKey: ea]. Processor allInstancesDo: [:process | #(ActiveHand ActiveEvent ActiveWorld) do: [:ea | process environmentRemoveKey: ea ifAbsent: []]]. { 'ToolBuilder-Morphic' . 'MorphicTests' . 'MorphicExtras' . 'Morphic' } do: [ :package | (MCPackage named: package) unload ].! ! !ProjectNavigationMorph methodsFor: 'stepping and presenter' stamp: 'ct 9/11/2020 20:34'! undoButtonWording "Answer the wording for the Undo button." | wdng | wdng := Project current world commandHistory undoOrRedoMenuWording. (wdng endsWith: ' (z)') ifTrue: [ wdng := wdng copyFrom: 1to: wdng size - 4]. ^ wdng! ! !ProjectNavigationMorph methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:34'! undoOrRedoLastCommand "Undo or redo the last command, as approrpiate." ^ Project current world commandHistory undoOrRedoCommand! ! !EventRecordingSpaceNavigator methodsFor: 'the actions' stamp: 'ct 9/11/2020 19:47'! doNewPainting "Make a new painting" | worldlet | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. worldlet closeNavigatorFlap. worldlet makeNewDrawing: (self currentEvent copy setPosition: worldlet center).! ! !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/11/2020 20:34'! maximumUsableArea ^ self maximumUsableAreaInWorld: Project current world! ! !RecordingControls methodsFor: 'private' stamp: 'ct 9/12/2020 14:52'! makeSoundMorph "Hand the user an anonymous-sound object representing the receiver's sound." | m aName | recorder verifyExistenceOfRecordedSound ifFalse: [^ self]. recorder pause. recordingSaved := true. m := AnonymousSoundMorph new. m sound: recorder recordedSound interimName: (aName := 'Unnamed Sound'). m setNameTo: aName. self currentHand attachMorph: m.! ! !ReleaseBuilder class methodsFor: 'scripts - support' stamp: 'ct 9/11/2020 20:33'! setProjectBackground: aFormOrColorOrFillStyle | world | world := Project current world. world fillStyle: aFormOrColorOrFillStyle. MorphicProject defaultFill: world fillStyle. world removeProperty: #hasCustomBackground.! ! !SARInstaller methodsFor: 'client services' stamp: 'ct 9/11/2020 20:33'! fileInMorphsNamed: memberName addToWorld: aBoolean "This will load the Morph (or Morphs) from the given member. Answers a Morph, or a list of Morphs, or nil if no such member or error. If aBoolean is true, also adds them and their models to the World." | member morphOrList | member := self memberNamed: memberName. member ifNil: [^ self errorNoSuchMember: memberName]. self installed: member. morphOrList := member contentStream fileInObjectAndCode. morphOrList ifNil: [^ nil]. aBoolean ifTrue: [Project current world addMorphsAndModel: morphOrList]. ^ morphOrList! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/12/2020 14:52'! addYesNoToHand "Place a test/yes/no complex in the hand of the beloved user" | ms messageNodeMorph aMorph | Preferences universalTiles ifTrue: [ms := MessageSend receiver: true selector: #ifTrue:ifFalse: arguments: {['do nothing']. ['do nothing']}. messageNodeMorph := ms asTilesIn: playerScripted class globalNames: true. self primaryHand attachMorph: messageNodeMorph] ifFalse: [aMorph := CompoundTileMorph new. self currentHand attachMorph: aMorph. aMorph setNamePropertyTo: 'TestTile' translated. aMorph position: self currentHand position. aMorph formerPosition: self currentHand position. self startSteppingSelector: #trackDropZones].! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:32'! dismiss "Dismiss the scriptor, usually nondestructively. Possibly animate the dismissal." | endPoint aForm startPoint topRend | owner ifNil: [^ self]. scriptName ifNil: [^ self delete]. "ad hoc fixup for bkwrd compat" endPoint := self viewerTile ifNotNilDo: [:tile | tile topLeft] ifNil: [owner topRight]. aForm := (topRend := self topRendererOrSelf) imageForm offset: (0 at 0). handWithTile := nil. startPoint := topRend topLeft. topRend topRendererOrSelf delete. (playerScripted isExpendableScript: scriptName) ifTrue: [ ^ playerScripted removeScript: scriptName fromWorld: Project current world]. Project current world displayWorld. aForm slideFrom: startPoint to: endPoint nSteps: 4 delay: 30. "The OLPC Virtual Screen wouldn't notice the last update here." Display forceToScreen: (endPoint extent: aForm extent).! ! !ScriptEditorMorph methodsFor: 'other' stamp: 'ct 9/12/2020 14:52'! offerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer" | aMenu count | self modernize. self currentHand showTemporaryCursor: nil. Preferences eToyFriendly ifTrue: [^ self offerSimplerScriptorMenu]. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addStayUpItem. "NB: the kids version in #offerSimplerScriptorMenu does not deploy the stay-up item" aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: { {'button to fire this script' translatedNoop. #tearOfButtonToFireScript}. {'fires per tick...' translatedNoop. #chooseFrequency}. #- }]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. "aMenu addLine. self addGoldBoxItemsTo: aMenu." aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addTranslatedList: { #-. {'open viewer' translatedNoop. #openObjectsViewer. 'open the viewer of the object to which this script belongs' translatedNoop}. {'detached method pane' translatedNoop. #makeIsolatedCodePane. 'open a little window that shows the Smalltalk code underlying this script.' translatedNoop}. #-. {'destroy this script' translatedNoop. #destroyScript} }. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-menu commands' stamp: 'ct 9/11/2020 20:32'! findObject "Reveal the object bearing the code " playerScripted revealPlayerIn: Project current world.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:52'! handUserTimesRepeatTile "Hand the user a times-repeat tile, presumably to drop in the script" | aMorph | aMorph := TimesRepeatTile new. self currentHand attachMorph: aMorph. aMorph position: self currentHand position.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:53'! offerSimplerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer. This variant is used when eToyFriendly preference is true." | aMenu count | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: #( ('button to fire this script' tearOfButtonToFireScript) -) translatedNoop]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu addTranslatedList: #( - ('open viewer' openObjectsViewer 'open the viewer of the object to which this script belongs') - ('destroy this script' destroyScript)) translatedNoop. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:32'! goldBoxMenu "Answer a graphical menu to be put up in conjunction with the Gold Box" | aBox | aBox := Project current world findA: GoldBoxMenu. aBox ifNil: [aBox := GoldBoxMenu new]. aBox initializeFor: self. ^ aBox! ! !ScriptEncoder methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:31'! init: class notifying: parser super init: class notifying: parser. self referenceObject: Project current world referenceWorld.! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/12/2020 14:53'! offerMenuIn: aStatusViewer "Put up a menu." | aMenu | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu title: player knownName, ' ', selector. aMenu addStayUpItem. (player class instanceCount > 1) ifTrue: [aMenu add: 'propagate status to siblings' translated selector: #assignStatusToAllSiblingsIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'Make the status of this script in all of my sibling instances be the same as the status you see here' translated]. aMenu addLine. aMenu add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: player selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: player selector: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu add: 'open this script''s Scriptor' translated target: player selector: #grabScriptorForSelector:in: argumentList: {selector. aStatusViewer world}. aMenu balloonTextForLastItem: 'Open up the Scriptor for this script' translated. aMenu add: 'open this object''s Viewer' translated target: player selector: #beViewed. aMenu balloonTextForLastItem: 'Open up a Viewer for this object' translated. aMenu addLine. aMenu add: 'more...' translated target: self selector: #offerShiftedMenuIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'The "more..." branch offers you menu items that are less frequently used.' translated. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/11/2020 20:30'! offerShiftedMenuIn: aStatusViewer "Put up the shifted menu" ^ (MenuMorph new defaultTarget: self) title: player knownName, ' ', selector; add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld; balloonTextForLastItem: 'Wherever this object currently is, the "grab" command will rip it out, and place it in your "hand". This is a very drastic step, that can disassemble things that may be very hard to put back together!!' translated; add: 'destroy this script' translated target: player selector: #removeScriptWithSelector: argument: selector; balloonTextForLastItem: 'Caution!! This is irreversibly destructive -- it removes the script from the system.' translated; addLine; add: 'inspect morph' translated target: player costume selector: #inspect; add: 'inspect player' translated target: player selector: #inspect; popUpInWorld: self currentWorld! ! !ScriptNameType methodsFor: 'queries' stamp: 'ct 9/11/2020 20:29'! choices "Answer an alphabetized list of known script selectors in the current project" ^ Project current world presenter allKnownUnaryScriptSelectors ! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:29'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock "Answer a MethodNode for the argument, sourceStream, that is the root of a parse tree. Parsing is done with respect to the argument, class, to find instance, class, and pool variables; and with respect to the argument, ctxt, to find temporary variables. Errors in parsing are reported to the argument, req, if not nil; otherwise aBlock is evaluated. The argument noPattern is a Boolean that is true if the the sourceStream does not contain a method header (i.e., for DoIts)." "Copied from superclass, use ScriptEncoder and give it a referenceWorld. This assumes worldLoading has been set to the right world this player belongs to. --bf 5/4/2010" | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: Project current world referenceWorld )] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:28'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock for: anInstance | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: (anInstance costume ifNotNil: [anInstance costume referenceWorld] ifNil: [Project current world]))] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !SearchTopic methodsFor: 'private' stamp: 'ct 9/11/2020 20:28'! triggerUpdateContents self mutex critical: [ updatePending == true ifFalse: [ updatePending := true. Project current addDeferredUIMessage: [Project current world addAlarm: #updateContents withArguments: #() for: self at: Time millisecondClockValue + 250]]].! ! !SelectionMorph methodsFor: 'halo commands' stamp: 'ct 9/11/2020 20:28'! duplicate "Make a duplicate of the receiver and havbe the hand grab it" selectedItems := self duplicateMorphCollection: selectedItems. selectedItems reverseDo: [:m | (owner ifNil: [self currentWorld]) addMorph: m]. dupLoc := self position. self currentHand grabMorph: self. self currentWorld presenter flushPlayerListCache.! ! !SimpleHierarchicalListMorph methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:28'! specialKeyPressed: asciiValue (self arrowKey: asciiValue) ifTrue: [^ true]. asciiValue = 27 "escape" ifTrue: [ self currentEvent shiftPressed ifTrue: [self currentWorld putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]. ^ true]. ^ false! ! !SimpleSelectionMorph methodsFor: 'extending' stamp: 'ct 9/11/2020 20:27'! extendByHand: aHand "Assumes selection has just been created and added to some pasteUp or world" | startPoint handle m inner | startPoint := Sensor cursorPoint. handle := NewHandleMorph new followHand: aHand forEachPointDo: [:newPoint | | localPt | Cursor crossHair show. localPt := (self transformFrom: self world) globalPointToLocal: newPoint. self bounds: (startPoint rect: localPt)] lastPointDo: [:newPoint | inner := self bounds insetBy: 2 at 2. inner area >= 16 ifTrue: [m := SketchMorph new form: (Form fromDisplay: inner). aHand attachMorph: m. self currentWorld fullRepaintNeeded] "selection tracking can leave unwanted artifacts" ifFalse: [Beeper beep]. "throw minnows back" self delete]. handle visible: false. aHand attachMorph: handle. handle startStepping! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! cancelOutOfPainting "The user requested to back out of a painting session without saving" self deleteSelfAndSubordinates. emptyPicBlock ifNotNil: [emptyPicBlock value]. "note no args to block!!" hostView ifNotNil: [hostView changed]. Project current world resumeScriptsPausedByPainting. ^ nil! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! deliverPainting: result evt: evt "Done painting. May come from resume, or from original call. Execute user's post painting instructions in the block. Always use this standard one. 4/21/97 tk" | newBox newForm ans | palette ifNotNil: "nil happens" [palette setAction: #paint: evt: evt]. "Get out of odd modes" "rot := palette getRotations." "rotate with heading, or turn to and fro" "palette setRotation: #normal." result == #cancel ifTrue: [ ans := UIManager default chooseFrom: { 'throw it away' translated. 'keep painting it' translated. } title: 'Do you really want to throw away what you just painted?' translated. ^ ans = 1 ifTrue: [self cancelOutOfPainting] ifFalse: [nil]]. "cancelled out of cancelling." "hostView rotationStyle: rot." "rotate with heading, or turn to and fro" newBox := paintingForm rectangleEnclosingPixelsNotOfColor: Color transparent. registrationPoint ifNotNil: [registrationPoint := registrationPoint - newBox origin]. "relative to newForm origin" newForm := Form extent: newBox extent depth: paintingForm depth. newForm copyBits: newBox from: paintingForm at: 0 at 0 clippingBox: newForm boundingBox rule: Form over fillColor: nil. newForm isAllWhite ifTrue: [ (self valueOfProperty: #background) == true ifFalse: [^ self cancelOutOfPainting]]. newForm fixAlpha. "so alpha channel stays intact for 32bpp" self delete. "so won't find me again" dimForm ifNotNil: [dimForm delete]. newPicBlock value: newForm value: (newBox copy translateBy: bounds origin). Project current world resumeScriptsPausedByPainting.! ! !SketchMorph methodsFor: 'menus' stamp: 'ct 9/11/2020 20:27'! collapse "Replace the receiver with a collapsed rendition of itself." | w collapsedVersion a ht | (w := self world) ifNil: [^ self]. collapsedVersion := (self imageForm scaledToSize: 50 at 50) asMorph. collapsedVersion setProperty: #uncollapsedMorph toValue: self. collapsedVersion on: #mouseUp send: #uncollapseSketch to: collapsedVersion. collapsedVersion setBalloonText: ('A collapsed version of {1}. Click to open it back up.' translated format: {self externalName}). self delete. w addMorphFront: ( a := AlignmentMorph newRow hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4; borderColor: Color white; addMorph: collapsedVersion; yourself). a setNameTo: self externalName. ht := (Smalltalk at: #SugarNavTab ifPresent: [:c | Project current world findA: c]) ifNotNil: [:tab | tab height] ifNil: [80]. a position: 0 at ht. collapsedVersion setProperty: #collapsedMorphCarrier toValue: a. (self valueOfProperty: #collapsedPosition) ifNotNil: [:priorPosition | a position: priorPosition].! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-event handling' stamp: 'ct 9/12/2020 14:39'! deleteBoxHit "The delete box was hit..." self currentHand showTemporaryCursor: nil. self delete.! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:39'! openPropertySheet "Delete the receiver and open a property sheet on my target instead." self currentHand showTemporaryCursor: nil. target openAppropriatePropertySheet. self delete.! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer cardNum: cardNum "Call once to search a card of the stack. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [self currentPage allStringsAfter: oldContainer text]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findWordStart: searchString startingAt: start) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" "wasIn := (pages at: pageNum) isInMemory." self goToCardNumber: cardNum "wasIn ifFalse: ['search again, on the real current text. Know page is in.'. ^ self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) recompute it startAt: startIndex container: oldContainer pageNum: pageNum]"]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findViaTemplate | list pl cardInst | "Current card is the template. Only search cards in this background. Look at cards directly (not allText). Key must be found in the same field as in the template. HyperCard style (multiple starts of words). Put results in a list, outside the stack." list := self templateMatches. list isEmpty ifTrue: [^ self inform: 'No matches were found. Be sure the current card is mostly blank and only has text you want to match.' translated]. "put up a PluggableListMorph" cardInst := self currentCard. cardInst matchIndex: 0. "establish entries" cardInst results at: 1 put: list. self currentPage setProperty: #myStack toValue: self. "way to get back" pl := PluggableListMorph new on: cardInst list: #matchNames selected: #matchIndex changeSelected: #matchIndex: menu: nil "#matchMenu:shifted:" keystroke: nil. self currentHand attachMorph: (self formatList: pl). ! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! assureFlapOfLabel: aTitle withContents: aString "Answer an info flap with the given title and contents. If one exists in the project, use that, else create one & insert it in the world. Answer the flap tab." | allFlapTabs aTab | allFlapTabs := Project current world localFlapTabs, Project current world extantGlobalFlapTabs. aTab := allFlapTabs detect: [:ft | ft flapID = aTitle] ifNone: [nil]. aTab ifNotNil: [^ aTab]. "already present" aTab := self openInfoFlapWithLabel: aTitle helpContents: aString edge: #left. aTab bottom: Project current world bottom. self cleanUpFlapTabsOnLeft. aTab hideFlap. aTab referent show. aTab show. ^ aTab " ScriptingSystem assureFlapOfLabel: 'Egg Sample' withContents: EventRollMorph basicNew helpString "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! cleanUpFlapTabsOnLeft "Make sure the flap tabs on the left of the screen line up nicely, making best use of realestate." | tabsOnLeft current | tabsOnLeft := ((Project current world localFlapTabs, Project current world extantGlobalFlapTabs) select: [:f | f edgeToAdhereTo = #left]) sort: [:a :b | a top <= b top]. current := SugarNavigatorBar showSugarNavigator ifTrue: [75] ifFalse: [0]. tabsOnLeft do: [:aTab | aTab top: (current min: Project current world height - aTab height). current := aTab bottom + 2]. " ScriptingSystem cleanUpFlapTabsOnLeft "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:25'! openInfoFlapWithLabel: aTitle helpContents: aString edge: anEdge "Open an info flap with the given label, contents, and edge" | aPlug outer leftStrip rightStrip titleRow aDismissButton aFlapTab | Preferences enable: #scrollBarsOnRight. Preferences enable: #inboardScrollbars. aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab referentMargin: 0 @ Project current world sugarAllowance. outer := HelpFlap newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #morphicLayerNumber toValue: 26. leftStrip := Morph new beTransparent. leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip beTransparent. rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. outer addMorphBack: rightStrip. outer clipSubmorphs: true. titleRow := AlignmentMorph newRow. titleRow borderColor: Color veryVeryLightGray; borderWidth: 1. titleRow hResizing: #spaceFill; vResizing: #shrinkWrap. titleRow beTransparent. aDismissButton := aFlapTab tanOButton. aDismissButton actionSelector: #dismissViaHalo. titleRow addMorphFront: aDismissButton. titleRow addTransparentSpacerOfSize: 8 @ 0. titleRow addMorphBack: (StringMorph contents: aTitle font: Preferences standardEToysTitleFont). rightStrip addMorph: titleRow. aPlug := PluggableTextMorph new. aPlug width: 540. aPlug setText: aString. aPlug textMorph beAllFont: Preferences standardEToysFont. aPlug retractable: false; scrollBarOnLeft: false. aPlug hScrollBarPolicy: #never. aPlug borderColor: ScriptingSystem borderColor. aPlug setNameTo: aTitle. aPlug hResizing: #spaceFill. aPlug vResizing: #spaceFill. rightStrip addMorphBack: aPlug. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: aTitle edge: anEdge color: (Color r: 0.677 g: 0.935 b: 0.484). aFlapTab submorphs first beAllFont: Preferences standardEToysFont. Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. anEdge == #left ifTrue: [aFlapTab position: (outer left @ outer top). outer extent: (540 @ Project current world height)]. anEdge == #right ifTrue: [aFlapTab position: ((Project current world right - aFlapTab width) @ Project current world top). outer extent: (540 @ Project current world height)]. outer beFlap: true. outer color: Color green veryMuchLighter. aPlug textMorph lock. aFlapTab referent hide. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. Project current world doOneCycle. aPlug width: 540. aPlug setText: aString. "hmm, again" aPlug color: outer color. aPlug borderWidth: 0. aPlug textMorph contents: aString wrappedTo: 520. aFlapTab applyThickness: 560. aFlapTab fitOnScreen. aFlapTab referent show. ^ aFlapTab! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:24'! systemQueryPhraseWithActionString: aString labelled: anotherString "Answer a system-query-phrase with the give action-string and label." ^ Project current world presenter systemQueryPhraseWithActionString: aString labelled: anotherString! ! !StandardViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 20:24'! currentVocabulary "Answer the vocabulary currently associated with the receiver" | aSym aVocab | aSym := self valueOfProperty: #currentVocabularySymbol ifAbsent: [nil]. aSym ifNil: [aVocab := self valueOfProperty: #currentVocabulary ifAbsent: [nil]. aVocab ifNotNil: [aSym := aVocab vocabularyName. self removeProperty: #currentVocabulary. self setProperty: #currentVocabularySymbol toValue: aSym]]. ^ aSym ifNotNil: [Vocabulary vocabularyNamed: aSym] ifNil: [(self world ifNil: [Project current world]) currentVocabularyFor: scriptedPlayer]! ! !SugarLauncher methodsFor: 'commands' stamp: 'ct 9/12/2020 14:07'! viewSource Project current world addDeferredUIMessage: [ Project current world showSourceKeyHit].! ! !SugarNavTab methodsFor: 'positioning' stamp: 'ct 9/11/2020 20:24'! occupyTopRightCorner "Make the receiver be the correct size, and occupy the top-right corner of the screen." | worldBounds toUse | worldBounds := Project current world bounds. " toUse := Preferences useArtificialSweetenerBar ifFalse: [75] ifTrue: [(ActiveWorld extent >= (1200 @ 900)) ifTrue: [75] ifFalse: [40]]." toUse := 40. "Trying for the moment to use the smaller icon always when in this mode." referent height: toUse; resizeButtonsAndTabTo: toUse. self extent: toUse @ toUse. self topRight: worldBounds topRight! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelp " SugarNavigatorBar putUpInitialBalloonHelp " | suppliesButton b1 b2 p b | suppliesButton := paintButton owner submorphs detect: [:e | e isButton and: [e actionSelector = #toggleSupplies]]. b1 := BalloonMorph string: self paintButtonInitialExplanation for: paintButton corner: #topRight force: false. b2 := BalloonMorph string: self suppliesButtonInitialExplanation for: suppliesButton corner: #topLeft force: true. p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. p addMorph: b1. p addMorph: b2. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelpFor: quads "Given a list of quads of the form (see senders for examples), put up initial balloon help for them." " SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((doNewPainting 'make a new painting' topRight false) (toggleSupplies 'open the supplies bin' topLeft true)) SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((showNavBar 'show the tool bar' bottomLeft false) (hideNavBar 'hide the tool bar' bottomLeft false)) " | b1 p b | p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. quads do: [:aQuad | (submorphs first submorphs detect: [:e | e isButton and: [e actionSelector = aQuad first]] ifNone: [nil]) ifNotNil: [:aButton | b1 := BalloonMorph string: aQuad second for: aButton corner: aQuad third force: aQuad fourth. p addMorph: b1]]. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'help flap' stamp: 'ct 9/12/2020 14:37'! buildAndOpenHelpFlap "Called only when flaps are being created afresh." | aFlapTab outer leftStrip rightStrip aGuide | aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab setProperty: #rigidThickness toValue: true. outer := AlignmentMorph newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #wantsHaloFromClick toValue: false. leftStrip := Morph new beTransparent. "This provides space for tabs to be seen." leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip color: (Color green veryMuchLighter alpha: 0.2). rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. rightStrip setProperty: #wantsHaloFromClick toValue: false. outer addMorphBack: rightStrip. outer clipSubmorphs: true. aGuide := QuickGuideMorph new. aGuide initializeIndexPage. " aGuide order: QuickGuideMorph defaultOrder. " QuickGuideMorph loadIndexAndPeekOnDisk. aGuide loadPages. rightStrip addMorphBack: aGuide. aGuide beSticky. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: 'Help' translated edge: #left color: (Color r: 0.677 g: 0.935 b: 0.484). Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. aFlapTab position: outer left @ outer top. outer extent: 462 @ Project current world height. outer beFlap: true. outer beTransparent. aFlapTab referent hide. aFlapTab referentMargin: 0 at self height. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. aFlapTab applyThickness: 462. aFlapTab fitOnScreen. aFlapTab referent show. aFlapTab show. aFlapTab makeFlapCompact: true. aFlapTab setToPopOutOnDragOver: false. Flaps addGlobalFlap: aFlapTab. Project current world addGlobalFlaps. ScriptingSystem cleanUpFlapTabsOnLeft! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/12/2020 14:53'! gotoAnother EToyProjectHistoryMorph new position: self currentHand position; openInWorld ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! makeProjectNameLabel | t | projectNameField := SugarRoundedField new. t := UpdatingStringMorph new. t setProperty: #okToTextEdit toValue: true. t putSelector: #projectNameChanged:. t getSelector: #projectName. projectNameField backgroundColor: self color. t target: self. t useStringFormat. t beSticky. t label: Project current name font: (StrikeFont familyName: 'BitstreamVeraSans' size: 24). t color: Color black. t width: projectNameField width - 10. projectNameField label: t. projectNameField setBalloonText: self projectNameFieldBalloonHelp. projectNameField on: #mouseDown send: #mouseDown: to: t. projectNameField on: #mouseUp send: #mouseUp: to: t. self resizeProjectNameField. ^projectNameField.! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectName ^ Project current name ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectNameChanged: aString Project current renameTo: aString. ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! shareMenu | menu item ext | menu := MenuMorph new. ext := 200 at 50. #((stopSharing makePrivateLabelIn:) (startSharing makeMyNeighborhoodLabelIn:) "(shareThisWorld makeBadgeLabelIn:)") do: [:pair | item := MenuItemMorph new contents: ''; target: self; selector: pair first; arguments: #(). item color: Color black. item addMorph: (self perform: pair second with: ext). item setProperty: #minHeight toValue: ext y. item fitContents. item extent: ext. item setProperty: #selectionFillStyle toValue: (Color gray alpha: 0.5). menu addMorphBack: item. ]. menu color: Color black. menu borderColor: Color white. ^ menu invokeModalAt: shareButton position + (10 at 20) in: Project current world allowKeyboard: false.! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/12/2020 14:37'! startNebraska | nebraska | Project current world remoteServer: nil. Project current world submorphs do: [:e | (e isMemberOf: NebraskaServerMorph) ifTrue: [e delete]]. nebraska := NebraskaServerMorph serveWorld. SugarLauncher current offerStreamTube: 'sqk-nebraska' inBackgroundOnPort: [nebraska listeningPort]. ! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! startP2P listener ifNotNil: [listener stopListening]. listener ifNil: [listener := SugarListenerMorph new]. listener position: -200@ -200. Project current world addMorphBack: listener. listener startListening. SugarLauncher current offerStreamTube: 'sqk-etoy-p2p' inBackgroundOnPort: [listener listeningPort].! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! stopSharing SugarLauncher current leaveSharedActivity. listener ifNotNil: [listener stopListening. listener := nil]. Project current world remoteServer: nil. Project current world submorphs do: [:ea | (ea isMemberOf: NebraskaServerMorph) ifTrue: [ea delete]]. self sharingChanged.! ! !SugarNavigatorBar methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:22'! undoButtonAppearance | wording | undoButton ifNotNil: [ Project current world commandHistory undoEnabled ifTrue: [undoButton enabled] ifFalse: [undoButton disabled]. wording := self undoButtonWording. undoButton setBalloonText: wording. ]. ! ! !InteriorSugarNavBar methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:12'! doNewPainting "Make a new painting" | worldlet aRect | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. aRect := (worldlet topLeft + (0 @ self height)) corner: worldlet bottomRight. worldlet makeNewDrawing: (self currentEvent copy setPosition: aRect center).! ! !SugarNavigatorBar class methodsFor: 'utilitity' stamp: 'ct 9/11/2020 20:22'! findAnythingMorph ^ FileList2 morphicViewProjectLoader2InWorld: Project current world title: 'Find...' translated reallyLoad: true dirFilterType: #initialDirectoryList isGeneral: true.! ! !SugarRoundedField methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:22'! resizeLabel | small | (label notNil and: [label hasFocus not]) ifTrue: [ label width: self width - 10. small :=self height < 45. label label: Project current world project name font: (StrikeFont familyName: 'BitstreamVeraSans' size: (small ifTrue: [15] ifFalse: [24])). label center: self center. label left: self left + 10. self addMorph: label. ]. ! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerTilesMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'me, by name' target: self selector: #attachTileForCode:nodeType: argumentList: {''. aReceiver}. menu add: 'self' target: self selector: #attachTileForCode:nodeType: argumentList: {'self'. VariableNode}. menu add: '_ (assignment)' target: self selector: #attachTileForCode:nodeType: argumentList: {''. nil}. menu add: '"a Comment"' target: self selector: #attachTileForCode:nodeType: argumentList: {'"a comment"\' withCRs. CommentNode}. menu submorphs last color: Color blue. menu add: 'a Number' target: self selector: #attachTileForCode:nodeType: argumentList: {'5'. LiteralNode}. menu add: 'a Character' target: self selector: #attachTileForCode:nodeType: argumentList: {'$z'. LiteralNode}. menu add: '''abc''' target: self selector: #attachTileForCode:nodeType: argumentList: {'''abc'''. LiteralNode}. menu add: 'a Symbol constant' target: self selector: #attachTileForCode:nodeType: argumentList: {'#next'. LiteralNode}. menu add: 'true' target: self selector: #attachTileForCode:nodeType: argumentList: {'true'. VariableNode}. menu add: 'a Test' target: self selector: #attachTileForCode:nodeType: argumentList: {'true ifTrue: [self] ifFalse: [self]'. MessageNode}. menu add: 'a Loop' target: self selector: #attachTileForCode:nodeType: argumentList: {'1 to: 10 do: [:index | self]'. MessageNode}. menu add: 'a Block' target: self selector: #attachTileForCode:nodeType: argumentList: {'[self]'. BlockNode}. menu add: 'a Class or Global' target: self selector: #attachTileForCode:nodeType: argumentList: {'Character'. LiteralVariableNode}. menu add: 'a Reply' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. ReturnNode}. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerVarsMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu instVarList cls | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'new temp variable' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. TempVariableNode}. instVarList := OrderedCollection new. cls := aReceiver class. [instVarList addAllFirst: cls instVarNames. cls == aLexiconModel limitClass] whileFalse: [cls := cls superclass]. instVarList do: [:nn | menu add: nn target: self selector: #instVarTile: argument: nn]. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! attachToHand "Adjust my look and attach me to the hand" self roundedCorners. self currentHand attachMorph: self. Preferences tileTranslucentDrag ifTrue: [self lookTranslucent. self align: self center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ self align: self topLeft with: self currentHand position + self cursorBaseOffset].! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! instVarTile: aName "Make and put into hand a tile for an instance variable" | sm | sm := ((VariableNode new name: aName index: 1 type: 1 "LdInstType") asMorphicSyntaxIn: SyntaxMorph new). sm roundedCorners. self currentHand attachMorph: sm. Preferences tileTranslucentDrag ifTrue: [sm lookTranslucent. sm align: sm center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ sm align: sm topLeft with: self currentHand position + self cursorBaseOffset]! ! !SyntaxMorph methodsFor: 'scripting' stamp: 'ct 9/12/2020 14:55'! tearOffTile "For a SyntaxMorph, this means give a copy of me" | dup | dup := self duplicate. self currentHand attachMorph: dup. ^ Preferences tileTranslucentDrag ifTrue: [dup lookTranslucent] ifFalse: [dup align: dup topLeft with: self currentHand position + self cursorBaseOffset]! ! !SystemWindow methodsFor: 'events' stamp: 'ct 9/11/2020 20:22'! doFastFrameDrag: grabPoint "Do fast frame dragging from the given point" | offset newBounds outerWorldBounds clearArea | outerWorldBounds := self boundsIn: nil. offset := outerWorldBounds origin - grabPoint. clearArea := Project current world clearArea. newBounds := outerWorldBounds newRectFrom: [:f | | p selector | p := Sensor cursorPoint. (self class dragToEdges and: [(selector := self dragToEdgesSelectorFor: p in: clearArea) notNil]) ifTrue: [clearArea perform: selector] ifFalse: [p + offset extent: outerWorldBounds extent]]. self bounds: newBounds; comeToFront! ! !CollapsedMorph methodsFor: 'collapse/expand' stamp: 'ct 9/12/2020 14:39'! uncollapseToHand "Hand the uncollapsedMorph to the user, placing it in her hand, after remembering appropriate state for possible future use" | nakedMorph | nakedMorph := uncollapsedMorph. uncollapsedMorph := nil. nakedMorph setProperty: #collapsedPosition toValue: self position. mustNotClose := false. "so the delete will succeed" self delete. self currentHand attachMorph: nakedMorph.! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 18:01'! rotateWindows "Rotate the z-ordering of the windows." self currentEvent shiftPressed ifTrue: [self sendTopWindowBackOne] ifFalse: [self sendTopWindowToBack].! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 20:21'! sendTopWindowBackOne "Rotate the window-list one downward, i.e., make the bottommost one be the active one, pushing the receiver to next-to-topmost." | dows | dows := Project current world submorphs select: [:m | m isSystemWindow]. dows ifNotEmpty: [dows last expand; comeToFront]! ! !TextEditor methodsFor: 'menu commands' stamp: 'ct 9/11/2020 18:02'! offerMenuFromEsc: aKeyboardEvent "The escape key was hit while the receiver has the keyboard focus; take action." self currentEvent shiftPressed ifFalse: [ self raiseContextMenu: aKeyboardEvent]. ^ true! ! !ThreePhaseButtonMorph methodsFor: 'button' stamp: 'ct 9/11/2020 18:02'! doButtonAction "Perform the action of this button. Subclasses may override this method. The default behavior is to send the button's actionSelector to its target object with its arguments." | args | (target notNil and: [actionSelector notNil]) ifTrue: [ args := actionSelector numArgs > arguments size ifTrue: [arguments copyWith: self currentEvent] ifFalse: [arguments]. Cursor normal showWhile: [ target perform: actionSelector withArguments: args]. target isMorph ifTrue: [target changed]].! ! !TileMorph methodsFor: 'arrows' stamp: 'ct 9/11/2020 18:02'! showSuffixChoices "The suffix arrow has been hit, so respond appropriately" | plusPhrase phrase pad outer num | self currentEvent shiftPressed ifTrue: [^ self wrapPhraseInFunction]. (phrase := self ownerThatIsA: PhraseTileMorph orA: FunctionTile) ifNil: [nil]. (type == #literal) & (literal isNumber) ifTrue: ["Tile is a constant number" (phrase isNil or: [phrase finalTilePadSubmorph == owner]) "pad" ifTrue: ["we are adding the first time (at end of our phrase)" plusPhrase := self phraseForOp: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). owner acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. num := plusPhrase firstSubmorph firstSubmorph. num deleteSuffixArrow]]. (#(function expression parameter) includes: type) ifTrue: [pad := self ownerThatIsA: TilePadMorph. plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: self. pad topEditor scriptEdited "recompile"]. type = #operator ifTrue: ["Tile is accessor of an expression" phrase resultType == #Number ifTrue: [outer := phrase ownerThatIsA: PhraseTileMorph orA: TimesRepeatTile. pad := self ownerThatIsA: TilePadMorph. outer ifNotNil: [(outer lastSubmorph == pad or: [true]) ifTrue: [ "first time" plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: phrase. "car's heading" pad topEditor scriptEdited "recompile & deal with carets"]]]]. (self topEditor ifNil: [phrase ifNil: [^ self]]) enforceTileColorPolicy! ! !TileMorph methodsFor: 'code generation' stamp: 'ct 9/11/2020 20:21'! acceptNewLiteral "Tell the scriptEditor who I belong to that I have a new literal value." | topScript | topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript installWithNewLiteral]. (self ownerThatIsA: ViewerLine) ifNotNil: [:aLine | (self ownerThatIsA: PhraseTileMorph) ifNotNil: [aLine removeHighlightFeedback. self layoutChanged. Project current world doOneSubCycle. aLine addCommandFeedback: nil]]! ! !TileMorph methodsFor: 'misc' stamp: 'ct 9/12/2020 14:56'! handReferentMorph "Hand the user the actual morph referred to" | aMorph surrogate | ((aMorph := actualObject costume) isMorph and: [aMorph isWorldMorph not]) ifTrue: [ surrogate := CollapsedMorph collapsedMorphOrNilFor: aMorph. surrogate ifNotNil: [surrogate uncollapseToHand] ifNil: [self currentHand attachMorph: aMorph]].! ! !SymbolListTile methodsFor: 'user interface' stamp: 'ct 9/11/2020 20:22'! choices "Answer the list of current choices for the receiver's symbol" dataType == #ScriptName ifTrue: "Backward compatibility with old tiles" [^ Project current world presenter allKnownUnaryScriptSelectors]. ^ choices! ! !ScriptNameTile methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:29'! choices "Answer the current list of choices" ^ Project current world presenter allKnownUnaryScriptSelectors! ! !TileMorph class methodsFor: '*Etoys-Squeakland-utilities' stamp: 'ct 9/11/2020 20:21'! implicitSelfInTilesChanged "The implicitSelfInTiles preference changed. Caution: although this may appear to have no senders in the image, it is in fact invoked when the implicitSelfInTiles preference is toggled... so please do not delete it." Smalltalk isMorphic ifFalse: [^ self]. Project current world allScriptEditorsInProject do: [:scriptEditor | scriptEditor install]. Project current world allViewersInProject do: [:viewer | viewer enforceImplicitSelf]. " (Preferences buttonForPreference: #implicitSelfInTiles) openInHand. "! ! !TileMorphTest methodsFor: 'testing' stamp: 'ct 9/12/2020 14:56'! testAssignmentTile "self debug: #testAssignmentTile" | player viewer tile phrase | player := Morph new assuredPlayer. viewer := CategoryViewer new invisiblySetPlayer: player. viewer makeSetter: #(#getX #Number) event: nil from: player costume. phrase := self currentHand firstSubmorph. self currentHand removeAllMorphs. tile := phrase submorphs second. self assert: tile codeString = 'setX: '. tile arrowAction: 1. self assert: tile codeString = 'setX: self getX + '.! ! !TypeListTile methodsFor: 'mouse handling' stamp: 'ct 9/12/2020 14:56'! showOptions | topScript | suffixArrow ifNotNil: [(suffixArrow bounds containsPoint: self currentHand cursorPoint) ifTrue: [^ super showOptions]]. topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript handUserParameterTile]! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice at: aPointOrNil "UserDialogBoxMorph confirm: 'Make your choice carefully' withCRs title: 'Do you like chocolate?' trueChoice: 'Oh yessir!!' falseChoice: 'Not so much...'" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: 1; registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponse! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice default: default triggerAfter: seconds at: aPointOrNil "UserDialogBoxMorph confirm: 'I like hot java' title: 'What do you say?' trueChoice: 'You bet!!' falseChoice: 'Nope' default: false triggerAfter: 12 at: 121 at 212" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: (default ifTrue: [1] ifFalse: [2]); registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponseAfter: seconds! ! !UserText methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:56'! keyStroke: evt "Handle a keystroke event." | newSel | super keyStroke: evt. evt hand keyboardFocus == self ifFalse: [self releaseEditor. ^ self]. newSel := self editor selectionInterval. "restore editor state" self refreshParagraph. self editor selectFrom: newSel first to: newSel last. wrapFlag ifFalse: [self fullBounds right > owner right ifTrue: [self wrapFlag: true. self right: owner right. self refreshParagraph. self editor selectFrom: text string size + 1 to: text string size]].! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs fourth topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 0)). aMorph useRoundedCorners; beTransparent; borderWidth: 2; borderColor: (Color r: 1.0 g: 0.548 b: 0.452); lock. aMorph setProperty: #highlight toValue: true. ^ Project current world addMorphFront: aMorph! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addGetterFeedback "Add feedback during mouseover of a getter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: (self firstAlignmentMorph ifNil: [self submorphs last bottomRight] ifNotNil: [:m | m bottomLeft])). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem getterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil. " Color fromUser (Color r: 1.0 g: 0.355 b: 0.839) "! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addSetterFeedback "Add screen feedback showing what would be torn off to make a setter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: self bounds bottomRight). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem setterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !ViewerLine methodsFor: '*Etoys-Squeakland-slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs third topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !Vocabulary class methodsFor: '*Etoys-Squeakland-type vocabularies' stamp: 'ct 9/11/2020 20:19'! typeChoicesForUserVariables "Answer a list of all user-choosable value types for variables." | aList | aList := #(Boolean Color CustomEvents Graphic Number Patch Player Point ScriptName Sound String) copy. self currentWorld isKedamaPresent ifFalse: [ ^ aList copyWithout: #Patch]. ^ aList " Vocabulary typeChoicesForUserVariables "! ! !WorldState methodsFor: 'hands' stamp: 'ct 9/12/2020 15:21'! removeHand: aHandMorph "Remove the given hand from the list of hands for this world." (hands includes: aHandMorph) ifFalse: [^self]. hands := hands copyWithout: aHandMorph. self activeHand == aHandMorph ifTrue: [self activeHand: nil].! ! !WorldState methodsFor: 'stepping' stamp: 'ct 9/12/2020 15:08'! runLocalStepMethodsIn: aWorld "Run morph 'step' methods (LOCAL TO THIS WORLD) whose time has come. Purge any morphs that are no longer in this world. ar 3/13/1999: Remove buggy morphs from the step list so that they don't raise repeated errors." | now morphToStep stepTime | now := Time millisecondClockValue. self activateWorld: aWorld during: [ self triggerAlarmsBefore: now. stepList ifEmpty: [^ self]. (now < lastStepTime or: [now - lastStepTime > 5000]) ifTrue: [ self adjustWakeupTimes: now]. "clock slipped" [stepList notEmpty and: [stepList first scheduledTime < now]] whileTrue: [ lastStepMessage := stepList removeFirst. morphToStep := lastStepMessage receiver. (morphToStep shouldGetStepsFrom: aWorld) ifTrue: [ lastStepMessage value: now. lastStepMessage ifNotNil: [ stepTime := lastStepMessage stepTime ifNil: [morphToStep stepTime]. lastStepMessage scheduledTime: now + (stepTime max: 1). stepList add: lastStepMessage]]. lastStepMessage := nil]. lastStepTime := now].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/12/2020 15:20'! doOneCycleNowFor: aWorld "Immediately do one cycle of the interaction loop. This should not be called directly, but only via doOneCycleFor:" | capturingGesture | DisplayScreen checkForNewScreenSize. capturingGesture := false. "self flag: #bob. " "need to consider remote hands in lower worlds" "process user input events" LastCycleTime := Time millisecondClockValue. self handsDo: [:hand | hand becomeActiveDuring: [ hand processEvents. capturingGesture := capturingGesture or: [hand isCapturingGesturePoints]]]. "the default is the primary hand" self activeHand: self hands first. "The gesture recognizer needs enough points to be accurate. Therefore morph stepping is disabled while capturing points for the recognizer" capturingGesture ifFalse: [ aWorld runStepMethods. "there are currently some variations here" self displayWorldSafely: aWorld].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/12/2020 15:21'! doOneSubCycleFor: aWorld "Like #doOneCycle, but preserves activeHand." ^ self activateHand: self activeHand during: [ self doOneCycleFor: aWorld]! ! !WorldState methodsFor: '*MorphicExtras-update cycle' stamp: 'ct 9/12/2020 15:18'! doOneCycleInBackground "Do one cycle of the interactive loop. This method is called repeatedly when this world is not the active window but is running in the background." self halt. "not ready for prime time" "process user input events, but only for remote hands" self handsDo: [:hand | (hand isKindOf: RemoteHandMorph) ifTrue: [ hand becomeActiveDuring: [ hand processEvents]]]. self runStepMethods. self displayWorldSafely.! ! !ZASMCameraMarkMorph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:02'! setTransition "Set the transition" ^ self setTransition: self currentEvent! ! WorldState removeSelector: #activeHand! WorldState removeSelector: #activeHand:! Flaps class removeSelector: #addLocalFlapTitled:onEdge:! !Browser reorganize! ('*46Deprecated') ('*60Deprecated-multi-window support' classHierarchy) ('*Etoys-Squeakland-class functions' buildClassBrowser) ('*Etoys-Squeakland-drag and drop' overwriteDialogHierarchyChange:higher:sourceClassName:destinationClassName:methodSelector:) ('*Etoys-Squeakland-initialize-release' browserWindowActivated) ('*Etoys-Squeakland-message functions' buildMessageBrowser) ('*SUnitTools-class list functions' testRunTests) ('*SUnitTools-menus' testsClassListMenu: testsSystemCategoryMenu:) ('*SUnitTools-system category functions' hasSystemCategoryWithTestsSelected testRunTestsCategory) ('*services-base' browseReference: classCategoryMenuServices: classListMenuServices: messageCategoryMenuServices: methodReference optionalButtonRow selectReference:) ('accessing' contents contents:notifying: contentsSelection couldBrowseAnyClass doItReceiver editSelection editSelection: environment newClassContents noteSelectionIndex:for: request:initialAnswer: selectEnvironment: spawn: suggestCategoryToSpawnedBrowser:) ('annotation' annotation annotation:) ('class comment pane' annotationForClassCommentFor: annotationForClassDefinitionFor: noCommentNagString stripNaggingAttributeFromComment:) ('class functions' addAllMethodsToCurrentChangeSet classCommentText classDefinitionText classListMenu: classListMenu:shifted: classListMenuMore: copyClass createInstVarAccessors defineClass:notifying: editClass editComment explainSpecial: fileOutClass findMethod hierarchy makeNewSubclass plusButtonHit printOutClass removeClass renameClass shiftedClassListMenu: shiftedClassListMenuMore:) ('class list' classIconAt: classList classListIndex classListIndex: classListIndexOf: classListSingleton createHierarchyTreeOf: defaultClassList flattenHierarchyTree:on:indent: flattenHierarchyTree:on:indent:by: flattenHierarchyTree:on:indent:by:format: hasClassSelected hierarchicalClassList recent selectClass: selectClassNamed: selectedClass selectedClassName) ('code pane' aboutToStyle: compileMessage:notifying: showBytecodes) ('controls' decorateButtons) ('copying' veryDeepInner:) ('drag and drop' dragFromClassList: dragFromMessageList: dropOnMessageCategories:at: dropOnSystemCategories:at: wantsMessageCategoriesDrop: wantsSystemCategoriesDrop:) ('initialize-release' classListFrame: classListFrame:fromLeft:width: classListFrame:fromTop:fromLeft:width: defaultBrowserTitle frameOffsetFromTop:fromLeft:width:bottomFraction: labelString methodCategoryChanged setClass: setClass:selector: setSelector: switchesFrame: switchesFrame:fromLeft:width: systemCatSingletonKey:from: systemOrganizer: topConstantHeightFrame:fromLeft:width:) ('message category functions' addCategory alphabetizeMessageCategories buildMessageCategoryBrowser buildMessageCategoryBrowserEditString: canShowMultipleMessageCategories categoryOfCurrentMethod changeMessageCategories: editMessageCategories fileOutMessageCategories highlightMessageList:with: mainMessageCategoryMenu: messageCategoryMenu: printOutMessageCategories removeEmptyCategories removeMessageCategory renameCategory showHomeCategory) ('message category list' categorizeAllUncategorizedMethods hasMessageCategorySelected messageCatListSingleton messageCategoryList messageCategoryListIndex messageCategoryListIndex: messageCategoryListKey:from: messageCategoryListSelection rawMessageCategoryList recategorizeMethodSelector: selectMessageCategoryNamed: selectedMessageCategoryName setOriginalCategoryIndexForCurrentMethod toggleCategorySelectionForCurrentMethod) ('message functions' browseAllCommentsForClass defineMessageFrom:notifying: inspectInstances inspectSubInstances mainMessageListMenu: removeMessage removeMessageFromBrowser) ('message list' addExtraShiftedItemsTo: hasMessageSelected lastMessageName messageHelpAt: messageIconAt: messageIconFor: messageIconHelpFor: messageList messageListIndex messageListIndex: messageListIndexOf: messageListMenu:shifted: reformulateList selectMessageNamed: selectedMessage selectedMessageName selectedMessageName: shiftedMessageListMenu:) ('metaclass' classCommentIndicated classDefinitionIndicated classMessagesIndicated classOrMetaClassOrganizer indicateClassMessages indicateInstanceMessages instanceMessagesIndicated metaClassIndicated metaClassIndicated: selectedClassOrMetaClass selectedClassOrMetaClassName setClassDefinition setClassOrganizer) ('multi-window support' arrowKey:from: browseClassHierarchy isHierarchy isPackage multiWindowName multiWindowNameForState: okToClose restoreMultiWindowState: restoreToCategory:className:protocol:selector:mode:meta: saveMultiWindowState) ('pluggable menus - hooks' classListMenuHook:shifted: messageCategoryMenuHook:shifted: messageListMenuHook:shifted: systemCategoryMenuHook:shifted:) ('self-updating' didCodeChangeElsewhere) ('system category functions' addSystemCategory alphabetizeSystemCategories browseAllClasses buildSystemCategoryBrowser buildSystemCategoryBrowserEditString: changeSystemCategories: classNotFound editSystemCategories fileOutSystemCategory findClass mainSystemCategoryMenu: printOutSystemCategory removeSystemCategory renameSystemCategory systemCatSingletonMenu: systemCategoryMenu: updateSystemCategories) ('system category list' hasSystemCategorySelected indexIsOne indexIsOne: selectCategoryForClass: selectSystemCategory: selectedEnvironment selectedSystemCategory selectedSystemCategoryName systemCatListKey:from: systemCategoryList systemCategoryListIndex systemCategoryListIndex: systemCategorySingleton) ('toolbuilder' buildAndOpenCategoryBrowser buildAndOpenCategoryBrowserLabel: buildAndOpenClassBrowserLabel: buildAndOpenFullBrowser buildAndOpenMessageCategoryBrowserLabel: buildCategoryBrowserWith: buildClassListSingletonWith: buildClassListWith: buildDefaultBrowserWith: buildMessageCategoryListWith: buildMessageListCatSingletonWith: buildMessageListWith: buildSwitchesWith: buildSystemCatListSingletonWith: buildSystemCategoryListWith: buildWith: setMultiWindowFor:) ('traits' addSpecialMenu: addTrait defineTrait:notifying: newClass newTrait) ('user interface' addModelItemsToWindowMenu: defaultWindowColor) ('private' spawnOrNavigateTo:) ! Object removeSelector: #setActiveWorld:during:! Smalltalk removeClassNamed: #FileContentsBrowserTestTestObject! > From lewis at mail.msen.com Sat Sep 12 17:43:17 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 12 Sep 2020 13:43:17 -0400 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> Message-ID: <20200912174317.GB35925@shell.msen.com> On Sat, Sep 12, 2020 at 06:57:03PM +0200, Tobias Pape wrote: > > > On 12.09.2020, at 16:31, Eliot Miranda wrote: > > > > With the REPL image if it starts up and evaluates an expression of two (which are not logged) and then quits nothing has changed but the QUIT/NO SAVE has been written, and now my reproducible image/changes pair is no longer in exactly the same state; the changes file has grown. > > > > In that particular case, wouldn't it suffice to just make the Changes file read-only? > Just exit with Smalltalk quitPrimitive. It's perfectly safe on unix. Dave From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 18:38:38 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 18:38:38 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <20200912173950.GA35925@shell.msen.com> References: <953da6739f364d0388e5659c97299396@student.hpi.uni-potsdam.de>, <20200912173950.GA35925@shell.msen.com> Message-ID: <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> Glad you like it, David! :-) > In the case of the global World variable, we were able to make it an instance variable in Project. I heard of this, but where can you see the difference? If I evaluate "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World is still listed in my Smalltalk bindings. Or are you talking about making ActiveEvent & Co. instance variables of (Morphic)Project rather than process local variables? Not sure about this, do we really want to forbid multiple concurrent event processes in one Project? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Samstag, 12. September 2020 19:39:50 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic Wow Christoph, this is great work :-) I cannot review in detail now but I loaded your changes into my image and all is good so far. I am going to strongly urge that we get this into trunk as soon as we can, but I also want to challenge us all to see if we can eliminate the bindings entirely from the Environment. In the case of the global World variable, we were able to make it an instance variable in Project. It is possible that we could do something similar with the other global bindings. I is really GREAT to see this, thank you for taking it on :-) Dave On Sat, Sep 12, 2020 at 02:37:59PM +0000, Thiede, Christoph wrote: > Hi all, > > > recent discussions have shown just another time that in spite of its overall modular and object-oriented design, the Morphic System still incorporates a number of global state variables that impede modular processes in some situations. For instance, running or even debugging any form of UI simulation code in a background process was likely to cause problems because, via the global state variables, two planned-to-be-independent projects undesirably shared their events, hands, and worlds. Concrete systems suffering from this global state include various UI tests executed using AutoTDD [1], or the screenshot generation framework for Squeak by Example [2] which I had the joy to co-develop. > > > The attached changeset tackles these issues for all packages in the Trunk by wrapping the following three globals into process-local accessors: ActiveEvent, ActiveHand, and ActiveWorld. > > As the changeset contains patches of over 300 selectors in more than 100 classes every single line of which you probably will not feel like reading in detail, here is a summary of all changes I applied: > > * Added #activeEvent[:], #activeHand[:], and #activeWorld[:] process-local accessors on Object as Morphic-Kernel extensions. The actual values are stored directly on the active process in the manner of a ProcessSpecificVariable. For backward compatibility, the global variables are still kept up to date here. > * Added #activateHand:during: and #activateWorld:during: as dynamic scope setters on Object as Morphic-Kernel extensions. > * Replaced all references to ActiveEvent, ActiveHand, and ActiveWorld by "self activeEvent", "self activeHand", and "self activeWorld" accordingly. I also spent some time reflecting in which cases you actually would like to receive a possible nil value and ended up with changing the most senders that are not involved into the critical event processing logic into their "#current*" equivalents (#currentEvent, #currentHand, and #currentWorld) which already guarantee to return non-nil values. In the case of #currentWorld, I also replaced many senders with "Project current world" that were not invoked in an event-related context. > * While skimming over all the implementations, I also applied a number of really minor refactorings: improve multilingual support by adding some "#translated"s to user strings, remove nil checks that could never be reached, and reformat some of the very hardest to read methods I came across. > > > Please review! I'm looking forward to eliminating these unnecessary artifacts of global state and making Squeak an even more purely object-oriented and modular system by merging these changes into the Trunk. > > > Best, > > Christoph > > > [1] https://github.com/hpi-swa-teaching/AutoTDD > > [2] https://github.com/codeZeilen/SqueakByExample-english/ Content-Description: Hide activeVariables.2.cs > 'From Squeak6.0alpha of 6 September 2020 [latest update: #19838] on 12 September 2020 at 4:29:53 pm'! !Object methodsFor: 'user interface' stamp: 'ct 9/12/2020 14:13'! launchPartVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins" | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph openInHand.! ! !Object methodsFor: '*Protocols' stamp: 'ct 9/12/2020 14:14'! haveFullProtocolBrowsedShowingSelector: aSelector "Open up a Lexicon on the receiver, having it open up showing aSelector, which may be nil" "(2 at 3) haveFullProtocolBrowsed" | aBrowser | aBrowser := (Smalltalk at: #InstanceBrowser ifAbsent: [^ nil]) new useVocabulary: Vocabulary fullVocabulary. aBrowser openOnObject: self inWorld: Project current world showingSelector: aSelector! ! !Object methodsFor: '*Etoys-Squeakland-user interface' stamp: 'ct 9/12/2020 14:13'! launchPartOffsetVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins. This variant makes the morph offset from the hand position by an amount suitable for tile-scripting in some circumstances." | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph setProperty: #offsetForAttachingToHand toValue: 10@ -10. aMorph fullBounds. aMorph openInHand! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:45'! activateHand: aHand during: aBlock | priorHand | priorHand := self activeHand. self activeHand: aHand. ^ aBlock ensure: [ self activeHand: priorHand]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 16:12'! activateWorld: aWorld during: aBlock | priorWorld | priorWorld := self activeWorld. self activeWorld: aWorld. ^ aBlock ensure: [ self activeWorld: priorWorld]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:53'! activeEvent "Answer the active morphic event for the current process, or nil if no event is active. Private!! Usually, you will want to send #currentEvent instead." ^ Processor activeProcess environmentAt: #ActiveEvent ifAbsent: [nil]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:53'! activeEvent: anEvent "Set the active morphic event for the current process. Can be nil." Processor activeProcess environmentAt: #ActiveEvent put: anEvent. "for backword compatibility <6.0" ActiveEvent := anEvent.! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:52'! activeHand "Answer the active HandMorph for the current process, or nil if no hand is active. Private!! Usually, you will want to send #currentHand instead." ^ Processor activeProcess environmentAt: #ActiveHand ifAbsent: [nil]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:51'! activeHand: aHand "Set the active HandMorph for the current process. Can be nil." Processor activeProcess environmentAt: #ActiveHand put: aHand. "for backword compatibility <6.0" ActiveHand := aHand.! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:52'! activeWorld "Answer the active morphic world for the current process, or nil if no world is active. Private!! Usually, you will want to send #currentWorld instead." ^ Processor activeProcess environmentAt: #ActiveWorld ifAbsent: [nil]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:52'! activeWorld: aWorld "Set the active morphic world for the current process. Can be nil." Processor activeProcess environmentAt: #ActiveWorld put: aWorld. "for backword compatibility <6.0" ActiveWorld := aWorld.! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:33'! currentEvent "Answer the current Morphic event. This method never returns nil." ^ self activeEvent ifNil: [self currentHand lastEvent]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:17'! currentHand "Return a usable HandMorph -- the one associated with the object's current environment. This method will always return a hand, even if it has to conjure one up as a last resort. If a particular hand is actually handling events at the moment (such as a remote hand or a ghost hand), it will be returned." ^ self activeHand ifNil: [self currentWorld primaryHand]! ! !Object methodsFor: '*Morphic-Kernel' stamp: 'ct 9/12/2020 15:01'! currentWorld "Answer a morphic world that is the current UI focus." ^ self activeWorld ifNil: [Project current world]! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:05'! currentBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: Browser]]) asSet! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:04'! currentHierarchyBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: HierarchyBrowser]]) asSet! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'Your name?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint onCancelReturn: cancelResponse! ! !Flaps class methodsFor: 'construction support' stamp: 'ct 9/11/2020 20:09'! possiblyReplaceEToyFlaps "If in eToyFriendly mode, and if it's ok to reinitialize flaps, replace the existing flaps with up-too-date etoy flaps. Caution: this is destructive of existing flaps. If preserving the contents of existing flaps is important, set the preference 'okToReinitializeFlaps' to true" PartsBin thumbnailForPartsDescription: StickyPadMorph descriptionForPartsBin. "Puts StickyPadMorph's custom icon back in the cache which typically will have been called" (Preferences eToyFriendly and: [Preferences okToReinitializeFlaps]) ifTrue: [Flaps disableGlobalFlaps: false. Flaps addAndEnableEToyFlaps. Smalltalk isMorphic ifTrue: [Project current world enableGlobalFlaps]]. "PartsBin clearThumbnailCache" "Flaps possiblyReplaceEToyFlaps"! ! !Flaps class methodsFor: 'menu commands' stamp: 'ct 9/11/2020 19:49'! disableGlobalFlaps: interactive "Clobber all the shared flaps structures. First read the user her Miranda rights." interactive ifTrue: [(self confirm: 'CAUTION!! This will destroy all the shared flaps, so that they will not be present in *any* project. If, later, you want them back, you will have to reenable them, from this same menu, whereupon the standard default set of shared flaps will be created. Do you really want to go ahead and clobber all shared flaps at this time?' translated) ifFalse: [^ self]]. self globalFlapTabsIfAny do: [:aFlapTab | self removeFlapTab: aFlapTab keepInList: false. aFlapTab isInWorld ifTrue: [self error: 'Flap problem' translated]]. self clobberFlapTabList. self initializeFlapsQuads. SharedFlapsAllowed := false. Smalltalk isMorphic ifTrue: [ Project current world restoreMorphicDisplay; reformulateUpdatingMenus]. "The following reduces the risk that flaps will be created with variant IDs such as 'Stack Tools2', potentially causing some shared flap logic to fail." "Smalltalk garbageCollect." "-- see if we are OK without this"! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:08'! enableGlobalFlaps "Start using global flaps, given that they were not present." Cursor wait showWhile: [ SharedFlapsAllowed := true. self globalFlapTabs. "This will create them" Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. FlapTab allInstancesDo: [:tab | tab computeEdgeFraction]. Project current world reformulateUpdatingMenus]]! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:09'! setUpSuppliesFlapOnly "Set up the Supplies flap as the only shared flap. A special version formulated for this stand-alone use is used, defined in #newLoneSuppliesFlap" | supplies | SharedFlapTabs isEmptyOrNil ifFalse: "get rid of pre-existing guys if any" [SharedFlapTabs do: [:t | t referent delete. t delete]]. SharedFlapsAllowed := true. SharedFlapTabs := OrderedCollection new. SharedFlapTabs add: (supplies := self newLoneSuppliesFlap). self enableGlobalFlapWithID: 'Supplies' translated. supplies setToPopOutOnMouseOver: false. Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps; reformulateUpdatingMenus].! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:08'! enableClassicNavigatorChanged "The #classicNavigatorEnabled preference has changed. No senders in easily traceable in the image, but this is really sent by a Preference object!!" Preferences classicNavigatorEnabled ifTrue: [Flaps disableGlobalFlapWithID: 'Navigator' translated. Preferences enable: #showProjectNavigator. self disableGlobalFlapWithID: 'Navigator' translated.] ifFalse: [self enableGlobalFlapWithID: 'Navigator' translated. Project current world addGlobalFlaps]. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. Project current world reformulateUpdatingMenus.! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:09'! makeNavigatorFlapResembleGoldenBar "At explicit request, make the flap-based navigator resemble the golden bar. No senders in the image, but sendable from a doit" "Flaps makeNavigatorFlapResembleGoldenBar" Preferences setPreference: #classicNavigatorEnabled toValue: false. Preferences setPreference: #showProjectNavigator toValue: false. (self globalFlapTabWithID: 'Navigator' translated) ifNil: [SharedFlapTabs add: self newNavigatorFlap delete]. self enableGlobalFlapWithID: 'Navigator' translated. Preferences setPreference: #navigatorOnLeftEdge toValue: true. (self globalFlapTabWithID: 'Navigator' translated) arrangeToPopOutOnMouseOver: true. Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. ! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:58'! addLocalFlap ^ self addLocalFlap: self currentEvent! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent "Menu command -- let the user add a new project-local flap. Once the new flap is born, the user can tell it to become a shared flap. Obtain an initial name and edge for the flap, launch the flap, and also launch a menu governing the flap, so that the user can get started right away with customizing it." | title edge | edge := self askForEdgeOfNewFlap. edge ifNil: [^ self]. title := UIManager default request: 'Wording for this flap:' translated initialAnswer: 'Flap' translated. title isEmptyOrNil ifTrue: [^ self]. ^ self addLocalFlap: anEvent titled: title onEdge: edge! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent titled: title onEdge: edge | flapTab menu world | flapTab := self newFlapTitled: title onEdge: edge. (world := anEvent hand world) addMorphFront: flapTab. flapTab adaptToWorld: world. menu := flapTab buildHandleMenu: anEvent hand. flapTab addTitleForHaloMenu: menu. flapTab computeEdgeFraction. menu popUpEvent: anEvent in: world.! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! enableOnlyGlobalFlapsWithIDs: survivorList "In the current project, suppress all global flaps other than those with ids in the survivorList" self globalFlapTabsIfAny do: [:flapTab | (survivorList includes: flapTab flapID) ifTrue: [self enableGlobalFlapWithID: flapTab flapID] ifFalse: [self disableGlobalFlapWithID: flapTab flapID]]. Project current world addGlobalFlaps "Flaps enableOnlyGlobalFlapsWithIDs: #('Supplies')"! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! positionVisibleFlapsRightToLeftOnEdge: edgeSymbol butPlaceAtLeftFlapsWithIDs: idList "Lay out flaps along the designated edge right-to-left, while laying left-to-right any flaps found in the exception list Flaps positionVisibleFlapsRightToLeftOnEdge: #bottom butPlaceAtLeftFlapWithIDs: {'Navigator' translated. 'Supplies' translated} Flaps sharedFlapsAlongBottom" | leftX flapList flapsOnRight flapsOnLeft | flapList := self globalFlapTabsIfAny select: [:aFlapTab | aFlapTab isInWorld and: [aFlapTab edgeToAdhereTo == edgeSymbol]]. flapsOnLeft := OrderedCollection new. flapsOnRight := OrderedCollection new. flapList do: [:fl | (idList includes: fl flapID) ifTrue: [ flapsOnLeft addLast: fl ] ifFalse: [ flapsOnRight addLast: fl ] ]. leftX := Project current world width - 15. flapsOnRight sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab right: leftX - 3. leftX := aFlapTab left]. leftX := Project current world left. flapsOnLeft sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab left: leftX + 3. leftX := aFlapTab right]. flapList do: [:ft | ft computeEdgeFraction. ft flapID = 'Navigator' translated ifTrue: [ft referent left: (ft center x - (ft referent width//2) max: 0)]]! ! !Flaps class methodsFor: '*Etoys-Squeakland-predefined flaps' stamp: 'ct 9/12/2020 14:29'! newSuppliesFlapFromQuads: quads positioning: positionSymbol withPreviousEntries: aCollection "Answer a fully-instantiated flap named 'Supplies' to be placed at the bottom of the screen. Use #center as the positionSymbol to have it centered at the bottom of the screen, or #right to have it placed off near the right edge." | aFlapTab aStrip aWidth sugarNavigator | sugarNavigator := SugarNavigatorBar showSugarNavigator. aStrip := PartsBin newPartsBinWithOrientation: #leftToRight andColor: Color gray muchLighter from: quads withPreviousEntries: aCollection. self twiddleSuppliesButtonsIn: aStrip. aFlapTab := (sugarNavigator ifTrue: [SolidSugarSuppliesTab] ifFalse: [FlapTab]) new referent: aStrip beSticky. aFlapTab setName: 'Supplies' translated edge: (sugarNavigator ifTrue: [#top] ifFalse: [#bottom]) color: Color red lighter. aFlapTab position: (0 @ Project current world sugarAllowance). aFlapTab setBalloonText: aFlapTab balloonTextForFlapsMenu. aFlapTab applyThickness: 20. aWidth := self currentWorld width. aStrip extent: aWidth @ (76 * (1 + (1350 // aWidth))). aStrip beFlap: true. aStrip autoLineLayout: true. aStrip vResizeToFit: true. sugarNavigator ifTrue: [ aFlapTab useSolidTab. aFlapTab height: 20; color: (Color r: 0.804 g: 0.804 b: 0.804)] ifFalse: [ aFlapTab color: Color red lighter]. ^ aFlapTab "Flaps replaceGlobalFlapwithID: 'Supplies' translated"! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleColorSees "Form exampleColorSees" "First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area - where red touches blue - superimposed on the original scene. Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" | formA formB maskA offset tally map intersection left top dCanvas sensitiveColor soughtColor index | formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. sensitiveColor := Color red. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: sensitiveColor. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map at: (index := sensitiveColor indexInMap: map) put: 1. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map at: index put: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((sensitiveColor pixelValueForDepth: formA depth) ) to: ((soughtColor pixelValueForDepth: formB depth) ) test: (Form compareMatchColor bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchTest "Form exampleTouchTest" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a non-transparent pixel of the background upon which it is displayed. First column shows a form with a red block in the midst of transparent area sneaking up on a form with a transparent LHS and blue RHS. The green frame shows the intersection area. Second column shows in grey the part of the red that is within the intersection. Third column shows in black the blue that is within the intersection. Fourth column shows just the A touching B area. Fifth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA maskB offset tally map intersection left top dCanvas| formA := formB := maskA := maskB := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := Form extent: 100 at 50 depth: 32. formB := Form extent: 100 at 50 depth: 16. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color yellow. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color red. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: Color blue. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 2. formA displayOn: maskA at: offset - intersection origin rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskB := Form extent: intersection extent depth: 2. formB displayOn: maskB at: intersection origin negated rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. map := Bitmap new: 4 withAll: 1. map at: 1 put: 0. "transparent" maskA copyBits: maskA boundingBox from: maskA at: 0 at 0 colorMap: map. "maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB copyBits: maskB boundingBox from: maskB at: 0 at 0 colorMap: map. "maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB displayOn: maskA at: 0 at 0 rule: Form and. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA boundingBox area -( maskA tallyPixelValues at: 1)) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((Color transparent pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((Color transparent pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorANotColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchingColor "Form exampleTouchingColor" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a particular color pixel of the background upon which it is displayed. First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area (black) superimposed on the original scene Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA offset tally map intersection left top dCanvas ignoreColor soughtColor| formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. ignoreColor := Color transparent. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color red. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map atAllPut: 1. map at: ( ignoreColor indexInMap: map) put: 0. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map atAllPut: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((ignoreColor pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((soughtColor pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorAMatchColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !HandBugs methodsFor: 'tests' stamp: 'ct 9/12/2020 14:41'! testTargetPoint "self new testTargetPoint" "self run: #testTargetPoint" "This should not throw an exception." self currentHand targetPoint ! ! !Lexicon methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:16'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'Lexicon' translated. aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var assignments (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !InstanceBrowser methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:12'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: ('Messages of {1}' translated format: {objectViewed nameForViewer}). aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var defs (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('viewer on me' viewViewee) ('inspector on me' inspectViewee) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !ListChooser methodsFor: 'actions' stamp: 'ct 9/12/2020 14:42'! accept "if the user submits with no valid entry, make them start over" | choice | self canAccept ifFalse: [ self canAdd ifTrue: [^ self add]. ^ self changed: #textSelection]. choice := self selectedItem. self canAdd ifTrue: [ "Ask the user whether to add the new item or choose the list selection." (UserDialogBoxMorph confirm: 'You can either choose an existing item or add a new one.\What do you want?' translated withCRs title: 'Choose or Add' translated trueChoice: choice asString falseChoice: self searchText asString at: self currentHand position) ifNil: ["Cancelled" self result: nil. ^ self] ifNotNil: [:answer | answer ifTrue: [self result: choice] ifFalse: [self result: self searchText asString]] ] ifFalse: [self result: choice]. self changed: #close.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! setUp previousID := Locale current localeID. previousKeyboardInterpreter := self currentHand instVarNamed: 'keyboardInterpreter'. previousClipboardInterpreter := Clipboard default instVarNamed: 'interpreter'. self currentHand clearKeyboardInterpreter. Clipboard default clearInterpreter.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! tearDown self currentHand instVarNamed: 'keyboardInterpreter' put: previousKeyboardInterpreter. Clipboard default instVarNamed: 'interpreter' put: previousClipboardInterpreter. Locale switchToID: (LocaleID isoLanguage: previousID).! ! !LocaleTest methodsFor: 'tests' stamp: 'ct 9/12/2020 14:43'! testLocaleChanged "self debug: #testLocaleChanged" "LanguageEnvironment >> startUp is called from Prject >> localeChanged" "takes quite a while" Project current updateLocaleDependents. self assert: (self currentHand instVarNamed: 'keyboardInterpreter') isNil description: 'non-nil keyboardInterpreter'. self assert: (Clipboard default instVarNamed: 'interpreter') isNil description: 'non-nil interpreter'. Locale switchToID: (LocaleID isoLanguage: 'ja'). self assert: 'ja' equals: Locale current localeID isoLanguage. Locale switchToID: (LocaleID isoLanguage: 'en'). self assert: 'en' equals: Locale current localeID isoLanguage.! ! !MCCodeTool methodsFor: 'menus' stamp: 'ct 9/11/2020 20:17'! browseFullProtocol "Open up a protocol-category browser on the value of the receiver's current selection. If in mvc, an old-style protocol browser is opened instead. Someone who still uses mvc might wish to make the protocol-category-browser work there too, thanks." (Smalltalk isMorphic and: [Smalltalk hasClassNamed: #Lexicon]) ifFalse: [^ self spawnFullProtocol]. self selectedClassOrMetaClass ifNotNil: [:class | ^ (Smalltalk at: #Lexicon) new openOnClass: class inWorld: self currentWorld showingSelector: self selectedMessageName]. ^ nil! ! !Morph methodsFor: 'copying' stamp: 'ct 9/12/2020 14:20'! duplicate "Make and return a duplicate of the receiver" | newMorph aName w aPlayer topRend | ((topRend := self topRendererOrSelf) ~~ self) ifTrue: [^ topRend duplicate]. self okayToDuplicate ifFalse: [^ self]. aName := (w := self world) ifNotNil: [w nameForCopyIfAlreadyNamed: self]. newMorph := self veryDeepCopy. aName ifNotNil: [newMorph setNameTo: aName]. newMorph arrangeToStartStepping. newMorph privateOwner: nil. "no longer in world" newMorph isPartsDonor: false. "no longer parts donor" (aPlayer := newMorph player) belongsToUniClass ifTrue: [aPlayer class bringScriptsUpToDate]. aPlayer ifNotNil: [self currentWorld presenter flushPlayerListCache]. ^ newMorph! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:20'! justDroppedInto: aMorph event: anEvent "This message is sent to a dropped morph after it has been dropped on -- and been accepted by -- a drop-sensitive morph" | partsBinCase cmd | (self formerOwner notNil and: [self formerOwner ~~ aMorph]) ifTrue: [self removeHalo]. self formerOwner: nil. self formerPosition: nil. cmd := self valueOfProperty: #undoGrabCommand. cmd ifNotNil:[aMorph rememberCommand: cmd. self removeProperty: #undoGrabCommand]. (partsBinCase := aMorph isPartsBin) ifFalse: [self isPartsDonor: false]. (self isInWorld and: [partsBinCase not]) ifTrue: [self world startSteppingSubmorphsOf: self]. "Note an unhappy inefficiency here: the startStepping... call will often have already been called in the sequence leading up to entry to this method, but unfortunately the isPartsDonor: call often will not have already happened, with the result that the startStepping... call will not have resulted in the startage of the steppage." "An object launched by certain parts-launcher mechanisms should end up fully visible..." (self hasProperty: #beFullyVisibleAfterDrop) ifTrue: [aMorph == self currentWorld ifTrue: [self goHome]. self removeProperty: #beFullyVisibleAfterDrop].! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:19'! slideToTrash: evt "Perhaps slide the receiver across the screen to a trash can and make it disappear into it. In any case, remove the receiver from the screen." | aForm trash startPoint endPoint morphToSlide | ((self renderedMorph == ScrapBook default scrapBook) or: [self renderedMorph isKindOf: TrashCanMorph]) ifTrue: [self dismissMorph. ^ self]. TrashCanMorph slideDismissalsToTrash ifTrue: [morphToSlide := self representativeNoTallerThan: 200 norWiderThan: 200 thumbnailHeight: 100. aForm := morphToSlide imageForm offset: (0 at 0). trash := self currentWorld findDeepSubmorphThat: [:aMorph | (aMorph isKindOf: TrashCanMorph) and: [aMorph topRendererOrSelf owner == self currentWorld]] ifAbsent: [trash := TrashCanMorph new. trash position: self currentWorld bottomLeft - (0 @ (trash extent y + 26)). trash openInWorld. trash]. endPoint := trash fullBoundsInWorld center. startPoint := self topRendererOrSelf fullBoundsInWorld center - (aForm extent // 2)]. self dismissMorph. self currentWorld displayWorld. TrashCanMorph slideDismissalsToTrash ifTrue: [aForm slideFrom: startPoint to: endPoint nSteps: 12 delay: 15]. ScrapBook default addToTrash: self! ! !Morph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:45'! yellowButtonActivity: shiftState "Find me or my outermost owner that has items to add to a yellow button menu. shiftState is true if the shift was pressed. Otherwise, build a menu that contains the contributions from myself and my interested submorphs, and present it to the user." | menu | self isWorldMorph ifFalse: [| outerOwner | outerOwner := self outermostOwnerWithYellowButtonMenu. outerOwner ifNil: [^ self]. outerOwner == self ifFalse: [^ outerOwner yellowButtonActivity: shiftState]]. menu := self buildYellowButtonMenu: self currentHand. menu addTitle: self externalName icon: (self iconOrThumbnailOfSize: (Preferences tinyDisplay ifTrue: [16] ifFalse: [28])). menu popUpInWorld: self currentWorld! ! !Morph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:00'! buildYellowButtonMenu: aHand "Build the morph menu for the yellow button." | menu | menu := MenuMorph new defaultTarget: self. self addNestedYellowButtonItemsTo: menu event: self currentEvent. MenuIcons decorateMenu: menu. ^ menu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:44'! addMiscExtrasTo: aMenu "Add a submenu of miscellaneous extra items to the menu." | realOwner realMorph subMenu | subMenu := MenuMorph new defaultTarget: self. (self isWorldMorph not and: [(self renderedMorph isSystemWindow) not]) ifTrue: [subMenu add: 'put in a window' translated action: #embedInWindow]. self isWorldMorph ifFalse: [subMenu add: 'adhere to edge...' translated action: #adhereToEdge. subMenu addLine]. realOwner := (realMorph := self topRendererOrSelf) owner. (realOwner isKindOf: TextPlusPasteUpMorph) ifTrue: [subMenu add: 'GeeMail stuff...' translated subMenu: (realOwner textPlusMenuFor: realMorph)]. subMenu add: 'add mouse up action' translated action: #addMouseUpAction; add: 'remove mouse up action' translated action: #removeMouseUpAction; add: 'hand me tiles to fire this button' translated action: #handMeTilesToFire. subMenu addLine. subMenu add: 'arrowheads on pen trails...' translated action: #setArrowheads. subMenu addLine. subMenu defaultTarget: self topRendererOrSelf. subMenu add: 'draw new path' translated action: #definePath. subMenu add: 'follow existing path' translated action: #followPath. subMenu add: 'delete existing path' translated action: #deletePath. subMenu addLine. self addGestureMenuItems: subMenu hand: self currentHand. aMenu add: 'extras...' translated subMenu: subMenu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:21'! chooseNewGraphicCoexisting: aBoolean "Allow the user to choose a different form for her form-based morph" | replacee aGraphicalMenu | self isInWorld ifFalse: "menu must have persisted for a not-in-world object." [aGraphicalMenu := Project current world submorphThat: [:m | (m isKindOf: GraphicalMenu) and: [m target == self]] ifNone: [^ self]. ^ aGraphicalMenu show; flashBounds]. aGraphicalMenu := GraphicalMenu new initializeFor: self withForms: self reasonableForms coexist: aBoolean. aBoolean ifTrue: [self primaryHand attachMorph: aGraphicalMenu] ifFalse: [replacee := self topRendererOrSelf. replacee owner replaceSubmorph: replacee by: aGraphicalMenu]! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/12/2020 14:20'! indicateAllSiblings "Indicate all the receiver and all its siblings by flashing momentarily." | aPlayer allBoxes | (aPlayer := self topRendererOrSelf player) belongsToUniClass ifFalse: [^ self "error: 'not uniclass'"]. allBoxes := aPlayer class allInstances select: [:m | m costume world == self currentWorld] thenCollect: [:m | m costume boundsInWorld]. 5 timesRepeat: [Display flashAll: allBoxes andWait: 120].! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/11/2020 18:00'! resizeFromMenu "Commence an interaction that will resize the receiver" ^ self resizeMorph: self currentEvent! ! !Morph methodsFor: 'structure' stamp: 'ct 9/12/2020 15:17'! activeHand ^ super activeHand ifNil: [ self isInWorld ifTrue: [self world activeHand] ifFalse: [nil]]! ! !Morph methodsFor: 'structure' stamp: 'ct 9/12/2020 14:40'! primaryHand | outer | outer := self outermostWorldMorph ifNil: [^ nil]. ^ outer activeHand ifNil: [outer firstHand]! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:45'! deleteUnlessHasFocus "Runs on a step timer because we cannot be guaranteed to get focus change events." (self currentHand keyboardFocus ~= self and: [ self isInWorld ]) ifTrue: [ self stopSteppingSelector: #deleteUnlessHasFocus ; delete ]! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:20'! dismissViaHalo "The user has clicked in the delete halo-handle. This provides a hook in case some concomitant action should be taken, or if the particular morph is not one which should be put in the trash can, for example." | cmd | self setProperty: #lastPosition toValue: self positionInWorld. self dismissMorph. TrashCanMorph preserveTrash ifTrue: [ TrashCanMorph slideDismissalsToTrash ifTrue:[self slideToTrash: nil] ifFalse:[TrashCanMorph moveToTrash: self]. ]. cmd := Command new cmdWording: 'dismiss ' translated, self externalName. cmd undoTarget: Project current world selector: #reintroduceIntoWorld: argument: self. cmd redoTarget: Project current world selector: #onceAgainDismiss: argument: self. Project current world rememberCommand: cmd.! ! !Morph methodsFor: 'e-toy support' stamp: 'ct 9/12/2020 14:19'! referencePlayfield "Answer the PasteUpMorph to be used for cartesian-coordinate reference" | former | owner ifNotNil: [(self topRendererOrSelf owner isHandMorph and: [(former := self formerOwner) notNil]) ifTrue: [former := former renderedMorph. ^ former isPlayfieldLike ifTrue: [former] ifFalse: [former referencePlayfield]]]. self allOwnersDo: [:o | o isPlayfieldLike ifTrue: [^ o]]. ^ Project current world! ! !Morph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:45'! handMeTilesToFire "Construct a phrase of tiles comprising a line of code that will 'fire' this object, and hand it to the user" self currentHand attachMorph: (self assuredPlayer tilesToCall: MethodInterface firingInterface)! ! !Morph methodsFor: '*Etoys-Squeakland-geometry' stamp: 'ct 9/12/2020 14:19'! stagingArea "Answer a containing Worldlet, or the World if none." ^ (self ownerThatIsA: Worldlet) ifNil: [self currentWorld]! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:22'! changeColorTarget: anObject selector: aSymbol originalColor: aColor hand: aHand showPalette: showPalette "Put up a color picker for changing some kind of color. May be modal or modeless, depending on #modalColorPickers setting" | c aRectangle | self flag: #arNote. "Simplify this due to anObject == self for almost all cases" c := ColorPickerMorph new. c choseModalityFromPreference; sourceHand: aHand; target: anObject; selector: aSymbol; originalColor: aColor. showPalette ifFalse: [c initializeForJustCursor]. aRectangle := (anObject == self currentWorld) ifTrue: [self currentHand position extent: (20 at 20)] ifFalse: [anObject isMorph ifFalse: [Rectangle center: self position extent: (20 at 20)] ifTrue: [anObject fullBoundsInWorld]]. c putUpFor: anObject near: aRectangle.! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:45'! showEmbedMenu "Put up a menu offering embed targets. Emphasize the current position. Theoretically this method will only be called when there are at least two alternatives." | aMenu | aMenu := self addEmbeddingMenuItemsTo: nil hand: self currentHand. aMenu title: ('embed {1} in...' translated format: {self externalName }). aMenu popUpInWorld! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:20'! hideWillingnessToAcceptDropFeedback "Make the receiver stop looking ready to show some welcoming feedback" self currentWorld removeHighlightFeedback ! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:19'! showWillingnessToAcceptDropFeedback "Make the receiver look ready to show show some welcoming feedback" | aMorph | aMorph := RectangleMorph new bounds: self bounds.. aMorph beTransparent; borderWidth: 4; borderColor: (Color green); lock. aMorph setProperty: #affilliatedPad toValue: (self ownerThatIsA: TilePadMorph). self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !Morph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 15:17'! openInWorldOrWorldlet "Open in the world-like creature affiliated with the active Hand." | aRecorder aWorldlet | (self currentHand isKindOf: HandMorphForReplay) ifTrue: [((aRecorder := self currentHand recorder) isKindOf: MentoringEventRecorder) ifTrue: [aWorldlet := aRecorder contentArea. self center: aWorldlet center. aWorldlet addMorphFront: self. ^ self]]. self openInWorld.! ! !AllPlayersTool methodsFor: 'reinvigoration' stamp: 'ct 9/12/2020 14:25'! reinvigorate "Referesh the contents of the receiver" (submorphs copyFrom: 3 to: submorphs size) do: [:m | m delete]. self currentWorld doOneCycleNow. self playSoundNamed: 'scritch'. (Delay forMilliseconds: 700) wait. self currentWorld presenter reinvigoratePlayersTool: self. self playSoundNamed: 'scratch'.! ! !AllScriptsTool methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:26'! addSecondLineOfControls "Add the second line of controls" | aRow outerButton aButton worldToUse | aRow := AlignmentMorph newRow listCentering: #center; color: Color transparent. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingOnlyActiveScripts; getSelector: #showingOnlyActiveScripts. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'tickers only' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then only scripts that are paused or ticking will be shown' translated. aRow addMorphBack: outerButton. aRow addTransparentSpacerOfSize: 20 at 0. aRow addMorphBack: self helpButton. aRow addTransparentSpacerOfSize: 20 at 0. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingAllInstances; getSelector: #showingAllInstances. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'all instances' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then entries for all instances will be shown, but if not checked, scripts for only one representative of each different kind of object will be shown. Consult the help available by clicking on the purple ? for more information.' translated. aRow addMorphBack: outerButton. self addMorphBack: aRow. worldToUse := self isInWorld ifTrue: [self world] ifFalse: [self currentWorld]. worldToUse presenter reinvigorateAllScriptsTool: self. self layoutChanged.! ! !BookMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:39'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer pageNum: pageNum "Call once to search a page of the book. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container wasIn strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [(pages at: pageNum) isInMemory ifFalse: [rawStrings] ifTrue: [(pages at: pageNum) allStringsAfter: oldContainer]]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findString: searchString startingAt: start caseSensitive: false) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" wasIn := (pages at: pageNum) isInMemory. self goToPage: pageNum. wasIn ifFalse: ["search again, on the real current text. Know page is in." ^self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) startAt: startIndex container: oldContainer pageNum: pageNum "recompute"]]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !BookMorph methodsFor: 'navigation' stamp: 'ct 9/12/2020 14:26'! goToPageMorph: newPage transitionSpec: transitionSpec "Go to a page, which is assumed to be an element of my pages array (if it is not, this method returns quickly. Apply the transitionSpec provided." | pageIndex aWorld oldPageIndex ascending tSpec readIn | pages isEmpty ifTrue: [^self]. self setProperty: #searchContainer toValue: nil. "forget previous search" self setProperty: #searchOffset toValue: nil. self setProperty: #searchKey toValue: nil. pageIndex := pages identityIndexOf: newPage ifAbsent: [^self "abort"]. readIn := newPage isInMemory not. oldPageIndex := pages identityIndexOf: currentPage ifAbsent: [nil]. ascending := (oldPageIndex isNil or: [newPage == currentPage]) ifTrue: [nil] ifFalse: [oldPageIndex < pageIndex]. tSpec := transitionSpec ifNil: ["If transition not specified by requestor..." newPage valueOfProperty: #transitionSpec ifAbsent: [" ... then consult new page" self transitionSpecFor: self " ... otherwise this is the default"]]. self flag: #arNote. "Probably unnecessary" (aWorld := self world) ifNotNil: [self primaryHand releaseKeyboardFocus]. currentPage ifNotNil: [currentPage updateCachedThumbnail]. self currentPage notNil ifTrue: [(((pages at: pageIndex) owner isKindOf: TransitionMorph) and: [(pages at: pageIndex) isInWorld]) ifTrue: [^self "In the process of a prior pageTurn"]. self currentPlayerDo: [:aPlayer | aPlayer runAllClosingScripts]. self removeViewersOnSubsIn: self currentWorld presenter. ascending ifNotNil: ["Show appropriate page transition and start new page when done" currentPage stopStepping. (pages at: pageIndex) position: currentPage position. ^(TransitionMorph effect: tSpec second direction: tSpec third inverse: (ascending or: [transitionSpec notNil]) not) showTransitionFrom: currentPage to: (pages at: pageIndex) in: self whenStart: [self playPageFlipSound: tSpec first] whenDone: [currentPage delete; fullReleaseCachedState. self insertPageMorphInCorrectSpot: (pages at: pageIndex). self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrlInBook: self url. currentPage sqkPage computeThumbnail "just store it"]]]. "No transition, but at least decommission current page" currentPage delete; fullReleaseCachedState]. self insertPageMorphInCorrectSpot: (pages at: pageIndex). "sets currentPage" self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrl. currentPage sqkPage computeThumbnail "just store it"]. self currentWorld presenter flushPlayerListCache.! ! !BookMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:38'! addAdvancedItemsTo: aMenu "Add advanced items to a menu which allow the user to affect all the pages of the book. NB balloon help msgs still pending." | subMenu | subMenu := MenuMorph new defaultTarget: self. subMenu addTranslatedList: #( ('make all pages the same size as this page' makeUniformPageSize 'Make all the pages of this book be the same size as the page currently showing.') ('set background color for all pages' #setPageColor 'Choose a color to assign as the background color for all of this book''s pages') - ('uncache page sorter' uncachePageSorter) ('make a thread of projects in this book' buildThreadOfProjects) - ('make this the template for new pages' setNewPagePrototype)) translatedNoop. "NB The following 2 items do not get auto-updated in a persistent menu." newPagePrototype ifNotNil: [ subMenu add: 'clear new-page template' translated action: #clearNewPagePrototype]. self isInFullScreenMode ifTrue: [ subMenu add: 'exit full screen' translated action: #exitFullScreen] ifFalse: [ subMenu add: 'show full screen' translated action: #goFullScreen]. (self currentHand pasteBuffer isKindOf: PasteUpMorph) ifTrue: [ subMenu addLine. subMenu add: 'paste book page' translated action: #pasteBookPage]. aMenu add: 'advanced...' translated subMenu: subMenu.! ! !CategoryViewer methodsFor: 'get/set slots' stamp: 'ct 9/12/2020 14:39'! makeUniversalTilesGetter: aMethodInterface event: evt from: aMorph "Button in viewer performs this to make a universal-tiles getter and attach it to hand." | newTiles | newTiles := self newGetterTilesFor: scriptedPlayer methodInterface: aMethodInterface. newTiles setProperty: #beScript toValue: true. owner ifNil: [^ newTiles]. self currentHand attachMorph: newTiles. newTiles align: newTiles topLeft with: evt hand position + (7 at 14).! ! !CategoryViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 19:42'! currentVocabulary "Answer the vocabulary currently installed in the viewer. The outer StandardViewer object holds this information." ^ self outerViewer ifNotNil: [:viewer | viewer currentVocabulary] ifNil: [(self world ifNil: [self currentWorld]) currentVocabularyFor: scriptedPlayer]! ! !CategoryViewer methodsFor: '*Etoys-Squeakland-categories' stamp: 'ct 9/11/2020 19:42'! assureCategoryFullyVisible "Keep deleting categoryviewers other than the receiver until the receiver is fully visible." | ready toDelete | ready := false. [(self bounds bottom > self world bottom) and: [ready not]] whileTrue: [ owner submorphs size > 2 ifTrue: [ toDelete := owner submorphs allButFirst reversed detect: [:cv | cv ~~ self] ifNone: [^ self]. toDelete delete. self world doOneCycleNow] ifFalse: [ ready := true]].! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: self bounds; beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock; yourself. self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! removeHighlightFeedback "Remove any existing highlight feedback" self world removeHighlightFeedback. ! ! !DialogWindow methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:44'! initialize super initialize. self changeTableLayout; listDirection: #topToBottom; hResizing: #shrinkWrap; vResizing: #shrinkWrap; rubberBandCells: true; setProperty: #indicateKeyboardFocus toValue: #never. self createTitle: 'Dialog'. self createBody. self setDefaultParameters. keyMap := Dictionary new. exclusive := true. autoCancel := false. preferredPosition := self currentWorld center.! ! !DockingBarMorph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:40'! delete self currentHand removeKeyboardListener: self. activeSubMenu ifNotNil: [ activeSubMenu delete]. ^ super delete! ! !EtoyDAVLoginMorph methodsFor: 'private' stamp: 'ct 9/11/2020 19:45'! loginAndDo: aBlock ifCanceled: cb "EtoyDAVLoginMorph loginAndDo:[:n :p | true] ifCanceled:[]" self name: '' actionBlock: aBlock cancelBlock: cb; fullBounds; position: Display extent - self extent // 2. self position: self position + (0 at 40). self currentWorld addMorphInLayer: self.! ! !EtoyDAVLoginMorph methodsFor: 'actions' stamp: 'ct 9/11/2020 19:45'! launchBrowser self currentWorld addMorph: self buildPanel centeredNear: Sensor cursorPoint. (Smalltalk classNamed: #ScratchPlugin) ifNotNil: [:sp | sp primOpenURL: self url].! ! !EventMorph methodsFor: 'drag and drop' stamp: 'ct 9/11/2020 19:45'! brownDragConcluded "After the user has manually repositioned the receiver via brown-halo-drag, this is invoked." self currentWorld abandonAllHalos. self eventRoll ifNotNil: [:evtRoll | evtRoll pushChangesBackToEventTheatre]! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! abandonReplayHandsAndHalos "Cleanup after playback." self currentWorld abandonReplayHandsAndHalosFor: eventRecorder! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! dismantlePaintBoxArtifacts "Cleanup after playback -- if a paint-box has been left up, take it down." (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/12/2020 14:28'! makeHorizontalRoll "Create a horizontal roll viewer for this recording space" state = #readyToRecord ifTrue: [ ^ self inform: 'Nothing recorded yet' translated]. "self convertToCanonicalForm." "Would prefer to do this but there are still issues." eventRoll ifNil: [ eventRoll := EventRollMorph new. eventRoll eventTheatre: self]. eventRoll formulate. eventRoll isInWorld ifFalse: [eventRoll openInWorld; setExtentFromHalo: (self currentWorld width - 10) @ eventRoll height; top: self bottom; bottom: (eventRoll bottom min: self currentWorld bottom); left: self currentWorld left + 2] "presumably zero" ifTrue: [eventRoll comeToFront].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! pausePlayback "Pause the playback. Sender responsible for setting state to #suspendedPlayback" eventRecorder pausePlayback. (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting. ^ self rewind]. self borderColor: Color orange. self setProperty: #suspendedContentArea toValue: contentArea veryDeepCopy. self populateControlsPanel! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! record "Commence event recording..." self currentWorld abandonAllHalos. self comeToFront. initialContentArea := contentArea veryDeepCopy. self forgetPriorPaintBoxSettings. initialPicture := contentArea imageForm. self state: #recording. self borderColor: Color red. self populateControlsPanel. self currentWorld doOneCycleNow. eventRecorder record! ! !EventRecordingSpace methodsFor: 'processing' stamp: 'ct 9/11/2020 19:45'! assureContentAreaStaysAt: aPoint "selbst-verst??ndlich" self currentWorld doOneCycleNow. self topLeft: ((self topLeft - contentArea topLeft ) + aPoint)! ! !EventRecordingSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:46'! initializeFromPlaybackButton: anEventPlaybackButton "Initialize my content area, caption, and tape from a playback button." | soundEvent | initialContentArea := anEventPlaybackButton contentArea veryDeepCopy. eventRecorder tape: anEventPlaybackButton tape veryDeepCopy. eventRecorder caption: anEventPlaybackButton caption. soundEvent := eventRecorder tape detect: [:evt | evt type = #startSound] ifNone: [nil]. soundEvent ifNotNil: "For benefit of possible re-record of voiceover" [eventRecorder startSoundEvent: soundEvent]. initialPicture := anEventPlaybackButton initialPicture veryDeepCopy ifNil: [self inform: 'caution - old playback; button lacks vital data.' translated. ^ nil]. finalPicture := anEventPlaybackButton finalPicture veryDeepCopy. eventRecorder saved: true. self rewind. self center: self currentWorld center.! ! !EventPlaybackSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:45'! launchFrom: aButton "Initialize the receiver from an invoker button, and launch it." | where | self setProperty: #originatingButton toValue: aButton. self contentArea: aButton contentArea veryDeepCopy tape: aButton tape veryDeepCopy. self captionString: aButton caption. self rewind. autoStart := aButton autoStart. autoDismiss := aButton autoDismiss. "showChrome := aButton showChrome." where := aButton whereToAppear. self openInWorld. where = #screenCenter ifTrue: [self center: self currentWorld center]. where = #buttonPosition ifTrue: [self position: aButton position]. where = #containerOrigin ifTrue: [self position: aButton owner position]. self goHome. self addStopper. autoStart ifTrue: [self play]! ! !FlapTab methodsFor: 'globalness' stamp: 'ct 9/11/2020 19:47'! toggleIsGlobalFlap "Toggle whether the receiver is currently a global flap or not" | oldWorld | self hideFlap. oldWorld := self currentWorld. self isGlobalFlap ifTrue: [Flaps removeFromGlobalFlapTabList: self. oldWorld addMorphFront: self] ifFalse: [self delete. Flaps addGlobalFlap: self. self currentWorld addGlobalFlaps]. self currentWorld reformulateUpdatingMenus.! ! !GoldBoxMenu methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:41'! initializeFor: aScriptor "Answer a graphical menu to be put up in conjunction with the Gold Box" | aButton goldBox aReceiver boxBounds example toScale | scriptor := aScriptor. lastItemMousedOver := nil. self removeAllMorphs. self setProperty: #goldBox toValue: true. self listDirection: #topToBottom; hResizing: #spaceFill; extent: 1 at 1; vResizing: #spaceFill. "standard #newColumn stuff" self setNameTo: 'Gold Box' translated. self useRoundedCorners. self color: Color white. self borderColor: (Color r: 1.0 g: 0.839 b: 0.065). self hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4. { {ScriptingSystem. #yesNoComplexOfTiles. 'test' translated. 'Test/Yes/No panes for testing a condition.' translated}. {ScriptingSystem. #timesRepeatComplexOfTiles. 'repeat' translated. 'TimesRepeat panes for running a section of code repeatedly.' translated}. { ScriptingSystem. #randomNumberTile. 'random' translated. 'A tile that will produce a random number in a given range.' translated}. { ScriptingSystem. #seminalFunctionTile. 'function' translated. 'A tile representing a function call. Click on the function name or the arrows to change functions.' translated}. {ScriptingSystem. #buttonUpTile. 'button up?' translated. 'Reports whether the mouse button is up' translated}. {ScriptingSystem. #buttonDownTile. 'button down?' translated. 'Reports whether the mouse button is down' translated}. {ScriptingSystem. #randomColorTile. 'random color' translated. 'A tile returning a random color' translated}. {scriptor playerScripted. #tileToRefer. 'tile for me' translated. 'A tile representing the object being scripted' translated}. {self. #numericConstantTile. 'number' translated. 'A tile holding a plain number' translated}. } do: [:tuple | aReceiver := tuple first. example := aReceiver perform: tuple second. aButton := IconicButton new target: aReceiver. aButton borderWidth: 0; color: Color transparent. toScale := tuple size >= 5 ifTrue: [tuple first perform: tuple fifth] "bail-out for intractable images." ifFalse: [example imageForm]. aButton labelGraphic: (toScale copy scaledToHeight: 40). aButton actionSelector: #launchPartOffsetVia:label:. aButton arguments: {tuple second. tuple third}. (tuple size > 3 and: [tuple fourth isEmptyOrNil not]) ifTrue: [aButton setBalloonText: tuple fourth]. aButton actWhen: #buttonDown. aButton on: #mouseEnter send: #mousedOverEvent:button: to: self. aButton on: #click send: #delete to: self. self addMorphBack: aButton]. goldBox := aScriptor submorphs first submorphThat: [:m | (m isKindOf: SimpleButtonMorph) and: [m actionSelector == #offerGoldBoxMenu]] ifNone: [nil]. goldBox ifNil: [self position: self currentHand position] ifNotNil: [boxBounds := goldBox boundsInWorld. self center: boxBounds center. self left: (boxBounds center x - (self width // 2)). self top: boxBounds bottom]. lastItemMousedOver := nil. self on: #mouseLeave send: #mouseLeftMenuWithEvent: to: self. self on: #mouseLeaveDragging send: #delete to: self.! ! !GrabPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:41'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair.! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! doDirection: anEvent with: directionHandle "The mouse went down on the forward-direction halo handle; respond appropriately." anEvent hand obtainHalo: self. anEvent shiftPressed ifTrue: [directionArrowAnchor := (target point: target referencePosition in: self world) rounded. self positionDirectionShaft: directionHandle. self removeAllHandlesBut: directionHandle. directionHandle setProperty: #trackDirectionArrow toValue: true] ifFalse: [self currentHand spawnBalloonFor: directionHandle]! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:10'! maybeDismiss: evt with: dismissHandle "Ask hand to dismiss my target if mouse comes up in it." evt hand obtainHalo: self. (dismissHandle containsPoint: evt cursorPoint) ifFalse: [ self delete. target addHalo: evt] ifTrue: [ target resistsRemoval ifTrue: [(UIManager default chooseFrom: { 'Yes' translated. 'Um, no, let me reconsider' translated. } title: 'Really throw this away?' translated) = 1 ifFalse: [^ self]]. evt hand removeHalo. self delete. target dismissViaHalo. self currentWorld presenter flushPlayerListCache].! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! prepareToTrackCenterOfRotation: evt with: rotationHandle "The mouse went down on the center of rotation." evt hand obtainHalo: self. evt shiftPressed ifTrue: [self removeAllHandlesBut: rotationHandle. rotationHandle setProperty: #trackCenterOfRotation toValue: true. evt hand showTemporaryCursor: Cursor blank] ifFalse: [self currentHand spawnBalloonFor: rotationHandle]! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:30'! cursorPoint "Implemented for allowing embedded worlds in an event cycle to query a hand's position and get it in its coordinates. The same can be achieved by #point:from: but this is simply much more convenient since it will look as if the hand is in the lower world." | pos world | pos := self position. world := self activeWorld. (world isNil or: [world == owner]) ifTrue: [^pos]. ^world point: pos from: owner! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:59'! processEvents "Process user input events from the local input devices." | evt evtBuf type hadAny | self activeEvent ifNotNil: [ "Meaning that we were invoked from within an event response. Make sure z-order is up to date." self mouseOverHandler processMouseOver: lastMouseEvent]. hadAny := false. [(evtBuf := Sensor nextEvent) isNil] whileFalse: [evt := nil. "for unknown event types" type := evtBuf first. type = EventTypeMouse ifTrue: [evt := self generateMouseEvent: evtBuf]. type = EventTypeMouseWheel ifTrue: [evt := self generateMouseWheelEvent: evtBuf]. type = EventTypeKeyboard ifTrue: [evt := self generateKeyboardEvent: evtBuf]. type = EventTypeDragDropFiles ifTrue: [evt := self generateDropFilesEvent: evtBuf]. type = EventTypeWindow ifTrue:[evt := self generateWindowEvent: evtBuf]. "All other events are ignored" (type ~= EventTypeDragDropFiles and: [evt isNil]) ifTrue: [^self]. evt ifNotNil: ["Finally, handle it." self handleEvent: evt. hadAny := true. "For better user feedback, return immediately after a mouse event has been processed." evt isMouse ifTrue: [^ self]]]. "note: if we come here we didn't have any mouse events" mouseClickState ifNotNil: [ "No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: [ "No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent].! ! !HandMorph methodsFor: 'initialization' stamp: 'ct 9/12/2020 15:33'! becomeActiveDuring: aBlock "Make the receiver the activeHand during the evaluation of aBlock." | priorHand | priorHand := self activeHand. self activeHand: self. ^ aBlock ensure: [ "check to support project switching." self activeHand == self ifTrue: [self activeHand: priorHand]].! ! !HandMorphForReplay methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:30'! processEvents "Play back the next event" | evt hadMouse hadAny tracker | suspended == true ifTrue: [^ self]. hadMouse := hadAny := false. tracker := recorder objectTrackingEvents. [(evt := recorder nextEventToPlay) isNil] whileFalse: [ ((evt isMemberOf: MouseMoveEvent) and: [evt trail isNil]) ifTrue: [^ self]. tracker ifNotNil: [tracker currentEventTimeStamp: evt timeStamp]. evt type == #EOF ifTrue: [recorder pauseIn: self currentWorld. ^ self]. evt type == #startSound ifTrue: [recorder perhapsPlaySound: evt argument. recorder synchronize. ^ self]. evt type == #startEventPlayback ifTrue: [evt argument launchPlayback. recorder synchronize. ^ self]. evt type == #noteTheatreBounds ifTrue: ["The argument holds the content rect --for now we don't make any use of that info in this form." ^ self]. evt isMouse ifTrue: [hadMouse := true]. (evt isMouse or: [evt isKeyboard]) ifTrue: [self handleEvent: (evt setHand: self) resetHandlerFields. hadAny := true]]. (mouseClickState notNil and: [hadMouse not]) ifTrue: ["No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: ["No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent]! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:12'! destroyThread "Manually destroy the thread" (self confirm: ('Destroy thread <{1}> ?' translated format:{threadName})) ifFalse: [^ self]. self class knownThreads removeKey: threadName ifAbsent: []. self setProperty: #moribund toValue: true. "In case pointed to in some other project" self currentWorld keyboardNavigationHandler == self ifTrue: [self stopKeyboardNavigation]. self delete.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:13'! moreCommands "Put up a menu of options" | allThreads aMenu others target | allThreads := self class knownThreads. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'navigation' translated. Preferences noviceMode ifFalse:[ self flag: #deferred. "Probably don't want that stay-up item, not least because the navigation-keystroke stuff is not dynamically handled" aMenu addStayUpItem ]. others := (allThreads keys reject: [ :each | each = threadName]) asArray sort. others do: [ :each | aMenu add: ('switch to <{1}>' translated format:{each}) selector: #switchToThread: argument: each ]. aMenu addList: { {'switch to recent projects' translated. #getRecentThread}. #-. {'create a new thread' translated. #threadOfNoProjects}. {'edit this thread' translated. #editThisThread}. {'create thread of all projects' translated. #threadOfAllProjects}. #-. {'First project in thread' translated. #firstPage}. {'Last project in thread' translated. #lastPage} }. (target := self currentIndex + 2) > listOfPages size ifFalse: [ aMenu add: ('skip over next project ({1})' translated format:{(listOfPages at: target - 1) first}) action: #skipOverNext ]. aMenu addList: { {'jump within this thread' translated. #jumpWithinThread}. {'insert new project' translated. #insertNewProject}. #-. {'simply close this navigator' translated. #delete}. {'destroy this thread' translated. #destroyThread}. #- }. (self currentWorld keyboardNavigationHandler == self) ifFalse:[ aMenu add: 'start keyboard navigation with this thread' translated action: #startKeyboardNavigation ] ifTrue: [ aMenu add: 'stop keyboard navigation with this thread' translated action: #stopKeyboardNavigation ]. aMenu popUpInWorld.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! positionAppropriately | others world otherRects overlaps bottomRight | (self ownerThatIsA: HandMorph) ifNotNil: [^self]. others := (world := Project currentWorld) submorphs select: [ :each | each ~~ self and: [each isKindOf: self class]]. otherRects := others collect: [ :each | each bounds]. bottomRight := (world hasProperty: #threadNavigatorPosition) ifTrue: [world valueOfProperty: #threadNavigatorPosition] ifFalse: [world bottomRight]. self align: self fullBounds bottomRight with: bottomRight. self setProperty: #previousWorldBounds toValue: self world bounds. [ overlaps := false. otherRects do: [ :r | (r intersects: bounds) ifTrue: [overlaps := true. self bottom: r top]. ]. self top < self world top ifTrue: [ self bottom: bottomRight y. self right: self left - 1. ]. overlaps ] whileTrue.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! startKeyboardNavigation "Tell the active world to starting navigating via desktop keyboard navigation via me" self currentWorld keyboardNavigationHandler: self! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:15'! stopKeyboardNavigation "Cease navigating via the receiver in response to desktop keystrokes" self currentWorld removeProperty: #keyboardNavigationHandler! ! !InternalThreadNavigationMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:13'! loadPageWithProgress "Load the desired page, showing a progress indicator as we go" | projectInfo projectName beSpaceHandler | projectInfo := listOfPages at: currentIndex. projectName := projectInfo first. loadedProject := Project named: projectName. self class know: listOfPages as: threadName. beSpaceHandler := (Project current world keyboardNavigationHandler == self). self currentWorld addDeferredUIMessage: [InternalThreadNavigationMorph openThreadNamed: threadName atIndex: currentIndex beKeyboardHandler: beSpaceHandler]. loadedProject ifNil: [ ComplexProgressIndicator new targetMorph: self; historyCategory: 'project loading' translated; withProgressDo: [ [ loadedProject := Project current fromMyServerLoad: projectName ] on: ProjectViewOpenNotification do: [ :ex | ex resume: false] "we probably don't want a project view morph in this case" ]. ]. loadedProject ifNil: [ ^self inform: 'I cannot find that project' translated ]. self delete. loadedProject enter.! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! resetBottomRightPosition self currentWorld removeProperty: #threadNavigatorPosition. ! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! setBottomRightPosition self currentWorld setProperty: #threadNavigatorPosition toValue: self bottomRight. ! ! !LassoPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:42'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! play "Play the movie, as it were." tape ifNil: [^ self]. tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! record "Commence recording or re-recording." tapeStream := WriteStream on: (Array new: 10000). self resumeRecordIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! resumePlayingWithoutPassingStop "Like play, but avoids the stop step that does more than we'd like." tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/12/2020 14:24'! stop "Stop recording or playing." tapeStream ifNotNil: [(#(recording recordingWithSound) includes: self state) ifTrue: [tape := tapeStream contents. saved := false]]. self terminateVoiceRecording. "In case doing" journalFile ifNotNil: [journalFile close]. self pauseIn: self currentWorld. tapeStream := nil. self state: #atEndOfPlayback. recordingSpace abandonReplayHandsAndHalos. recordMeter ifNotNil: [recordMeter width: 1].! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:43'! popUpEvent: evt in: aWorld "Present this menu in response to the given event." | aHand aPosition | aHand := evt ifNotNil: [evt hand] ifNil: [self currentHand]. aPosition := aHand position truncated. ^ self popUpAt: aPosition forHand: aHand in: aWorld! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:23'! popUpNoKeyboard "Present this menu in the current World, *not* allowing keyboard input into the menu" ^ self popUpAt: self currentHand position forHand: self currentHand in: self currentWorld allowKeyboard: false! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title world | title := self allMorphs detect: [ :ea | ea hasProperty: #titleString ]. title := title submorphs first. self visible: false. world := self currentWorld. aBlock value: [:string| self visible ifFalse:[ world addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: self currentHand cursorPoint hangOut: false. self changed. world displayWorld "show myself"]. self delete. world displayWorld.! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! invokeModal: allowKeyboardControl "Invoke this menu and don't return until the user has chosen a value. If the allowKeyboarControl boolean is true, permit keyboard control of the menu" ^ self invokeModalAt: self currentHand position in: self currentWorld allowKeyboard: allowKeyboardControl! ! !MenuMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:43'! positionAt: aPoint relativeTo: aMenuItem inWorld: aWorld "Note: items may not be laid out yet (I found them all to be at 0 at 0), so we have to add up heights of items above the selected item." | i yOffset sub delta | self fullBounds. "force layout" i := 0. yOffset := 0. [(sub := self submorphs at: (i := i + 1)) == aMenuItem] whileFalse: [yOffset := yOffset + sub height]. self position: aPoint - (2 @ (yOffset + 8)). "If it doesn't fit, show it to the left, not to the right of the hand." self right > aWorld worldBounds right ifTrue: [self right: aPoint x + 1]. "Make sure that the menu fits in the world." delta := self bounds amountToTranslateWithin: (aWorld worldBounds withHeight: ((aWorld worldBounds height - 18) max: (self currentHand position y) + 1)). delta isZero ifFalse: [self position: self position + delta].! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:17'! displayAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." Smalltalk isMorphic ifFalse: [^ self]. [self currentWorld addMorph: self centeredNear: aPoint. self world displayWorld. "show myself" aBlock value] ensure: [self delete]! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:18'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title w | Smalltalk isMorphic ifFalse: [^ self]. title := self allMorphs detect: [:ea | ea hasProperty: #titleString]. title := title submorphs first. self visible: false. w := self currentWorld. aBlock value: [:string| self visible ifFalse: [ w addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: Sensor cursorPoint hangOut: false. self changed. w displayWorld "show myself" ]. self delete. w displayWorld.! ! !Morph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:18'! fromFileName: fullName "Reconstitute a Morph from the file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | aFileStream morphOrList | aFileStream := (MultiByteBinaryOrTextStream with: ((FileStream readOnlyFileNamed: fullName) binary contentsOfEntireFile)) binary reset. morphOrList := aFileStream fileInObjectAndCode. (morphOrList isKindOf: SqueakPage) ifTrue: [morphOrList := morphOrList contentsMorph]. Smalltalk isMorphic ifTrue: [Project current world addMorphsAndModel: morphOrList] ifFalse: [morphOrList isMorph ifFalse: [self inform: 'Can only load a single morph into an mvc project via this mechanism.' translated]. morphOrList openInWorld]! ! !AllPlayersTool class methodsFor: '*Etoys-Squeakland-parts bin' stamp: 'ct 9/12/2020 14:26'! allPlayersToolForActiveWorld "Launch an AllPlayersTool to view the scripted objects of the active world" | aTool | aTool := self newStandAlone. aTool center: self currentWorld center. ^ aTool " AllPlayersTool allPlayersToolForActiveWorld "! ! !AllScriptsTool class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:26'! allScriptsToolForActiveWorld "Launch an AllScriptsTool to view scripts of the active world" | aTool | aTool := self newColumn. aTool initializeFor: self currentWorld presenter. ^ aTool! ! !AnonymousSoundMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:26'! fromFileName: fullName "Create an instance of the receiver from the given file path." | newPlayer aSound ext aName | newPlayer := self new initialize. ('*aif*' match: fullName) ifTrue: [aSound := SampledSound fromAIFFfileNamed: fullName]. ('*wav' match: fullName) ifTrue: [aSound := SampledSound fromWaveFileNamed: fullName]. newPlayer := self new. ext := FileDirectory extensionFor: fullName. aName := (FileDirectory on: fullName) pathParts last. ext size > 0 ifTrue: [aName := aName copyFrom: 1 to: (aName size - (ext size + 1))]. newPlayer sound: aSound interimName: aName. newPlayer openInWorld; position: self currentWorld center.! ! !BookMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:27'! openFromFile: fullName "Reconstitute a Morph from the selected file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | book aFileStream | Smalltalk verifyMorphicAvailability ifFalse: [^ self]. aFileStream := FileStream readOnlyFileNamed: fullName. book := BookMorph new. book setProperty: #url toValue: aFileStream url. book fromRemoteStream: aFileStream. aFileStream close. Smalltalk isMorphic ifTrue: [self currentWorld addMorphsAndModel: book] ifFalse: [book isMorph ifFalse: [^self inform: 'Can only load a single morph\into an mvc project via this mechanism.' withCRs translated]. book openInWorld]. book goToPage: 1! ! !EventRecordingSpace class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:46'! openFromPlaybackButton: aButton "Open an EventRecordingSpace derived from a playback button. The primary reason for doing this would be to re-record voiceover." | aSpace | aSpace := EventRecordingSpace new. aSpace initializeFromPlaybackButton: aButton. aSpace center: self currentWorld center. aSpace openInWorld! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer centerAt: aPoint "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels. This variant is only for calling from within a Morphic project." "FillInTheBlankMorph request: 'Type something, then type CR.' initialAnswer: 'yo ho ho!!' centerAt: Display center" ^ self request: queryString initialAnswer: defaultAnswer centerAt: aPoint inWorld: self currentWorld! ! !FillInTheBlankMorph class methodsFor: '*Etoys-Squeakland-instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: self currentHand cursorPoint inWorld: self currentWorld onCancelReturn: cancelResponse! ! !HandMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:11'! showEvents: aBool "HandMorph showEvents: true" "HandMorph showEvents: false" ShowEvents := aBool. aBool ifFalse: [ Project current world invalidRect: (0 at 0 extent: 250 at 120)].! ! !InternalThreadNavigationMorph class methodsFor: 'known threads' stamp: 'ct 9/11/2020 20:15'! openThreadNamed: nameOfThread atIndex: anInteger beKeyboardHandler: aBoolean "Activate the thread of the given name, from the given index; set it up to be navigated via desktop keys if indicated" | coll nav | coll := self knownThreads at: nameOfThread ifAbsent: [^self]. nav := Project current world submorphThat: [ :each | (each isKindOf: self) and: [each threadName = nameOfThread]] ifNone: [nav := self basicNew. nav listOfPages: coll; threadName: nameOfThread index: anInteger; initialize; openInWorld; positionAppropriately. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav]. ^ self]. nav listOfPages: coll; threadName: nameOfThread index: anInteger; removeAllMorphs; addButtons. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav].! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! chooseFrom: aList lines: linesArray title: queryString "Choose an item from the given list. Answer the index of the selected item." | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString. 1 to: aList size do: [:i| menu add: (aList at: i) asString target: aBlock selector: #value: argument: i. (linesArray includes: i) ifTrue:[menu addLine]]. MenuIcons decorateMenu: menu. result := 0. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! confirm: queryString trueChoice: trueChoice falseChoice: falseChoice "Put up a yes/no menu with caption queryString. The actual wording for the two choices will be as provided in the trueChoice and falseChoice parameters. Answer true if the response is the true-choice, false if it's the false-choice. This is a modal question -- the user must respond one way or the other." "MenuMorph confirm: 'Are you hungry?' trueChoice: 'yes, I''m famished' falseChoice: 'no, I just ate'" | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: trueChoice target: aBlock selector: #value: argument: true. menu add: falseChoice target: aBlock selector: #value: argument: false. MenuIcons decorateMenu: menu. [menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. result == nil] whileTrue. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:22'! inform: queryString "MenuMorph inform: 'I like Squeak'" | menu | menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: 'OK' translated target: self selector: #yourself. MenuIcons decorateMenu: menu. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true.! ! !MorphHierarchy class methodsFor: 'opening' stamp: 'ct 9/12/2020 14:45'! openOrDelete | oldMorph | oldMorph := Project current world submorphs detect: [:each | each hasProperty: #morphHierarchy] ifNone: [| newMorph | newMorph := self new asMorph. newMorph bottomLeft: self currentHand position. newMorph openInWorld. newMorph isFullOnScreen ifFalse: [newMorph goHome]. ^ self]. "" oldMorph delete! ! !MorphWorldController methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 15:15'! controlTerminate "This window is becoming inactive; restore the normal cursor." Cursor normal show. self activeWorld: nil; activeHand: nil; activeEvent: nil.! ! !MorphicEvent methodsFor: 'initialize' stamp: 'ct 9/12/2020 15:22'! becomeActiveDuring: aBlock "Make the receiver the activeEvent during the evaluation of aBlock." | priorEvent | priorEvent := self activeEvent. self activeEvent: self. ^ aBlock ensure: [ "check to support project switching." self activeEvent == self ifTrue: [self activeEvent: priorEvent]]! ! !MultiWindowLabelButtonMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:16'! performAction "Override to interpret the actionSelector as a menu accessor and to activate that menu." actionSelector ifNil: [^ self]- (model perform: actionSelector) ifNotNil: [:menu | menu invokeModalAt: self position - (0 at 5) in: self currentWorld allowKeyboard: Preferences menuKeyboardControl].! ! !NativeImageSegment methodsFor: 'read/write segment' stamp: 'ct 9/12/2020 14:15'! smartFillRoots: dummy | refs known ours ww blockers | "Put all traced objects into my arrayOfRoots. Remove some that want to be in outPointers. Return blockers, an IdentityDictionary of objects to replace in outPointers." blockers := dummy blockers. known := (refs := dummy references) size. refs keys do: [:obj | "copy keys to be OK with removing items" (obj isSymbol) ifTrue: [refs removeKey: obj. known := known-1]. (obj class == PasteUpMorph) ifTrue: [ obj isWorldMorph & (obj owner == nil) ifTrue: [ (dummy project ~~ nil and: [obj == dummy project world]) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: 'The worldMorph of a different world')]]]. "Make a ProjectViewMorph here" "obj class == Project ifTrue: [Transcript show: obj; cr]." (blockers includesKey: obj) ifTrue: [ refs removeKey: obj ifAbsent: [known := known+1]. known := known-1]. ]. ours := (dummy project ifNil: [Project current]) world. refs keysDo: [:obj | obj isMorph ifTrue: [ ww := obj world. (ww == ours) | (ww == nil) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: obj printString, ' from another world')]]]. "keep original roots on the front of the list" dummy rootObject do: [:rr | refs removeKey: rr ifAbsent: []]. (self respondsTo: #classOrganizersBeRoots:) ifTrue: "an EToys extension" [self classOrganizersBeRoots: dummy]. ^dummy rootObject, refs keys asArray! ! !NebraskaSenderMorph methodsFor: 'parts bin' stamp: 'ct 9/12/2020 14:14'! initializeToStandAlone super initializeToStandAlone. self installModelIn: Project current world.! ! !NebraskaServerMorph class methodsFor: 'as yet unclassified' stamp: 'ct 9/12/2020 14:14'! serveWorld ^ self serveWorld: self currentWorld ! ! !NewVariableDialogMorph methodsFor: 'build' stamp: 'ct 9/12/2020 14:14'! rebuild | buttonColor itsName enableDecimalPlaces | self removeAllMorphs. self addAColumn: { self lockedString: self title. }. self addSeparator. self addARow: { self inAColumn: { (self addARow: { self lockedString: 'Name:' translated. self spacer. varNameText := self newTextMorph contentsWrapped: self varName; selectAll; crAction: (MessageSend receiver: self selector: #doAccept); yourself }) cellPositioning: #center. self inAColumn: { (self addARow: { self lockedString: 'Type:' translated. self spacer. varTypeButton := self buildVarTypeButton }) cellPositioning: #center. } named: #varType. } }. self currentHand newKeyboardFocus: varNameText. self addSeparator. self addDecimalPlaces. enableDecimalPlaces := false. (#(#Number #Point) includes: self varType) ifTrue: [ enableDecimalPlaces := true]. self allMorphsDo: [ :each | itsName := each knownName. (#(decimalPlaces) includes: itsName) ifTrue: [self enable: each when: enableDecimalPlaces]]. buttonColor := self color lighter. self addARow: { self inAColumn: { (self addARow: { self buttonNamed: 'Accept' translated action: #doAccept color: buttonColor help: 'keep changes made and close panel' translated. self buttonNamed: 'Cancel' translated action: #doCancel color: buttonColor help: 'cancel changes made and close panel' translated. }) listCentering: #center } }! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! step "Let all views know that some of my objects need to be updated." self monitorList do: [ :object | object ifNotNil: [self changed: #objectChanged with: object]]. self monitorList ifEmpty: [ self world stopStepping: self selector: #step ].! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! world ^ Project current world! ! !ObjectExplorer methodsFor: 'accessing - view' stamp: 'ct 9/12/2020 14:12'! views ^ self findDeepSubmorphsIn: self world that: [:morph | morph modelOrNil = self]! ! !ObjectsTool methodsFor: 'search' stamp: 'ct 9/12/2020 14:45'! showSearchPane "Set the receiver up so that it shows the search pane" | tabsPane aPane | modeSymbol == #search ifTrue: [ ^self ]. self partsBin removeAllMorphs. tabsPane := self tabsPane. aPane := self newSearchPane. self replaceSubmorph: tabsPane by: aPane. self modeSymbol: #search. self showMorphsMatchingSearchString. self currentHand newKeyboardFocus: aPane! ! !ParagraphEditor methodsFor: 'nonediting/nontyping keys' stamp: 'ct 9/12/2020 14:12'! escapeToDesktop: characterStream "Pop up a morph to field keyboard input in the context of the desktop" Smalltalk isMorphic ifTrue: [ Project current world putUpWorldMenuFromEscapeKey]. ^ true! ! !ParagraphEditor methodsFor: '*Etoys-Squeakland-editing keys' stamp: 'ct 9/12/2020 14:07'! shiftEnclose: characterStream "Insert or remove bracket characters around the current selection. Flushes typeahead." | char left right startIndex stopIndex oldSelection which text | char := sensor keyboard. char = $9 ifTrue: [ char := $( ]. char = $, ifTrue: "[ char := $< ]" [self closeTypeIn. Project current world showSourceKeyHit. ^ true]. char = $[ ifTrue: [ char := ${ ]. char = $' ifTrue: [ char := $" ]. char asciiValue = 27 ifTrue: [ char := ${ ]. "ctrl-[" self closeTypeIn. startIndex := self startIndex. stopIndex := self stopIndex. oldSelection := self selection. which := '([<{"''' indexOf: char ifAbsent: [1]. left := '([<{"''' at: which. right := ')]>}"''' at: which. text := paragraph text. ((startIndex > 1 and: [stopIndex <= text size]) and: [(text at: startIndex-1) = left and: [(text at: stopIndex) = right]]) ifTrue: ["already enclosed; strip off brackets" self selectFrom: startIndex-1 to: stopIndex. self replaceSelectionWith: oldSelection] ifFalse: ["not enclosed; enclose by matching brackets" self replaceSelectionWith: (Text string: (String with: left), oldSelection string ,(String with: right) emphasis: emphasisHere). self selectFrom: startIndex+1 to: stopIndex]. ^true! ! !PasteUpMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:10'! flapTab "Answer the tab affilitated with the receiver. Normally every flap tab is expected to have a PasteUpMorph which serves as its 'referent.'" | ww | self isFlap ifFalse: [^ nil]. ww := self presenter associatedMorph ifNil: [self]. ^ ww flapTabs detect: [:any| any referent == self] ifNone: [nil]! ! !PasteUpMorph methodsFor: 'events-processing' stamp: 'ct 9/12/2020 15:11'! processEvent: anEvent using: defaultDispatcher "Reimplemented to install the receiver as the new ActiveWorld if it is one" self isWorldMorph ifFalse: [ ^ super processEvent: anEvent using: defaultDispatcher]. ^ self activateWorld: self during: [ super processEvent: anEvent using: defaultDispatcher]! ! !PasteUpMorph methodsFor: 'flaps' stamp: 'ct 9/12/2020 14:11'! correspondingFlapTab "If there is a flap tab whose referent is me, return it, else return nil. Will also work for flaps on the edge of embedded subareas such as within scripting-areas, but more slowly." self currentWorld flapTabs do: [:aTab | aTab referent == self ifTrue: [^ aTab]]. "Catch guys in embedded worldlets" self currentWorld allMorphs do: [:aTab | ((aTab isKindOf: FlapTab) and: [aTab referent == self]) ifTrue: [^ aTab]]. ^ nil! ! !PasteUpMorph methodsFor: 'initialization' stamp: 'ct 9/12/2020 15:33'! becomeActiveDuring: aBlock "Make the receiver the activeWorld during the evaluation of aBlock." | priorWorld | priorWorld := self activeWorld. self activeWorld: self. ^ aBlock ensure: [ "check to support project switching." self activeWorld == self ifTrue: [self activeWorld: priorWorld]]! ! !PasteUpMorph methodsFor: 'menu & halo' stamp: 'ct 9/12/2020 14:07'! putUpPenTrailsSubmenu "Put up the pen trails menu" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'pen trails' translated. aMenu addStayUpItem. self addPenTrailsMenuItemsTo: aMenu. ^ aMenu popUpInWorld: self! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/12/2020 14:45'! extractScreenRegion: poly andPutSketchInHand: hand "The user has specified a polygonal area of the Display. Now capture the pixels from that region, and put in the hand as a Sketch." | screenForm outline topLeft innerForm exterior | outline := poly shadowForm. topLeft := outline offset. exterior := (outline offset: 0 at 0) anyShapeFill reverse. screenForm := Form fromDisplay: (topLeft extent: outline extent). screenForm eraseShape: exterior. innerForm := screenForm trimBordersOfColor: Color transparent. self currentHand showTemporaryCursor: nil. innerForm isAllWhite ifFalse: [hand attachMorph: (self drawingClass withForm: innerForm)]! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 20:07'! initializeDesktopCommandKeySelectors "Provide the starting settings for desktop command key selectors. Answer the dictionary." "ActiveWorld initializeDesktopCommandKeySelectors" | dict | dict := IdentityDictionary new. self defaultDesktopCommandKeyTriplets do: [:trip | | messageSend | messageSend := MessageSend receiver: trip second selector: trip third. dict at: trip first put: messageSend]. self setProperty: #commandKeySelectors toValue: dict. ^ dict! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 18:00'! putUpWorldMenuFromEscapeKey Preferences noviceMode ifFalse: [self putUpWorldMenu: self currentEvent]! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/12/2020 15:11'! install owner := nil. "since we may have been inside another world previously" self activeWorld: self. self activeHand: self hands first. "default" self activeEvent: nil. submorphs do: [:ss | ss owner isNil ifTrue: [ss privateOwner: self]]. "Transcript that was in outPointers and then got deleted." self viewBox: Display boundingBox. EventSensor default flushEvents. worldState handsDo: [:h | h initForEvents]. self installFlaps. self borderWidth: 0. "default" (Preferences showSecurityStatus and: [SecurityManager default isInRestrictedMode]) ifTrue: [self borderWidth: 2; borderColor: Color red]. self presenter allExtantPlayers do: [:player | player prepareToBeRunning]. SystemWindow noteTopWindowIn: self.! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/12/2020 14:06'! repositionFlapsAfterScreenSizeChange "Reposition flaps after screen size change" (Flaps globalFlapTabsIfAny, self localFlapTabs) do: [:aFlapTab | aFlapTab applyEdgeFractionWithin: self bounds]. Flaps doAutomaticLayoutOfFlapsIfAppropriate! ! !PasteUpMorph methodsFor: '*Tools' stamp: 'ct 9/11/2020 20:07'! defaultDesktopCommandKeyTriplets "Answer a list of triplets of the form [+ optional fourth element, a for use in desktop-command-key-help] that will provide the default desktop command key handlers. If the selector takes an argument, that argument will be the command-key event" "World initializeDesktopCommandKeySelectors" | noviceKeys expertKeys | noviceKeys := { {$o. self. #activateObjectsTool. 'Activate the "Objects Tool"' translated}. {$r. self. #restoreMorphicDisplay. 'Redraw the screen' translated}. {$z. self. #undoOrRedoCommand. 'Undo or redo the last undoable command' translated}. {$F. Project current. #toggleFlapsSuppressed. 'Toggle the display of flaps' translated}. {$N. self. #toggleClassicNavigatorIfAppropriate. 'Show/Hide the classic Navigator, if appropriate' translated}. {$M. self. #toggleShowWorldMainDockingBar. 'Show/Hide the Main Docking Bar' translated}. {$]. Smalltalk. #saveSession. 'Save the image.' translated}. }. Preferences noviceMode ifTrue: [^ noviceKeys]. expertKeys := { {$b. SystemBrowser. #defaultOpenBrowser. 'Open a new System Browser' translated}. {$k. Workspace. #open. 'Open a new Workspace' translated}. {$m. self. #putUpNewMorphMenu. 'Put up the "New Morph" menu' translated}. {$O. self. #findAMonticelloBrowser. 'Bring a Monticello window into focus.' translated}. {$t. self. #findATranscript:. 'Make a System Transcript visible' translated}. {$w. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {Character escape. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {$C. self. #findAChangeSorter:. 'Make a Change Sorter visible' translated}. {$L. self. #findAFileList:. 'Make a File List visible' translated}. {$P. self. #findAPreferencesPanel:. 'Activate the Preferences tool' translated}. {$R. Utilities. #browseRecentSubmissions. 'Make a Recent Submissions browser visible' translated}. {$W. self. #findAMessageNamesWindow:. 'Make a MessageNames tool visible' translated}. {$Z. ChangeList. #browseRecentLog. 'Browse recently-logged changes' translated}. {$\. SystemWindow. #sendTopWindowToBack. 'Send the top window to the back' translated}. {$_. Smalltalk. #quitPrimitive. 'Quit the image immediately.' translated}. {$-. Preferences. #decreaseFontSize. 'Decrease all font sizes' translated}. {$+. Preferences. #increaseFontSize. 'Increase all font sizes' translated}. }. ^ noviceKeys, expertKeys! ! !PasteUpMorph methodsFor: '*Etoys-playfield' stamp: 'ct 9/12/2020 14:10'! galleryOfPlayers "Put up a tool showing all the players in the project" (Project current world findA: AllPlayersTool) ifNotNil: [:aTool | ^ aTool comeToFront]. AllPlayersTool newStandAlone openInHand "ActiveWorld galleryOfPlayers"! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:36'! attemptCleanupReporting: whetherToReport "Try to fix up some bad things that are known to occur in some etoy projects we've seen. If the whetherToReport parameter is true, an informer is presented after the cleanups" | fixes faultyStatusControls | fixes := 0. self world ifNotNil: [:world | world submorphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m submorphs isEmpty]] thenDo: [:m | m delete. fixes := fixes + 1]]. TransformationMorph allSubInstancesDo: [:m | (m player notNil and: [m renderedMorph ~~ m]) ifTrue: [m renderedMorph visible ifFalse: [m renderedMorph visible: true. fixes := fixes + 1]]]. (Player class allSubInstances select: [:cl | cl isUniClass and: [cl instanceCount > 0]]) do: [:aUniclass | fixes := fixes + aUniclass cleanseScripts]. self presenter flushPlayerListCache; allExtantPlayers. faultyStatusControls := ScriptStatusControl allInstances select: [:m |m fixUpScriptInstantiation]. fixes := fixes + faultyStatusControls size. ScriptNameTile allInstancesDo: [:aTile | aTile submorphs isEmpty ifTrue: [aTile setLiteral: aTile literal. fixes := fixes + 1]]. whetherToReport ifTrue: [self inform: ('{1} [or more] repair(s) made' translated format: {fixes printString})] ifFalse: [fixes > 0 ifTrue: [Transcript cr; show: fixes printString, ' repairs made to existing content.']] " ActiveWorld attemptCleanupReporting: true. ActiveWorld attemptCleanupReporting: false. "! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:09'! hideAllPlayers "Remove all Viewers belonging to scripted players associated with the receiver or any of its subjects from the screen." | a | a := OrderedCollection new. self allMorphsDo: [ :x | (self presenter currentlyViewing: x player) ifTrue: [a add: x player viewerFlapTab]]. a do: [ :each | each dismissViaHalo].! ! !PasteUpMorph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:08'! modernizeBJProject "Prepare a kids' project from the BJ fork of September 2000 -- a once-off thing for converting such projects forward to a modern 3.1a image, in July 2001. Except for the #enableOnlyGlobalFlapsWithIDs: call, this could conceivably be called upon reloading *any* project, just for safety." "ActiveWorld modernizeBJProject" self flag: #deprecate "ct: No senders". ScriptEditorMorph allInstancesDo: [:m | m userScriptObject]. Flaps enableOnlyGlobalFlapsWithIDs: {'Supplies' translated}. self abandonOldReferenceScheme. self relaunchAllViewers.! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:11'! abandonUnsituatedPlayers "If any objects in the project have references, in player-valued variables, to other objects otherwise not present in the project, abandon them and replace former references to them by references to Dot" | aList dot slotInfo varName ref allPlayers count | count := 0. allPlayers := self presenter reallyAllExtantPlayersNoSort. aList := allPlayers select: [:m | m belongsToUniClass]. dot := self presenter standardPlayer. aList do: [:p | p class slotInfo associationsDo: [:assoc | slotInfo := assoc value. varName := assoc key. (slotInfo type = #Player) ifTrue: [ref := p instVarNamed: varName. (allPlayers includes: ref) ifFalse: [p instVarNamed: varName put: dot. count := count + 1. Transcript cr; show: ('Variable named "{1}" in player named "{2}" changed to point to Dot' translated format: {varName. ref externalName})]]]]. aList := nil. "Increases chance of the next line having desired effect." self inform: ('{1} item(s) fixed up' translated format: {count}). WorldState addDeferredUIMessage: [Smalltalk garbageCollect]! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/12/2020 14:07'! putUpShowSourceMenu: evt title: aTitle "Put up a menu in response to the show-source button being hit" | menu | self bringTopmostsToFront. "put up the show-source menu" menu := (TheWorldMenu new adaptToWorld: self) buildShowSourceMenu. menu addTitle: aTitle. menu popUpEvent: evt in: self. ^ menu! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/11/2020 18:01'! showSourceKeyHit "The user hit the 'show source' key on the XO. Our current take on this is simply to put up the world menu..." ^ self putUpShowSourceMenu: self currentEvent title: 'etoys source' translated! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menus' stamp: 'ct 9/12/2020 14:46'! presentDesktopColorMenu "Present the menu that governs the fill style of the squeak desktop." | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'desktop color' translated. self fillStyle addFillStyleMenuItems: aMenu hand: self currentHand from: self. aMenu addLine. aMenu add: 'solid fill' translated action: #useSolidFill. aMenu add: 'gradient fill' translated action: #useGradientFill. aMenu add: 'bitmap fill' translated action: #useBitmapFill. aMenu add: 'default fill' translated action: #useDefaultFill. ^ aMenu popUpInWorld! ! !EventTimeline methodsFor: 'dropping/grabbing' stamp: 'ct 9/11/2020 19:47'! acceptDroppingMorph: aMorph event: evt "Accept the drop of a morph." | aRect anEventRoll itsDuration itsWidthAfterDrop | self flag: #deferred. "This is a possible place for discovering whether the drop would have damaging effects on the mouse track..." (aMorph isKindOf: MouseEventSequenceMorph) ifTrue: [itsDuration := aMorph durationInMilliseconds. itsWidthAfterDrop := itsDuration // self eventRoll millisecondsPerPixel. super acceptDroppingMorph: aMorph event: evt. aMorph bounds: ((aMorph left @ 6) extent: (itsWidthAfterDrop @ aMorph height)). submorphs do: [:m | ((m ~~ aMorph) and: [m isKindOf: MouseEventSequenceMorph]) ifTrue: [(m bounds intersects: aMorph bounds) ifTrue: ["Eureka" aMorph delete. aMorph position: 100 at 100. aMorph openInWorld. aMorph flash. ^ self]]]] ifFalse: [super acceptDroppingMorph: aMorph event: evt] . aRect := (((aMorph left + 10) max: 10) @ 0) extent: 100@ 10. (anEventRoll := self eventRoll) pushChangesBackToEventTheatre. "Note that will ultimately result in replacement of the receiver by a new timeline" aMorph delete. self currentWorld abandonAllHalos. anEventRoll scrollPaneForRoll scrollHorizontallyToShow: aRect! ! !PasteUpMorph class methodsFor: '*Etoys-Squeakland-eToys-scripting' stamp: 'ct 9/12/2020 14:09'! putativeAdditionsToViewerCategoryPlayfieldOptions "Answer playfield options additions. Some of these are not yet underpinned by code in the current image; these will follow in due course." self flag: #deprecate. "ct: No senders" ^ #(#'playfield options' ( (command roundUpStrays 'Bring back all objects whose current coordinates keep them from being visible, so that at least a portion of each of my interior objects can be seen.') (command makeFitContents 'Adjust my bounds so that I fit precisely around all the objects within me') (command showAllPlayers 'Make visible the viewers for all players which have user-written scripts in this playfield.') (command hideAllPlayers 'Make invisible the viewers for all players in this playfield. This will save space before you publish this project') (command shuffleSubmorphs 'Rearranges my contents in random order') (command showAllObjectNames 'show names beneath all the objects currently in my interior, except for those for which showing such names is inappropriate.') (command hideAllObjectNames 'stop showing names beneath all the objects of my interior, If any of them is marked to "always show name", remove that designation')))! ! !PartsBin class methodsFor: '*Etoys-Squeakland-thumbnail cache' stamp: 'ct 9/12/2020 14:11'! rebuildIconsWithProgress "Put up an eye-catching progress morph while doing a complete rebuild of all the parts icons in the system." | fixBlock | fixBlock := Project current displayProgressWithJump: 'Building icons' translated. self clearThumbnailCache. self cacheAllThumbnails. fixBlock value. Project current world fullRepaintNeeded.! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: ((submorphs at: (2 max: submorphs size)) bottomRight + (2 at 1))). "inHotZone := evt ifNil: [true] ifNotNil: [rect containsPoint: evt cursorPoint]." aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/11/2020 20:47'! createMultipleTestScripts: aCount "Simulate the action of dropping a copy of the receiver to launch a new script -- for performance testing. To use: Open an Inspector on some tile command in a Viewer, e.g. on 'Car forward 5'. In the trash pane of that Inspector, then, evaluate expressions like: [self createMultipleTestScripts: 10] timeToRun. and MessageTally spyOn: [self createMultipleTestScripts: 4] " | aPosition | aPosition := 10 at 10. 1 to: aCount do: [:i | self forceScriptCreationAt: aPosition. aPosition := aPosition + (0 @ 50). "avoid dropping into existing scriptor" Project current world doOneCycle] "refresh viewer"! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/12/2020 14:47'! forceScriptCreationAt: aPosition "For performance testing." | dup | dup := self duplicate. dup eventHandler: nil. "Remove viewer-related evt mouseover feedback" dup formerPosition: self currentHand position. self currentHand attachMorph: dup; simulateMorphDropAt: aPosition.! ! !PhraseTileForTest methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTest methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := CompoundTileMorph new. guyToTake setNamePropertyTo: 'TestTile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !PhraseTileForTimesRepeat methodsFor: 'hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTimesRepeat methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := TimesRepeatTile new. guyToTake setNamePropertyTo: 'Repeat Tile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! adoptScriptsFrom "Let the user click on another object form which the receiver should obtain scripts and code" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ Beeper beep]. (aMorph renderedMorph isSketchMorph and: [aMorph player belongsToUniClass] and: [self belongsToUniClass not]) ifTrue: [costume acquirePlayerSimilarTo: aMorph player] ifFalse: [Beeper beep].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! beRevealedInActiveWorld "Reveal my corresponding morph in the active world" self revealPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! grabPlayerInActiveWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" ^ self grabPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/12/2020 14:47'! grabPlayerIn: aWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" | aMorph newPosition | self costume == aWorld ifTrue: [^ self]. self currentHand releaseMouseFocus. (aMorph := self costume) visible: true. newPosition := self currentHand position - (aMorph extent // 2). aMorph isInWorld ifTrue: [aMorph goHome. aMorph formerPosition: aMorph positionInWorld] ifFalse: [aMorph formerPosition: aWorld center]. aMorph formerOwner: Project current world. aMorph position: newPosition. self currentHand targetOffset: aMorph position - self currentHand position; addMorphBack: aMorph.! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! impartSketchScripts "Let the user designate another object to which my scripts and code should be imparted" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ self]. (aMorph renderedMorph isSketchMorph) ifTrue: [ aMorph acquirePlayerSimilarTo: self].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:44'! offerAlternateViewerMenuFor: aViewer event: evt "Put up an alternate Viewer menu on behalf of the receiver." | menu world | world := aViewer world. menu := MenuMorph new defaultTarget: self. (costumes notNil and: [ (costumes size > 1 or: [costumes size == 1 and: [costumes first ~~ costume renderedMorph]])]) ifTrue: [menu add: 'forget other costumes' translated target: self selector: #forgetOtherCostumes]. menu add: 'expunge empty scripts' translated target: self action: #expungeEmptyScripts. menu addLine. menu add: 'choose vocabulary...' translated target: aViewer action: #chooseVocabulary; balloonTextForLastItem: 'Choose a different vocabulary for this Viewer.' translated. menu add: 'choose limit class...' translated target: aViewer action: #chooseLimitClass; balloonTextForLastItem: 'Specify what the limitClass should be for this Viewer -- i.e., the most generic class whose methods and categories should be considered here.' translated. menu add: 'open standard lexicon' translated target: aViewer action: #openLexicon; balloonTextForLastItem: 'open a window that shows the code for this object in traditional programmer format' translated. menu add: 'open lexicon with search pane' translated target: aViewer action: #openSearchingProtocolBrowser; balloonTextForLastItem: 'open a lexicon that has a type-in pane for search (not recommended!!)' translated. menu addLine. menu add: 'inspect morph' translated target: costume selector: #inspect. menu add: 'inspect player' translated target: self selector: #inspect. self belongsToUniClass ifTrue: [ menu add: 'browse class' translated target: self action: #browsePlayerClass. menu add: 'inspect class' translated target: self class action: #inspect]. menu add: 'inspect this Viewer' translated target: aViewer selector: #inspect. menu add: 'inspect this Vocabulary' translated target: aViewer currentVocabulary selector: #inspect. menu addLine. menu add: 'relaunch this Viewer' translated target: aViewer action: #relaunchViewer. menu add: 'attempt repairs' translated target: Project current world action: #attemptCleanup. menu add: 'destroy all this object''s scripts' translated target: self action: #destroyAllScripts. menu add: 'view morph directly' translated target: aViewer action: #viewMorphDirectly. menu balloonTextForLastItem: 'opens a Viewer directly on the rendered morph.' translated. costume renderedMorph isSketchMorph ifTrue: [ menu addLine. menu add: 'impart scripts to...' translated target: self action: #impartSketchScripts]. ^ menu popUpEvent: evt in: world! ! !Player methodsFor: 'scripts-standard' stamp: 'ct 9/12/2020 14:48'! hide "Make the object be hidden, as opposed to visible" self currentHand ifNotNil: [:hand | (hand keyboardFocus == self costume renderedMorph) ifTrue: [ hand releaseKeyboardFocus]]. self costume hide.! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:45'! getLastKeystroke "Answer the last keystroke fielded" ^ Project current world lastKeystroke! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:41'! setLastKeystroke: aString "Set the last keystroke fielded" ^ self currentWorld lastKeystroke: aString! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/12/2020 14:49'! setSecondColor: aColor "Setter for costume's second color, if it's using gradient fill; if not, does nothing" | morph fillStyle colorToUse | morph := costume renderedMorph. fillStyle := morph fillStyle. fillStyle isGradientFill ifFalse: [^ self]. colorToUse := (costume isWorldMorph and: [aColor isColor]) ifTrue: [aColor alpha: 1.0] "reject any translucency" ifFalse: [aColor]. fillStyle lastColor: colorToUse forMorph: morph hand: self currentHand.! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:47'! addInstanceVariable "Offer the user the opportunity to add an instance variable, and if he goes through with it, actually add it." Project current world addMorphInLayer: (NewVariableDialogMorph on: self costume) centeredNear: (self currentHand ifNil:[Sensor]) cursorPoint! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:46'! allPossibleWatchersFromWorld "Answer a list of all UpdatingStringMorphs, PlayerReferenceReadouts, ThumbnailMorphs, and UpdatingReferenceMorphs in the Active world and its hidden book pages, etc., which have me or any of my siblings as targets" | a | a := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: a. ^ a select: [:e | e isEtoyReadout and: [e target class == self class]]! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/12/2020 14:48'! offerGetterTiles: slotName "For a player-type slot, offer to build convenient compound tiles that otherwise would be hard to get" | typeChoices typeChosen thePlayerThereNow slotChoices slotChosen getterTiles aCategoryViewer playerGetter | typeChoices := Vocabulary typeChoices. typeChosen := UIManager default chooseFrom: (typeChoices collect: [:t | t translated]) values: typeChoices title: ('Choose the TYPE of data to get from {1}''s {2}' translated format: {self externalName. slotName translated}). typeChosen isEmptyOrNil ifTrue: [^self]. thePlayerThereNow := self perform: slotName asGetterSelector. thePlayerThereNow ifNil: [thePlayerThereNow := self presenter standardPlayer]. slotChoices := thePlayerThereNow slotNamesOfType: typeChosen. slotChoices isEmpty ifTrue: [^self inform: 'sorry -- no slots of that type' translated]. slotChoices sort. slotChosen := UIManager default chooseFrom: (slotChoices collect: [:t | t translated]) values: slotChoices title: ('Choose the datum you want to extract from {1}''s {2}' translated format: {self externalName. slotName translated}). slotChosen isEmptyOrNil ifTrue: [^self]. "Now we want to tear off tiles of the form holder's valueAtCursor's foo" getterTiles := nil. aCategoryViewer := CategoryViewer new initializeFor: thePlayerThereNow categoryChoice: 'basic'. getterTiles := aCategoryViewer getterTilesFor: slotChosen asGetterSelector type: typeChosen. aCategoryViewer := CategoryViewer new initializeFor: self categoryChoice: 'basic'. playerGetter := aCategoryViewer getterTilesFor: slotName asGetterSelector type: #Player. getterTiles submorphs first acceptDroppingMorph: playerGetter event: nil. "the pad" "simulate a drop" getterTiles makeAllTilesGreen. getterTiles justGrabbedFromViewer: false. (getterTiles firstSubmorph) changeTableLayout; hResizing: #shrinkWrap; vResizing: #spaceFill. self currentHand attachMorph: getterTiles.! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:46'! addPatchVarNamed: nameSymbol | f | f := KedamaPatchMorph newExtent: self costume dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). self addInstanceVariable2Named: nameSymbol type: #Patch value: f player. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatch | f usedNames newName | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f kedamaWorld: self costume renderedMorph. usedNames := Project current world allKnownNames, self class instVarNames. newName := Utilities keyLike: f innocuousName satisfying: [:aName | (usedNames includes: aName) not]. f setNameTo: newName. self createSlotForPatch: f. self addToPatchDisplayList: f assuredPlayer. self costume world primaryHand attachMorph: f. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtle | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). self costume world primaryHand attachMorph: m. ^ m! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleSilently | m | m := KedamaTurtleMorph new openInWorld. self useTurtle: m player. m turtleCount: 0. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !Player methodsFor: '*Etoys-Squeakland-scripts-standard' stamp: 'ct 9/11/2020 20:41'! printInTranscript "Print a line representing the receiver in the Transcript" Project current world findATranscript: nil. Transcript cr; show: (Time now printString copyWithoutAll: '()'); space; show: self costume printString.! ! !Player methodsFor: '*Etoys-Squeakland-slots-user' stamp: 'ct 9/12/2020 14:47'! changeSlotInfo: aSymbol Project current world addMorphInLayer: (ModifyVariableDialogMorph on: self costume slot: aSymbol) centeredNear: (self currentHand ifNil: [Sensor]) cursorPoint.! ! !Player methodsFor: '*Etoys-Squeakland-slot getters/setters' stamp: 'ct 9/12/2020 14:47'! handUserPictureOfPenTrail "Called from the user-interface: hand the user a picture of the pen trail" self getHasPenTrails ifFalse: [ ^ self inform: 'no pen trails present' translated]. self currentHand attachMorph: (SketchMorph new form: self getPenTrailGraphic).! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! kedamaWorld ^ Project current world findDeeplyA: KedamaMorph ! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatchForSet | f | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). f kedamaWorld: self costume renderedMorph. self createSlotForPatch: f. ^ f! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleForSet | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !PlayerSurrogate methodsFor: 'menu' stamp: 'ct 9/11/2020 20:37'! revealThisObject "Reveal the object I represent" playerRepresented revealPlayerIn: Project current world! ! !PlayerSurrogate methodsFor: '*Etoys-Squeakland-as yet unclassified' stamp: 'ct 9/11/2020 20:40'! forciblyRenamePlayer "Allow the receiver to seize a name already nominally in use in the project." | current reply currentlyBearingName newNameForHim binding | current := playerRepresented knownName. reply := FillInTheBlank request: 'Type the name you insist upon' translated initialAnswer: current. reply isEmptyOrNil ifTrue: [^ self]. Preferences uniquePlayerNames ifFalse: [^ self costume renameTo: reply]. reply := (reply asIdentifier: true) asSymbol. reply = current ifTrue: [^ self inform: 'no change' translated]. binding := Project current world referencePool hasBindingOf: reply. binding ifNotNil: [ currentlyBearingName := binding value. newNameForHim := Utilities keyLike: reply satisfying: [:name | (Project current world referencePool includesKey: name) not]. currentlyBearingName renameTo: newNameForHim]. playerRepresented renameTo: reply. self inform: (binding ifNil: [('There was no conflict; this object is now named {1}' translated format: {reply})] ifNotNil: ['Okay, this object is now named\{1}\and the object formerly known by this name is now called\{2}' translated format: {reply. newNameForHim}]).! ! !PlayerType methodsFor: 'tiles' stamp: 'ct 9/11/2020 20:37'! defaultArgumentTile "Answer a tile to represent the type" ^ Project current world presenter standardPlayer tileToRefer! ! !KedamaPatchType methodsFor: 'tile protocol' stamp: 'ct 9/11/2020 20:15'! defaultArgumentTile "Answer a tile to represent the type" | patch ks k p | patch := KedamaPatchTile new typeColor: self typeColor. ks := self world allMorphs select: [:e | e isKindOf: KedamaMorph]. ks isEmpty ifFalse: [ k := ks first. p := k player getPatch. ] ifTrue: [ k := KedamaPatchMorph new. k assuredPlayer. p := k player. ]. patch usePatch: p. ^ patch! ! !PluggableFileList methodsFor: 'StandardFileMenu' stamp: 'ct 9/12/2020 14:50'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PluggableListMorph methodsFor: 'model access - keystroke' stamp: 'ct 9/11/2020 18:01'! specialKeyPressed: asciiValue "A special key with the given ascii-value was pressed; dispatch it" | oldSelection nextSelection max howManyItemsShowing | (#(8 13) includes: asciiValue) ifTrue: [ "backspace key - clear the filter, restore the list with the selection" model okToChange ifFalse: [^ self]. self removeFilter. priorSelection ifNotNil: [ | prior | prior := priorSelection. priorSelection := self getCurrentSelectionIndex. asciiValue = 8 ifTrue: [ self changeModelSelection: prior ] ]. ^ self ]. asciiValue = 27 ifTrue: [" escape key" ^ self currentEvent shiftPressed ifTrue: [self currentEvent putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]]. max := self maximumSelection. max > 0 ifFalse: [^ self]. nextSelection := oldSelection := self selectionIndex. asciiValue = 31 ifTrue: [" down arrow" nextSelection := oldSelection + 1. nextSelection > max ifTrue: [nextSelection := 1]]. asciiValue = 30 ifTrue: [" up arrow" nextSelection := oldSelection - 1. nextSelection < 1 ifTrue: [nextSelection := max]]. asciiValue = 1 ifTrue: [" home" nextSelection := 1]. asciiValue = 4 ifTrue: [" end" nextSelection := max]. howManyItemsShowing := self numSelectionsInView. asciiValue = 11 ifTrue: [" page up" nextSelection := 1 max: oldSelection - howManyItemsShowing]. asciiValue = 12 ifTrue: [" page down" nextSelection := oldSelection + howManyItemsShowing min: max]. model okToChange ifFalse: [^ self]. "No change if model is locked" oldSelection = nextSelection ifTrue: [^ self flash]. ^ self changeModelSelection: (self modelIndexFor: nextSelection)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:50'! startUpCenteredWithCaption: captionOrNil "Differs from startUpWithCaption: by appearing with cursor in the menu, and thus ready to act on mouseUp, without requiring user tweak to confirm" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint - (20 @ 0)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." self flag: #fix. "mt: Could we manage to open pop-up menus in Morphic without accessing self currentHand?" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil icon: aForm "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil icon: aForm at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithoutKeyboard "Display and make a selection from the receiver as long as the button is pressed. Answer the current selection. Do not allow keyboard input into the menu" ^ self startUpWithCaption: nil at: ((self currentHand ifNil: [Sensor]) cursorPoint) allowKeyboard: false! ! !PopUpMenu methodsFor: '*Morphic-Menus' stamp: 'ct 9/11/2020 20:37'! morphicStartUpWithCaption: captionOrNil icon: aForm at: location allowKeyboard: aBoolean "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard." selection := Cursor normal showWhile: [| menuMorph | menuMorph := MVCMenuMorph from: self title: nil. (captionOrNil notNil or: [aForm notNil]) ifTrue: [menuMorph addTitle: captionOrNil icon: aForm]. MenuIcons decorateMenu: menuMorph. menuMorph invokeAt: location in: self currentWorld allowKeyboard: aBoolean]. ^ selection! ! !PopUpMenu methodsFor: '*Etoys-Squeakland-basic control sequence' stamp: 'ct 9/11/2020 20:37'! startUpWithCaption: captionOrNil at: location allowKeyboard: allowKeyboard centered: centered "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard If centered is true, the menu items are displayed centered.." | maxHeight aMenu | (ProvideAnswerNotification signal: captionOrNil) ifNotNil: [:answer | ^ selection := answer ifTrue: [1] ifFalse: [2]]. maxHeight := Display height*3//4. self frameHeight > maxHeight ifTrue: [^ self startUpSegmented: maxHeight withCaption: captionOrNil at: location allowKeyboard: allowKeyboard]. Smalltalk isMorphic ifTrue:[ selection := Cursor normal showWhile: [aMenu := MVCMenuMorph from: self title: captionOrNil. centered ifTrue: [aMenu submorphs allButFirst do: [:m | m setProperty: #centered toValue: true]]. aMenu invokeAt: location in: self currentWorld allowKeyboard: allowKeyboard]. ^ selection]. frame ifNil: [self computeForm]. Cursor normal showWhile: [self displayAt: location withCaption: captionOrNil during: [self controlActivity]]. ^ selection! ! !PopUpMenu class methodsFor: '*Etoys-Squeakland-dialogs' stamp: 'ct 9/12/2020 14:52'! informCenteredAboveCursor: aString "Put up an informer showing the given string in a box, with the OK button for dismissing the informer having the cursor at its center." "PopUpMenu informCenteredAboveCursor: 'I like Squeak\how about you?' withCRs" | lines maxWid xCoor | lines := Array streamContents: [:aStream | aString linesDo: [:l | aStream nextPut: l]]. maxWid := (lines collect: [:l | Preferences standardMenuFont widthOfString: l]) max. xCoor := self currentHand cursorPoint x - (maxWid // 2). ((xCoor + maxWid) > self currentWorld right) ifTrue: [xCoor := self currentWorld right]. "Caters to problematic PopUpMenu boundary behavior" (PopUpMenu labels: 'OK' translated) startUpWithCaption: aString at: (xCoor @ self currentHand cursorPoint y) allowKeyboard: true centered: true.! ! !PreferenceWizardMorph methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:35'! initializePreviewWorld | w1 w2 w3 | previewWorld := PasteUpMorph new hResizing: #spaceFill; vResizing: #spaceFill; viewBox: (0 at 0 corner: 500 at 500); layoutFrame: (LayoutFrame fractions: (0.3 @ 0 corner: 1.0 @ 1.0) offsets: (0@ titleMorph height corner: 0 @ buttonRowMorph height negated)); fillStyle: Project current world fillStyle; borderWidth: 2; borderColor: Color white; cornerStyle: (self hasLowPerformance ifTrue: [#square] ifFalse: [#rounded]); yourself. w1 := (ToolSet browse: Morph selector: #drawOn:) dependents detect: [:ea | ea isSystemWindow]. w2 := ToolSet browseMessageSet: (SystemNavigation default allCallsOn: #negated) name: 'Senders' translated autoSelect: 'negated'. w3 := (Workspace new contents: '3+4 "Select and hit [CMD]+[P]."') openLabel: 'Workspace'. {w1. w2. w3} do: [:ea | ea makeUnclosable. previewWorld addMorph: ea]. self updateWindowBounds.! ! !PreferenceWizardMorph methodsFor: 'support' stamp: 'ct 9/12/2020 14:36'! adjustSettingsForLowPerformance self updateLowPerformanceLabel: 'Please wait, optimizing performance...' translated. self refreshWorld. self stateGradients "flat look" ifFalse: [self toggleGradients]. self stateBlinkingCursor ifTrue: [self toggleBlinkingCursor]. self stateFastDrag ifFalse: [self toggleFastDrag]. self stateSoftShadows ifTrue: [self toggleSoftShadows]. self stateHardShadows ifTrue: [self toggleHardShadows]. self stateRoundedWindowLook ifTrue: [self toggleRoundedWindowLook]. self stateRoundedButtonLook ifTrue: [self toggleRoundedButtonLook]. self stateAttachToolsToMouse ifTrue: [self toggleAttachToolsToMouse]. self stateToolAndMenuIcons ifTrue: [self toggleToolAndMenuIcons]. self stateSmartHorizontalSplitters ifTrue: [self toggleSmartHorizontalSplitters]. self stateSmartVerticalSplitters ifTrue: [self toggleSmartVerticalSplitters]. PluggableListMorph highlightHoveredRow: false; filterableLists: false; highlightPreSelection: true; "Feedback is important!!" flashOnErrors: false. TheWorldMainDockingBar showSecondsInClock: false. Preferences disable: #balloonHelpInMessageLists. "Set simple background." Project current world setAsBackground: MorphicProject defaultFill. previewWorld fillStyle: Project current world fillStyle. "Done." self updateLowPerformanceLabel: 'Settings were adjusted for optimal performance.' translated.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! roundedWindowCornersChanged "The user changed the value of the roundedWindowCorners preference. React" Project current world fullRepaintNeeded.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! vectorVocabularySettingChanged "The current value of the useVectorVocabulary flag has changed; now react. No senders, but invoked by the Preference object associated with the #useVectorVocabulary preference." Smalltalk isMorphic ifFalse: [^ self]. Project current world makeVectorUseConformToPreference.! ! !Project methodsFor: '*Etoys-Squeakland-file in/out' stamp: 'ct 9/12/2020 15:10'! storeOnServerWithNoInteractionInnards "Save to disk as an Export Segment. Then put that file on the server I came from, as a new version. Version is literal piece of file name. Mime encoded and http encoded." | newName primaryServerDirectory serverVersionPair localDirectory localVersionPair myVersionNumber warning maxNumber myDepth | self assureIntegerVersion. "Find out what version" primaryServerDirectory := self defaultFolderForAutoSaving ifNil: [^self]. localDirectory := self squeakletDirectory. serverVersionPair := self class mostRecent: self name onServer: primaryServerDirectory. localVersionPair := self class mostRecent: self name onServer: localDirectory. maxNumber := myVersionNumber := self currentVersionNumber. ProgressNotification signal: '2:versionsDetected'. warning := ''. myVersionNumber < serverVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) on the server' translated. maxNumber := maxNumber max: serverVersionPair second. ]. myVersionNumber < localVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) in the local directory' translated. maxNumber := maxNumber max: localVersionPair second. ]. version := self bumpVersion: maxNumber. "write locally - now zipped automatically" Display isVirtualScreen ifTrue: [ myDepth := displayDepth. displayDepth := OLPCVirtualScreen preferredScreenDepth.. ]. newName := self versionedFileName. lastSavedAtSeconds := Time totalSeconds. self activateWorld: self activeWorld during: [ self flag: #suspicious. "ct: Are there any world side effects in the export logic?" self exportSegmentFileName: newName directory: localDirectory withoutInteraction: true]. (localDirectory readOnlyFileNamed: newName) setFileTypeToObject; close. Display isVirtualScreen ifTrue: [ displayDepth := myDepth. ]. ProgressNotification signal: '4:localSaveComplete'. "3 is deep in export logic" primaryServerDirectory ifNotNil: [ [ primaryServerDirectory writeProject: self inFileNamed: newName asFileName fromDirectory: localDirectory. ] on: ProjectPasswordNotification do: [ :ex | ex resume: '' ]. ]. ProgressNotification signal: '9999 save complete'.! ! !Project methodsFor: '*Etoys-Squeakland-language' stamp: 'ct 9/11/2020 20:34'! updateLocaleDependentsWithPreviousSupplies: aCollection gently: gentlyFlag "Set the project's natural language as indicated" | morphs scriptEditors | gentlyFlag ifTrue: [ LanguageEnvironment localeChangedGently. ] ifFalse: [ LanguageEnvironment localeChanged. ]. morphs := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: morphs. scriptEditors := morphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m topEditor == m]]. (morphs copyWithoutAll: scriptEditors) do: [:morph | morph localeChanged]. scriptEditors do: [:m | m localeChanged]. Flaps disableGlobalFlaps: false. SugarNavigatorBar showSugarNavigator ifTrue: [Flaps addAndEnableEToyFlapsWithPreviousEntries: aCollection. Project current world addGlobalFlaps] ifFalse: [Preferences eToyFriendly ifTrue: [Flaps addAndEnableEToyFlaps. Project current world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]]. (Project current isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ParagraphEditor initializeTextEditorMenus. MenuIcons initializeTranslations. #(PartsBin ParagraphEditor BitEditor FormEditor StandardSystemController) do: [ :key | Smalltalk at: key ifPresent: [ :class | class initialize ]]. Project current world reformulateUpdatingMenus. "self setFlaps. self setPaletteFor: aLanguageSymbol." ! ! !MorphicProject methodsFor: 'utilities' stamp: 'ct 9/12/2020 15:13'! createViewIfAppropriate "Create a project view for the receiver and place it appropriately on the screen." | aMorph requiredWidth existing proposedV proposedH despair | ProjectViewOpenNotification signal ifTrue: [Preferences projectViewsInWindows ifTrue: [(ProjectViewMorph newProjectViewInAWindowFor: self) openInWorld] ifFalse: [aMorph := ProjectViewMorph on: self. requiredWidth := aMorph width + 10. existing := self currentWorld submorphs select: [:m | m isKindOf: ProjectViewMorph] thenCollect: [:m | m fullBoundsInWorld]. proposedV := 85. proposedH := 10. despair := false. [despair not and: [((proposedH @ proposedV) extent: requiredWidth) intersectsAny: existing]] whileTrue: [proposedH := proposedH + requiredWidth. proposedH + requiredWidth > self currentWorld right ifTrue: [proposedH := 10. proposedV := proposedV + 90. proposedV > (self currentWorld bottom - 90) ifTrue: [proposedH := self currentWorld center x - 45. proposedV := self currentWorld center y - 30. despair := true]]]. aMorph position: (proposedH @ proposedV). aMorph openInWorld]]! ! !MorphicProject methodsFor: 'flaps support' stamp: 'ct 9/12/2020 14:34'! setFlaps | flapTabs flapIDs sharedFlapTabs navigationMorph | self flag: #toRemove. "check if this method still used by Etoys" flapTabs := self world flapTabs. flapIDs := flapTabs collect: [:tab | tab knownName]. flapTabs do: [:tab | (tab isMemberOf: ViewerFlapTab) ifFalse: [tab isGlobalFlap ifTrue: [Flaps removeFlapTab: tab keepInList: false. tab currentWorld reformulateUpdatingMenus] ifFalse: [| referent | referent := tab referent. referent isInWorld ifTrue: [referent delete]. tab delete]]]. sharedFlapTabs := Flaps classPool at: #SharedFlapTabs. flapIDs do: [:id | id = 'Navigator' translated ifTrue: [sharedFlapTabs add: Flaps newNavigatorFlap]. id = 'Widgets' translated ifTrue: [sharedFlapTabs add: Flaps newWidgetsFlap]. id = 'Tools' translated ifTrue: [sharedFlapTabs add: Flaps newToolsFlap]. id = 'Squeak' translated ifTrue: [sharedFlapTabs add: Flaps newSqueakFlap]. id = 'Supplies' translated ifTrue: [sharedFlapTabs add: Flaps newSuppliesFlap]. id = 'Stack Tools' translated ifTrue: [sharedFlapTabs add: Flaps newStackToolsFlap]. id = 'Painting' translated ifTrue: [sharedFlapTabs add: Flaps newPaintingFlap]. id = 'Objects' translated ifTrue: [sharedFlapTabs add: Flaps newObjectsFlap ]]. 2 timesRepeat: [flapIDs do: [:id | Flaps enableDisableGlobalFlapWithID: id]]. self world flapTabs do: [:flapTab | flapTab isCurrentlyTextual ifTrue: [flapTab changeTabText: flapTab knownName]]. Flaps positionNavigatorAndOtherFlapsAccordingToPreference. navigationMorph := self currentWorld findDeeplyA: ProjectNavigationMorph preferredNavigator. navigationMorph isNil ifTrue: [^ self]. navigationMorph allMorphs do: [:morph | morph class == SimpleButtonDelayedMenuMorph ifTrue: [(morph findA: ImageMorph) isNil ifTrue: [| label | label := morph label. label isNil ifFalse: [| name | name := morph knownName. name isNil ifTrue: [morph name: label. name := label]. morph label: name translated]]]]! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/12/2020 15:15'! clearGlobalState "Clean up global state. The global variables World, ActiveWorld, ActiveHand and ActiveEvent provide convenient access to the state of the active project in Morphic. Clear their prior values when leaving an active project. This method may be removed if the use of global state variables is eliminated." "If global World is defined, clear it now. The value is expected to be set again as a new project is entered." Smalltalk globals at: #World ifPresent: [:w | Smalltalk globals at: #World put: nil]. self activeWorld: nil; activeHand: nil; activeEvent: nil.! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/12/2020 14:45'! wakeUpTopWindow "Image has been restarted, and the startUp list has been processed. Perform any additional actions needed to restart the user interface." SystemWindow wakeUpTopWindowUponStartup. Preferences mouseOverForKeyboardFocus ifTrue: [ "Allow global command keys to work upon re-entry without having to cause a focus change first." self currentHand releaseKeyboardFocus ]! ! !MorphicProject methodsFor: 'language' stamp: 'ct 9/12/2020 14:17'! updateLocaleDependents "Set the project's natural language as indicated" (self world respondsTo: #isTileScriptingElement) ifTrue: "Etoys present" [ self world allTileScriptingElements do: [:viewerOrScriptor | viewerOrScriptor localeChanged]]. Flaps disableGlobalFlaps: false. (Preferences eToyFriendly or: [ (Smalltalk classNamed: #SugarNavigatorBar) ifNotNil: [:c | c showSugarNavigator] ifNil: [false]]) ifTrue: [ Flaps addAndEnableEToyFlaps. self world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]. (self isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ScrapBook default emptyScrapBook. MenuIcons initializeTranslations. super updateLocaleDependents. "self setFlaps. self setPaletteFor: aLanguageSymbol."! ! !MorphicProject methodsFor: 'protocols' stamp: 'ct 9/12/2020 14:18'! currentVocabulary ^ self world currentVocabulary! ! !MorphicProject methodsFor: 'scheduling & debugging' stamp: 'ct 9/12/2020 15:13'! interruptCleanUpFor: interruptedProcess "Clean up things in case of a process interrupt." super interruptCleanUpFor: interruptedProcess. self uiProcess == interruptedProcess ifTrue: [ self activeHand ifNotNil: [:hand | hand interrupted]. self activeWorld: world. "reinstall active globals" self activeHand: world primaryHand. self activeHand interrupted. "make sure this one's interrupted too" self activeEvent: nil. Preferences eToyFriendly ifTrue: [ Project current world stopRunningAll]].! ! !MorphicProject class methodsFor: 'shrinking' stamp: 'ct 9/12/2020 15:45'! unloadMorphic "MorphicProject unloadMorphic" Project current isMorphic ifTrue: [ ^ Error signal: 'You can only unload Morphic from within another kind of project.' translated]. MorphicProject removeProjectsFromSystem. #(ActiveEvent ActiveHand ActiveWorld World) do: [:ea | Smalltalk globals removeKey: ea]. Processor allInstancesDo: [:process | #(ActiveHand ActiveEvent ActiveWorld) do: [:ea | process environmentRemoveKey: ea ifAbsent: []]]. { 'ToolBuilder-Morphic' . 'MorphicTests' . 'MorphicExtras' . 'Morphic' } do: [ :package | (MCPackage named: package) unload ].! ! !ProjectNavigationMorph methodsFor: 'stepping and presenter' stamp: 'ct 9/11/2020 20:34'! undoButtonWording "Answer the wording for the Undo button." | wdng | wdng := Project current world commandHistory undoOrRedoMenuWording. (wdng endsWith: ' (z)') ifTrue: [ wdng := wdng copyFrom: 1to: wdng size - 4]. ^ wdng! ! !ProjectNavigationMorph methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:34'! undoOrRedoLastCommand "Undo or redo the last command, as approrpiate." ^ Project current world commandHistory undoOrRedoCommand! ! !EventRecordingSpaceNavigator methodsFor: 'the actions' stamp: 'ct 9/11/2020 19:47'! doNewPainting "Make a new painting" | worldlet | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. worldlet closeNavigatorFlap. worldlet makeNewDrawing: (self currentEvent copy setPosition: worldlet center).! ! !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/11/2020 20:34'! maximumUsableArea ^ self maximumUsableAreaInWorld: Project current world! ! !RecordingControls methodsFor: 'private' stamp: 'ct 9/12/2020 14:52'! makeSoundMorph "Hand the user an anonymous-sound object representing the receiver's sound." | m aName | recorder verifyExistenceOfRecordedSound ifFalse: [^ self]. recorder pause. recordingSaved := true. m := AnonymousSoundMorph new. m sound: recorder recordedSound interimName: (aName := 'Unnamed Sound'). m setNameTo: aName. self currentHand attachMorph: m.! ! !ReleaseBuilder class methodsFor: 'scripts - support' stamp: 'ct 9/11/2020 20:33'! setProjectBackground: aFormOrColorOrFillStyle | world | world := Project current world. world fillStyle: aFormOrColorOrFillStyle. MorphicProject defaultFill: world fillStyle. world removeProperty: #hasCustomBackground.! ! !SARInstaller methodsFor: 'client services' stamp: 'ct 9/11/2020 20:33'! fileInMorphsNamed: memberName addToWorld: aBoolean "This will load the Morph (or Morphs) from the given member. Answers a Morph, or a list of Morphs, or nil if no such member or error. If aBoolean is true, also adds them and their models to the World." | member morphOrList | member := self memberNamed: memberName. member ifNil: [^ self errorNoSuchMember: memberName]. self installed: member. morphOrList := member contentStream fileInObjectAndCode. morphOrList ifNil: [^ nil]. aBoolean ifTrue: [Project current world addMorphsAndModel: morphOrList]. ^ morphOrList! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/12/2020 14:52'! addYesNoToHand "Place a test/yes/no complex in the hand of the beloved user" | ms messageNodeMorph aMorph | Preferences universalTiles ifTrue: [ms := MessageSend receiver: true selector: #ifTrue:ifFalse: arguments: {['do nothing']. ['do nothing']}. messageNodeMorph := ms asTilesIn: playerScripted class globalNames: true. self primaryHand attachMorph: messageNodeMorph] ifFalse: [aMorph := CompoundTileMorph new. self currentHand attachMorph: aMorph. aMorph setNamePropertyTo: 'TestTile' translated. aMorph position: self currentHand position. aMorph formerPosition: self currentHand position. self startSteppingSelector: #trackDropZones].! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:32'! dismiss "Dismiss the scriptor, usually nondestructively. Possibly animate the dismissal." | endPoint aForm startPoint topRend | owner ifNil: [^ self]. scriptName ifNil: [^ self delete]. "ad hoc fixup for bkwrd compat" endPoint := self viewerTile ifNotNilDo: [:tile | tile topLeft] ifNil: [owner topRight]. aForm := (topRend := self topRendererOrSelf) imageForm offset: (0 at 0). handWithTile := nil. startPoint := topRend topLeft. topRend topRendererOrSelf delete. (playerScripted isExpendableScript: scriptName) ifTrue: [ ^ playerScripted removeScript: scriptName fromWorld: Project current world]. Project current world displayWorld. aForm slideFrom: startPoint to: endPoint nSteps: 4 delay: 30. "The OLPC Virtual Screen wouldn't notice the last update here." Display forceToScreen: (endPoint extent: aForm extent).! ! !ScriptEditorMorph methodsFor: 'other' stamp: 'ct 9/12/2020 14:52'! offerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer" | aMenu count | self modernize. self currentHand showTemporaryCursor: nil. Preferences eToyFriendly ifTrue: [^ self offerSimplerScriptorMenu]. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addStayUpItem. "NB: the kids version in #offerSimplerScriptorMenu does not deploy the stay-up item" aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: { {'button to fire this script' translatedNoop. #tearOfButtonToFireScript}. {'fires per tick...' translatedNoop. #chooseFrequency}. #- }]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. "aMenu addLine. self addGoldBoxItemsTo: aMenu." aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addTranslatedList: { #-. {'open viewer' translatedNoop. #openObjectsViewer. 'open the viewer of the object to which this script belongs' translatedNoop}. {'detached method pane' translatedNoop. #makeIsolatedCodePane. 'open a little window that shows the Smalltalk code underlying this script.' translatedNoop}. #-. {'destroy this script' translatedNoop. #destroyScript} }. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-menu commands' stamp: 'ct 9/11/2020 20:32'! findObject "Reveal the object bearing the code " playerScripted revealPlayerIn: Project current world.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:52'! handUserTimesRepeatTile "Hand the user a times-repeat tile, presumably to drop in the script" | aMorph | aMorph := TimesRepeatTile new. self currentHand attachMorph: aMorph. aMorph position: self currentHand position.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:53'! offerSimplerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer. This variant is used when eToyFriendly preference is true." | aMenu count | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: #( ('button to fire this script' tearOfButtonToFireScript) -) translatedNoop]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu addTranslatedList: #( - ('open viewer' openObjectsViewer 'open the viewer of the object to which this script belongs') - ('destroy this script' destroyScript)) translatedNoop. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:32'! goldBoxMenu "Answer a graphical menu to be put up in conjunction with the Gold Box" | aBox | aBox := Project current world findA: GoldBoxMenu. aBox ifNil: [aBox := GoldBoxMenu new]. aBox initializeFor: self. ^ aBox! ! !ScriptEncoder methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:31'! init: class notifying: parser super init: class notifying: parser. self referenceObject: Project current world referenceWorld.! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/12/2020 14:53'! offerMenuIn: aStatusViewer "Put up a menu." | aMenu | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu title: player knownName, ' ', selector. aMenu addStayUpItem. (player class instanceCount > 1) ifTrue: [aMenu add: 'propagate status to siblings' translated selector: #assignStatusToAllSiblingsIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'Make the status of this script in all of my sibling instances be the same as the status you see here' translated]. aMenu addLine. aMenu add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: player selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: player selector: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu add: 'open this script''s Scriptor' translated target: player selector: #grabScriptorForSelector:in: argumentList: {selector. aStatusViewer world}. aMenu balloonTextForLastItem: 'Open up the Scriptor for this script' translated. aMenu add: 'open this object''s Viewer' translated target: player selector: #beViewed. aMenu balloonTextForLastItem: 'Open up a Viewer for this object' translated. aMenu addLine. aMenu add: 'more...' translated target: self selector: #offerShiftedMenuIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'The "more..." branch offers you menu items that are less frequently used.' translated. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/11/2020 20:30'! offerShiftedMenuIn: aStatusViewer "Put up the shifted menu" ^ (MenuMorph new defaultTarget: self) title: player knownName, ' ', selector; add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld; balloonTextForLastItem: 'Wherever this object currently is, the "grab" command will rip it out, and place it in your "hand". This is a very drastic step, that can disassemble things that may be very hard to put back together!!' translated; add: 'destroy this script' translated target: player selector: #removeScriptWithSelector: argument: selector; balloonTextForLastItem: 'Caution!! This is irreversibly destructive -- it removes the script from the system.' translated; addLine; add: 'inspect morph' translated target: player costume selector: #inspect; add: 'inspect player' translated target: player selector: #inspect; popUpInWorld: self currentWorld! ! !ScriptNameType methodsFor: 'queries' stamp: 'ct 9/11/2020 20:29'! choices "Answer an alphabetized list of known script selectors in the current project" ^ Project current world presenter allKnownUnaryScriptSelectors ! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:29'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock "Answer a MethodNode for the argument, sourceStream, that is the root of a parse tree. Parsing is done with respect to the argument, class, to find instance, class, and pool variables; and with respect to the argument, ctxt, to find temporary variables. Errors in parsing are reported to the argument, req, if not nil; otherwise aBlock is evaluated. The argument noPattern is a Boolean that is true if the the sourceStream does not contain a method header (i.e., for DoIts)." "Copied from superclass, use ScriptEncoder and give it a referenceWorld. This assumes worldLoading has been set to the right world this player belongs to. --bf 5/4/2010" | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: Project current world referenceWorld )] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:28'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock for: anInstance | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: (anInstance costume ifNotNil: [anInstance costume referenceWorld] ifNil: [Project current world]))] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !SearchTopic methodsFor: 'private' stamp: 'ct 9/11/2020 20:28'! triggerUpdateContents self mutex critical: [ updatePending == true ifFalse: [ updatePending := true. Project current addDeferredUIMessage: [Project current world addAlarm: #updateContents withArguments: #() for: self at: Time millisecondClockValue + 250]]].! ! !SelectionMorph methodsFor: 'halo commands' stamp: 'ct 9/11/2020 20:28'! duplicate "Make a duplicate of the receiver and havbe the hand grab it" selectedItems := self duplicateMorphCollection: selectedItems. selectedItems reverseDo: [:m | (owner ifNil: [self currentWorld]) addMorph: m]. dupLoc := self position. self currentHand grabMorph: self. self currentWorld presenter flushPlayerListCache.! ! !SimpleHierarchicalListMorph methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:28'! specialKeyPressed: asciiValue (self arrowKey: asciiValue) ifTrue: [^ true]. asciiValue = 27 "escape" ifTrue: [ self currentEvent shiftPressed ifTrue: [self currentWorld putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]. ^ true]. ^ false! ! !SimpleSelectionMorph methodsFor: 'extending' stamp: 'ct 9/11/2020 20:27'! extendByHand: aHand "Assumes selection has just been created and added to some pasteUp or world" | startPoint handle m inner | startPoint := Sensor cursorPoint. handle := NewHandleMorph new followHand: aHand forEachPointDo: [:newPoint | | localPt | Cursor crossHair show. localPt := (self transformFrom: self world) globalPointToLocal: newPoint. self bounds: (startPoint rect: localPt)] lastPointDo: [:newPoint | inner := self bounds insetBy: 2 at 2. inner area >= 16 ifTrue: [m := SketchMorph new form: (Form fromDisplay: inner). aHand attachMorph: m. self currentWorld fullRepaintNeeded] "selection tracking can leave unwanted artifacts" ifFalse: [Beeper beep]. "throw minnows back" self delete]. handle visible: false. aHand attachMorph: handle. handle startStepping! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! cancelOutOfPainting "The user requested to back out of a painting session without saving" self deleteSelfAndSubordinates. emptyPicBlock ifNotNil: [emptyPicBlock value]. "note no args to block!!" hostView ifNotNil: [hostView changed]. Project current world resumeScriptsPausedByPainting. ^ nil! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! deliverPainting: result evt: evt "Done painting. May come from resume, or from original call. Execute user's post painting instructions in the block. Always use this standard one. 4/21/97 tk" | newBox newForm ans | palette ifNotNil: "nil happens" [palette setAction: #paint: evt: evt]. "Get out of odd modes" "rot := palette getRotations." "rotate with heading, or turn to and fro" "palette setRotation: #normal." result == #cancel ifTrue: [ ans := UIManager default chooseFrom: { 'throw it away' translated. 'keep painting it' translated. } title: 'Do you really want to throw away what you just painted?' translated. ^ ans = 1 ifTrue: [self cancelOutOfPainting] ifFalse: [nil]]. "cancelled out of cancelling." "hostView rotationStyle: rot." "rotate with heading, or turn to and fro" newBox := paintingForm rectangleEnclosingPixelsNotOfColor: Color transparent. registrationPoint ifNotNil: [registrationPoint := registrationPoint - newBox origin]. "relative to newForm origin" newForm := Form extent: newBox extent depth: paintingForm depth. newForm copyBits: newBox from: paintingForm at: 0 at 0 clippingBox: newForm boundingBox rule: Form over fillColor: nil. newForm isAllWhite ifTrue: [ (self valueOfProperty: #background) == true ifFalse: [^ self cancelOutOfPainting]]. newForm fixAlpha. "so alpha channel stays intact for 32bpp" self delete. "so won't find me again" dimForm ifNotNil: [dimForm delete]. newPicBlock value: newForm value: (newBox copy translateBy: bounds origin). Project current world resumeScriptsPausedByPainting.! ! !SketchMorph methodsFor: 'menus' stamp: 'ct 9/11/2020 20:27'! collapse "Replace the receiver with a collapsed rendition of itself." | w collapsedVersion a ht | (w := self world) ifNil: [^ self]. collapsedVersion := (self imageForm scaledToSize: 50 at 50) asMorph. collapsedVersion setProperty: #uncollapsedMorph toValue: self. collapsedVersion on: #mouseUp send: #uncollapseSketch to: collapsedVersion. collapsedVersion setBalloonText: ('A collapsed version of {1}. Click to open it back up.' translated format: {self externalName}). self delete. w addMorphFront: ( a := AlignmentMorph newRow hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4; borderColor: Color white; addMorph: collapsedVersion; yourself). a setNameTo: self externalName. ht := (Smalltalk at: #SugarNavTab ifPresent: [:c | Project current world findA: c]) ifNotNil: [:tab | tab height] ifNil: [80]. a position: 0 at ht. collapsedVersion setProperty: #collapsedMorphCarrier toValue: a. (self valueOfProperty: #collapsedPosition) ifNotNil: [:priorPosition | a position: priorPosition].! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-event handling' stamp: 'ct 9/12/2020 14:39'! deleteBoxHit "The delete box was hit..." self currentHand showTemporaryCursor: nil. self delete.! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:39'! openPropertySheet "Delete the receiver and open a property sheet on my target instead." self currentHand showTemporaryCursor: nil. target openAppropriatePropertySheet. self delete.! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer cardNum: cardNum "Call once to search a card of the stack. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [self currentPage allStringsAfter: oldContainer text]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findWordStart: searchString startingAt: start) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" "wasIn := (pages at: pageNum) isInMemory." self goToCardNumber: cardNum "wasIn ifFalse: ['search again, on the real current text. Know page is in.'. ^ self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) recompute it startAt: startIndex container: oldContainer pageNum: pageNum]"]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findViaTemplate | list pl cardInst | "Current card is the template. Only search cards in this background. Look at cards directly (not allText). Key must be found in the same field as in the template. HyperCard style (multiple starts of words). Put results in a list, outside the stack." list := self templateMatches. list isEmpty ifTrue: [^ self inform: 'No matches were found. Be sure the current card is mostly blank and only has text you want to match.' translated]. "put up a PluggableListMorph" cardInst := self currentCard. cardInst matchIndex: 0. "establish entries" cardInst results at: 1 put: list. self currentPage setProperty: #myStack toValue: self. "way to get back" pl := PluggableListMorph new on: cardInst list: #matchNames selected: #matchIndex changeSelected: #matchIndex: menu: nil "#matchMenu:shifted:" keystroke: nil. self currentHand attachMorph: (self formatList: pl). ! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! assureFlapOfLabel: aTitle withContents: aString "Answer an info flap with the given title and contents. If one exists in the project, use that, else create one & insert it in the world. Answer the flap tab." | allFlapTabs aTab | allFlapTabs := Project current world localFlapTabs, Project current world extantGlobalFlapTabs. aTab := allFlapTabs detect: [:ft | ft flapID = aTitle] ifNone: [nil]. aTab ifNotNil: [^ aTab]. "already present" aTab := self openInfoFlapWithLabel: aTitle helpContents: aString edge: #left. aTab bottom: Project current world bottom. self cleanUpFlapTabsOnLeft. aTab hideFlap. aTab referent show. aTab show. ^ aTab " ScriptingSystem assureFlapOfLabel: 'Egg Sample' withContents: EventRollMorph basicNew helpString "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! cleanUpFlapTabsOnLeft "Make sure the flap tabs on the left of the screen line up nicely, making best use of realestate." | tabsOnLeft current | tabsOnLeft := ((Project current world localFlapTabs, Project current world extantGlobalFlapTabs) select: [:f | f edgeToAdhereTo = #left]) sort: [:a :b | a top <= b top]. current := SugarNavigatorBar showSugarNavigator ifTrue: [75] ifFalse: [0]. tabsOnLeft do: [:aTab | aTab top: (current min: Project current world height - aTab height). current := aTab bottom + 2]. " ScriptingSystem cleanUpFlapTabsOnLeft "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:25'! openInfoFlapWithLabel: aTitle helpContents: aString edge: anEdge "Open an info flap with the given label, contents, and edge" | aPlug outer leftStrip rightStrip titleRow aDismissButton aFlapTab | Preferences enable: #scrollBarsOnRight. Preferences enable: #inboardScrollbars. aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab referentMargin: 0 @ Project current world sugarAllowance. outer := HelpFlap newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #morphicLayerNumber toValue: 26. leftStrip := Morph new beTransparent. leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip beTransparent. rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. outer addMorphBack: rightStrip. outer clipSubmorphs: true. titleRow := AlignmentMorph newRow. titleRow borderColor: Color veryVeryLightGray; borderWidth: 1. titleRow hResizing: #spaceFill; vResizing: #shrinkWrap. titleRow beTransparent. aDismissButton := aFlapTab tanOButton. aDismissButton actionSelector: #dismissViaHalo. titleRow addMorphFront: aDismissButton. titleRow addTransparentSpacerOfSize: 8 @ 0. titleRow addMorphBack: (StringMorph contents: aTitle font: Preferences standardEToysTitleFont). rightStrip addMorph: titleRow. aPlug := PluggableTextMorph new. aPlug width: 540. aPlug setText: aString. aPlug textMorph beAllFont: Preferences standardEToysFont. aPlug retractable: false; scrollBarOnLeft: false. aPlug hScrollBarPolicy: #never. aPlug borderColor: ScriptingSystem borderColor. aPlug setNameTo: aTitle. aPlug hResizing: #spaceFill. aPlug vResizing: #spaceFill. rightStrip addMorphBack: aPlug. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: aTitle edge: anEdge color: (Color r: 0.677 g: 0.935 b: 0.484). aFlapTab submorphs first beAllFont: Preferences standardEToysFont. Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. anEdge == #left ifTrue: [aFlapTab position: (outer left @ outer top). outer extent: (540 @ Project current world height)]. anEdge == #right ifTrue: [aFlapTab position: ((Project current world right - aFlapTab width) @ Project current world top). outer extent: (540 @ Project current world height)]. outer beFlap: true. outer color: Color green veryMuchLighter. aPlug textMorph lock. aFlapTab referent hide. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. Project current world doOneCycle. aPlug width: 540. aPlug setText: aString. "hmm, again" aPlug color: outer color. aPlug borderWidth: 0. aPlug textMorph contents: aString wrappedTo: 520. aFlapTab applyThickness: 560. aFlapTab fitOnScreen. aFlapTab referent show. ^ aFlapTab! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:24'! systemQueryPhraseWithActionString: aString labelled: anotherString "Answer a system-query-phrase with the give action-string and label." ^ Project current world presenter systemQueryPhraseWithActionString: aString labelled: anotherString! ! !StandardViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 20:24'! currentVocabulary "Answer the vocabulary currently associated with the receiver" | aSym aVocab | aSym := self valueOfProperty: #currentVocabularySymbol ifAbsent: [nil]. aSym ifNil: [aVocab := self valueOfProperty: #currentVocabulary ifAbsent: [nil]. aVocab ifNotNil: [aSym := aVocab vocabularyName. self removeProperty: #currentVocabulary. self setProperty: #currentVocabularySymbol toValue: aSym]]. ^ aSym ifNotNil: [Vocabulary vocabularyNamed: aSym] ifNil: [(self world ifNil: [Project current world]) currentVocabularyFor: scriptedPlayer]! ! !SugarLauncher methodsFor: 'commands' stamp: 'ct 9/12/2020 14:07'! viewSource Project current world addDeferredUIMessage: [ Project current world showSourceKeyHit].! ! !SugarNavTab methodsFor: 'positioning' stamp: 'ct 9/11/2020 20:24'! occupyTopRightCorner "Make the receiver be the correct size, and occupy the top-right corner of the screen." | worldBounds toUse | worldBounds := Project current world bounds. " toUse := Preferences useArtificialSweetenerBar ifFalse: [75] ifTrue: [(ActiveWorld extent >= (1200 @ 900)) ifTrue: [75] ifFalse: [40]]." toUse := 40. "Trying for the moment to use the smaller icon always when in this mode." referent height: toUse; resizeButtonsAndTabTo: toUse. self extent: toUse @ toUse. self topRight: worldBounds topRight! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelp " SugarNavigatorBar putUpInitialBalloonHelp " | suppliesButton b1 b2 p b | suppliesButton := paintButton owner submorphs detect: [:e | e isButton and: [e actionSelector = #toggleSupplies]]. b1 := BalloonMorph string: self paintButtonInitialExplanation for: paintButton corner: #topRight force: false. b2 := BalloonMorph string: self suppliesButtonInitialExplanation for: suppliesButton corner: #topLeft force: true. p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. p addMorph: b1. p addMorph: b2. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelpFor: quads "Given a list of quads of the form (see senders for examples), put up initial balloon help for them." " SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((doNewPainting 'make a new painting' topRight false) (toggleSupplies 'open the supplies bin' topLeft true)) SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((showNavBar 'show the tool bar' bottomLeft false) (hideNavBar 'hide the tool bar' bottomLeft false)) " | b1 p b | p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. quads do: [:aQuad | (submorphs first submorphs detect: [:e | e isButton and: [e actionSelector = aQuad first]] ifNone: [nil]) ifNotNil: [:aButton | b1 := BalloonMorph string: aQuad second for: aButton corner: aQuad third force: aQuad fourth. p addMorph: b1]]. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'help flap' stamp: 'ct 9/12/2020 14:37'! buildAndOpenHelpFlap "Called only when flaps are being created afresh." | aFlapTab outer leftStrip rightStrip aGuide | aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab setProperty: #rigidThickness toValue: true. outer := AlignmentMorph newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #wantsHaloFromClick toValue: false. leftStrip := Morph new beTransparent. "This provides space for tabs to be seen." leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip color: (Color green veryMuchLighter alpha: 0.2). rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. rightStrip setProperty: #wantsHaloFromClick toValue: false. outer addMorphBack: rightStrip. outer clipSubmorphs: true. aGuide := QuickGuideMorph new. aGuide initializeIndexPage. " aGuide order: QuickGuideMorph defaultOrder. " QuickGuideMorph loadIndexAndPeekOnDisk. aGuide loadPages. rightStrip addMorphBack: aGuide. aGuide beSticky. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: 'Help' translated edge: #left color: (Color r: 0.677 g: 0.935 b: 0.484). Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. aFlapTab position: outer left @ outer top. outer extent: 462 @ Project current world height. outer beFlap: true. outer beTransparent. aFlapTab referent hide. aFlapTab referentMargin: 0 at self height. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. aFlapTab applyThickness: 462. aFlapTab fitOnScreen. aFlapTab referent show. aFlapTab show. aFlapTab makeFlapCompact: true. aFlapTab setToPopOutOnDragOver: false. Flaps addGlobalFlap: aFlapTab. Project current world addGlobalFlaps. ScriptingSystem cleanUpFlapTabsOnLeft! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/12/2020 14:53'! gotoAnother EToyProjectHistoryMorph new position: self currentHand position; openInWorld ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! makeProjectNameLabel | t | projectNameField := SugarRoundedField new. t := UpdatingStringMorph new. t setProperty: #okToTextEdit toValue: true. t putSelector: #projectNameChanged:. t getSelector: #projectName. projectNameField backgroundColor: self color. t target: self. t useStringFormat. t beSticky. t label: Project current name font: (StrikeFont familyName: 'BitstreamVeraSans' size: 24). t color: Color black. t width: projectNameField width - 10. projectNameField label: t. projectNameField setBalloonText: self projectNameFieldBalloonHelp. projectNameField on: #mouseDown send: #mouseDown: to: t. projectNameField on: #mouseUp send: #mouseUp: to: t. self resizeProjectNameField. ^projectNameField.! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectName ^ Project current name ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectNameChanged: aString Project current renameTo: aString. ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! shareMenu | menu item ext | menu := MenuMorph new. ext := 200 at 50. #((stopSharing makePrivateLabelIn:) (startSharing makeMyNeighborhoodLabelIn:) "(shareThisWorld makeBadgeLabelIn:)") do: [:pair | item := MenuItemMorph new contents: ''; target: self; selector: pair first; arguments: #(). item color: Color black. item addMorph: (self perform: pair second with: ext). item setProperty: #minHeight toValue: ext y. item fitContents. item extent: ext. item setProperty: #selectionFillStyle toValue: (Color gray alpha: 0.5). menu addMorphBack: item. ]. menu color: Color black. menu borderColor: Color white. ^ menu invokeModalAt: shareButton position + (10 at 20) in: Project current world allowKeyboard: false.! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/12/2020 14:37'! startNebraska | nebraska | Project current world remoteServer: nil. Project current world submorphs do: [:e | (e isMemberOf: NebraskaServerMorph) ifTrue: [e delete]]. nebraska := NebraskaServerMorph serveWorld. SugarLauncher current offerStreamTube: 'sqk-nebraska' inBackgroundOnPort: [nebraska listeningPort]. ! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! startP2P listener ifNotNil: [listener stopListening]. listener ifNil: [listener := SugarListenerMorph new]. listener position: -200@ -200. Project current world addMorphBack: listener. listener startListening. SugarLauncher current offerStreamTube: 'sqk-etoy-p2p' inBackgroundOnPort: [listener listeningPort].! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! stopSharing SugarLauncher current leaveSharedActivity. listener ifNotNil: [listener stopListening. listener := nil]. Project current world remoteServer: nil. Project current world submorphs do: [:ea | (ea isMemberOf: NebraskaServerMorph) ifTrue: [ea delete]]. self sharingChanged.! ! !SugarNavigatorBar methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:22'! undoButtonAppearance | wording | undoButton ifNotNil: [ Project current world commandHistory undoEnabled ifTrue: [undoButton enabled] ifFalse: [undoButton disabled]. wording := self undoButtonWording. undoButton setBalloonText: wording. ]. ! ! !InteriorSugarNavBar methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:12'! doNewPainting "Make a new painting" | worldlet aRect | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. aRect := (worldlet topLeft + (0 @ self height)) corner: worldlet bottomRight. worldlet makeNewDrawing: (self currentEvent copy setPosition: aRect center).! ! !SugarNavigatorBar class methodsFor: 'utilitity' stamp: 'ct 9/11/2020 20:22'! findAnythingMorph ^ FileList2 morphicViewProjectLoader2InWorld: Project current world title: 'Find...' translated reallyLoad: true dirFilterType: #initialDirectoryList isGeneral: true.! ! !SugarRoundedField methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:22'! resizeLabel | small | (label notNil and: [label hasFocus not]) ifTrue: [ label width: self width - 10. small :=self height < 45. label label: Project current world project name font: (StrikeFont familyName: 'BitstreamVeraSans' size: (small ifTrue: [15] ifFalse: [24])). label center: self center. label left: self left + 10. self addMorph: label. ]. ! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerTilesMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'me, by name' target: self selector: #attachTileForCode:nodeType: argumentList: {''. aReceiver}. menu add: 'self' target: self selector: #attachTileForCode:nodeType: argumentList: {'self'. VariableNode}. menu add: '_ (assignment)' target: self selector: #attachTileForCode:nodeType: argumentList: {''. nil}. menu add: '"a Comment"' target: self selector: #attachTileForCode:nodeType: argumentList: {'"a comment"\' withCRs. CommentNode}. menu submorphs last color: Color blue. menu add: 'a Number' target: self selector: #attachTileForCode:nodeType: argumentList: {'5'. LiteralNode}. menu add: 'a Character' target: self selector: #attachTileForCode:nodeType: argumentList: {'$z'. LiteralNode}. menu add: '''abc''' target: self selector: #attachTileForCode:nodeType: argumentList: {'''abc'''. LiteralNode}. menu add: 'a Symbol constant' target: self selector: #attachTileForCode:nodeType: argumentList: {'#next'. LiteralNode}. menu add: 'true' target: self selector: #attachTileForCode:nodeType: argumentList: {'true'. VariableNode}. menu add: 'a Test' target: self selector: #attachTileForCode:nodeType: argumentList: {'true ifTrue: [self] ifFalse: [self]'. MessageNode}. menu add: 'a Loop' target: self selector: #attachTileForCode:nodeType: argumentList: {'1 to: 10 do: [:index | self]'. MessageNode}. menu add: 'a Block' target: self selector: #attachTileForCode:nodeType: argumentList: {'[self]'. BlockNode}. menu add: 'a Class or Global' target: self selector: #attachTileForCode:nodeType: argumentList: {'Character'. LiteralVariableNode}. menu add: 'a Reply' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. ReturnNode}. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerVarsMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu instVarList cls | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'new temp variable' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. TempVariableNode}. instVarList := OrderedCollection new. cls := aReceiver class. [instVarList addAllFirst: cls instVarNames. cls == aLexiconModel limitClass] whileFalse: [cls := cls superclass]. instVarList do: [:nn | menu add: nn target: self selector: #instVarTile: argument: nn]. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! attachToHand "Adjust my look and attach me to the hand" self roundedCorners. self currentHand attachMorph: self. Preferences tileTranslucentDrag ifTrue: [self lookTranslucent. self align: self center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ self align: self topLeft with: self currentHand position + self cursorBaseOffset].! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! instVarTile: aName "Make and put into hand a tile for an instance variable" | sm | sm := ((VariableNode new name: aName index: 1 type: 1 "LdInstType") asMorphicSyntaxIn: SyntaxMorph new). sm roundedCorners. self currentHand attachMorph: sm. Preferences tileTranslucentDrag ifTrue: [sm lookTranslucent. sm align: sm center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ sm align: sm topLeft with: self currentHand position + self cursorBaseOffset]! ! !SyntaxMorph methodsFor: 'scripting' stamp: 'ct 9/12/2020 14:55'! tearOffTile "For a SyntaxMorph, this means give a copy of me" | dup | dup := self duplicate. self currentHand attachMorph: dup. ^ Preferences tileTranslucentDrag ifTrue: [dup lookTranslucent] ifFalse: [dup align: dup topLeft with: self currentHand position + self cursorBaseOffset]! ! !SystemWindow methodsFor: 'events' stamp: 'ct 9/11/2020 20:22'! doFastFrameDrag: grabPoint "Do fast frame dragging from the given point" | offset newBounds outerWorldBounds clearArea | outerWorldBounds := self boundsIn: nil. offset := outerWorldBounds origin - grabPoint. clearArea := Project current world clearArea. newBounds := outerWorldBounds newRectFrom: [:f | | p selector | p := Sensor cursorPoint. (self class dragToEdges and: [(selector := self dragToEdgesSelectorFor: p in: clearArea) notNil]) ifTrue: [clearArea perform: selector] ifFalse: [p + offset extent: outerWorldBounds extent]]. self bounds: newBounds; comeToFront! ! !CollapsedMorph methodsFor: 'collapse/expand' stamp: 'ct 9/12/2020 14:39'! uncollapseToHand "Hand the uncollapsedMorph to the user, placing it in her hand, after remembering appropriate state for possible future use" | nakedMorph | nakedMorph := uncollapsedMorph. uncollapsedMorph := nil. nakedMorph setProperty: #collapsedPosition toValue: self position. mustNotClose := false. "so the delete will succeed" self delete. self currentHand attachMorph: nakedMorph.! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 18:01'! rotateWindows "Rotate the z-ordering of the windows." self currentEvent shiftPressed ifTrue: [self sendTopWindowBackOne] ifFalse: [self sendTopWindowToBack].! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 20:21'! sendTopWindowBackOne "Rotate the window-list one downward, i.e., make the bottommost one be the active one, pushing the receiver to next-to-topmost." | dows | dows := Project current world submorphs select: [:m | m isSystemWindow]. dows ifNotEmpty: [dows last expand; comeToFront]! ! !TextEditor methodsFor: 'menu commands' stamp: 'ct 9/11/2020 18:02'! offerMenuFromEsc: aKeyboardEvent "The escape key was hit while the receiver has the keyboard focus; take action." self currentEvent shiftPressed ifFalse: [ self raiseContextMenu: aKeyboardEvent]. ^ true! ! !ThreePhaseButtonMorph methodsFor: 'button' stamp: 'ct 9/11/2020 18:02'! doButtonAction "Perform the action of this button. Subclasses may override this method. The default behavior is to send the button's actionSelector to its target object with its arguments." | args | (target notNil and: [actionSelector notNil]) ifTrue: [ args := actionSelector numArgs > arguments size ifTrue: [arguments copyWith: self currentEvent] ifFalse: [arguments]. Cursor normal showWhile: [ target perform: actionSelector withArguments: args]. target isMorph ifTrue: [target changed]].! ! !TileMorph methodsFor: 'arrows' stamp: 'ct 9/11/2020 18:02'! showSuffixChoices "The suffix arrow has been hit, so respond appropriately" | plusPhrase phrase pad outer num | self currentEvent shiftPressed ifTrue: [^ self wrapPhraseInFunction]. (phrase := self ownerThatIsA: PhraseTileMorph orA: FunctionTile) ifNil: [nil]. (type == #literal) & (literal isNumber) ifTrue: ["Tile is a constant number" (phrase isNil or: [phrase finalTilePadSubmorph == owner]) "pad" ifTrue: ["we are adding the first time (at end of our phrase)" plusPhrase := self phraseForOp: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). owner acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. num := plusPhrase firstSubmorph firstSubmorph. num deleteSuffixArrow]]. (#(function expression parameter) includes: type) ifTrue: [pad := self ownerThatIsA: TilePadMorph. plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: self. pad topEditor scriptEdited "recompile"]. type = #operator ifTrue: ["Tile is accessor of an expression" phrase resultType == #Number ifTrue: [outer := phrase ownerThatIsA: PhraseTileMorph orA: TimesRepeatTile. pad := self ownerThatIsA: TilePadMorph. outer ifNotNil: [(outer lastSubmorph == pad or: [true]) ifTrue: [ "first time" plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: phrase. "car's heading" pad topEditor scriptEdited "recompile & deal with carets"]]]]. (self topEditor ifNil: [phrase ifNil: [^ self]]) enforceTileColorPolicy! ! !TileMorph methodsFor: 'code generation' stamp: 'ct 9/11/2020 20:21'! acceptNewLiteral "Tell the scriptEditor who I belong to that I have a new literal value." | topScript | topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript installWithNewLiteral]. (self ownerThatIsA: ViewerLine) ifNotNil: [:aLine | (self ownerThatIsA: PhraseTileMorph) ifNotNil: [aLine removeHighlightFeedback. self layoutChanged. Project current world doOneSubCycle. aLine addCommandFeedback: nil]]! ! !TileMorph methodsFor: 'misc' stamp: 'ct 9/12/2020 14:56'! handReferentMorph "Hand the user the actual morph referred to" | aMorph surrogate | ((aMorph := actualObject costume) isMorph and: [aMorph isWorldMorph not]) ifTrue: [ surrogate := CollapsedMorph collapsedMorphOrNilFor: aMorph. surrogate ifNotNil: [surrogate uncollapseToHand] ifNil: [self currentHand attachMorph: aMorph]].! ! !SymbolListTile methodsFor: 'user interface' stamp: 'ct 9/11/2020 20:22'! choices "Answer the list of current choices for the receiver's symbol" dataType == #ScriptName ifTrue: "Backward compatibility with old tiles" [^ Project current world presenter allKnownUnaryScriptSelectors]. ^ choices! ! !ScriptNameTile methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:29'! choices "Answer the current list of choices" ^ Project current world presenter allKnownUnaryScriptSelectors! ! !TileMorph class methodsFor: '*Etoys-Squeakland-utilities' stamp: 'ct 9/11/2020 20:21'! implicitSelfInTilesChanged "The implicitSelfInTiles preference changed. Caution: although this may appear to have no senders in the image, it is in fact invoked when the implicitSelfInTiles preference is toggled... so please do not delete it." Smalltalk isMorphic ifFalse: [^ self]. Project current world allScriptEditorsInProject do: [:scriptEditor | scriptEditor install]. Project current world allViewersInProject do: [:viewer | viewer enforceImplicitSelf]. " (Preferences buttonForPreference: #implicitSelfInTiles) openInHand. "! ! !TileMorphTest methodsFor: 'testing' stamp: 'ct 9/12/2020 14:56'! testAssignmentTile "self debug: #testAssignmentTile" | player viewer tile phrase | player := Morph new assuredPlayer. viewer := CategoryViewer new invisiblySetPlayer: player. viewer makeSetter: #(#getX #Number) event: nil from: player costume. phrase := self currentHand firstSubmorph. self currentHand removeAllMorphs. tile := phrase submorphs second. self assert: tile codeString = 'setX: '. tile arrowAction: 1. self assert: tile codeString = 'setX: self getX + '.! ! !TypeListTile methodsFor: 'mouse handling' stamp: 'ct 9/12/2020 14:56'! showOptions | topScript | suffixArrow ifNotNil: [(suffixArrow bounds containsPoint: self currentHand cursorPoint) ifTrue: [^ super showOptions]]. topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript handUserParameterTile]! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice at: aPointOrNil "UserDialogBoxMorph confirm: 'Make your choice carefully' withCRs title: 'Do you like chocolate?' trueChoice: 'Oh yessir!!' falseChoice: 'Not so much...'" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: 1; registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponse! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice default: default triggerAfter: seconds at: aPointOrNil "UserDialogBoxMorph confirm: 'I like hot java' title: 'What do you say?' trueChoice: 'You bet!!' falseChoice: 'Nope' default: false triggerAfter: 12 at: 121 at 212" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: (default ifTrue: [1] ifFalse: [2]); registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponseAfter: seconds! ! !UserText methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:56'! keyStroke: evt "Handle a keystroke event." | newSel | super keyStroke: evt. evt hand keyboardFocus == self ifFalse: [self releaseEditor. ^ self]. newSel := self editor selectionInterval. "restore editor state" self refreshParagraph. self editor selectFrom: newSel first to: newSel last. wrapFlag ifFalse: [self fullBounds right > owner right ifTrue: [self wrapFlag: true. self right: owner right. self refreshParagraph. self editor selectFrom: text string size + 1 to: text string size]].! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs fourth topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 0)). aMorph useRoundedCorners; beTransparent; borderWidth: 2; borderColor: (Color r: 1.0 g: 0.548 b: 0.452); lock. aMorph setProperty: #highlight toValue: true. ^ Project current world addMorphFront: aMorph! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addGetterFeedback "Add feedback during mouseover of a getter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: (self firstAlignmentMorph ifNil: [self submorphs last bottomRight] ifNotNil: [:m | m bottomLeft])). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem getterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil. " Color fromUser (Color r: 1.0 g: 0.355 b: 0.839) "! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addSetterFeedback "Add screen feedback showing what would be torn off to make a setter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: self bounds bottomRight). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem setterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !ViewerLine methodsFor: '*Etoys-Squeakland-slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs third topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !Vocabulary class methodsFor: '*Etoys-Squeakland-type vocabularies' stamp: 'ct 9/11/2020 20:19'! typeChoicesForUserVariables "Answer a list of all user-choosable value types for variables." | aList | aList := #(Boolean Color CustomEvents Graphic Number Patch Player Point ScriptName Sound String) copy. self currentWorld isKedamaPresent ifFalse: [ ^ aList copyWithout: #Patch]. ^ aList " Vocabulary typeChoicesForUserVariables "! ! !WorldState methodsFor: 'hands' stamp: 'ct 9/12/2020 15:21'! removeHand: aHandMorph "Remove the given hand from the list of hands for this world." (hands includes: aHandMorph) ifFalse: [^self]. hands := hands copyWithout: aHandMorph. self activeHand == aHandMorph ifTrue: [self activeHand: nil].! ! !WorldState methodsFor: 'stepping' stamp: 'ct 9/12/2020 15:08'! runLocalStepMethodsIn: aWorld "Run morph 'step' methods (LOCAL TO THIS WORLD) whose time has come. Purge any morphs that are no longer in this world. ar 3/13/1999: Remove buggy morphs from the step list so that they don't raise repeated errors." | now morphToStep stepTime | now := Time millisecondClockValue. self activateWorld: aWorld during: [ self triggerAlarmsBefore: now. stepList ifEmpty: [^ self]. (now < lastStepTime or: [now - lastStepTime > 5000]) ifTrue: [ self adjustWakeupTimes: now]. "clock slipped" [stepList notEmpty and: [stepList first scheduledTime < now]] whileTrue: [ lastStepMessage := stepList removeFirst. morphToStep := lastStepMessage receiver. (morphToStep shouldGetStepsFrom: aWorld) ifTrue: [ lastStepMessage value: now. lastStepMessage ifNotNil: [ stepTime := lastStepMessage stepTime ifNil: [morphToStep stepTime]. lastStepMessage scheduledTime: now + (stepTime max: 1). stepList add: lastStepMessage]]. lastStepMessage := nil]. lastStepTime := now].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/12/2020 15:20'! doOneCycleNowFor: aWorld "Immediately do one cycle of the interaction loop. This should not be called directly, but only via doOneCycleFor:" | capturingGesture | DisplayScreen checkForNewScreenSize. capturingGesture := false. "self flag: #bob. " "need to consider remote hands in lower worlds" "process user input events" LastCycleTime := Time millisecondClockValue. self handsDo: [:hand | hand becomeActiveDuring: [ hand processEvents. capturingGesture := capturingGesture or: [hand isCapturingGesturePoints]]]. "the default is the primary hand" self activeHand: self hands first. "The gesture recognizer needs enough points to be accurate. Therefore morph stepping is disabled while capturing points for the recognizer" capturingGesture ifFalse: [ aWorld runStepMethods. "there are currently some variations here" self displayWorldSafely: aWorld].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/12/2020 15:21'! doOneSubCycleFor: aWorld "Like #doOneCycle, but preserves activeHand." ^ self activateHand: self activeHand during: [ self doOneCycleFor: aWorld]! ! !WorldState methodsFor: '*MorphicExtras-update cycle' stamp: 'ct 9/12/2020 15:18'! doOneCycleInBackground "Do one cycle of the interactive loop. This method is called repeatedly when this world is not the active window but is running in the background." self halt. "not ready for prime time" "process user input events, but only for remote hands" self handsDo: [:hand | (hand isKindOf: RemoteHandMorph) ifTrue: [ hand becomeActiveDuring: [ hand processEvents]]]. self runStepMethods. self displayWorldSafely.! ! !ZASMCameraMarkMorph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:02'! setTransition "Set the transition" ^ self setTransition: self currentEvent! ! WorldState removeSelector: #activeHand! WorldState removeSelector: #activeHand:! Flaps class removeSelector: #addLocalFlapTitled:onEdge:! !Browser reorganize! ('*46Deprecated') ('*60Deprecated-multi-window support' classHierarchy) ('*Etoys-Squeakland-class functions' buildClassBrowser) ('*Etoys-Squeakland-drag and drop' overwriteDialogHierarchyChange:higher:sourceClassName:destinationClassName:methodSelector:) ('*Etoys-Squeakland-initialize-release' browserWindowActivated) ('*Etoys-Squeakland-message functions' buildMessageBrowser) ('*SUnitTools-class list functions' testRunTests) ('*SUnitTools-menus' testsClassListMenu: testsSystemCategoryMenu:) ('*SUnitTools-system category functions' hasSystemCategoryWithTestsSelected testRunTestsCategory) ('*services-base' browseReference: classCategoryMenuServices: classListMenuServices: messageCategoryMenuServices: methodReference optionalButtonRow selectReference:) ('accessing' contents contents:notifying: contentsSelection couldBrowseAnyClass doItReceiver editSelection editSelection: environment newClassContents noteSelectionIndex:for: request:initialAnswer: selectEnvironment: spawn: suggestCategoryToSpawnedBrowser:) ('annotation' annotation annotation:) ('class comment pane' annotationForClassCommentFor: annotationForClassDefinitionFor: noCommentNagString stripNaggingAttributeFromComment:) ('class functions' addAllMethodsToCurrentChangeSet classCommentText classDefinitionText classListMenu: classListMenu:shifted: classListMenuMore: copyClass createInstVarAccessors defineClass:notifying: editClass editComment explainSpecial: fileOutClass findMethod hierarchy makeNewSubclass plusButtonHit printOutClass removeClass renameClass shiftedClassListMenu: shiftedClassListMenuMore:) ('class list' classIconAt: classList classListIndex classListIndex: classListIndexOf: classListSingleton createHierarchyTreeOf: defaultClassList flattenHierarchyTree:on:indent: flattenHierarchyTree:on:indent:by: flattenHierarchyTree:on:indent:by:format: hasClassSelected hierarchicalClassList recent selectClass: selectClassNamed: selectedClass selectedClassName) ('code pane' aboutToStyle: compileMessage:notifying: showBytecodes) ('controls' decorateButtons) ('copying' veryDeepInner:) ('drag and drop' dragFromClassList: dragFromMessageList: dropOnMessageCategories:at: dropOnSystemCategories:at: wantsMessageCategoriesDrop: wantsSystemCategoriesDrop:) ('initialize-release' classListFrame: classListFrame:fromLeft:width: classListFrame:fromTop:fromLeft:width: defaultBrowserTitle frameOffsetFromTop:fromLeft:width:bottomFraction: labelString methodCategoryChanged setClass: setClass:selector: setSelector: switchesFrame: switchesFrame:fromLeft:width: systemCatSingletonKey:from: systemOrganizer: topConstantHeightFrame:fromLeft:width:) ('message category functions' addCategory alphabetizeMessageCategories buildMessageCategoryBrowser buildMessageCategoryBrowserEditString: canShowMultipleMessageCategories categoryOfCurrentMethod changeMessageCategories: editMessageCategories fileOutMessageCategories highlightMessageList:with: mainMessageCategoryMenu: messageCategoryMenu: printOutMessageCategories removeEmptyCategories removeMessageCategory renameCategory showHomeCategory) ('message category list' categorizeAllUncategorizedMethods hasMessageCategorySelected messageCatListSingleton messageCategoryList messageCategoryListIndex messageCategoryListIndex: messageCategoryListKey:from: messageCategoryListSelection rawMessageCategoryList recategorizeMethodSelector: selectMessageCategoryNamed: selectedMessageCategoryName setOriginalCategoryIndexForCurrentMethod toggleCategorySelectionForCurrentMethod) ('message functions' browseAllCommentsForClass defineMessageFrom:notifying: inspectInstances inspectSubInstances mainMessageListMenu: removeMessage removeMessageFromBrowser) ('message list' addExtraShiftedItemsTo: hasMessageSelected lastMessageName messageHelpAt: messageIconAt: messageIconFor: messageIconHelpFor: messageList messageListIndex messageListIndex: messageListIndexOf: messageListMenu:shifted: reformulateList selectMessageNamed: selectedMessage selectedMessageName selectedMessageName: shiftedMessageListMenu:) ('metaclass' classCommentIndicated classDefinitionIndicated classMessagesIndicated classOrMetaClassOrganizer indicateClassMessages indicateInstanceMessages instanceMessagesIndicated metaClassIndicated metaClassIndicated: selectedClassOrMetaClass selectedClassOrMetaClassName setClassDefinition setClassOrganizer) ('multi-window support' arrowKey:from: browseClassHierarchy isHierarchy isPackage multiWindowName multiWindowNameForState: okToClose restoreMultiWindowState: restoreToCategory:className:protocol:selector:mode:meta: saveMultiWindowState) ('pluggable menus - hooks' classListMenuHook:shifted: messageCategoryMenuHook:shifted: messageListMenuHook:shifted: systemCategoryMenuHook:shifted:) ('self-updating' didCodeChangeElsewhere) ('system category functions' addSystemCategory alphabetizeSystemCategories browseAllClasses buildSystemCategoryBrowser buildSystemCategoryBrowserEditString: changeSystemCategories: classNotFound editSystemCategories fileOutSystemCategory findClass mainSystemCategoryMenu: printOutSystemCategory removeSystemCategory renameSystemCategory systemCatSingletonMenu: systemCategoryMenu: updateSystemCategories) ('system category list' hasSystemCategorySelected indexIsOne indexIsOne: selectCategoryForClass: selectSystemCategory: selectedEnvironment selectedSystemCategory selectedSystemCategoryName systemCatListKey:from: systemCategoryList systemCategoryListIndex systemCategoryListIndex: systemCategorySingleton) ('toolbuilder' buildAndOpenCategoryBrowser buildAndOpenCategoryBrowserLabel: buildAndOpenClassBrowserLabel: buildAndOpenFullBrowser buildAndOpenMessageCategoryBrowserLabel: buildCategoryBrowserWith: buildClassListSingletonWith: buildClassListWith: buildDefaultBrowserWith: buildMessageCategoryListWith: buildMessageListCatSingletonWith: buildMessageListWith: buildSwitchesWith: buildSystemCatListSingletonWith: buildSystemCategoryListWith: buildWith: setMultiWindowFor:) ('traits' addSpecialMenu: addTrait defineTrait:notifying: newClass newTrait) ('user interface' addModelItemsToWindowMenu: defaultWindowColor) ('private' spawnOrNavigateTo:) ! Object removeSelector: #setActiveWorld:during:! Smalltalk removeClassNamed: #FileContentsBrowserTestTestObject! > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sat Sep 12 18:52:27 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 12 Sep 2020 18:52:27 0000 Subject: [squeak-dev] The Inbox: System-dtl.1170.mcz Message-ID: A new version of System was added to project The Inbox: http://source.squeak.org/inbox/System-dtl.1170.mcz ==================== Summary ==================== Name: System-dtl.1170 Author: dtl Time: 12 September 2020, 2:52:23.835083 pm UUID: cdd6e922-e25e-433b-a516-c503d0a772a8 Ancestors: System-mt.1169 End of the #World as we know it. Package postscript only. About two years ago we eliminated all dependencies on global World. However, the global binding has remained functional on the theory that some external packages might still expect it. It is time to get rid of the binding now because: - It gives the false impressing that the World is still in use as a global - If a package is loaded that does need World, then "Smalltalk at: #World put: Project current world" will restore prior behavior. =============== Diff against System-mt.1169 =============== Item was changed: + (PackageInfo named: 'System') postscript: '"Global World is functional but no longer used in the image. Remove it now. If any package is loaded that requires the obsolete global binding, the prior behavior may be restored with: + + Smalltalk at: #World put: Project current world + " + + Smalltalk globals unbind: #World. + '! - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! From lewis at mail.msen.com Sat Sep 12 19:10:59 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 12 Sep 2020 15:10:59 -0400 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> Message-ID: <20200912191059.GA48621@shell.msen.com> On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > Glad you like it, David! :-) > > > > > In the case of the global World variable, we were able to make it an instance variable in Project. > > I heard of this, but where can you see the difference? If I evaluate "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World is still listed in my Smalltalk bindings. I just put System-dtl.1170 in the inbox to make it go away. For a long time, the World variable was shared all over the place, even in MVC if you can believe that. From a modularity perspective it was a real mess. Basically, the solution was to make it an instance variable in Project. The current project always has a world, and when transitioning from one project to another, you can do the handoff between those two worlds without referencing a global. All that got done about two years ago, but I intentionally left the #World binding in place on the theory that there might be external packages (or Etoys projects) that expect to have access to global World. The way it works now (see MorphicProject>>setWorld:) is that the global World will be updated as before, but this happens if and only if the global binding exists. If you remove the binding, the World no longer exists. If you load a package or project that does have references to World, then restoring the #World binding in the current environment should make it work again. I don't really know if it's a good idea to remove the #World binding now (that's why it's in the inbox). But leaving it in the image is confusing because it gives the wrong impression that it is still being used. Dave From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 19:19:00 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 19:19:00 +0000 Subject: [squeak-dev] Squeak needs a third-party package updater Message-ID: Hi all, maybe I am missing some central Squeak tool, but I have never been happy with the current way to update all installed third-party packages in Squeak - because I don't know anyone. Please allow me to give you a short outline of the tool I would like to use for updating packages: There should be a simple button in the Squeak Menu, just below "Update image", named "Update packages", that enables you to install the latest versions of all installed packages for which a remote does exist. In addition, if I made any change to a package, it should be possible to merge the updates into my working copy. In particular, this does not only include Squeaksource packages but also packages from GitHub, BitBucket, etc. Great packages such as Autocompletion, MethodWrappers, Vivide, or Squot (see below) that are all hosted on GitHub and can be installed using a Metacello script. So how much of this is reality and how much is fiction today? For packages installed from Squeaksource, I can open them in the Monticello Browser and load or merge the latest version. At least, this works, but we could need a button to do this for all available packages where a "default remote" is selected. But for packages managed via git, this is not possible at all! The Monticello Browser does not know their remote repositories that were used to install the packages originally. Without extra settings made per repository baseline, you cannot even re-load a package again via the install script, see also this open issue: https://github.com/Metacello/metacello/issues/513 Meanwhile, I have made a habit of cloning all interesting projects via Squot in order to update them, even if I do not plan to commit to them ... I believe there is also something like SqueakMap which has an "Update" and an "Upgrade" button, but this tool is only made for accessing the Squeaksource, is this correct? From what I can see, Squeaksource and SqueakMap have become less important compared to GitHub (the reasons might include better visibility/explorability of projects, a more efficient VCS, and features like issues/pull requests; but that's probably not the point here). What do you think? Am I the only one facing this issue? How would a roadmap for such a tool look like? Looking forward to your ideas! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 12 19:19:37 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 12 Sep 2020 12:19:37 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> Message-ID: Hi Tobias, On Sat, Sep 12, 2020 at 9:56 AM Tobias Pape wrote: > > > On 12.09.2020, at 16:31, Eliot Miranda wrote: > > > > With the REPL image if it starts up and evaluates an expression of two > (which are not logged) and then quits nothing has changed but the QUIT/NO > SAVE has been written, and now my reproducible image/changes pair is no > longer in exactly the same state; the changes file has grown. > > > > In that particular case, wouldn't it suffice to just make the Changes file > read-only? > No. Sometimes one wants to file-in code into the REPL. > Best regards > -Tobias > _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 12 19:22:23 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 12 Sep 2020 12:22:23 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <20200912174317.GB35925@shell.msen.com> References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> <20200912174317.GB35925@shell.msen.com> Message-ID: On Sat, Sep 12, 2020 at 10:43 AM David T. Lewis wrote: > On Sat, Sep 12, 2020 at 06:57:03PM +0200, Tobias Pape wrote: > > > > > On 12.09.2020, at 16:31, Eliot Miranda > wrote: > > > > > > With the REPL image if it starts up and evaluates an expression of two > (which are not logged) and then quits nothing has changed but the QUIT/NO > SAVE has been written, and now my reproducible image/changes pair is no > longer in exactly the same state; the changes file has grown. > > > > > > > In that particular case, wouldn't it suffice to just make the Changes > file read-only? > > > > Just exit with Smalltalk quitPrimitive. It's perfectly safe on unix. > That's hardly the point. The REPL should work on any platform with a command line, Windows included. And e.g. LibTerm.app even gives me a command line on my iPhone ;-) And it *won't* safe in a system a little more advanced than we have now where ephemerons are used for output streams and we want them to be automatically flushed on exit. It is possible to get this right. Dave > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 12 19:26:13 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 12 Sep 2020 12:26:13 -0700 Subject: [squeak-dev] Squeak needs a third-party package updater In-Reply-To: References: Message-ID: Hi Christoph, On Sat, Sep 12, 2020 at 12:19 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi all, > > > maybe I am missing some central Squeak tool, but I have never been > happy with the current way to update all installed third-party packages in > Squeak - because I don't know anyone. Please allow me to give you a short > outline of the tool I would like to use for updating packages: > > > There should be a simple button in the Squeak Menu, just below "Update > image", named "Update packages", that enables you to install the latest > versions of all installed packages for which a remote does exist. In > addition, if I made any change to a package, it should be possible to > merge the updates into my working copy. In particular, this does not only > include Squeaksource packages but also packages from GitHub, BitBucket, > etc. Great packages such as Autocompletion, MethodWrappers, Vivide, or > Squot (see below) that are all hosted on GitHub and can be installed using > a Metacello script. > > So how much of this is reality and how much is fiction today? > For packages installed from Squeaksource, I can open them in the > Monticello Browser and load or merge the latest version. At least, this > works, but we could need a button to do this for all available packages > where a "default remote" is selected. > But for packages managed via git, this is not possible at all! The > Monticello Browser does not know their remote repositories that were used > to install the packages originally. Without extra settings made per > repository baseline, you cannot even re-load a package again via the > install script, see also this open issue: > https://github.com/Metacello/metacello/issues/513 Meanwhile, I have made > a habit of cloning all interesting projects via Squot in order to update > them, even if I do not plan to commit to them ... > > I believe there is also something like SqueakMap which has an "Update" and > an "Upgrade" button, but this tool is only made for accessing the > Squeaksource, is this correct? From what I can see, Squeaksource and > SqueakMap have become less important compared to GitHub (the reasons > might include better visibility/explorability of projects, a more efficient > VCS, and features like issues/pull requests; but that's probably not the > point here). > > What do you think? Am I the only one facing this issue? How would a > roadmap for such a tool look like? Looking forward to your ideas! :-) > I've been wanting something like this for the VMMaker development system too. And for FFI. Any system builder who uses Squeak as a base, who has on-going development, could benefit from such a system. And us making it easy to develop is I think a great idea. So keep going, This is an excellent direction. I would also extend the idea with being able to upgrade the VM and dlls within it, but for now that should be on the back burner. Getting image-level support for update of non-trunk packages is the mosyt important thing. Thank you. Best, > Christoph > _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Sat Sep 12 19:29:11 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sat, 12 Sep 2020 21:29:11 +0200 (CEST) Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <953da6739f364d0388e5659c97299396@student.hpi.uni-potsdam.de> References: <953da6739f364d0388e5659c97299396@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, Your description made me think that you created ProcessLocalVariables. Instead you reimplemented their logic. Why? Levente On Sat, 12 Sep 2020, Thiede, Christoph wrote: > > Hi all, > > > recent discussions have shown just another time that in spite of its overall modular and object-oriented design, the Morphic System still incorporates a number of global state variables that impede modular processes in some > situations. For instance, running or even debugging any form of UI simulation code in a background process was likely to cause problems because, via the global state variables, two planned-to-be-independent > projects undesirably shared their events, hands, and worlds. Concrete systems suffering from this global state include various UI tests executed using AutoTDD [1], or the screenshot generation framework for Squeak by Example > [2] which I had the joy to co-develop. > > > The attached changeset tackles these issues for all packages in the Trunk by wrapping the following three globals into process-local accessors: ActiveEvent, ActiveHand, and ActiveWorld. > > As the changeset contains patches of over 300 selectors in more than 100 classes every single line of which you probably will not feel like reading in detail, here is a summary of all changes I applied: > > * Added #activeEvent[:], #activeHand[:], and #activeWorld[:] process-local accessors on Object as Morphic-Kernel extensions. The actual values are stored directly on the active process in the manner of > a ProcessSpecificVariable. For backward compatibility, the global variables are still kept up to date here. > * Added #activateHand:during: and #activateWorld:during: as dynamic scope setters on Object as Morphic-Kernel extensions. > * Replaced all references to ActiveEvent, ActiveHand, and ActiveWorld by "self activeEvent", "self activeHand", and "self activeWorld" accordingly. I also spent some time reflecting in which cases you actually would like to > receive a possible nil value and ended up with changing the most senders that are not involved into the critical event processing logic into their "#current*" equivalents (#currentEvent, #currentHand, and #currentWorld) > which already guarantee to return non-nil values. In the case of #currentWorld, I also replaced many senders with "Project current world" that were not invoked in an event-related context. > * While skimming over all the implementations, I also applied a number of really minor refactorings: improve multilingual support by adding some "#translated"s to user strings, remove nil checks that could never be reached, > and reformat some of the very hardest to read methods I came across. > > > Please review! I'm looking forward to eliminating these unnecessary artifacts of global state and making Squeak an even more purely object-oriented and modular system by merging these changes into the Trunk. > > > Best, > > Christoph > > > [1] https://github.com/hpi-swa-teaching/AutoTDD > > [2] https://github.com/codeZeilen/SqueakByExample-english/ > > > From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 12 19:32:17 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 12 Sep 2020 19:32:17 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <20200912191059.GA48621@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de>, <20200912191059.GA48621@shell.msen.com> Message-ID: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> Hi David, I think I understand the dilemma: If we keep #World in the bindings, it will keep being used (I have to admit that, for personal scripts, I'm doing so, too). If we remove it, we don't provide backward compatibility for older projects. This would be really a pity because even if you write in your inbox version: "If a package is loaded that does need World, then 'Smalltalk at: #World put: Project current world' will restore prior behavior.", how many people will know that when they attempt to refloat an ancient piece of Squeak code? As far as we only have these two options, I'm tending to vote for the first one. Unless we use it in the Trunk, it does no harm, and actual bugs should occur rather seldom because some third-party package is still using #World. If there should be a bug somewhere, it can be easily fixed. But hypothetically, there would be a third option, which would be my favorite: Handle accesses to #World like calls on a deprecated method, i.e. by signaling a DeprecationWarning. That way we could even make sure that novices do not learn to use a deprecated variable. This could be realized by wrapping the value of #World into an ObjectTracer or a similar class that raises a DeprecationWarning on every call that is made to it. But I fear this could be a piece of overengineering, what do you think? Also, this would not protect the global variable from being reassigned ... The very last solution could be to make a (global) list of deprecated bindings and raise a Warning from the Compiler when an attempt is made to access one of them. Unfortunately, this would also be the most invasive change. @Levente: It seemed kind of overengineering to me to create three subclasses of ProcessLocalVariable for this purpose. Or am I misunderstand their concept? Maybe we could also need something like a PluggableProcessLocalVariable that can be instantiated with two accessor blocks? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Samstag, 12. September 2020 21:10:59 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > Glad you like it, David! :-) > > > > > In the case of the global World variable, we were able to make it an instance variable in Project. > > I heard of this, but where can you see the difference? If I evaluate "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World is still listed in my Smalltalk bindings. I just put System-dtl.1170 in the inbox to make it go away. For a long time, the World variable was shared all over the place, even in MVC if you can believe that. From a modularity perspective it was a real mess. Basically, the solution was to make it an instance variable in Project. The current project always has a world, and when transitioning from one project to another, you can do the handoff between those two worlds without referencing a global. All that got done about two years ago, but I intentionally left the #World binding in place on the theory that there might be external packages (or Etoys projects) that expect to have access to global World. The way it works now (see MorphicProject>>setWorld:) is that the global World will be updated as before, but this happens if and only if the global binding exists. If you remove the binding, the World no longer exists. If you load a package or project that does have references to World, then restoring the #World binding in the current environment should make it work again. I don't really know if it's a good idea to remove the #World binding now (that's why it's in the inbox). But leaving it in the image is confusing because it gives the wrong impression that it is still being used. Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Sat Sep 12 19:48:22 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 12 Sep 2020 15:48:22 -0400 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> <20200912174317.GB35925@shell.msen.com> Message-ID: <20200912194822.GA56261@shell.msen.com> On Sat, Sep 12, 2020 at 12:22:23PM -0700, Eliot Miranda wrote: > On Sat, Sep 12, 2020 at 10:43 AM David T. Lewis wrote: > > > On Sat, Sep 12, 2020 at 06:57:03PM +0200, Tobias Pape wrote: > > > > > > > On 12.09.2020, at 16:31, Eliot Miranda > > wrote: > > > > > > > > With the REPL image if it starts up and evaluates an expression of two > > (which are not logged) and then quits nothing has changed but the QUIT/NO > > SAVE has been written, and now my reproducible image/changes pair is no > > longer in exactly the same state; the changes file has grown. > > > > > > > > > > In that particular case, wouldn't it suffice to just make the Changes > > file read-only? > > > > > > > Just exit with Smalltalk quitPrimitive. It's perfectly safe on unix. > > > > That's hardly the point. The REPL should work on any platform with a > command line, Windows included. And e.g. LibTerm.app even gives me a > command line on my iPhone ;-) > And it *won't* safe in a system a little more advanced than we have now > where ephemerons are used for output streams and we want them to be > automatically flushed on exit. > > > It is possible to get this right. No disagreement on any of that :-) Dave > > Dave > > > > > -- > _,,,^..^,,,_ > best, Eliot > From lewis at mail.msen.com Sat Sep 12 19:58:43 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 12 Sep 2020 15:58:43 -0400 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: <20200911215416.GA42161@shell.msen.com> Message-ID: <20200912195843.GB56261@shell.msen.com> Thanks Tim, I've been digging through version control history, and I think what I see is that the assumption of prim 94 matching prim 135 was valid through the life of the interpreter VM (squeakvm.org SVN sources) but that it had changed for Windows as of the public release of Cog. I don't see anything wrong with the Windows VM implementation (aside from the one bug that Christoph fixed), instead I think we may just be dealing with a holdover assumption that is no longer valid. I'm inclined to think we should see if we can make the prim 135 dependence go away in the image. Some other way of synthesizing event time stamps maybe? Dave On Fri, Sep 11, 2020 at 03:45:35PM -0700, tim Rowledge wrote: > I think the key thing is that the answers from the primitive 135 must be aligned with the tick values obtained from primitive 94. > > Of course if prim 94 is properly functional (all elements of the event array are correctly filled in) then prim 135 should never be needed; it was created for those machines that waaaaay back when I added the input events still couldn't provide such data. I'm not entirely sure there actually were any.... > > The only usage of prim 135 not related directly to events seems to be SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods that looks a bit iffy. > > EventSensor>>#createMouseEvent is only used in a couple of places to do with rectangle transforming interactively, and probably ought to be improved. > EventSensor>>#nextEventSynthesized is only used if the EventSensor has no event queue and that is ... a bit complex but probably should never happen since we removed the old InputSensor years and years ago. > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe we should drop the fake-it code and really insist on the prim being there? > MorphicEvent>>#timeStamp looks like a probably redundant backup? > MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to copy the time stamp from the receiver? > > > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > There are no stupid questions. But, there are a lot of inquisitive idiots. > > > From asqueaker at gmail.com Sat Sep 12 20:08:56 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sat, 12 Sep 2020 15:08:56 -0500 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: Message-ID: Hi Eliot, >> Hi Eliot, may I recommend the "flat-line your image" hot key? It's >> really useful for that and other use cases like debugging shutdown >> code, or when Morphic gets slammed with events due to a bug that >> disrupts the UI. It calls Smalltalk quitPrimitive, so skips all >> shutdown processing and logging. It's one of the global command keys, >> Cmd+Shift+_. > > Thanks. However quitting via quitPrimitive is a bad idea. Soon enough we will have a system with ephemerons around files and could expect file vuffers etc to be flushed before exit. So we will want to run pending finalization actions, etc. exiting via the quit primitive without running these activities could well break some applications. It's a bad idea as normal course, yes, but a godsend when all you want to do is stop the process immediately before system memory fills up (e.g., in a stack-recursion bug scenario). #quitPrimitive has also been used for Magma's db-recovery test case since forever. It was the easiest way to simulate the reality that processes can and do get killed at inopportune times, and allowed me to place it at exactly the worst place possible for Magma (e.g., write of a commit, but prior to flush). It may not run pending finalizations, but I do believe I observed #quitPrimitive properly closing open file handles (or, maybe the VM?). The point is, #quitPrimitive does have a couple of use cases, including even with its dangerous aspects. >> With those two easily-accessible paths to a non-logging exit for the >> user, I hope we won't rush into changing the logging of >> exit-without-save. That's a pretty legacy feature that I do use >> occasionally to help me keep track of which images I'm looking at, >> without wanting to change their .image file timestamp. > > OK, I abandon the idea. Forget I said anything. Your call. There's a lot of ground between not rushing and full abandonment. I simply wanted a moment to be able to think about it. Not logging when there is no change sounds reasonable at first blush, but there are also Squeak systems that run a fixed image that don't save on exit -- it may be useful for operators of those systems to be able to know when there were interactions with the system, regardless whether development causing .changes changes was done or not. >> That SoundPlugin one does sound like one to optimize, but for the >> Form, I think you want that processing because it's about what bits >> you want to record in the image file, regardless whether continuing >> the session. > > No, the Form thing is compressing forms so they take less space in the image file. A complete waste of time if one is merely quitting. Yes, you're right. I was thinking about save+exit vs. save+continue, but you're referring to quit+no save. - Chris From asqueaker at gmail.com Sat Sep 12 20:50:45 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sat, 12 Sep 2020 15:50:45 -0500 Subject: [squeak-dev] The Inbox: System-dtl.1170.mcz In-Reply-To: References: Message-ID: LOL... that first sentence... although as from the users' perspective, the global is going away, so it might be better without the hashtag... lol On Sat, Sep 12, 2020 at 1:52 PM wrote: > > A new version of System was added to project The Inbox: > http://source.squeak.org/inbox/System-dtl.1170.mcz > > ==================== Summary ==================== > > Name: System-dtl.1170 > Author: dtl > Time: 12 September 2020, 2:52:23.835083 pm > UUID: cdd6e922-e25e-433b-a516-c503d0a772a8 > Ancestors: System-mt.1169 > > End of the #World as we know it. Package postscript only. About two years ago we eliminated all dependencies on global World. However, the global binding has remained functional on the theory that some external packages might still expect it. > > It is time to get rid of the binding now because: > - It gives the false impressing that the World is still in use as a global > - If a package is loaded that does need World, then "Smalltalk at: #World put: Project current world" will restore prior behavior. > > =============== Diff against System-mt.1169 =============== > > Item was changed: > + (PackageInfo named: 'System') postscript: '"Global World is functional but no longer used in the image. Remove it now. If any package is loaded that requires the obsolete global binding, the prior behavior may be restored with: > + > + Smalltalk at: #World put: Project current world > + " > + > + Smalltalk globals unbind: #World. > + '! > - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! > > From vanessa at codefrau.net Sat Sep 12 21:16:18 2020 From: vanessa at codefrau.net (Vanessa Freudenberg) Date: Sat, 12 Sep 2020 14:16:18 -0700 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> Message-ID: On Fri, Sep 11, 2020 at 8:11 AM Tony Garnock-Jones < tonyg at leastfixedpoint.com> wrote: > Hi Vanessa, > > On 9/10/20 10:23 PM, Vanessa Freudenberg wrote: > > Oh, exciting! Please record a multi-hand Etoys demo. Maybe like this: > > https://youtu.be/gYrp31fH-Jk?t=56 > > Whoa, awesome! I didn't know this existed! > > I have questions! > > - How much of that shift-to-get-other-colour-buttons support is still > in the image? So far I haven't touched the default world menu bar, which > is mildly inappropriate for small and/or multitouch devices. > I never cleaned it up enough to make it into an official release. I *may* have sent a changeset to some mailing list but my memory is really hazy. A lot has happened in the past 10 years 😅 ... (searching) ... Oh, found it, here, from 2010: http://lists.squeakfoundation.org/pipermail/vm-dev/2010-September/005364.html And from Frank4iPad (2011), see attached two changesets. - What other ideas about mapping multitouch to Morphic interaction are > out there I have missed and am in danger of reinventing? > > Also, I am intrigued by the idea of simply having each touch be mapped > to a Hand. It seems like the obvious way to do things, on one level, but > doesn't quite fit my fuzzy intuitions for how (multi-finger) gesture > recognition might work on another level. So: > Given that Morphic is one of the very few UI environments that supports multiple pointers out-of-the-box, mapping each finger to a hand seemed like the obvious thing to do to me. Also, I had done it before - probably like 15 years ago I added Wacom tablet support to the unix vm, and in Morphic that got turned into a separate hand for each device (stylus, puck, mouse). The stylus "hand" even showed the stylus' rotation and tilt using a line and its shadow. So for multi-touch I just did TSTTCPW and it did indeed work. But it never went anywhere, partly because of Apple's refusal to let Squeak into the app store, though mostly because my focus shifted elsewhere. Also, nobody else back in the day seemed interested enough to build on what I had shared. Adding multi-touch + gestures to SqueakJS is on my idea list but I have not found the time yet. What I did implement is VM-side gestures for panning / zooming and right click (all using 2 fingers). Would love to have morph scale and rotation gestures too but that would require image-side support. However, with those VM gestures, SqueakJS is now quite usable on iPhones / iPads. I do like the big "Cmd" button for two-handed interaction, and that could be implemented either in the image (see touchCmdMorph attached) or in the VM. For SqueakJS I'd do it in the VM because one goal for SqueakJS is to be able to run as many unmodified images as possible. But that means until I get around to implement it, there is no way to do "Cmd-p" using the keyboard like you saw in the iPad video. I can only invoke print-it using the menu. In SqueakJS there is an explicit button for showing / hiding the keyboard. I always planned to add IMM plugin support to automate showing/hiding the keyboard but never got around to do it. Another thought was to invoke the character recognizer that is still in the image. At some point its cmd-r key binding got re-used though, so I don't think there is a way to invoke it now. - When you were working on this, did you have any thoughts about > deficiencies of the Morphic model wrt multitouch/gestures/etc., and then > did you have any ideas about what to change and how to change it? > If I did, I don't recall those thoughts now. Agreeing on a multi-touch event format for VM events would be a good step forward—the one John McIntosh did for the iOS VM is pretty much a 1-to-1 mapping of the iOS API. I'd prefer something more platform-neutral, like mimicking the Web's Pointer Event API https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events (or the Touch Events API, but I like the unified API for all kinds of pointing devices). I'm excited you're working on this! - Vanessa - -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 2596touchCmdMorph-bf.cs Type: text/x-csharp Size: 1633 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 2595touchEvents-bf.cs Type: text/x-csharp Size: 10889 bytes Desc: not available URL: From vanessa at codefrau.net Sat Sep 12 21:25:24 2020 From: vanessa at codefrau.net (Vanessa Freudenberg) Date: Sat, 12 Sep 2020 14:25:24 -0700 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> Message-ID: Ha, talk about a trip down memory lane! I had no idea that still existed :) - Vanessa - On Sat, Sep 12, 2020 at 5:42 AM karl ramberg wrote: > There is also the Frank stuff which probably have some of the same multi > cursor funktionality: > http://tinlizzie.org/~bert/frank4ipad/ > > One can download the Frank.ipa and unzip to look at the image. > > > Best, > Karl > > > > On Fri, Sep 11, 2020 at 5:04 PM Tony Garnock-Jones < > tonyg at leastfixedpoint.com> wrote: > >> Hi Vanessa, >> >> On 9/10/20 10:23 PM, Vanessa Freudenberg wrote: >> > Oh, exciting! Please record a multi-hand Etoys demo. Maybe like this: >> > https://youtu.be/gYrp31fH-Jk?t=56 >> >> Whoa, awesome! I didn't know this existed! >> >> I have questions! >> >> - How much of that shift-to-get-other-colour-buttons support is still >> in the image? So far I haven't touched the default world menu bar, which >> is mildly inappropriate for small and/or multitouch devices. >> >> - What other ideas about mapping multitouch to Morphic interaction are >> out there I have missed and am in danger of reinventing? >> >> Also, I am intrigued by the idea of simply having each touch be mapped >> to a Hand. It seems like the obvious way to do things, on one level, but >> doesn't quite fit my fuzzy intuitions for how (multi-finger) gesture >> recognition might work on another level. So: >> >> - When you were working on this, did you have any thoughts about >> deficiencies of the Morphic model wrt multitouch/gestures/etc., and then >> did you have any ideas about what to change and how to change it? >> >> I'll try to cook up a little demo like that sometime soon. >> >> Tony >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Sat Sep 12 22:03:19 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sat, 12 Sep 2020 17:03:19 -0500 Subject: [squeak-dev] Squeak needs a third-party package updater In-Reply-To: References: Message-ID: Hi Christoph, A solution made several years ago is still around today, including for FFI and VMMaker. Simply find the package in the SqueakMap browser, expand the tree, select the "head" version, yellow-click "install". [image: image.png] It will do a *merge*, starting with the lowest level dependent packages. How to do this using MCConfigurations is documented here, under the heading, *Guidelines for writing the script for the head release.* http://wiki.squeak.org/squeak/6182 I know this is not quite "all" packages, but I do see a "Upgrade all" few choices lower which may do what you want or, if not, could trivially be made to. The important thing is, you get to maintain the finer-grained control of being able to load at any node point in the package hierarchy structure (including lower-level packages). This hierarchy was defined for many packages as an upgrade to Installer several years ago. The upgrade solves your use-case and several others including for configuration and deployment. If you select "Edit Release" on that (head) selection for FFI, you can see it executes just a one-line workspace: Installer new merge: #ffi That feature is documented here: http://wiki.squeak.org/squeak/6366 But, as you mentioned, none of this is git, so this has limited the interest from the community. I'm most interested in interacting with git via the GraphQL API, but when I went to study the schema, I was shocked to see it had hundreds upon hundreds of types, so I didn't have time to try to make a tidy client. HTH, Chris On Sat, Sep 12, 2020 at 2:26 PM Eliot Miranda wrote: > Hi Christoph, > > On Sat, Sep 12, 2020 at 12:19 PM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Hi all, >> >> >> maybe I am missing some central Squeak tool, but I have never been >> happy with the current way to update all installed third-party packages in >> Squeak - because I don't know anyone. Please allow me to give you a short >> outline of the tool I would like to use for updating packages: >> >> >> There should be a simple button in the Squeak Menu, just below "Update >> image", named "Update packages", that enables you to install the latest >> versions of all installed packages for which a remote does exist. In >> addition, if I made any change to a package, it should be possible to >> merge the updates into my working copy. In particular, this does not >> only include Squeaksource packages but also packages from GitHub, >> BitBucket, etc. Great packages such as Autocompletion, MethodWrappers, >> Vivide, or Squot (see below) that are all hosted on GitHub and can be >> installed using a Metacello script. >> >> So how much of this is reality and how much is fiction today? >> For packages installed from Squeaksource, I can open them in the >> Monticello Browser and load or merge the latest version. At least, this >> works, but we could need a button to do this for all available packages >> where a "default remote" is selected. >> But for packages managed via git, this is not possible at all! The >> Monticello Browser does not know their remote repositories that were used >> to install the packages originally. Without extra settings made per >> repository baseline, you cannot even re-load a package again via the >> install script, see also this open issue: >> https://github.com/Metacello/metacello/issues/513 Meanwhile, I have made >> a habit of cloning all interesting projects via Squot in order to update >> them, even if I do not plan to commit to them ... >> >> I believe there is also something like SqueakMap which has an "Update" >> and an "Upgrade" button, but this tool is only made for accessing the >> Squeaksource, is this correct? From what I can see, Squeaksource and >> SqueakMap have become less important compared to GitHub (the reasons >> might include better visibility/explorability of projects, a more efficient >> VCS, and features like issues/pull requests; but that's probably not the >> point here). >> >> What do you think? Am I the only one facing this issue? How would a >> roadmap for such a tool look like? Looking forward to your ideas! :-) >> > > I've been wanting something like this for the VMMaker development system > too. And for FFI. Any system builder who uses Squeak as a base, who has > on-going development, could benefit from such a system. And us making it > easy to develop is I think a great idea. So keep going, This is an > excellent direction. > > I would also extend the idea with being able to upgrade the VM and dlls > within it, but for now that should be on the back burner. Getting > image-level support for update of non-trunk packages is the mosyt important > thing. Thank you. > > Best, >> Christoph >> > > _,,,^..^,,,_ > best, Eliot > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 60106 bytes Desc: not available URL: From tim at rowledge.org Sat Sep 12 22:14:41 2020 From: tim at rowledge.org (tim Rowledge) Date: Sat, 12 Sep 2020 15:14:41 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: Message-ID: <98A66D57-509D-4B7E-9C02-435FF1BE13AF@rowledge.org> Ah, I was wondering what purpose you had in mind... > On 2020-09-12, at 1:08 PM, Chris Muller wrote: > > Not logging when there is no change sounds reasonable at first blush, > but there are also Squeak systems that run a fixed image that don't > save on exit -- it may be useful for operators of those systems to be > able to know when there were interactions with the system, regardless > whether development causing .changes changes was done or not. Since an image set up that way is already going to be fairly heavily configured, would it not be likely that there is some added logging (I have been using the Squeaksource Toothpick package for a while now with considerable success) that would be a better target? Indeed a simple extension to the ToothPick stuff could add some of the logging to the changes file if wanted, offering the best of both worlds. One could even consider making such a system part of the image so that changes logging is simply a particular case, and allowing even that to be logged in some way other than simply writing to a local file. It might make for an interesting option for a cluster of image to write to a single logging target, for example. You could process that info and reply to alert a developer that someone else has recently touched the same code; or use ReallyCoolAI(™) to analyse it and suggest improvements - always a popular option :-) tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Useful random insult:- Ignorant, and proud of it. From asqueaker at gmail.com Sat Sep 12 22:22:22 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sat, 12 Sep 2020 17:22:22 -0500 Subject: [squeak-dev] Squeak needs a third-party package updater In-Reply-To: References: Message-ID: Just to be clear, SqueakMap is not a VCS but a portal. Since it's just a database of Smalltalk scripts, it works as a portal to configure via anything -- Monticello, Git, Bitbucket, SVN, CVS, ENVY, whether a development workstation or end-user app. It's meant to be the place that answers the question, "How do I load XYZ package?" - Chris On Sat, Sep 12, 2020 at 2:19 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi all, > > > maybe I am missing some central Squeak tool, but I have never been > happy with the current way to update all installed third-party packages in > Squeak - because I don't know anyone. Please allow me to give you a short > outline of the tool I would like to use for updating packages: > > > There should be a simple button in the Squeak Menu, just below "Update > image", named "Update packages", that enables you to install the latest > versions of all installed packages for which a remote does exist. In > addition, if I made any change to a package, it should be possible to > merge the updates into my working copy. In particular, this does not only > include Squeaksource packages but also packages from GitHub, BitBucket, > etc. Great packages such as Autocompletion, MethodWrappers, Vivide, or > Squot (see below) that are all hosted on GitHub and can be installed using > a Metacello script. > > So how much of this is reality and how much is fiction today? > For packages installed from Squeaksource, I can open them in the > Monticello Browser and load or merge the latest version. At least, this > works, but we could need a button to do this for all available packages > where a "default remote" is selected. > But for packages managed via git, this is not possible at all! The > Monticello Browser does not know their remote repositories that were used > to install the packages originally. Without extra settings made per > repository baseline, you cannot even re-load a package again via the > install script, see also this open issue: > https://github.com/Metacello/metacello/issues/513 Meanwhile, I have made > a habit of cloning all interesting projects via Squot in order to update > them, even if I do not plan to commit to them ... > > I believe there is also something like SqueakMap which has an "Update" and > an "Upgrade" button, but this tool is only made for accessing the > Squeaksource, is this correct? From what I can see, Squeaksource and > SqueakMap have become less important compared to GitHub (the reasons > might include better visibility/explorability of projects, a more efficient > VCS, and features like issues/pull requests; but that's probably not the > point here). > > What do you think? Am I the only one facing this issue? How would a > roadmap for such a tool look like? Looking forward to your ideas! :-) > > Best, > Christoph > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Sat Sep 12 22:27:21 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 12 Sep 2020 18:27:21 -0400 Subject: [squeak-dev] The Inbox: System-dtl.1170.mcz In-Reply-To: References: Message-ID: <20200912222721.GA79276@shell.msen.com> Sorry I just couldn't resist :-) On Sat, Sep 12, 2020 at 03:50:45PM -0500, Chris Muller wrote: > LOL... that first sentence... although as from the users' > perspective, the global is going away, so it might be better without > the hashtag... lol > > On Sat, Sep 12, 2020 at 1:52 PM wrote: > > > > A new version of System was added to project The Inbox: > > http://source.squeak.org/inbox/System-dtl.1170.mcz > > > > ==================== Summary ==================== > > > > Name: System-dtl.1170 > > Author: dtl > > Time: 12 September 2020, 2:52:23.835083 pm > > UUID: cdd6e922-e25e-433b-a516-c503d0a772a8 > > Ancestors: System-mt.1169 > > > > End of the #World as we know it. Package postscript only. About two years ago we eliminated all dependencies on global World. However, the global binding has remained functional on the theory that some external packages might still expect it. > > > > It is time to get rid of the binding now because: > > - It gives the false impressing that the World is still in use as a global > > - If a package is loaded that does need World, then "Smalltalk at: #World put: Project current world" will restore prior behavior. > > > > =============== Diff against System-mt.1169 =============== > > > > Item was changed: > > + (PackageInfo named: 'System') postscript: '"Global World is functional but no longer used in the image. Remove it now. If any package is loaded that requires the obsolete global binding, the prior behavior may be restored with: > > + > > + Smalltalk at: #World put: Project current world > > + " > > + > > + Smalltalk globals unbind: #World. > > + '! > > - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! > > > > > From eliot.miranda at gmail.com Sat Sep 12 23:00:37 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 12 Sep 2020 16:00:37 -0700 Subject: [squeak-dev] The Inbox: System-dtl.1170.mcz In-Reply-To: <20200912222721.GA79276@shell.msen.com> References: <20200912222721.GA79276@shell.msen.com> Message-ID: <3EAB4542-90FB-4CB7-AA6F-42631651B822@gmail.com> > On Sep 12, 2020, at 3:27 PM, David T. Lewis wrote: > > Sorry I just couldn't resist :-) > >> On Sat, Sep 12, 2020 at 03:50:45PM -0500, Chris Muller wrote: >> LOL... that first sentence... although as from the users' >> perspective, the global is going away, so it might be better without >> the hashtag... lol >> >>> On Sat, Sep 12, 2020 at 1:52 PM wrote: >>> >>> A new version of System was added to project The Inbox: >>> http://source.squeak.org/inbox/System-dtl.1170.mcz >>> >>> ==================== Summary ==================== >>> >>> Name: System-dtl.1170 >>> Author: dtl >>> Time: 12 September 2020, 2:52:23.835083 pm >>> UUID: cdd6e922-e25e-433b-a516-c503d0a772a8 >>> Ancestors: System-mt.1169 >>> >>> End of the #World as we know it. ROTFL <3 >>> Package postscript only. About two years ago we eliminated all dependencies on global World. However, the global binding has remained functional on the theory that some external packages might still expect it. >>> >>> It is time to get rid of the binding now because: >>> - It gives the false impressing that the World is still in use as a global >>> - If a package is loaded that does need World, then "Smalltalk at: #World put: Project current world" will restore prior behavior. >>> >>> =============== Diff against System-mt.1169 =============== >>> >>> Item was changed: >>> + (PackageInfo named: 'System') postscript: '"Global World is functional but no longer used in the image. Remove it now. If any package is loaded that requires the obsolete global binding, the prior behavior may be restored with: >>> + >>> + Smalltalk at: #World put: Project current world >>> + " >>> + >>> + Smalltalk globals unbind: #World. >>> + '! >>> - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! >>> >>> >> > From tim at rowledge.org Sat Sep 12 23:28:54 2020 From: tim at rowledge.org (tim Rowledge) Date: Sat, 12 Sep 2020 16:28:54 -0700 Subject: [squeak-dev] Squeak needs a third-party package updater In-Reply-To: References: Message-ID: > On 2020-09-12, at 3:22 PM, Chris Muller wrote: > > Just to be clear, SqueakMap is not a VCS but a portal. Since it's just a database of Smalltalk scripts, it works as a portal to configure via anything -- Monticello, Git, Bitbucket, SVN, CVS, ENVY, whether a development workstation or end-user app. It's meant to be the place that answers the question, "How do I load XYZ package?" And it's a damn good answer - when it's kept up to date properly. Any ways we can be better at this should be considered. Not least making sure the SM server is kept up to date and running well so that the package scripts can be easily maintained. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Shift to the left! Shift to the right! Pop up, push down, byte, byte, byte! From lewis at mail.msen.com Sat Sep 12 23:50:53 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sat, 12 Sep 2020 19:50:53 -0400 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> Message-ID: <20200912235053.GA88390@shell.msen.com> On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > Glad you like it, David! :-) > > > > > In the case of the global World variable, we were able to make it an instance variable in Project. > > I heard of this, but where can you see the difference? If I evaluate "World" > in my fresh trunk image, I get a PasteUpMorph instance ... #World is still > listed in my Smalltalk bindings. Or are you talking about making ActiveEvent & Co. > instance variables of (Morphic)Project rather than process local variables? > Not sure about this, do we really want to forbid multiple concurrent event > processes in one Project? > Hi Christoph, The direct use of the global World has been eliminated in the image, even though the global binding is still in place for compatibility reasons. But the other globals used in Morphic have not yet been addressed. I think that your changes make it easier to see what the next steps might be. You have organized the accesses to global state so that it seems more clear to me. So what might be the next steps? As an example, consider ActiveHand. It is a global in the current Environment. But if that was not the case, what object should know about the active hand? We know that the World is naturally associated with the active project (Project current world). Whatever a "hand" is, it seems like something that might be associated with that world, as opposed to just being some global thing associated with who-knows-what. Noticing this, we can also look around for things that might already have references to the ActiveHand, and notice that WorldState has exactly this. It also has a class comment that says "The state of a Morphic world. (This needs some serious commenting!!)" Well, d'oh! Yes it certainly does need serious commenting, but we did not write that thing, so set the issue aside for the moment. But where is that WorldState actually used? Aha: Project current world activeHand == ActiveHand "==> true" So the current project, if it is a MorphicProject, actually already holds on to a reference to the ActiveHand. It might be a bit messy, and we can all agree that it would benefit from some comments, but it's there already so we will not need to reinvent it. So now we know that ActiveHand is meaningful only in the context of a MorphicProject, and we know that the project already has a reference to its active hand (Project current activeHand). So why do we really need a global #ActiveHand? I am not sure, but my guess (based on experience of trying to make the global #World go away) is that it will have something to do with transitioning between projects. For example, if we are in an MVCProject and enter a MorphicProject, then we need to make sure that the reference to the hand is valid. And maybe we need to consider moving from one kind of MorphicProject to another (think Etoys), so we might need to change the ActiveHand during that transition. To test this hypothesis, we might try changing the direct references to ActiveHand to "Project current activeHand" until something breaks. It will probably be something related to entering projects, and it will probably put us into an emergency debugger (hopefulliy an MVC debugger). So save often, proceed in small steps, and eventually the need for the global will be gone. Then circle back and find the performance problems that may have been introduced by removing the global, and find a way to make them better. And fix up whatever other problems came up along the way, I'm not sure what that will look like, but there are sure to be some issues. Last but not least, by the time all this is done we probably understand WorldState well enough to go back and add those long overdue comments. Dave From tim at rowledge.org Sun Sep 13 01:27:54 2020 From: tim at rowledge.org (tim Rowledge) Date: Sat, 12 Sep 2020 18:27:54 -0700 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <20200912235053.GA88390@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <20200912235053.GA88390@shell.msen.com> Message-ID: <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> > On 2020-09-12, at 4:50 PM, David T. Lewis wrote: > > Last but not least, by the time all this is done we probably understand > WorldState well enough to go back and add those long overdue comments. Sheesh, you and your being all reasonable and sensible... tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: CPP: Crush Plotter Pen From kksubbu.ml at gmail.com Sun Sep 13 11:27:13 2020 From: kksubbu.ml at gmail.com (K K Subbu) Date: Sun, 13 Sep 2020 16:57:13 +0530 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> Message-ID: <585c515c-14db-2d51-7e12-c9aea36e317e@gmail.com> On 12/09/20 8:01 pm, Eliot Miranda wrote: > > That makes sense.  So we do want the QUIT/NO SAVE indication written to > the changes file if anything is written to the changes file because we > know that when doing crash recovery we want to ignore everything > before QUIT/NO SAVE.  So, Chris Muller’s objections aside, I would argue > that if the changes file is undisturbed (nothing written to it in the > current session) when the system is about to quit without saving, it > should do so without writing the QUIT/NO SAVE stamp. What if a START log is lazily written just before the first write to the change log? If no changes are made then the changes file remains untouched. The first write to the change log is prefixed with a START log entry. A QUIT/NO SAVE entry is not needed or may be added if and only if a START log was written. Regards .. Subbu From kksubbu.ml at gmail.com Sun Sep 13 11:38:10 2020 From: kksubbu.ml at gmail.com (K K Subbu) Date: Sun, 13 Sep 2020 17:08:10 +0530 Subject: [squeak-dev] The Inbox: System-dtl.1170.mcz In-Reply-To: References: Message-ID: On 12/09/20 6:52 pm, commits at source.squeak.org wrote: > End of the #World as we know it. Package postscript only. About two > years ago we eliminated all dependencies on global World. However, > the global binding has remained functional on the theory that some > external packages might still expect it. When an undefined global var is encountered, is it possible to detect it as an obsolete reference and then offer an option to replace it with a proper object accessor? Perhaps a 'Obsolete Globals' dictionary mapping an obsolete global symbol to an object access? Currently, we just throw up a 'Undefined Variable' dialog offering closest symbols to correct a possible misspelling. Regards .. Subbu From gettimothy at zoho.com Sun Sep 13 11:47:07 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 13 Sep 2020 07:47:07 -0400 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: <6cc549c462334c02a95d8e9a76830291@student.hpi.uni-potsdam.de> <44031340-448E-429E-8CAA-9FE95B684C38@gmail.com> Message-ID: <174874a4b40.b1e607ff21798.5858757050664217972@zoho.com> Where can I download this REPL image? I cannot find it on files.squeak.org thx in advance. ---- On Sat, 12 Sep 2020 15:19:37 -0400 Eliot Miranda wrote ---- Hi Tobias, On Sat, Sep 12, 2020 at 9:56 AM Tobias Pape wrote: > On 12.09.2020, at 16:31, Eliot Miranda wrote: > > With the REPL image if it starts up and evaluates an expression of two (which are not logged) and then quits nothing has changed but the QUIT/NO SAVE has been written, and now my reproducible image/changes pair is no longer in exactly the same state; the changes file has grown. > In that particular case, wouldn't it suffice to just make the Changes file read-only? No.  Sometimes one wants to file-in code into the REPL.   Best regards         -Tobias _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From erik.stel at gmail.com Sun Sep 13 12:55:54 2020 From: erik.stel at gmail.com (Erik Stel) Date: Sun, 13 Sep 2020 07:55:54 -0500 (CDT) Subject: [squeak-dev] UK Smalltalk User Group meeting - Wednesday, August 26th In-Reply-To: References: Message-ID: <1600001754156-0.post@n4.nabble.com> The recording is online at https://vimeo.com/457353130 Regards, Erik -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From gettimothy at zoho.com Sun Sep 13 13:15:01 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 13 Sep 2020 09:15:01 -0400 Subject: [squeak-dev] =?utf-8?b?JzEuX0ZDX1RhdHJhbl9QcmXDhcKhb3ZfbWFuYWdl?= =?utf-8?q?rs=27__blowing_up_Seaside?= Message-ID: <174879ac761.d5de9ea422403.5082700001459195675@zoho.com> Hi folks, I am searching for way to detect that ''1._FC_Tatran_PreÅ¡ov_managers'' will blow up  Seaside rendering. It is a WideString and I would just assume ignore it for now to render many other strings on my dev app. It displays differently in the workspace and inspectors than it does here. 'PreÅ¡ov' here is 'Pre(s with a bar over it)ov' in my image. Isolate that (s with a bar over it) it is Character: code 353 How would you attack detecting this in the WideString protocol? thx in advance. -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Sun Sep 13 14:36:27 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 13 Sep 2020 16:36:27 +0200 (CEST) Subject: [squeak-dev] =?iso-8859-15?q?=271=2E=5FFC=5FTatran=5FPre=C5=A1ov?= =?iso-8859-15?q?=5Fmanagers=27__blowing_up_Seaside?= In-Reply-To: <174879ac761.d5de9ea422403.5082700001459195675@zoho.com> References: <174879ac761.d5de9ea422403.5082700001459195675@zoho.com> Message-ID: Hi Tim, On Sun, 13 Sep 2020, gettimothy via Squeak-dev wrote: > Hi folks, > > I am searching for way to detect that ''1._FC_Tatran_PreÅ¡ov_managers'' will blow up  Seaside rendering. Seaside should have no problem rendering such strings, provided that the character encoding is set properly (e.g. to utf-8). > > It is a WideString and I would just assume ignore it for now to render many other strings on my dev app. > > It displays differently in the workspace and inspectors than it does here. I presume you don't use the default font, as that doesn't have that character. > > 'PreÅ¡ov' here is 'Pre(s with a bar over it)ov' in my image. > > Isolate that (s with a bar over it) it is Character: code 353 That bar is supposed to be "broken" in the middle: https://codepoints.net/U+0161 > > How would you attack detecting this in the WideString protocol? Normally you don't. Just make sure the Seaside adaptor has the proper character encoding set. Levente > > thx in advance. > > > > > > > > > > > From Tom.Beckmann at student.hpi.uni-potsdam.de Sun Sep 13 15:22:04 2020 From: Tom.Beckmann at student.hpi.uni-potsdam.de (Beckmann, Tom) Date: Sun, 13 Sep 2020 15:22:04 +0000 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> , Message-ID: <051927075cb34f3cb016f40ef063f3d6@student.hpi.uni-potsdam.de> Hi everyone, just wanted to mention that I experimented with bringing XInput2 [4] support to the X11 plugin for Squeak. With this, we get smooth (pixel perfect) scrolling and multitouch support. The code is lacking the device management part, so it may not work correctly when new devices are attached, for example. Some pointers to code: - Here are the changes to the X11 event handling loop [1] - For the touch event tuple [2], I stayed rather close to the info XI2 provides, meaning you get the x,y coordinate, a sequence number that identifies a continuous gesture in the stream of events, and a touch type (begin/end/update/cancel). In terms of the range of information (not its structure) this also mirrors what you get in a browser if we omit experimental features such as radius of the touch area [3]. On the image side, I just simply added new #touch[Begin,Update,Update,Cancel]: callbacks to Morph, to which I forwarded the deconstructed event tuples as TouchEvent objects for demonstration purposes. Nothing fancy there just yet. I agree that a concept of multiple hands will likely provide a better abstraction. Hope this can be of some interest :) Best, Tom [1] https://github.com/tom95/opensmalltalk-vm/blob/xi-experiment/platforms/unix/vm-display-X11/sqUnixX11.c#L3638 [2] https://github.com/tom95/opensmalltalk-vm/blob/xi-experiment/platforms/unix/vm/sqUnixEvent.c#L188 [3] https://developer.mozilla.org/en-US/docs/Web/API/Touch [4] https://www.x.org/releases/X11R7.7/doc/inputproto/XI2proto.txt ________________________________________ From: Squeak-dev on behalf of Vanessa Freudenberg Sent: Saturday, September 12, 2020 11:25:24 PM To: The general-purpose Squeak developers list Subject: Re: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) Ha, talk about a trip down memory lane! I had no idea that still existed :) - Vanessa - On Sat, Sep 12, 2020 at 5:42 AM karl ramberg > wrote: There is also the Frank stuff which probably have some of the same multi cursor funktionality: http://tinlizzie.org/~bert/frank4ipad/ One can download the Frank.ipa and unzip to look at the image. Best, Karl On Fri, Sep 11, 2020 at 5:04 PM Tony Garnock-Jones > wrote: Hi Vanessa, On 9/10/20 10:23 PM, Vanessa Freudenberg wrote: > Oh, exciting! Please record a multi-hand Etoys demo. Maybe like this: > https://youtu.be/gYrp31fH-Jk?t=56 Whoa, awesome! I didn't know this existed! I have questions! - How much of that shift-to-get-other-colour-buttons support is still in the image? So far I haven't touched the default world menu bar, which is mildly inappropriate for small and/or multitouch devices. - What other ideas about mapping multitouch to Morphic interaction are out there I have missed and am in danger of reinventing? Also, I am intrigued by the idea of simply having each touch be mapped to a Hand. It seems like the obvious way to do things, on one level, but doesn't quite fit my fuzzy intuitions for how (multi-finger) gesture recognition might work on another level. So: - When you were working on this, did you have any thoughts about deficiencies of the Morphic model wrt multitouch/gestures/etc., and then did you have any ideas about what to change and how to change it? I'll try to cook up a little demo like that sometime soon. Tony From gettimothy at zoho.com Sun Sep 13 15:39:44 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 13 Sep 2020 11:39:44 -0400 Subject: [squeak-dev] =?utf-8?b?JzEuX0ZDX1RhdHJhbl9QcmXDhcKhb3ZfbWFuYWdl?= =?utf-8?q?rs=27__blowing_up_Seaside?= In-Reply-To: References: <174879ac761.d5de9ea422403.5082700001459195675@zoho.com> Message-ID: <174881f4538.12561ee7724861.8354266645550647318@zoho.com> Hi Levente, Character set in http://localhost:8080/config/tools is set to UTF-8 My system font is Liberation-Mono, something I imported from somewhere trying to get cyrillic to display. I have just verified that that single character causes the "spew" I am getting. renderContentOn: html       html render: 'Å¡'. reproduces the bug. I will run this by Esteban on the Pharo discord Seaside channel. thank you for the pointers. ---- On Sun, 13 Sep 2020 10:36:27 -0400 Levente Uzonyi wrote ---- Hi Tim, On Sun, 13 Sep 2020, gettimothy via Squeak-dev wrote: > Hi folks, > > I am searching for way to detect that ''1._FC_Tatran_PreÅ¡ov_managers'' will blow up  Seaside rendering. Seaside should have no problem rendering such strings, provided that the character encoding is set properly (e.g. to utf-8). > > It is a WideString and I would just assume ignore it for now to render many other strings on my dev app. > > It displays differently in the workspace and inspectors than it does here. I presume you don't use the default font, as that doesn't have that character. > > 'PreÅ¡ov' here is 'Pre(s with a bar over it)ov' in my image. html render: '1._FC_Tatran_PreÅ¡ov_managers'. > > Isolate that (s with a bar over it) it is Character: code 353 That bar is supposed to be "broken" in the middle: https://codepoints.net/U+0161 > > How would you attack detecting this in the WideString protocol? Normally you don't. Just make sure the Seaside adaptor has the proper character encoding set. Levente > > thx in advance. > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 13 15:47:47 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 13 Sep 2020 11:47:47 -0400 Subject: [squeak-dev] =?utf-8?b?JzEuX0ZDX1RhdHJhbl9QcmXDhcKhb3ZfbWFuYWdl?= =?utf-8?q?rs=27__blowing_up_Seaside?= In-Reply-To: References: <174879ac761.d5de9ea422403.5082700001459195675@zoho.com> Message-ID: <1748826a339.fe0862d124909.3595162206923689505@zoho.com> Levente, btw, this relates directly to the XTreams-Parsing I am doing. So thank you again for your help. t ---- On Sun, 13 Sep 2020 10:36:27 -0400 Levente Uzonyi wrote ---- Hi Tim, On Sun, 13 Sep 2020, gettimothy via Squeak-dev wrote: > Hi folks, > > I am searching for way to detect that ''1._FC_Tatran_PreÅ¡ov_managers'' will blow up  Seaside rendering. Seaside should have no problem rendering such strings, provided that the character encoding is set properly (e.g. to utf-8). > > It is a WideString and I would just assume ignore it for now to render many other strings on my dev app. > > It displays differently in the workspace and inspectors than it does here. I presume you don't use the default font, as that doesn't have that character. > > 'PreÅ¡ov' here is 'Pre(s with a bar over it)ov' in my image. > > Isolate that (s with a bar over it) it is Character: code 353 That bar is supposed to be "broken" in the middle: https://codepoints.net/U+0161 > > How would you attack detecting this in the WideString protocol? Normally you don't. Just make sure the Seaside adaptor has the proper character encoding set. Levente > > thx in advance. > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 13 16:06:05 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 13 Sep 2020 12:06:05 -0400 Subject: [squeak-dev] =?utf-8?b?JzEuX0ZDX1RhdHJhbl9QcmXDhcKhb3ZfbWFuYWdl?= =?utf-8?q?rs=27__blowing_up_Seaside?= In-Reply-To: References: <174879ac761.d5de9ea422403.5082700001459195675@zoho.com> Message-ID: <1748837638e.e6b9df2325036.3497220720097930186@zoho.com> renderContenton:html       html render: (Character codePoint: 353) asString works perfectly on Squeak 6.0 alpha with "latest" Seaside3 installed, but no Liberation system fault installed. hmmmm.... can I reproduce it by installing the Liberation mono-fault.... now...where did I stick that font... ---- On Sun, 13 Sep 2020 10:36:27 -0400 Levente Uzonyi wrote ---- Hi Tim, On Sun, 13 Sep 2020, gettimothy via Squeak-dev wrote: > Hi folks, > > I am searching for way to detect that ''1._FC_Tatran_PreÅ¡ov_managers'' will blow up  Seaside rendering. Seaside should have no problem rendering such strings, provided that the character encoding is set properly (e.g. to utf-8). > > It is a WideString and I would just assume ignore it for now to render many other strings on my dev app. > > It displays differently in the workspace and inspectors than it does here. I presume you don't use the default font, as that doesn't have that character. > > 'PreÅ¡ov' here is 'Pre(s with a bar over it)ov' in my image. > > Isolate that (s with a bar over it) it is Character: code 353 That bar is supposed to be "broken" in the middle: https://codepoints.net/U+0161 > > How would you attack detecting this in the WideString protocol? Normally you don't. Just make sure the Seaside adaptor has the proper character encoding set. Levente > > thx in advance. > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 13 16:13:19 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 13 Sep 2020 12:13:19 -0400 Subject: [squeak-dev] =?utf-8?b?JzEuX0ZDX1RhdHJhbl9QcmXDhcKhb3ZfbWFuYWdl?= =?utf-8?q?rs=27__blowing_up_Seaside?= In-Reply-To: <1748837638e.e6b9df2325036.3497220720097930186@zoho.com> References: <174879ac761.d5de9ea422403.5082700001459195675@zoho.com> <1748837638e.e6b9df2325036.3497220720097930186@zoho.com> Message-ID: <174883e0155.b362037825101.8997678057038563801@zoho.com> ok... works perfectly on Squeak6.0alphalatest update: #19835 with Liberation Mono set to default text font. The performance of setting the new font live is fantastic. It was MUCH slower on 5.3. renderContentOn: html html render: (Character codePoint: 353) asString renders perfectly. I think I will port my work to Squeak6.0 alpha . thanks for your help. tty ---- On Sun, 13 Sep 2020 12:06:05 -0400 gettimothy wrote ---- renderContenton:html       html render: (Character codePoint: 353) asString works perfectly on Squeak 6.0 alpha with "latest" Seaside3 installed, but no Liberation system fault installed. hmmmm.... can I reproduce it by installing the Liberation mono-fault.... now...where did I stick that font... ---- On Sun, 13 Sep 2020 10:36:27 -0400 Levente Uzonyi wrote ---- Hi Tim, On Sun, 13 Sep 2020, gettimothy via Squeak-dev wrote: > Hi folks, > > I am searching for way to detect that ''1._FC_Tatran_PreÅ¡ov_managers'' will blow up  Seaside rendering. Seaside should have no problem rendering such strings, provided that the character encoding is set properly (e.g. to utf-8). > > It is a WideString and I would just assume ignore it for now to render many other strings on my dev app. > > It displays differently in the workspace and inspectors than it does here. I presume you don't use the default font, as that doesn't have that character. > > 'PreÅ¡ov' here is 'Pre(s with a bar over it)ov' in my image. > > Isolate that (s with a bar over it) it is Character: code 353 That bar is supposed to be "broken" in the middle: https://codepoints.net/U+0161 > > How would you attack detecting this in the WideString protocol? Normally you don't. Just make sure the Seaside adaptor has the proper character encoding set. Levente > > thx in advance. > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sun Sep 13 16:23:41 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 13 Sep 2020 09:23:41 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <174874a4b40.b1e607ff21798.5858757050664217972@zoho.com> References: <174874a4b40.b1e607ff21798.5858757050664217972@zoho.com> Message-ID: > On Sep 13, 2020, at 4:47 AM, gettimothy via Squeak-dev wrote: > >  > Where can I download this REPL image? > > I cannot find it on files.squeak.org It gets built by a script in the image directory of the OpenSmalltalk-vm repository. Clone, cd to the image directory and run buildsqueaktrunkreader64.sh or some such > > thx in advance. > > > ---- On Sat, 12 Sep 2020 15:19:37 -0400 Eliot Miranda wrote ---- > > Hi Tobias, > > On Sat, Sep 12, 2020 at 9:56 AM Tobias Pape wrote: > > > On 12.09.2020, at 16:31, Eliot Miranda wrote: > > > > With the REPL image if it starts up and evaluates an expression of two (which are not logged) and then quits nothing has changed but the QUIT/NO SAVE has been written, and now my reproducible image/changes pair is no longer in exactly the same state; the changes file has grown. > > > > In that particular case, wouldn't it suffice to just make the Changes file read-only? > > No. Sometimes one wants to file-in code into the REPL. > > Best regards > -Tobias > > _,,,^..^,,,_ > best, Eliot > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sun Sep 13 16:25:10 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 13 Sep 2020 09:25:10 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <585c515c-14db-2d51-7e12-c9aea36e317e@gmail.com> References: <585c515c-14db-2d51-7e12-c9aea36e317e@gmail.com> Message-ID: Hi Subbu, > On Sep 13, 2020, at 4:27 AM, K K Subbu wrote: > > On 12/09/20 8:01 pm, Eliot Miranda wrote: >> That makes sense. So we do want the QUIT/NO SAVE indication written to the changes file if anything is written to the changes file because we know that when doing crash recovery we want to ignore everything before QUIT/NO SAVE. So, Chris Muller’s objections aside, I would argue that if the changes file is undisturbed (nothing written to it in the current session) when the system is about to quit without saving, it should do so without writing the QUIT/NO SAVE stamp. > > What if a START log is lazily written just before the first write to the change log? > > If no changes are made then the changes file remains untouched. The first write to the change log is prefixed with a START log entry. A QUIT/NO SAVE entry is not needed or may be added if and only if a START log was written. Good idea. That would work. > > Regards .. Subbu > From tim at rowledge.org Sun Sep 13 18:29:38 2020 From: tim at rowledge.org (tim Rowledge) Date: Sun, 13 Sep 2020 11:29:38 -0700 Subject: [squeak-dev] logging to Transcript from not-UI process Message-ID: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> The recent discussions about change logs etc, along with thinking about how the Toothpick package might do the job better (just realised that one could add an MQTT logger to it so that clusters of images could simply publish the logs and 'normal' tools could be used for sysadmin purposes) have reminded of a problem that has bitten me a couple of times over the last few weeks. Basically, if we have a non-UI process (like Seaside handling) that tries to send something to the Transcript, then just occasionally and always, of course, during a demo, it will cause a problem. Obviously, I can't find a log file of it happening to illustrate; that would be far to helpful. IIRC it's something relating to the re-layout phase of the text morph. Now I'd swear (and did!) that we 'fixed' this several years ago but I can't find any email about it so maybe I'm just hallucinating again. Has anybody else seen this, or can remember about it or... ? tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Java: the best argument for Smalltalk since C++ From tim at rowledge.org Sun Sep 13 18:30:17 2020 From: tim at rowledge.org (tim Rowledge) Date: Sun, 13 Sep 2020 11:30:17 -0700 Subject: [squeak-dev] Squeak on a cellphone responds to touchscreen input now (was Re: Squeak on a PostmarketOS cellphone) In-Reply-To: <051927075cb34f3cb016f40ef063f3d6@student.hpi.uni-potsdam.de> References: <48d3ea49-67ad-9f21-14e8-de5817c16941@leastfixedpoint.com> <005a30c9-a58d-ed40-09c6-c81b5df62844@leastfixedpoint.com> <051927075cb34f3cb016f40ef063f3d6@student.hpi.uni-potsdam.de> Message-ID: > On 2020-09-13, at 8:22 AM, Beckmann, Tom wrote: > > Hi everyone, > > just wanted to mention that I experimented with bringing XInput2 [4] support to the X11 plugin for Squeak. With this, we get smooth (pixel perfect) scrolling and multitouch support. Excellent news! tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: IA: Illogical And From lewis at mail.msen.com Sun Sep 13 19:06:38 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 13 Sep 2020 15:06:38 -0400 Subject: [squeak-dev] logging to Transcript from not-UI process In-Reply-To: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> Message-ID: <20200913190638.GA75346@shell.msen.com> On Sun, Sep 13, 2020 at 11:29:38AM -0700, tim Rowledge wrote: > The recent discussions about change logs etc, along with thinking about how the Toothpick package might do the job better (just realised that one could add an MQTT logger to it so that clusters of images could simply publish the logs and 'normal' tools could be used for sysadmin purposes) have reminded of a problem that has bitten me a couple of times over the last few weeks. > > Basically, if we have a non-UI process (like Seaside handling) that tries to send something to the Transcript, then just occasionally and always, of course, during a demo, it will cause a problem. Obviously, I can't find a log file of it happening to illustrate; that would be far to helpful. IIRC it's something relating to the re-layout phase of the text morph. > > Now I'd swear (and did!) that we 'fixed' this several years ago but I can't find any email about it so maybe I'm just hallucinating again. > > Has anybody else seen this, or can remember about it or... ? > This does not answer your question, but just in case you have overlooked it, "OSProcess trace: something asString" is useful for dirt-simple logging to the console from any process. I have never trusted the Transcript for things like that. Dave From robert.withers at pm.me Sun Sep 13 20:03:50 2020 From: robert.withers at pm.me (Robert Withers) Date: Sun, 13 Sep 2020 20:03:50 +0000 Subject: [squeak-dev] logging to Transcript from not-UI process In-Reply-To: <20200913190638.GA75346@shell.msen.com> References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> <20200913190638.GA75346@shell.msen.com> Message-ID: <4333e0a5-3650-eae0-54e6-208f1704db53@pm.me> I'll throw onto this discussion. I believe tim tested the TraceMonitor, but went with Toothpick, which I have never used. TraceMonitor (package in Cryptography) is mutex protected and writes to multiple streams, including the Transcript. Therefore, multiple event loops or other background processes can concurrently write to Transcript and it will be properly interleaved. K, r On 9/13/20 3:06 PM, David T. Lewis wrote: > On Sun, Sep 13, 2020 at 11:29:38AM -0700, tim Rowledge wrote: >> The recent discussions about change logs etc, along with thinking about how the Toothpick package might do the job better (just realised that one could add an MQTT logger to it so that clusters of images could simply publish the logs and 'normal' tools could be used for sysadmin purposes) have reminded of a problem that has bitten me a couple of times over the last few weeks. >> >> Basically, if we have a non-UI process (like Seaside handling) that tries to send something to the Transcript, then just occasionally and always, of course, during a demo, it will cause a problem. Obviously, I can't find a log file of it happening to illustrate; that would be far to helpful. IIRC it's something relating to the re-layout phase of the text morph. >> >> Now I'd swear (and did!) that we 'fixed' this several years ago but I can't find any email about it so maybe I'm just hallucinating again. >> >> Has anybody else seen this, or can remember about it or... ? >> > This does not answer your question, but just in case you have overlooked > it, "OSProcess trace: something asString" is useful for dirt-simple logging > to the console from any process. I have never trusted the Transcript for > things like that. > > Dave > > From lewis at mail.msen.com Sun Sep 13 20:34:22 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 13 Sep 2020 16:34:22 -0400 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> Message-ID: <20200913203422.GA85590@shell.msen.com> Is anyone is a position to try loading old Etoys projects such as CarAndPen.014.pr into an up to date trunk image? I have not tried this in a while, and I get errors on loading the project. I don't know how to easily fix that so I am asking for help from anyone who may be more up to date on the topic. The reason I am asking is that I would like to know if removing the global variable #World from trunk would cause any new or unusual problems with respect to Etoys project loading. To be specific, if you have a trunk image and are able to load any Etoys project, then I would like to know what sort of errors you encounter if you first do this before loading that same Etoys project: Smalltalk globals unbind: #World I would expect that this will lead to some kind of error condition, and what I would like to know is whether that failure or error message gives a good clue as to the problem, such that you might be able to figure out that you would need to fix it by doing this: Smalltalk at: #World put: Project current world Thanks! Dave On Sat, Sep 12, 2020 at 07:32:17PM +0000, Thiede, Christoph wrote: > Hi David, > > > I think I understand the dilemma: If we keep #World in the bindings, it will keep being used (I have to admit that, for personal scripts, I'm doing so, too). If we remove it, we don't provide backward compatibility for older projects. This would be really a pity because even if you write in your inbox version: "If a package is loaded that does need World, then 'Smalltalk at: #World put: Project current world' will restore prior behavior.", how many people will know that when they attempt to refloat an ancient piece of Squeak code? > > As far as we only have these two options, I'm tending to vote for the first one. Unless we use it in the Trunk, it does no harm, and actual bugs should occur rather seldom because some third-party package is still using #World. If there should be a bug somewhere, it can be easily fixed. > > > But hypothetically, there would be a third option, which would be my favorite: Handle accesses to #World like calls on a deprecated method, i.e. by signaling a DeprecationWarning. That way we could even make sure that novices do not learn to use a deprecated variable. > > This could be realized by wrapping the value of #World into an ObjectTracer or a similar class that raises a DeprecationWarning on every call that is made to it. > > But I fear this could be a piece of overengineering, what do you think? Also, this would not protect the global variable from being reassigned ... > > The very last solution could be to make a (global) list of deprecated bindings and raise a Warning from the Compiler when an attempt is made to access one of them. Unfortunately, this would also be the most invasive change. > > > @Levente: It seemed kind of overengineering to me to create three subclasses of ProcessLocalVariable for this purpose. Or am I misunderstand their concept? Maybe we could also need something like a PluggableProcessLocalVariable that can be instantiated with two accessor blocks? > > > Best, > > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Samstag, 12. September 2020 21:10:59 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > > Glad you like it, David! :-) > > > > > > > > > In the case of the global World variable, we were able to make it an instance variable in Project. > > > > I heard of this, but where can you see the difference? If I evaluate "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World is still listed in my Smalltalk bindings. > > > I just put System-dtl.1170 in the inbox to make it go away. For a long > time, the World variable was shared all over the place, even in MVC if > you can believe that. From a modularity perspective it was a real mess. > Basically, the solution was to make it an instance variable in Project. > The current project always has a world, and when transitioning from one > project to another, you can do the handoff between those two worlds > without referencing a global. > > All that got done about two years ago, but I intentionally left the #World > binding in place on the theory that there might be external packages (or > Etoys projects) that expect to have access to global World. > > The way it works now (see MorphicProject>>setWorld:) is that the global > World will be updated as before, but this happens if and only if the > global binding exists. If you remove the binding, the World no longer > exists. If you load a package or project that does have references to > World, then restoring the #World binding in the current environment should > make it work again. > > I don't really know if it's a good idea to remove the #World binding now > (that's why it's in the inbox). But leaving it in the image is confusing > because it gives the wrong impression that it is still being used. > > Dave > > > From leves at caesar.elte.hu Sun Sep 13 21:15:19 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 13 Sep 2020 23:15:19 +0200 (CEST) Subject: [squeak-dev] logging to Transcript from not-UI process In-Reply-To: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> Message-ID: Hi Tim, On Sun, 13 Sep 2020, tim Rowledge wrote: > The recent discussions about change logs etc, along with thinking about how the Toothpick package might do the job better (just realised that one could add an MQTT logger to it so that clusters of images could simply publish the logs and 'normal' tools could be used for sysadmin purposes) have reminded of a problem that has bitten me a couple of times over the last few weeks. > > Basically, if we have a non-UI process (like Seaside handling) that tries to send something to the Transcript, then just occasionally and always, of course, during a demo, it will cause a problem. Obviously, I can't find a log file of it happening to illustrate; that would be far to helpful. IIRC it's something relating to the re-layout phase of the text morph. > > Now I'd swear (and did!) that we 'fixed' this several years ago but I can't find any email about it so maybe I'm just hallucinating again. > > Has anybody else seen this, or can remember about it or... ? Only the #endEntry message is protected against concurrent access, so you can't write anything in a thread-safe manner outside the UI process. I would say it was written with a single process in mind, and later "patched" to be able to "handle" concurrent access. My workaround is that whenever I need to log something to the Transcript from potentially another process, I prepare the string to log in my process then use #addDeferredUIMessage: to inject it into the UI process in a safe way. E.g. Transcript open. [ | message | message := 'This is a very {1} log message at {2}.' format: { 'important'. DateAndTime now }. Project current addDeferredUIMessage: [ Transcript nextPutAll: message; cr; endEntry ] ] fork. What I've been thinking about in the past decade is to add new formatting messages to TranscriptStream. E.g. #show:formattedWith:, which does what the above thing would but you could just send it to Transcript directly, like: Transcript show: 'This is a very {1} log message at {2}.{3}' formattedWith: { 'important'. DateAndTime now. String cr } Or #showStream:, which would let you safely write a message on a stream. E.g.: Transcript showStream: [ :stream | stream nextPutAll: 'foo'; nextPutAll: 'bar'; cr ]. However, that would require a few changes like turning AccessSema into a Mutex to be reentrant. (also, why is AccessSema a class variable used by an instance?) #nextPut:, #nextPutAll: and all the other Stream API exposed by inheritance are useless, because tThey can't be made concurrent unless they are all overridden. So, IMHO TranscriptStream should be replaced with a new class inheriting from Object, containing a single WriteStream, and providing a thread-safe API (which can be the same as the current one). Levente > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > Java: the best argument for Smalltalk since C++ From tim at rowledge.org Mon Sep 14 02:28:31 2020 From: tim at rowledge.org (tim Rowledge) Date: Sun, 13 Sep 2020 19:28:31 -0700 Subject: [squeak-dev] logging to Transcript from not-UI process In-Reply-To: <4333e0a5-3650-eae0-54e6-208f1704db53@pm.me> References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> <20200913190638.GA75346@shell.msen.com> <4333e0a5-3650-eae0-54e6-208f1704db53@pm.me> Message-ID: <6F1F27CE-418D-41AB-A1F2-DE54584533E8@rowledge.org> > On 2020-09-13, at 1:03 PM, Robert Withers via Squeak-dev wrote: > > I'll throw onto this discussion. I believe tim tested the TraceMonitor, > but went with Toothpick, which I have never used. There's nothing particularly wrong with TraceMonitor but Toothpick has a lot more options and has a very nice explanatory wiki on squeaksource. Explaining in order to be able to understand how to use it is always a winning strategy. Now, it is indeed true that I had to find the doc buried deep in the Wayback Machine and convert it to the wiki myself... tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: DTF: Dump Tape to Floor From eliot.miranda at gmail.com Mon Sep 14 02:45:30 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 13 Sep 2020 19:45:30 -0700 Subject: [squeak-dev] autocomplete exit Message-ID: Hi, would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Mon Sep 14 05:18:07 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 13 Sep 2020 22:18:07 -0700 Subject: [squeak-dev] logging to Transcript from not-UI process In-Reply-To: References: Message-ID: <73C4836A-E361-4951-AF5C-95DAA5B669E0@gmail.com> Hi Levente, > On Sep 13, 2020, at 2:17 PM, Levente Uzonyi wrote: > > Hi Tim, > >> On Sun, 13 Sep 2020, tim Rowledge wrote: >> >> The recent discussions about change logs etc, along with thinking about how the Toothpick package might do the job better (just realised that one could add an MQTT logger to it so that clusters of images could simply publish the logs and 'normal' tools could be used for sysadmin purposes) have reminded of a problem that has bitten me a couple of times over the last few weeks. >> >> Basically, if we have a non-UI process (like Seaside handling) that tries to send something to the Transcript, then just occasionally and always, of course, during a demo, it will cause a problem. Obviously, I can't find a log file of it happening to illustrate; that would be far to helpful. IIRC it's something relating to the re-layout phase of the text morph. >> >> Now I'd swear (and did!) that we 'fixed' this several years ago but I can't find any email about it so maybe I'm just hallucinating again. >> >> Has anybody else seen this, or can remember about it or... ? > > Only the #endEntry message is protected against concurrent access, so you can't write anything in a thread-safe manner outside the UI process. > > I would say it was written with a single process in mind, and later "patched" to be able to "handle" concurrent access. > > My workaround is that whenever I need to log something to the Transcript from potentially another process, I prepare the string to log in my process then use #addDeferredUIMessage: to inject it into the UI process in a safe way. E.g. > > Transcript open. > [ > | message | > message := 'This is a very {1} log message at {2}.' format: { 'important'. DateAndTime now > }. > Project current addDeferredUIMessage: [ Transcript nextPutAll: > message; cr; endEntry ] ] fork. > > What I've been thinking about in the past decade is to add new formatting messages to TranscriptStream. E.g. #show:formattedWith:, which does what the above thing would but you could just send it to Transcript directly, like: > > Transcript show: 'This is a very {1} log message at {2}.{3}' formattedWith: { 'important'. DateAndTime now. String cr } > > Or #showStream:, which would let you safely write a message on a stream. E.g.: > > Transcript showStream: [ :stream | stream nextPutAll: 'foo'; nextPutAll: 'bar'; cr ]. > > However, that would require a few changes like turning AccessSema into a Mutex to be reentrant. (also, why is AccessSema a class variable used by an instance?) Agreed. We should at least fix this. The mutex should be an inst var. > #nextPut:, #nextPutAll: and all the other Stream API exposed by inheritance are useless, because tThey can't be made concurrent unless they are all overridden. > So, IMHO TranscriptStream should be replaced with a new class inheriting from Object, containing a single WriteStream, and providing a thread-safe API (which can be the same as the current one). Agreed. And if the internal architecture uses a shared queue we can cleanly separate writing from rendering. > Levente > >> >> tim >> -- >> tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim >> Java: the best argument for Smalltalk since C++ > From robert.withers at pm.me Mon Sep 14 05:27:19 2020 From: robert.withers at pm.me (Robert Withers) Date: Mon, 14 Sep 2020 05:27:19 +0000 Subject: [squeak-dev] logging to Transcript from not-UI process In-Reply-To: <6F1F27CE-418D-41AB-A1F2-DE54584533E8@rowledge.org> References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> <20200913190638.GA75346@shell.msen.com> <4333e0a5-3650-eae0-54e6-208f1704db53@pm.me> <6F1F27CE-418D-41AB-A1F2-DE54584533E8@rowledge.org> Message-ID: On 9/13/20 10:28 PM, tim Rowledge wrote: >> On 2020-09-13, at 1:03 PM, Robert Withers via Squeak-dev wrote: >> >> I'll throw onto this discussion. I believe tim tested the TraceMonitor, >> but went with Toothpick, which I have never used. > There's nothing particularly wrong with TraceMonitor but Toothpick has a lot more options and has a very nice explanatory wiki on squeaksource. Explaining in order to be able to understand how to use it is always a winning strategy. Now, it is indeed true that I had to find the doc buried deep in the Wayback Machine and convert it to the wiki myself... Yes, indeed. This is the exact issue I am facing with PromisesLocal, providing a good explanation of its operation. What is the link to the Toothpick wiki page? I found the SqueakSource project with some documentation: http://www.squeaksource.com/Toothpick.html. I believe Toothpick uses Aspects, so in this regard it may be easier to add Logging to some code, than with TraceMonitor. In TraceMonitor one must start monitoring by sending 'traceMonitor monitor: mainObject', there after the mainObject can send 'mainObject monitor: subObject' and so on, to allow subObjects to send #etrace:msg: and be monitored...my paltry documentation, here. :/ K, r From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:17:53 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:17:53 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <20200912235053.GA88390@shell.msen.com>, <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> Message-ID: Hi Dave, I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. What do you think? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von tim Rowledge Gesendet: Sonntag, 13. September 2020 03:27:54 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > On 2020-09-12, at 4:50 PM, David T. Lewis wrote: > > Last but not least, by the time all this is done we probably understand > WorldState well enough to go back and add those long overdue comments. Sheesh, you and your being all reasonable and sensible... tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: CPP: Crush Plotter Pen -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:28:28 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:28:28 +0000 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <20200913203422.GA85590@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de>, <20200913203422.GA85590@shell.msen.com> Message-ID: <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> Hi David, I cannot load CarAndPen.014.pr in a fresh trunk image, it gives me an error from the SmartRefStream: Key not found: a PasteUpMorph(1723393) [world] So I could not test how it behaves without the #World binding. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Sonntag, 13. September 2020 22:34:22 An: The general-purpose Squeak developers list Betreff: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) Is anyone is a position to try loading old Etoys projects such as CarAndPen.014.pr into an up to date trunk image? I have not tried this in a while, and I get errors on loading the project. I don't know how to easily fix that so I am asking for help from anyone who may be more up to date on the topic. The reason I am asking is that I would like to know if removing the global variable #World from trunk would cause any new or unusual problems with respect to Etoys project loading. To be specific, if you have a trunk image and are able to load any Etoys project, then I would like to know what sort of errors you encounter if you first do this before loading that same Etoys project: Smalltalk globals unbind: #World I would expect that this will lead to some kind of error condition, and what I would like to know is whether that failure or error message gives a good clue as to the problem, such that you might be able to figure out that you would need to fix it by doing this: Smalltalk at: #World put: Project current world Thanks! Dave On Sat, Sep 12, 2020 at 07:32:17PM +0000, Thiede, Christoph wrote: > Hi David, > > > I think I understand the dilemma: If we keep #World in the bindings, it will keep being used (I have to admit that, for personal scripts, I'm doing so, too). If we remove it, we don't provide backward compatibility for older projects. This would be really a pity because even if you write in your inbox version: "If a package is loaded that does need World, then 'Smalltalk at: #World put: Project current world' will restore prior behavior.", how many people will know that when they attempt to refloat an ancient piece of Squeak code? > > As far as we only have these two options, I'm tending to vote for the first one. Unless we use it in the Trunk, it does no harm, and actual bugs should occur rather seldom because some third-party package is still using #World. If there should be a bug somewhere, it can be easily fixed. > > > But hypothetically, there would be a third option, which would be my favorite: Handle accesses to #World like calls on a deprecated method, i.e. by signaling a DeprecationWarning. That way we could even make sure that novices do not learn to use a deprecated variable. > > This could be realized by wrapping the value of #World into an ObjectTracer or a similar class that raises a DeprecationWarning on every call that is made to it. > > But I fear this could be a piece of overengineering, what do you think? Also, this would not protect the global variable from being reassigned ... > > The very last solution could be to make a (global) list of deprecated bindings and raise a Warning from the Compiler when an attempt is made to access one of them. Unfortunately, this would also be the most invasive change. > > > @Levente: It seemed kind of overengineering to me to create three subclasses of ProcessLocalVariable for this purpose. Or am I misunderstand their concept? Maybe we could also need something like a PluggableProcessLocalVariable that can be instantiated with two accessor blocks? > > > Best, > > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Samstag, 12. September 2020 21:10:59 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > > Glad you like it, David! :-) > > > > > > > > > In the case of the global World variable, we were able to make it an instance variable in Project. > > > > I heard of this, but where can you see the difference? If I evaluate "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World is still listed in my Smalltalk bindings. > > > I just put System-dtl.1170 in the inbox to make it go away. For a long > time, the World variable was shared all over the place, even in MVC if > you can believe that. From a modularity perspective it was a real mess. > Basically, the solution was to make it an instance variable in Project. > The current project always has a world, and when transitioning from one > project to another, you can do the handoff between those two worlds > without referencing a global. > > All that got done about two years ago, but I intentionally left the #World > binding in place on the theory that there might be external packages (or > Etoys projects) that expect to have access to global World. > > The way it works now (see MorphicProject>>setWorld:) is that the global > World will be updated as before, but this happens if and only if the > global binding exists. If you remove the binding, the World no longer > exists. If you load a package or project that does have references to > World, then restoring the #World binding in the current environment should > make it work again. > > I don't really know if it's a good idea to remove the #World binding now > (that's why it's in the inbox). But leaving it in the image is confusing > because it gives the wrong impression that it is still being used. > > Dave > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Mon Sep 14 11:34:28 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Mon, 14 Sep 2020 13:34:28 +0200 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> Message-ID: <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> > On 14.09.2020, at 13:28, Thiede, Christoph wrote: > > Hi David, > > I cannot load CarAndPen.014.pr in a fresh trunk image, it gives me an error from the SmartRefStream: > > Key not found: a PasteUpMorph(1723393) [world] > > So I could not test how it behaves without the #World binding. Yea, that's the problem. It's trying to look up the world and does not find it, right? :D Best regards -Tobias > > Best, > Christoph > > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Sonntag, 13. September 2020 22:34:22 > An: The general-purpose Squeak developers list > Betreff: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) > > Is anyone is a position to try loading old Etoys projects such as CarAndPen.014.pr > into an up to date trunk image? I have not tried this in a while, and I get errors > on loading the project. I don't know how to easily fix that so I am asking for > help from anyone who may be more up to date on the topic. > > The reason I am asking is that I would like to know if removing the global > variable #World from trunk would cause any new or unusual problems with respect > to Etoys project loading. > > To be specific, if you have a trunk image and are able to load any Etoys > project, then I would like to know what sort of errors you encounter if > you first do this before loading that same Etoys project: > > Smalltalk globals unbind: #World > > I would expect that this will lead to some kind of error condition, and > what I would like to know is whether that failure or error message gives > a good clue as to the problem, such that you might be able to figure out > that you would need to fix it by doing this: > > Smalltalk at: #World put: Project current world > > > Thanks! > Dave > > > > On Sat, Sep 12, 2020 at 07:32:17PM +0000, Thiede, Christoph wrote: > > Hi David, > > > > > > I think I understand the dilemma: If we keep #World in the bindings, it will keep being used (I have to admit that, for personal scripts, I'm doing so, too). If we remove it, we don't provide backward compatibility for older projects. This would be really a pity because even if you write in your inbox version: "If a package is loaded that does need World, then 'Smalltalk at: #World put: Project current world' will restore prior behavior.", how many people will know that when they attempt to refloat an ancient piece of Squeak code? > > > > As far as we only have these two options, I'm tending to vote for the first one. Unless we use it in the Trunk, it does no harm, and actual bugs should occur rather seldom because some third-party package is still using #World. If there should be a bug somewhere, it can be easily fixed. > > > > > > But hypothetically, there would be a third option, which would be my favorite: Handle accesses to #World like calls on a deprecated method, i.e. by signaling a DeprecationWarning. That way we could even make sure that novices do not learn to use a deprecated variable. > > > > This could be realized by wrapping the value of #World into an ObjectTracer or a similar class that raises a DeprecationWarning on every call that is made to it. > > > > But I fear this could be a piece of overengineering, what do you think? Also, this would not protect the global variable from being reassigned ... > > > > The very last solution could be to make a (global) list of deprecated bindings and raise a Warning from the Compiler when an attempt is made to access one of them. Unfortunately, this would also be the most invasive change. > > > > > > @Levente: It seemed kind of overengineering to me to create three subclasses of ProcessLocalVariable for this purpose. Or am I misunderstand their concept? Maybe we could also need something like a PluggableProcessLocalVariable that can be instantiated with two accessor blocks? > > > > > > Best, > > > > Christoph > > > > ________________________________ > > Von: Squeak-dev im Auftrag von David T. Lewis > > Gesendet: Samstag, 12. September 2020 21:10:59 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > > > On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > > > Glad you like it, David! :-) > > > > > > > > > > > > > In the case of the global World variable, we were able to make it an instance variable in Project. > > > > > > I heard of this, but where can you see the difference? If I evaluate "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World is still listed in my Smalltalk bindings. > > > > > > I just put System-dtl.1170 in the inbox to make it go away. For a long > > time, the World variable was shared all over the place, even in MVC if > > you can believe that. From a modularity perspective it was a real mess. > > Basically, the solution was to make it an instance variable in Project. > > The current project always has a world, and when transitioning from one > > project to another, you can do the handoff between those two worlds > > without referencing a global. > > > > All that got done about two years ago, but I intentionally left the #World > > binding in place on the theory that there might be external packages (or > > Etoys projects) that expect to have access to global World. > > > > The way it works now (see MorphicProject>>setWorld:) is that the global > > World will be updated as before, but this happens if and only if the > > global binding exists. If you remove the binding, the World no longer > > exists. If you load a package or project that does have references to > > World, then restoring the #World binding in the current environment should > > make it work again. > > > > I don't really know if it's a good idea to remove the #World binding now > > (that's why it's in the inbox). But leaving it in the image is confusing > > because it gives the wrong impression that it is still being used. > > > > Dave > > > > > > > From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:36:21 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:36:21 +0000 Subject: [squeak-dev] The Inbox: System-dtl.1170.mcz In-Reply-To: References: , Message-ID: <38b3b98946d949d4bdcc8b96cd61fc58@student.hpi.uni-potsdam.de> Hi all, I just took a short look at all references to the #World binding in my main image, and there are several packages that would probably break if we completely dropped this binding: Animations, MagicMouse, Morphic Testing Framework, RefactoringTools, Pheno, SwaLint, Signals, Widgets, WindowAcrobatics. And this enumeration does neither yet include my own projects nor all other valuable packages I do not know yet. Even the trunk itself still somehow depends on #World, see HandMorph >> #objectForDataStream: or DiskProxy >> #comeFullyUpOnReload:. I would really wish we could find a solution to raise a DeprecationWarning if the binding is still accessed but still provide a fallback for it. Compiler support sounds interesting as well, but we should notice that existing images should still keep working without reinstalling/patching any package. Hypothetically, we could change the Compiler (*not* the Parser) to compile a deprecated literal binding from an extra list differently, so that the source code still will look the same, but the binding will actually point to a method call. Or would such a source code/bytecode discrepancy be too confusing for other components such as the decompiler? Please note that this problem is not only applicable to the World global. Whenever we decide to deprecate a class, we could benefit from a solution that raises a DeprecationWarning when an attempt is made to access it as well. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von K K Subbu Gesendet: Sonntag, 13. September 2020 13:38:10 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] The Inbox: System-dtl.1170.mcz On 12/09/20 6:52 pm, commits at source.squeak.org wrote: > End of the #World as we know it. Package postscript only. About two > years ago we eliminated all dependencies on global World. However, > the global binding has remained functional on the theory that some > external packages might still expect it. When an undefined global var is encountered, is it possible to detect it as an obsolete reference and then offer an option to replace it with a proper object accessor? Perhaps a 'Obsolete Globals' dictionary mapping an obsolete global symbol to an object access? Currently, we just throw up a 'Undefined Variable' dialog offering closest symbols to correct a possible misspelling. Regards .. Subbu -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:36:56 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:36:56 +0000 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de>, <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> Message-ID: <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> I tested this in a fresh trunk image before deleting any binding. ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Montag, 14. September 2020 13:34:28 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) > On 14.09.2020, at 13:28, Thiede, Christoph wrote: > > Hi David, > > I cannot load CarAndPen.014.pr in a fresh trunk image, it gives me an error from the SmartRefStream: > > Key not found: a PasteUpMorph(1723393) [world] > > So I could not test how it behaves without the #World binding. Yea, that's the problem. It's trying to look up the world and does not find it, right? :D Best regards -Tobias > > Best, > Christoph > > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Sonntag, 13. September 2020 22:34:22 > An: The general-purpose Squeak developers list > Betreff: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) > > Is anyone is a position to try loading old Etoys projects such as CarAndPen.014.pr > into an up to date trunk image? I have not tried this in a while, and I get errors > on loading the project. I don't know how to easily fix that so I am asking for > help from anyone who may be more up to date on the topic. > > The reason I am asking is that I would like to know if removing the global > variable #World from trunk would cause any new or unusual problems with respect > to Etoys project loading. > > To be specific, if you have a trunk image and are able to load any Etoys > project, then I would like to know what sort of errors you encounter if > you first do this before loading that same Etoys project: > > Smalltalk globals unbind: #World > > I would expect that this will lead to some kind of error condition, and > what I would like to know is whether that failure or error message gives > a good clue as to the problem, such that you might be able to figure out > that you would need to fix it by doing this: > > Smalltalk at: #World put: Project current world > > > Thanks! > Dave > > > > On Sat, Sep 12, 2020 at 07:32:17PM +0000, Thiede, Christoph wrote: > > Hi David, > > > > > > I think I understand the dilemma: If we keep #World in the bindings, it will keep being used (I have to admit that, for personal scripts, I'm doing so, too). If we remove it, we don't provide backward compatibility for older projects. This would be really a pity because even if you write in your inbox version: "If a package is loaded that does need World, then 'Smalltalk at: #World put: Project current world' will restore prior behavior.", how many people will know that when they attempt to refloat an ancient piece of Squeak code? > > > > As far as we only have these two options, I'm tending to vote for the first one. Unless we use it in the Trunk, it does no harm, and actual bugs should occur rather seldom because some third-party package is still using #World. If there should be a bug somewhere, it can be easily fixed. > > > > > > But hypothetically, there would be a third option, which would be my favorite: Handle accesses to #World like calls on a deprecated method, i.e. by signaling a DeprecationWarning. That way we could even make sure that novices do not learn to use a deprecated variable. > > > > This could be realized by wrapping the value of #World into an ObjectTracer or a similar class that raises a DeprecationWarning on every call that is made to it. > > > > But I fear this could be a piece of overengineering, what do you think? Also, this would not protect the global variable from being reassigned ... > > > > The very last solution could be to make a (global) list of deprecated bindings and raise a Warning from the Compiler when an attempt is made to access one of them. Unfortunately, this would also be the most invasive change. > > > > > > @Levente: It seemed kind of overengineering to me to create three subclasses of ProcessLocalVariable for this purpose. Or am I misunderstand their concept? Maybe we could also need something like a PluggableProcessLocalVariable that can be instantiated with two accessor blocks? > > > > > > Best, > > > > Christoph > > > > ________________________________ > > Von: Squeak-dev im Auftrag von David T. Lewis > > Gesendet: Samstag, 12. September 2020 21:10:59 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > > > On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > > > Glad you like it, David! :-) > > > > > > > > > > > > > In the case of the global World variable, we were able to make it an instance variable in Project. > > > > > > I heard of this, but where can you see the difference? If I evaluate "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World is still listed in my Smalltalk bindings. > > > > > > I just put System-dtl.1170 in the inbox to make it go away. For a long > > time, the World variable was shared all over the place, even in MVC if > > you can believe that. From a modularity perspective it was a real mess. > > Basically, the solution was to make it an instance variable in Project. > > The current project always has a world, and when transitioning from one > > project to another, you can do the handoff between those two worlds > > without referencing a global. > > > > All that got done about two years ago, but I intentionally left the #World > > binding in place on the theory that there might be external packages (or > > Etoys projects) that expect to have access to global World. > > > > The way it works now (see MorphicProject>>setWorld:) is that the global > > World will be updated as before, but this happens if and only if the > > global binding exists. If you remove the binding, the World no longer > > exists. If you load a package or project that does have references to > > World, then restoring the #World binding in the current environment should > > make it work again. > > > > I don't really know if it's a good idea to remove the #World binding now > > (that's why it's in the inbox). But leaving it in the image is confusing > > because it gives the wrong impression that it is still being used. > > > > Dave > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Mon Sep 14 11:45:39 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Mon, 14 Sep 2020 13:45:39 +0200 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> Message-ID: <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> > On 14.09.2020, at 13:36, Thiede, Christoph wrote: > > I tested this in a fresh trunk image before deleting any binding. ah, well, But it doesn't find it's world, either way :D -t From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:49:10 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:49:10 +0000 Subject: [squeak-dev] Squeak needs a third-party package updater In-Reply-To: References: , Message-ID: Hi all, according to the activity of this thread, there seems to be a rather great demand for a solution to this problem indeed. Do I hear that it could be an option to make SqueakMap support different repository sources as well? Only theoretically, would this only require to change some URLs and SM already works with Metacello repositories, or would it be necessary to write a larger amount of adapter logic between both components? > I would also extend the idea with being able to upgrade the VM and dlls within it, but for now that should be on the back burner. >From my perspective, developing an updater for every host system would be a larger task, considering the different application registries for Windows, Linux, etc. ... Wouldn't it be simpler to put the VM into the different stores, i.e. Windows Store for Windows, apt for Linux etc.? > So keep going, This is an excellent direction. Just to be clear, at the moment this was only a question arisen from curiosity. :-) My list of interesting Squeak projects is already long enough to fill a couple of years, it's all about prioritization ... :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von tim Rowledge Gesendet: Sonntag, 13. September 2020 01:28:54 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Squeak needs a third-party package updater > On 2020-09-12, at 3:22 PM, Chris Muller wrote: > > Just to be clear, SqueakMap is not a VCS but a portal. Since it's just a database of Smalltalk scripts, it works as a portal to configure via anything -- Monticello, Git, Bitbucket, SVN, CVS, ENVY, whether a development workstation or end-user app. It's meant to be the place that answers the question, "How do I load XYZ package?" And it's a damn good answer - when it's kept up to date properly. Any ways we can be better at this should be considered. Not least making sure the SM server is kept up to date and running well so that the package scripts can be easily maintained. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Shift to the left! Shift to the right! Pop up, push down, byte, byte, byte! -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:53:14 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:53:14 +0000 Subject: [squeak-dev] logging to Transcript from not-UI process In-Reply-To: References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> <20200913190638.GA75346@shell.msen.com> <4333e0a5-3650-eae0-54e6-208f1704db53@pm.me> <6F1F27CE-418D-41AB-A1F2-DE54584533E8@rowledge.org>, Message-ID: <29a138384ec04f419c6d306a76ca11dc@student.hpi.uni-potsdam.de> It would also be great if the model of a transcript window would no longer be a subinstance of Behavior but one of Model. The current situation has led to several bugs in the past where a selector from StringHolder or Model has been missing, e.g. #spawn:. ________________________________ Von: Squeak-dev im Auftrag von Robert Withers via Squeak-dev Gesendet: Montag, 14. September 2020 07:27:19 An: The general-purpose Squeak developers list; tim Rowledge Betreff: Re: [squeak-dev] logging to Transcript from not-UI process On 9/13/20 10:28 PM, tim Rowledge wrote: >> On 2020-09-13, at 1:03 PM, Robert Withers via Squeak-dev wrote: >> >> I'll throw onto this discussion. I believe tim tested the TraceMonitor, >> but went with Toothpick, which I have never used. > There's nothing particularly wrong with TraceMonitor but Toothpick has a lot more options and has a very nice explanatory wiki on squeaksource. Explaining in order to be able to understand how to use it is always a winning strategy. Now, it is indeed true that I had to find the doc buried deep in the Wayback Machine and convert it to the wiki myself... Yes, indeed. This is the exact issue I am facing with PromisesLocal, providing a good explanation of its operation. What is the link to the Toothpick wiki page? I found the SqueakSource project with some documentation: http://www.squeaksource.com/Toothpick.html. I believe Toothpick uses Aspects, so in this regard it may be easier to add Logging to some code, than with TraceMonitor. In TraceMonitor one must start monitoring by sending 'traceMonitor monitor: mainObject', there after the mainObject can send 'mainObject monitor: subObject' and so on, to allow subObjects to send #etrace:msg: and be monitored...my paltry documentation, here. :/ K, r -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:54:28 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:54:28 +0000 Subject: [squeak-dev] autocomplete exit In-Reply-To: References: Message-ID: <63a34e02f15346e5b99b06b9df4f2921@student.hpi.uni-potsdam.de> Which completion are you using? Autocompletion already behaves like this. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Montag, 14. September 2020 04:45:30 An: The general-purpose Squeak developers list Betreff: [squeak-dev] autocomplete exit Hi, would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 11:58:53 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 11:58:53 +0000 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: References: <585c515c-14db-2d51-7e12-c9aea36e317e@gmail.com>, Message-ID: <55910d19f2b04d1695c8c9883b3fa51b@student.hpi.uni-potsdam.de> Hi Subbu, > What if a START log is lazily written just before the first write to the change log? > > If no changes are made then the changes file remains untouched. The first write to the change log is prefixed with a START log entry. A QUIT/NO SAVE entry is not needed or may be added if and only if a START log was written. -1. :-) I think Chris found exactly the right words about an important drawback of such behavior: > Not logging when there is no change sounds reasonable at first blush, but there are also Squeak systems that run a fixed image that don't save on exit -- it may be useful for operators of those systems to be able to know when there were interactions with the system, regardless whether development causing .changes changes was done or not. IMO this sounds like something that you should be able to control via a special VM flag: ./squeak my.image --quite-changes-logging # Does not write startup/shutdown messages to the changes file Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Sonntag, 13. September 2020 18:25:10 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] a few small quit issues I want to note Hi Subbu, > On Sep 13, 2020, at 4:27 AM, K K Subbu wrote: > > On 12/09/20 8:01 pm, Eliot Miranda wrote: >> That makes sense. So we do want the QUIT/NO SAVE indication written to the changes file if anything is written to the changes file because we know that when doing crash recovery we want to ignore everything before QUIT/NO SAVE. So, Chris Muller’s objections aside, I would argue that if the changes file is undisturbed (nothing written to it in the current session) when the system is about to quit without saving, it should do so without writing the QUIT/NO SAVE stamp. > > What if a START log is lazily written just before the first write to the change log? > > If no changes are made then the changes file remains untouched. The first write to the change log is prefixed with a START log entry. A QUIT/NO SAVE entry is not needed or may be added if and only if a START log was written. Good idea. That would work. > > Regards .. Subbu > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forums.jakob at resfarm.de Mon Sep 14 11:58:49 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Mon, 14 Sep 2020 13:58:49 +0200 Subject: [squeak-dev] Squeak needs a third-party package updater In-Reply-To: References: Message-ID: Am Mo., 14. Sept. 2020 um 13:49 Uhr schrieb Thiede, Christoph : > > Do I hear that it could be an option to make SqueakMap support different repository sources as well? Only theoretically, would this only require to change some URLs and SM already works with Metacello repositories, or would it be necessary to write a larger amount of adapter logic between both components? > As far as I know SqueakMap just hosts scripts, which can be anything, including `Metacello new baseline: '...'; repository: '...'; load`. From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 12:17:36 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 12:17:36 +0000 Subject: [squeak-dev] Compiler evaluate: '' | 'inner' argument in parser Message-ID: <459d23268a2141dd87d2a951c5b02366@student.hpi.uni-potsdam.de> Hi all, I just evaluated the following line and was surprised by the result: Compiler evaluate: '' for: 42. The answer was nil, whereas I rather would have expected the answer to life the universe and everything 42, as it is the case for full methods compiled on an object. What do you think about this, which should be the right output? I was even more surprised after taking a look into the relevant Parser implementation, which is #statements:innerBlock:blockNode:. Actually, the 'inner' argument's sole purpose is to use 'nil' instead of 'self' as a return value if there are zero statements. If we would not like to have this extra rule for doIt snippets, we would only have to change #method:context:[encoder:] and pass 'false' instead of 'doit' to #statements:innerBlock:. I'm probably missing some logical consequences of the syntactic concept of blocks, methods, and doits, so I would like to hear your thoughts on this. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 12:27:25 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 12:27:25 +0000 Subject: [squeak-dev] Changeset: Deprecate SyntaxErrorNotification >> #errorMessage Message-ID: Hi all, please have a look at the attached changeset. It deprecates the IMO redundant instance variable 'errorMessage' in SyntaxErrorNotification and uses super's 'messageText' instead. The two single senders in the Trunk image are patched as well. I wonder at all how the usage in DecompilerTests could have worked before, giving the prior implementation of #messageText in SyntaxErrorNotification ... Please review and merge if you like it. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: SyntaxErrorNotification errorMessage.1.cs URL: From eliot.miranda at gmail.com Mon Sep 14 12:32:12 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 14 Sep 2020 05:32:12 -0700 Subject: [squeak-dev] Compiler evaluate: '' | 'inner' argument in parser In-Reply-To: <459d23268a2141dd87d2a951c5b02366@student.hpi.uni-potsdam.de> References: <459d23268a2141dd87d2a951c5b02366@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, > On Sep 14, 2020, at 5:17 AM, Thiede, Christoph wrote: > > Hi all, > > I just evaluated the following line and was surprised by the result: > > Compiler evaluate: '' for: 42. > > The answer was nil, whereas I rather would have expected the answer to life the universe and everything 42, as it is the case for full methods compiled on an object. What do you think about this, which should be the right output? > > I was even more surprised after taking a look into the relevant Parser implementation, which is #statements:innerBlock:blockNode:. Actually, the 'inner' argument's sole purpose is to use 'nil' instead of 'self' as a return value if there are zero statements. If we would not like to have this extra rule for doIt snippets, we would only have to change #method:context:[encoder:] and pass 'false' instead of 'doit' to #statements:innerBlock:. > > I'm probably missing some logical consequences of the syntactic concept of blocks, methods, and doits, so I would like to hear your thoughts on this. > Smells like my bug. I agree with you that the null statement should evaluate to self at method level. IIRC, the ANSI standard says that the null statement evaluated to the last block argument did blocks with arguments (to legalize a quirk in Peter Deutsch’s closure compiler for ObjectWorks 2.5 and subsequent). And clearly the null statement in a block evaluates to nil. I hunk that the null statement should evaluate to self at method level and nil at block level, irrespective of the number of block arguments. To be clear I would have these tests self assert: (Compiler evaluate: '' for: 42) equals: 42. self assert: ([:arg|] value: 42) equals: nil. self assert: [] value equals: nil. > Best, > > Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Mon Sep 14 13:29:00 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 14 Sep 2020 06:29:00 -0700 Subject: [squeak-dev] Compiler evaluate: '' | 'inner' argument in parser In-Reply-To: <459d23268a2141dd87d2a951c5b02366@student.hpi.uni-potsdam.de> References: <459d23268a2141dd87d2a951c5b02366@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, ignore my first response. I hadn't looked at the code yet. It hasn't changed my conclusions, but it alters the proposed solution. On Mon, Sep 14, 2020 at 5:17 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi all, > > I just evaluated the following line and was surprised by the result: > > Compiler evaluate: '' for: 42. > > The answer was nil, whereas I rather would have expected the answer to > life the universe and everything 42, as it is the case for full methods > compiled on an object. What do you think about this, which should be the > right output? > I agree with you that the null statement should evaluate to self at method level. IIRC, the ANSI standard says that the null statement evaluated to the last block argument did blocks with arguments (to legalize a quirk in Peter Deutsch’s closure compiler for ObjectWorks 2.5 and subsequent). And clearly the null statement in a block evaluates to nil. I hunk that the null statement should evaluate to self at method level and nil at block level, irrespective of the number of block arguments. To be clear I would have these tests self assert: (Compiler evaluate: '' for: 42) equals: 42. self assert: ([:arg|] value: 42) equals: nil. self assert: [] value equals: nil. Looking at the code, when I added closures I took the existing compiler as a given and hence perpetuated the bug that (Compiler evaluate: '' for: 42) isNil. Looking at the code I don't like the inner: argument, and for the longest time I have felt like the statements:innerBlock: method was unnecessary. So one fix in Parser>>#method:context: is to replace self statements: #() innerBlock: doit. with self statements: #() innerBlock: false blockNode: BlockNode new. Another improvement would be to simply substitute the variable to be used for empty statements, so we would use Parser>>#method:context: ... self statements: #() defaultVariable: 'self' blockNode: BlockNode new. ... Parser>>#statements: argNodes defaultVariable: selfOrNil blockNode: theBlockNode ... ifFalse: [self addComment. stmts size = 0 ifTrue: [stmts addLast: (encoder encodeVariable: selfOrNil)]]]. ... Parser>>#blockExpression ... self statements: variableNodes defaultVariable: 'nil' blockNode: blockNode. ... but maybe the following: Parser>>#method:context: ... doit ifTrue: [blk returnLast] ifFalse: [blk returnSelfIfNoOther: encoder]. ... indicates that either stmts size = 0 ifTrue: [stmts addLast: (encoder encodeVariable: selfOrNil)] is wrong and the defaulting should happen in the sender (in Parser>>#method:context: and Parser>>#primaryExpression where it sends blockExpression), or that we should pass in a selector which is performed in the method to determine what to do. So the method would then be e.g. Parser>>#statements:blockNode:ifEmpty: and the senders would look like, e.g. Parser>>#method:context: ... self statements: #() blockNode: BlockNode new ifEmpty: (doit ifTrue: [#returnLast:] ifFalse: [#returnSelfIfNoOther:). blk := parseNode. hereType == #doIt ifFalse: [^self expected: 'Nothing more']. ... Parser>>#blockExpression ... self statements: variableNodes blockNode: BlockNode new ifEmpty: #returnNilIfEmpty:. ... I'm interested in your reading. I think you should implement whatever makes most sense to you. I'm happy to rebiew. I was even more surprised after taking a look into the relevant Parser > implementation, which is #statements:innerBlock:blockNode:. Actually, the > 'inner' argument's sole purpose is to use 'nil' instead of 'self' as a > return value if there are zero statements. If we would not like to have > this extra rule for doIt snippets, we would only have to change > #method:context:[encoder:] and pass 'false' instead of 'doit' to > #statements:innerBlock:. > > I'm probably missing some logical consequences of the syntactic concept of > blocks, methods, and doits, so I would like to hear your thoughts on this. > This is not so much philosophical as rushed. When I added closures I wanted not to break the compiler and DTSTTCPW because I had a VM to write. I wanted to clean things up but I risked making too many mistakes so I erred on the side of minimally extending the compiler rather than making it better. > Best, > > Christoph > _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Mon Sep 14 15:01:12 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 11:01:12 -0400 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> Message-ID: <20200914150112.GA80288@shell.msen.com> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > > > On 14.09.2020, at 13:36, Thiede, Christoph wrote: > > > > I tested this in a fresh trunk image before deleting any binding. > > ah, well, > But it doesn't find it's world, either way :D > -t > I see very different errors when I try to load an old Etoys project. I do not know why, and I don't have time to debug it. That is why I am hoping that someone (Karl perhaps) has a trunk-level image that *can* load old projects. If so, then my question is: What sort of errors or failure do you actually see when you load one of those projects after "Smalltalk removeKey: #World"? It may or may not be a problem that needs to be fixed. It is hard to know without actually seeing the problem. Dave From karlramberg at gmail.com Mon Sep 14 15:31:20 2020 From: karlramberg at gmail.com (karl ramberg) Date: Mon, 14 Sep 2020 17:31:20 +0200 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <20200914150112.GA80288@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> Message-ID: While looking at this error I found another issue I think is a bug. I can't explore IndentityDictionary but I can inspect it. [image: bild.png] [image: bild.png] Best, Karl On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis wrote: > On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > > > > > On 14.09.2020, at 13:36, Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > > > > I tested this in a fresh trunk image before deleting any binding. > > > > ah, well, > > But it doesn't find it's world, either way :D > > -t > > > > I see very different errors when I try to load an old Etoys project. I do > not know why, and I don't have time to debug it. That is why I am hoping > that > someone (Karl perhaps) has a trunk-level image that *can* load old > projects. > > If so, then my question is: What sort of errors or failure do you actually > see when you load one of those projects after "Smalltalk removeKey: > #World"? > > It may or may not be a problem that needs to be fixed. It is hard to know > without actually seeing the problem. > > Dave > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 35457 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 19686 bytes Desc: not available URL: From kksubbu.ml at gmail.com Mon Sep 14 15:38:02 2020 From: kksubbu.ml at gmail.com (K K Subbu) Date: Mon, 14 Sep 2020 21:08:02 +0530 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <55910d19f2b04d1695c8c9883b3fa51b@student.hpi.uni-potsdam.de> References: <585c515c-14db-2d51-7e12-c9aea36e317e@gmail.com> <55910d19f2b04d1695c8c9883b3fa51b@student.hpi.uni-potsdam.de> Message-ID: <729b1c39-e4a2-54ab-dd26-fcd1a32ec608@gmail.com> On 14/09/20 5:28 pm, Thiede, Christoph wrote: > > Not logging when there is no change sounds reasonable at first blush, > but there are also Squeak systems that run a fixed image that don't save > on exit -- it may be useful for operators of those systems to be able to > know when there were interactions with the system, regardless whether > development causing .changes changes was done or not. Changes file tracks source code *modifications* to an image. Writing entries to change log just to detect if an image was run is an overkill, particularly when the file is stored in flash memory. There must be cheaper ways to log such runs. Any specific use cases? Regards .. Subbu From lewis at mail.msen.com Mon Sep 14 15:49:34 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 11:49:34 -0400 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> Message-ID: <20200914154934.GA85344@shell.msen.com> On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong? Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares. From commits at source.squeak.org Mon Sep 14 16:27:50 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 14 Sep 2020 16:27:50 0000 Subject: [squeak-dev] The Trunk: Collections-mt.908.mcz Message-ID: Marcel Taeumel uploaded a new version of Collections to project The Trunk: http://source.squeak.org/trunk/Collections-mt.908.mcz ==================== Summary ==================== Name: Collections-mt.908 Author: mt Time: 14 September 2020, 6:27:49.862914 pm UUID: abfcedd9-2516-7c49-b6e9-714619e50986 Ancestors: Collections-eem.907 Fixes some error messages regarding #become(Forward):. =============== Diff against Collections-eem.907 =============== Item was changed: ----- Method: Array>>elementsExchangeIdentityWith: (in category 'converting') ----- elementsExchangeIdentityWith: otherArray "This primitive performs a bulk mutation, causing all pointers to the elements of the receiver to be replaced by pointers to the corresponding elements of otherArray. At the same time, all pointers to the elements of otherArray are replaced by pointers to the corresponding elements of this array. The identityHashes remain with the pointers rather than with the objects so that objects in hashed structures should still be properly indexed after the mutation." ec == #'no modification' ifTrue: [^self modificationForbiddenFor: otherArray becomeSelector: #elementsExchangeIdentityWith:]. ec == #'bad receiver' ifTrue: [^self error: 'receiver must be of class Array']. ec == #'bad argument' ifTrue: [^self error: (otherArray class == Array + ifFalse: ['arg must be of class Array'] + ifTrue: ['receiver and argument must have the same size'])]. - ifTrue: ['arg must be of class Array'] - ifFalse: ['receiver and argument must have the same size'])]. ec == #'inappropriate operation' ifTrue: [^self error: 'can''t become immediates such as SmallIntegers or Characters']. ec == #'object is pinned' ifTrue: [^self error: 'can''t become pinned objects']. ec == #'insufficient object memory' ifTrue: [| maxRequired | "In Spur, two-way become may involve making each pair of objects into a forwarder into a copy of the other. So if become fails with #'insufficient object memory', garbage collect, and if necessary, grow memory." maxRequired := (self detectSum: [:obj | obj class byteSizeOfInstanceOfSize: obj basicSize]) + (otherArray detectSum: [:obj | obj class byteSizeOfInstanceOfSize: obj basicSize]). (Smalltalk garbageCollectMost < maxRequired and: [Smalltalk garbageCollect < maxRequired]) ifTrue: [Smalltalk growMemoryByAtLeast: maxRequired]. ^self elementsExchangeIdentityWith: otherArray]. self primitiveFailed! Item was changed: ----- Method: Array>>elementsForwardIdentityAndHashTo: (in category 'converting') ----- elementsForwardIdentityAndHashTo: otherArray "This primitive performs a bulk mutation, causing all pointers to the elements of the receiver to be replaced by pointers to the corresponding elements of otherArray. The identityHashes remain with the pointers rather than with the objects so that the objects in this array should still be properly indexed in any existing hashed structures after the mutation." ec == #'no modification' ifTrue: [^self modificationForbiddenFor: otherArray becomeSelector: #elementsForwardIdentityAndHashTo:]. ec == #'bad receiver' ifTrue: [^self error: 'receiver must be of class Array']. ec == #'bad argument' ifTrue: [^self error: (otherArray class == Array + ifFalse: ['arg must be of class Array'] + ifTrue: ['receiver and argument must have the same size'])]. - ifTrue: ['arg must be of class Array'] - ifFalse: ['receiver and argument must have the same size'])]. ec == #'inappropriate operation' ifTrue: [^self error: 'can''t become immediates such as SmallIntegers or Characters']. ec == #'object is pinned' ifTrue: [^self error: 'can''t become pinned objects']. ec == #'insufficient object memory' ifTrue: [self error: 'The virtual machine is out-of-date. Please upgrade.']. self primitiveFailed! Item was changed: ----- Method: Array>>elementsForwardIdentityTo: (in category 'converting') ----- elementsForwardIdentityTo: otherArray "This primitive performs a bulk mutation, causing all pointers to the elements of the receiver to be replaced by pointers to the corresponding elements of otherArray. The identityHashes are not copied to the target objects so that the objects in otherArray should still be properly indexed in any existing hashed structures after the mutation." ec == #'no modification' ifTrue: [^self modificationForbiddenFor: otherArray becomeSelector: #elementsForwardIdentityTo:]. ec == #'bad receiver' ifTrue: [^self error: 'receiver must be of class Array']. ec == #'bad argument' ifTrue: [^self error: (otherArray class == Array + ifFalse: ['arg must be of class Array'] + ifTrue: ['receiver and argument must have the same size'])]. - ifTrue: ['arg must be of class Array'] - ifFalse: ['receiver and argument must have the same size'])]. ec == #'inappropriate operation' ifTrue: [^self error: 'can''t become immediates such as SmallIntegers or Characters']. ec == #'object is pinned' ifTrue: [^self error: 'can''t become pinned objects']. ec == #'insufficient object memory' ifTrue: [self error: 'The virtual machine is out-of-date. Please upgrade.']. self primitiveFailed! Item was changed: ----- Method: Array>>elementsForwardIdentityTo:copyHash: (in category 'converting') ----- elementsForwardIdentityTo: otherArray copyHash: copyHash "This primitive performs a bulk mutation, causing all pointers to the elements of the receiver to be replaced by pointers to the corresponding elements of otherArray. If copyHash is true, the identityHashes remain with the pointers rather than with the objects so that the objects in the receiver should still be properly indexed in any existing hashed structures after the mutation. If copyHash is false, then the hashes of the objects in otherArray remain unchanged. If you know what you're doing this may indeed be what you want." ec == #'no modification' ifTrue: [^self modificationForbiddenFor: otherArray argument: copyHash becomeSelector: #elementsForwardIdentityTo:copyHash:]. ec == #'bad receiver' ifTrue: [^self error: 'receiver must be of class Array']. ec == #'bad argument' ifTrue: [^self error: (otherArray class == Array + ifFalse: ['arg must be of class Array'] + ifTrue: ['receiver and argument must have the same size'])]. - ifTrue: ['arg must be of class Array'] - ifFalse: ['receiver and argument must have the same size'])]. ec == #'inappropriate operation' ifTrue: [^self error: 'can''t become immediates such as SmallIntegers or Characters']. ec == #'object is pinned' ifTrue: [^self error: 'can''t become pinned objects']. self primitiveFailed! From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 16:35:37 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 16:35:37 +0000 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de>, Message-ID: Hi Jakob, do you have a description of the correct loading order? In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Jakob Reschke Gesendet: Donnerstag, 10. September 2020 08:04:51 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] SyntaxError while loading FFI Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph : > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 16:38:51 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 16:38:51 +0000 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com>, Message-ID: <1ac3fab6155c428980e6b48939488ecd@student.hpi.uni-potsdam.de> Assuming you meant Smalltalk globals removeKey: #World, I receive another error ... "Key not found: a SketchMorph(3425711)", still raised by the SmartRefStream >> #convert2:allVarMaps:. Dave, can't you reproduce this in your image? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Montag, 14. September 2020 17:31:20 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) While looking at this error I found another issue I think is a bug. I can't explore IndentityDictionary but I can inspect it. [bild.png] [bild.png] Best, Karl On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis > wrote: On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > > > On 14.09.2020, at 13:36, Thiede, Christoph > wrote: > > > > I tested this in a fresh trunk image before deleting any binding. > > ah, well, > But it doesn't find it's world, either way :D > -t > I see very different errors when I try to load an old Etoys project. I do not know why, and I don't have time to debug it. That is why I am hoping that someone (Karl perhaps) has a trunk-level image that *can* load old projects. If so, then my question is: What sort of errors or failure do you actually see when you load one of those projects after "Smalltalk removeKey: #World"? It may or may not be a problem that needs to be fixed. It is hard to know without actually seeing the problem. Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 35457 bytes Desc: bild.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 19686 bytes Desc: bild.png URL: From karlramberg at gmail.com Mon Sep 14 16:44:47 2020 From: karlramberg at gmail.com (karl ramberg) Date: Mon, 14 Sep 2020 18:44:47 +0200 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> Message-ID: There is some change that broke project loading. I can load CarAndPen.014.pr in image update: #19422 In #19616 I get the "Key not found:" error from IdentityDictionary Best, Karl On Mon, Sep 14, 2020 at 5:31 PM karl ramberg wrote: > While looking at this error I found another issue I think is a bug. > I can't explore IndentityDictionary but I can inspect it. > > [image: bild.png] > > [image: bild.png] > Best, > Karl > > On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis > wrote: > >> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: >> > >> > > On 14.09.2020, at 13:36, Thiede, Christoph < >> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >> > > >> > > I tested this in a fresh trunk image before deleting any binding. >> > >> > ah, well, >> > But it doesn't find it's world, either way :D >> > -t >> > >> >> I see very different errors when I try to load an old Etoys project. I do >> not know why, and I don't have time to debug it. That is why I am hoping >> that >> someone (Karl perhaps) has a trunk-level image that *can* load old >> projects. >> >> If so, then my question is: What sort of errors or failure do you actually >> see when you load one of those projects after "Smalltalk removeKey: >> #World"? >> >> It may or may not be a problem that needs to be fixed. It is hard to know >> without actually seeing the problem. >> >> Dave >> >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 35457 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 19686 bytes Desc: not available URL: From tim at rowledge.org Mon Sep 14 16:56:57 2020 From: tim at rowledge.org (tim Rowledge) Date: Mon, 14 Sep 2020 09:56:57 -0700 Subject: [squeak-dev] *****SPAM***** Re: logging to Transcript from not-UI process In-Reply-To: References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> <20200913190638.GA75346@shell.msen.com> <4333e0a5-3650-eae0-54e6-208f1704db53@pm.me> <6F1F27CE-418D-41AB-A1F2-DE54584533E8@rowledge.org> Message-ID: <7B23A112-226D-44E9-AB74-C88B812B879A@rowledge.org> > On 2020-09-13, at 10:27 PM, Robert Withers wrote: > > Yes, indeed. This is the exact issue I am facing with PromisesLocal, > providing a good explanation of its operation. What is the link to the > Toothpick wiki page? I found the SqueakSource project with some > documentation: http://www.squeaksource.com/Toothpick.html. It's the tab at the near-top of the page conveniently labelled 'Wiki' :-) tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Useful Latin Phrases:- Ventis secundis, tene cursum. = Go with the flow. From commits at source.squeak.org Mon Sep 14 17:40:22 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 14 Sep 2020 17:40:22 0000 Subject: [squeak-dev] The Trunk: Collections-eem.909.mcz Message-ID: Eliot Miranda uploaded a new version of Collections to project The Trunk: http://source.squeak.org/trunk/Collections-eem.909.mcz ==================== Summary ==================== Name: Collections-eem.909 Author: eem Time: 14 September 2020, 10:40:20.674349 am UUID: de499e36-5394-4102-86f7-aa1def93b595 Ancestors: Collections-mt.908 Make TranscriptStream's access protect an inst var and a Mutex. =============== Diff against Collections-mt.908 =============== Item was changed: WriteStream subclass: #TranscriptStream + instanceVariableNames: 'lastChar lock' + classVariableNames: 'CharacterLimit ForceUpdate RedirectToStdOut' - instanceVariableNames: 'lastChar' - classVariableNames: 'AccessSema CharacterLimit ForceUpdate RedirectToStdOut' poolDictionaries: '' category: 'Collections-Streams'! !TranscriptStream commentStamp: 'fbs 12/30/2013 09:53' prior: 0! This class is a much simpler implementation of Transcript protocol that supports multiple views and very simple conversion to morphic. Because it inherits from Stream, it is automatically compatible with code that is designed to write to streams.! Item was changed: ----- Method: TranscriptStream>>endEntry (in category 'stream extensions') ----- endEntry "Display all the characters since the last endEntry, and reset the stream" + self lock critical: + [self changed: (self class forceUpdate + ifTrue: [#appendEntry] + ifFalse: [self changed: #appendEntryLater])]! - self semaphore critical:[ - self class forceUpdate - ifTrue: [self changed: #appendEntry] - ifFalse: [self changed: #appendEntryLater]. - self reset. - ].! Item was added: + ----- Method: TranscriptStream>>lock (in category 'private') ----- + lock + ^lock ifNil:[lock := Mutex new]! Item was removed: - ----- Method: TranscriptStream>>semaphore (in category 'private') ----- - semaphore - ^AccessSema ifNil:[AccessSema := Semaphore forMutualExclusion]! From tim at rowledge.org Mon Sep 14 17:40:23 2020 From: tim at rowledge.org (tim Rowledge) Date: Mon, 14 Sep 2020 10:40:23 -0700 Subject: [squeak-dev] a few small quit issues I want to note In-Reply-To: <729b1c39-e4a2-54ab-dd26-fcd1a32ec608@gmail.com> References: <585c515c-14db-2d51-7e12-c9aea36e317e@gmail.com> <55910d19f2b04d1695c8c9883b3fa51b@student.hpi.uni-potsdam.de> <729b1c39-e4a2-54ab-dd26-fcd1a32ec608@gmail.com> Message-ID: <04CD69D3-C57D-4B38-8FE2-73E7152AB702@rowledge.org> > On 2020-09-14, at 8:38 AM, K K Subbu wrote: > > On 14/09/20 5:28 pm, Thiede, Christoph wrote: >> > Not logging when there is no change sounds reasonable at first blush, but there are also Squeak systems that run a fixed image that don't save on exit -- it may be useful for operators of those systems to be able to know when there were interactions with the system, regardless whether development causing .changes changes was done or not. > > Changes file tracks source code *modifications* to an image. Writing entries to change log just to detect if an image was run is an overkill, particularly when the file is stored in flash memory. Agreed, and the flash stuff is an interesting extra point. > There must be cheaper ways to log such runs. > > Any specific use cases? This is why I'm thinking something like toothpick would be good as a way to do *all the logging* whether of code changes going to the changelog, or error messages going to a unix syslog and the Transcript and the local stderr, or general status updates going via MQTT to a sysadmin console a continent away. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim New: It comes in different colors from the previous version. From eliot.miranda at gmail.com Mon Sep 14 17:41:27 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 14 Sep 2020 10:41:27 -0700 Subject: [squeak-dev] The Trunk: Collections-eem.909.mcz In-Reply-To: References: Message-ID: Get your Collections commit in quickly because in the comment you get to state that this is the One After 909 ;-) On Mon, Sep 14, 2020 at 10:40 AM wrote: > Eliot Miranda uploaded a new version of Collections to project The Trunk: > http://source.squeak.org/trunk/Collections-eem.909.mcz > > ==================== Summary ==================== > > Name: Collections-eem.909 > Author: eem > Time: 14 September 2020, 10:40:20.674349 am > UUID: de499e36-5394-4102-86f7-aa1def93b595 > Ancestors: Collections-mt.908 > > Make TranscriptStream's access protect an inst var and a Mutex. > > =============== Diff against Collections-mt.908 =============== > > Item was changed: > WriteStream subclass: #TranscriptStream > + instanceVariableNames: 'lastChar lock' > + classVariableNames: 'CharacterLimit ForceUpdate RedirectToStdOut' > - instanceVariableNames: 'lastChar' > - classVariableNames: 'AccessSema CharacterLimit ForceUpdate > RedirectToStdOut' > poolDictionaries: '' > category: 'Collections-Streams'! > > !TranscriptStream commentStamp: 'fbs 12/30/2013 09:53' prior: 0! > This class is a much simpler implementation of Transcript protocol that > supports multiple views and very simple conversion to morphic. Because it > inherits from Stream, it is automatically compatible with code that is > designed to write to streams.! > > Item was changed: > ----- Method: TranscriptStream>>endEntry (in category 'stream > extensions') ----- > endEntry > "Display all the characters since the last endEntry, and reset the > stream" > + self lock critical: > + [self changed: (self class forceUpdate > + ifTrue: > [#appendEntry] > + ifFalse: [self > changed: #appendEntryLater])]! > - self semaphore critical:[ > - self class forceUpdate > - ifTrue: [self changed: #appendEntry] > - ifFalse: [self changed: #appendEntryLater]. > - self reset. > - ].! > > Item was added: > + ----- Method: TranscriptStream>>lock (in category 'private') ----- > + lock > + ^lock ifNil:[lock := Mutex new]! > > Item was removed: > - ----- Method: TranscriptStream>>semaphore (in category 'private') ----- > - semaphore > - ^AccessSema ifNil:[AccessSema := Semaphore forMutualExclusion]! > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Mon Sep 14 17:44:30 2020 From: karlramberg at gmail.com (karl ramberg) Date: Mon, 14 Sep 2020 19:44:30 +0200 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> Message-ID: So I narrowed it down a little more. In image #19537 I can both Inspect and Explore IdentityDictionary allVarMaps here: SmartRefStream>>convert2: partiallyCorrectInst allVarMaps: allVarMaps "Go through the normal instance conversion process and return a modern object." | className varMap | self flag: #bobconv. self halt. varMap := allVarMaps at: partiallyCorrectInst. className := varMap at: #ClassName. "original" ^self applyConversionMethodsTo: partiallyCorrectInst className: className varMap: varMap. In image 19582 it is broken. I can Inspect allVarMaps but Explore i give error 'Key not found:' So that is between 13 of mars and 16 of april 2020. Best, Karl On Mon, Sep 14, 2020 at 6:44 PM karl ramberg wrote: > There is some change that broke project loading. > I can load CarAndPen.014.pr in image update: #19422 > In #19616 I get the "Key not found:" error from IdentityDictionary > > Best, > Karl > > > > On Mon, Sep 14, 2020 at 5:31 PM karl ramberg > wrote: > >> While looking at this error I found another issue I think is a bug. >> I can't explore IndentityDictionary but I can inspect it. >> >> [image: bild.png] >> >> [image: bild.png] >> Best, >> Karl >> >> On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis >> wrote: >> >>> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: >>> > >>> > > On 14.09.2020, at 13:36, Thiede, Christoph < >>> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >>> > > >>> > > I tested this in a fresh trunk image before deleting any binding. >>> > >>> > ah, well, >>> > But it doesn't find it's world, either way :D >>> > -t >>> > >>> >>> I see very different errors when I try to load an old Etoys project. I do >>> not know why, and I don't have time to debug it. That is why I am hoping >>> that >>> someone (Karl perhaps) has a trunk-level image that *can* load old >>> projects. >>> >>> If so, then my question is: What sort of errors or failure do you >>> actually >>> see when you load one of those projects after "Smalltalk removeKey: >>> #World"? >>> >>> It may or may not be a problem that needs to be fixed. It is hard to know >>> without actually seeing the problem. >>> >>> Dave >>> >>> >>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 35457 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 19686 bytes Desc: not available URL: From commits at source.squeak.org Mon Sep 14 17:50:37 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 14 Sep 2020 17:50:37 0000 Subject: [squeak-dev] The Trunk: Collections-eem.910.mcz Message-ID: Eliot Miranda uploaded a new version of Collections to project The Trunk: http://source.squeak.org/trunk/Collections-eem.910.mcz ==================== Summary ==================== Name: Collections-eem.910 Author: eem Time: 14 September 2020, 10:50:35.614055 am UUID: e9380f4a-daa8-441f-bc2e-a5e6445e4452 Ancestors: Collections-eem.909 Travelling on the one after 909, fix a slip in that last fix. =============== Diff against Collections-eem.909 =============== Item was changed: ----- Method: TranscriptStream>>endEntry (in category 'stream extensions') ----- endEntry "Display all the characters since the last endEntry, and reset the stream" self lock critical: [self changed: (self class forceUpdate ifTrue: [#appendEntry] + ifFalse: [#appendEntryLater])]! - ifFalse: [self changed: #appendEntryLater])]! From robert.withers at pm.me Mon Sep 14 17:11:43 2020 From: robert.withers at pm.me (Robert Withers) Date: Mon, 14 Sep 2020 17:11:43 +0000 Subject: [squeak-dev] *****SPAM***** Re: logging to Transcript from not-UI process In-Reply-To: <7B23A112-226D-44E9-AB74-C88B812B879A@rowledge.org> References: <6205F264-C659-42EA-9D5B-5BECB041633C@rowledge.org> <20200913190638.GA75346@shell.msen.com> <4333e0a5-3650-eae0-54e6-208f1704db53@pm.me> <6F1F27CE-418D-41AB-A1F2-DE54584533E8@rowledge.org> <7B23A112-226D-44E9-AB74-C88B812B879A@rowledge.org> Message-ID: On 9/14/20 12:56 PM, tim Rowledge wrote: > On 2020-09-13, at 10:27 PM, Robert Withers wrote: >> Yes, indeed. This is the exact issue I am facing with PromisesLocal, >> providing a good explanation of its operation. What is the link to the >> Toothpick wiki page? I found the SqueakSource project with some >> documentation: http://www.squeaksource.com/Toothpick.html. > It's the tab at the near-top of the page conveniently labelled 'Wiki' :-) Ok, so I had already found the documentation you mentioned. Good. Thx. > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > Useful Latin Phrases:- Ventis secundis, tene cursum. = Go with the flow. > > > From commits at source.squeak.org Mon Sep 14 18:03:04 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 14 Sep 2020 18:03:04 0000 Subject: [squeak-dev] The Trunk: Collections-eem.911.mcz Message-ID: Eliot Miranda uploaded a new version of Collections to project The Trunk: http://source.squeak.org/trunk/Collections-eem.911.mcz ==================== Summary ==================== Name: Collections-eem.911 Author: eem Time: 14 September 2020, 11:03:02.426231 am UUID: 28806f32-6ba9-4ef5-b940-571d50d3f942 Ancestors: Collections-eem.910 Correct the redirect logc so that it is the Transcript, and not any old TranscriptStream, that gets redirected to stdout when the redirectToStdOut preference is set Fix a regression in the redefinition of endEntry (forgot to send reset). =============== Diff against Collections-eem.910 =============== Item was changed: ----- Method: TranscriptStream>>endEntry (in category 'stream extensions') ----- endEntry "Display all the characters since the last endEntry, and reset the stream" self lock critical: + [(self == Transcript and: [self class redirectToStdOut]) + ifTrue: + [FileStream stdout nextPutAll: self contents; flush] + ifFalse: + [self changed: (self class forceUpdate - [self changed: (self class forceUpdate ifTrue: [#appendEntry] + ifFalse: [#appendEntryLater])]. + self reset]! - ifFalse: [#appendEntryLater])]! Item was removed: - ----- Method: TranscriptStream>>nextPut: (in category 'stream extensions') ----- - nextPut: anObject - self target == self ifFalse: [self target nextPut: anObject]. "delegated to stdout" - ^ super nextPut: anObject.! Item was removed: - ----- Method: TranscriptStream>>nextPutAll: (in category 'stream extensions') ----- - nextPutAll: aCollection - self target == self ifFalse: [self target nextPutAll: aCollection]. "delegated to stdout" - ^ super nextPutAll: aCollection.! Item was changed: ----- Method: TranscriptStream>>target (in category 'stream extensions') ----- target + ^(self == Transcript and: [self class redirectToStdOut]) - - ^ self class redirectToStdOut ifTrue: [FileStream stdout] ifFalse: [self]! From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 14 18:29:56 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 14 Sep 2020 18:29:56 +0000 Subject: [squeak-dev] Dead exceptions Message-ID: <9d60e8b589bf4afb81404ed9b5e1f78e@student.hpi.uni-potsdam.de> Hi all, while browsing a bit through our exception framework, I stumbled upon a significant number of exception classes for all of which I neither found any sender in my Trunk image, nor any subclass: Abort EndOfStream ExceptionAboutToReturn FontSubstitutionDuringLoading NanError UndeclaredVariableReference So here is my question: Would you agree to deprecate them all? Or could any of them still be relevant for non-in-trunk senders? I can prepare a changeset if you think we should get rid of them. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Mon Sep 14 19:41:34 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 14 Sep 2020 12:41:34 -0700 Subject: [squeak-dev] Dead exceptions In-Reply-To: <9d60e8b589bf4afb81404ed9b5e1f78e@student.hpi.uni-potsdam.de> References: <9d60e8b589bf4afb81404ed9b5e1f78e@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Mon, Sep 14, 2020 at 11:30 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi all, > > while browsing a bit through our exception framework, I stumbled upon a > significant number of exception classes for all of which I neither found > any sender in my Trunk image, nor any subclass: > > Abort EndOfStream ExceptionAboutToReturn FontSubstitutionDuringLoading > NanError UndeclaredVariableReference > > So here is my question: Would you agree to deprecate them all? Or could > any of them still be relevant for non-in-trunk senders? I can prepare a > changeset if you think we should get rid of them. > I would like to see us keep at least EndOfStream. ExceptionAboutToReturn and Abort can be deleted, not just deprecated. They're ancient and obsolete. > Best, > > Christoph > _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Mon Sep 14 19:48:41 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 14 Sep 2020 12:48:41 -0700 Subject: [squeak-dev] autocomplete exit In-Reply-To: <63a34e02f15346e5b99b06b9df4f2921@student.hpi.uni-potsdam.de> References: <63a34e02f15346e5b99b06b9df4f2921@student.hpi.uni-potsdam.de> Message-ID: On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Which completion are you using? Autocompletion > already behaves like this. > OComplete > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Eliot Miranda > *Gesendet:* Montag, 14. September 2020 04:45:30 > *An:* The general-purpose Squeak developers list > *Betreff:* [squeak-dev] autocomplete exit > > Hi, > > would it be possible to make autocomplete exit whenever a non-selector > character was typed, or at least when an arrow key was typed? > > _,,,^..^,,,_ > best, Eliot > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Mon Sep 14 20:29:34 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 16:29:34 -0400 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> Message-ID: <20200914202934.GA25753@shell.msen.com> Karl, Thank you very much! Clearly we have introduced a bug in trunk since the 5.3 release, so we'll need to fix that. But to my original question - I was able to start with a fresh 5.3 image, and I can load CarAndPen.014.pr without problems. Then I removed the #World binding, and tried loading CarAndPen.014.pr to find out what the errors would look like. Big surprise - There was no error. The project still loads and runs as before. That is not what I expected, so I'm glad we checked. Dave On Mon, Sep 14, 2020 at 07:44:30PM +0200, karl ramberg wrote: > So I narrowed it down a little more. > In image #19537 I can both Inspect and Explore IdentityDictionary > allVarMaps here: > > SmartRefStream>>convert2: partiallyCorrectInst allVarMaps: allVarMaps > "Go through the normal instance conversion process and return a modern > object." > > | className varMap | > > self flag: #bobconv. > self halt. > varMap := allVarMaps at: partiallyCorrectInst. > className := varMap at: #ClassName. "original" > ^self applyConversionMethodsTo: partiallyCorrectInst className: className > varMap: varMap. > > > In image 19582 it is broken. I can Inspect allVarMaps but Explore i give > error 'Key not found:' > > So that is between 13 of mars and 16 of april 2020. > > > > > Best, > Karl > > > > > On Mon, Sep 14, 2020 at 6:44 PM karl ramberg wrote: > > > There is some change that broke project loading. > > I can load CarAndPen.014.pr in image update: #19422 > > In #19616 I get the "Key not found:" error from IdentityDictionary > > > > Best, > > Karl > > > > > > > > On Mon, Sep 14, 2020 at 5:31 PM karl ramberg > > wrote: > > > >> While looking at this error I found another issue I think is a bug. > >> I can't explore IndentityDictionary but I can inspect it. > >> > >> [image: bild.png] > >> > >> [image: bild.png] > >> Best, > >> Karl > >> > >> On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis > >> wrote: > >> > >>> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > >>> > > >>> > > On 14.09.2020, at 13:36, Thiede, Christoph < > >>> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >>> > > > >>> > > I tested this in a fresh trunk image before deleting any binding. > >>> > > >>> > ah, well, > >>> > But it doesn't find it's world, either way :D > >>> > -t > >>> > > >>> > >>> I see very different errors when I try to load an old Etoys project. I do > >>> not know why, and I don't have time to debug it. That is why I am hoping > >>> that > >>> someone (Karl perhaps) has a trunk-level image that *can* load old > >>> projects. > >>> > >>> If so, then my question is: What sort of errors or failure do you > >>> actually > >>> see when you load one of those projects after "Smalltalk removeKey: > >>> #World"? > >>> > >>> It may or may not be a problem that needs to be fixed. It is hard to know > >>> without actually seeing the problem. > >>> > >>> Dave > >>> > >>> > >>> > >>> > From lewis at mail.msen.com Mon Sep 14 20:45:08 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 16:45:08 -0400 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) In-Reply-To: References: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> Message-ID: <20200914204508.GB25753@shell.msen.com> Changing the subject line for follow up. We seem to have introduced a bug in project loading somewhere between #19537 and #19582 in trunk. Karl has done the initial analysis, see below. Dave On Mon, Sep 14, 2020 at 07:44:30PM +0200, karl ramberg wrote: > So I narrowed it down a little more. > In image #19537 I can both Inspect and Explore IdentityDictionary > allVarMaps here: > > SmartRefStream>>convert2: partiallyCorrectInst allVarMaps: allVarMaps > "Go through the normal instance conversion process and return a modern > object." > > | className varMap | > > self flag: #bobconv. > self halt. > varMap := allVarMaps at: partiallyCorrectInst. > className := varMap at: #ClassName. "original" > ^self applyConversionMethodsTo: partiallyCorrectInst className: className > varMap: varMap. > > > In image 19582 it is broken. I can Inspect allVarMaps but Explore i give > error 'Key not found:' > > So that is between 13 of mars and 16 of april 2020. > > > > > Best, > Karl > > > > > On Mon, Sep 14, 2020 at 6:44 PM karl ramberg wrote: > > > There is some change that broke project loading. > > I can load CarAndPen.014.pr in image update: #19422 > > In #19616 I get the "Key not found:" error from IdentityDictionary > > > > Best, > > Karl > > > > > > > > On Mon, Sep 14, 2020 at 5:31 PM karl ramberg > > wrote: > > > >> While looking at this error I found another issue I think is a bug. > >> I can't explore IndentityDictionary but I can inspect it. > >> > >> [image: bild.png] > >> > >> [image: bild.png] > >> Best, > >> Karl > >> > >> On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis > >> wrote: > >> > >>> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > >>> > > >>> > > On 14.09.2020, at 13:36, Thiede, Christoph < > >>> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >>> > > > >>> > > I tested this in a fresh trunk image before deleting any binding. > >>> > > >>> > ah, well, > >>> > But it doesn't find it's world, either way :D > >>> > -t > >>> > > >>> > >>> I see very different errors when I try to load an old Etoys project. I do > >>> not know why, and I don't have time to debug it. That is why I am hoping > >>> that > >>> someone (Karl perhaps) has a trunk-level image that *can* load old > >>> projects. > >>> > >>> If so, then my question is: What sort of errors or failure do you > >>> actually > >>> see when you load one of those projects after "Smalltalk removeKey: > >>> #World"? > >>> > >>> It may or may not be a problem that needs to be fixed. It is hard to know > >>> without actually seeing the problem. > >>> > >>> Dave > >>> > >>> > >>> > >>> > From craig at blackpagedigital.com Mon Sep 14 21:18:00 2020 From: craig at blackpagedigital.com (Craig Latta) Date: Mon, 14 Sep 2020 14:18:00 -0700 Subject: [squeak-dev] The Trunk: Collections-eem.909.mcz In-Reply-To: References: Message-ID: <2a665c6f-77d1-e88a-d5fe-409eeaa75ab5@blackpagedigital.com> > Get your Collections commit in quickly because in the comment you get > to state that this is the One After 909 ;-) Or just Let It Be. :) -C -- Craig Latta :: research computer scientist Black Page Digital :: Berkeley, California 663137D7940BF5C0AFC 1349FB2ADA32C4D5314CE From lewis at mail.msen.com Mon Sep 14 21:25:26 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 17:25:26 -0400 Subject: [squeak-dev] The Trunk: Collections-eem.910.mcz In-Reply-To: References: Message-ID: <20200914212526.GA35243@shell.msen.com> On Mon, Sep 14, 2020 at 05:50:37PM +0000, commits at source.squeak.org wrote: > Eliot Miranda uploaded a new version of Collections to project The Trunk: > > Travelling on the one after 909, fix a slip in that last fix. > In case any of you young folks did not catch the reference :-) https://www.youtube.com/watch?v=qdeVBJe0AHw From vanessa at codefrau.net Mon Sep 14 21:30:59 2020 From: vanessa at codefrau.net (Vanessa Freudenberg) Date: Mon, 14 Sep 2020 14:30:59 -0700 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <20200914202934.GA25753@shell.msen.com> References: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914202934.GA25753@shell.msen.com> Message-ID: On Mon, Sep 14, 2020 at 1:29 PM David T. Lewis wrote: > Karl, > > Thank you very much! > Indeed, great sleuthing! Clearly we have introduced a bug in trunk since the 5.3 release, so > we'll need to fix that. > > But to my original question - I was able to start with a fresh 5.3 > image, and I can load CarAndPen.014.pr without problems. > > Then I removed the #World binding, and tried loading CarAndPen.014.pr > to find out what the errors would look like. > > Big surprise - There was no error. The project still loads and runs > as before. That is not what I expected, so I'm glad we checked. > > Dave > Wouldn't the binding still live in Undeclared? - Vanessa - -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Mon Sep 14 21:40:22 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 14 Sep 2020 14:40:22 -0700 Subject: [squeak-dev] The Trunk: Collections-eem.910.mcz In-Reply-To: <20200914212526.GA35243@shell.msen.com> References: <20200914212526.GA35243@shell.msen.com> Message-ID: <4B06EC50-853F-4D4F-9090-06320E0461FF@gmail.com> > On Sep 14, 2020, at 2:25 PM, David T. Lewis wrote: > > On Mon, Sep 14, 2020 at 05:50:37PM +0000, commits at source.squeak.org wrote: >> Eliot Miranda uploaded a new version of Collections to project The Trunk: >> >> Travelling on the one after 909, fix a slip in that last fix. >> > > In case any of you young folks did not catch the reference :-) > > https://www.youtube.com/watch?v=qdeVBJe0AHw Interrailing to Italy one summer I had a train to catch in a southern Paris station at 9:09am and the train from Calais arrived at Gare du Nord very late, leaving me 40 minutes to cross Paris. Needless to say I had to catch the one after the 9:09. From lewis at mail.msen.com Mon Sep 14 21:41:56 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 17:41:56 -0400 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914202934.GA25753@shell.msen.com> Message-ID: <20200914214156.GA39130@shell.msen.com> On Mon, Sep 14, 2020 at 02:30:59PM -0700, Vanessa Freudenberg wrote: > On Mon, Sep 14, 2020 at 1:29 PM David T. Lewis wrote: > > > Karl, > > > > Thank you very much! > > > > Indeed, great sleuthing! > > Clearly we have introduced a bug in trunk since the 5.3 release, so > > we'll need to fix that. > > > > But to my original question - I was able to start with a fresh 5.3 > > image, and I can load CarAndPen.014.pr without problems. > > > > Then I removed the #World binding, and tried loading CarAndPen.014.pr > > to find out what the errors would look like. > > > > Big surprise - There was no error. The project still loads and runs > > as before. That is not what I expected, so I'm glad we checked. > > > > Dave > > > > Wouldn't the binding still live in Undeclared? > That does not appear to be the case. And after "Smalltalk cleanOoutUndeclared" the project still loads with no apparent problems. Whatever the reason, it seems that it Just Works :-) Dave From lewis at mail.msen.com Mon Sep 14 21:44:21 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 17:44:21 -0400 Subject: [squeak-dev] The Trunk: Collections-eem.910.mcz In-Reply-To: <4B06EC50-853F-4D4F-9090-06320E0461FF@gmail.com> References: <20200914212526.GA35243@shell.msen.com> <4B06EC50-853F-4D4F-9090-06320E0461FF@gmail.com> Message-ID: <20200914214421.GB39130@shell.msen.com> On Mon, Sep 14, 2020 at 02:40:22PM -0700, Eliot Miranda wrote: > > > > On Sep 14, 2020, at 2:25 PM, David T. Lewis wrote: > > > > ???On Mon, Sep 14, 2020 at 05:50:37PM +0000, commits at source.squeak.org wrote: > >> Eliot Miranda uploaded a new version of Collections to project The Trunk: > >> > >> Travelling on the one after 909, fix a slip in that last fix. > >> > > > > In case any of you young folks did not catch the reference :-) > > > > https://www.youtube.com/watch?v=qdeVBJe0AHw > > Interrailing to Italy one summer I had a train to catch in a southern Paris station at 9:09am and the train from Calais arrived at Gare du Nord very late, leaving me 40 minutes to cross Paris. Needless to say I had to catch the one after the 9:09. > After all these years, we finally learn the inspiration for the oldest known Beatles song! Dave From commits at source.squeak.org Tue Sep 15 00:15:51 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 15 Sep 2020 00:15:51 0000 Subject: [squeak-dev] The Inbox: System-dtl.1171.mcz Message-ID: A new version of System was added to project The Inbox: http://source.squeak.org/inbox/System-dtl.1171.mcz ==================== Summary ==================== Name: System-dtl.1171 Author: dtl Time: 14 September 2020, 8:15:47.762334 pm UUID: f8df8270-7a58-4a52-b251-e5a2dcff65dd Ancestors: System-dtl.1170 Fix Etoys project loading. Update ImageSegment>>reshapeClasses:refStream: to accomodate the elementsForwardIdentityTo: changes from Collections-eem.885. Also update other methods in ImageSegment and NativeImageSegment similarly on the (untested) assumption that the behavior should remain consistent for image segments. Note that Binding class>>convertInstances in package Environments may require similar attention. =============== Diff against System-dtl.1170 =============== Item was changed: ----- Method: ImageSegment>>install (in category 'read/write segment') ----- install "This operation retrieves the segment if necessary from file storage, installs it in memory, and replaces (using become:) all the root stubs with the reconstructed roots of the segment." | allObjectsInSegment newRoots | state = #onFile ifTrue: [self readFromFile]. state = #onFileWithSymbols ifTrue: [self readFromFileWithSymbols]. (state = #active) | (state = #imported) ifFalse: [self errorWrongState]. allObjectsInSegment := self loadSegmentFrom: segment outPointers: outPointers. newRoots := allObjectsInSegment first. self checkAndReportLoadError. (state = #imported "just came in from exported file" or: [arrayOfRoots isNil "testing..."]) ifTrue: [arrayOfRoots := newRoots] + ifFalse: [arrayOfRoots elementsForwardIdentityAndHashTo: newRoots]. - ifFalse: [arrayOfRoots elementsForwardIdentityTo: newRoots]. state := #inactive. Beeper beepPrimitive! Item was changed: ----- Method: ImageSegment>>reshapeClasses:refStream: (in category 'fileIn') ----- reshapeClasses: mapFakeClassesToReal refStream: smartRefStream | bads allVarMaps partials in out perfect | self flag: #bobconv. partials := OrderedCollection new. bads := OrderedCollection new. allVarMaps := IdentityDictionary new. mapFakeClassesToReal keysAndValuesDo: [ :aFakeClass :theRealClass | aFakeClass allInstances do: [ :misShapen | perfect := smartRefStream convert1: misShapen to: theRealClass allVarMaps: allVarMaps. bads detect: [ :x | x == misShapen] ifNone: [ bads add: misShapen. partials add: perfect ]. ]. ]. bads isEmpty ifFalse: [ + bads asArray elementsForwardIdentityAndHashTo: partials asArray - bads asArray elementsForwardIdentityTo: partials asArray ]. in := OrderedCollection new. out := OrderedCollection new. partials do: [ :each | perfect := smartRefStream convert2: each allVarMaps: allVarMaps. in detect: [ :x | x == each] ifNone: [ in add: each. out add: perfect ] ]. in isEmpty ifFalse: [ + in asArray elementsForwardIdentityAndHashTo: out asArray - in asArray elementsForwardIdentityTo: out asArray ]. ! Item was changed: ----- Method: NativeImageSegment>>extract (in category 'read/write segment') ----- extract "This operation replaces (using become:) all the original roots of a segment with segmentRootStubs. Thus the original objects will be reclaimed, and the root stubs will remain to bring the segment back in if it is needed." Cursor write showWhile: [ state = #inactive ifTrue: [self copyFromRoots: arrayOfRoots sizeHint: 0]. state = #activeCopy ifFalse: [self errorWrongState]. + arrayOfRoots elementsForwardIdentityAndHashTo: - arrayOfRoots elementsForwardIdentityTo: (arrayOfRoots collect: [:r | r rootStubInImageSegment: self]). state := #active].! Item was changed: ----- Method: NativeImageSegment>>extractThenInstall (in category 'read/write segment') ----- extractThenInstall "For testing only" | allObjectsInSegment newRoots | state = #activeCopy ifFalse: [self errorWrongState]. + arrayOfRoots elementsForwardIdentityAndHashTo: - arrayOfRoots elementsForwardIdentityTo: (arrayOfRoots collect: [:r | r rootStubInImageSegment: self]). state := #active. allObjectsInSegment := self loadSegmentFrom: segment outPointers: outPointers. newRoots := allObjectsInSegment first. state := #inactive. + arrayOfRoots elementsForwardIdentityAndHashTo: newRoots.! - arrayOfRoots elementsForwardIdentityTo: newRoots.! Item was changed: ----- Method: NativeImageSegment>>revert (in category 'read/write segment') ----- revert "Pretend this segment was never brought in. Check that it has a fileName. Replace (using become:) all the original roots of a segment with segmentRootStubs. Thus the original objects will be reclaimed, and the root stubs will remain to bring the segment back in if it is needed. How to use revert: In the project, choose 'save for reverting'. ReEnter the project. Make changes. Either exit normally, and change will be kept, or Choose 'Revert to saved version'." fileName ifNil: [^ self]. (state = #inactive) | (state = #onFile) ifFalse: [^ self]. Cursor write showWhile: [ + arrayOfRoots elementsForwardIdentityAndHashTo: - arrayOfRoots elementsForwardIdentityTo: (arrayOfRoots collect: [:r | r rootStubInImageSegment: self]). state := #onFile. segment := nil] "Old version: How to use revert: In the project, execute (Project current projectParameters at: #frozen put: true) Leave the project. Check that the project went out to disk (it is gray in the Jump to Project list). ReEnter the project. Hear a plink as it comes in from disk. Make a change. Exit the project. Choose 'Revert to previous version' in the dialog box. Check that the project went out to disk (it is gray in the Jump to Project list). ReEnter the project and see that it is in the original state."! From lewis at mail.msen.com Tue Sep 15 00:24:36 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 20:24:36 -0400 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) In-Reply-To: <20200914204508.GB25753@shell.msen.com> References: <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914204508.GB25753@shell.msen.com> Message-ID: <20200915002436.GA61505@shell.msen.com> I tracked this down a bit further, and the issue began after this change: Name: Collections-eem.885 Author: eem Time: 15 April 2020, 4:37:54.800631 pm UUID: 45d219d3-6ed0-4401-a820-44eebe21d71a Ancestors: Collections-eem.883, Collections-dtl.884 Switch elementsForwardIdentityTo: to not copy the hash, see http://forum.world.st/How-to-become-immediate-objects-td5114931.html. Add elementsForwardIdentityAndHashTo: for the old behavior. I made some updates to image segments to accomodate this change, and the Etoys project loading works again. Updates are in the inbox in System-dtl.1171. @eliot- This needs review because I just made naive updates that seem to work, but I touched several methods and I'm not sure if it got it right. Dave On Mon, Sep 14, 2020 at 04:45:08PM -0400, David T. Lewis wrote: > Changing the subject line for follow up. > > We seem to have introduced a bug in project loading somewhere between > #19537 and #19582 in trunk. Karl has done the initial analysis, see below. > > Dave > > > On Mon, Sep 14, 2020 at 07:44:30PM +0200, karl ramberg wrote: > > So I narrowed it down a little more. > > In image #19537 I can both Inspect and Explore IdentityDictionary > > allVarMaps here: > > > > SmartRefStream>>convert2: partiallyCorrectInst allVarMaps: allVarMaps > > "Go through the normal instance conversion process and return a modern > > object." > > > > | className varMap | > > > > self flag: #bobconv. > > self halt. > > varMap := allVarMaps at: partiallyCorrectInst. > > className := varMap at: #ClassName. "original" > > ^self applyConversionMethodsTo: partiallyCorrectInst className: className > > varMap: varMap. > > > > > > In image 19582 it is broken. I can Inspect allVarMaps but Explore i give > > error 'Key not found:' > > > > So that is between 13 of mars and 16 of april 2020. > > > > > > > > > > Best, > > Karl > > > > > > > > > > On Mon, Sep 14, 2020 at 6:44 PM karl ramberg wrote: > > > > > There is some change that broke project loading. > > > I can load CarAndPen.014.pr in image update: #19422 > > > In #19616 I get the "Key not found:" error from IdentityDictionary > > > > > > Best, > > > Karl > > > > > > > > > > > > On Mon, Sep 14, 2020 at 5:31 PM karl ramberg > > > wrote: > > > > > >> While looking at this error I found another issue I think is a bug. > > >> I can't explore IndentityDictionary but I can inspect it. > > >> > > >> [image: bild.png] > > >> > > >> [image: bild.png] > > >> Best, > > >> Karl > > >> > > >> On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis > > >> wrote: > > >> > > >>> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > > >>> > > > >>> > > On 14.09.2020, at 13:36, Thiede, Christoph < > > >>> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > >>> > > > > >>> > > I tested this in a fresh trunk image before deleting any binding. > > >>> > > > >>> > ah, well, > > >>> > But it doesn't find it's world, either way :D > > >>> > -t > > >>> > > > >>> > > >>> I see very different errors when I try to load an old Etoys project. I do > > >>> not know why, and I don't have time to debug it. That is why I am hoping > > >>> that > > >>> someone (Karl perhaps) has a trunk-level image that *can* load old > > >>> projects. > > >>> > > >>> If so, then my question is: What sort of errors or failure do you > > >>> actually > > >>> see when you load one of those projects after "Smalltalk removeKey: > > >>> #World"? > > >>> > > >>> It may or may not be a problem that needs to be fixed. It is hard to know > > >>> without actually seeing the problem. > > >>> > > >>> Dave > > >>> > > >>> > > >>> > > >>> > > > > > > > > From craig at blackpagedigital.com Tue Sep 15 00:30:28 2020 From: craig at blackpagedigital.com (Craig Latta) Date: Mon, 14 Sep 2020 17:30:28 -0700 Subject: [squeak-dev] The Trunk: Collections-eem.910.mcz In-Reply-To: <20200914214421.GB39130@shell.msen.com> References: <20200914212526.GA35243@shell.msen.com> <4B06EC50-853F-4D4F-9090-06320E0461FF@gmail.com> <20200914214421.GB39130@shell.msen.com> Message-ID: > After all these years, we finally learn the inspiration for the oldest > known Beatles song! Or perhaps John is just about to write it, inspired by his retro TR-909, before he hops back in the time machine... https://www.roland.com/us/products/rc_tr-909 -C -- Craig Latta :: research computer scientist Black Page Digital :: Berkeley, California 663137D7940BF5C0AFC 1349FB2ADA32C4D5314CE From eliot.miranda at gmail.com Tue Sep 15 02:43:47 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 14 Sep 2020 19:43:47 -0700 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) In-Reply-To: <20200915002436.GA61505@shell.msen.com> References: <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914204508.GB25753@shell.msen.com> <20200915002436.GA61505@shell.msen.com> Message-ID: Hi David, On Mon, Sep 14, 2020 at 5:24 PM David T. Lewis wrote: > I tracked this down a bit further, and the issue began after this change: > > Name: Collections-eem.885 > Author: eem > Time: 15 April 2020, 4:37:54.800631 pm > UUID: 45d219d3-6ed0-4401-a820-44eebe21d71a > Ancestors: Collections-eem.883, Collections-dtl.884 > > Switch elementsForwardIdentityTo: to not copy the hash, see > http://forum.world.st/How-to-become-immediate-objects-td5114931.html. > Add elementsForwardIdentityAndHashTo: for the old behavior. > > I made some updates to image segments to accomodate this change, and > the Etoys project loading works again. Updates are in the inbox in > System-dtl.1171. > > @eliot- This needs review because I just made naive updates that seem > to work, but I touched several methods and I'm not sure if it got it > right. > First, sorry I broke this. Second, I looked at your changes. They look correct to me. > Dave > > > On Mon, Sep 14, 2020 at 04:45:08PM -0400, David T. Lewis wrote: > > Changing the subject line for follow up. > > > > We seem to have introduced a bug in project loading somewhere between > > #19537 and #19582 in trunk. Karl has done the initial analysis, see > below. > > > > Dave > > > > > > On Mon, Sep 14, 2020 at 07:44:30PM +0200, karl ramberg wrote: > > > So I narrowed it down a little more. > > > In image #19537 I can both Inspect and Explore IdentityDictionary > > > allVarMaps here: > > > > > > SmartRefStream>>convert2: partiallyCorrectInst allVarMaps: allVarMaps > > > "Go through the normal instance conversion process and return a modern > > > object." > > > > > > | className varMap | > > > > > > self flag: #bobconv. > > > self halt. > > > varMap := allVarMaps at: partiallyCorrectInst. > > > className := varMap at: #ClassName. "original" > > > ^self applyConversionMethodsTo: partiallyCorrectInst className: > className > > > varMap: varMap. > > > > > > > > > In image 19582 it is broken. I can Inspect allVarMaps but Explore i > give > > > error 'Key not found:' > > > > > > So that is between 13 of mars and 16 of april 2020. > > > > > > > > > > > > > > > Best, > > > Karl > > > > > > > > > > > > > > > On Mon, Sep 14, 2020 at 6:44 PM karl ramberg > wrote: > > > > > > > There is some change that broke project loading. > > > > I can load CarAndPen.014.pr in image update: #19422 > > > > In #19616 I get the "Key not found:" error from IdentityDictionary > > > > > > > > Best, > > > > Karl > > > > > > > > > > > > > > > > On Mon, Sep 14, 2020 at 5:31 PM karl ramberg > > > > wrote: > > > > > > > >> While looking at this error I found another issue I think is a bug. > > > >> I can't explore IndentityDictionary but I can inspect it. > > > >> > > > >> [image: bild.png] > > > >> > > > >> [image: bild.png] > > > >> Best, > > > >> Karl > > > >> > > > >> On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis > > > > >> wrote: > > > >> > > > >>> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > > > >>> > > > > >>> > > On 14.09.2020, at 13:36, Thiede, Christoph < > > > >>> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > >>> > > > > > >>> > > I tested this in a fresh trunk image before deleting any > binding. > > > >>> > > > > >>> > ah, well, > > > >>> > But it doesn't find it's world, either way :D > > > >>> > -t > > > >>> > > > > >>> > > > >>> I see very different errors when I try to load an old Etoys > project. I do > > > >>> not know why, and I don't have time to debug it. That is why I am > hoping > > > >>> that > > > >>> someone (Karl perhaps) has a trunk-level image that *can* load old > > > >>> projects. > > > >>> > > > >>> If so, then my question is: What sort of errors or failure do you > > > >>> actually > > > >>> see when you load one of those projects after "Smalltalk removeKey: > > > >>> #World"? > > > >>> > > > >>> It may or may not be a problem that needs to be fixed. It is hard > to know > > > >>> without actually seeing the problem. > > > >>> > > > >>> Dave > > > >>> > > > >>> > > > >>> > > > >>> > > > > > > > > > > > > > > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Tue Sep 15 02:58:01 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 14 Sep 2020 22:58:01 -0400 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) In-Reply-To: References: <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914204508.GB25753@shell.msen.com> <20200915002436.GA61505@shell.msen.com> Message-ID: <20200915025801.GA81945@shell.msen.com> On Mon, Sep 14, 2020 at 07:43:47PM -0700, Eliot Miranda wrote: > Hi David, > > On Mon, Sep 14, 2020 at 5:24 PM David T. Lewis wrote: > > > I tracked this down a bit further, and the issue began after this change: > > > > Name: Collections-eem.885 > > Author: eem > > Time: 15 April 2020, 4:37:54.800631 pm > > UUID: 45d219d3-6ed0-4401-a820-44eebe21d71a > > Ancestors: Collections-eem.883, Collections-dtl.884 > > > > Switch elementsForwardIdentityTo: to not copy the hash, see > > http://forum.world.st/How-to-become-immediate-objects-td5114931.html. > > Add elementsForwardIdentityAndHashTo: for the old behavior. > > > > I made some updates to image segments to accomodate this change, and > > the Etoys project loading works again. Updates are in the inbox in > > System-dtl.1171. > > > > @eliot- This needs review because I just made naive updates that seem > > to work, but I touched several methods and I'm not sure if it got it > > right. > > > > First, sorry I broke this. Second, I looked at your changes. They look > correct to me. > Thanks Eliot. If nobody objects in the next day or so, I will move both System-dtl.1170 and System-dtl.1171 from inbox to trunk. That will remove the #World binding. I realize that there are some externally maintained projects that may be affected, but I am reassured that old Etoys projects can still be loaded, and I expect that any remaining problems can be easily addressed. Dave From karlramberg at gmail.com Tue Sep 15 04:29:39 2020 From: karlramberg at gmail.com (karl ramberg) Date: Tue, 15 Sep 2020 06:29:39 +0200 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) In-Reply-To: <20200915002436.GA61505@shell.msen.com> References: <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914204508.GB25753@shell.msen.com> <20200915002436.GA61505@shell.msen.com> Message-ID: Great :-D Best, Karl On Tue, Sep 15, 2020 at 2:24 AM David T. Lewis wrote: > I tracked this down a bit further, and the issue began after this change: > > Name: Collections-eem.885 > Author: eem > Time: 15 April 2020, 4:37:54.800631 pm > UUID: 45d219d3-6ed0-4401-a820-44eebe21d71a > Ancestors: Collections-eem.883, Collections-dtl.884 > > Switch elementsForwardIdentityTo: to not copy the hash, see > http://forum.world.st/How-to-become-immediate-objects-td5114931.html. > Add elementsForwardIdentityAndHashTo: for the old behavior. > > I made some updates to image segments to accomodate this change, and > the Etoys project loading works again. Updates are in the inbox in > System-dtl.1171. > > @eliot- This needs review because I just made naive updates that seem > to work, but I touched several methods and I'm not sure if it got it > right. > > Dave > > > On Mon, Sep 14, 2020 at 04:45:08PM -0400, David T. Lewis wrote: > > Changing the subject line for follow up. > > > > We seem to have introduced a bug in project loading somewhere between > > #19537 and #19582 in trunk. Karl has done the initial analysis, see > below. > > > > Dave > > > > > > On Mon, Sep 14, 2020 at 07:44:30PM +0200, karl ramberg wrote: > > > So I narrowed it down a little more. > > > In image #19537 I can both Inspect and Explore IdentityDictionary > > > allVarMaps here: > > > > > > SmartRefStream>>convert2: partiallyCorrectInst allVarMaps: allVarMaps > > > "Go through the normal instance conversion process and return a modern > > > object." > > > > > > | className varMap | > > > > > > self flag: #bobconv. > > > self halt. > > > varMap := allVarMaps at: partiallyCorrectInst. > > > className := varMap at: #ClassName. "original" > > > ^self applyConversionMethodsTo: partiallyCorrectInst className: > className > > > varMap: varMap. > > > > > > > > > In image 19582 it is broken. I can Inspect allVarMaps but Explore i > give > > > error 'Key not found:' > > > > > > So that is between 13 of mars and 16 of april 2020. > > > > > > > > > > > > > > > Best, > > > Karl > > > > > > > > > > > > > > > On Mon, Sep 14, 2020 at 6:44 PM karl ramberg > wrote: > > > > > > > There is some change that broke project loading. > > > > I can load CarAndPen.014.pr in image update: #19422 > > > > In #19616 I get the "Key not found:" error from IdentityDictionary > > > > > > > > Best, > > > > Karl > > > > > > > > > > > > > > > > On Mon, Sep 14, 2020 at 5:31 PM karl ramberg > > > > wrote: > > > > > > > >> While looking at this error I found another issue I think is a bug. > > > >> I can't explore IndentityDictionary but I can inspect it. > > > >> > > > >> [image: bild.png] > > > >> > > > >> [image: bild.png] > > > >> Best, > > > >> Karl > > > >> > > > >> On Mon, Sep 14, 2020 at 5:01 PM David T. Lewis > > > > >> wrote: > > > >> > > > >>> On Mon, Sep 14, 2020 at 01:45:39PM +0200, Tobias Pape wrote: > > > >>> > > > > >>> > > On 14.09.2020, at 13:36, Thiede, Christoph < > > > >>> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > >>> > > > > > >>> > > I tested this in a fresh trunk image before deleting any > binding. > > > >>> > > > > >>> > ah, well, > > > >>> > But it doesn't find it's world, either way :D > > > >>> > -t > > > >>> > > > > >>> > > > >>> I see very different errors when I try to load an old Etoys > project. I do > > > >>> not know why, and I don't have time to debug it. That is why I am > hoping > > > >>> that > > > >>> someone (Karl perhaps) has a trunk-level image that *can* load old > > > >>> projects. > > > >>> > > > >>> If so, then my question is: What sort of errors or failure do you > > > >>> actually > > > >>> see when you load one of those projects after "Smalltalk removeKey: > > > >>> #World"? > > > >>> > > > >>> It may or may not be a problem that needs to be fixed. It is hard > to know > > > >>> without actually seeing the problem. > > > >>> > > > >>> Dave > > > >>> > > > >>> > > > >>> > > > >>> > > > > > > > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 15 05:48:53 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 15 Sep 2020 05:48:53 0000 Subject: [squeak-dev] The Inbox: Kernel-kfr.1339.mcz Message-ID: A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-kfr.1339.mcz ==================== Summary ==================== Name: Kernel-kfr.1339 Author: kfr Time: 15 September 2020, 7:48:44.907977 am UUID: 9e1761d0-c2a1-b74a-bfe7-90dd49e320f1 Ancestors: Kernel-ct.1338 Fix a deprecation warning =============== Diff against Kernel-ct.1338 =============== Item was changed: ----- Method: Class>>binding (in category 'compiling') ----- binding "Answer a binding for the receiver, sharing if possible" | binding | + binding := Smalltalk globals associationAt: name ifAbsent: [nil -> self]. - binding := self environment associationAt: name ifAbsent: [nil -> self]. ^binding value == self ifTrue:[binding] ifFalse:[nil -> self].! From lecteur at zogotounga.net Tue Sep 15 07:10:42 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Tue, 15 Sep 2020 09:10:42 +0200 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) In-Reply-To: <20200915025801.GA81945@shell.msen.com> References: <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914204508.GB25753@shell.msen.com> <20200915002436.GA61505@shell.msen.com> <20200915025801.GA81945@shell.msen.com> Message-ID: <00564247-b3e3-4fef-3079-50577d0f10ae@zogotounga.net> > If nobody objects in the next day or so, I will move both > System-dtl.1170 and System-dtl.1171 from inbox to trunk. That will remove > the #World binding. > > I realize that there are some externally maintained projects that may be > affected, but I am reassured that old Etoys projects can still be loaded, > and I expect that any remaining problems can be easily addressed. In muO I use Ned Konz' Connectors package, where there are several references to World. Will the package load at all? In that case it will not be a problem I guess. Or will the package just fail to load? Stef From marcel.taeumel at hpi.de Tue Sep 15 09:52:50 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 11:52:50 +0200 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) In-Reply-To: <00564247-b3e3-4fef-3079-50577d0f10ae@zogotounga.net> References: <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914204508.GB25753@shell.msen.com> <20200915002436.GA61505@shell.msen.com> <20200915025801.GA81945@shell.msen.com> <00564247-b3e3-4fef-3079-50577d0f10ae@zogotounga.net> Message-ID: Hi Stef, I just loaded Connectors into Trunk and it seems to work fine: Metacello new configuration: #Connectors; load. Best, Marcel Am 15.09.2020 09:10:47 schrieb Stéphane Rollandin : > If nobody objects in the next day or so, I will move both > System-dtl.1170 and System-dtl.1171 from inbox to trunk. That will remove > the #World binding. > > I realize that there are some externally maintained projects that may be > affected, but I am reassured that old Etoys projects can still be loaded, > and I expect that any remaining problems can be easily addressed. In muO I use Ned Konz' Connectors package, where there are several references to World. Will the package load at all? In that case it will not be a problem I guess. Or will the package just fail to load? Stef -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 15 12:15:05 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 14:15:05 +0200 Subject: [squeak-dev] Dead exceptions In-Reply-To: References: <9d60e8b589bf4afb81404ed9b5e1f78e@student.hpi.uni-potsdam.de> Message-ID: Hi all! > I would like to see us keep at least EndOfStream. ExceptionAboutToReturn and Abort can be deleted, not just deprecated. They're ancient and obsolete. +1 Best, Marcel Am 14.09.2020 21:42:01 schrieb Eliot Miranda : Hi Christoph, On Mon, Sep 14, 2020 at 11:30 AM Thiede, Christoph wrote: Hi all, while browsing a bit through our exception framework, I stumbled upon a significant number of exception classes for all of which I neither found any sender in my Trunk image, nor any subclass: Abort EndOfStream ExceptionAboutToReturn FontSubstitutionDuringLoading NanError UndeclaredVariableReference So here is my question: Would you agree to deprecate them all? Or could any of them still be relevant for non-in-trunk senders? I can prepare a changeset if you think we should get rid of them. I would like to see us keep at least EndOfStream. ExceptionAboutToReturn and Abort can be deleted, not just deprecated.  They're ancient and obsolete. Best, Christoph _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Tue Sep 15 13:18:42 2020 From: gettimothy at zoho.com (gettimothy) Date: Tue, 15 Sep 2020 09:18:42 -0400 Subject: [squeak-dev] SmalltalkImage ShutdownList oddity Message-ID: <17491eade58.112eb962850315.3770421909532371986@zoho.com> Hi folks, /Squeak6.0alpha-19802-64bit.image Squeak6.0alpha latest update: #19847 I attempted to install XTreams and I got an error where XTreams tries to add itself to the ShudownList after DisplayScreen but DisplayScreen is not there,so the install fails. I manually ran SmalltalkImage initializeShutDownList and everything is now working. -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 15 13:19:27 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 15 Sep 2020 13:19:27 0000 Subject: [squeak-dev] FFI: ConfigurationOfFFI-mt.43.mcz Message-ID: Marcel Taeumel uploaded a new version of ConfigurationOfFFI to project FFI: http://source.squeak.org/FFI/ConfigurationOfFFI-mt.43.mcz ==================== Summary ==================== Name: ConfigurationOfFFI-mt.43 Author: mt Time: 15 September 2020, 3:18:22.281259 pm UUID: 8223cd49-aca7-4902-aa47-c43fd4af59ba Ancestors: ConfigurationOfFFI-nice.42 Adds version 2.0 to make FFI loadable through this configuration in Squeak 6.0alpha. =============== Diff against ConfigurationOfFFI-nice.42 =============== Item was added: + ----- Method: ConfigurationOfFFI>>baseline20: (in category 'baselines') ----- + baseline20: spec + + + spec for: #common do: [ + spec blessing: #baseline. + spec repository: 'http://source.squeakfoundation.org/FFI'. + + spec + package: 'FFI-Pools'; + package: 'FFI-Kernel' with: [ spec requires: 'FFI-Pools' ]; + package: 'FFI-Callbacks' with: [ spec requires: 'FFI-Pools' ]; "Experimental. Not compatible with Alien." + package: 'FFI-Tools' with: [ spec requires: 'FFI-Kernel' ]; + + package: 'FFI-Tests' with: [ spec requires: 'FFI-Kernel' ]; + package: 'FFI-PoolsTests' with: [ spec requires: 'FFI-Pools' ]; + + package: 'FFI-MacOS' with: [ spec requires: 'FFI-Kernel' ]; + package: 'FFI-Unix' with: [ spec requires: 'FFI-Kernel' ]; + package: 'FFI-Win32' with: [ spec requires: 'FFI-Kernel' ]. + + spec + group: 'default' with: #('Core' 'Tools' 'Examples' 'Tests'); + group: 'dev' with: #('Core' 'Callbacks' 'Tools' 'Examples' 'Tests'); "Experimental. Not compatible with Alien." + + group: 'Core' with: #('FFI-Pools' 'FFI-Kernel'); + group: 'Tools' with: #('FFI-Tools'); + group: 'Callbacks' with: #('FFI-Callbacks'); "Experimental. Not compatible with Alien." + + group: 'Examples' with: #('FFI-MacOS' 'FFI-Unix' 'FFI-Win32'); + group: 'Tests' with: #('FFI-Tests') ].! Item was changed: ----- Method: ConfigurationOfFFI>>development: (in category 'symbolic versions') ----- development: spec + spec for: #squeakCommon version: '2.0-baseline'.! - spec for: #common version: '1.4-baseline'.! Item was changed: ----- Method: ConfigurationOfFFI>>stable: (in category 'symbolic versions') ----- stable: spec + spec for: #squeakCommon version: '1.5'. + spec for: #pharo version: '1.7'. spec for: #'pharo3.x' version: '1.8'. spec for: #'pharo4.x' version: '1.9'. spec for: #'pharo5.x' version: '1.10.1'. + spec for: #'squeak' version: '1.12'. spec for: #'squeak5.x' version: '1.13'. + + spec for: #'squeak6.x' version: '2.0'. - spec for: #'squeak6.x' version: '1.13'. - spec for: #squeakCommon version: '1.5'. ! Item was added: + ----- Method: ConfigurationOfFFI>>version20: (in category 'versions') ----- + version20: spec + + + spec for: #common do: [ + spec + blessing: #release; + package: 'FFI-Pools' with: 'FFI-Pools-mt.25'; + package: 'FFI-Kernel' with: 'FFI-Kernel-mt.119'; + package: 'FFI-Tools' with: 'FFI-Tools-mt.20'; + package: 'FFI-Callbacks' with: 'FFI-Callbacks-mt.3'; "Experimental. Not compatible with Alien." + + package: 'FFI-Tests' with: 'FFI-Tests-mt.21'; + package: 'FFI-PoolsTests' with: 'FFI-PoolsTests-mt.10'; + + package: 'FFI-MacOS' with: 'FFI-MacOS-mt.6'; + package: 'FFI-Win32' with: 'FFI-Win32-nice.20'; + package: 'FFI-Unix' with: 'FFI-Unix-mtf.4'].! From marcel.taeumel at hpi.de Tue Sep 15 13:26:51 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 15:26:51 +0200 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> <,> Message-ID: Hi all. >  In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Loading FFI via the Preference Wizard -- which uses the Installer directly -- works fine in current (fresh) trunk. No debuggers to confirm. No syntax error. I am updating that Metacello script right now. Be patient. :-) Best, Marcel Am 14.09.2020 18:35:46 schrieb Thiede, Christoph : Hi Jakob, do you have a description of the correct loading order? In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Best, Christoph Von: Squeak-dev im Auftrag von Jakob Reschke Gesendet: Donnerstag, 10. September 2020 08:04:51 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] SyntaxError while loading FFI   Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph : > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 15 14:03:10 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 16:03:10 +0200 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> <,> Message-ID: Hi all. There is a bug in Metacello in MetacelloSqueakPlatform >> #defaultPlatformAttributes. We will fix that ASAP. Until then, use this to load FFI in Trunk via Metacello: Metacello new configuration: 'FFI'; version: '2.0'; load. Best, Marcel Am 15.09.2020 15:26:51 schrieb Marcel Taeumel : Hi all. >  In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Loading FFI via the Preference Wizard -- which uses the Installer directly -- works fine in current (fresh) trunk. No debuggers to confirm. No syntax error. I am updating that Metacello script right now. Be patient. :-) Best, Marcel Am 14.09.2020 18:35:46 schrieb Thiede, Christoph : Hi Jakob, do you have a description of the correct loading order? In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Best, Christoph Von: Squeak-dev im Auftrag von Jakob Reschke Gesendet: Donnerstag, 10. September 2020 08:04:51 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] SyntaxError while loading FFI   Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph : > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 15 14:13:10 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 15 Sep 2020 14:13:10 0000 Subject: [squeak-dev] The Trunk: System-mt.1170.mcz Message-ID: Marcel Taeumel uploaded a new version of System to project The Trunk: http://source.squeak.org/trunk/System-mt.1170.mcz ==================== Summary ==================== Name: System-mt.1170 Author: mt Time: 15 September 2020, 4:13:03.457876 pm UUID: 387b054e-21c0-45f7-8270-25bc7bc82049 Ancestors: System-mt.1169 Remove obsolete entries for Startup- and Shutdown-List from the global initialization code. I think this is dead code. However, this here gave me a pointer: http://forum.world.st/SmalltalkImage-ShutdownList-oddity-td5121890.html =============== Diff against System-mt.1169 =============== Item was changed: ----- Method: SmalltalkImage class>>initializeShutDownList (in category 'class initialization') ----- initializeShutDownList "SmalltalkImage initialize" | oldList | oldList := ShutDownList. ShutDownList := OrderedCollection new. "These get processed from the bottom up..." #( Delay - DisplayScreen EventSensor Form - ControlManager - PasteUpMorph StrikeFont Color FileDirectory SoundPlayer HttpUrl Password PWS MailDB ImageSegment ) do:[:clsName| Smalltalk at: clsName ifPresent:[:cls| Smalltalk addToShutDownList: cls]. ]. oldList ifNotNil: [oldList reverseDo: [:className | Smalltalk at: className ifPresent: [:theClass | Smalltalk addToShutDownList: theClass]]]. ! Item was changed: ----- Method: SmalltalkImage class>>initializeStartUpList (in category 'class initialization') ----- initializeStartUpList "SmalltalkImage initialize" | oldList | oldList := StartUpList. StartUpList := OrderedCollection new. "These get processed from the top down..." - self flag: #'revisit in Squeak 5.3'. #( SmallInteger Delay + Time + DateAndTime - DisplayScreen Cursor EventSensor + ProcessorScheduler "Starts low space watcher and background." - ProcessorScheduler "Starts low space watcher and bkground." FileDirectory "Enables file stack dump and opens sources." ShortIntegerArray ShortRunArray - CrLfFileStream "Remove this in Squeak 5.3" ) do:[:clsName| Smalltalk at: clsName ifPresent:[:cls| Smalltalk addToStartUpList: cls]. ]. oldList ifNotNil: [oldList do: [:className | Smalltalk at: className ifPresent: [:theClass | Smalltalk addToStartUpList: theClass]]]. #( ImageSegment + Project + AutoStart + ReleaseBuilder - PasteUpMorph - ControlManager ) do:[:clsName| Smalltalk at: clsName ifPresent:[:cls| Smalltalk addToStartUpList: cls]. ]. ! From marcel.taeumel at hpi.de Tue Sep 15 14:15:01 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 16:15:01 +0200 Subject: [squeak-dev] SmalltalkImage ShutdownList oddity In-Reply-To: <17491eade58.112eb962850315.3770421909532371986@zoho.com> References: <17491eade58.112eb962850315.3770421909532371986@zoho.com> Message-ID: Hi Timothy, DisplayScreen is no longer in the StartupList nor in the ShutdownList. Try to add XStreams after "Project" in the ShutdownList. Or just remove that specificity. Best, Marcel Am 15.09.2020 15:18:55 schrieb gettimothy via Squeak-dev : Hi folks, /Squeak6.0alpha-19802-64bit.image Squeak6.0alpha latest update: #19847 I attempted to install XTreams and I got an error where XTreams tries to add itself to the ShudownList after DisplayScreen but DisplayScreen is not there,so the install fails. I manually ran SmalltalkImage initializeShutDownList and everything is now working. -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Tue Sep 15 14:28:52 2020 From: gettimothy at zoho.com (gettimothy) Date: Tue, 15 Sep 2020 10:28:52 -0400 Subject: [squeak-dev] Levente do you have PG3 running on 6.0Alpha? Message-ID: <174922b1b51.fa8febbc51549.1123089187633474927@zoho.com> Hi Levente, I am migrating my work to 6.0 alpha. Getting some glitches and wondering if you have gotten it running there. thx -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 15 14:32:59 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 16:32:59 +0200 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: <20200912195843.GB56261@shell.msen.com> References: <20200911215416.GA42161@shell.msen.com> <20200912195843.GB56261@shell.msen.com> Message-ID: Hi all. Here is a quick thought: It should be possible to synthesize events from within the image to then use those synthesized events along with regular events. Think about testing, mouse over etc. Thus, either #eventMillisecondClock should be platform-specific, or all event timestamps that come from the VM should be similar ... which means that the Windows VM code needs to be changed to not use the MSG structure but ioMSecs. Best, Marcel Am 12.09.2020 21:58:50 schrieb David T. Lewis : Thanks Tim, I've been digging through version control history, and I think what I see is that the assumption of prim 94 matching prim 135 was valid through the life of the interpreter VM (squeakvm.org SVN sources) but that it had changed for Windows as of the public release of Cog. I don't see anything wrong with the Windows VM implementation (aside from the one bug that Christoph fixed), instead I think we may just be dealing with a holdover assumption that is no longer valid. I'm inclined to think we should see if we can make the prim 135 dependence go away in the image. Some other way of synthesizing event time stamps maybe? Dave On Fri, Sep 11, 2020 at 03:45:35PM -0700, tim Rowledge wrote: > I think the key thing is that the answers from the primitive 135 must be aligned with the tick values obtained from primitive 94. > > Of course if prim 94 is properly functional (all elements of the event array are correctly filled in) then prim 135 should never be needed; it was created for those machines that waaaaay back when I added the input events still couldn't provide such data. I'm not entirely sure there actually were any.... > > The only usage of prim 135 not related directly to events seems to be SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods that looks a bit iffy. > > EventSensor>>#createMouseEvent is only used in a couple of places to do with rectangle transforming interactively, and probably ought to be improved. > EventSensor>>#nextEventSynthesized is only used if the EventSensor has no event queue and that is ... a bit complex but probably should never happen since we removed the old InputSensor years and years ago. > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe we should drop the fake-it code and really insist on the prim being there? > MorphicEvent>>#timeStamp looks like a probably redundant backup? > MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to copy the time stamp from the receiver? > > > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > There are no stupid questions. But, there are a lot of inquisitive idiots. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 15 14:42:11 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 16:42:11 +0200 Subject: [squeak-dev] FormInspector, or also: Text>>#= and its consequences In-Reply-To: <10d552ed992347e0ba801c1423dada6b@student.hpi.uni-potsdam.de> References: <10d552ed992347e0ba801c1423dada6b@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph. Performance. Change it, bench it, post the results here. :-) Please specify you machine and try it on a slow RaspPi, too. Best, Marcel Am 10.09.2020 20:32:34 schrieb Thiede, Christoph : Hi all, is there any old thread about the design discussion of how Text>>#= works? (It does not consider attributes for quality.) Has this decision ever been questioned? Naively and without an overview of any existing components that could rely on this implementation, I would like to question it. Why should 'foo' asText allBold be equal to 'foo' asText addAttribute: TextURL new? With the same logic, we could also say that two dictionaries are equal iff they have got the same keys ... There is even a concrete client in the Trunk suffering from this design decision: Marcel's new FormInspector (and analogously, MorphInspector). It uses  TextFontReference with a FormSetFont to display a screenshot right in the inspector pane. Unfortunately, the pane is never updated automatically because even if the screenshot changes, the text morph thinks the old text would equal the new one. I'd like to fix that without hacking any workaround into the inspectors. Even though this inspector implementation is a bit unusual, in my opinion, it shows that the current Text >> #= implementation might not be a perfect solution. I'm looking forward to your opinions. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 15 14:51:22 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 16:51:22 +0200 Subject: [squeak-dev] The Inbox: Graphics-ct.437.mcz In-Reply-To: References: <3d88675e-5375-63c7-5e30-1f06c88d2896@zogotounga.net> Message-ID: Hmpf. Why can't we change Rectangle >> #center:extent: to use #/ instead of #//? What would break? I am surprised that my values get rounded at that location. They get rounded at a later anyway. At least in the graphics system and in Morphic. Best, Marcel Am 24.08.2020 21:04:26 schrieb karl ramberg : Scaling for morphs with TransformationMorph is a little weird. If you rotate a SystemWindow and use the scale handle, the window scales as a form Best, Karl On Mon, Aug 24, 2020 at 2:25 PM Stéphane Rollandin wrote: > In my opinion, the integer/float arithmetic of Point and Rectangle is > not really clear Indeed. In my own images I have implemented #preciseCenter: and similar methods both in Pont and Rectangle to bypass rounding and work with floats. But I do not think that transitioning to an overall new convention (like getting rid of rounding altogether) is going to be safe. A lot of morphic code assumes that rectangles are always about pixels and expect their dimensions to be integers. Stef -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Tue Sep 15 15:04:09 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 15 Sep 2020 11:04:09 -0400 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: <20200911215416.GA42161@shell.msen.com> <20200912195843.GB56261@shell.msen.com> Message-ID: <20200915150409.GA6861@shell.msen.com> I do not have any specific solution or approach in mind, but I think that we should first look at how we might handle this better in the image. The Windows VM is not doing anything wrong, on the contrary I think that its policy of using native MSG timestamps feels like the right thing to do. It seems to me that the problem is our assumption that event timestamps should match the millisecond clock in the VM. Certainly that can be made to work, but seems to me like fixing the wrong problem. Time class>>eventMillisecondClock is a recent addition. Maybe we can find another way to synthesize event time stamps in the image. Dave On Tue, Sep 15, 2020 at 04:32:59PM +0200, Marcel Taeumel wrote: > Hi all. > > Here is a quick thought: It should be possible to synthesize events from within the image to then use those synthesized events along with regular events. Think about testing, mouse over etc. Thus, either #eventMillisecondClock should be platform-specific, or all event timestamps that come from the VM should be similar ... which means that the Windows VM code needs to be changed to not use the MSG structure but ioMSecs. > > Best, > Marcel > Am 12.09.2020 21:58:50 schrieb David T. Lewis : > Thanks Tim, > > I've been digging through version control history, and I think what > I see is that the assumption of prim 94 matching prim 135 was valid > through the life of the interpreter VM (squeakvm.org SVN sources) but > that it had changed for Windows as of the public release of Cog. > > I don't see anything wrong with the Windows VM implementation (aside > from the one bug that Christoph fixed), instead I think we may just > be dealing with a holdover assumption that is no longer valid. > > I'm inclined to think we should see if we can make the prim 135 > dependence go away in the image. Some other way of synthesizing event > time stamps maybe? > > Dave > > On Fri, Sep 11, 2020 at 03:45:35PM -0700, tim Rowledge wrote: > > I think the key thing is that the answers from the primitive 135 must be aligned with the tick values obtained from primitive 94. > > > > Of course if prim 94 is properly functional (all elements of the event array are correctly filled in) then prim 135 should never be needed; it was created for those machines that waaaaay back when I added the input events still couldn't provide such data. I'm not entirely sure there actually were any.... > > > > The only usage of prim 135 not related directly to events seems to be SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods that looks a bit iffy. > > > > EventSensor>>#createMouseEvent is only used in a couple of places to do with rectangle transforming interactively, and probably ought to be improved. > > EventSensor>>#nextEventSynthesized is only used if the EventSensor has no event queue and that is ... a bit complex but probably should never happen since we removed the old InputSensor years and years ago. > > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe we should drop the fake-it code and really insist on the prim being there? > > MorphicEvent>>#timeStamp looks like a probably redundant backup? > > MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to copy the time stamp from the receiver? > > > > > > > > tim > > -- > > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > > There are no stupid questions. But, there are a lot of inquisitive idiots. > > > > > > > > From marcel.taeumel at hpi.de Tue Sep 15 15:28:14 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 15 Sep 2020 17:28:14 +0200 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: <20200915150409.GA6861@shell.msen.com> References: <20200911215416.GA42161@shell.msen.com> <20200912195843.GB56261@shell.msen.com> <20200915150409.GA6861@shell.msen.com> Message-ID: Hi Dave. > ...  but I think that we should first look at how we might handle this better in the image ... To some extent, this will be the good'ol discussion about how to achieve cross-platform compatibility. Whose job is it? VM? Image? Both? Do we want to have "platformName = 'Win32'" (or similar) checks in the image? Or can we rely on platform-independent information coming from the VM? Well, thinking about maybe the future role of FFI for Squeak, we may not want to change too much in the VM at this point. A minimal-effort approach with in-image change might be the right thing to do. :-) Best, Marcel Am 15.09.2020 17:04:18 schrieb David T. Lewis : I do not have any specific solution or approach in mind, but I think that we should first look at how we might handle this better in the image. The Windows VM is not doing anything wrong, on the contrary I think that its policy of using native MSG timestamps feels like the right thing to do. It seems to me that the problem is our assumption that event timestamps should match the millisecond clock in the VM. Certainly that can be made to work, but seems to me like fixing the wrong problem. Time class>>eventMillisecondClock is a recent addition. Maybe we can find another way to synthesize event time stamps in the image. Dave On Tue, Sep 15, 2020 at 04:32:59PM +0200, Marcel Taeumel wrote: > Hi all. > > Here is a quick thought: It should be possible to synthesize events from within the image to then use those synthesized events along with regular events. Think about testing, mouse over etc. Thus, either #eventMillisecondClock should be platform-specific, or all event timestamps that come from the VM should be similar ... which means that the Windows VM code needs to be changed to not use the MSG structure but ioMSecs. > > Best, > Marcel > Am 12.09.2020 21:58:50 schrieb David T. Lewis : > Thanks Tim, > > I've been digging through version control history, and I think what > I see is that the assumption of prim 94 matching prim 135 was valid > through the life of the interpreter VM (squeakvm.org SVN sources) but > that it had changed for Windows as of the public release of Cog. > > I don't see anything wrong with the Windows VM implementation (aside > from the one bug that Christoph fixed), instead I think we may just > be dealing with a holdover assumption that is no longer valid. > > I'm inclined to think we should see if we can make the prim 135 > dependence go away in the image. Some other way of synthesizing event > time stamps maybe? > > Dave > > On Fri, Sep 11, 2020 at 03:45:35PM -0700, tim Rowledge wrote: > > I think the key thing is that the answers from the primitive 135 must be aligned with the tick values obtained from primitive 94. > > > > Of course if prim 94 is properly functional (all elements of the event array are correctly filled in) then prim 135 should never be needed; it was created for those machines that waaaaay back when I added the input events still couldn't provide such data. I'm not entirely sure there actually were any.... > > > > The only usage of prim 135 not related directly to events seems to be SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods that looks a bit iffy. > > > > EventSensor>>#createMouseEvent is only used in a couple of places to do with rectangle transforming interactively, and probably ought to be improved. > > EventSensor>>#nextEventSynthesized is only used if the EventSensor has no event queue and that is ... a bit complex but probably should never happen since we removed the old InputSensor years and years ago. > > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe we should drop the fake-it code and really insist on the prim being there? > > MorphicEvent>>#timeStamp looks like a probably redundant backup? > > MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to copy the time stamp from the receiver? > > > > > > > > tim > > -- > > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > > There are no stupid questions. But, there are a lot of inquisitive idiots. > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Tue Sep 15 15:50:37 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Tue, 15 Sep 2020 17:50:37 +0200 (CEST) Subject: [squeak-dev] Levente do you have PG3 running on 6.0Alpha? In-Reply-To: <174922b1b51.fa8febbc51549.1123089187633474927@zoho.com> References: <174922b1b51.fa8febbc51549.1123089187633474927@zoho.com> Message-ID: Hi Tim, Yes, I do. On Tue, 15 Sep 2020, gettimothy via Squeak-dev wrote: > Hi Levente, > > I am migrating my work to 6.0 alpha. > > Getting some glitches and wondering if you have gotten it running there. Which versions of the packages do you have in your image? Can you describe what problems you see? Levente > > thx > > > From commits at source.squeak.org Tue Sep 15 15:51:53 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 15 Sep 2020 15:51:53 0000 Subject: [squeak-dev] The Trunk: System-dtl.1172.mcz Message-ID: David T. Lewis uploaded a new version of System to project The Trunk: http://source.squeak.org/trunk/System-dtl.1172.mcz ==================== Summary ==================== Name: System-dtl.1172 Author: dtl Time: 15 September 2020, 11:51:49.283423 am UUID: ff268fb9-4fff-4981-bc66-1b0f2a5e2589 Ancestors: System-mt.1170 Fix Etoys project loading. Update ImageSegment>>reshapeClasses:refStream: to accomodate the elementsForwardIdentityTo: changes from Collections-eem.885. Also update other methods in ImageSegment and NativeImageSegment similarly on the (untested) assumption that the behavior should remain consistent for image segments. Note that Binding class>>convertInstances in package Environments may require similar attention. Replaces System-dtl.1171 from inbox (moved to treated) =============== Diff against System-mt.1170 =============== Item was changed: ----- Method: ImageSegment>>install (in category 'read/write segment') ----- install "This operation retrieves the segment if necessary from file storage, installs it in memory, and replaces (using become:) all the root stubs with the reconstructed roots of the segment." | allObjectsInSegment newRoots | state = #onFile ifTrue: [self readFromFile]. state = #onFileWithSymbols ifTrue: [self readFromFileWithSymbols]. (state = #active) | (state = #imported) ifFalse: [self errorWrongState]. allObjectsInSegment := self loadSegmentFrom: segment outPointers: outPointers. newRoots := allObjectsInSegment first. self checkAndReportLoadError. (state = #imported "just came in from exported file" or: [arrayOfRoots isNil "testing..."]) ifTrue: [arrayOfRoots := newRoots] + ifFalse: [arrayOfRoots elementsForwardIdentityAndHashTo: newRoots]. - ifFalse: [arrayOfRoots elementsForwardIdentityTo: newRoots]. state := #inactive. Beeper beepPrimitive! Item was changed: ----- Method: ImageSegment>>reshapeClasses:refStream: (in category 'fileIn') ----- reshapeClasses: mapFakeClassesToReal refStream: smartRefStream | bads allVarMaps partials in out perfect | self flag: #bobconv. partials := OrderedCollection new. bads := OrderedCollection new. allVarMaps := IdentityDictionary new. mapFakeClassesToReal keysAndValuesDo: [ :aFakeClass :theRealClass | aFakeClass allInstances do: [ :misShapen | perfect := smartRefStream convert1: misShapen to: theRealClass allVarMaps: allVarMaps. bads detect: [ :x | x == misShapen] ifNone: [ bads add: misShapen. partials add: perfect ]. ]. ]. bads isEmpty ifFalse: [ + bads asArray elementsForwardIdentityAndHashTo: partials asArray - bads asArray elementsForwardIdentityTo: partials asArray ]. in := OrderedCollection new. out := OrderedCollection new. partials do: [ :each | perfect := smartRefStream convert2: each allVarMaps: allVarMaps. in detect: [ :x | x == each] ifNone: [ in add: each. out add: perfect ] ]. in isEmpty ifFalse: [ + in asArray elementsForwardIdentityAndHashTo: out asArray - in asArray elementsForwardIdentityTo: out asArray ]. ! Item was changed: ----- Method: NativeImageSegment>>extract (in category 'read/write segment') ----- extract "This operation replaces (using become:) all the original roots of a segment with segmentRootStubs. Thus the original objects will be reclaimed, and the root stubs will remain to bring the segment back in if it is needed." Cursor write showWhile: [ state = #inactive ifTrue: [self copyFromRoots: arrayOfRoots sizeHint: 0]. state = #activeCopy ifFalse: [self errorWrongState]. + arrayOfRoots elementsForwardIdentityAndHashTo: - arrayOfRoots elementsForwardIdentityTo: (arrayOfRoots collect: [:r | r rootStubInImageSegment: self]). state := #active].! Item was changed: ----- Method: NativeImageSegment>>extractThenInstall (in category 'read/write segment') ----- extractThenInstall "For testing only" | allObjectsInSegment newRoots | state = #activeCopy ifFalse: [self errorWrongState]. + arrayOfRoots elementsForwardIdentityAndHashTo: - arrayOfRoots elementsForwardIdentityTo: (arrayOfRoots collect: [:r | r rootStubInImageSegment: self]). state := #active. allObjectsInSegment := self loadSegmentFrom: segment outPointers: outPointers. newRoots := allObjectsInSegment first. state := #inactive. + arrayOfRoots elementsForwardIdentityAndHashTo: newRoots.! - arrayOfRoots elementsForwardIdentityTo: newRoots.! Item was changed: ----- Method: NativeImageSegment>>revert (in category 'read/write segment') ----- revert "Pretend this segment was never brought in. Check that it has a fileName. Replace (using become:) all the original roots of a segment with segmentRootStubs. Thus the original objects will be reclaimed, and the root stubs will remain to bring the segment back in if it is needed. How to use revert: In the project, choose 'save for reverting'. ReEnter the project. Make changes. Either exit normally, and change will be kept, or Choose 'Revert to saved version'." fileName ifNil: [^ self]. (state = #inactive) | (state = #onFile) ifFalse: [^ self]. Cursor write showWhile: [ + arrayOfRoots elementsForwardIdentityAndHashTo: - arrayOfRoots elementsForwardIdentityTo: (arrayOfRoots collect: [:r | r rootStubInImageSegment: self]). state := #onFile. segment := nil] "Old version: How to use revert: In the project, execute (Project current projectParameters at: #frozen put: true) Leave the project. Check that the project went out to disk (it is gray in the Jump to Project list). ReEnter the project. Hear a plink as it comes in from disk. Make a change. Exit the project. Choose 'Revert to previous version' in the dialog box. Check that the project went out to disk (it is gray in the Jump to Project list). ReEnter the project and see that it is in the original state."! From eliot.miranda at gmail.com Tue Sep 15 16:04:24 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 15 Sep 2020 09:04:24 -0700 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: Message-ID: <81AD7450-676E-4C78-BA29-2E84BD97A539@gmail.com> Hi Marcel, Hi Dave, > On Sep 15, 2020, at 8:28 AM, Marcel Taeumel wrote: > >  > Hi Dave. > > > ... but I think that we should first look at how we might handle this better in the image ... > > To some extent, this will be the good'ol discussion about how to achieve cross-platform compatibility. Whose job is it? VM? Image? Both? Do we want to have "platformName = 'Win32'" (or similar) checks in the image? Or can we rely on platform-independent information coming from the VM? > > Well, thinking about maybe the future role of FFI for Squeak, we may not want to change too much in the VM at this point. A minimal-effort approach with in-image change might be the right thing to do. :-) Agreed. My concerns are 1. The wrapping millisecond clock is a baaaaaad idea. It wraps in 49 days. The code that used to exist in Time was extremely complex and hard to maintain (fooling the system into being close to wrap around is not easy). And now we do have applications which are expected to run for long periods. But even though it is a bad idea we presumably have applications in images (someone mentioned RFB) which depend on the vm stamping events using the wrapping millisecond clock, and if possible the vm should continue to do so 2. we have a much better much simpler clock that does not wrap and can be used to compare times between images across the globe, the utc microsecond clock. If we want timestamps we should derive them from this clock 3. backwards compatibility. If we change event timestamps do we lose the ability to run 5.x images on the new vm? Is there a way that the vm can be informed by the image whether it wants timestamps? (We can add another image header flag to control the vm response) Regarding 3. I can imagine adding a 32-bit field at the end of a vm event and have having 64 bits (existing time stamp plus new field) to allow stamping the event with the utc clock (& not utc now but the utc updated by the vm heartbeat at 500Hz). > Best, > Marcel >> Am 15.09.2020 17:04:18 schrieb David T. Lewis : >> >> I do not have any specific solution or approach in mind, but I think >> that we should first look at how we might handle this better in the >> image. The Windows VM is not doing anything wrong, on the contrary I >> think that its policy of using native MSG timestamps feels like the >> right thing to do. >> >> It seems to me that the problem is our assumption that event timestamps >> should match the millisecond clock in the VM. Certainly that can >> be made to work, but seems to me like fixing the wrong problem. >> >> Time class>>eventMillisecondClock is a recent addition. Maybe we >> can find another way to synthesize event time stamps in the image. >> >> Dave >> >> >> On Tue, Sep 15, 2020 at 04:32:59PM +0200, Marcel Taeumel wrote: >> > Hi all. >> > >> > Here is a quick thought: It should be possible to synthesize events from within the image to then use those synthesized events along with regular events. Think about testing, mouse over etc. Thus, either #eventMillisecondClock should be platform-specific, or all event timestamps that come from the VM should be similar ... which means that the Windows VM code needs to be changed to not use the MSG structure but ioMSecs. >> > >> > Best, >> > Marcel >> > Am 12.09.2020 21:58:50 schrieb David T. Lewis : >> > Thanks Tim, >> > >> > I've been digging through version control history, and I think what >> > I see is that the assumption of prim 94 matching prim 135 was valid >> > through the life of the interpreter VM (squeakvm.org SVN sources) but >> > that it had changed for Windows as of the public release of Cog. >> > >> > I don't see anything wrong with the Windows VM implementation (aside >> > from the one bug that Christoph fixed), instead I think we may just >> > be dealing with a holdover assumption that is no longer valid. >> > >> > I'm inclined to think we should see if we can make the prim 135 >> > dependence go away in the image. Some other way of synthesizing event >> > time stamps maybe? >> > >> > Dave >> > >> > On Fri, Sep 11, 2020 at 03:45:35PM -0700, tim Rowledge wrote: >> > > I think the key thing is that the answers from the primitive 135 must be aligned with the tick values obtained from primitive 94. >> > > >> > > Of course if prim 94 is properly functional (all elements of the event array are correctly filled in) then prim 135 should never be needed; it was created for those machines that waaaaay back when I added the input events still couldn't provide such data. I'm not entirely sure there actually were any.... >> > > >> > > The only usage of prim 135 not related directly to events seems to be SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods that looks a bit iffy. >> > > >> > > EventSensor>>#createMouseEvent is only used in a couple of places to do with rectangle transforming interactively, and probably ought to be improved. >> > > EventSensor>>#nextEventSynthesized is only used if the EventSensor has no event queue and that is ... a bit complex but probably should never happen since we removed the old InputSensor years and years ago. >> > > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe we should drop the fake-it code and really insist on the prim being there? >> > > MorphicEvent>>#timeStamp looks like a probably redundant backup? >> > > MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to copy the time stamp from the receiver? >> > > >> > > >> > > >> > > tim >> > > -- >> > > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim >> > > There are no stupid questions. But, there are a lot of inquisitive idiots. >> > > >> > > >> > > >> > >> >> > >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Tue Sep 15 17:03:04 2020 From: karlramberg at gmail.com (karl ramberg) Date: Tue, 15 Sep 2020 19:03:04 +0200 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <20200913203422.GA85590@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <20200912191059.GA48621@shell.msen.com> <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> Message-ID: Now that project loading works again (thanks) I was able to do a few tests. I found no problems with projects after executing Smalltalk globals unbind: #World Best, Karl On Sun, Sep 13, 2020 at 10:34 PM David T. Lewis wrote: > Is anyone is a position to try loading old Etoys projects such as > CarAndPen.014.pr > into an up to date trunk image? I have not tried this in a while, and I > get errors > on loading the project. I don't know how to easily fix that so I am asking > for > help from anyone who may be more up to date on the topic. > > The reason I am asking is that I would like to know if removing the global > variable #World from trunk would cause any new or unusual problems with > respect > to Etoys project loading. > > To be specific, if you have a trunk image and are able to load any Etoys > project, then I would like to know what sort of errors you encounter if > you first do this before loading that same Etoys project: > > Smalltalk globals unbind: #World > > I would expect that this will lead to some kind of error condition, and > what I would like to know is whether that failure or error message gives > a good clue as to the problem, such that you might be able to figure out > that you would need to fix it by doing this: > > Smalltalk at: #World put: Project current world > > > Thanks! > Dave > > > > On Sat, Sep 12, 2020 at 07:32:17PM +0000, Thiede, Christoph wrote: > > Hi David, > > > > > > I think I understand the dilemma: If we keep #World in the bindings, it > will keep being used (I have to admit that, for personal scripts, I'm doing > so, too). If we remove it, we don't provide backward compatibility for > older projects. This would be really a pity because even if you write in > your inbox version: "If a package is loaded that does need World, then > 'Smalltalk at: #World put: Project current world' will restore prior > behavior.", how many people will know that when they attempt to refloat an > ancient piece of Squeak code? > > > > As far as we only have these two options, I'm tending to vote for the > first one. Unless we use it in the Trunk, it does no harm, and actual bugs > should occur rather seldom because some third-party package is still using > #World. If there should be a bug somewhere, it can be easily fixed. > > > > > > But hypothetically, there would be a third option, which would be my > favorite: Handle accesses to #World like calls on a deprecated method, i.e. > by signaling a DeprecationWarning. That way we could even make sure that > novices do not learn to use a deprecated variable. > > > > This could be realized by wrapping the value of #World into an > ObjectTracer or a similar class that raises a DeprecationWarning on every > call that is made to it. > > > > But I fear this could be a piece of overengineering, what do you think? > Also, this would not protect the global variable from being reassigned ... > > > > The very last solution could be to make a (global) list of deprecated > bindings and raise a Warning from the Compiler when an attempt is made to > access one of them. Unfortunately, this would also be the most invasive > change. > > > > > > @Levente: It seemed kind of overengineering to me to create three > subclasses of ProcessLocalVariable for this purpose. Or am I misunderstand > their concept? Maybe we could also need something like a > PluggableProcessLocalVariable that can be instantiated with two accessor > blocks? > > > > > > Best, > > > > Christoph > > > > ________________________________ > > Von: Squeak-dev im > Auftrag von David T. Lewis > > Gesendet: Samstag, 12. September 2020 21:10:59 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from > Morphic > > > > On Sat, Sep 12, 2020 at 06:38:38PM +0000, Thiede, Christoph wrote: > > > Glad you like it, David! :-) > > > > > > > > > > > > > In the case of the global World variable, we were able to make it an > instance variable in Project. > > > > > > I heard of this, but where can you see the difference? If I evaluate > "World" in my fresh trunk image, I get a PasteUpMorph instance ... #World > is still listed in my Smalltalk bindings. > > > > > > I just put System-dtl.1170 in the inbox to make it go away. For a long > > time, the World variable was shared all over the place, even in MVC if > > you can believe that. From a modularity perspective it was a real mess. > > Basically, the solution was to make it an instance variable in Project. > > The current project always has a world, and when transitioning from one > > project to another, you can do the handoff between those two worlds > > without referencing a global. > > > > All that got done about two years ago, but I intentionally left the > #World > > binding in place on the theory that there might be external packages (or > > Etoys projects) that expect to have access to global World. > > > > The way it works now (see MorphicProject>>setWorld:) is that the global > > World will be updated as before, but this happens if and only if the > > global binding exists. If you remove the binding, the World no longer > > exists. If you load a package or project that does have references to > > World, then restoring the #World binding in the current environment > should > > make it work again. > > > > I don't really know if it's a good idea to remove the #World binding now > > (that's why it's in the inbox). But leaving it in the image is confusing > > because it gives the wrong impression that it is still being used. > > > > Dave > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 15 18:20:33 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 15 Sep 2020 11:20:33 -0700 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> Message-ID: Hi Marcel, On Tue, Sep 15, 2020 at 6:27 AM Marcel Taeumel wrote: > Hi all. > > > In my main image, I tried to load FFI via the Preference Wizard, and > after some loading conflicts which I explicitly needed to #allow through > the debugger, I got the same syntax error ... > > Loading FFI via the Preference Wizard -- which uses the Installer directly > -- works fine in current (fresh) trunk. No debuggers to confirm. No syntax > error. > That's good to know. I'll alter my VMMaker image build scripts to use this. But there's one code smell about this. PreferenceWizardMorph is a morph. i wonder if it's worth-while separating PreferenceWizardMorph into PreferenceWizardMorph and PreferenceWizard. It feels better changing the script to read PreferenceWizard new installFFI than having it rad PreferenceWizardMorph new installFFI. If you agree that this is OK, I can make the changes. > I am updating that Metacello script right now. Be patient. :-) > > Best, > Marcel > > Am 14.09.2020 18:35:46 schrieb Thiede, Christoph < > christoph.thiede at student.hpi.uni-potsdam.de>: > > Hi Jakob, > > > do you have a description of the correct loading order? In my main image, > I tried to load FFI via the Preference Wizard, and after some loading > conflicts which I explicitly needed to #allow through the debugger, I got > the same syntax error ... > > > Best, > > Christoph > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Jakob Reschke > *Gesendet:* Donnerstag, 10. September 2020 08:04:51 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] SyntaxError while loading FFI > > Hi Christoph, > > I had a similar issue and if I remember correctly got it resolved by > loading the Tools subpackage of FFI. I think Marcel has recently split > it. > > So if that is true, the load order in the baseline of FFI should be > updated. > > Kind regards, > Jakob > > Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph > : > > > > Hi all, > > > > > > I fear this could be a frequently discussed topic, but I did not know > where to start else. > > > > > > In the web, you can find the following instruction in order to load FFI > at several places, amongst them the Swiki: > > > > > > Metacello new configuration: 'FFI'; load. > > > > > > If I do this in a fresh trunk image (#19838) without any extra installs, > I get a syntax error: > > > > > > ffiPrintString: aString > > > > "FFITestLibrary ffiPrintString: 'Hello'" > > > > "char* 'ffiPrintString' (char *) > module:'SqueakFFIPrims'> > > > > ^self externalCallFailed > > > > > > This is at the very least confusing, I think :-) Are there any > undocumented dependencies or something like this? > > > > If I use the preference wizard instead, the installation succeeds. (It's > not very convenient that it asks you for your initials during the > installation, but this is only a small critique. :-)) > > > > I just wanted to inform you about that. > > > > > > Best, > > > > Christoph > > > > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 15 18:29:18 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 15 Sep 2020 11:29:18 -0700 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> Message-ID: Hi Marcel, Hi All, On Tue, Sep 15, 2020 at 11:20 AM Eliot Miranda wrote: > Hi Marcel, > > On Tue, Sep 15, 2020 at 6:27 AM Marcel Taeumel > wrote: > >> Hi all. >> >> > In my main image, I tried to load FFI via the Preference Wizard, and >> after some loading conflicts which I explicitly needed to #allow through >> the debugger, I got the same syntax error ... >> >> Loading FFI via the Preference Wizard -- which uses the Installer >> directly -- works fine in current (fresh) trunk. No debuggers to confirm. >> No syntax error. >> > > That's good to know. I'll alter my VMMaker image build scripts to use > this. But there's one code smell about this. PreferenceWizardMorph is a > morph. i wonder if it's worth-while separating PreferenceWizardMorph > into PreferenceWizardMorph and PreferenceWizard. It feels better > changing the script to read PreferenceWizard new installFFI than having > it rad PreferenceWizardMorph new installFFI. If you agree that this is > OK, I can make the changes. > Hmmm, maybe better is to move these install scripts to the Installer hierarchy. Not sure where they should live. But it would be nice to be able to say Installer installFFI and have it choose a default path, as well as be able to say Installer squeak installFFI and have it install via InstallerMonticello I am updating that Metacello script right now. Be patient. :-) >> >> Best, >> Marcel >> >> Am 14.09.2020 18:35:46 schrieb Thiede, Christoph < >> christoph.thiede at student.hpi.uni-potsdam.de>: >> >> Hi Jakob, >> >> >> do you have a description of the correct loading order? In my main image, >> I tried to load FFI via the Preference Wizard, and after some loading >> conflicts which I explicitly needed to #allow through the debugger, I got >> the same syntax error ... >> >> >> Best, >> >> Christoph >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von Jakob Reschke >> *Gesendet:* Donnerstag, 10. September 2020 08:04:51 >> *An:* The general-purpose Squeak developers list >> *Betreff:* Re: [squeak-dev] SyntaxError while loading FFI >> >> Hi Christoph, >> >> I had a similar issue and if I remember correctly got it resolved by >> loading the Tools subpackage of FFI. I think Marcel has recently split >> it. >> >> So if that is true, the load order in the baseline of FFI should be >> updated. >> >> Kind regards, >> Jakob >> >> Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph >> : >> > >> > Hi all, >> > >> > >> > I fear this could be a frequently discussed topic, but I did not know >> where to start else. >> > >> > >> > In the web, you can find the following instruction in order to load FFI >> at several places, amongst them the Swiki: >> > >> > >> > Metacello new configuration: 'FFI'; load. >> > >> > >> > If I do this in a fresh trunk image (#19838) without any extra >> installs, I get a syntax error: >> > >> > >> > ffiPrintString: aString >> > >> > "FFITestLibrary ffiPrintString: 'Hello'" >> > >> > "char* 'ffiPrintString' (char *) >> module:'SqueakFFIPrims'> >> > >> > ^self externalCallFailed >> > >> > >> > This is at the very least confusing, I think :-) Are there any >> undocumented dependencies or something like this? >> > >> > If I use the preference wizard instead, the installation succeeds. >> (It's not very convenient that it asks you for your initials during the >> installation, but this is only a small critique. :-)) >> > >> > I just wanted to inform you about that. >> > >> > >> > Best, >> > >> > Christoph >> > >> > >> >> >> > > -- > _,,,^..^,,,_ > best, Eliot > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 15 19:18:11 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 15 Sep 2020 12:18:11 -0700 Subject: [squeak-dev] FormInspector, or also: Text>>#= and its consequences In-Reply-To: References: <10d552ed992347e0ba801c1423dada6b@student.hpi.uni-potsdam.de> Message-ID: Hi Marcel, Hi Levente, Hi Christoph, Hi All, On Tue, Sep 15, 2020 at 7:42 AM Marcel Taeumel wrote: > Hi Christoph. > > Performance. Change it, bench it, post the results here. :-) Please > specify you machine and try it on a slow RaspPi, too. > I think it's a historical hold-over. Here's the same method in Smalltalk-80 v2: !Text methodsFor: 'comparing'! = anotherText ^string = anotherText string! ! Changing it to read other isText ifTrue: [ ^string = other string and: [runs = other runs]]. other isString ifTrue: [ ^string = other ]. ^false is not going to affect performance noticeably (runs are typically shorter than the strings and Array comparison isn't particularly slow). However, it corresponds much closer to my intuitive understanding of Texts. If I wanted to see if two texts had the same characters I would use aText string = bText string. I see Levente's comment. but I think he's just commenting the anomaly inherited from Smalltalk-80, not saying "it must be this way". Am I right Levente? So how bad is the performance? I chose some texts (they happen to be in the help browser, and as such they represent representative large texts, which I think is what we're worried about for performance) via Text allInstances select: [:t| t size > 5000 and: [t runs runs size > (t size / 200)]] (why text runs runs size? Because text runs size = text size. text runs runs answers the size of the array holding the lengths of each emphasis run) Then I benchmarked the comparison via "Using the existing method compare strings." | copy | copy := self first copy. [self first = copy] bench '186,000 per second. 5.39 microseconds per run. 0 % GC time.' "Estimate the additional cost of comparing runs in a typical text" | copy | copy := self first copy. [self first string = copy string and: [self first runs = copy runs]] bench '154,000 per second. 6.48 microseconds per run. 0 % GC time.' "Estimate the additional cost when there is some difference in emphasis" | copy | copy := self first copy. copy addAttribute: (TextColor color: Color red) from: copy size // 2 to: copy size. [self first string = copy string and: [self first runs = copy runs]] bench '187,000 per second. 5.36 microseconds per run. 0 % GC time.' What the second one shows is that including testing for runs worsens performance by about 20%. For me that's acceptable. What the third one shows is that if emphases do in fact differ the overhead is far less, because in the runs comparison there is a size comparison, and that fails without bothering to compare all the elements. And of course the additional cost of comparing runs depends on how complex typical runs are. Here's a histogram: | texts | texts := Text allInstances select: [:t| t size > 0]. (10 to: 100 by: 10) collect: [:percentage| { percentage. (texts select: [:t| | ratio | ratio := t runs runs size / t size * 100. (ratio between: percentage - 10 and: percentage) and: [ratio ~= (percentage - 10)]]) size * 100.0 / texts size roundTo: 0.01} ] #(#(10 67.62) #(20 15.48) #(30 6.58) #(40 2.49) #(50 2.49) #(60 0.36) #(70 0.18) #(80 0.0) #(90 0.0) #(100 4.8)) So most texts have very few emphases (typically one ;-). Only 5.3% of texts have runs longer than half the size of the text. So in most cases the slow down by adding the runs comparison to the mix will be less than the 20% overhead above. The worst case is represented by benchmark two above, a large text compared against an identical copy. Best, > Marcel > > Am 10.09.2020 20:32:34 schrieb Thiede, Christoph < > christoph.thiede at student.hpi.uni-potsdam.de>: > > Hi all, > > > is there any old thread about the design discussion of how Text>>#= works? > (It does not consider attributes for quality.) Has this decision ever been > questioned? > > > Naively and without an overview of any existing components that could rely > on this implementation, I would like to question it. > > Why should *'foo' asText allBold* be equal to *'foo' asText addAttribute: > TextURL new*? With the same logic, we could also say that two > dictionaries are equal iff they have got the same keys ... > > > There is even a concrete client in the Trunk suffering from this design > decision: Marcel's new FormInspector (and analogously, MorphInspector). It > uses > TextFontReference with a FormSetFont to display a screenshot right in the > inspector pane. Unfortunately, the pane is never updated automatically > because even if the screenshot changes, the text morph thinks the old text > would equal the new one. I'd like to fix that without hacking any > workaround into the inspectors. > Even though this inspector implementation is a bit unusual, in my opinion, > it shows that the current Text >> #= implementation might not be a perfect > solution. > > I'm looking forward to your opinions. > > Best, > Christoph > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 15 20:04:34 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 15 Sep 2020 20:04:34 0000 Subject: [squeak-dev] The Trunk: Morphic-dtl.1680.mcz Message-ID: David T. Lewis uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-dtl.1680.mcz ==================== Summary ==================== Name: Morphic-dtl.1680 Author: dtl Time: 15 September 2020, 4:04:29.45022 pm UUID: ecb7a7fd-061d-44e0-802f-b08ac8ae9778 Ancestors: Morphic-mt.1679 For MouseEvent>>asMouseMove retain the original event timeStamp. Removes an unnecessary dependency on eventMillisecondClock. =============== Diff against Morphic-mt.1679 =============== Item was changed: ----- Method: MouseEvent>>asMouseMove (in category 'converting') ----- asMouseMove "Convert the receiver into a mouse move" + ^MouseMoveEvent new setType: #mouseMove startPoint: position endPoint: position trail: {position. position} buttons: buttons hand: source stamp: timeStamp! - ^MouseMoveEvent new setType: #mouseMove startPoint: position endPoint: position trail: {position. position} buttons: buttons hand: source stamp: Time eventMillisecondClock! From lewis at mail.msen.com Tue Sep 15 20:12:39 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 15 Sep 2020 16:12:39 -0400 Subject: [squeak-dev] The Trunk: Morphic-dtl.1680.mcz In-Reply-To: References: Message-ID: <20200915201239.GA52156@shell.msen.com> This was the first issue that popped up when I put a halt in eventMillisecondClock. I see a number of other senders of eventMillisecondClock that handle the case of an event buffer with time stamp of 0. Is that even possible? Dave On Tue, Sep 15, 2020 at 08:04:34PM +0000, commits at source.squeak.org wrote: > David T. Lewis uploaded a new version of Morphic to project The Trunk: > http://source.squeak.org/trunk/Morphic-dtl.1680.mcz > > ==================== Summary ==================== > > Name: Morphic-dtl.1680 > Author: dtl > Time: 15 September 2020, 4:04:29.45022 pm > UUID: ecb7a7fd-061d-44e0-802f-b08ac8ae9778 > Ancestors: Morphic-mt.1679 > > For MouseEvent>>asMouseMove retain the original event timeStamp. Removes an unnecessary dependency on eventMillisecondClock. > > =============== Diff against Morphic-mt.1679 =============== > > Item was changed: > ----- Method: MouseEvent>>asMouseMove (in category 'converting') ----- > asMouseMove > "Convert the receiver into a mouse move" > + ^MouseMoveEvent new setType: #mouseMove startPoint: position endPoint: position trail: {position. position} buttons: buttons hand: source stamp: timeStamp! > - ^MouseMoveEvent new setType: #mouseMove startPoint: position endPoint: position trail: {position. position} buttons: buttons hand: source stamp: Time eventMillisecondClock! > > From tim at rowledge.org Tue Sep 15 20:17:41 2020 From: tim at rowledge.org (tim Rowledge) Date: Tue, 15 Sep 2020 13:17:41 -0700 Subject: [squeak-dev] The Trunk: Morphic-dtl.1680.mcz In-Reply-To: <20200915201239.GA52156@shell.msen.com> References: <20200915201239.GA52156@shell.msen.com> Message-ID: <77AC941C-25BB-47CE-B1B8-690D2EC375A6@rowledge.org> > On 2020-09-15, at 1:12 PM, David T. Lewis wrote: > > This was the first issue that popped up when I put a halt in eventMillisecondClock. > > I see a number of other senders of eventMillisecondClock that handle the case > of an event buffer with time stamp of 0. Is that even possible? I wrote about some of those last week; I suspect that the idiom was there because some very early VM stuff couldn't provide the time stamp. Certainly shouldn't be the case any longer and I'd be inclined to remove them all. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim I haven't lost my mind; it's backed up on tape somewhere. From lewis at mail.msen.com Tue Sep 15 20:48:33 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 15 Sep 2020 16:48:33 -0400 Subject: [squeak-dev] The Trunk: Morphic-dtl.1680.mcz In-Reply-To: <77AC941C-25BB-47CE-B1B8-690D2EC375A6@rowledge.org> References: <20200915201239.GA52156@shell.msen.com> <77AC941C-25BB-47CE-B1B8-690D2EC375A6@rowledge.org> Message-ID: <20200915204833.GA58276@shell.msen.com> On Tue, Sep 15, 2020 at 01:17:41PM -0700, tim Rowledge wrote: > > > > On 2020-09-15, at 1:12 PM, David T. Lewis wrote: > > > > This was the first issue that popped up when I put a halt in eventMillisecondClock. > > > > I see a number of other senders of eventMillisecondClock that handle the case > > of an event buffer with time stamp of 0. Is that even possible? > > I wrote about some of those last week; I suspect that the idiom was there because some very early VM stuff couldn't provide the time stamp. Certainly shouldn't be the case any longer and I'd be inclined to remove them all. > Yes thanks, sounds right. This one was kind of a trivial change, but it seemed to be the most common use of eventMillisecondClock in practice, so it seemed worth a commit to make it go away. Dave From eliot.miranda at gmail.com Tue Sep 15 21:07:57 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 15 Sep 2020 14:07:57 -0700 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: <81AD7450-676E-4C78-BA29-2E84BD97A539@gmail.com> References: <81AD7450-676E-4C78-BA29-2E84BD97A539@gmail.com> Message-ID: Here's the VM event template: typedef struct sqInputEvent { sqIntptr_t type; /* type of event; either one of EventTypeXXX */ usqIntptr_t timeStamp; /* time stamp */ /* the interpretation of the following fields depend on the type of the event */ sqIntptr_t unused1; sqIntptr_t unused2; sqIntptr_t unused3; sqIntptr_t unused4; sqIntptr_t unused5; sqIntptr_t windowIndex; /* SmallInteger used in image to identify a host window structure */ } sqInputEvent; The interesting thing here is that the timeStamp is already a 64-bit quantity on 64-bit platforms. So it is trivial to use UTC microseconds divided by 1000, or (UTC microseconds minus start microseconds) divided by 1000, to timestamp events on 64-bit platforms. On 32-bit platforms we could either add an extra field at the end, or do something heinous with type. We could use 8 bits for the type and the top 24 bits to extend the 32-bit timestamp to 56 bits, which gives us 2.28 million years of milliseconds, or 2.28 thousand years of microseconds, while still leaving us 248 new event types (we use 8 at the moment). So why don't we - modify the VM to stamp events with the utc second clock, either using the full 64-bits of the timestamp on 64-bit platforms, or the 32-bit timestamp and the top 24 bits of the type field on 32-bit platforms. - add a flag to the image flags that says whether events are time stamped in utc microseconds (if set) or in wrapping milliseconds from startup (or whatever the cross-platform backward-compatiblity semantics should be) - modify primitive 94 primitiveGetNextEvent to examine the flag and either pass the event up as is, or convert it into a backards=compatible event depending on the flag copied from the image header at startup? The benefit is that we can ditch support for millisecond time stamps in all parts of the VM other than in primitive 94 primitiveGetNextEvent. On Tue, Sep 15, 2020 at 9:04 AM Eliot Miranda wrote: > Hi Marcel, Hi Dave, > > On Sep 15, 2020, at 8:28 AM, Marcel Taeumel wrote: > >  > Hi Dave. > > > ... but I think that we should first look at how we might handle this > better in the image ... > > To some extent, this will be the good'ol discussion about how to achieve > cross-platform compatibility. Whose job is it? VM? Image? Both? Do we want > to have "platformName = 'Win32'" (or similar) checks in the image? Or can > we rely on platform-independent information coming from the VM? > > Well, thinking about maybe the future role of FFI for Squeak, we may not > want to change too much in the VM at this point. A minimal-effort approach > with in-image change might be the right thing to do. :-) > > > Agreed. My concerns are > > 1. The wrapping millisecond clock is a baaaaaad idea. It wraps in 49 > days. The code that used to exist in Time was extremely complex and hard > to maintain (fooling the system into being close to wrap around is not > easy). And now we do have applications which are expected to run for long > periods. But even though it is a bad idea we presumably have applications > in images (someone mentioned RFB) which depend on the vm stamping events > using the wrapping millisecond clock, and if possible the vm should > continue to do so > > 2. we have a much better much simpler clock that does not wrap and can be > used to compare times between images across the globe, the utc microsecond > clock. If we want timestamps we should derive them from this clock > > 3. backwards compatibility. If we change event timestamps do we lose the > ability to run 5.x images on the new vm? Is there a way that the vm can be > informed by the image whether it wants timestamps? (We can add another > image header flag to control the vm response) > > Regarding 3. I can imagine adding a 32-bit field at the end of a vm event > and have having 64 bits (existing time stamp plus new field) to allow > stamping the event with the utc clock (& not utc now but the utc updated by > the vm heartbeat at 500Hz). > > Best, > Marcel > > Am 15.09.2020 17:04:18 schrieb David T. Lewis : > I do not have any specific solution or approach in mind, but I think > that we should first look at how we might handle this better in the > image. The Windows VM is not doing anything wrong, on the contrary I > think that its policy of using native MSG timestamps feels like the > right thing to do. > > It seems to me that the problem is our assumption that event timestamps > should match the millisecond clock in the VM. Certainly that can > be made to work, but seems to me like fixing the wrong problem. > > Time class>>eventMillisecondClock is a recent addition. Maybe we > can find another way to synthesize event time stamps in the image. > > Dave > > > On Tue, Sep 15, 2020 at 04:32:59PM +0200, Marcel Taeumel wrote: > > Hi all. > > > > Here is a quick thought: It should be possible to synthesize events from > within the image to then use those synthesized events along with regular > events. Think about testing, mouse over etc. Thus, either > #eventMillisecondClock should be platform-specific, or all event timestamps > that come from the VM should be similar ... which means that the Windows VM > code needs to be changed to not use the MSG structure but ioMSecs. > > > > Best, > > Marcel > > Am 12.09.2020 21:58:50 schrieb David T. Lewis : > > Thanks Tim, > > > > I've been digging through version control history, and I think what > > I see is that the assumption of prim 94 matching prim 135 was valid > > through the life of the interpreter VM (squeakvm.org SVN sources) but > > that it had changed for Windows as of the public release of Cog. > > > > I don't see anything wrong with the Windows VM implementation (aside > > from the one bug that Christoph fixed), instead I think we may just > > be dealing with a holdover assumption that is no longer valid. > > > > I'm inclined to think we should see if we can make the prim 135 > > dependence go away in the image. Some other way of synthesizing event > > time stamps maybe? > > > > Dave > > > > On Fri, Sep 11, 2020 at 03:45:35PM -0700, tim Rowledge wrote: > > > I think the key thing is that the answers from the primitive 135 must > be aligned with the tick values obtained from primitive 94. > > > > > > Of course if prim 94 is properly functional (all elements of the event > array are correctly filled in) then prim 135 should never be needed; it was > created for those machines that waaaaay back when I added the input events > still couldn't provide such data. I'm not entirely sure there actually were > any.... > > > > > > The only usage of prim 135 not related directly to events seems to be > SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods > that looks a bit iffy. > > > > > > EventSensor>>#createMouseEvent is only used in a couple of places to > do with rectangle transforming interactively, and probably ought to be > improved. > > > EventSensor>>#nextEventSynthesized is only used if the EventSensor has > no event queue and that is ... a bit complex but probably should never > happen since we removed the old InputSensor years and years ago. > > > EventSensor>>#primGetNextEvent: - since we declare the prim essential > maybe we should drop the fake-it code and really insist on the prim being > there? > > > MorphicEvent>>#timeStamp looks like a probably redundant backup? > > > MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to > copy the time stamp from the receiver? > > > > > > > > > > > > tim > > > -- > > > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > > > There are no stupid questions. But, there are a lot of inquisitive > idiots. > > > > > > > > > > > > > > > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 15 22:00:57 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 15 Sep 2020 15:00:57 -0700 Subject: [squeak-dev] The Inbox: Kernel-kfr.1339.mcz In-Reply-To: References: Message-ID: Hi Karl, On Mon, Sep 14, 2020 at 10:48 PM wrote: > A new version of Kernel was added to project The Inbox: > http://source.squeak.org/inbox/Kernel-kfr.1339.mcz > > ==================== Summary ==================== > > Name: Kernel-kfr.1339 > Author: kfr > Time: 15 September 2020, 7:48:44.907977 am > UUID: 9e1761d0-c2a1-b74a-bfe7-90dd49e320f1 > Ancestors: Kernel-ct.1338 > > Fix a deprecation warning > > =============== Diff against Kernel-ct.1338 =============== > > Item was changed: > ----- Method: Class>>binding (in category 'compiling') ----- > binding > "Answer a binding for the receiver, sharing if possible" > | binding | > + binding := Smalltalk globals associationAt: name ifAbsent: [nil -> > self]. > - binding := self environment associationAt: name ifAbsent: [nil -> > self]. > ^binding value == self ifTrue:[binding] ifFalse:[nil -> self].! > See Environment>>associationAt: aSymbol ifAbsent: aBlock "Senders of this should probably be using #bindingOf:" self flag: #review. ^ declarations associationAt: aSymbol ifAbsent: aBlock So let me suggest Class>>binding "Answer a binding for the receiver, sharing if possible" (self environment bindingOf: name ifAbsent: nil) ifNotNil: [:bindingOrNil| bindingOrNil value == self ifTrue: [^bindingOrNil]]. ^ClassBinding key: nil value: self and then gradually all those anonymous Associations in class methods will disappear :-) _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Tue Sep 15 23:29:32 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 15 Sep 2020 19:29:32 -0400 Subject: [squeak-dev] The Trunk: Morphic-dtl.1680.mcz In-Reply-To: <20200915204833.GA58276@shell.msen.com> References: <20200915201239.GA52156@shell.msen.com> <77AC941C-25BB-47CE-B1B8-690D2EC375A6@rowledge.org> <20200915204833.GA58276@shell.msen.com> Message-ID: <20200915232932.GA80156@shell.msen.com> On Tue, Sep 15, 2020 at 04:48:33PM -0400, David T. Lewis wrote: > On Tue, Sep 15, 2020 at 01:17:41PM -0700, tim Rowledge wrote: > > > > > > > On 2020-09-15, at 1:12 PM, David T. Lewis wrote: > > > > > > This was the first issue that popped up when I put a halt in eventMillisecondClock. > > > > > > I see a number of other senders of eventMillisecondClock that handle the case > > > of an event buffer with time stamp of 0. Is that even possible? > > > > I wrote about some of those last week; I suspect that the idiom was there because some very early VM stuff couldn't provide the time stamp. Certainly shouldn't be the case any longer and I'd be inclined to remove them all. > > > > Yes thanks, sounds right. > > This one was kind of a trivial change, but it seemed to be the most common use > of eventMillisecondClock in practice, so it seemed worth a commit to make it > go away. > Tim, And as you also said in your earlier message: > EventSensor>>#createMouseEvent is only used in a couple of places to do > with rectangle transforming interactively, and probably ought to be improved. > EventSensor>>#nextEventSynthesized is only used if the EventSensor has > no event queue and that is ... a bit complex but probably should > never happen since we removed the old InputSensor years and years ago. > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe > we should drop the fake-it code and really insist on the prim being there? These three seem to be at the heart of the matter. If we can sort these out, then the the millisecond clock may become a moot point. Dave From commits at source.squeak.org Wed Sep 16 02:28:39 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 16 Sep 2020 02:28:39 0000 Subject: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz Message-ID: A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-dtl.1340.mcz ==================== Summary ==================== Name: Kernel-dtl.1340 Author: dtl Time: 15 September 2020, 10:28:36.703545 pm UUID: d155f790-f808-4f68-ae73-3d98434f66d0 Ancestors: Kernel-ul.1339 Implement Sensor eventTimeNow for support of synthesized events. Eliminates the sometimes incorrect assumption that event time stamps must match the obsolete VM millisecond clock, and allows the Windows VM to consistently use native Windows event time stamps in the event queue. =============== Diff against Kernel-ul.1339 =============== Item was changed: Object subclass: #EventSensor + instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore lastEventTime' - instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore' classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptWatcherProcess KeyDecodeTable' poolDictionaries: 'EventSensorConstants' category: 'Kernel-Processes'! !EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0! An EventSensor is an interface to the user input devices. There is at least one instance of EventSensor named Sensor in the system. EventSensor is a replacement for the earlier InputSensor implementation based on a set of (optional) event primitives. An EventSensor updates its state when events are received so that all state based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor mouseButtons) will work exactly as before, by moving the current VM mechanisms into EventSensor itself. An optional input semaphore is part of the new design. For platforms that support true asynchronous event notification, the semaphore will be signaled to indicate pending events. On platforms that do not support asynchronous notifications about events, the UI will have to poll EventSensor periodically to read events from the VM. Instance variables: mouseButtons - mouse button state as replacement for primMouseButtons mousePosition - mouse position as replacement for primMousePt keyboardBuffer - keyboard input buffer interruptKey - currently defined interrupt key interruptSemaphore - the semaphore signaled when the interruptKey is detected eventQueue - an optional event queue for event driven applications inputSemaphore - the semaphore signaled by the VM if asynchronous event notification is supported lastEventPoll - the last millisecondClockValue at which we called fetchMoreEvents hasInputSemaphore - true if my inputSemaphore has actually been signaled at least once. Class variables: ButtonDecodeTable - maps mouse buttons as reported by the VM to ones reported in the events. KeyDecodeTable SmallInteger>> - maps some keys and their modifiers to other keys (used for instance to map Ctrl-X to Alt-X) InterruptSemaphore - signalled by the the VM and/or the event loop upon receiving an interrupt keystroke. InterruptWatcherProcess - waits on the InterruptSemaphore and then responds as appropriate. EventPollPeriod - the number of milliseconds to wait between polling for more events in the userInterruptHandler. EventTicklerProcess - the process that makes sure that events are polled for often enough (at least every EventPollPeriod milliseconds). Event format: The current event format is very simple. Each event is recorded into an 8 element array. All events must provide some SmallInteger ID (the first field in the event buffer) and a time stamp (the second field in the event buffer), so that the difference between the time stamp of an event and the current time can be reported. Currently, the following events are defined: Null event ============= The Null event is returned when the ST side asks for more events but no more events are available. Structure: [1] - event type 0 [2-8] - unused Mouse event structure ========================== Mouse events are generated when mouse input is detected. [1] - event type 1 [2] - time stamp [3] - mouse x position [4] - mouse y position [5] - button state; bitfield with the following entries: 1 - 2r001 yellow (e.g., right) button 2 - 2r010 blue (e.g., middle) button 4 - 2r100 red (e.g., left) button [all other bits are currently undefined] [6] - modifier keys; bitfield with the following entries: 1 - shift key 2 - ctrl key 4 - (Mac specific) option key 8 - Cmd/Alt key [all other bits are currently undefined] [7] - reserved. [8] - host window id. Keyboard events ==================== Keyboard events are generated when keyboard input is detected. [1] - event type 2 [2] - time stamp [3] - character code (Ascii) For now the character code is in Mac Roman encoding. See #macToSqueak. For key press/release (see [4]), character codes are normalized. [4] - press state; integer with the following meaning 0 - character (aka. key stroke or key still pressed) 1 - key press (aka. key down) 2 - key release (aka. key up) [5] - modifier keys (same as in mouse events) For key press/release (see [4]), modifier keys are still accessible. [6] - character code (Unicode UTF32) Manual decoding via KeyboardInputInterpreter possible. For key press/release (see [4]), character codes are normalized. [7] - reserved. [8] - host window id. Mouse-wheel event structure ========================== Mouse-wheel events are generated when mouse-wheel input is detected. [1] - event type 7 [2] - time stamp [3] - horizontal scroll delta [4] - vertical scroll delta [5] - button state (same as in mouse events) [6] - modifier keys (same as in mouse events) [7] - reserved. [8] - host window id. ! Item was changed: ----- Method: EventSensor>>createMouseEvent (in category 'mouse') ----- createMouseEvent "create and return a new mouse event from the current mouse position; this is useful for restarting normal event queue processing after manual polling" | buttons modifiers pos mapped eventBuffer | eventBuffer := Array new: 8. buttons := self peekButtons. pos := self peekPosition. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. eventBuffer at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ^ eventBuffer! Item was added: + ----- Method: EventSensor>>eventTimeNow (in category 'private') ----- + eventTimeNow + "Answer an event timeStamp that is slightly more recent than that of + the most recently processed event. Intended for synthesized events to + be processed in line with those from the real event queue." + + ^ lastEventTime + 1. + ! Item was changed: ----- Method: EventSensor>>peekEventSynthesized (in category 'private') ----- peekEventSynthesized "Return a synthesized event. This method is called if an event driven client wants to receive events but the primary user interface is not event-driven (e.g., the receiver does not have an event queue but only updates its state). This can, for instance, happen if a Morphic World is run in an MVC window. To simplify the clients work this method will always return all available keyboard events first, and then (repeatedly) the mouse events. Since mouse events come last, the client can assume that after one mouse event has been received there are no more to come. Note that it is impossible for EventSensor to determine if a mouse event has been issued before so the client must be aware of the possible problem of getting repeatedly the same mouse events. See HandMorph>>processEvents for an example on how to deal with this." | kbd array buttons pos modifiers mapped | "First check for keyboard" array := Array new: 8. keyboardBuffer isEmpty ifFalse: ["simulate keyboard event" array at: 1 put: EventTypeKeyboard. "evt type" + array at: 2 put: self eventTimeNow. "time stamp" - array at: 2 put: Time eventMillisecondClock. "time stamp" array at: 3 put: ((kbd := keyboardBuffer peek) bitAnd: 255). "char code" array at: 4 put: EventKeyChar. "key press/release" array at: 5 put: (kbd bitShift: -8). "modifier keys" ^ array]. "Then check for mouse" pos := mousePosition. buttons := mouseButtons. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. array at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ^ array ! Item was changed: ----- Method: EventSensor>>primGetNextEvent: (in category 'private-I/O') ----- primGetNextEvent: array "Store the next OS event available into the provided array. Essential. If the VM is not event driven the ST code will fall back to the old-style mechanism and use the state based primitives instead." | kbd buttons modifiers pos mapped | "Simulate the events" array at: 1 put: EventTypeNone. "assume no more events" "First check for keyboard" kbd := self oldPrimKbdNext. kbd = nil ifFalse:[ "simulate keyboard event" array at: 1 put: EventTypeKeyboard. "evt type" + array at: 2 put: self eventTimeNow. "time stamp" - array at: 2 put: Time eventMillisecondClock. "time stamp" array at: 3 put: (kbd bitAnd: 255). "char code" array at: 4 put: EventKeyChar. "key press/release" array at: 5 put: (kbd bitShift: -8). "modifier keys" ^self]. "Then check for mouse" buttons := self oldPrimMouseButtons. pos := self oldPrimMousePt. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. (pos = mousePosition and:[(mapped bitOr: (modifiers bitShift: 3)) = mouseButtons]) ifTrue:[^self]. array at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ! Item was changed: ----- Method: EventSensor>>processEvent: (in category 'private-I/O') ----- processEvent: evt "Process a single event. This method is run at high priority." | type buttons window | type := evt at: 1. + lastEventTime := evt at: 2. "Only process main window events, forward others to host window proxies" window := evt at: 8. (window isNil or: [window isZero]) ifTrue: [window := 1. evt at: 8 put: window]. window = 1 ifFalse: [ ^Smalltalk at: #HostWindowProxy ifPresent: [:w | w processEvent: evt]]. "Tackle mouse events and mouse wheel events first" (type = EventTypeMouse or: [type = EventTypeMouseWheel]) ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) + 1). evt at: 5 put: (Smalltalk platformName = 'Mac OS' ifTrue: [ buttons ] ifFalse: [ self mapButtons: buttons modifiers: (evt at: 6) ]). self queueEvent: evt. type = EventTypeMouseWheel ifTrue: [^ self processMouseWheelEvent: evt]. type = EventTypeMouse ifTrue: [^ self processMouseEvent: evt]]. "Store the event in the queue if there's any" type = EventTypeKeyboard ifTrue: [ "Check if the event is a user interrupt" ((evt at: 4) = EventKeyChar and: [((evt at: 3) bitOr: (((evt at: 5) bitAnd: 8) bitShift: 8)) = interruptKey]) ifTrue: ["interrupt key is meta - not reported as event" ^ interruptSemaphore signal]. "Decode keys for characters (i.e., duplicate or swap, ctrl <-> alt/cmd)." (evt at: 4) = EventKeyChar ifTrue: [ | unicode ascii | "Copy lookup key first in case of key swap." unicode := {evt at: 6. evt at: 5}. ascii := {evt at: 3. evt at: 5}. KeyDecodeTable "Unicode character first" at: unicode ifPresent: [:a | evt at: 6 put: a first; at: 5 put: a second]. KeyDecodeTable "ASCII character second" at: ascii ifPresent: [:a | evt at: 3 put: a first; at: 5 put: a second]]. self queueEvent: evt. self processKeyboardEvent: evt . ^self ]. "Handle all events other than Keyboard or Mouse." self queueEvent: evt. ! From commits at source.squeak.org Wed Sep 16 02:29:46 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 16 Sep 2020 02:29:46 0000 Subject: [squeak-dev] The Inbox: Morphic-dtl.1681.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-dtl.1681.mcz ==================== Summary ==================== Name: Morphic-dtl.1681 Author: dtl Time: 15 September 2020, 10:29:40.709583 pm UUID: ee5dc42a-7d85-4c8b-b9e0-618eae3df221 Ancestors: Morphic-dtl.1680 Use Sensor eventTimeNow for synthesized event times. =============== Diff against Morphic-dtl.1680 =============== Item was changed: ----- Method: HandMorph>>generateDropFilesEvent: (in category 'private events') ----- generateDropFilesEvent: evtBuf "Generate the appropriate mouse event for the given raw event buffer" "Note: This is still in an experimental phase and will need more work" | position buttons modifiers stamp numFiles dragType | stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. dragType := evtBuf third. position := evtBuf fourth @ evtBuf fifth. buttons := 0. modifiers := evtBuf sixth. buttons := buttons bitOr: (modifiers bitShift: 3). numFiles := evtBuf seventh. dragType = 4 ifTrue: ["e.g., drop" owner borderWidth: 0. ^DropFilesEvent new setPosition: position contents: numFiles hand: self]. "the others are currently not handled by morphs themselves" dragType = 1 ifTrue: ["experimental drag enter" owner borderWidth: 4; borderColor: owner color asColor negated]. dragType = 2 ifTrue: ["experimental drag move" ]. dragType = 3 ifTrue: ["experimental drag leave" owner borderWidth: 0]. ^nil! Item was changed: ----- Method: HandMorph>>generateKeyboardEvent: (in category 'private events') ----- generateKeyboardEvent: evtBuf "Generate the appropriate mouse event for the given raw event buffer" | buttons modifiers type pressType stamp keyValue | stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. pressType := evtBuf fourth. pressType = EventKeyDown ifTrue: [type := #keyDown]. pressType = EventKeyUp ifTrue: [type := #keyUp]. pressType = EventKeyChar ifTrue: [type := #keystroke]. modifiers := evtBuf fifth. buttons := (modifiers bitShift: 3) bitOr: (lastMouseEvent buttons bitAnd: 7). type = #keystroke ifTrue: [keyValue := (self keyboardInterpreter nextCharFrom: Sensor firstEvt: evtBuf) asInteger] ifFalse: [keyValue := evtBuf third]. ^ KeyboardEvent new setType: type buttons: buttons position: self position keyValue: keyValue hand: self stamp: stamp. ! Item was changed: ----- Method: HandMorph>>generateMouseEvent: (in category 'private events') ----- generateMouseEvent: evtBuf "Generate the appropriate mouse event for the given raw event buffer" | position buttons modifiers type trail stamp oldButtons evtChanged | evtBuf first = lastEventBuffer first ifTrue: ["Workaround for Mac VM bug, *always* generating 3 events on clicks" evtChanged := false. 3 to: evtBuf size do: [:i | (lastEventBuffer at: i) = (evtBuf at: i) ifFalse: [evtChanged := true]]. evtChanged ifFalse: [^nil]]. stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. position := evtBuf third @ evtBuf fourth. buttons := evtBuf fifth. modifiers := evtBuf sixth. type := buttons = 0 ifTrue:[ lastEventBuffer fifth = 0 ifTrue: [#mouseMove] "this time no button and previously no button .. just mouse move" ifFalse: [#mouseUp] "this time no button but previously some button ... therefore button was released" ] ifFalse:[ buttons = lastEventBuffer fifth ifTrue: [#mouseMove] "button states are the same .. now and past .. therfore a mouse movement" ifFalse: [ "button states are different .. button was pressed or released" buttons > lastEventBuffer fifth ifTrue: [#mouseDown] ifFalse:[#mouseUp]. ]. ]. buttons := buttons bitOr: (modifiers bitShift: 3). oldButtons := lastEventBuffer fifth bitOr: (lastEventBuffer sixth bitShift: 3). lastEventBuffer := evtBuf. type == #mouseMove ifTrue: [trail := self mouseTrailFrom: evtBuf. ^MouseMoveEvent new setType: type startPoint: (self position) endPoint: trail last trail: trail buttons: buttons hand: self stamp: stamp]. ^MouseButtonEvent new setType: type position: position which: (oldButtons bitXor: buttons) buttons: buttons nClicks: (evtBuf seventh ifNil: [0]) hand: self stamp: stamp! Item was changed: ----- Method: HandMorph>>generateMouseWheelEvent: (in category 'private events') ----- generateMouseWheelEvent: evtBuf "Generate the appropriate mouse wheel event for the given raw event buffer" | buttons modifiers deltaX deltaY stamp nextEvent | stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. deltaX := evtBuf third. deltaY := evtBuf fourth. buttons := evtBuf fifth. modifiers := evtBuf sixth. [(deltaX abs + deltaY abs < self class minimumWheelDelta) and: [(nextEvent := Sensor peekEvent) notNil and: [nextEvent first = evtBuf first and: [nextEvent fifth = evtBuf fifth and: [nextEvent sixth = evtBuf sixth] and: [nextEvent third isZero = evtBuf third isZero "both horizontal or vertical"]]]]] whileTrue: ["nextEvent is similar. Remove it from the queue, and check the next." nextEvent := Sensor nextEvent. deltaX := deltaX + nextEvent third. deltaY := deltaY + nextEvent fourth]. ^ MouseWheelEvent new setType: #mouseWheel position: self position delta: deltaX at deltaY buttons: buttons hand: self stamp: stamp! Item was changed: ----- Method: HandMorph>>generateWindowEvent: (in category 'private events') ----- generateWindowEvent: evtBuf "Generate the appropriate window event for the given raw event buffer" | evt | evt := WindowEvent new. evt setTimeStamp: evtBuf second. + evt timeStamp = 0 ifTrue: [evt setTimeStamp: Sensor eventTimeNow]. - evt timeStamp = 0 ifTrue: [evt setTimeStamp: Time eventMillisecondClock]. evt action: evtBuf third. evt rectangle: (Rectangle origin: evtBuf fourth @ evtBuf fifth corner: evtBuf sixth @ evtBuf seventh ). ^evt! Item was changed: ----- Method: MorphicEvent>>timeStamp (in category 'accessing') ----- timeStamp "Return the millisecond clock value at which the event was generated" + ^timeStamp ifNil:[timeStamp := Sensor eventTimeNow]! - ^timeStamp ifNil:[timeStamp := Time eventMillisecondClock]! From lewis at mail.msen.com Wed Sep 16 02:48:16 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 15 Sep 2020 22:48:16 -0400 Subject: [squeak-dev] The Trunk: Morphic-dtl.1680.mcz In-Reply-To: <20200915232932.GA80156@shell.msen.com> References: <20200915201239.GA52156@shell.msen.com> <77AC941C-25BB-47CE-B1B8-690D2EC375A6@rowledge.org> <20200915204833.GA58276@shell.msen.com> <20200915232932.GA80156@shell.msen.com> Message-ID: <20200916024816.GA8853@shell.msen.com> On Tue, Sep 15, 2020 at 07:29:32PM -0400, David T. Lewis wrote: > On Tue, Sep 15, 2020 at 04:48:33PM -0400, David T. Lewis wrote: > > On Tue, Sep 15, 2020 at 01:17:41PM -0700, tim Rowledge wrote: > > > > > > > > > > On 2020-09-15, at 1:12 PM, David T. Lewis wrote: > > > > > > > > This was the first issue that popped up when I put a halt in eventMillisecondClock. > > > > > > > > I see a number of other senders of eventMillisecondClock that handle the case > > > > of an event buffer with time stamp of 0. Is that even possible? > > > > > > I wrote about some of those last week; I suspect that the idiom was there because some very early VM stuff couldn't provide the time stamp. Certainly shouldn't be the case any longer and I'd be inclined to remove them all. > > > > > > > Yes thanks, sounds right. > > > > This one was kind of a trivial change, but it seemed to be the most common use > > of eventMillisecondClock in practice, so it seemed worth a commit to make it > > go away. > > > > Tim, > > And as you also said in your earlier message: > > > EventSensor>>#createMouseEvent is only used in a couple of places to do > > with rectangle transforming interactively, and probably ought to be improved. > > EventSensor>>#nextEventSynthesized is only used if the EventSensor has > > no event queue and that is ... a bit complex but probably should > > never happen since we removed the old InputSensor years and years ago. > > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe > > we should drop the fake-it code and really insist on the prim being there? > > > These three seem to be at the heart of the matter. If we can sort these out, > then the the millisecond clock may become a moot point. > Hmmm... it couldn't possibly be this simple?!? Maybe I am overlooking something. In any case, I put these in the inbox: Name: Kernel-dtl.1340 Implement Sensor eventTimeNow for support of synthesized events. Eliminates the sometimes incorrect assumption that event time stamps must match the obsolete VM millisecond clock, and allows the Windows VM to consistently use native Windows event time stamps in the event queue. Name: Morphic-dtl.1681 Use Sensor eventTimeNow for synthesized event times. Dave From tonyg at leastfixedpoint.com Wed Sep 16 07:49:10 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Wed, 16 Sep 2020 09:49:10 +0200 Subject: [squeak-dev] FFI type for 'char[12]'? Message-ID: Hi all, Is there some way in the FFI to specify that an ExternalStructure has a member whose type is like 'char x[12]'? Similarly, given some ExternalStructure Foo, can I have another structure that has a member like 'Foo x[5]'? Tony From marcel.taeumel at hpi.de Wed Sep 16 07:49:25 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Wed, 16 Sep 2020 09:49:25 +0200 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> Message-ID: Hi Eliot, hi all. We had the discussion about the role of Installer recently on the list. After fixing Metacello (and the script for FFI), the wizard will likely to use "Metacello new configuration: 'FFI'; load." again to manage support for different Squeak versions. Here is the discussion about the future of "Installer": http://forum.world.st/Installer-metacello-tp5115805.html [http://forum.world.st/Installer-metacello-tp5115805.html] Best, Marcel Am 15.09.2020 20:29:46 schrieb Eliot Miranda : Hi Marcel, Hi All, On Tue, Sep 15, 2020 at 11:20 AM Eliot Miranda wrote: Hi Marcel, On Tue, Sep 15, 2020 at 6:27 AM Marcel Taeumel wrote: Hi all. >  In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Loading FFI via the Preference Wizard -- which uses the Installer directly -- works fine in current (fresh) trunk. No debuggers to confirm. No syntax error. That's good to know.  I'll alter my VMMaker image build scripts to use this.  But there's one code smell about this.  PreferenceWizardMorph is a morph.  i wonder if it's worth-while separating PreferenceWizardMorph into PreferenceWizardMorph and PreferenceWizard.  It feels better changing the script to read PreferenceWizard new installFFI than having it rad PreferenceWizardMorph new installFFI.  If you agree that this is OK, I can make the changes. Hmmm, maybe better is to move these install scripts to the Installer hierarchy.  Not sure where they should live.  But it would be nice to be able to say      Installer installFFI and have it choose a default path, as well as be able to say     Installer squeak installFFI and have it install via InstallerMonticello I am updating that Metacello script right now. Be patient. :-) Best, Marcel Am 14.09.2020 18:35:46 schrieb Thiede, Christoph : Hi Jakob, do you have a description of the correct loading order? In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Best, Christoph Von: Squeak-dev im Auftrag von Jakob Reschke Gesendet: Donnerstag, 10. September 2020 08:04:51 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] SyntaxError while loading FFI   Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph : > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > -- _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Wed Sep 16 07:58:45 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Wed, 16 Sep 2020 09:58:45 +0200 Subject: [squeak-dev] FFI type for 'char[12]'? In-Reply-To: References: Message-ID: Hi Tony, such container types are still work-in-progress. :-/ For char[12], you can either use "string" or "char*", I suppose. "Foo x[5]" can be expressed through ExternalData, too, which would be "Foo*". Best, Marcel Am 16.09.2020 09:49:20 schrieb Tony Garnock-Jones : Hi all, Is there some way in the FFI to specify that an ExternalStructure has a member whose type is like 'char x[12]'? Similarly, given some ExternalStructure Foo, can I have another structure that has a member like 'Foo x[5]'? Tony -------------- next part -------------- An HTML attachment was scrubbed... URL: From Tom.Beckmann at student.hpi.uni-potsdam.de Wed Sep 16 08:11:59 2020 From: Tom.Beckmann at student.hpi.uni-potsdam.de (Beckmann, Tom) Date: Wed, 16 Sep 2020 08:11:59 +0000 Subject: [squeak-dev] FFI type for 'char[12]'? In-Reply-To: References: , Message-ID: Hi, for ExternalStructure fields definitions I worked around this by exploding the array, as in #((str1 'char') (str2 'char') (str3 'char') (str4 'char') (str5 'char')) The same works for other ExternalTypes like Foo of course. I also have an experimental set of changes that adds an ExternalArrayType to support this, however it still has some untested corner cases. I'll investigate getting it merged to FFI soon. Best, Tom ________________________________________ From: Squeak-dev on behalf of Taeumel, Marcel Sent: Wednesday, September 16, 2020 9:58:45 AM To: squeak-dev Subject: Re: [squeak-dev] FFI type for 'char[12]'? Hi Tony, such container types are still work-in-progress. :-/ For char[12], you can either use "string" or "char*", I suppose. "Foo x[5]" can be expressed through ExternalData, too, which would be "Foo*". Best, Marcel Am 16.09.2020 09:49:20 schrieb Tony Garnock-Jones : Hi all, Is there some way in the FFI to specify that an ExternalStructure has a member whose type is like 'char x[12]'? Similarly, given some ExternalStructure Foo, can I have another structure that has a member like 'Foo x[5]'? Tony From christoph.thiede at student.hpi.uni-potsdam.de Wed Sep 16 10:07:43 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Wed, 16 Sep 2020 10:07:43 +0000 Subject: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) Message-ID: Hi Dave, > If nobody objects in the next day or so, I will move both System-dtl.1170 and System-dtl.1171 from inbox to trunk. That will removethe #World binding. I would like to kindly object. :-) I share your desire to clean up outdated variables and unify access interfaces, but in this particular case IMHO the damage of probably dozens of packages not working any longer until individual patching clearly outweighs the advantages of such a cleanup ... I am afraid that many of these projects are not actively being maintained, never will be patched, and thus will be lost forever. Of course I would patch my favorite packages but I doubt that every other weekend Squeaker, likely not having subscribed to this list, will do so, too. This is why I would prefer a fallback solution with DeprecationWarning. Best,Christoph Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Dienstag, 15. September 2020, 04:58 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Project loading broking in trunk since Squeak 5.3 release (was: Etoys help needed loading e.g. CarAndPen.014.pr) On Mon, Sep 14, 2020 at 07:43:47PM -0700, Eliot Miranda wrote: > Hi David, > > On Mon, Sep 14, 2020 at 5:24 PM David T. Lewis wrote: > > > I tracked this down a bit further, and the issue began after this change: > > > > Name: Collections-eem.885 > > Author: eem > > Time: 15 April 2020, 4:37:54.800631 pm > > UUID: 45d219d3-6ed0-4401-a820-44eebe21d71a > > Ancestors: Collections-eem.883, Collections-dtl.884 > > > > Switch elementsForwardIdentityTo: to not copy the hash, see > > http://forum.world.st/How-to-become-immediate-objects-td5114931.html. > > Add elementsForwardIdentityAndHashTo: for the old behavior. > > > > I made some updates to image segments to accomodate this change, and > > the Etoys project loading works again. Updates are in the inbox in > > System-dtl.1171. > > > > @eliot- This needs review because I just made naive updates that seem > > to work, but I touched several methods and I'm not sure if it got it > > right. > > > > First, sorry I broke this. Second, I looked at your changes. They look > correct to me. > Thanks Eliot. If nobody objects in the next day or so, I will move both System-dtl.1170 and System-dtl.1171 from inbox to trunk. That will remove the #World binding. I realize that there are some externally maintained projects that may be affected, but I am reassured that old Etoys projects can still be loaded, and I expect that any remaining problems can be easily addressed. Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 11:07:33 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 11:07:33 +0000 Subject: [squeak-dev] Dead exceptions In-Reply-To: References: <9d60e8b589bf4afb81404ed9b5e1f78e@student.hpi.uni-potsdam.de> , Message-ID: Hi all! 1. What is the purpose of EndOfStream if we don't use it even longer in Trunk? Sounds like an alternative iteration concept (cf. Python's StopIteration) which however would be contrary to the existing mechanism using #next and #atEnd. 2. What's about the other exception classes I have listed below? May I deprecate them all? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Dienstag, 15. September 2020 14:15:05 An: squeak-dev Betreff: Re: [squeak-dev] Dead exceptions Hi all! > I would like to see us keep at least EndOfStream. ExceptionAboutToReturn and Abort can be deleted, not just deprecated. They're ancient and obsolete. +1 Best, Marcel Am 14.09.2020 21:42:01 schrieb Eliot Miranda : Hi Christoph, On Mon, Sep 14, 2020 at 11:30 AM Thiede, Christoph > wrote: Hi all, while browsing a bit through our exception framework, I stumbled upon a significant number of exception classes for all of which I neither found any sender in my Trunk image, nor any subclass: Abort EndOfStream ExceptionAboutToReturn FontSubstitutionDuringLoading NanError UndeclaredVariableReference So here is my question: Would you agree to deprecate them all? Or could any of them still be relevant for non-in-trunk senders? I can prepare a changeset if you think we should get rid of them. I would like to see us keep at least EndOfStream. ExceptionAboutToReturn and Abort can be deleted, not just deprecated. They're ancient and obsolete. Best, Christoph _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 11:11:47 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 11:11:47 +0000 Subject: [squeak-dev] FormInspector, or also: Text>>#= and its consequences In-Reply-To: References: <10d552ed992347e0ba801c1423dada6b@student.hpi.uni-potsdam.de>, Message-ID: Interesting, I would not have assumed that this would be only about performance, sounded like a more profound design decision to me. If no one sees a problem in changing this behavior, I can try my luck. :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Dienstag, 15. September 2020 16:42:11 An: squeak-dev Betreff: Re: [squeak-dev] FormInspector, or also: Text>>#= and its consequences Hi Christoph. Performance. Change it, bench it, post the results here. :-) Please specify you machine and try it on a slow RaspPi, too. Best, Marcel Am 10.09.2020 20:32:34 schrieb Thiede, Christoph : Hi all, is there any old thread about the design discussion of how Text>>#= works? (It does not consider attributes for quality.) Has this decision ever been questioned? Naively and without an overview of any existing components that could rely on this implementation, I would like to question it. Why should 'foo' asText allBold be equal to 'foo' asText addAttribute: TextURL new? With the same logic, we could also say that two dictionaries are equal iff they have got the same keys ... There is even a concrete client in the Trunk suffering from this design decision: Marcel's new FormInspector (and analogously, MorphInspector). It uses TextFontReference with a FormSetFont to display a screenshot right in the inspector pane. Unfortunately, the pane is never updated automatically because even if the screenshot changes, the text morph thinks the old text would equal the new one. I'd like to fix that without hacking any workaround into the inspectors. Even though this inspector implementation is a bit unusual, in my opinion, it shows that the current Text >> #= implementation might not be a perfect solution. I'm looking forward to your opinions. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 11:35:11 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 11:35:11 +0000 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914202934.GA25753@shell.msen.com>, Message-ID: <02bd205c43974b2b8fce90ed05e7cea1@student.hpi.uni-potsdam.de> Hi Vanessa, > Wouldn't the binding still live in Undeclared? Are there any pointers to the concept of Undeclared? It seems to be a weak dictionary, so this does not look like a durable compatibility solution to me. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Vanessa Freudenberg Gesendet: Montag, 14. September 2020 23:30:59 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) On Mon, Sep 14, 2020 at 1:29 PM David T. Lewis > wrote: Karl, Thank you very much! Indeed, great sleuthing! Clearly we have introduced a bug in trunk since the 5.3 release, so we'll need to fix that. But to my original question - I was able to start with a fresh 5.3 image, and I can load CarAndPen.014.pr without problems. Then I removed the #World binding, and tried loading CarAndPen.014.pr to find out what the errors would look like. Big surprise - There was no error. The project still loads and runs as before. That is not what I expected, so I'm glad we checked. Dave Wouldn't the binding still live in Undeclared? - Vanessa - -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 11:41:15 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 11:41:15 +0000 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> , Message-ID: <820d12b3751241d29b78f65a4f3f967c@student.hpi.uni-potsdam.de> Hi Marcel, thanks for fixing this. The problem was that per some package installed earlier, my main image already contained an older version of FFI. In fact, before I read your replies to this thread, I had fixed the problem in my image by loading the new FFI-Kernel versions step by step and copying a number of missing methods manually into the image. Not very elegant, but now I can use the latest FFI version in my image. 😅 I confirm that FFI loads now in a fresh trunk image without any problems, which is great. However, you have to look carefully when you the installation appears to take more than 10 minutes and the progress bars are not changing: [cid:a4162153-2d7d-497e-a16c-450b8b1d5340] It still wants to know my author initials during installation, this is a bit confusing. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Mittwoch, 16. September 2020 09:49:25 An: squeak-dev Betreff: Re: [squeak-dev] SyntaxError while loading FFI Hi Eliot, hi all. We had the discussion about the role of Installer recently on the list. After fixing Metacello (and the script for FFI), the wizard will likely to use "Metacello new configuration: 'FFI'; load." again to manage support for different Squeak versions. Here is the discussion about the future of "Installer": http://forum.world.st/Installer-metacello-tp5115805.html Best, Marcel Am 15.09.2020 20:29:46 schrieb Eliot Miranda : Hi Marcel, Hi All, On Tue, Sep 15, 2020 at 11:20 AM Eliot Miranda > wrote: Hi Marcel, On Tue, Sep 15, 2020 at 6:27 AM Marcel Taeumel > wrote: Hi all. > In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Loading FFI via the Preference Wizard -- which uses the Installer directly -- works fine in current (fresh) trunk. No debuggers to confirm. No syntax error. That's good to know. I'll alter my VMMaker image build scripts to use this. But there's one code smell about this. PreferenceWizardMorph is a morph. i wonder if it's worth-while separating PreferenceWizardMorph into PreferenceWizardMorph and PreferenceWizard. It feels better changing the script to read PreferenceWizard new installFFI than having it rad PreferenceWizardMorph new installFFI. If you agree that this is OK, I can make the changes. Hmmm, maybe better is to move these install scripts to the Installer hierarchy. Not sure where they should live. But it would be nice to be able to say Installer installFFI and have it choose a default path, as well as be able to say Installer squeak installFFI and have it install via InstallerMonticello I am updating that Metacello script right now. Be patient. :-) Best, Marcel Am 14.09.2020 18:35:46 schrieb Thiede, Christoph >: Hi Jakob, do you have a description of the correct loading order? In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Jakob Reschke > Gesendet: Donnerstag, 10. September 2020 08:04:51 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] SyntaxError while loading FFI Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph >: > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > -- _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 40985 bytes Desc: pastedImage.png URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 11:53:50 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 11:53:50 +0000 Subject: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz In-Reply-To: References: Message-ID: Hi David, how smoothly will this work if the latest send to #processEvent: is some time ago? And couldn't we run into any sort of problems with the "lastEventTime + 1" trick when making multiple fast requests to #eventTimeNow? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Mittwoch, 16. September 2020 04:28:39 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-dtl.1340.mcz ==================== Summary ==================== Name: Kernel-dtl.1340 Author: dtl Time: 15 September 2020, 10:28:36.703545 pm UUID: d155f790-f808-4f68-ae73-3d98434f66d0 Ancestors: Kernel-ul.1339 Implement Sensor eventTimeNow for support of synthesized events. Eliminates the sometimes incorrect assumption that event time stamps must match the obsolete VM millisecond clock, and allows the Windows VM to consistently use native Windows event time stamps in the event queue. =============== Diff against Kernel-ul.1339 =============== Item was changed: Object subclass: #EventSensor + instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore lastEventTime' - instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore' classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptWatcherProcess KeyDecodeTable' poolDictionaries: 'EventSensorConstants' category: 'Kernel-Processes'! !EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0! An EventSensor is an interface to the user input devices. There is at least one instance of EventSensor named Sensor in the system. EventSensor is a replacement for the earlier InputSensor implementation based on a set of (optional) event primitives. An EventSensor updates its state when events are received so that all state based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor mouseButtons) will work exactly as before, by moving the current VM mechanisms into EventSensor itself. An optional input semaphore is part of the new design. For platforms that support true asynchronous event notification, the semaphore will be signaled to indicate pending events. On platforms that do not support asynchronous notifications about events, the UI will have to poll EventSensor periodically to read events from the VM. Instance variables: mouseButtons - mouse button state as replacement for primMouseButtons mousePosition - mouse position as replacement for primMousePt keyboardBuffer - keyboard input buffer interruptKey - currently defined interrupt key interruptSemaphore - the semaphore signaled when the interruptKey is detected eventQueue - an optional event queue for event driven applications inputSemaphore - the semaphore signaled by the VM if asynchronous event notification is supported lastEventPoll - the last millisecondClockValue at which we called fetchMoreEvents hasInputSemaphore - true if my inputSemaphore has actually been signaled at least once. Class variables: ButtonDecodeTable - maps mouse buttons as reported by the VM to ones reported in the events. KeyDecodeTable SmallInteger>> - maps some keys and their modifiers to other keys (used for instance to map Ctrl-X to Alt-X) InterruptSemaphore - signalled by the the VM and/or the event loop upon receiving an interrupt keystroke. InterruptWatcherProcess - waits on the InterruptSemaphore and then responds as appropriate. EventPollPeriod - the number of milliseconds to wait between polling for more events in the userInterruptHandler. EventTicklerProcess - the process that makes sure that events are polled for often enough (at least every EventPollPeriod milliseconds). Event format: The current event format is very simple. Each event is recorded into an 8 element array. All events must provide some SmallInteger ID (the first field in the event buffer) and a time stamp (the second field in the event buffer), so that the difference between the time stamp of an event and the current time can be reported. Currently, the following events are defined: Null event ============= The Null event is returned when the ST side asks for more events but no more events are available. Structure: [1] - event type 0 [2-8] - unused Mouse event structure ========================== Mouse events are generated when mouse input is detected. [1] - event type 1 [2] - time stamp [3] - mouse x position [4] - mouse y position [5] - button state; bitfield with the following entries: 1 - 2r001 yellow (e.g., right) button 2 - 2r010 blue (e.g., middle) button 4 - 2r100 red (e.g., left) button [all other bits are currently undefined] [6] - modifier keys; bitfield with the following entries: 1 - shift key 2 - ctrl key 4 - (Mac specific) option key 8 - Cmd/Alt key [all other bits are currently undefined] [7] - reserved. [8] - host window id. Keyboard events ==================== Keyboard events are generated when keyboard input is detected. [1] - event type 2 [2] - time stamp [3] - character code (Ascii) For now the character code is in Mac Roman encoding. See #macToSqueak. For key press/release (see [4]), character codes are normalized. [4] - press state; integer with the following meaning 0 - character (aka. key stroke or key still pressed) 1 - key press (aka. key down) 2 - key release (aka. key up) [5] - modifier keys (same as in mouse events) For key press/release (see [4]), modifier keys are still accessible. [6] - character code (Unicode UTF32) Manual decoding via KeyboardInputInterpreter possible. For key press/release (see [4]), character codes are normalized. [7] - reserved. [8] - host window id. Mouse-wheel event structure ========================== Mouse-wheel events are generated when mouse-wheel input is detected. [1] - event type 7 [2] - time stamp [3] - horizontal scroll delta [4] - vertical scroll delta [5] - button state (same as in mouse events) [6] - modifier keys (same as in mouse events) [7] - reserved. [8] - host window id. ! Item was changed: ----- Method: EventSensor>>createMouseEvent (in category 'mouse') ----- createMouseEvent "create and return a new mouse event from the current mouse position; this is useful for restarting normal event queue processing after manual polling" | buttons modifiers pos mapped eventBuffer | eventBuffer := Array new: 8. buttons := self peekButtons. pos := self peekPosition. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. eventBuffer at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ^ eventBuffer! Item was added: + ----- Method: EventSensor>>eventTimeNow (in category 'private') ----- + eventTimeNow + "Answer an event timeStamp that is slightly more recent than that of + the most recently processed event. Intended for synthesized events to + be processed in line with those from the real event queue." + + ^ lastEventTime + 1. + ! Item was changed: ----- Method: EventSensor>>peekEventSynthesized (in category 'private') ----- peekEventSynthesized "Return a synthesized event. This method is called if an event driven client wants to receive events but the primary user interface is not event-driven (e.g., the receiver does not have an event queue but only updates its state). This can, for instance, happen if a Morphic World is run in an MVC window. To simplify the clients work this method will always return all available keyboard events first, and then (repeatedly) the mouse events. Since mouse events come last, the client can assume that after one mouse event has been received there are no more to come. Note that it is impossible for EventSensor to determine if a mouse event has been issued before so the client must be aware of the possible problem of getting repeatedly the same mouse events. See HandMorph>>processEvents for an example on how to deal with this." | kbd array buttons pos modifiers mapped | "First check for keyboard" array := Array new: 8. keyboardBuffer isEmpty ifFalse: ["simulate keyboard event" array at: 1 put: EventTypeKeyboard. "evt type" + array at: 2 put: self eventTimeNow. "time stamp" - array at: 2 put: Time eventMillisecondClock. "time stamp" array at: 3 put: ((kbd := keyboardBuffer peek) bitAnd: 255). "char code" array at: 4 put: EventKeyChar. "key press/release" array at: 5 put: (kbd bitShift: -8). "modifier keys" ^ array]. "Then check for mouse" pos := mousePosition. buttons := mouseButtons. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. array at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ^ array ! Item was changed: ----- Method: EventSensor>>primGetNextEvent: (in category 'private-I/O') ----- primGetNextEvent: array "Store the next OS event available into the provided array. Essential. If the VM is not event driven the ST code will fall back to the old-style mechanism and use the state based primitives instead." | kbd buttons modifiers pos mapped | "Simulate the events" array at: 1 put: EventTypeNone. "assume no more events" "First check for keyboard" kbd := self oldPrimKbdNext. kbd = nil ifFalse:[ "simulate keyboard event" array at: 1 put: EventTypeKeyboard. "evt type" + array at: 2 put: self eventTimeNow. "time stamp" - array at: 2 put: Time eventMillisecondClock. "time stamp" array at: 3 put: (kbd bitAnd: 255). "char code" array at: 4 put: EventKeyChar. "key press/release" array at: 5 put: (kbd bitShift: -8). "modifier keys" ^self]. "Then check for mouse" buttons := self oldPrimMouseButtons. pos := self oldPrimMousePt. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. (pos = mousePosition and:[(mapped bitOr: (modifiers bitShift: 3)) = mouseButtons]) ifTrue:[^self]. array at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ! Item was changed: ----- Method: EventSensor>>processEvent: (in category 'private-I/O') ----- processEvent: evt "Process a single event. This method is run at high priority." | type buttons window | type := evt at: 1. + lastEventTime := evt at: 2. "Only process main window events, forward others to host window proxies" window := evt at: 8. (window isNil or: [window isZero]) ifTrue: [window := 1. evt at: 8 put: window]. window = 1 ifFalse: [ ^Smalltalk at: #HostWindowProxy ifPresent: [:w | w processEvent: evt]]. "Tackle mouse events and mouse wheel events first" (type = EventTypeMouse or: [type = EventTypeMouseWheel]) ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) + 1). evt at: 5 put: (Smalltalk platformName = 'Mac OS' ifTrue: [ buttons ] ifFalse: [ self mapButtons: buttons modifiers: (evt at: 6) ]). self queueEvent: evt. type = EventTypeMouseWheel ifTrue: [^ self processMouseWheelEvent: evt]. type = EventTypeMouse ifTrue: [^ self processMouseEvent: evt]]. "Store the event in the queue if there's any" type = EventTypeKeyboard ifTrue: [ "Check if the event is a user interrupt" ((evt at: 4) = EventKeyChar and: [((evt at: 3) bitOr: (((evt at: 5) bitAnd: 8) bitShift: 8)) = interruptKey]) ifTrue: ["interrupt key is meta - not reported as event" ^ interruptSemaphore signal]. "Decode keys for characters (i.e., duplicate or swap, ctrl <-> alt/cmd)." (evt at: 4) = EventKeyChar ifTrue: [ | unicode ascii | "Copy lookup key first in case of key swap." unicode := {evt at: 6. evt at: 5}. ascii := {evt at: 3. evt at: 5}. KeyDecodeTable "Unicode character first" at: unicode ifPresent: [:a | evt at: 6 put: a first; at: 5 put: a second]. KeyDecodeTable "ASCII character second" at: ascii ifPresent: [:a | evt at: 3 put: a first; at: 5 put: a second]]. self queueEvent: evt. self processKeyboardEvent: evt . ^self ]. "Handle all events other than Keyboard or Mouse." self queueEvent: evt. ! -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Wed Sep 16 11:56:00 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Wed, 16 Sep 2020 13:56:00 +0200 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: <820d12b3751241d29b78f65a4f3f967c@student.hpi.uni-potsdam.de> References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> <,> <820d12b3751241d29b78f65a4f3f967c@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph. > It still wants to know my author initials during installation, this is a bit confusing. Good catch! We may want to set those initials to "FFI" when auto-generating methods. Best, Marcel Am 16.09.2020 13:41:26 schrieb Thiede, Christoph : Hi Marcel, thanks for fixing this. The problem was that per some package installed earlier, my main image already contained an older version of FFI. In fact, before I read your replies to this thread, I had fixed the problem in my image by loading the new FFI-Kernel versions step by step and copying a number of missing methods manually into the image. Not very elegant, but now I can use the latest FFI version in my image. 😅 I confirm that FFI loads now in a fresh trunk image without any problems, which is great. However, you have to look carefully when you the installation appears to take more than 10 minutes and the progress bars are not changing: It still wants to know my author initials during installation, this is a bit confusing. Best, Christoph [http://www.hpi.de/] Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Mittwoch, 16. September 2020 09:49:25 An: squeak-dev Betreff: Re: [squeak-dev] SyntaxError while loading FFI   Hi Eliot, hi all. We had the discussion about the role of Installer recently on the list. After fixing Metacello (and the script for FFI), the wizard will likely to use "Metacello new configuration: 'FFI'; load." again to manage support for different Squeak versions. Here is the discussion about the future of "Installer": http://forum.world.st/Installer-metacello-tp5115805.html [http://forum.world.st/Installer-metacello-tp5115805.html] Best, Marcel Am 15.09.2020 20:29:46 schrieb Eliot Miranda : Hi Marcel, Hi All, On Tue, Sep 15, 2020 at 11:20 AM Eliot Miranda wrote: Hi Marcel, On Tue, Sep 15, 2020 at 6:27 AM Marcel Taeumel wrote: Hi all. >  In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Loading FFI via the Preference Wizard -- which uses the Installer directly -- works fine in current (fresh) trunk. No debuggers to confirm. No syntax error. That's good to know.  I'll alter my VMMaker image build scripts to use this.  But there's one code smell about this.  PreferenceWizardMorph is a morph.  i wonder if it's worth-while separating PreferenceWizardMorph into PreferenceWizardMorph and PreferenceWizard.  It feels better changing the script to read PreferenceWizard new installFFI than having it rad PreferenceWizardMorph new installFFI.  If you agree that this is OK, I can make the changes. Hmmm, maybe better is to move these install scripts to the Installer hierarchy.  Not sure where they should live.  But it would be nice to be able to say      Installer installFFI and have it choose a default path, as well as be able to say     Installer squeak installFFI and have it install via InstallerMonticello I am updating that Metacello script right now. Be patient. :-) Best, Marcel Am 14.09.2020 18:35:46 schrieb Thiede, Christoph : Hi Jakob, do you have a description of the correct loading order? In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Best, Christoph Von: Squeak-dev im Auftrag von Jakob Reschke Gesendet: Donnerstag, 10. September 2020 08:04:51 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] SyntaxError while loading FFI   Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph : > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > -- _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 40985 bytes Desc: not available URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 11:57:09 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 11:57:09 +0000 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: <81AD7450-676E-4C78-BA29-2E84BD97A539@gmail.com>, Message-ID: <61c90e5473a44b26a5eaa321a80f281e@student.hpi.uni-potsdam.de> Hi all, I still have very little experience with the VM side stuff, but what Dave says is precisely what I would have claimed, too: > The Windows VM is not doing anything wrong, on the contrary I think that its policy of using native MSG timestamps feels like the right thing to do. So please answer my naive question, what's wrong with #eventMillisecondClock at all, why can't we change its implementation in the Win32 VM to use GetTickCount() for it? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Dienstag, 15. September 2020 23:07:57 An: The general-purpose Squeak developers list Cc: Open Smalltalk Virtual Machine Development Discussion Betreff: Re: [squeak-dev] Time>>eventMillisecondClock and event timestamps Here's the VM event template: typedef struct sqInputEvent { sqIntptr_t type; /* type of event; either one of EventTypeXXX */ usqIntptr_t timeStamp; /* time stamp */ /* the interpretation of the following fields depend on the type of the event */ sqIntptr_t unused1; sqIntptr_t unused2; sqIntptr_t unused3; sqIntptr_t unused4; sqIntptr_t unused5; sqIntptr_t windowIndex; /* SmallInteger used in image to identify a host window structure */ } sqInputEvent; The interesting thing here is that the timeStamp is already a 64-bit quantity on 64-bit platforms. So it is trivial to use UTC microseconds divided by 1000, or (UTC microseconds minus start microseconds) divided by 1000, to timestamp events on 64-bit platforms. On 32-bit platforms we could either add an extra field at the end, or do something heinous with type. We could use 8 bits for the type and the top 24 bits to extend the 32-bit timestamp to 56 bits, which gives us 2.28 million years of milliseconds, or 2.28 thousand years of microseconds, while still leaving us 248 new event types (we use 8 at the moment). So why don't we - modify the VM to stamp events with the utc second clock, either using the full 64-bits of the timestamp on 64-bit platforms, or the 32-bit timestamp and the top 24 bits of the type field on 32-bit platforms. - add a flag to the image flags that says whether events are time stamped in utc microseconds (if set) or in wrapping milliseconds from startup (or whatever the cross-platform backward-compatiblity semantics should be) - modify primitive 94 primitiveGetNextEvent to examine the flag and either pass the event up as is, or convert it into a backards=compatible event depending on the flag copied from the image header at startup? The benefit is that we can ditch support for millisecond time stamps in all parts of the VM other than in primitive 94 primitiveGetNextEvent. On Tue, Sep 15, 2020 at 9:04 AM Eliot Miranda > wrote: Hi Marcel, Hi Dave, On Sep 15, 2020, at 8:28 AM, Marcel Taeumel > wrote:  Hi Dave. > ... but I think that we should first look at how we might handle this better in the image ... To some extent, this will be the good'ol discussion about how to achieve cross-platform compatibility. Whose job is it? VM? Image? Both? Do we want to have "platformName = 'Win32'" (or similar) checks in the image? Or can we rely on platform-independent information coming from the VM? Well, thinking about maybe the future role of FFI for Squeak, we may not want to change too much in the VM at this point. A minimal-effort approach with in-image change might be the right thing to do. :-) Agreed. My concerns are 1. The wrapping millisecond clock is a baaaaaad idea. It wraps in 49 days. The code that used to exist in Time was extremely complex and hard to maintain (fooling the system into being close to wrap around is not easy). And now we do have applications which are expected to run for long periods. But even though it is a bad idea we presumably have applications in images (someone mentioned RFB) which depend on the vm stamping events using the wrapping millisecond clock, and if possible the vm should continue to do so 2. we have a much better much simpler clock that does not wrap and can be used to compare times between images across the globe, the utc microsecond clock. If we want timestamps we should derive them from this clock 3. backwards compatibility. If we change event timestamps do we lose the ability to run 5.x images on the new vm? Is there a way that the vm can be informed by the image whether it wants timestamps? (We can add another image header flag to control the vm response) Regarding 3. I can imagine adding a 32-bit field at the end of a vm event and have having 64 bits (existing time stamp plus new field) to allow stamping the event with the utc clock (& not utc now but the utc updated by the vm heartbeat at 500Hz). Best, Marcel Am 15.09.2020 17:04:18 schrieb David T. Lewis >: I do not have any specific solution or approach in mind, but I think that we should first look at how we might handle this better in the image. The Windows VM is not doing anything wrong, on the contrary I think that its policy of using native MSG timestamps feels like the right thing to do. It seems to me that the problem is our assumption that event timestamps should match the millisecond clock in the VM. Certainly that can be made to work, but seems to me like fixing the wrong problem. Time class>>eventMillisecondClock is a recent addition. Maybe we can find another way to synthesize event time stamps in the image. Dave On Tue, Sep 15, 2020 at 04:32:59PM +0200, Marcel Taeumel wrote: > Hi all. > > Here is a quick thought: It should be possible to synthesize events from within the image to then use those synthesized events along with regular events. Think about testing, mouse over etc. Thus, either #eventMillisecondClock should be platform-specific, or all event timestamps that come from the VM should be similar ... which means that the Windows VM code needs to be changed to not use the MSG structure but ioMSecs. > > Best, > Marcel > Am 12.09.2020 21:58:50 schrieb David T. Lewis : > Thanks Tim, > > I've been digging through version control history, and I think what > I see is that the assumption of prim 94 matching prim 135 was valid > through the life of the interpreter VM (squeakvm.org SVN sources) but > that it had changed for Windows as of the public release of Cog. > > I don't see anything wrong with the Windows VM implementation (aside > from the one bug that Christoph fixed), instead I think we may just > be dealing with a holdover assumption that is no longer valid. > > I'm inclined to think we should see if we can make the prim 135 > dependence go away in the image. Some other way of synthesizing event > time stamps maybe? > > Dave > > On Fri, Sep 11, 2020 at 03:45:35PM -0700, tim Rowledge wrote: > > I think the key thing is that the answers from the primitive 135 must be aligned with the tick values obtained from primitive 94. > > > > Of course if prim 94 is properly functional (all elements of the event array are correctly filled in) then prim 135 should never be needed; it was created for those machines that waaaaay back when I added the input events still couldn't provide such data. I'm not entirely sure there actually were any.... > > > > The only usage of prim 135 not related directly to events seems to be SmalltalkImage>>#vmStatisticsReportOn:, though there are several methods that looks a bit iffy. > > > > EventSensor>>#createMouseEvent is only used in a couple of places to do with rectangle transforming interactively, and probably ought to be improved. > > EventSensor>>#nextEventSynthesized is only used if the EventSensor has no event queue and that is ... a bit complex but probably should never happen since we removed the old InputSensor years and years ago. > > EventSensor>>#primGetNextEvent: - since we declare the prim essential maybe we should drop the fake-it code and really insist on the prim being there? > > MorphicEvent>>#timeStamp looks like a probably redundant backup? > > MouseEvent>>#asMouseMove looks like the new MouseMoveEvent ought to copy the time stamp from the receiver? > > > > > > > > tim > > -- > > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > > There are no stupid questions. But, there are a lot of inquisitive idiots. > > > > > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 11:58:33 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 11:58:33 +0000 Subject: [squeak-dev] SyntaxError while loading FFI In-Reply-To: References: <902a62c617d54275a3f8ca44eb76eb01@student.hpi.uni-potsdam.de> <,> <820d12b3751241d29b78f65a4f3f967c@student.hpi.uni-potsdam.de>, Message-ID: <3e5d3488b76f4059b6aea7d836bc9487@student.hpi.uni-potsdam.de> By the way, I did not yet trace this down, but I found a couple of methods in my image that I have written myself but are annotated with the "FFI" user. Probably FFI is missing an #ensure: somewhere and forgets to reset the initials ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Mittwoch, 16. September 2020 13:56:00 An: squeak-dev Betreff: Re: [squeak-dev] SyntaxError while loading FFI Hi Christoph. > It still wants to know my author initials during installation, this is a bit confusing. Good catch! We may want to set those initials to "FFI" when auto-generating methods. Best, Marcel Am 16.09.2020 13:41:26 schrieb Thiede, Christoph : Hi Marcel, thanks for fixing this. The problem was that per some package installed earlier, my main image already contained an older version of FFI. In fact, before I read your replies to this thread, I had fixed the problem in my image by loading the new FFI-Kernel versions step by step and copying a number of missing methods manually into the image. Not very elegant, but now I can use the latest FFI version in my image. 😅 I confirm that FFI loads now in a fresh trunk image without any problems, which is great. However, you have to look carefully when you the installation appears to take more than 10 minutes and the progress bars are not changing: [cid:a4162153-2d7d-497e-a16c-450b8b1d5340] It still wants to know my author initials during installation, this is a bit confusing. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Mittwoch, 16. September 2020 09:49:25 An: squeak-dev Betreff: Re: [squeak-dev] SyntaxError while loading FFI Hi Eliot, hi all. We had the discussion about the role of Installer recently on the list. After fixing Metacello (and the script for FFI), the wizard will likely to use "Metacello new configuration: 'FFI'; load." again to manage support for different Squeak versions. Here is the discussion about the future of "Installer": http://forum.world.st/Installer-metacello-tp5115805.html Best, Marcel Am 15.09.2020 20:29:46 schrieb Eliot Miranda : Hi Marcel, Hi All, On Tue, Sep 15, 2020 at 11:20 AM Eliot Miranda > wrote: Hi Marcel, On Tue, Sep 15, 2020 at 6:27 AM Marcel Taeumel > wrote: Hi all. > In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Loading FFI via the Preference Wizard -- which uses the Installer directly -- works fine in current (fresh) trunk. No debuggers to confirm. No syntax error. That's good to know. I'll alter my VMMaker image build scripts to use this. But there's one code smell about this. PreferenceWizardMorph is a morph. i wonder if it's worth-while separating PreferenceWizardMorph into PreferenceWizardMorph and PreferenceWizard. It feels better changing the script to read PreferenceWizard new installFFI than having it rad PreferenceWizardMorph new installFFI. If you agree that this is OK, I can make the changes. Hmmm, maybe better is to move these install scripts to the Installer hierarchy. Not sure where they should live. But it would be nice to be able to say Installer installFFI and have it choose a default path, as well as be able to say Installer squeak installFFI and have it install via InstallerMonticello I am updating that Metacello script right now. Be patient. :-) Best, Marcel Am 14.09.2020 18:35:46 schrieb Thiede, Christoph >: Hi Jakob, do you have a description of the correct loading order? In my main image, I tried to load FFI via the Preference Wizard, and after some loading conflicts which I explicitly needed to #allow through the debugger, I got the same syntax error ... Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Jakob Reschke > Gesendet: Donnerstag, 10. September 2020 08:04:51 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] SyntaxError while loading FFI Hi Christoph, I had a similar issue and if I remember correctly got it resolved by loading the Tools subpackage of FFI. I think Marcel has recently split it. So if that is true, the load order in the baseline of FFI should be updated. Kind regards, Jakob Am Mi., 9. Sept. 2020 um 23:50 Uhr schrieb Thiede, Christoph >: > > Hi all, > > > I fear this could be a frequently discussed topic, but I did not know where to start else. > > > In the web, you can find the following instruction in order to load FFI at several places, amongst them the Swiki: > > > Metacello new configuration: 'FFI'; load. > > > If I do this in a fresh trunk image (#19838) without any extra installs, I get a syntax error: > > > ffiPrintString: aString > > "FFITestLibrary ffiPrintString: 'Hello'" > > "char* 'ffiPrintString' (char *) module:'SqueakFFIPrims'> > > ^self externalCallFailed > > > This is at the very least confusing, I think :-) Are there any undocumented dependencies or something like this? > > If I use the preference wizard instead, the installation succeeds. (It's not very convenient that it asks you for your initials during the installation, but this is only a small critique. :-)) > > I just wanted to inform you about that. > > > Best, > > Christoph > > -- _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 40985 bytes Desc: pastedImage.png URL: From lewis at mail.msen.com Wed Sep 16 12:13:03 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 16 Sep 2020 08:13:03 -0400 Subject: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz In-Reply-To: References: Message-ID: <20200916121303.GA13181@shell.msen.com> Hi Christoph, It seems to work just fine :-) Try putting a halt in that new eventTimeNow method. It is very rarely called, and the exact value of the timeStamp does not matter just as long as it is plausibly in sequence with other event time stamps. Dave On Wed, Sep 16, 2020 at 11:53:50AM +0000, Thiede, Christoph wrote: > Hi David, > > > how smoothly will this work if the latest send to #processEvent: is some time ago? And couldn't we run into any sort of problems with the "lastEventTime + 1" trick when making multiple fast requests to #eventTimeNow? > > > Best, > > Christoph > > > ________________________________ > Von: Squeak-dev im Auftrag von commits at source.squeak.org > Gesendet: Mittwoch, 16. September 2020 04:28:39 > An: squeak-dev at lists.squeakfoundation.org > Betreff: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz > > A new version of Kernel was added to project The Inbox: > http://source.squeak.org/inbox/Kernel-dtl.1340.mcz > > ==================== Summary ==================== > > Name: Kernel-dtl.1340 > Author: dtl > Time: 15 September 2020, 10:28:36.703545 pm > UUID: d155f790-f808-4f68-ae73-3d98434f66d0 > Ancestors: Kernel-ul.1339 > > Implement Sensor eventTimeNow for support of synthesized events. Eliminates the sometimes incorrect assumption that event time stamps must match the obsolete VM millisecond clock, and allows the Windows VM to consistently use native Windows event time stamps in the event queue. > > =============== Diff against Kernel-ul.1339 =============== > > Item was changed: > Object subclass: #EventSensor > + instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore lastEventTime' > - instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore' > classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptWatcherProcess KeyDecodeTable' > poolDictionaries: 'EventSensorConstants' > category: 'Kernel-Processes'! > > !EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0! > An EventSensor is an interface to the user input devices. > There is at least one instance of EventSensor named Sensor in the system. > > EventSensor is a replacement for the earlier InputSensor implementation based on a set of (optional) event primitives. An EventSensor updates its state when events are received so that all state based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor mouseButtons) will work exactly as before, by moving the current VM mechanisms into EventSensor itself. An optional input semaphore is part of the new design. > > For platforms that support true asynchronous event notification, the semaphore will be signaled to indicate pending events. > On platforms that do not support asynchronous notifications about events, the UI will have to poll EventSensor periodically to read events from the VM. > > Instance variables: > mouseButtons - mouse button state as replacement for primMouseButtons > mousePosition - mouse position as replacement for primMousePt > keyboardBuffer - keyboard input buffer > interruptKey - currently defined interrupt key > interruptSemaphore - the semaphore signaled when the interruptKey is detected > eventQueue - an optional event queue for event driven applications > inputSemaphore - the semaphore signaled by the VM if asynchronous event notification is supported > lastEventPoll - the last millisecondClockValue at which we called fetchMoreEvents > hasInputSemaphore - true if my inputSemaphore has actually been signaled at least once. > > Class variables: > ButtonDecodeTable - maps mouse buttons as reported by the VM to ones reported in the events. > KeyDecodeTable SmallInteger>> - maps some keys and their modifiers to other keys (used for instance to map Ctrl-X to Alt-X) > InterruptSemaphore - signalled by the the VM and/or the event loop upon receiving an interrupt keystroke. > InterruptWatcherProcess - waits on the InterruptSemaphore and then responds as appropriate. > EventPollPeriod - the number of milliseconds to wait between polling for more events in the userInterruptHandler. > EventTicklerProcess - the process that makes sure that events are polled for often enough (at least every EventPollPeriod milliseconds). > > Event format: > The current event format is very simple. Each event is recorded into an 8 element array. All events must provide some SmallInteger ID (the first field in the event buffer) and a time stamp (the second field in the event buffer), so that the difference between the time stamp of an event and the current time can be reported. > > Currently, the following events are defined: > > Null event > ============= > The Null event is returned when the ST side asks for more events but no more events are available. > Structure: > [1] - event type 0 > [2-8] - unused > > Mouse event structure > ========================== > Mouse events are generated when mouse input is detected. > [1] - event type 1 > [2] - time stamp > [3] - mouse x position > [4] - mouse y position > [5] - button state; bitfield with the following entries: > 1 - 2r001 yellow (e.g., right) button > 2 - 2r010 blue (e.g., middle) button > 4 - 2r100 red (e.g., left) button > [all other bits are currently undefined] > [6] - modifier keys; bitfield with the following entries: > 1 - shift key > 2 - ctrl key > 4 - (Mac specific) option key > 8 - Cmd/Alt key > [all other bits are currently undefined] > [7] - reserved. > [8] - host window id. > > Keyboard events > ==================== > Keyboard events are generated when keyboard input is detected. > [1] - event type 2 > [2] - time stamp > [3] - character code (Ascii) > For now the character code is in Mac Roman encoding. See #macToSqueak. > For key press/release (see [4]), character codes are normalized. > [4] - press state; integer with the following meaning > 0 - character (aka. key stroke or key still pressed) > 1 - key press (aka. key down) > 2 - key release (aka. key up) > [5] - modifier keys (same as in mouse events) > For key press/release (see [4]), modifier keys are still accessible. > [6] - character code (Unicode UTF32) > Manual decoding via KeyboardInputInterpreter possible. > For key press/release (see [4]), character codes are normalized. > [7] - reserved. > [8] - host window id. > > Mouse-wheel event structure > ========================== > Mouse-wheel events are generated when mouse-wheel input is detected. > [1] - event type 7 > [2] - time stamp > [3] - horizontal scroll delta > [4] - vertical scroll delta > [5] - button state (same as in mouse events) > [6] - modifier keys (same as in mouse events) > [7] - reserved. > [8] - host window id. > ! > > Item was changed: > ----- Method: EventSensor>>createMouseEvent (in category 'mouse') ----- > createMouseEvent > "create and return a new mouse event from the current mouse > position; this is useful for restarting normal event queue > processing after manual polling" > > | buttons modifiers pos mapped eventBuffer | > eventBuffer := Array new: 8. > buttons := self peekButtons. > pos := self peekPosition. > modifiers := buttons bitShift: -3. > buttons := buttons bitAnd: 7. > mapped := self mapButtons: buttons modifiers: modifiers. > eventBuffer > at: 1 > put: EventTypeMouse; > + at: 2 put: self eventTimeNow; > - at: 2 put: Time eventMillisecondClock; > at: 3 put: pos x; > at: 4 put: pos y; > at: 5 put: mapped; > at: 6 put: modifiers. > ^ eventBuffer! > > Item was added: > + ----- Method: EventSensor>>eventTimeNow (in category 'private') ----- > + eventTimeNow > + "Answer an event timeStamp that is slightly more recent than that of > + the most recently processed event. Intended for synthesized events to > + be processed in line with those from the real event queue." > + > + ^ lastEventTime + 1. > + ! > > Item was changed: > ----- Method: EventSensor>>peekEventSynthesized (in category 'private') ----- > peekEventSynthesized > "Return a synthesized event. This method is called if an event driven client wants to receive events but the primary user interface is not event-driven (e.g., the receiver does not have an event queue but only updates its state). This can, for instance, happen if a Morphic World is run in an MVC window. To simplify the clients work this method will always return all available keyboard events first, and then (repeatedly) the mouse events. Since mouse events come last, the client can assume that after one mouse event has been received there are no more to come. Note that it is impossible for EventSensor to determine if a mouse event has been issued before so the client must be aware of the possible problem of getting repeatedly the same mouse events. See HandMorph>>processEvents for an example on how to deal with this." > | kbd array buttons pos modifiers mapped | > "First check for keyboard" > array := Array new: 8. > keyboardBuffer isEmpty ifFalse: > ["simulate keyboard event" > array at: 1 put: EventTypeKeyboard. "evt type" > + array at: 2 put: self eventTimeNow. "time stamp" > - array at: 2 put: Time eventMillisecondClock. "time stamp" > array at: 3 put: ((kbd := keyboardBuffer peek) bitAnd: 255). "char code" > array at: 4 put: EventKeyChar. "key press/release" > array at: 5 put: (kbd bitShift: -8). "modifier keys" > ^ array]. > > "Then check for mouse" > pos := mousePosition. > buttons := mouseButtons. > modifiers := buttons bitShift: -3. > buttons := buttons bitAnd: 7. > mapped := self mapButtons: buttons modifiers: modifiers. > array > at: 1 put: EventTypeMouse; > + at: 2 put: self eventTimeNow; > - at: 2 put: Time eventMillisecondClock; > at: 3 put: pos x; > at: 4 put: pos y; > at: 5 put: mapped; > at: 6 put: modifiers. > ^ array > > ! > > Item was changed: > ----- Method: EventSensor>>primGetNextEvent: (in category 'private-I/O') ----- > primGetNextEvent: array > "Store the next OS event available into the provided array. > Essential. If the VM is not event driven the ST code will fall > back to the old-style mechanism and use the state based > primitives instead." > | kbd buttons modifiers pos mapped | > > "Simulate the events" > array at: 1 put: EventTypeNone. "assume no more events" > > "First check for keyboard" > kbd := self oldPrimKbdNext. > kbd = nil ifFalse:[ > "simulate keyboard event" > array at: 1 put: EventTypeKeyboard. "evt type" > + array at: 2 put: self eventTimeNow. "time stamp" > - array at: 2 put: Time eventMillisecondClock. "time stamp" > array at: 3 put: (kbd bitAnd: 255). "char code" > array at: 4 put: EventKeyChar. "key press/release" > array at: 5 put: (kbd bitShift: -8). "modifier keys" > ^self]. > > "Then check for mouse" > buttons := self oldPrimMouseButtons. > pos := self oldPrimMousePt. > modifiers := buttons bitShift: -3. > buttons := buttons bitAnd: 7. > mapped := self mapButtons: buttons modifiers: modifiers. > (pos = mousePosition and:[(mapped bitOr: (modifiers bitShift: 3)) = mouseButtons]) > ifTrue:[^self]. > array > at: 1 put: EventTypeMouse; > + at: 2 put: self eventTimeNow; > - at: 2 put: Time eventMillisecondClock; > at: 3 put: pos x; > at: 4 put: pos y; > at: 5 put: mapped; > at: 6 put: modifiers. > ! > > Item was changed: > ----- Method: EventSensor>>processEvent: (in category 'private-I/O') ----- > processEvent: evt > "Process a single event. This method is run at high priority." > | type buttons window | > type := evt at: 1. > + lastEventTime := evt at: 2. > > "Only process main window events, forward others to host window proxies" > window := evt at: 8. > (window isNil or: [window isZero]) ifTrue: > [window := 1. > evt at: 8 put: window]. > window = 1 ifFalse: [ > ^Smalltalk at: #HostWindowProxy ifPresent: [:w | w processEvent: evt]]. > > "Tackle mouse events and mouse wheel events first" > (type = EventTypeMouse or: [type = EventTypeMouseWheel]) > ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) + 1). > evt at: 5 put: (Smalltalk platformName = 'Mac OS' > ifTrue: [ buttons ] > ifFalse: [ self mapButtons: buttons modifiers: (evt at: 6) ]). > self queueEvent: evt. > type = EventTypeMouseWheel > ifTrue: [^ self processMouseWheelEvent: evt]. > type = EventTypeMouse > ifTrue: [^ self processMouseEvent: evt]]. > > "Store the event in the queue if there's any" > type = EventTypeKeyboard > ifTrue: [ "Check if the event is a user interrupt" > ((evt at: 4) = EventKeyChar > and: [((evt at: 3) > bitOr: (((evt at: 5) > bitAnd: 8) > bitShift: 8)) > = interruptKey]) > ifTrue: ["interrupt key is meta - not reported as event" > ^ interruptSemaphore signal]. > "Decode keys for characters (i.e., duplicate or swap, ctrl <-> alt/cmd)." > (evt at: 4) = EventKeyChar > ifTrue: [ | unicode ascii | > "Copy lookup key first in case of key swap." > unicode := {evt at: 6. evt at: 5}. > ascii := {evt at: 3. evt at: 5}. > KeyDecodeTable "Unicode character first" > at: unicode > ifPresent: [:a | evt at: 6 put: a first; > at: 5 put: a second]. > KeyDecodeTable "ASCII character second" > at: ascii > ifPresent: [:a | evt at: 3 put: a first; > at: 5 put: a second]]. > self queueEvent: evt. > self processKeyboardEvent: evt . > ^self ]. > > "Handle all events other than Keyboard or Mouse." > self queueEvent: evt. > ! > > > From leves at caesar.elte.hu Wed Sep 16 13:00:28 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Wed, 16 Sep 2020 15:00:28 +0200 (CEST) Subject: [squeak-dev] FormInspector, or also: Text>>#= and its consequences In-Reply-To: References: <10d552ed992347e0ba801c1423dada6b@student.hpi.uni-potsdam.de>, Message-ID: Hi Christoph, On Wed, 16 Sep 2020, Thiede, Christoph wrote: > > Interesting, I would not have assumed that this would be only about performance, sounded like a more profound design decision to me. If you have a look at the comment of Text >> #=, you'll find that it's a design decision (no reasoning though): = other "Am I equal to the other Text or String? ***** Warning ***** Two Texts are considered equal if they have the same characters in them. They might have completely different emphasis, fonts, sizes, text actions, or embedded morphs. If you need to find out if one is a true copy of the other, you must do (text1 = text2 and: [text1 runs = text2 runs])." Though equality with Strings is not symmetric; 'foo' asText = 'foo'. "==> true" 'foo' = 'foo' asText. "==> false" I don't know what relies on Text-String equality, but probably many things assume that Texts and Strings are somewhat interchangable (remember when you changed SHParserST80 >> #initializeVariablesFromContext, and Shout ran into errors because it expected source to be a String but got a Text?) You can't keep equality with Strings if you change #= because you'll lose transitivity: 'foo' asText allBold = 'foo'. "==> true" 'foo' asText = 'foo'. "==> true" 'foo' asText allBold = 'foo' asText "==> false" Should you decide to change #=, remember to change #hash as well, and rehash all hashed collections with text keys. Levente > > > If no one sees a problem in changing this behavior, I can try my luck. :-) > > > Best, > > Christoph > > __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Taeumel, Marcel > Gesendet: Dienstag, 15. September 2020 16:42:11 > An: squeak-dev > Betreff: Re: [squeak-dev] FormInspector, or also: Text>>#= and its consequences   > Hi Christoph. > Performance. Change it, bench it, post the results here. :-) Please specify you machine and try it on a slow RaspPi, too. > > Best, > Marcel > > Am 10.09.2020 20:32:34 schrieb Thiede, Christoph : > > Hi all, > > > is there any old thread about the design discussion of how Text>>#= works? (It does not consider attributes for quality.) Has this decision ever been questioned? > > > Naively and without an overview of any existing components that could rely on this implementation, I would like to question it. > > Why should 'foo' asText allBold be equal to 'foo' asText addAttribute: TextURL new? With the same logic, we could also say that two dictionaries are equal iff they have got the same keys ... > > > There is even a concrete client in the Trunk suffering from this design decision: Marcel's new FormInspector (and analogously, MorphInspector). It uses  > > TextFontReference with a FormSetFont to display a screenshot right in the inspector pane. Unfortunately, the pane is never updated automatically because even if the screenshot changes, the text morph thinks the old text would equal the new one. I'd like to fix that without hacking any workaround into > the inspectors. > Even though this inspector implementation is a bit unusual, in my opinion, it shows that the current Text >> #= implementation might not be a perfect solution. > > I'm looking forward to your opinions. > > Best, > Christoph > > > From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 14:41:13 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 14:41:13 +0000 Subject: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz In-Reply-To: <20200916121303.GA13181@shell.msen.com> References: , <20200916121303.GA13181@shell.msen.com> Message-ID: <422fd43a6f224c7991a65deec275f51a@student.hpi.uni-potsdam.de> Alright, then it seems fine! PS: Did you know #isThisEverCalled? That's a bit cooler than "putting a halt somewhere". ;-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Mittwoch, 16. September 2020 14:13:03 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz Hi Christoph, It seems to work just fine :-) Try putting a halt in that new eventTimeNow method. It is very rarely called, and the exact value of the timeStamp does not matter just as long as it is plausibly in sequence with other event time stamps. Dave On Wed, Sep 16, 2020 at 11:53:50AM +0000, Thiede, Christoph wrote: > Hi David, > > > how smoothly will this work if the latest send to #processEvent: is some time ago? And couldn't we run into any sort of problems with the "lastEventTime + 1" trick when making multiple fast requests to #eventTimeNow? > > > Best, > > Christoph > > > ________________________________ > Von: Squeak-dev im Auftrag von commits at source.squeak.org > Gesendet: Mittwoch, 16. September 2020 04:28:39 > An: squeak-dev at lists.squeakfoundation.org > Betreff: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz > > A new version of Kernel was added to project The Inbox: > http://source.squeak.org/inbox/Kernel-dtl.1340.mcz > > ==================== Summary ==================== > > Name: Kernel-dtl.1340 > Author: dtl > Time: 15 September 2020, 10:28:36.703545 pm > UUID: d155f790-f808-4f68-ae73-3d98434f66d0 > Ancestors: Kernel-ul.1339 > > Implement Sensor eventTimeNow for support of synthesized events. Eliminates the sometimes incorrect assumption that event time stamps must match the obsolete VM millisecond clock, and allows the Windows VM to consistently use native Windows event time stamps in the event queue. > > =============== Diff against Kernel-ul.1339 =============== > > Item was changed: > Object subclass: #EventSensor > + instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore lastEventTime' > - instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore' > classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptWatcherProcess KeyDecodeTable' > poolDictionaries: 'EventSensorConstants' > category: 'Kernel-Processes'! > > !EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0! > An EventSensor is an interface to the user input devices. > There is at least one instance of EventSensor named Sensor in the system. > > EventSensor is a replacement for the earlier InputSensor implementation based on a set of (optional) event primitives. An EventSensor updates its state when events are received so that all state based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor mouseButtons) will work exactly as before, by moving the current VM mechanisms into EventSensor itself. An optional input semaphore is part of the new design. > > For platforms that support true asynchronous event notification, the semaphore will be signaled to indicate pending events. > On platforms that do not support asynchronous notifications about events, the UI will have to poll EventSensor periodically to read events from the VM. > > Instance variables: > mouseButtons - mouse button state as replacement for primMouseButtons > mousePosition - mouse position as replacement for primMousePt > keyboardBuffer - keyboard input buffer > interruptKey - currently defined interrupt key > interruptSemaphore - the semaphore signaled when the interruptKey is detected > eventQueue - an optional event queue for event driven applications > inputSemaphore - the semaphore signaled by the VM if asynchronous event notification is supported > lastEventPoll - the last millisecondClockValue at which we called fetchMoreEvents > hasInputSemaphore - true if my inputSemaphore has actually been signaled at least once. > > Class variables: > ButtonDecodeTable - maps mouse buttons as reported by the VM to ones reported in the events. > KeyDecodeTable SmallInteger>> - maps some keys and their modifiers to other keys (used for instance to map Ctrl-X to Alt-X) > InterruptSemaphore - signalled by the the VM and/or the event loop upon receiving an interrupt keystroke. > InterruptWatcherProcess - waits on the InterruptSemaphore and then responds as appropriate. > EventPollPeriod - the number of milliseconds to wait between polling for more events in the userInterruptHandler. > EventTicklerProcess - the process that makes sure that events are polled for often enough (at least every EventPollPeriod milliseconds). > > Event format: > The current event format is very simple. Each event is recorded into an 8 element array. All events must provide some SmallInteger ID (the first field in the event buffer) and a time stamp (the second field in the event buffer), so that the difference between the time stamp of an event and the current time can be reported. > > Currently, the following events are defined: > > Null event > ============= > The Null event is returned when the ST side asks for more events but no more events are available. > Structure: > [1] - event type 0 > [2-8] - unused > > Mouse event structure > ========================== > Mouse events are generated when mouse input is detected. > [1] - event type 1 > [2] - time stamp > [3] - mouse x position > [4] - mouse y position > [5] - button state; bitfield with the following entries: > 1 - 2r001 yellow (e.g., right) button > 2 - 2r010 blue (e.g., middle) button > 4 - 2r100 red (e.g., left) button > [all other bits are currently undefined] > [6] - modifier keys; bitfield with the following entries: > 1 - shift key > 2 - ctrl key > 4 - (Mac specific) option key > 8 - Cmd/Alt key > [all other bits are currently undefined] > [7] - reserved. > [8] - host window id. > > Keyboard events > ==================== > Keyboard events are generated when keyboard input is detected. > [1] - event type 2 > [2] - time stamp > [3] - character code (Ascii) > For now the character code is in Mac Roman encoding. See #macToSqueak. > For key press/release (see [4]), character codes are normalized. > [4] - press state; integer with the following meaning > 0 - character (aka. key stroke or key still pressed) > 1 - key press (aka. key down) > 2 - key release (aka. key up) > [5] - modifier keys (same as in mouse events) > For key press/release (see [4]), modifier keys are still accessible. > [6] - character code (Unicode UTF32) > Manual decoding via KeyboardInputInterpreter possible. > For key press/release (see [4]), character codes are normalized. > [7] - reserved. > [8] - host window id. > > Mouse-wheel event structure > ========================== > Mouse-wheel events are generated when mouse-wheel input is detected. > [1] - event type 7 > [2] - time stamp > [3] - horizontal scroll delta > [4] - vertical scroll delta > [5] - button state (same as in mouse events) > [6] - modifier keys (same as in mouse events) > [7] - reserved. > [8] - host window id. > ! > > Item was changed: > ----- Method: EventSensor>>createMouseEvent (in category 'mouse') ----- > createMouseEvent > "create and return a new mouse event from the current mouse > position; this is useful for restarting normal event queue > processing after manual polling" > > | buttons modifiers pos mapped eventBuffer | > eventBuffer := Array new: 8. > buttons := self peekButtons. > pos := self peekPosition. > modifiers := buttons bitShift: -3. > buttons := buttons bitAnd: 7. > mapped := self mapButtons: buttons modifiers: modifiers. > eventBuffer > at: 1 > put: EventTypeMouse; > + at: 2 put: self eventTimeNow; > - at: 2 put: Time eventMillisecondClock; > at: 3 put: pos x; > at: 4 put: pos y; > at: 5 put: mapped; > at: 6 put: modifiers. > ^ eventBuffer! > > Item was added: > + ----- Method: EventSensor>>eventTimeNow (in category 'private') ----- > + eventTimeNow > + "Answer an event timeStamp that is slightly more recent than that of > + the most recently processed event. Intended for synthesized events to > + be processed in line with those from the real event queue." > + > + ^ lastEventTime + 1. > + ! > > Item was changed: > ----- Method: EventSensor>>peekEventSynthesized (in category 'private') ----- > peekEventSynthesized > "Return a synthesized event. This method is called if an event driven client wants to receive events but the primary user interface is not event-driven (e.g., the receiver does not have an event queue but only updates its state). This can, for instance, happen if a Morphic World is run in an MVC window. To simplify the clients work this method will always return all available keyboard events first, and then (repeatedly) the mouse events. Since mouse events come last, the client can assume that after one mouse event has been received there are no more to come. Note that it is impossible for EventSensor to determine if a mouse event has been issued before so the client must be aware of the possible problem of getting repeatedly the same mouse events. See HandMorph>>processEvents for an example on how to deal with this." > | kbd array buttons pos modifiers mapped | > "First check for keyboard" > array := Array new: 8. > keyboardBuffer isEmpty ifFalse: > ["simulate keyboard event" > array at: 1 put: EventTypeKeyboard. "evt type" > + array at: 2 put: self eventTimeNow. "time stamp" > - array at: 2 put: Time eventMillisecondClock. "time stamp" > array at: 3 put: ((kbd := keyboardBuffer peek) bitAnd: 255). "char code" > array at: 4 put: EventKeyChar. "key press/release" > array at: 5 put: (kbd bitShift: -8). "modifier keys" > ^ array]. > > "Then check for mouse" > pos := mousePosition. > buttons := mouseButtons. > modifiers := buttons bitShift: -3. > buttons := buttons bitAnd: 7. > mapped := self mapButtons: buttons modifiers: modifiers. > array > at: 1 put: EventTypeMouse; > + at: 2 put: self eventTimeNow; > - at: 2 put: Time eventMillisecondClock; > at: 3 put: pos x; > at: 4 put: pos y; > at: 5 put: mapped; > at: 6 put: modifiers. > ^ array > > ! > > Item was changed: > ----- Method: EventSensor>>primGetNextEvent: (in category 'private-I/O') ----- > primGetNextEvent: array > "Store the next OS event available into the provided array. > Essential. If the VM is not event driven the ST code will fall > back to the old-style mechanism and use the state based > primitives instead." > | kbd buttons modifiers pos mapped | > > "Simulate the events" > array at: 1 put: EventTypeNone. "assume no more events" > > "First check for keyboard" > kbd := self oldPrimKbdNext. > kbd = nil ifFalse:[ > "simulate keyboard event" > array at: 1 put: EventTypeKeyboard. "evt type" > + array at: 2 put: self eventTimeNow. "time stamp" > - array at: 2 put: Time eventMillisecondClock. "time stamp" > array at: 3 put: (kbd bitAnd: 255). "char code" > array at: 4 put: EventKeyChar. "key press/release" > array at: 5 put: (kbd bitShift: -8). "modifier keys" > ^self]. > > "Then check for mouse" > buttons := self oldPrimMouseButtons. > pos := self oldPrimMousePt. > modifiers := buttons bitShift: -3. > buttons := buttons bitAnd: 7. > mapped := self mapButtons: buttons modifiers: modifiers. > (pos = mousePosition and:[(mapped bitOr: (modifiers bitShift: 3)) = mouseButtons]) > ifTrue:[^self]. > array > at: 1 put: EventTypeMouse; > + at: 2 put: self eventTimeNow; > - at: 2 put: Time eventMillisecondClock; > at: 3 put: pos x; > at: 4 put: pos y; > at: 5 put: mapped; > at: 6 put: modifiers. > ! > > Item was changed: > ----- Method: EventSensor>>processEvent: (in category 'private-I/O') ----- > processEvent: evt > "Process a single event. This method is run at high priority." > | type buttons window | > type := evt at: 1. > + lastEventTime := evt at: 2. > > "Only process main window events, forward others to host window proxies" > window := evt at: 8. > (window isNil or: [window isZero]) ifTrue: > [window := 1. > evt at: 8 put: window]. > window = 1 ifFalse: [ > ^Smalltalk at: #HostWindowProxy ifPresent: [:w | w processEvent: evt]]. > > "Tackle mouse events and mouse wheel events first" > (type = EventTypeMouse or: [type = EventTypeMouseWheel]) > ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) + 1). > evt at: 5 put: (Smalltalk platformName = 'Mac OS' > ifTrue: [ buttons ] > ifFalse: [ self mapButtons: buttons modifiers: (evt at: 6) ]). > self queueEvent: evt. > type = EventTypeMouseWheel > ifTrue: [^ self processMouseWheelEvent: evt]. > type = EventTypeMouse > ifTrue: [^ self processMouseEvent: evt]]. > > "Store the event in the queue if there's any" > type = EventTypeKeyboard > ifTrue: [ "Check if the event is a user interrupt" > ((evt at: 4) = EventKeyChar > and: [((evt at: 3) > bitOr: (((evt at: 5) > bitAnd: 8) > bitShift: 8)) > = interruptKey]) > ifTrue: ["interrupt key is meta - not reported as event" > ^ interruptSemaphore signal]. > "Decode keys for characters (i.e., duplicate or swap, ctrl <-> alt/cmd)." > (evt at: 4) = EventKeyChar > ifTrue: [ | unicode ascii | > "Copy lookup key first in case of key swap." > unicode := {evt at: 6. evt at: 5}. > ascii := {evt at: 3. evt at: 5}. > KeyDecodeTable "Unicode character first" > at: unicode > ifPresent: [:a | evt at: 6 put: a first; > at: 5 put: a second]. > KeyDecodeTable "ASCII character second" > at: ascii > ifPresent: [:a | evt at: 3 put: a first; > at: 5 put: a second]]. > self queueEvent: evt. > self processKeyboardEvent: evt . > ^self ]. > > "Handle all events other than Keyboard or Mouse." > self queueEvent: evt. > ! > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 14:43:59 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 14:43:59 +0000 Subject: [squeak-dev] FormInspector, or also: Text>>#= and its consequences In-Reply-To: References: <10d552ed992347e0ba801c1423dada6b@student.hpi.uni-potsdam.de>, , Message-ID: <5c2348d9b5d248ea81e4269bae13c846@student.hpi.uni-potsdam.de> Hi Levente, hm, I think #= should be always commutative and transitive, everything else is at least confusing ... Can't we move that "attribute invariant" comparison rather to something like Text >> #sameAs:? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Mittwoch, 16. September 2020 15:00:28 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] FormInspector, or also: Text>>#= and its consequences Hi Christoph, On Wed, 16 Sep 2020, Thiede, Christoph wrote: > > Interesting, I would not have assumed that this would be only about performance, sounded like a more profound design decision to me. If you have a look at the comment of Text >> #=, you'll find that it's a design decision (no reasoning though): = other "Am I equal to the other Text or String? ***** Warning ***** Two Texts are considered equal if they have the same characters in them. They might have completely different emphasis, fonts, sizes, text actions, or embedded morphs. If you need to find out if one is a true copy of the other, you must do (text1 = text2 and: [text1 runs = text2 runs])." Though equality with Strings is not symmetric; 'foo' asText = 'foo'. "==> true" 'foo' = 'foo' asText. "==> false" I don't know what relies on Text-String equality, but probably many things assume that Texts and Strings are somewhat interchangable (remember when you changed SHParserST80 >> #initializeVariablesFromContext, and Shout ran into errors because it expected source to be a String but got a Text?) You can't keep equality with Strings if you change #= because you'll lose transitivity: 'foo' asText allBold = 'foo'. "==> true" 'foo' asText = 'foo'. "==> true" 'foo' asText allBold = 'foo' asText "==> false" Should you decide to change #=, remember to change #hash as well, and rehash all hashed collections with text keys. Levente > > > If no one sees a problem in changing this behavior, I can try my luck. :-) > > > Best, > > Christoph > > __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Taeumel, Marcel > Gesendet: Dienstag, 15. September 2020 16:42:11 > An: squeak-dev > Betreff: Re: [squeak-dev] FormInspector, or also: Text>>#= and its consequences > Hi Christoph. > Performance. Change it, bench it, post the results here. :-) Please specify you machine and try it on a slow RaspPi, too. > > Best, > Marcel > > Am 10.09.2020 20:32:34 schrieb Thiede, Christoph : > > Hi all, > > > is there any old thread about the design discussion of how Text>>#= works? (It does not consider attributes for quality.) Has this decision ever been questioned? > > > Naively and without an overview of any existing components that could rely on this implementation, I would like to question it. > > Why should 'foo' asText allBold be equal to 'foo' asText addAttribute: TextURL new? With the same logic, we could also say that two dictionaries are equal iff they have got the same keys ... > > > There is even a concrete client in the Trunk suffering from this design decision: Marcel's new FormInspector (and analogously, MorphInspector). It uses > > TextFontReference with a FormSetFont to display a screenshot right in the inspector pane. Unfortunately, the pane is never updated automatically because even if the screenshot changes, the text morph thinks the old text would equal the new one. I'd like to fix that without hacking any workaround into > the inspectors. > Even though this inspector implementation is a bit unusual, in my opinion, it shows that the current Text >> #= implementation might not be a perfect solution. > > I'm looking forward to your opinions. > > Best, > Christoph > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Wed Sep 16 14:45:23 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 16 Sep 2020 10:45:23 -0400 Subject: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz In-Reply-To: <422fd43a6f224c7991a65deec275f51a@student.hpi.uni-potsdam.de> References: <20200916121303.GA13181@shell.msen.com> <422fd43a6f224c7991a65deec275f51a@student.hpi.uni-potsdam.de> Message-ID: <20200916144523.GA40905@shell.msen.com> On Wed, Sep 16, 2020 at 02:41:13PM +0000, Thiede, Christoph wrote: > Alright, then it seems fine! > > > PS: Did you know #isThisEverCalled? That's a bit cooler than "putting a halt somewhere". ;-) > > Actually no, I did not know about that. Thanks for the tip :-) Amazing it's been in the image since day one and I never noticed. Dave From tonyg at leastfixedpoint.com Wed Sep 16 15:04:33 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Wed, 16 Sep 2020 17:04:33 +0200 Subject: [squeak-dev] FFI type for 'char[12]'? In-Reply-To: References: Message-ID: <35fb09ea-952b-701a-38dc-b2d4d8a03085@leastfixedpoint.com> Thanks, Tom and Marcel. On 9/16/20 10:11 AM, Beckmann, Tom wrote: > for ExternalStructure fields definitions I worked around this by exploding the array, as in > #((str1 'char') (str2 'char') (str3 'char') (str4 'char') (str5 'char')) Yes, this occurred to me but I ended up not doing it :-P > I also have an experimental set of changes that adds an ExternalArrayType to support this, however it still has some untested corner cases. I'll investigate getting it merged to FFI soon. The ExternalArrayType idea sounds good! Looking forward to it. For now I've just parsed the structure by hand out of a ByteArray, which works well enough until ExternalArrayType or similar lands. Regards, Tony From eliot.miranda at gmail.com Wed Sep 16 15:08:24 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 16 Sep 2020 08:08:24 -0700 Subject: [squeak-dev] FFI type for 'char[12]'? In-Reply-To: References: Message-ID: Hi Tom, On Wed, Sep 16, 2020 at 1:12 AM Beckmann, Tom < Tom.Beckmann at student.hpi.uni-potsdam.de> wrote: > Hi, > > for ExternalStructure fields definitions I worked around this by exploding > the array, as in > #((str1 'char') (str2 'char') (str3 'char') (str4 'char') (str5 'char')) > > The same works for other ExternalTypes like Foo of course. > > I also have an experimental set of changes that adds an ExternalArrayType > to support this, however it still has some untested corner cases. I'll > investigate getting it merged to FFI soon. > It is much better to have a new facility "out in the wild" available to look at b others and test than it is to sit on something until it is perfect. Please do release it asap. Then we can all collaborate on writing tests for it (we would need tests in the FFI test suite), reviewing the code, etc. > Best, > Tom > ________________________________________ > From: Squeak-dev on > behalf of Taeumel, Marcel > Sent: Wednesday, September 16, 2020 9:58:45 AM > To: squeak-dev > Subject: Re: [squeak-dev] FFI type for 'char[12]'? > > Hi Tony, > > such container types are still work-in-progress. :-/ For char[12], you can > either use "string" or "char*", I suppose. "Foo x[5]" can be expressed > through ExternalData, too, which would be "Foo*". > > Best, > Marcel > > Am 16.09.2020 09:49:20 schrieb Tony Garnock-Jones < > tonyg at leastfixedpoint.com>: > > Hi all, > > Is there some way in the FFI to specify that an ExternalStructure has a > member whose type is like 'char x[12]'? > > Similarly, given some ExternalStructure Foo, can I have another > structure that has a member like 'Foo x[5]'? > > Tony > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Wed Sep 16 15:21:27 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 16 Sep 2020 08:21:27 -0700 Subject: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz In-Reply-To: <422fd43a6f224c7991a65deec275f51a@student.hpi.uni-potsdam.de> References: <20200916121303.GA13181@shell.msen.com> <422fd43a6f224c7991a65deec275f51a@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Wed, Sep 16, 2020 at 7:41 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Alright, then it seems fine! > > > PS: Did you know #isThisEverCalled? That's a bit cooler than "putting a > halt somewhere". ;-) > What would be cooler than the current implementation is something that maintains a pair of dates in the properties of a method that sends isThisEverCalled. If the method's properties are empty then isThisEverCalled would (silently) update the properties with the "start date". Subsequent calls would set the "end date" to the current time. We could then browse for senders of isThisEverCalled and see when exectly was the method called. No propertiers => it wasn't called. properties with an end date long in the past => not for a while. The problem with an implementation based on halt is that one can only use it for code that can safely be halted. > > Best, > > Christoph > ------------------------------ > *Von:* Squeak-dev im > Auftrag von David T. Lewis > *Gesendet:* Mittwoch, 16. September 2020 14:13:03 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz > > Hi Christoph, > > It seems to work just fine :-) > > Try putting a halt in that new eventTimeNow method. It is very rarely > called, and the exact value of the timeStamp does not matter just as > long as it is plausibly in sequence with other event time stamps. > > Dave > > > > On Wed, Sep 16, 2020 at 11:53:50AM +0000, Thiede, Christoph wrote: > > Hi David, > > > > > > how smoothly will this work if the latest send to #processEvent: is some > time ago? And couldn't we run into any sort of problems with the > "lastEventTime + 1" trick when making multiple fast requests to > #eventTimeNow? > > > > > > Best, > > > > Christoph > > > > > > ________________________________ > > Von: Squeak-dev im > Auftrag von commits at source.squeak.org > > Gesendet: Mittwoch, 16. September 2020 04:28:39 > > An: squeak-dev at lists.squeakfoundation.org > > Betreff: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz > > > > A new version of Kernel was added to project The Inbox: > > http://source.squeak.org/inbox/Kernel-dtl.1340.mcz > > > > ==================== Summary ==================== > > > > Name: Kernel-dtl.1340 > > Author: dtl > > Time: 15 September 2020, 10:28:36.703545 pm > > UUID: d155f790-f808-4f68-ae73-3d98434f66d0 > > Ancestors: Kernel-ul.1339 > > > > Implement Sensor eventTimeNow for support of synthesized events. > Eliminates the sometimes incorrect assumption that event time stamps must > match the obsolete VM millisecond clock, and allows the Windows VM to > consistently use native Windows event time stamps in the event queue. > > > > =============== Diff against Kernel-ul.1339 =============== > > > > Item was changed: > > Object subclass: #EventSensor > > + instanceVariableNames: 'mouseButtons mousePosition > mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue > inputSemaphore lastEventPoll hasInputSemaphore lastEventTime' > > - instanceVariableNames: 'mouseButtons mousePosition > mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue > inputSemaphore lastEventPoll hasInputSemaphore' > > classVariableNames: 'ButtonDecodeTable EventPollPeriod > EventTicklerProcess InterruptWatcherProcess KeyDecodeTable' > > poolDictionaries: 'EventSensorConstants' > > category: 'Kernel-Processes'! > > > > !EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0! > > An EventSensor is an interface to the user input devices. > > There is at least one instance of EventSensor named Sensor in the > system. > > > > EventSensor is a replacement for the earlier InputSensor > implementation based on a set of (optional) event primitives. An > EventSensor updates its state when events are received so that all state > based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor > mouseButtons) will work exactly as before, by moving the current VM > mechanisms into EventSensor itself. An optional input semaphore is part of > the new design. > > > > For platforms that support true asynchronous event notification, the > semaphore will be signaled to indicate pending events. > > On platforms that do not support asynchronous notifications about > events, the UI will have to poll EventSensor periodically to read events > from the VM. > > > > Instance variables: > > mouseButtons - mouse button state as replacement for > primMouseButtons > > mousePosition - mouse position as replacement for > primMousePt > > keyboardBuffer - keyboard input buffer > > interruptKey - currently defined > interrupt key > > interruptSemaphore - the semaphore signaled when > the interruptKey is detected > > eventQueue - an optional event queue for > event driven applications > > inputSemaphore - the semaphore signaled by the VM if > asynchronous event notification is supported > > lastEventPoll - the last > millisecondClockValue at which we called fetchMoreEvents > > hasInputSemaphore - true if my inputSemaphore has > actually been signaled at least once. > > > > Class variables: > > ButtonDecodeTable - maps mouse buttons as reported > by the VM to ones reported in the events. > > KeyDecodeTable SmallInteger>> - maps > some keys and their modifiers to other keys (used for instance to map > Ctrl-X to Alt-X) > > InterruptSemaphore - signalled by the the VM and/or > the event loop upon receiving an interrupt keystroke. > > InterruptWatcherProcess - waits on the > InterruptSemaphore and then responds as appropriate. > > EventPollPeriod - the number of milliseconds to > wait between polling for more events in the userInterruptHandler. > > EventTicklerProcess - the process that makes sure > that events are polled for often enough (at least every EventPollPeriod > milliseconds). > > > > Event format: > > The current event format is very simple. Each event is recorded into > an 8 element array. All events must provide some SmallInteger ID (the first > field in the event buffer) and a time stamp (the second field in the event > buffer), so that the difference between the time stamp of an event and the > current time can be reported. > > > > Currently, the following events are defined: > > > > Null event > > ============= > > The Null event is returned when the ST side asks for more events but > no more events are available. > > Structure: > > [1] - event type 0 > > [2-8] - unused > > > > Mouse event structure > > ========================== > > Mouse events are generated when mouse input is detected. > > [1] - event type 1 > > [2] - time stamp > > [3] - mouse x position > > [4] - mouse y position > > [5] - button state; bitfield with the following entries: > > 1 - 2r001 yellow (e.g., right) button > > 2 - 2r010 blue (e.g., middle) button > > 4 - 2r100 red (e.g., left) button > > [all other bits are currently undefined] > > [6] - modifier keys; bitfield with the following entries: > > 1 - shift key > > 2 - ctrl key > > 4 - (Mac specific) option key > > 8 - Cmd/Alt key > > [all other bits are currently undefined] > > [7] - reserved. > > [8] - host window id. > > > > Keyboard events > > ==================== > > Keyboard events are generated when keyboard input is detected. > > [1] - event type 2 > > [2] - time stamp > > [3] - character code (Ascii) > > For now the character code is in Mac Roman encoding. > See #macToSqueak. > > For key press/release (see [4]), character codes are > normalized. > > [4] - press state; integer with the following meaning > > 0 - character (aka. key stroke or key still > pressed) > > 1 - key press (aka. key down) > > 2 - key release (aka. key up) > > [5] - modifier keys (same as in mouse events) > > For key press/release (see [4]), modifier keys are > still accessible. > > [6] - character code (Unicode UTF32) > > Manual decoding via KeyboardInputInterpreter possible. > > For key press/release (see [4]), character codes are > normalized. > > [7] - reserved. > > [8] - host window id. > > > > Mouse-wheel event structure > > ========================== > > Mouse-wheel events are generated when mouse-wheel input is detected. > > [1] - event type 7 > > [2] - time stamp > > [3] - horizontal scroll delta > > [4] - vertical scroll delta > > [5] - button state (same as in mouse events) > > [6] - modifier keys (same as in mouse events) > > [7] - reserved. > > [8] - host window id. > > ! > > > > Item was changed: > > ----- Method: EventSensor>>createMouseEvent (in category 'mouse') ----- > > createMouseEvent > > "create and return a new mouse event from the current mouse > > position; this is useful for restarting normal event queue > > processing after manual polling" > > > > | buttons modifiers pos mapped eventBuffer | > > eventBuffer := Array new: 8. > > buttons := self peekButtons. > > pos := self peekPosition. > > modifiers := buttons bitShift: -3. > > buttons := buttons bitAnd: 7. > > mapped := self mapButtons: buttons modifiers: modifiers. > > eventBuffer > > at: 1 > > put: EventTypeMouse; > > + at: 2 put: self eventTimeNow; > > - at: 2 put: Time eventMillisecondClock; > > at: 3 put: pos x; > > at: 4 put: pos y; > > at: 5 put: mapped; > > at: 6 put: modifiers. > > ^ eventBuffer! > > > > Item was added: > > + ----- Method: EventSensor>>eventTimeNow (in category 'private') ----- > > + eventTimeNow > > + "Answer an event timeStamp that is slightly more recent than > that of > > + the most recently processed event. Intended for synthesized > events to > > + be processed in line with those from the real event queue." > > + > > + ^ lastEventTime + 1. > > + ! > > > > Item was changed: > > ----- Method: EventSensor>>peekEventSynthesized (in category > 'private') ----- > > peekEventSynthesized > > "Return a synthesized event. This method is called if an event > driven client wants to receive events but the primary user interface is not > event-driven (e.g., the receiver does not have an event queue but only > updates its state). This can, for instance, happen if a Morphic World is > run in an MVC window. To simplify the clients work this method will always > return all available keyboard events first, and then (repeatedly) the mouse > events. Since mouse events come last, the client can assume that after one > mouse event has been received there are no more to come. Note that it is > impossible for EventSensor to determine if a mouse event has been issued > before so the client must be aware of the possible problem of getting > repeatedly the same mouse events. See HandMorph>>processEvents for an > example on how to deal with this." > > | kbd array buttons pos modifiers mapped | > > "First check for keyboard" > > array := Array new: 8. > > keyboardBuffer isEmpty ifFalse: > > ["simulate keyboard event" > > array at: 1 put: EventTypeKeyboard. "evt type" > > + array at: 2 put: self eventTimeNow. "time stamp" > > - array at: 2 put: Time eventMillisecondClock. "time > stamp" > > array at: 3 put: ((kbd := keyboardBuffer peek) bitAnd: > 255). "char code" > > array at: 4 put: EventKeyChar. "key press/release" > > array at: 5 put: (kbd bitShift: -8). "modifier keys" > > ^ array]. > > > > "Then check for mouse" > > pos := mousePosition. > > buttons := mouseButtons. > > modifiers := buttons bitShift: -3. > > buttons := buttons bitAnd: 7. > > mapped := self mapButtons: buttons modifiers: modifiers. > > array > > at: 1 put: EventTypeMouse; > > + at: 2 put: self eventTimeNow; > > - at: 2 put: Time eventMillisecondClock; > > at: 3 put: pos x; > > at: 4 put: pos y; > > at: 5 put: mapped; > > at: 6 put: modifiers. > > ^ array > > > > ! > > > > Item was changed: > > ----- Method: EventSensor>>primGetNextEvent: (in category > 'private-I/O') ----- > > primGetNextEvent: array > > "Store the next OS event available into the provided array. > > Essential. If the VM is not event driven the ST code will fall > > back to the old-style mechanism and use the state based > > primitives instead." > > | kbd buttons modifiers pos mapped | > > > > "Simulate the events" > > array at: 1 put: EventTypeNone. "assume no more events" > > > > "First check for keyboard" > > kbd := self oldPrimKbdNext. > > kbd = nil ifFalse:[ > > "simulate keyboard event" > > array at: 1 put: EventTypeKeyboard. "evt type" > > + array at: 2 put: self eventTimeNow. "time stamp" > > - array at: 2 put: Time eventMillisecondClock. "time > stamp" > > array at: 3 put: (kbd bitAnd: 255). "char code" > > array at: 4 put: EventKeyChar. "key press/release" > > array at: 5 put: (kbd bitShift: -8). "modifier keys" > > ^self]. > > > > "Then check for mouse" > > buttons := self oldPrimMouseButtons. > > pos := self oldPrimMousePt. > > modifiers := buttons bitShift: -3. > > buttons := buttons bitAnd: 7. > > mapped := self mapButtons: buttons modifiers: modifiers. > > (pos = mousePosition and:[(mapped bitOr: (modifiers bitShift: > 3)) = mouseButtons]) > > ifTrue:[^self]. > > array > > at: 1 put: EventTypeMouse; > > + at: 2 put: self eventTimeNow; > > - at: 2 put: Time eventMillisecondClock; > > at: 3 put: pos x; > > at: 4 put: pos y; > > at: 5 put: mapped; > > at: 6 put: modifiers. > > ! > > > > Item was changed: > > ----- Method: EventSensor>>processEvent: (in category 'private-I/O') > ----- > > processEvent: evt > > "Process a single event. This method is run at high priority." > > | type buttons window | > > type := evt at: 1. > > + lastEventTime := evt at: 2. > > > > "Only process main window events, forward others to host window > proxies" > > window := evt at: 8. > > (window isNil or: [window isZero]) ifTrue: > > [window := 1. > > evt at: 8 put: window]. > > window = 1 ifFalse: [ > > ^Smalltalk at: #HostWindowProxy ifPresent: [:w | w > processEvent: evt]]. > > > > "Tackle mouse events and mouse wheel events first" > > (type = EventTypeMouse or: [type = EventTypeMouseWheel]) > > ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) > + 1). > > evt at: 5 put: (Smalltalk platformName > = 'Mac OS' > > ifTrue: [ > buttons ] > > ifFalse: [ self > mapButtons: buttons modifiers: (evt at: 6) ]). > > self queueEvent: evt. > > type = EventTypeMouseWheel > > ifTrue: [^ self > processMouseWheelEvent: evt]. > > type = EventTypeMouse > > ifTrue: [^ self > processMouseEvent: evt]]. > > > > "Store the event in the queue if there's any" > > type = EventTypeKeyboard > > ifTrue: [ "Check if the event is a user interrupt" > > ((evt at: 4) = EventKeyChar > > and: [((evt at: 3) > > bitOr: (((evt at: 5) > > bitAnd: 8) > > bitShift: 8)) > > = interruptKey]) > > ifTrue: ["interrupt key is meta > - not reported as event" > > ^ > interruptSemaphore signal]. > > "Decode keys for characters (i.e., duplicate or > swap, ctrl <-> alt/cmd)." > > (evt at: 4) = EventKeyChar > > ifTrue: [ | unicode ascii | > > "Copy lookup key first in case > of key swap." > > unicode := {evt at: 6. evt at: > 5}. > > ascii := {evt at: 3. evt at: 5}. > > KeyDecodeTable "Unicode > character first" > > at: unicode > > ifPresent: [:a | evt > at: 6 put: a first; > > at: 5 > put: a second]. > > KeyDecodeTable "ASCII character > second" > > at: ascii > > ifPresent: [:a | evt > at: 3 put: a first; > > at: 5 > put: a second]]. > > self queueEvent: evt. > > self processKeyboardEvent: evt . > > ^self ]. > > > > "Handle all events other than Keyboard or Mouse." > > self queueEvent: evt. > > ! > > > > > > > > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From christoph.thiede at student.hpi.uni-potsdam.de Wed Sep 16 16:08:33 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Wed, 16 Sep 2020 11:08:33 -0500 (CDT) Subject: [squeak-dev] The Trunk: KernelTests-ul.386.mcz In-Reply-To: References: Message-ID: <1600272513365-0.post@n4.nabble.com> Ouch, this one conflicts with http://forum.world.st/The-Inbox-KernelTests-ct-375-mcz-td5110760.html! :-) -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From karlramberg at gmail.com Wed Sep 16 16:54:09 2020 From: karlramberg at gmail.com (karl ramberg) Date: Wed, 16 Sep 2020 18:54:09 +0200 Subject: [squeak-dev] The Inbox: Kernel-kfr.1339.mcz In-Reply-To: References: Message-ID: I tested this change. I get a DNU for 'bindingOf: ifAbsent:' because 'self environment' evaluates to Smalltalk here. Best, Karl On Wed, Sep 16, 2020 at 12:01 AM Eliot Miranda wrote: > Hi Karl, > > On Mon, Sep 14, 2020 at 10:48 PM wrote: > >> A new version of Kernel was added to project The Inbox: >> http://source.squeak.org/inbox/Kernel-kfr.1339.mcz >> >> ==================== Summary ==================== >> >> Name: Kernel-kfr.1339 >> Author: kfr >> Time: 15 September 2020, 7:48:44.907977 am >> UUID: 9e1761d0-c2a1-b74a-bfe7-90dd49e320f1 >> Ancestors: Kernel-ct.1338 >> >> Fix a deprecation warning >> >> =============== Diff against Kernel-ct.1338 =============== >> >> Item was changed: >> ----- Method: Class>>binding (in category 'compiling') ----- >> binding >> "Answer a binding for the receiver, sharing if possible" >> | binding | >> + binding := Smalltalk globals associationAt: name ifAbsent: [nil >> -> self]. >> - binding := self environment associationAt: name ifAbsent: [nil -> >> self]. >> ^binding value == self ifTrue:[binding] ifFalse:[nil -> self].! >> > > See Environment>>associationAt: aSymbol ifAbsent: aBlock > "Senders of this should probably be using #bindingOf:" > self flag: #review. > ^ declarations associationAt: aSymbol ifAbsent: aBlock > > So let me suggest > > Class>>binding > "Answer a binding for the receiver, sharing if possible" > (self environment bindingOf: name ifAbsent: nil) ifNotNil: > [:bindingOrNil| > bindingOrNil value == self ifTrue: > [^bindingOrNil]]. > ^ClassBinding key: nil value: self > > and then gradually all those anonymous Associations in class methods will > disappear :-) > > _,,,^..^,,,_ > best, Eliot > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Wed Sep 16 16:57:11 2020 From: karlramberg at gmail.com (karl ramberg) Date: Wed, 16 Sep 2020 18:57:11 +0200 Subject: [squeak-dev] The Inbox: Kernel-kfr.1339.mcz In-Reply-To: References: Message-ID: This works: Class>>binding "Answer a binding for the receiver, sharing if possible" (Smalltalk globals bindingOf: name ifAbsent: nil) ifNotNil: [:bindingOrNil| bindingOrNil value == self ifTrue: [^bindingOrNil]]. ^ClassBinding key: nil value: self On Wed, Sep 16, 2020 at 6:54 PM karl ramberg wrote: > I tested this change. > I get a DNU for 'bindingOf: ifAbsent:' because 'self environment' > evaluates to Smalltalk here. > > Best, > Karl > > > > On Wed, Sep 16, 2020 at 12:01 AM Eliot Miranda > wrote: > >> Hi Karl, >> >> On Mon, Sep 14, 2020 at 10:48 PM wrote: >> >>> A new version of Kernel was added to project The Inbox: >>> http://source.squeak.org/inbox/Kernel-kfr.1339.mcz >>> >>> ==================== Summary ==================== >>> >>> Name: Kernel-kfr.1339 >>> Author: kfr >>> Time: 15 September 2020, 7:48:44.907977 am >>> UUID: 9e1761d0-c2a1-b74a-bfe7-90dd49e320f1 >>> Ancestors: Kernel-ct.1338 >>> >>> Fix a deprecation warning >>> >>> =============== Diff against Kernel-ct.1338 =============== >>> >>> Item was changed: >>> ----- Method: Class>>binding (in category 'compiling') ----- >>> binding >>> "Answer a binding for the receiver, sharing if possible" >>> | binding | >>> + binding := Smalltalk globals associationAt: name ifAbsent: [nil >>> -> self]. >>> - binding := self environment associationAt: name ifAbsent: [nil >>> -> self]. >>> ^binding value == self ifTrue:[binding] ifFalse:[nil -> self].! >>> >> >> See Environment>>associationAt: aSymbol ifAbsent: aBlock >> "Senders of this should probably be using #bindingOf:" >> self flag: #review. >> ^ declarations associationAt: aSymbol ifAbsent: aBlock >> >> So let me suggest >> >> Class>>binding >> "Answer a binding for the receiver, sharing if possible" >> (self environment bindingOf: name ifAbsent: nil) ifNotNil: >> [:bindingOrNil| >> bindingOrNil value == self ifTrue: >> [^bindingOrNil]]. >> ^ClassBinding key: nil value: self >> >> and then gradually all those anonymous Associations in class methods will >> disappear :-) >> >> _,,,^..^,,,_ >> best, Eliot >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From tim at rowledge.org Wed Sep 16 17:12:16 2020 From: tim at rowledge.org (tim Rowledge) Date: Wed, 16 Sep 2020 10:12:16 -0700 Subject: [squeak-dev] The Inbox: Kernel-dtl.1340.mcz In-Reply-To: References: <20200916121303.GA13181@shell.msen.com> <422fd43a6f224c7991a65deec275f51a@student.hpi.uni-potsdam.de> Message-ID: > On 2020-09-16, at 8:21 AM, Eliot Miranda wrote: > > Hi Christoph, > > On Wed, Sep 16, 2020 at 7:41 AM Thiede, Christoph wrote: > Alright, then it seems fine! > > > > PS: Did you know #isThisEverCalled? That's a bit cooler than "putting a halt somewhere". ;-) > > > What would be cooler than the current implementation is something that maintains a pair of dates in the properties of a method that sends isThisEverCalled. If the method's properties are empty then isThisEverCalled would (silently) update the properties with the "start date". Subsequent calls would set the "end date" to the current time. We could then browse for senders of isThisEverCalled and see when exectly was the method called. No propertiers => it wasn't called. properties with an end date long in the past => not for a while. The problem with an implementation based on halt is that one can only use it for code that can safely be halted. Ooh, sneaky. Could also hook to a logger that writes to non-image output (file/stdio/mqtt/etc) to get live updates. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Liability: a valuable political skill From tim at rowledge.org Wed Sep 16 17:36:00 2020 From: tim at rowledge.org (tim Rowledge) Date: Wed, 16 Sep 2020 10:36:00 -0700 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: <81AD7450-676E-4C78-BA29-2E84BD97A539@gmail.com> Message-ID: > On 2020-09-15, at 2:07 PM, Eliot Miranda wrote: > > > > So why don't we > - modify the VM to stamp events with the utc second clock, The one possible problem I can foresee is if any OS event stuff we try to talk to expects timestamps consistent with its own view; imagine getting some event that needs to be replied to with everything the same except for one field updated etc. If we've stuck our own timestamp in then it could bollix things up. No idea if such events actually exist outside the realm of ancient, faintly remembered, RISC OS cases. *IF* this seems to have any plausibility, then we could stick with using the OS timestamp for the incoming events, but derive our prim 135 tick value from a) catching the first event, reading its timestamp, deriving an offset from our uSec tick, saving that and then doing the math anytime someone wants a prim 135 value, or b) finding out which OS time api is being used by the OS to stamp events; it really should be documented. Of course, we have plenty of experience of OS doc being... imprecise. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: PIC: Permute Instruction Codes From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 16 18:47:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 16 Sep 2020 18:47:19 +0000 Subject: [squeak-dev] Deferred UI messages in MVC can be deferred for a really long time Message-ID: <68a5b6d9a3584c48aa9845062170d060@student.hpi.uni-potsdam.de> Hi all! Quick experiment: Open an MVC project, open a Workspace in it, and do it: Project current addDeferredUIMessage: [self inform: #foo] In Morphic, this produces a dialog message "nearly immediately". But in MVC, apparently, I can sit there for hours and wait for the dialog and it will not pop up before I move my cursor out of the Workspace, giving the world controller (is this the correct naming?) a chance to process the deferred UI messages. Is this desired behavior? If not, has someone already considered a chain of responsibility pattern for every controller that could allow its parent controller to take some actions (such as deferred UI messages or also handling system events like a VM window close request)? Or would this be contrary to the MVC idea? :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Wed Sep 16 19:28:30 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 16 Sep 2020 19:28:30 0000 Subject: [squeak-dev] The Inbox: EToys-ct.403.mcz Message-ID: Christoph Thiede uploaded a new version of EToys to project The Inbox: http://source.squeak.org/inbox/EToys-ct.403.mcz ==================== Summary ==================== Name: EToys-ct.403 Author: ct Time: 16 September 2020, 9:28:21.732871 pm UUID: da566a07-8012-ee4b-88d6-58f224234d76 Ancestors: EToys-eem.400 Fixes an AssertionFailure concerning #doLayoutAgain when a particular arrangement of EToys tiles is laid out. TileMorph wants to change its owner's layout policy if that is a TilePadMorph as soon as it is embedded into that. However, the wrong hook was chosen for this. #ownerChanged is signaled whenever the owner has changed its layout. Instead, #noteNewOner: is signaled whenever the receiver is embedded into a new owner. Note that this practice of lazy layout modification is still suspect from my perspective, but at least it works again. Pooh, this one was really hard to find. Thanks a lot to Marcel (mt) and the MessageSendRecorder for their important help! ;-) =============== Diff against EToys-eem.400 =============== Item was added: + ----- Method: TileMorph>>noteNewOwner: (in category 'change reporting') ----- + noteNewOwner: ownerMorph + + super noteNewOwner: ownerMorph. + + ((ownerMorph isKindOf: TilePadMorph) + and: [ownerMorph layoutPolicy isNil]) + ifTrue: [ + ownerMorph + layoutPolicy: TableLayout new; + hResizing: #shrinkWrap; + vResizing: #spaceFill].! Item was removed: - ----- Method: TileMorph>>ownerChanged (in category 'change reporting') ----- - ownerChanged - super ownerChanged. - (owner class == TilePadMorph and:[owner layoutPolicy isNil]) ifTrue:[ - owner layoutPolicy: TableLayout new. - owner hResizing: #shrinkWrap. - owner vResizing: #spaceFill ].! From commits at source.squeak.org Wed Sep 16 20:12:55 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 16 Sep 2020 20:12:55 0000 Subject: [squeak-dev] The Trunk: Kernel-dtl.1340.mcz Message-ID: David T. Lewis uploaded a new version of Kernel to project The Trunk: http://source.squeak.org/trunk/Kernel-dtl.1340.mcz ==================== Summary ==================== Name: Kernel-dtl.1340 Author: dtl Time: 15 September 2020, 10:28:36.703545 pm UUID: d155f790-f808-4f68-ae73-3d98434f66d0 Ancestors: Kernel-ul.1339 Implement Sensor eventTimeNow for support of synthesized events. Eliminates the sometimes incorrect assumption that event time stamps must match the obsolete VM millisecond clock, and allows the Windows VM to consistently use native Windows event time stamps in the event queue. =============== Diff against Kernel-ul.1339 =============== Item was changed: Object subclass: #EventSensor + instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore lastEventTime' - instanceVariableNames: 'mouseButtons mousePosition mouseWheelDelta keyboardBuffer interruptKey interruptSemaphore eventQueue inputSemaphore lastEventPoll hasInputSemaphore' classVariableNames: 'ButtonDecodeTable EventPollPeriod EventTicklerProcess InterruptWatcherProcess KeyDecodeTable' poolDictionaries: 'EventSensorConstants' category: 'Kernel-Processes'! !EventSensor commentStamp: 'mt 12/13/2019 14:38' prior: 0! An EventSensor is an interface to the user input devices. There is at least one instance of EventSensor named Sensor in the system. EventSensor is a replacement for the earlier InputSensor implementation based on a set of (optional) event primitives. An EventSensor updates its state when events are received so that all state based users of Sensor (e.g., Sensor keyboard, Sensor leftShiftDown, Sensor mouseButtons) will work exactly as before, by moving the current VM mechanisms into EventSensor itself. An optional input semaphore is part of the new design. For platforms that support true asynchronous event notification, the semaphore will be signaled to indicate pending events. On platforms that do not support asynchronous notifications about events, the UI will have to poll EventSensor periodically to read events from the VM. Instance variables: mouseButtons - mouse button state as replacement for primMouseButtons mousePosition - mouse position as replacement for primMousePt keyboardBuffer - keyboard input buffer interruptKey - currently defined interrupt key interruptSemaphore - the semaphore signaled when the interruptKey is detected eventQueue - an optional event queue for event driven applications inputSemaphore - the semaphore signaled by the VM if asynchronous event notification is supported lastEventPoll - the last millisecondClockValue at which we called fetchMoreEvents hasInputSemaphore - true if my inputSemaphore has actually been signaled at least once. Class variables: ButtonDecodeTable - maps mouse buttons as reported by the VM to ones reported in the events. KeyDecodeTable SmallInteger>> - maps some keys and their modifiers to other keys (used for instance to map Ctrl-X to Alt-X) InterruptSemaphore - signalled by the the VM and/or the event loop upon receiving an interrupt keystroke. InterruptWatcherProcess - waits on the InterruptSemaphore and then responds as appropriate. EventPollPeriod - the number of milliseconds to wait between polling for more events in the userInterruptHandler. EventTicklerProcess - the process that makes sure that events are polled for often enough (at least every EventPollPeriod milliseconds). Event format: The current event format is very simple. Each event is recorded into an 8 element array. All events must provide some SmallInteger ID (the first field in the event buffer) and a time stamp (the second field in the event buffer), so that the difference between the time stamp of an event and the current time can be reported. Currently, the following events are defined: Null event ============= The Null event is returned when the ST side asks for more events but no more events are available. Structure: [1] - event type 0 [2-8] - unused Mouse event structure ========================== Mouse events are generated when mouse input is detected. [1] - event type 1 [2] - time stamp [3] - mouse x position [4] - mouse y position [5] - button state; bitfield with the following entries: 1 - 2r001 yellow (e.g., right) button 2 - 2r010 blue (e.g., middle) button 4 - 2r100 red (e.g., left) button [all other bits are currently undefined] [6] - modifier keys; bitfield with the following entries: 1 - shift key 2 - ctrl key 4 - (Mac specific) option key 8 - Cmd/Alt key [all other bits are currently undefined] [7] - reserved. [8] - host window id. Keyboard events ==================== Keyboard events are generated when keyboard input is detected. [1] - event type 2 [2] - time stamp [3] - character code (Ascii) For now the character code is in Mac Roman encoding. See #macToSqueak. For key press/release (see [4]), character codes are normalized. [4] - press state; integer with the following meaning 0 - character (aka. key stroke or key still pressed) 1 - key press (aka. key down) 2 - key release (aka. key up) [5] - modifier keys (same as in mouse events) For key press/release (see [4]), modifier keys are still accessible. [6] - character code (Unicode UTF32) Manual decoding via KeyboardInputInterpreter possible. For key press/release (see [4]), character codes are normalized. [7] - reserved. [8] - host window id. Mouse-wheel event structure ========================== Mouse-wheel events are generated when mouse-wheel input is detected. [1] - event type 7 [2] - time stamp [3] - horizontal scroll delta [4] - vertical scroll delta [5] - button state (same as in mouse events) [6] - modifier keys (same as in mouse events) [7] - reserved. [8] - host window id. ! Item was changed: ----- Method: EventSensor>>createMouseEvent (in category 'mouse') ----- createMouseEvent "create and return a new mouse event from the current mouse position; this is useful for restarting normal event queue processing after manual polling" | buttons modifiers pos mapped eventBuffer | eventBuffer := Array new: 8. buttons := self peekButtons. pos := self peekPosition. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. eventBuffer at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ^ eventBuffer! Item was added: + ----- Method: EventSensor>>eventTimeNow (in category 'private') ----- + eventTimeNow + "Answer an event timeStamp that is slightly more recent than that of + the most recently processed event. Intended for synthesized events to + be processed in line with those from the real event queue." + + ^ lastEventTime + 1. + ! Item was changed: ----- Method: EventSensor>>peekEventSynthesized (in category 'private') ----- peekEventSynthesized "Return a synthesized event. This method is called if an event driven client wants to receive events but the primary user interface is not event-driven (e.g., the receiver does not have an event queue but only updates its state). This can, for instance, happen if a Morphic World is run in an MVC window. To simplify the clients work this method will always return all available keyboard events first, and then (repeatedly) the mouse events. Since mouse events come last, the client can assume that after one mouse event has been received there are no more to come. Note that it is impossible for EventSensor to determine if a mouse event has been issued before so the client must be aware of the possible problem of getting repeatedly the same mouse events. See HandMorph>>processEvents for an example on how to deal with this." | kbd array buttons pos modifiers mapped | "First check for keyboard" array := Array new: 8. keyboardBuffer isEmpty ifFalse: ["simulate keyboard event" array at: 1 put: EventTypeKeyboard. "evt type" + array at: 2 put: self eventTimeNow. "time stamp" - array at: 2 put: Time eventMillisecondClock. "time stamp" array at: 3 put: ((kbd := keyboardBuffer peek) bitAnd: 255). "char code" array at: 4 put: EventKeyChar. "key press/release" array at: 5 put: (kbd bitShift: -8). "modifier keys" ^ array]. "Then check for mouse" pos := mousePosition. buttons := mouseButtons. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. array at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ^ array ! Item was changed: ----- Method: EventSensor>>primGetNextEvent: (in category 'private-I/O') ----- primGetNextEvent: array "Store the next OS event available into the provided array. Essential. If the VM is not event driven the ST code will fall back to the old-style mechanism and use the state based primitives instead." | kbd buttons modifiers pos mapped | "Simulate the events" array at: 1 put: EventTypeNone. "assume no more events" "First check for keyboard" kbd := self oldPrimKbdNext. kbd = nil ifFalse:[ "simulate keyboard event" array at: 1 put: EventTypeKeyboard. "evt type" + array at: 2 put: self eventTimeNow. "time stamp" - array at: 2 put: Time eventMillisecondClock. "time stamp" array at: 3 put: (kbd bitAnd: 255). "char code" array at: 4 put: EventKeyChar. "key press/release" array at: 5 put: (kbd bitShift: -8). "modifier keys" ^self]. "Then check for mouse" buttons := self oldPrimMouseButtons. pos := self oldPrimMousePt. modifiers := buttons bitShift: -3. buttons := buttons bitAnd: 7. mapped := self mapButtons: buttons modifiers: modifiers. (pos = mousePosition and:[(mapped bitOr: (modifiers bitShift: 3)) = mouseButtons]) ifTrue:[^self]. array at: 1 put: EventTypeMouse; + at: 2 put: self eventTimeNow; - at: 2 put: Time eventMillisecondClock; at: 3 put: pos x; at: 4 put: pos y; at: 5 put: mapped; at: 6 put: modifiers. ! Item was changed: ----- Method: EventSensor>>processEvent: (in category 'private-I/O') ----- processEvent: evt "Process a single event. This method is run at high priority." | type buttons window | type := evt at: 1. + lastEventTime := evt at: 2. "Only process main window events, forward others to host window proxies" window := evt at: 8. (window isNil or: [window isZero]) ifTrue: [window := 1. evt at: 8 put: window]. window = 1 ifFalse: [ ^Smalltalk at: #HostWindowProxy ifPresent: [:w | w processEvent: evt]]. "Tackle mouse events and mouse wheel events first" (type = EventTypeMouse or: [type = EventTypeMouseWheel]) ifTrue: [buttons := (ButtonDecodeTable at: (evt at: 5) + 1). evt at: 5 put: (Smalltalk platformName = 'Mac OS' ifTrue: [ buttons ] ifFalse: [ self mapButtons: buttons modifiers: (evt at: 6) ]). self queueEvent: evt. type = EventTypeMouseWheel ifTrue: [^ self processMouseWheelEvent: evt]. type = EventTypeMouse ifTrue: [^ self processMouseEvent: evt]]. "Store the event in the queue if there's any" type = EventTypeKeyboard ifTrue: [ "Check if the event is a user interrupt" ((evt at: 4) = EventKeyChar and: [((evt at: 3) bitOr: (((evt at: 5) bitAnd: 8) bitShift: 8)) = interruptKey]) ifTrue: ["interrupt key is meta - not reported as event" ^ interruptSemaphore signal]. "Decode keys for characters (i.e., duplicate or swap, ctrl <-> alt/cmd)." (evt at: 4) = EventKeyChar ifTrue: [ | unicode ascii | "Copy lookup key first in case of key swap." unicode := {evt at: 6. evt at: 5}. ascii := {evt at: 3. evt at: 5}. KeyDecodeTable "Unicode character first" at: unicode ifPresent: [:a | evt at: 6 put: a first; at: 5 put: a second]. KeyDecodeTable "ASCII character second" at: ascii ifPresent: [:a | evt at: 3 put: a first; at: 5 put: a second]]. self queueEvent: evt. self processKeyboardEvent: evt . ^self ]. "Handle all events other than Keyboard or Mouse." self queueEvent: evt. ! From commits at source.squeak.org Wed Sep 16 20:13:48 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 16 Sep 2020 20:13:48 0000 Subject: [squeak-dev] The Trunk: Morphic-dtl.1681.mcz Message-ID: David T. Lewis uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-dtl.1681.mcz ==================== Summary ==================== Name: Morphic-dtl.1681 Author: dtl Time: 15 September 2020, 10:29:40.709583 pm UUID: ee5dc42a-7d85-4c8b-b9e0-618eae3df221 Ancestors: Morphic-dtl.1680 Use Sensor eventTimeNow for synthesized event times. =============== Diff against Morphic-dtl.1680 =============== Item was changed: ----- Method: HandMorph>>generateDropFilesEvent: (in category 'private events') ----- generateDropFilesEvent: evtBuf "Generate the appropriate mouse event for the given raw event buffer" "Note: This is still in an experimental phase and will need more work" | position buttons modifiers stamp numFiles dragType | stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. dragType := evtBuf third. position := evtBuf fourth @ evtBuf fifth. buttons := 0. modifiers := evtBuf sixth. buttons := buttons bitOr: (modifiers bitShift: 3). numFiles := evtBuf seventh. dragType = 4 ifTrue: ["e.g., drop" owner borderWidth: 0. ^DropFilesEvent new setPosition: position contents: numFiles hand: self]. "the others are currently not handled by morphs themselves" dragType = 1 ifTrue: ["experimental drag enter" owner borderWidth: 4; borderColor: owner color asColor negated]. dragType = 2 ifTrue: ["experimental drag move" ]. dragType = 3 ifTrue: ["experimental drag leave" owner borderWidth: 0]. ^nil! Item was changed: ----- Method: HandMorph>>generateKeyboardEvent: (in category 'private events') ----- generateKeyboardEvent: evtBuf "Generate the appropriate mouse event for the given raw event buffer" | buttons modifiers type pressType stamp keyValue | stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. pressType := evtBuf fourth. pressType = EventKeyDown ifTrue: [type := #keyDown]. pressType = EventKeyUp ifTrue: [type := #keyUp]. pressType = EventKeyChar ifTrue: [type := #keystroke]. modifiers := evtBuf fifth. buttons := (modifiers bitShift: 3) bitOr: (lastMouseEvent buttons bitAnd: 7). type = #keystroke ifTrue: [keyValue := (self keyboardInterpreter nextCharFrom: Sensor firstEvt: evtBuf) asInteger] ifFalse: [keyValue := evtBuf third]. ^ KeyboardEvent new setType: type buttons: buttons position: self position keyValue: keyValue hand: self stamp: stamp. ! Item was changed: ----- Method: HandMorph>>generateMouseEvent: (in category 'private events') ----- generateMouseEvent: evtBuf "Generate the appropriate mouse event for the given raw event buffer" | position buttons modifiers type trail stamp oldButtons evtChanged | evtBuf first = lastEventBuffer first ifTrue: ["Workaround for Mac VM bug, *always* generating 3 events on clicks" evtChanged := false. 3 to: evtBuf size do: [:i | (lastEventBuffer at: i) = (evtBuf at: i) ifFalse: [evtChanged := true]]. evtChanged ifFalse: [^nil]]. stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. position := evtBuf third @ evtBuf fourth. buttons := evtBuf fifth. modifiers := evtBuf sixth. type := buttons = 0 ifTrue:[ lastEventBuffer fifth = 0 ifTrue: [#mouseMove] "this time no button and previously no button .. just mouse move" ifFalse: [#mouseUp] "this time no button but previously some button ... therefore button was released" ] ifFalse:[ buttons = lastEventBuffer fifth ifTrue: [#mouseMove] "button states are the same .. now and past .. therfore a mouse movement" ifFalse: [ "button states are different .. button was pressed or released" buttons > lastEventBuffer fifth ifTrue: [#mouseDown] ifFalse:[#mouseUp]. ]. ]. buttons := buttons bitOr: (modifiers bitShift: 3). oldButtons := lastEventBuffer fifth bitOr: (lastEventBuffer sixth bitShift: 3). lastEventBuffer := evtBuf. type == #mouseMove ifTrue: [trail := self mouseTrailFrom: evtBuf. ^MouseMoveEvent new setType: type startPoint: (self position) endPoint: trail last trail: trail buttons: buttons hand: self stamp: stamp]. ^MouseButtonEvent new setType: type position: position which: (oldButtons bitXor: buttons) buttons: buttons nClicks: (evtBuf seventh ifNil: [0]) hand: self stamp: stamp! Item was changed: ----- Method: HandMorph>>generateMouseWheelEvent: (in category 'private events') ----- generateMouseWheelEvent: evtBuf "Generate the appropriate mouse wheel event for the given raw event buffer" | buttons modifiers deltaX deltaY stamp nextEvent | stamp := evtBuf second. + stamp = 0 ifTrue: [stamp := Sensor eventTimeNow]. - stamp = 0 ifTrue: [stamp := Time eventMillisecondClock]. deltaX := evtBuf third. deltaY := evtBuf fourth. buttons := evtBuf fifth. modifiers := evtBuf sixth. [(deltaX abs + deltaY abs < self class minimumWheelDelta) and: [(nextEvent := Sensor peekEvent) notNil and: [nextEvent first = evtBuf first and: [nextEvent fifth = evtBuf fifth and: [nextEvent sixth = evtBuf sixth] and: [nextEvent third isZero = evtBuf third isZero "both horizontal or vertical"]]]]] whileTrue: ["nextEvent is similar. Remove it from the queue, and check the next." nextEvent := Sensor nextEvent. deltaX := deltaX + nextEvent third. deltaY := deltaY + nextEvent fourth]. ^ MouseWheelEvent new setType: #mouseWheel position: self position delta: deltaX at deltaY buttons: buttons hand: self stamp: stamp! Item was changed: ----- Method: HandMorph>>generateWindowEvent: (in category 'private events') ----- generateWindowEvent: evtBuf "Generate the appropriate window event for the given raw event buffer" | evt | evt := WindowEvent new. evt setTimeStamp: evtBuf second. + evt timeStamp = 0 ifTrue: [evt setTimeStamp: Sensor eventTimeNow]. - evt timeStamp = 0 ifTrue: [evt setTimeStamp: Time eventMillisecondClock]. evt action: evtBuf third. evt rectangle: (Rectangle origin: evtBuf fourth @ evtBuf fifth corner: evtBuf sixth @ evtBuf seventh ). ^evt! Item was changed: ----- Method: MorphicEvent>>timeStamp (in category 'accessing') ----- timeStamp "Return the millisecond clock value at which the event was generated" + ^timeStamp ifNil:[timeStamp := Sensor eventTimeNow]! - ^timeStamp ifNil:[timeStamp := Time eventMillisecondClock]! From vanessa at codefrau.net Wed Sep 16 20:26:32 2020 From: vanessa at codefrau.net (Vanessa Freudenberg) Date: Wed, 16 Sep 2020 13:26:32 -0700 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: <02bd205c43974b2b8fce90ed05e7cea1@student.hpi.uni-potsdam.de> References: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914202934.GA25753@shell.msen.com> <02bd205c43974b2b8fce90ed05e7cea1@student.hpi.uni-potsdam.de> Message-ID: See page 41 "The Origin of Smalltalk SymbolTables" in Dan's Smalltalk Evolution paper: https://smalltalkzoo.computerhistory.org/papers/EvolutionOfSmalltalk.pdf There's likely others but this one I remembered ;) Vanessa On Wed, Sep 16, 2020 at 4:35 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Vanessa, > > > > Wouldn't the binding still live in Undeclared? > > > Are there any pointers to the concept of Undeclared? It seems to be a weak > dictionary, so this does not look like a durable compatibility solution to > me. > > Best, > Christoph > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Vanessa Freudenberg > *Gesendet:* Montag, 14. September 2020 23:30:59 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] Etoys help needed loading e.g. > CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) > > On Mon, Sep 14, 2020 at 1:29 PM David T. Lewis > wrote: > >> Karl, >> >> Thank you very much! >> > > Indeed, great sleuthing! > > Clearly we have introduced a bug in trunk since the 5.3 release, so >> we'll need to fix that. >> >> But to my original question - I was able to start with a fresh 5.3 >> image, and I can load CarAndPen.014.pr without problems. >> >> Then I removed the #World binding, and tried loading CarAndPen.014.pr >> to find out what the errors would look like. >> >> Big surprise - There was no error. The project still loads and runs >> as before. That is not what I expected, so I'm glad we checked. >> >> Dave >> > > Wouldn't the binding still live in Undeclared? > > - Vanessa - > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Wed Sep 16 23:37:06 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 16 Sep 2020 16:37:06 -0700 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: Message-ID: Hi Tim, > On Sep 16, 2020, at 10:36 AM, tim Rowledge wrote: > >  > >> On 2020-09-15, at 2:07 PM, Eliot Miranda wrote: >> >> >> >> So why don't we >> - modify the VM to stamp events with the utc second clock, > > The one possible problem I can foresee is if any OS event stuff we try to talk to expects timestamps consistent with its own view; imagine getting some event that needs to be replied to with everything the same except for one field updated etc. If we've stuck our own timestamp in then it could bollix things up. No idea if such events actually exist outside the realm of ancient, faintly remembered, RISC OS cases. Well, having the vm insert the timestamp into the event means that the timestamp can be accurate. What we have now with the proposed changes is that the image timestamps events *immediately after the image retrieves the event via primGetNextEvent:*. This could be significantly later than when the event was delivered to the vm, and quite different to what timestamp, if any, the GUI applied to the event. My proposal merely determines the representation of the timestamp in the image. If, for example, the is timestamps an e end with the time from system startup, then my proposal would require that we obtain the time of system startup and stamp the event delivered up to Smalltalk with the event timestamp plus the startup time, all respires enters as microseconds from the epoch. Mapping back to the GUI’s timestamp is therefore trivial. So using utc microseconds is not the issue. The issue is whether we derive the timestamp from the event itself, or we timestamp the event ourselves. If the GUI timestamps events then overriding that timestamp in the image is incorrect. If the GUI does not timestamp events then having the vm supply a timestamp at the earliest opportunity gives us more accurate timestamps. So in fact your concern makes the case for the vm applying the timestamp: - whether or not the GUI timestamps events is something platform-dependent that the vm must know about, and is in the best position to know about - representing their event timestamp as utc microseconds does nothing other than apply an offset to an event’s timestamp. The events actual timestamp is easily reconstructed given the scale and the offset of the timestamp from microseconds and the epoch. > *IF* this seems to have any plausibility, then we could stick with using the OS timestamp for the incoming events, but derive our prim 135 tick value from > > a) catching the first event, reading its timestamp, deriving an offset from our uSec tick, saving that and then doing the math anytime someone wants a prim 135 value, or > > b) finding out which OS time api is being used by the OS to stamp events; it really should be documented. Of course, we have plenty of experience of OS doc being... imprecise. > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > Strange OpCodes: PIC: Permute Instruction Codes > > > From commits at source.squeak.org Wed Sep 16 23:55:41 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 16 Sep 2020 23:55:41 0000 Subject: [squeak-dev] The Inbox: Monticello-ul.727.mcz Message-ID: Levente Uzonyi uploaded a new version of Monticello to project The Inbox: http://source.squeak.org/inbox/Monticello-ul.727.mcz ==================== Summary ==================== Name: Monticello-ul.727 Author: ul Time: 17 September 2020, 1:54:51.056164 am UUID: ad776836-42eb-4aa2-b788-f10dd9e07da2 Ancestors: Monticello-cmm.726 MCHttpRepository changes: - before up- or downloading files, transform the urls using #rewriteUrl:forDownload:. The default rules (see #urlRewriteRules) switch from http to https for source.squeak.org and squeaksource.com, and switch to the the static smalltalkhub site for downloads. The url rewriting is Eliot's idea, but this implementation uses a list of rewrite rules instead of a dictionary-based mapping. - use WebClient (and the shared webclient instance) for uploads too - retry down/uploading with WebClient at most 3 times. This should work around the case where the underlying socket was closed but the state of the socket has not been updated in Squeak. - use https in #creationTemplate =============== Diff against Monticello-cmm.726 =============== Item was changed: MCFileBasedRepository subclass: #MCHttpRepository instanceVariableNames: 'location user password readerCache indexed webClient' + classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' - classVariableNames: 'UseSharedWebClientInstance' poolDictionaries: '' category: 'Monticello-Repositories'! Item was changed: ----- Method: MCHttpRepository class>>creationTemplate (in category 'ui-support') ----- creationTemplate + ^self creationTemplateLocation: 'https://www.squeaksource.com/ProjectName' - ^self creationTemplateLocation: 'http://www.squeaksource.com/ProjectName' user: 'squeak' password: 'squeak' ! Item was added: + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in category 'url rewrite') ----- + rewriteUrl: aString forDownload: forDownload + + | result | + result := aString. + self urlRewriteRules groupsDo: [ :regexString :replacement :downloadOnly | + (forDownload or: [ downloadOnly not ]) ifTrue: [ + result := result copyWithRegex: regexString matchesReplacedWith: replacement ] ]. + ^result + + " + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'http://static.smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). + self assert: 'http://smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). + "! Item was added: + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url rewrite') ----- + urlRewriteRules + + ^URLRewriteRules ifNil: [ + URLRewriteRules := #( + "Regex to be replaced" "static replacement string" "download only" + '^http\://source\.squeak\.org/' 'https://source.squeak.org/' false + '^http\://squeaksource\.com/' 'https://squeaksource.com/' false + '^http\://www.squeaksource\.com/' 'https://www.squeaksource.com/' false + '^http\://smalltalkhub.com/' 'http://static.smalltalkhub.com/' true + ) asOrderedCollection ]! Item was changed: ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- httpGet: url arguments: arguments + | urlString | - | progress urlString client response result | - progress := [ :total :amount | - HTTPProgress new - total: total; - amount: amount; - signal: 'Downloading...' ]. urlString := arguments ifNil: [ url ] ifNotNil: [ | queryString | queryString := WebUtils encodeUrlEncodedForm: arguments. (url includes: $?) ifTrue: [ url, '&', queryString ] ifFalse: [ url, '?', queryString ] ]. + urlString := self class rewriteUrl: urlString forDownload: true. + ^self webClientDo: [ :client | + client + username: self user; + password: self password; + httpGet: urlString do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ]! - self class useSharedWebClientInstance ifTrue: [ - "Acquire webClient by atomically storing it in the client variable and setting its value to nil." - client := webClient. - webClient := nil ]. - client - ifNil: [ client := WebClient new ] - ifNotNil: [ - "Attempt to avoid an error on windows by recreating the underlying stream." - client isConnected ifFalse: [ client close ] ]. - response := client - username: self user; - password: self password; - httpGet: urlString do: [ :request | - request - headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; - headerAt: 'Connection' put: 'Keep-Alive'; - headerAt: 'Accept' put: '*/*' ]. - result := (response code between: 200 and: 299) - ifFalse: [ - response content. "Make sure content is read." - nil ] - ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. - self class useSharedWebClientInstance - ifTrue: [ - "Save the WebClient instance for reuse, but only if there is no client cached." - webClient - ifNil: [ webClient := client ] - ifNotNil: [ client close ] ] - ifFalse: [ client close ]. - result ifNil: [ NetworkError signal: 'Could not access ', location ]. - ^result! Item was added: + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') ----- + webClientDo: aBlock + + | client attemptsLeft response result | + self class useSharedWebClientInstance ifTrue: [ + "Acquire webClient by atomically storing it in the client variable and setting its value to nil." + client := webClient. + webClient := nil ]. + + client + ifNil: [ client := WebClient new ] + ifNotNil: [ + "Attempt to avoid an error by recreating the underlying stream." + client isConnected ifFalse: [ client close ] ]. + + attemptsLeft := 3. + response := nil. + [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ + response := [ aBlock value: client ] + on: NetworkError + do: [ :error | + attemptsLeft = 0 ifTrue: [ error pass ]. + (3 - attemptsLeft) seconds asDelay wait. + attemptsLeft := attemptsLeft - 1. + nil "The response" ] ]. + + result := (response code between: 200 and: 299) + ifFalse: [ + response content. "Make sure content is read." + nil ] + ifTrue: [ + (RWBinaryOrTextStream with: ( + response contentWithProgress: [ :total :amount | + HTTPProgress new + total: total; + amount: amount; + signal ])) reset ]. + + self class useSharedWebClientInstance + ifTrue: [ + "Save the WebClient instance for reuse, but only if there is no client cached." + webClient + ifNil: [ webClient := client ] + ifNotNil: [ client close ] ] + ifFalse: [ client close ]. + + result ifNil: [ NetworkError signal: 'Could not access ', location ]. + ^result! Item was changed: ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in category 'private') ----- writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock + + | stream urlString | - | stream response statusLine code | stream := RWBinaryOrTextStream on: String new. aBlock value: stream. + urlString := self urlForFileNamed: aString. + urlString := self class rewriteUrl: urlString forDownload: false. + ^self displayProgress: 'Uploading ', aString during: [ + self webClientDo: [ :client | + client + username: self user; + password: self password; + httpPut: urlString + content: stream contents + type: nil + do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ] ]! - self displayProgress: 'Uploading ', aString during:[ - response := HTTPSocket - httpPut: stream contents - to: (self urlForFileNamed: aString) - user: self user - passwd: self password. - ]. - "More robust handling of HTTP responses. Instead of enumerating - all possible return codes and http versions, do a quick parse" - (response beginsWith: 'HTTP/') ifTrue:[ - "Looks like an HTTP header, not some error message" - statusLine := response copyUpTo: Character cr. - code := [(statusLine findTokens: ' ') second asInteger] on: Error do:[]. - ]. - (code isInteger and:[code between: 200 and: 299]) - ifFalse:[self error: response].! From lewis at mail.msen.com Thu Sep 17 00:16:23 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 16 Sep 2020 20:16:23 -0400 Subject: [squeak-dev] Deferred UI messages in MVC can be deferred for a really long time In-Reply-To: <68a5b6d9a3584c48aa9845062170d060@student.hpi.uni-potsdam.de> References: <68a5b6d9a3584c48aa9845062170d060@student.hpi.uni-potsdam.de> Message-ID: <20200917001623.GA21470@shell.msen.com> On Wed, Sep 16, 2020 at 06:47:19PM +0000, Thiede, Christoph wrote: > Hi all! > > > Quick experiment: > > Open an MVC project, open a Workspace in it, and do it: > > Project current addDeferredUIMessage: [self inform: #foo] > > > In Morphic, this produces a dialog message "nearly immediately". But in MVC, apparently, I can sit there for hours and wait for the dialog and it will not pop up before I move my cursor out of the Workspace, giving the world controller (is this the correct naming?) a chance to process the deferred UI messages. > > > Is this desired behavior? If not, has someone already considered a chain of responsibility pattern for every controller that could allow its parent controller to take some actions (such as deferred UI messages or also handling system events like a VM window close request)? Or would this be contrary to the MVC idea? :-) > > Yes that is how it works. The deferred action queue is handled when transferring control from one controller to another (in Controller>>controlActivity). MVC is not as directly interactive as Morphic, and things seem to "happen" when your mouse movement causes a different controller to become active. When active, each controller has its own process. There is no such thing as a UI process that controls the world as in Morphic. Instead, Project current world is a ControlManager, which coordinates the various controllers and their processes. The deferred action queue was never part of the original MVC, so if you can think of a way to make it more responsive, no one will take any offence :-) Dave From tim at rowledge.org Thu Sep 17 01:13:27 2020 From: tim at rowledge.org (tim Rowledge) Date: Wed, 16 Sep 2020 18:13:27 -0700 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: Message-ID: <3B4C7C86-D413-40B0-8403-08B179E3A620@rowledge.org> > On 2020-09-16, at 4:37 PM, Eliot Miranda wrote: > > Well, having the vm insert the timestamp into the event means that the timestamp can be accurate. > > What we have now with the proposed changes is that the image timestamps events *immediately after the image retrieves the event via primGetNextEvent:*. Tiny nitpick - *only* if the primitive code didn't provide it or we're synthesizing an event. Otherwise, yes, we're already having the VM do the timestamp. Right now (assuming I'me reading the right files!) a look through all the getevent routines suggests - unix adds the timestamp from a call to ioMSecs, mac does the same except *maybe* for some tablet events?, Windows appears to use OS provided timestamp in the minheadless branch, or a value from GetTickCount() in the host window plugin, or something that looks like an OS provided value in sqWin32DirectInput * sqWin32Window. I'm sure we'll all be relieved to hear that RISC OS & Plan 9 use ioMSecs(). ;-) So we shouldn't ever see the timestamp being empty (for events fetched from the VM) and so shouldn't ever be manually fudging it. I'm pretty sure that is an ancient artefact of no value. It looks like the only platform deriving the timestamp from OS event provided data is Windows. I think that there could only be a problem if synthesized events were being used, AND the time ordering had to be monotonic - do we ever sort events by timestamp? AND the clock used by the Windows to provide the stamp is not aligned with whatever we use fo ioMSecs() etc. But otherwise, yah, no problems, the VM is already providing the timestamp and can use whatever mechanism we want. As for 64/32bit, whatever, I'm sure it can work OK. Would it be too problematic to simply make an LPI to stick into the event buffer? Sure, a bit more garbage but maybe? tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Manual Writer's Creed: Garbage in, gospel out. From lewis at mail.msen.com Thu Sep 17 01:47:02 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 16 Sep 2020 21:47:02 -0400 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: <20200911215416.GA42161@shell.msen.com> References: <20200911215416.GA42161@shell.msen.com> Message-ID: <20200917014702.GA35207@shell.msen.com> For purposes of closing out my original questions for this thread, conclusions are inline below. On Fri, Sep 11, 2020 at 05:54:16PM -0400, David T. Lewis wrote: > > This leads me to ask three questions: > > 1) Does #eventMillisecondClock need to exist? It looks like it might be > a kludge, if so could we make it go away? > No, eventMillisecondClock does not need to exist. Following recent updates in trunk, it is no longer used for anything related to event time stamps. > 2) If the eventMillisecondClock method does need to exist, should it be changed > to retrieve a millisecond value that is aligned with that of the events? > No. See above. No change is needed. > 3) If not 2) then do we need to change the Windows VM to somehow use event > timestamps that match eventMillisecondClock? > No. Event timestamps from the VM do not need to match eventMillisecondClock. The VM does not need to be fixed. Dave From rssnow1 at gmail.com Thu Sep 17 02:27:29 2020 From: rssnow1 at gmail.com (Richard Snow) Date: Wed, 16 Sep 2020 21:27:29 -0500 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: <20200917014702.GA35207@shell.msen.com> References: <20200911215416.GA42161@shell.msen.com> <20200917014702.GA35207@shell.msen.com> Message-ID: I am RSS.i will rto home server to pack my Tape backups in Kim 1 supertape format and take fruity 3.14 witm me to line star74464. z999 = 1935 a..d. Synonym 0 b.c. RSS On Wed, Sep 16, 2020, 8:47 PM David T. Lewis wrote: > For purposes of closing out my original questions for this thread, > conclusions are inline below. > > On Fri, Sep 11, 2020 at 05:54:16PM -0400, David T. Lewis wrote: > > > > This leads me to ask three questions: > > > > 1) Does #eventMillisecondClock need to exist? It looks like it might be > > a kludge, if so could we make it go away? > > > > No, eventMillisecondClock does not need to exist. Following recent updates > in trunk, it is no longer used for anything related to event time stamps. > > > > 2) If the eventMillisecondClock method does need to exist, should it be > changed > > to retrieve a millisecond value that is aligned with that of the events? > > > > No. See above. No change is needed. > > > > 3) If not 2) then do we need to change the Windows VM to somehow use > event > > timestamps that match eventMillisecondClock? > > > > No. Event timestamps from the VM do not need to match > eventMillisecondClock. > The VM does not need to be fixed. > > Dave > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From rssnow1 at gmail.com Thu Sep 17 02:30:28 2020 From: rssnow1 at gmail.com (Richard Snow) Date: Wed, 16 Sep 2020 21:30:28 -0500 Subject: [squeak-dev] Time>>eventMillisecondClock and event timestamps In-Reply-To: References: <20200911215416.GA42161@shell.msen.com> <20200917014702.GA35207@shell.msen.com> Message-ID: Will revive Vac on return to line star VAX On Wed, Sep 16, 2020, 9:27 PM Richard Snow wrote: > I am RSS.i will rto home server to pack my > Tape backups in Kim 1 supertape format and take fruity 3.14 witm me to > line star74464. z999 = 1935 a..d. > > > Synonym > > 0 b.c. > RSS > > > On Wed, Sep 16, 2020, 8:47 PM David T. Lewis wrote: > >> For purposes of closing out my original questions for this thread, >> conclusions are inline below. >> >> On Fri, Sep 11, 2020 at 05:54:16PM -0400, David T. Lewis wrote: >> > >> > This leads me to ask three questions: >> > >> > 1) Does #eventMillisecondClock need to exist? It looks like it might be >> > a kludge, if so could we make it go away? >> > >> >> No, eventMillisecondClock does not need to exist. Following recent updates >> in trunk, it is no longer used for anything related to event time stamps. >> >> >> > 2) If the eventMillisecondClock method does need to exist, should it be >> changed >> > to retrieve a millisecond value that is aligned with that of the events? >> > >> >> No. See above. No change is needed. >> >> >> > 3) If not 2) then do we need to change the Windows VM to somehow use >> event >> > timestamps that match eventMillisecondClock? >> > >> >> No. Event timestamps from the VM do not need to match >> eventMillisecondClock. >> The VM does not need to be fixed. >> >> Dave >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 20200916_211059_Audio message Type: audio/amr Size: 5990 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: rtc-snapshot-2665781299070614601.jpeg Type: image/jpeg Size: 18193 bytes Desc: not available URL: From forums.jakob at resfarm.de Thu Sep 17 05:54:54 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Thu, 17 Sep 2020 07:54:54 +0200 Subject: [squeak-dev] Not getting all mail? Message-ID: Hello, Over the past week I have observed people reply to some messages that I didn't receive from the list. For example, I do not have Christoph's message to which Eliot replied below. In one instance it even looked like I did not get the original post, but only a reply. I checked my spam folder, there are no Squeak mails in there. Do you experience the same? Or do I just always see cases where one person did not reply to the list, but the next one does again? Kind regards, Jakob Eliot Miranda schrieb am Mo., 14. Sep. 2020, 21:49: > > > On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Which completion are you using? Autocompletion >> already behaves like this. >> > > OComplete > >> >> Best, >> >> Christoph >> >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von Eliot Miranda >> *Gesendet:* Montag, 14. September 2020 04:45:30 >> *An:* The general-purpose Squeak developers list >> *Betreff:* [squeak-dev] autocomplete exit >> >> Hi, >> >> would it be possible to make autocomplete exit whenever a non-selector >> character was typed, or at least when an arrow key was typed? >> >> _,,,^..^,,,_ >> best, Eliot >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 17 06:51:07 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 06:51:07 0000 Subject: [squeak-dev] The Trunk: PreferenceBrowser-mt.106.mcz Message-ID: Marcel Taeumel uploaded a new version of PreferenceBrowser to project The Trunk: http://source.squeak.org/trunk/PreferenceBrowser-mt.106.mcz ==================== Summary ==================== Name: PreferenceBrowser-mt.106 Author: mt Time: 17 September 2020, 8:51:08.314726 am UUID: b9d92cdc-bbf8-6441-bd0a-e3b2a287e934 Ancestors: PreferenceBrowser-mt.105 Let the wizard install FFI through Metacello again to manage different Squeak versions. Note that the Metacello script was fixed so that it should work for Squeak 5.3, too. =============== Diff against PreferenceBrowser-mt.105 =============== Item was changed: ----- Method: PreferenceWizardMorph>>installFFI (in category 'actions - packages') ----- installFFI + Metacello new - " Metacello new configuration: 'FFI'; + load. + " - load." (Installer squeak) project: 'FFI'; install: 'FFI-Pools'; install: 'FFI-Kernel'; install: 'FFI-Tools'; install: 'FFI-Tests'; + install: 'FFI-PoolsTests'. + "! - install: 'FFI-PoolsTests'.! From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 07:14:42 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 07:14:42 +0000 Subject: [squeak-dev] The Inbox: Monticello-ul.727.mcz In-Reply-To: References: Message-ID: <1ae1c3850d3548dab48fe0da68bffac4@student.hpi.uni-potsdam.de> Very nice idea! :-) If I wanted to hijack an image, these URLRewriteRules would probably be my first approach - but security has never been an issue for Squeak, so I guess this is not a problem. Does this also fix the problem with the classic HTTP URLs returned by the update map or will we still need to patch them on the server side? And one last question regarding to your tests in the method comment of #rewriteUrl:forDownload:: Couldn't you put them into a real test case? I'm pretty sure that not everyone will run these out-commented tests manually, and it would be a pity not to automate them. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Donnerstag, 17. September 2020 01:55:41 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: Monticello-ul.727.mcz Levente Uzonyi uploaded a new version of Monticello to project The Inbox: http://source.squeak.org/inbox/Monticello-ul.727.mcz ==================== Summary ==================== Name: Monticello-ul.727 Author: ul Time: 17 September 2020, 1:54:51.056164 am UUID: ad776836-42eb-4aa2-b788-f10dd9e07da2 Ancestors: Monticello-cmm.726 MCHttpRepository changes: - before up- or downloading files, transform the urls using #rewriteUrl:forDownload:. The default rules (see #urlRewriteRules) switch from http to https for source.squeak.org and squeaksource.com, and switch to the the static smalltalkhub site for downloads. The url rewriting is Eliot's idea, but this implementation uses a list of rewrite rules instead of a dictionary-based mapping. - use WebClient (and the shared webclient instance) for uploads too - retry down/uploading with WebClient at most 3 times. This should work around the case where the underlying socket was closed but the state of the socket has not been updated in Squeak. - use https in #creationTemplate =============== Diff against Monticello-cmm.726 =============== Item was changed: MCFileBasedRepository subclass: #MCHttpRepository instanceVariableNames: 'location user password readerCache indexed webClient' + classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' - classVariableNames: 'UseSharedWebClientInstance' poolDictionaries: '' category: 'Monticello-Repositories'! Item was changed: ----- Method: MCHttpRepository class>>creationTemplate (in category 'ui-support') ----- creationTemplate + ^self creationTemplateLocation: 'https://www.squeaksource.com/ProjectName' - ^self creationTemplateLocation: 'http://www.squeaksource.com/ProjectName' user: 'squeak' password: 'squeak' ! Item was added: + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in category 'url rewrite') ----- + rewriteUrl: aString forDownload: forDownload + + | result | + result := aString. + self urlRewriteRules groupsDo: [ :regexString :replacement :downloadOnly | + (forDownload or: [ downloadOnly not ]) ifTrue: [ + result := result copyWithRegex: regexString matchesReplacedWith: replacement ] ]. + ^result + + " + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'http://static.smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). + self assert: 'http://smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). + "! Item was added: + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url rewrite') ----- + urlRewriteRules + + ^URLRewriteRules ifNil: [ + URLRewriteRules := #( + "Regex to be replaced" "static replacement string" "download only" + '^http\://source\.squeak\.org/' 'https://source.squeak.org/' false + '^http\://squeaksource\.com/' 'https://squeaksource.com/' false + '^http\://www.squeaksource\.com/' 'https://www.squeaksource.com/' false + '^http\://smalltalkhub.com/' 'http://static.smalltalkhub.com/' true + ) asOrderedCollection ]! Item was changed: ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- httpGet: url arguments: arguments + | urlString | - | progress urlString client response result | - progress := [ :total :amount | - HTTPProgress new - total: total; - amount: amount; - signal: 'Downloading...' ]. urlString := arguments ifNil: [ url ] ifNotNil: [ | queryString | queryString := WebUtils encodeUrlEncodedForm: arguments. (url includes: $?) ifTrue: [ url, '&', queryString ] ifFalse: [ url, '?', queryString ] ]. + urlString := self class rewriteUrl: urlString forDownload: true. + ^self webClientDo: [ :client | + client + username: self user; + password: self password; + httpGet: urlString do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ]! - self class useSharedWebClientInstance ifTrue: [ - "Acquire webClient by atomically storing it in the client variable and setting its value to nil." - client := webClient. - webClient := nil ]. - client - ifNil: [ client := WebClient new ] - ifNotNil: [ - "Attempt to avoid an error on windows by recreating the underlying stream." - client isConnected ifFalse: [ client close ] ]. - response := client - username: self user; - password: self password; - httpGet: urlString do: [ :request | - request - headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; - headerAt: 'Connection' put: 'Keep-Alive'; - headerAt: 'Accept' put: '*/*' ]. - result := (response code between: 200 and: 299) - ifFalse: [ - response content. "Make sure content is read." - nil ] - ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. - self class useSharedWebClientInstance - ifTrue: [ - "Save the WebClient instance for reuse, but only if there is no client cached." - webClient - ifNil: [ webClient := client ] - ifNotNil: [ client close ] ] - ifFalse: [ client close ]. - result ifNil: [ NetworkError signal: 'Could not access ', location ]. - ^result! Item was added: + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') ----- + webClientDo: aBlock + + | client attemptsLeft response result | + self class useSharedWebClientInstance ifTrue: [ + "Acquire webClient by atomically storing it in the client variable and setting its value to nil." + client := webClient. + webClient := nil ]. + + client + ifNil: [ client := WebClient new ] + ifNotNil: [ + "Attempt to avoid an error by recreating the underlying stream." + client isConnected ifFalse: [ client close ] ]. + + attemptsLeft := 3. + response := nil. + [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ + response := [ aBlock value: client ] + on: NetworkError + do: [ :error | + attemptsLeft = 0 ifTrue: [ error pass ]. + (3 - attemptsLeft) seconds asDelay wait. + attemptsLeft := attemptsLeft - 1. + nil "The response" ] ]. + + result := (response code between: 200 and: 299) + ifFalse: [ + response content. "Make sure content is read." + nil ] + ifTrue: [ + (RWBinaryOrTextStream with: ( + response contentWithProgress: [ :total :amount | + HTTPProgress new + total: total; + amount: amount; + signal ])) reset ]. + + self class useSharedWebClientInstance + ifTrue: [ + "Save the WebClient instance for reuse, but only if there is no client cached." + webClient + ifNil: [ webClient := client ] + ifNotNil: [ client close ] ] + ifFalse: [ client close ]. + + result ifNil: [ NetworkError signal: 'Could not access ', location ]. + ^result! Item was changed: ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in category 'private') ----- writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock + + | stream urlString | - | stream response statusLine code | stream := RWBinaryOrTextStream on: String new. aBlock value: stream. + urlString := self urlForFileNamed: aString. + urlString := self class rewriteUrl: urlString forDownload: false. + ^self displayProgress: 'Uploading ', aString during: [ + self webClientDo: [ :client | + client + username: self user; + password: self password; + httpPut: urlString + content: stream contents + type: nil + do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ] ]! - self displayProgress: 'Uploading ', aString during:[ - response := HTTPSocket - httpPut: stream contents - to: (self urlForFileNamed: aString) - user: self user - passwd: self password. - ]. - "More robust handling of HTTP responses. Instead of enumerating - all possible return codes and http versions, do a quick parse" - (response beginsWith: 'HTTP/') ifTrue:[ - "Looks like an HTTP header, not some error message" - statusLine := response copyUpTo: Character cr. - code := [(statusLine findTokens: ' ') second asInteger] on: Error do:[]. - ]. - (code isInteger and:[code between: 200 and: 299]) - ifFalse:[self error: response].! -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 17 07:15:53 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 07:15:53 0000 Subject: [squeak-dev] The Trunk: ST80-mt.258.mcz Message-ID: Marcel Taeumel uploaded a new version of ST80 to project The Trunk: http://source.squeak.org/trunk/ST80-mt.258.mcz ==================== Summary ==================== Name: ST80-mt.258 Author: mt Time: 17 September 2020, 9:15:53.534726 am UUID: c7ff9de0-a71a-4646-a639-211d94848498 Ancestors: ST80-mt.257 Fixes processing of deferred actions in MVC. Note that windows in MVC delegate control to their sub-controllers, however, remaining the active controller from the project's world perspective (i.e. the controller manager). Thus, we have to process two deferred-action queues in for most cases. For example, the text field in a workspace involves a PluggableTextController, embedded in a StandardSystemController. Any do-it like "Project current addDeferredUIMessage: [...]" will add the message to the StandardSystemController's queue. =============== Diff against ST80-mt.257 =============== Item was changed: ----- Method: Controller>>controlActivity (in category 'control defaults') ----- controlActivity "Pass control to the next control level (that is, to the Controller of a subView of the receiver's view) if possible. It is sent by Controller|controlLoop each time through the main control loop. It should be redefined in a subclass if some other action is needed." + self processDeferredActions. + Project current world activeController processDeferredActions. - [self deferredActionQueue isEmpty] - whileFalse: [deferredActionQueue next value]. self controlToNextLevel! Item was added: + ----- Method: Controller>>processDeferredActions (in category 'control defaults') ----- + processDeferredActions + + [self deferredActionQueue isEmpty] + whileFalse: [deferredActionQueue next value].! Item was changed: ----- Method: ParagraphEditor>>normalActivity (in category 'controlling') ----- normalActivity self processKeyboard. + self processMouseButtons. + super normalActivity. + ! - self processMouseButtons! From marcel.taeumel at hpi.de Thu Sep 17 07:16:07 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 09:16:07 +0200 Subject: [squeak-dev] Deferred UI messages in MVC can be deferred for a really long time In-Reply-To: <20200917001623.GA21470@shell.msen.com> References: <68a5b6d9a3584c48aa9845062170d060@student.hpi.uni-potsdam.de> <20200917001623.GA21470@shell.msen.com> Message-ID: Hi Christoph, hi Dave, fixed in ST80-mt.258 Best, Marcel Am 17.09.2020 02:16:31 schrieb David T. Lewis : On Wed, Sep 16, 2020 at 06:47:19PM +0000, Thiede, Christoph wrote: > Hi all! > > > Quick experiment: > > Open an MVC project, open a Workspace in it, and do it: > > Project current addDeferredUIMessage: [self inform: #foo] > > > In Morphic, this produces a dialog message "nearly immediately". But in MVC, apparently, I can sit there for hours and wait for the dialog and it will not pop up before I move my cursor out of the Workspace, giving the world controller (is this the correct naming?) a chance to process the deferred UI messages. > > > Is this desired behavior? If not, has someone already considered a chain of responsibility pattern for every controller that could allow its parent controller to take some actions (such as deferred UI messages or also handling system events like a VM window close request)? Or would this be contrary to the MVC idea? :-) > > Yes that is how it works. The deferred action queue is handled when transferring control from one controller to another (in Controller>>controlActivity). MVC is not as directly interactive as Morphic, and things seem to "happen" when your mouse movement causes a different controller to become active. When active, each controller has its own process. There is no such thing as a UI process that controls the world as in Morphic. Instead, Project current world is a ControlManager, which coordinates the various controllers and their processes. The deferred action queue was never part of the original MVC, so if you can think of a way to make it more responsive, no one will take any offence :-) Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 07:46:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 07:46:19 +0000 Subject: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body Message-ID: I think the screencast should be self-explaining: [cid:13d406ba-d3ab-4834-99d8-613351ad9e53] The problem might be the check in TextMorph >> #mouseDown:, but I'm not yet sure. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 22929 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bug-keyboardFocus-textMorph.gif Type: image/gif Size: 372612 bytes Desc: bug-keyboardFocus-textMorph.gif URL: From commits at source.squeak.org Thu Sep 17 07:52:31 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 07:52:31 0000 Subject: [squeak-dev] The Inbox: Collections-ct.912.mcz Message-ID: A new version of Collections was added to project The Inbox: http://source.squeak.org/inbox/Collections-ct.912.mcz ==================== Summary ==================== Name: Collections-ct.912 Author: ct Time: 17 September 2020, 9:52:26.194813 am UUID: 8613bd82-d0a4-7444-9e25-7ced81133021 Ancestors: Collections-eem.911 Fixes a typo in two method comments. *Setting an imaginary MINOR flag for this commit* =============== Diff against Collections-eem.911 =============== Item was changed: ----- Method: Base64MimeConverter>>mimeDecode (in category 'conversion') ----- mimeDecode + "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Return a whole stream for the user to read." - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Reutrn a whole stream for the user to read." | nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)) asCharacter. nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)) asCharacter. nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD) asCharacter. ]. ^ dataStream! Item was changed: ----- Method: Base64MimeConverter>>mimeDecodeToByteArray (in category 'conversion') ----- mimeDecodeToByteArray + "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Return a whole stream for the user to read." - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Reutrn a whole stream for the user to read." | nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)). nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)). nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD). ]. ^ dataStream! From christoph.thiede at student.hpi.uni-potsdam.de Thu Sep 17 07:55:19 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Thu, 17 Sep 2020 02:55:19 -0500 (CDT) Subject: [squeak-dev] UndeclaredVariable correction raises Error in Parser In-Reply-To: <4ac7e9892bd94176acb6699ac87d4bcc@student.hpi.uni-potsdam.de> References: <4ac7e9892bd94176acb6699ac87d4bcc@student.hpi.uni-potsdam.de> Message-ID: <1600329319725-0.post@n4.nabble.com> Hi all, nine months later here is another friendly bump of this tiny patch. It would be really great if someone could spend some minutes on reviewing and merging it! :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From commits at source.squeak.org Thu Sep 17 10:08:18 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 10:08:18 0000 Subject: [squeak-dev] The Inbox: EToys-ct.404.mcz Message-ID: Christoph Thiede uploaded a new version of EToys to project The Inbox: http://source.squeak.org/inbox/EToys-ct.404.mcz ==================== Summary ==================== Name: EToys-ct.404 Author: ct Time: 17 September 2020, 12:08:06.252813 pm UUID: d9eaf56f-8f92-314a-91b7-f15e0b463c55 Ancestors: EToys-eem.400 Refactors new parse node to tile conversion and revises editor menu intergration. Minor refactorings: Method splitting, renaming, and some typo and layout fixes. Keeps depending on EToys-ct.366, EToys-ct.368, EToys-ct.369, EToys-ct.370, EToys-ct.389, EToys-ct.390. Sorry for the cluttered ancestry, for the next project, I will really use changesets! =============== Diff against EToys-eem.400 =============== Item was added: + ----- Method: MessageNode>>asTileForPlayer: (in category '*Etoys-tiles') ----- + asTileForPlayer: aPlayer + + | receiverType argumentType resultType phrase receiverTiles | + "Catch edge case: Color tile" + (self receiver isVariableNode and: [self receiver key = (Smalltalk bindingOf: #Color)]) + ifTrue: [ | source result | + source := String streamContents: (MessageSend receiver: self selector: #shortPrintOn:). + result := [Compiler evaluate: source] ifError: [nil]. + result isColor ifTrue: [^ result newTileMorphRepresentative]]. + + "Catch edge case: Test tile" + self ifConditionNormalizeAndDo: [:conditionNode :trueNode :falseNode | | compound | + compound := StandardScriptingSystem new yesNoComplexOfTiles. + compound testPart insertTileRow: (conditionNode asTileSetForPlayer: aPlayer) after: 0. + compound yesPart insertTileRow: (trueNode withoutImplicitReturns asTileSetForPlayer: aPlayer) after: 0. + compound noPart insertTileRow: (falseNode withoutImplicitReturns asTileSetForPlayer: aPlayer) after: 0. + compound enforceTileColorPolicy; layoutChanged; fullBounds. + ^ compound]. + + "Otherwise, try to build a phrase tile" + self arguments size < 2 ifFalse: [^ self convertToTileError]. + + receiverType := #unknown. + argumentType := self arguments ifEmpty: [nil] ifNotEmpty: [#unknown]. + resultType := #unknown. + phrase := PhraseTileMorph new. + phrase + setOperator: self selector key + type: resultType + rcvrType: receiverType + argType: argumentType. + receiverTiles := self receiver asTileSetForPlayer: aPlayer. + receiverTiles size = 1 ifFalse: [^ self convertToTileError]. + phrase firstSubmorph + addMorph: receiverTiles first; + hResizing: #shrinkWrap; vResizing: #shrinkWrap. + self arguments ifNotEmpty: [ | argumentTiles | + argumentTiles := self arguments first asTileSetForPlayer: aPlayer. + argumentTiles size = 1 ifFalse: [^ self convertToTileError]. + phrase lastSubmorph + setType: argumentType; + changeTableLayout; + addMorph: argumentTiles first; + hResizing: #shrinkWrap; vResizing: #shrinkWrap]. + + ^ phrase + hResizing: #shrinkWrap; vResizing: #shrinkWrap; + yourself! Item was added: + ----- Method: MethodNode>>asScriptEditorFor: (in category '*Etoys-tiles') ----- + asScriptEditorFor: aPlayer + + | editor | + editor := ScriptEditorMorph new. + editor + playerScripted: aPlayer; + setMorph: aPlayer costume scriptName: self selector. + + (self asTileSetForPlayer: aPlayer) + withIndexDo: [:tile :index | + editor insertTileRow: {tile} after: index]. + editor + removeSpaces; + enforceTileColorPolicy; + scriptEdited; + allMorphsDo: #layoutChanged. + ^ editor! Item was added: + ----- Method: MethodWithInterface>>revertTileVersionFrom:for: (in category 'updating') ----- + revertTileVersionFrom: anEditor for: playerScripted + "Only for universal tiles." + + ^ self revertToLastSavedTileVersionFor: anEditor! Item was added: + ----- Method: ScriptEditorMorph>>convertToTileVersion (in category 'save & revert') ----- + convertToTileVersion + "The receiver, currently showing textual code, is asked to revert to the last-saved tile version" + + | aUserScript | + + self + hResizing: #shrinkWrap; + vResizing: #shrinkWrap. + aUserScript := playerScripted class userScriptForPlayer: playerScripted selector: scriptName. + aUserScript revertTileVersionFrom: self for: playerScripted. + self currentWorld startSteppingSubmorphsOf: self! Item was changed: ----- Method: ScriptEditorMorph>>offerScriptorMenu (in category 'other') ----- offerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer" | aMenu count | - self modernize. ActiveHand showTemporaryCursor: nil. + - Preferences eToyFriendly ifTrue: [^ self offerSimplerScriptorMenu]. + - aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addStayUpItem. "NB: the kids version in #offerSimplerScriptorMenu does not deploy the stay-up item" + - aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: { {'button to fire this script' translatedNoop. #tearOfButtonToFireScript}. {'fires per tick...' translatedNoop. #chooseFrequency}. #- }]. + + aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. - - aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. + + Preferences universalTiles ifFalse: [ + count := self savedTileVersionsCount. - - - Preferences universalTiles ifFalse: - [count := self savedTileVersionsCount. self showingMethodPane + ifFalse: [ "currently showing tiles" + aMenu add: 'show code textually' translated action: #showSourceInScriptor. + count > 0 ifTrue: [ + aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. + aMenu add: 'save this version' translated action: #saveScriptVersion ] + ifTrue: [ "current showing textual source" + aMenu add: 'convert to tile version' translated action: #toggleWhetherShowingTiles. + count > 0 ifTrue: + [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]] ]. + - ifFalse: "currently showing tiles" - [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. - count > 0 ifTrue: - [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. - aMenu add: 'save this version' translated action: #saveScriptVersion] - - ifTrue: "current showing textual source" - [count >= 1 ifTrue: - [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. - "aMenu addLine. self addGoldBoxItemsTo: aMenu." + - aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: ActiveWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. + - aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: ActiveWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. + - aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. + - aMenu addTranslatedList: { #-. {'open viewer' translatedNoop. #openObjectsViewer. 'open the viewer of the object to which this script belongs' translatedNoop}. {'detached method pane' translatedNoop. #makeIsolatedCodePane. 'open a little window that shows the Smalltalk code underlying this script.' translatedNoop}. #-. {'destroy this script' translatedNoop. #destroyScript} }. + + aMenu popUpInWorld: self currentWorld.! - - - aMenu popUpInWorld: self currentWorld. - ! Item was removed: - ----- Method: ScriptEditorMorph>>revertToTileVersion (in category 'save & revert') ----- - revertToTileVersion - "The receiver, currently showing textual code, is asked to revert to the last-saved tile version" - - | aUserScript | - - self - hResizing: #shrinkWrap; - vResizing: #shrinkWrap. - aUserScript := playerScripted class userScriptForPlayer: playerScripted selector: scriptName. - aUserScript revertToLastSavedTileVersionFor: self. - self currentWorld startSteppingSubmorphsOf: self! Item was changed: ----- Method: ScriptEditorMorph>>toggleWhetherShowingTiles (in category 'other') ----- toggleWhetherShowingTiles "Toggle between showing the method pane and showing the tiles pane" + - self showingMethodPane + ifFalse: [ "currently showing tiles" + self showSourceInScriptor ] + ifTrue: [ "currently showing textual source" + self convertToTileVersion ].! - ifFalse: "currently showing tiles" - [self showSourceInScriptor] - - ifTrue: "current showing textual source" - [Preferences universalTiles - ifTrue: [^ self revertToTileVersion]. - self savedTileVersionsCount >= 1 - ifTrue: - [(self userScriptObject lastSourceString = (playerScripted class sourceCodeAt: scriptName)) - ifFalse: - [(self confirm: - 'Caution -- this script was changed - textually; if you revert to tiles at this - point you will lose all the changes you - may have made textually. Do you - really want to do this?' translated) ifFalse: [^ self]]. - self revertToTileVersion] - ifFalse: - [Beeper beep]]! Item was added: + ----- Method: UserScript>>revertTileVersionFrom:for: (in category 'versions') ----- + revertTileVersionFrom: anEditor for: playerScripted + + anEditor removeAllButFirstSubmorph. + ((self playerClass >> self selector) decompile asTileSetForPlayer: playerScripted) + withIndexDo: [:tile :index | + anEditor insertTileRow: {tile} after: index]. + anEditor allMorphsDo: #layoutChanged. + anEditor showingMethodPane: false. + self becomeTextuallyCoded.! From commits at source.squeak.org Thu Sep 17 10:12:16 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 10:12:16 0000 Subject: [squeak-dev] The Inbox: EToys-ct.405.mcz Message-ID: Christoph Thiede uploaded a new version of EToys to project The Inbox: http://source.squeak.org/inbox/EToys-ct.405.mcz ==================== Summary ==================== Name: EToys-ct.405 Author: ct Time: 17 September 2020, 12:12:06.449813 pm UUID: db0ca850-9a21-1849-b3e2-bd9ce983b266 Ancestors: EToys-eem.400 Adds WatchMorph example method. This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against EToys-eem.400 =============== Item was added: + ----- Method: WatchMorph class>>example (in category 'examples') ----- + example + "WatchMorph example openInWorld" + + ^ (WatchMorph + fontName: #BitstreamVeraSerif + bgColor: Color lightGray + centerColor: Color red paler) + handsColor: Color grape; + toggleRoman; + yourself! From commits at source.squeak.org Thu Sep 17 10:23:28 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 10:23:28 0000 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz Message-ID: Christoph Thiede uploaded a new version of EToys to project The Inbox: http://source.squeak.org/inbox/EToys-ct.406.mcz ==================== Summary ==================== Name: EToys-ct.406 Author: ct Time: 17 September 2020, 12:23:18.233813 pm UUID: 0b76e3b6-1567-c542-8350-de5b7a28fcf2 Ancestors: EToys-eem.400 Minor Tetris refactorings. Expose action selectors for game control as part of the public Tetris protocol. This will be used to play/pause a tetris game from the outside. Recategorize some methods. This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against EToys-eem.400 =============== Item was changed: + ----- Method: Tetris class>>colors (in category 'constants') ----- - ----- Method: Tetris class>>colors (in category 'as yet unclassified') ----- colors ^{ Color r: 0.5 g: 0 b: 0. Color r: 0 g: 0.5 b: 0. Color r: 0 g: 0 b: 0.5. Color r: 0.5 g: 0.5 b: 0. Color r: 0.5 g: 0 b: 0.5. Color r: 0 g: 0.5 b: 0.5 } ! Item was changed: ----- Method: Tetris>>makeGameControls (in category 'initialization') ----- makeGameControls ^ self rowForButtons addMorph: (self buildButtonTarget: self label: 'Quit' translated selector: #delete help: 'quit' translated); addMorph: (self + buildButtonTarget: self - buildButtonTarget: board label: 'Pause' translated selector: #pause help: 'pause' translated); addMorph: (self + buildButtonTarget: self - buildButtonTarget: board label: 'New game' translated selector: #newGame help: 'new game' translated)! Item was added: + ----- Method: Tetris>>newGame (in category 'actions') ----- + newGame + + board newGame.! Item was added: + ----- Method: Tetris>>pause (in category 'actions') ----- + pause + + board pause.! Item was changed: + ----- Method: TetrisBlock class>>flipShapes: (in category 'support') ----- - ----- Method: TetrisBlock class>>flipShapes: (in category 'as yet unclassified') ----- flipShapes: anArray ^OrderedCollection new add: anArray; add: (anArray collect: [ :each | each y negated @ each x]); add: (anArray collect: [ :each | each x negated @ each y negated]); add: (anArray collect: [ :each | each y @ each x negated]); yourself ! Item was changed: + ----- Method: TetrisBlock class>>shapeChoices (in category 'constants') ----- - ----- Method: TetrisBlock class>>shapeChoices (in category 'as yet unclassified') ----- shapeChoices ^ ShapeChoices ifNil: [ ShapeChoices := { { { 0 @ 0 . 1 @ 0 . 0 @ 1 . 1 @ 1 } }. "square - one is sufficient here" self flipShapes: { 0 @ 0 . -1 @ 0 . 1 @ 0 . 0 @ -1 }. "T" { { 0 @ 0 . -1 @ 0 . 1 @ 0 . 2 @ 0 }. { 0 @ 0 . 0 @ -1 . 0 @ 1 . 0 @ 2 } "long - two are sufficient here" }. self flipShapes: { 0 @ 0 . 0 @ -1 . 0 @ 1 . 1 @ 1 }. "L" self flipShapes: { 0 @ 0 . 0 @ -1 . 0 @ 1 . -1 @ 1 }. "inverted L" self flipShapes: { 0 @ 0 . -1 @ 0 . 0 @ -1 . 1 @ -1 }. "S" self flipShapes: { 0 @ 0 . 1 @ 0 . 0 @ -1 . -1 @ -1 } "Z" }. ] ! Item was changed: + ----- Method: TetrisBoard>>cellSize (in category 'accessing') ----- - ----- Method: TetrisBoard>>cellSize (in category 'as yet unclassified') ----- cellSize ^12 at 12! Item was changed: + ----- Method: TetrisBoard>>originForCell: (in category 'accessing') ----- - ----- Method: TetrisBoard>>originForCell: (in category 'as yet unclassified') ----- originForCell: aPoint ^aPoint - (1 at 1) * self cellSize + self position ! From commits at source.squeak.org Thu Sep 17 10:39:01 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 10:39:01 0000 Subject: [squeak-dev] The Inbox: EToys-ct.407.mcz Message-ID: Christoph Thiede uploaded a new version of EToys to project The Inbox: http://source.squeak.org/inbox/EToys-ct.407.mcz ==================== Summary ==================== Name: EToys-ct.407 Author: ct Time: 17 September 2020, 12:38:51.188813 pm UUID: 1ba5c4b2-8ee7-dd46-9d86-5a7526c91629 Ancestors: EToys-eem.400 Activates Shout styling in EToys MethodMorph. Also includes recategorization of some selectors. Is the reference from EToys to ToolBuilder-Morphic okay? The dependency already existed before. =============== Diff against EToys-eem.400 =============== Item was changed: + PluggableTextMorphPlus subclass: #MethodMorph - PluggableTextMorph subclass: #MethodMorph instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Etoys-Scripting'! Item was changed: + ----- Method: MethodMorph class>>defaultNameStemForInstances (in category 'constants') ----- - ----- Method: MethodMorph class>>defaultNameStemForInstances (in category 'as yet unclassified') ----- defaultNameStemForInstances ^ 'Method' translatedNoop! Item was changed: ----- Method: MethodMorph>>initialize (in category 'initialization') ----- initialize "initialize the state of the receiver" super initialize. + self useDefaultStyler.! - ! Item was changed: + ----- Method: MethodMorph>>installRollBackButtons: (in category 'initialization') ----- - ----- Method: MethodMorph>>installRollBackButtons: (in category 'as yet unclassified') ----- installRollBackButtons: target | mine | "If I don't already have such a button, put one in at the upper right. Set its target to the furtherest enclosing book. Send chooseAndRevertToVersion when clicked. Stay in place via scrollBar install." mine := self submorphNamed: #chooseAndRevertToVersion ifNone: [nil]. mine ifNil: [mine := SimpleButtonMorph new. "mine height: mine height - 2." mine label: 'Roll Back'; cornerStyle: #square. mine color: Color white; borderColor: Color black. mine actionSelector: #chooseAndRevertToVersion. mine align: mine topRight with: (self findA: ScrollBar) topLeft +(1 at 1). self addMorphFront: mine. mine height: mine height - 5 "14"]. mine target: target.! Item was added: + ----- Method: MethodMorph>>okToStyle (in category 'testing') ----- + okToStyle + + styler ifNil: [^ false]. + styler + classOrMetaClass: self model selectedClassOrMetaClass; + environment: self model environment. + ^ true! From lecteur at zogotounga.net Thu Sep 17 10:42:22 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Thu, 17 Sep 2020 12:42:22 +0200 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: References: Message-ID: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> > This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). Cool! Stef From commits at source.squeak.org Thu Sep 17 10:49:07 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 10:49:07 0000 Subject: [squeak-dev] The Inbox: EToys-ct.408.mcz Message-ID: Christoph Thiede uploaded a new version of EToys to project The Inbox: http://source.squeak.org/inbox/EToys-ct.408.mcz ==================== Summary ==================== Name: EToys-ct.408 Author: ct Time: 17 September 2020, 12:48:54.350813 pm UUID: c530407c-bee4-a54c-8781-1ba7af10c1c1 Ancestors: EToys-eem.400 Improves MovingEyeMorph support for custom colors. =============== Diff against EToys-eem.400 =============== Item was added: + ----- Method: MovingEyeMorph class>>color:irisColor: (in category 'instance creation') ----- + color: aColor irisColor: anotherColor + + ^ self new color: aColor irisColor: anotherColor! Item was changed: ----- Method: MovingEyeMorph>>color: (in category 'accessing') ----- color: aColor super color: aColor. "Migrate old instances." inner color: Color transparent. + self keepIrisVisible.! - "Keep iris visible." - aColor = iris color - ifTrue: [iris borderWidth: 1; borderColor: aColor negated] - ifFalse: [iris borderWidth: 0].! Item was added: + ----- Method: MovingEyeMorph>>color:irisColor: (in category 'accessing') ----- + color: aColor irisColor: anotherColor + + self color: aColor. + self irisColor: anotherColor.! Item was added: + ----- Method: MovingEyeMorph>>defaultBorderWidth (in category 'accessing') ----- + defaultBorderWidth + + ^ 0! Item was changed: ----- Method: MovingEyeMorph>>irisColor: (in category 'accessing') ----- irisColor: aColor iris color: aColor. + self keepIrisVisible.! - "Keep iris visible." - aColor = self color - ifTrue: [iris borderWidth: 1; borderColor: aColor negated] - ifFalse: [iris borderWidth: 0].! Item was added: + ----- Method: MovingEyeMorph>>keepIrisVisible (in category 'private') ----- + keepIrisVisible + + self color = self irisColor + ifTrue: [ + iris borderWidth: 1; + borderColor: self color makeForegroundColor] + ifFalse: [ + iris borderWidth: 0].! From leves at caesar.elte.hu Thu Sep 17 10:49:28 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Thu, 17 Sep 2020 12:49:28 +0200 (CEST) Subject: [squeak-dev] The Inbox: Monticello-ul.727.mcz In-Reply-To: <1ae1c3850d3548dab48fe0da68bffac4@student.hpi.uni-potsdam.de> References: <1ae1c3850d3548dab48fe0da68bffac4@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Thu, 17 Sep 2020, Thiede, Christoph wrote: > > Very nice idea! :-) If I wanted to hijack an image, these URLRewriteRules would probably be my first approach - but security has never been an issue for Squeak, so I guess this is not a problem. If you can manipulate objects like URLRewriteRules, you have already hijacked the image. > > > Does this also fix the problem with the classic HTTP URLs returned by the update map or will we still need to patch them on the server side? The urls in the update map are used to create repositories. That's why simply changing the existing http repository urls to https doesn't suffice, because the updater will see the http urls and create new repositores with them if they are absent. So, this rewrite trick of Eliot works around the problem of the update maps as well. > > > And one last question regarding to your tests in the method comment of #rewriteUrl:forDownload:: Couldn't you put them into a real test case? I'm pretty sure that not everyone will run these out-commented tests manually, and > it would be a pity not to automate them. I left the possibility to change the rewrite rules to whatever you want. If there were a test case with those asserts in the comment, the test would start failing as soon as you changed the rules. Thought it's possible to create a test case which temporarily resets to the default rules and tests them. Levente > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von commits at source.squeak.org > Gesendet: Donnerstag, 17. September 2020 01:55:41 > An: squeak-dev at lists.squeakfoundation.org > Betreff: [squeak-dev] The Inbox: Monticello-ul.727.mcz   > Levente Uzonyi uploaded a new version of Monticello to project The Inbox: > http://source.squeak.org/inbox/Monticello-ul.727.mcz > > ==================== Summary ==================== > > Name: Monticello-ul.727 > Author: ul > Time: 17 September 2020, 1:54:51.056164 am > UUID: ad776836-42eb-4aa2-b788-f10dd9e07da2 > Ancestors: Monticello-cmm.726 > > MCHttpRepository changes: > - before up- or downloading files, transform the urls using #rewriteUrl:forDownload:. The default rules (see #urlRewriteRules) switch from http to https for source.squeak.org and squeaksource.com, and switch to the the static > smalltalkhub site for downloads. The url rewriting is Eliot's idea, but this implementation uses a list of rewrite rules instead of a dictionary-based mapping. > - use WebClient (and the shared webclient instance) for uploads too > - retry down/uploading with WebClient at most 3 times. This should work around the case where the underlying socket was closed but the state of the socket has not been updated in Squeak. > - use https in #creationTemplate > > =============== Diff against Monticello-cmm.726 =============== > > Item was changed: >   MCFileBasedRepository subclass: #MCHttpRepository >          instanceVariableNames: 'location user password readerCache indexed webClient' > +        classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' > -        classVariableNames: 'UseSharedWebClientInstance' >          poolDictionaries: '' >          category: 'Monticello-Repositories'! > > Item was changed: >   ----- Method: MCHttpRepository class>>creationTemplate (in category 'ui-support') ----- >   creationTemplate > +        ^self creationTemplateLocation: 'https://www.squeaksource.com/ProjectName' > -        ^self creationTemplateLocation: 'http://www.squeaksource.com/ProjectName' >                  user: 'squeak' >                  password: 'squeak' >   ! > > Item was added: > + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in category 'url rewrite') ----- > + rewriteUrl: aString forDownload: forDownload > + > +        | result | > +        result := aString. > +        self urlRewriteRules groupsDo: [ :regexString :replacement :downloadOnly | > +                (forDownload or: [ downloadOnly not ])  ifTrue: [ > +                        result := result copyWithRegex: regexString matchesReplacedWith: replacement ] ]. > +        ^result > +        > + " > + self assert:  'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). > + self assert:  'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). > + self assert:  'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). > + self assert:  'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). > + self assert:  'http://static.smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). > + self assert:  'http://smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). > + "! > > Item was added: > + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url rewrite') ----- > + urlRewriteRules > + > +        ^URLRewriteRules ifNil: [ > +                URLRewriteRules := #( > +                        "Regex to be replaced"  "static replacement string"     "download only" > +                        '^http\://source\.squeak\.org/' 'https://source.squeak.org/' false > +                        '^http\://squeaksource\.com/' 'https://squeaksource.com/' false > +                        '^http\://www.squeaksource\.com/' 'https://www.squeaksource.com/' false > +                        '^http\://smalltalkhub.com/' 'http://static.smalltalkhub.com/' true     > +                )  asOrderedCollection ]! > > Item was changed: >   ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- >   httpGet: url arguments: arguments >   > +        | urlString | > -        | progress urlString client  response result | > -        progress := [ :total :amount | > -                HTTPProgress new > -                        total: total; > -                        amount: amount; > -                        signal: 'Downloading...' ]. >          urlString := arguments >                  ifNil: [ url ] >                  ifNotNil: [ >                          | queryString | >                          queryString := WebUtils encodeUrlEncodedForm: arguments. >                          (url includes: $?) >                                  ifTrue: [ url, '&', queryString ] >                                  ifFalse: [ url, '?', queryString ] ]. > +        urlString := self class rewriteUrl: urlString forDownload: true. > +        ^self webClientDo: [ :client | > +                client > +                        username: self user; > +                        password: self password; > +                        httpGet: urlString do: [ :request | > +                                request > +                                        headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > +                                        headerAt: 'Connection' put: 'Keep-Alive'; > +                                        headerAt: 'Accept' put: '*/*' ] ]! > -        self class useSharedWebClientInstance ifTrue: [ > -                "Acquire webClient by atomically storing it in the client variable and setting its value to nil." > -                client := webClient. > -                webClient := nil ]. > -        client > -                ifNil: [ client := WebClient new ] > -                ifNotNil: [ > -                        "Attempt to avoid an error on windows by recreating the underlying stream." > -                        client isConnected ifFalse: [ client close ] ]. > -        response := client > -                username: self user; > -                password: self password; > -                httpGet: urlString do: [ :request | > -                        request > -                                headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > -                                headerAt: 'Connection' put: 'Keep-Alive'; > -                                headerAt: 'Accept' put: '*/*' ]. > -        result := (response code between: 200 and: 299) > -                ifFalse: [ > -                        response content. "Make sure content is read." > -                        nil ] > -                ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. > -        self class useSharedWebClientInstance > -                ifTrue: [ > -                        "Save the WebClient instance for reuse, but only if there is no client cached." > -                        webClient  > -                                ifNil: [ webClient := client ] > -                                ifNotNil: [ client close ] ] > -                ifFalse: [ client close ]. > -        result ifNil: [ NetworkError signal: 'Could not access ', location ]. > -        ^result! > > Item was added: > + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') ----- > + webClientDo: aBlock > + > +        | client attemptsLeft response result | > +        self class useSharedWebClientInstance ifTrue: [ > +                "Acquire webClient by atomically storing it in the client variable and setting its value to nil." > +                client := webClient. > +                webClient := nil ]. > +        > +        client > +                ifNil: [ client := WebClient new ] > +                ifNotNil: [ > +                        "Attempt to avoid an error by recreating the underlying stream." > +                        client isConnected ifFalse: [ client close ] ]. > +                > +        attemptsLeft := 3. > +        response := nil. > +        [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ > +                response := [ aBlock value: client ] > +                        on: NetworkError > +                        do: [ :error | > +                                attemptsLeft = 0 ifTrue: [ error pass ]. > +                                (3 - attemptsLeft) seconds asDelay wait. > +                                attemptsLeft := attemptsLeft - 1. > +                                nil "The response" ] ]. > +        > +        result := (response code between: 200 and: 299) > +                ifFalse: [ > +                        response content. "Make sure content is read." > +                        nil ] > +                ifTrue: [ > +                        (RWBinaryOrTextStream with: ( > +                                response contentWithProgress:  [ :total :amount | > +                                        HTTPProgress new > +                                                total: total; > +                                                amount: amount; > +                                                signal ])) reset ]. > + > +        self class useSharedWebClientInstance > +                ifTrue: [ > +                        "Save the WebClient instance for reuse, but only if there is no client cached." > +                        webClient  > +                                ifNil: [ webClient := client ] > +                                ifNotNil: [ client close ] ] > +                ifFalse: [ client close ]. > + > +        result ifNil: [ NetworkError signal: 'Could not access ', location ]. > +        ^result! > > Item was changed: >   ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in category 'private') ----- >   writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock > + > +        | stream urlString | > -        | stream response statusLine code | >          stream := RWBinaryOrTextStream on: String new. >          aBlock value: stream. > +        urlString := self urlForFileNamed: aString. > +        urlString := self class rewriteUrl: urlString forDownload: false. > +        ^self displayProgress: 'Uploading ', aString during: [ > +                self webClientDo: [ :client | > +                        client > +                                username: self user; > +                                password: self password; > +                                httpPut: urlString > +                                        content: stream contents > +                                        type: nil > +                                        do: [ :request | > +                                                request > +                                                        headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > +                                                        headerAt: 'Connection' put: 'Keep-Alive'; > +                                                        headerAt: 'Accept' put: '*/*'  ] ] ]! > -        self displayProgress: 'Uploading ', aString during:[ > -                response := HTTPSocket > -                                        httpPut: stream contents > -                                        to: (self urlForFileNamed: aString) > -                                        user: self user > -                                        passwd: self password. > -        ]. > -        "More robust handling of HTTP responses. Instead of enumerating > -        all possible return codes and http versions, do a quick parse" > -        (response beginsWith: 'HTTP/') ifTrue:[ > -                "Looks like an HTTP header, not some error message" > -                statusLine := response copyUpTo: Character cr. > -                code := [(statusLine findTokens: ' ') second asInteger] on: Error do:[]. > -        ]. > -        (code isInteger and:[code between: 200 and: 299]) > -                ifFalse:[self error: response].! > > > > From eliot.miranda at gmail.com Thu Sep 17 10:56:18 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Thu, 17 Sep 2020 03:56:18 -0700 Subject: [squeak-dev] The Trunk: ST80-mt.258.mcz In-Reply-To: References: Message-ID: Hi Marcel, > On Sep 17, 2020, at 12:15 AM, commits at source.squeak.org wrote: > > Marcel Taeumel uploaded a new version of ST80 to project The Trunk: > http://source.squeak.org/trunk/ST80-mt.258.mcz > > ==================== Summary ==================== > > Name: ST80-mt.258 > Author: mt > Time: 17 September 2020, 9:15:53.534726 am > UUID: c7ff9de0-a71a-4646-a639-211d94848498 > Ancestors: ST80-mt.257 > > Fixes processing of deferred actions in MVC. > > Note that windows in MVC delegate control to their sub-controllers, however, remaining the active controller from the project's world perspective (i.e. the controller manager). Thus, we have to process two deferred-action queues in for most cases. For example, the text field in a workspace involves a PluggableTextController, embedded in a StandardSystemController. Any do-it like "Project current addDeferredUIMessage: [...]" will add the message to the StandardSystemController's queue. > > =============== Diff against ST80-mt.257 =============== > > Item was changed: > ----- Method: Controller>>controlActivity (in category 'control defaults') ----- > controlActivity > "Pass control to the next control level (that is, to the Controller of a > subView of the receiver's view) if possible. It is sent by > Controller|controlLoop each time through the main control loop. It should > be redefined in a subclass if some other action is needed." > > + self processDeferredActions. > + Project current world activeController processDeferredActions. > - [self deferredActionQueue isEmpty] > - whileFalse: [deferredActionQueue next value]. > self controlToNextLevel! > > Item was added: > + ----- Method: Controller>>processDeferredActions (in category 'control defaults') ----- > + processDeferredActions > + > + [self deferredActionQueue isEmpty] > + whileFalse: [deferredActionQueue next value].! > > Item was changed: > ----- Method: ParagraphEditor>>normalActivity (in category 'controlling') ----- > normalActivity > self processKeyboard. > + self processMouseButtons. > + super normalActivity. > + ! > - self processMouseButtons! first regarding the fix, wouldn’t it make more sense to put Project current world activeController processDeferredActions. inside Controller>>processDeferredActions and have Controller>>controlActivity read Controller|controlLoop each time through the main control loop. It should be redefined in a subclass if some other action is needed." self processDeferredActions. self controlToNextLevel ? Second, doesn’t the two queue solution introduce a serious bug? Doesn’t it reorder deferred actions depending on which queue the action gets added to? Shouldn’t there in fact be a single SharedQueue for deferred actions? Is this bug in Morphic too? _,,,^..^,,,_ (phone) From marcel.taeumel at hpi.de Thu Sep 17 10:56:38 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 12:56:38 +0200 Subject: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body In-Reply-To: References: Message-ID: Hi Christoph, can you rephrase that? What's the issue here? Best, Marcel Am 17.09.2020 09:46:34 schrieb Thiede, Christoph : I think the screencast should be self-explaining: The problem might be the check in TextMorph >> #mouseDown:, but I'm not yet sure. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 22929 bytes Desc: not available URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 11:02:08 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 11:02:08 +0000 Subject: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body In-Reply-To: References: , Message-ID: Hi Marcel, clicking inside the text morph of the dialog window does not bring the dialog window to the front. I think this is quite confusing because the inspector window is still *displayed* at the front, but any click into it is processed by the text morph. Do you understand what I mean? :) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 17. September 2020 12:56:38 An: squeak-dev Betreff: Re: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body Hi Christoph, can you rephrase that? What's the issue here? Best, Marcel Am 17.09.2020 09:46:34 schrieb Thiede, Christoph : I think the screencast should be self-explaining: [cid:13d406ba-d3ab-4834-99d8-613351ad9e53] The problem might be the check in TextMorph >> #mouseDown:, but I'm not yet sure. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 22929 bytes Desc: pastedImage.png URL: From commits at source.squeak.org Thu Sep 17 11:05:51 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 11:05:51 0000 Subject: [squeak-dev] The Inbox: Graphics-ct.439.mcz Message-ID: A new version of Graphics was added to project The Inbox: http://source.squeak.org/inbox/Graphics-ct.439.mcz ==================== Summary ==================== Name: Graphics-ct.439 Author: ct Time: 17 September 2020, 1:05:42.958813 pm UUID: 523992a5-7677-d14c-a4b1-e2a676e92c6b Ancestors: Graphics-tonyg.438 Fixes transparency handling in Color >> #negated. Negating a color should not remove its alpha channel. =============== Diff against Graphics-tonyg.438 =============== Item was changed: ----- Method: Color>>negated (in category 'transformations') ----- negated "Return an RGB inverted color" ^Color r: 1.0 - self red g: 1.0 - self green + b: 1.0 - self blue + alpha: self alpha! - b: 1.0 - self blue! From commits at source.squeak.org Thu Sep 17 11:06:32 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 11:06:32 0000 Subject: [squeak-dev] The Inbox: GraphicsTests-ct.56.mcz Message-ID: A new version of GraphicsTests was added to project The Inbox: http://source.squeak.org/inbox/GraphicsTests-ct.56.mcz ==================== Summary ==================== Name: GraphicsTests-ct.56 Author: ct Time: 17 September 2020, 1:06:30.897813 pm UUID: 31ca4fe7-71ba-2d4e-9578-06cb5b8f3a8a Ancestors: GraphicsTests-pre.55 Adds regression test for Graphics-ct.439 (Color >> #negated). =============== Diff against GraphicsTests-pre.55 =============== Item was added: + ----- Method: ColorTest>>testNegated (in category 'tests') ----- + testNegated + + self assert: Color blue equals: Color yellow negated. + self assert: (Color blue alpha: 0.4) equals: (Color yellow alpha: 0.4) negated.! From marcel.taeumel at hpi.de Thu Sep 17 11:06:52 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 13:06:52 +0200 Subject: [squeak-dev] The Trunk: ST80-mt.258.mcz In-Reply-To: References: Message-ID: Hi Eliot. > inside Controller>>processDeferredActions and have Controller>>controlActivity read This would produce an endless recursion. But one could check with #== to avoid that. :-) > Shouldn’t there in fact be a single SharedQueue for deferred actions? Agreed. This would be a next step, now that we can see that this is actually happening. Might have been by accident so far.  > Is this bug in Morphic too? No, in Morphic, there is only single queue for all Morphic projects. It's a class variable in WorldState. Best, Marcel Am 17.09.2020 12:56:30 schrieb Eliot Miranda : Hi Marcel, > On Sep 17, 2020, at 12:15 AM, commits at source.squeak.org wrote: > > Marcel Taeumel uploaded a new version of ST80 to project The Trunk: > http://source.squeak.org/trunk/ST80-mt.258.mcz > > ==================== Summary ==================== > > Name: ST80-mt.258 > Author: mt > Time: 17 September 2020, 9:15:53.534726 am > UUID: c7ff9de0-a71a-4646-a639-211d94848498 > Ancestors: ST80-mt.257 > > Fixes processing of deferred actions in MVC. > > Note that windows in MVC delegate control to their sub-controllers, however, remaining the active controller from the project's world perspective (i.e. the controller manager). Thus, we have to process two deferred-action queues in for most cases. For example, the text field in a workspace involves a PluggableTextController, embedded in a StandardSystemController. Any do-it like "Project current addDeferredUIMessage: [...]" will add the message to the StandardSystemController's queue. > > =============== Diff against ST80-mt.257 =============== > > Item was changed: > ----- Method: Controller>>controlActivity (in category 'control defaults') ----- > controlActivity > "Pass control to the next control level (that is, to the Controller of a > subView of the receiver's view) if possible. It is sent by > Controller|controlLoop each time through the main control loop. It should > be redefined in a subclass if some other action is needed." > > + self processDeferredActions. > + Project current world activeController processDeferredActions. > - [self deferredActionQueue isEmpty] > - whileFalse: [deferredActionQueue next value]. > self controlToNextLevel! > > Item was added: > + ----- Method: Controller>>processDeferredActions (in category 'control defaults') ----- > + processDeferredActions > + > + [self deferredActionQueue isEmpty] > + whileFalse: [deferredActionQueue next value].! > > Item was changed: > ----- Method: ParagraphEditor>>normalActivity (in category 'controlling') ----- > normalActivity > self processKeyboard. > + self processMouseButtons. > + super normalActivity. > + ! > - self processMouseButtons! first regarding the fix, wouldn’t it make more sense to put Project current world activeController processDeferredActions. inside Controller>>processDeferredActions and have Controller>>controlActivity read Controller|controlLoop each time through the main control loop. It should be redefined in a subclass if some other action is needed." self processDeferredActions. self controlToNextLevel ? Second, doesn’t the two queue solution introduce a serious bug? Doesn’t it reorder deferred actions depending on which queue the action gets added to? Shouldn’t there in fact be a single SharedQueue for deferred actions? Is this bug in Morphic too? _,,,^..^,,,_ (phone) -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Thu Sep 17 11:09:34 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 13:09:34 +0200 Subject: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body In-Reply-To: References: <,> Message-ID: Hi Christoph, aha! We should add #handleMouseDown: like the one in SystemWindow to bring that dialog to the front. No need to change the TextMorph. Then we can remove "self comeToFront" from DialogWindow >> #mouseDown:. Best, Marcel Am 17.09.2020 13:02:17 schrieb Thiede, Christoph : Hi Marcel, clicking inside the text morph of the dialog window does not bring the dialog window to the front. I think this is quite confusing because the inspector window is still *displayed* at the front, but any click into it is processed by the text morph. Do you understand what I mean? :) Best, Christoph Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 17. September 2020 12:56:38 An: squeak-dev Betreff: Re: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body   Hi Christoph, can you rephrase that? What's the issue here? Best, Marcel Am 17.09.2020 09:46:34 schrieb Thiede, Christoph : I think the screencast should be self-explaining: The problem might be the check in TextMorph >> #mouseDown:, but I'm not yet sure. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 22929 bytes Desc: not available URL: From commits at source.squeak.org Thu Sep 17 11:11:54 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 11:11:54 0000 Subject: [squeak-dev] The Trunk: Morphic-mt.1682.mcz Message-ID: Marcel Taeumel uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-mt.1682.mcz ==================== Summary ==================== Name: Morphic-mt.1682 Author: mt Time: 17 September 2020, 1:11:51.011573 pm UUID: af1dbefd-44de-e849-b99d-a8d62204a9fc Ancestors: Morphic-dtl.1681 Fixes come-to-front bug in dialog windows. No behaves like system windows do. =============== Diff against Morphic-dtl.1681 =============== Item was added: + ----- Method: DialogWindow>>handleMouseDown: (in category 'events') ----- + handleMouseDown: event + "Always bring me to the front since I am modal" + + self comeToFront. + ^ super handleMouseDown: event! Item was changed: ----- Method: DialogWindow>>mouseDown: (in category 'events') ----- mouseDown: event self stopAutoTrigger. - - "Always bring me to the front since I am modal" - self comeToFront. (self containsPoint: event position) ifFalse: [ ^ self autoCancel ifTrue: [self cancelDialog] ifFalse: [self flash]]. event hand waitForClicksOrDrag: self event: event selectors: { nil. nil. nil. #startDrag: } threshold: HandMorph dragThreshold.! From marcel.taeumel at hpi.de Thu Sep 17 11:13:00 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 13:13:00 +0200 Subject: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body In-Reply-To: References: <,> Message-ID: Fixed in Morphic-mt.1682. Am 17.09.2020 13:09:34 schrieb Marcel Taeumel : Hi Christoph, aha! We should add #handleMouseDown: like the one in SystemWindow to bring that dialog to the front. No need to change the TextMorph. Then we can remove "self comeToFront" from DialogWindow >> #mouseDown:. Best, Marcel Am 17.09.2020 13:02:17 schrieb Thiede, Christoph : Hi Marcel, clicking inside the text morph of the dialog window does not bring the dialog window to the front. I think this is quite confusing because the inspector window is still *displayed* at the front, but any click into it is processed by the text morph. Do you understand what I mean? :) Best, Christoph Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 17. September 2020 12:56:38 An: squeak-dev Betreff: Re: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body   Hi Christoph, can you rephrase that? What's the issue here? Best, Marcel Am 17.09.2020 09:46:34 schrieb Thiede, Christoph : I think the screencast should be self-explaining: The problem might be the check in TextMorph >> #mouseDown:, but I'm not yet sure. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 22929 bytes Desc: not available URL: From Das.Linux at gmx.de Thu Sep 17 11:23:33 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Thu, 17 Sep 2020 13:23:33 +0200 Subject: [squeak-dev] Not getting all mail? In-Reply-To: References: Message-ID: <143C0EDA-0401-4AA9-8ECA-F8785EC61142@gmx.de> > On 17.09.2020, at 07:54, Jakob Reschke wrote: > > Hello, > > Over the past week I have observed people reply to some messages that I didn't receive from the list. For example, I do not have Christoph's message to which Eliot replied below. In one instance it even looked like I did not get the original post, but only a reply. I checked my spam folder, there are no Squeak mails in there. > > Do you experience the same? Or do I just always see cases where one person did not reply to the list, but the next one does again? Jep, same here. -t > > Kind regards, > Jakob > > > Eliot Miranda schrieb am Mo., 14. Sep. 2020, 21:49: > > > On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph wrote: > Which completion are you using? Autocompletion already behaves like this. > > > OComplete > > Best, > > Christoph > > Von: Squeak-dev im Auftrag von Eliot Miranda > Gesendet: Montag, 14. September 2020 04:45:30 > An: The general-purpose Squeak developers list > Betreff: [squeak-dev] autocomplete exit > > Hi, > > would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? > > _,,,^..^,,,_ > best, Eliot > > > From christoph.thiede at student.hpi.uni-potsdam.de Thu Sep 17 11:26:42 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Thu, 17 Sep 2020 06:26:42 -0500 (CDT) Subject: [squeak-dev] The Inbox: Graphics-ct.419.mcz In-Reply-To: References: <475380389fc84fd2b2f6cc9c0502e2c7@student.hpi.uni-potsdam.de> Message-ID: <1600342002587-0.post@n4.nabble.com> Hi Chris, hi all, I'd like to bring this topic up again. Personally, this tool has helped me in two projects until now. In particular, I plan to use it for the reconstruction of Objectland. Do you see any chance to merge this as is, or should we introduce a second version #exactPointAtFraction: or something similar? See also Graphics-ct.437 for nearly the same discussion. I did not meet a use case where rounding is important, but if you like so, we can implement two versions for this one, too. Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From commits at source.squeak.org Thu Sep 17 11:33:37 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 11:33:37 0000 Subject: [squeak-dev] The Trunk: ST80-mt.259.mcz Message-ID: Marcel Taeumel uploaded a new version of ST80 to project The Trunk: http://source.squeak.org/trunk/ST80-mt.259.mcz ==================== Summary ==================== Name: ST80-mt.259 Author: mt Time: 17 September 2020, 1:33:37.991573 pm UUID: 8717279a-d050-7a42-9989-bb849160b60d Ancestors: ST80-mt.258 Let there be only one queue of deferred actions for all MVC projects to mirror the behavior of Morphic projects. =============== Diff against ST80-mt.258 =============== Item was changed: Object subclass: #ControlManager instanceVariableNames: 'scheduledControllers activeController activeControllerProcess screenController newTopClicked' + classVariableNames: 'DeferredActionQueue' - classVariableNames: '' poolDictionaries: '' category: 'ST80-Controllers'! !ControlManager commentStamp: '' prior: 0! I represent the top level control over scheduling which controller of a view on the screen the user is actively using. ScheduledControllers is the global reference to an instance of me, the one attached to the Project currently being used.! Item was added: + ----- Method: ControlManager class>>addDeferredUIMessage: (in category 'class initialization') ----- + addDeferredUIMessage: valuableObject + "Arrange for valuableObject to be evaluated the next time the controlActivity in any controller becomes active." + + self deferredActionQueue nextPut: valuableObject.! Item was added: + ----- Method: ControlManager class>>deferredActionQueue (in category 'class initialization') ----- + deferredActionQueue + + ^DeferredActionQueue ifNil: [DeferredActionQueue := SharedQueue new]! Item was added: + ----- Method: ControlManager>>processDeferredActions (in category 'scheduling') ----- + processDeferredActions + + [self class deferredActionQueue isEmpty] + whileFalse: [self class deferredActionQueue next value].! Item was removed: - ----- Method: Controller>>addDeferredUIMessage: (in category 'basic control sequence') ----- - addDeferredUIMessage: valuableObject - "Arrange for valuableObject to be evaluated the next time the - controlActivity for this controller becomes active." - - self deferredActionQueue nextPut: valuableObject! Item was changed: ----- Method: Controller>>controlActivity (in category 'control defaults') ----- controlActivity "Pass control to the next control level (that is, to the Controller of a subView of the receiver's view) if possible. It is sent by Controller|controlLoop each time through the main control loop. It should be redefined in a subclass if some other action is needed." self processDeferredActions. - Project current world activeController processDeferredActions. self controlToNextLevel! Item was changed: ----- Method: Controller>>processDeferredActions (in category 'control defaults') ----- processDeferredActions + + Project current isMVC ifFalse: [^ self]. + Project current world processDeferredActions.! - - [self deferredActionQueue isEmpty] - whileFalse: [deferredActionQueue next value].! Item was changed: ----- Method: MVCProject>>addDeferredUIMessage: (in category 'scheduling & debugging') ----- addDeferredUIMessage: valuableObject "Arrange for valuableObject to be evaluated at a time when the user interface is in a coherent state." + self flag: #discuss. "mt: Why are deferred UI messages shared among all MVC projects?" + ControlManager addDeferredUIMessage: valuableObject.! - world activeController - ifNotNil: [:controller | controller addDeferredUIMessage: valuableObject]! From marcel.taeumel at hpi.de Thu Sep 17 11:34:40 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 13:34:40 +0200 Subject: [squeak-dev] The Trunk: ST80-mt.258.mcz In-Reply-To: References: Message-ID: Hi Eliot. See ST80-mt.259. Both MVC and Morphic projects now have the same behavior regarding deferred messages. Each kind of project has its own, single queue. Best, Marcel Am 17.09.2020 13:06:52 schrieb Marcel Taeumel : Hi Eliot. > inside Controller>>processDeferredActions and have Controller>>controlActivity read This would produce an endless recursion. But one could check with #== to avoid that. :-) > Shouldn’t there in fact be a single SharedQueue for deferred actions? Agreed. This would be a next step, now that we can see that this is actually happening. Might have been by accident so far.  > Is this bug in Morphic too? No, in Morphic, there is only single queue for all Morphic projects. It's a class variable in WorldState. Best, Marcel Am 17.09.2020 12:56:30 schrieb Eliot Miranda : Hi Marcel, > On Sep 17, 2020, at 12:15 AM, commits at source.squeak.org wrote: > > Marcel Taeumel uploaded a new version of ST80 to project The Trunk: > http://source.squeak.org/trunk/ST80-mt.258.mcz > > ==================== Summary ==================== > > Name: ST80-mt.258 > Author: mt > Time: 17 September 2020, 9:15:53.534726 am > UUID: c7ff9de0-a71a-4646-a639-211d94848498 > Ancestors: ST80-mt.257 > > Fixes processing of deferred actions in MVC. > > Note that windows in MVC delegate control to their sub-controllers, however, remaining the active controller from the project's world perspective (i.e. the controller manager). Thus, we have to process two deferred-action queues in for most cases. For example, the text field in a workspace involves a PluggableTextController, embedded in a StandardSystemController. Any do-it like "Project current addDeferredUIMessage: [...]" will add the message to the StandardSystemController's queue. > > =============== Diff against ST80-mt.257 =============== > > Item was changed: > ----- Method: Controller>>controlActivity (in category 'control defaults') ----- > controlActivity > "Pass control to the next control level (that is, to the Controller of a > subView of the receiver's view) if possible. It is sent by > Controller|controlLoop each time through the main control loop. It should > be redefined in a subclass if some other action is needed." > > + self processDeferredActions. > + Project current world activeController processDeferredActions. > - [self deferredActionQueue isEmpty] > - whileFalse: [deferredActionQueue next value]. > self controlToNextLevel! > > Item was added: > + ----- Method: Controller>>processDeferredActions (in category 'control defaults') ----- > + processDeferredActions > + > + [self deferredActionQueue isEmpty] > + whileFalse: [deferredActionQueue next value].! > > Item was changed: > ----- Method: ParagraphEditor>>normalActivity (in category 'controlling') ----- > normalActivity > self processKeyboard. > + self processMouseButtons. > + super normalActivity. > + ! > - self processMouseButtons! first regarding the fix, wouldn’t it make more sense to put Project current world activeController processDeferredActions. inside Controller>>processDeferredActions and have Controller>>controlActivity read Controller|controlLoop each time through the main control loop. It should be redefined in a subclass if some other action is needed." self processDeferredActions. self controlToNextLevel ? Second, doesn’t the two queue solution introduce a serious bug? Doesn’t it reorder deferred actions depending on which queue the action gets added to? Shouldn’t there in fact be a single SharedQueue for deferred actions? Is this bug in Morphic too? _,,,^..^,,,_ (phone) -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 17 11:39:17 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 11:39:17 0000 Subject: [squeak-dev] The Inbox: HelpSystem-Core-ct.133.mcz Message-ID: A new version of HelpSystem-Core was added to project The Inbox: http://source.squeak.org/inbox/HelpSystem-Core-ct.133.mcz ==================== Summary ==================== Name: HelpSystem-Core-ct.133 Author: ct Time: 17 September 2020, 1:39:15.362813 pm UUID: ccf30c3d-f0c8-584b-8c04-51e436bad97c Ancestors: HelpSystem-Core-mt.119 Adds HelpBrowser class >> #on: for conveniently instantiating but not opening a HelpBrowser. =============== Diff against HelpSystem-Core-mt.119 =============== Item was added: + ----- Method: HelpBrowser class>>on: (in category 'instance creation') ----- + on: aHelpTopic + + ^ self defaultHelpBrowser new + rootTopic: aHelpTopic; + yourself! Item was changed: ----- Method: HelpBrowser class>>openOn: (in category 'instance creation') ----- openOn: aHelpTopic + "Open the receiver on the given help topic or any other object that can be transformed into + a help topic by sending #asHelpTopic." + + ^ (self on: aHelpTopic) open! - "Open the receiver on the given help topic or any other object that can be transformed into - a help topic by sending #asHelpTopic." - - ^(self defaultHelpBrowser new) - rootTopic: aHelpTopic; - open! From commits at source.squeak.org Thu Sep 17 11:47:02 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 11:47:02 0000 Subject: [squeak-dev] The Inbox: Monticello-ct.728.mcz Message-ID: A new version of Monticello was added to project The Inbox: http://source.squeak.org/inbox/Monticello-ct.728.mcz ==================== Summary ==================== Name: Monticello-ct.728 Author: ct Time: 17 September 2020, 1:47:00.019813 pm UUID: 7d44995a-f19b-ee49-b16b-c710c9f2bce6 Ancestors: Monticello-cmm.726 Implements comparing a Monticello version against any of its ancestors. Not absolutely sure about the code quality, but it's a relevant feature for me (until now I always used to consult the Nabble archive). Also improves multilingual support. =============== Diff against Monticello-cmm.726 =============== Item was changed: ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- httpGet: url arguments: arguments | progress urlString client response result | progress := [ :total :amount | HTTPProgress new total: total; amount: amount; + signal: 'Downloading...' translated ]. - signal: 'Downloading...' ]. urlString := arguments ifNil: [ url ] ifNotNil: [ | queryString | queryString := WebUtils encodeUrlEncodedForm: arguments. (url includes: $?) ifTrue: [ url, '&', queryString ] ifFalse: [ url, '?', queryString ] ]. self class useSharedWebClientInstance ifTrue: [ "Acquire webClient by atomically storing it in the client variable and setting its value to nil." client := webClient. webClient := nil ]. client ifNil: [ client := WebClient new ] ifNotNil: [ "Attempt to avoid an error on windows by recreating the underlying stream." client isConnected ifFalse: [ client close ] ]. response := client username: self user; password: self password; httpGet: urlString do: [ :request | request headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; headerAt: 'Connection' put: 'Keep-Alive'; headerAt: 'Accept' put: '*/*' ]. result := (response code between: 200 and: 299) ifFalse: [ response content. "Make sure content is read." nil ] ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. self class useSharedWebClientInstance ifTrue: [ "Save the WebClient instance for reuse, but only if there is no client cached." webClient ifNil: [ webClient := client ] ifNotNil: [ client close ] ] ifFalse: [ client close ]. + result ifNil: [ NetworkError signal: ('Could not access {1} (Code {2})' translated format: {location. response code}) ]. - result ifNil: [ NetworkError signal: 'Could not access ', location ]. ^result! Item was changed: ----- Method: MCRepositoryInspector>>versionListMenu: (in category 'morphic ui') ----- versionListMenu: aMenu + 1 to: self orderSpecs size do: [ :index | aMenu addUpdating: #orderString: target: self selector: #order: argumentList: { index } ]. aMenu addLine. + aMenu add: 'Changes against ...' translated action: [ + | ri versions seen | - aMenu add: 'Changes against ...' action: [| ri | ri := aMenu defaultTarget. + versions := ri versionList + collect: [:name | MCVersionName on: name] + as: OrderedCollection. + seen := versions asSet. + self version info breadthFirstAncestors do: [:ancestor | + (seen includes: ancestor name) ifFalse: [ + versions add: ancestor. + seen add: ancestor name]]. (UIManager default + chooseFrom: (versions collect: [:version | version name]) + values: versions + title: 'Select version to show patch against ...' translated) ifNotNil: [:name | + | target base | - chooseFrom: ri versionList - values: ri versionList - title: 'Select version to show patch against ...') ifNotNil: [:name | - | versionName target base | - versionName := MCVersionName on: name. target := ri repository versionNamed: ri versionInfo name. + base := name isString + ifTrue: [ri repository versionNamed: name] + ifFalse: [ri version workingCopy repositoryGroup versionWithInfo: name]. - base := aMenu defaultTarget repository versionNamed: versionName. (MCPatchBrowser forPatch: (target snapshot patchRelativeToBase: base snapshot)) + showLabelled: ('Changes from {1} to {2}' translated format: {name. ri versionInfo name})]]. - showLabelled: 'Changes from ', versionName, ' to ', ri versionInfo name]]. ^aMenu! Item was added: + ----- Method: MCVersionName>>name (in category 'as yet unclassified') ----- + name + + ^ self! From commits at source.squeak.org Thu Sep 17 12:17:26 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 12:17:26 0000 Subject: [squeak-dev] The Inbox: ToolBuilder-Morphic-ct.264.mcz Message-ID: A new version of ToolBuilder-Morphic was added to project The Inbox: http://source.squeak.org/inbox/ToolBuilder-Morphic-ct.264.mcz ==================== Summary ==================== Name: ToolBuilder-Morphic-ct.264 Author: ct Time: 17 September 2020, 2:17:24.709813 pm UUID: f0cb613b-9652-a146-9d77-016577d87700 Ancestors: ToolBuilder-Morphic-mt.263 Initially refresh window color when building a window. While this also happens later #openInWorld, the call is missing is something else is done with the window instead, e.g. #openInWindow or #imageForm. =============== Diff against ToolBuilder-Morphic-mt.263 =============== Item was changed: ----- Method: MorphicToolBuilder>>buildPluggableWindow: (in category 'widgets required') ----- buildPluggableWindow: aSpec | widget | aSpec layout == #proportional ifFalse:[ "This needs to be implemented - probably by adding a single pane and then the rest" ^self error: 'Not implemented'. ]. widget := (self windowClassFor: aSpec) new. self register: widget id: aSpec name. widget model: aSpec model. "Set child dependent layout properties." widget wantsPaneSplitters: (aSpec wantsResizeHandles ifNil: [true]). self setLayoutHintsFor: widget spec: aSpec. widget layoutInset: (aSpec padding ifNil: [ProportionalSplitterMorph gripThickness]). widget cellGap: (aSpec spacing ifNil: [ProportionalSplitterMorph gripThickness]). "Now create the children." panes := OrderedCollection new. aSpec children isSymbol ifTrue: [ widget getChildrenSelector: aSpec children. widget update: aSpec children] ifFalse: [ self buildAll: aSpec children in: widget]. widget setUpdatablePanesFrom: panes. aSpec label ifNotNil: [:label| label isSymbol ifTrue:[widget getLabelSelector: label] ifFalse:[widget setLabel: label]]. aSpec multiWindowStyle notNil ifTrue: [widget savedMultiWindowState: (SavedMultiWindowState on: aSpec model)]. widget closeWindowSelector: aSpec closeAction. self buildHelpFor: widget spec: aSpec. widget bounds: (RealEstateAgent initialFrameFor: widget initialExtent: (aSpec extent ifNil:[widget initialExtent]) world: self currentWorld). + widget refreshWindowColor. + ^ widget! From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 12:19:20 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 12:19:20 +0000 Subject: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body In-Reply-To: References: <, > , Message-ID: <832334c320804ba98c6b2cf2115e0cb9@student.hpi.uni-potsdam.de> Thank you for the fast fix! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 17. September 2020 13:13:00 An: squeak-dev Betreff: Re: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body Fixed in Morphic-mt.1682. Am 17.09.2020 13:09:34 schrieb Marcel Taeumel : Hi Christoph, aha! We should add #handleMouseDown: like the one in SystemWindow to bring that dialog to the front. No need to change the TextMorph. Then we can remove "self comeToFront" from DialogWindow >> #mouseDown:. Best, Marcel Am 17.09.2020 13:02:17 schrieb Thiede, Christoph : Hi Marcel, clicking inside the text morph of the dialog window does not bring the dialog window to the front. I think this is quite confusing because the inspector window is still *displayed* at the front, but any click into it is processed by the text morph. Do you understand what I mean? :) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 17. September 2020 12:56:38 An: squeak-dev Betreff: Re: [squeak-dev] [BUG] Dialog window does not keyboard focus together with its body Hi Christoph, can you rephrase that? What's the issue here? Best, Marcel Am 17.09.2020 09:46:34 schrieb Thiede, Christoph : I think the screencast should be self-explaining: [cid:13d406ba-d3ab-4834-99d8-613351ad9e53] The problem might be the check in TextMorph >> #mouseDown:, but I'm not yet sure. Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 22929 bytes Desc: pastedImage.png URL: From commits at source.squeak.org Thu Sep 17 13:38:44 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 13:38:44 0000 Subject: [squeak-dev] The Trunk: Morphic-mt.1683.mcz Message-ID: Marcel Taeumel uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-mt.1683.mcz ==================== Summary ==================== Name: Morphic-mt.1683 Author: mt Time: 17 September 2020, 3:38:41.086573 pm UUID: 146f304e-7d94-624b-9d19-d193c3522b86 Ancestors: Morphic-mt.1682 Fixes a bug in the definition of height-for-width and width-for-height layout behavior. =============== Diff against Morphic-mt.1682 =============== Item was changed: ----- Method: Morph>>changesHeightForWidth (in category 'layout-menu') ----- changesHeightForWidth + "When both axes are on #spaceFill, the receiver (morph) usually adapts any inner height-for-width layout unless there is more space to fill. Since we cannot know in advance, we assume that an extra layout run might be necessary." + ^ (self hResizing == #spaceFill and: [self vResizing == #spaceFill]) + or: [(self hResizing ~= #shrinkWrap + and: [self vResizing = #shrinkWrap]) + and: [self wrapDirection ~= #none]]! - ^ (self hResizing ~= #shrinkWrap - and: [self vResizing = #shrinkWrap]) - and: [self wrapDirection ~= #none]! Item was changed: ----- Method: Morph>>changesWidthForHeight (in category 'layout-menu') ----- changesWidthForHeight + "When both axes are on #spaceFill, the receiver (morph) usually adapts any inner height-for-width layout unless there is more space to fill. Since we cannot know in advance, we assume that an extra layout run might be necessary." + + ^ (self hResizing == #spaceFill and: [self vResizing == #spaceFill]) + or: [(self hResizing = #shrinkWrap + and: [self vResizing ~= #shrinkWrap]) + and: [self wrapDirection ~= #none]]! - - ^ (self hResizing = #shrinkWrap - and: [self vResizing ~= #shrinkWrap]) - and: [self wrapDirection ~= #none]! From commits at source.squeak.org Thu Sep 17 13:41:45 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 13:41:45 0000 Subject: [squeak-dev] The Trunk: MorphicTests-mt.65.mcz Message-ID: Marcel Taeumel uploaded a new version of MorphicTests to project The Trunk: http://source.squeak.org/trunk/MorphicTests-mt.65.mcz ==================== Summary ==================== Name: MorphicTests-mt.65 Author: mt Time: 17 September 2020, 3:41:47.157573 pm UUID: 50b2cc27-06d5-5d40-936d-7b029ddc01b1 Ancestors: MorphicTests-mt.64, MorphicTests-ct.61 Updates layout tests following MorphicTests-nice.61 (inbox->treated). Complements Morphic-mt.1683. Merges MorphicTests-ct.61 (inbox->trunk). =============== Diff against MorphicTests-mt.64 =============== Item was changed: TestCase subclass: #MorphLayoutTest + instanceVariableNames: 'reset' - instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'MorphicTests-Layouts'! Item was removed: - ----- Method: MorphLayoutTest>>testScrollPaneBarUpdate (in category 'tests') ----- - testScrollPaneBarUpdate - - | oldPreferences | - oldPreferences := ScrollPane useRetractableScrollBars. - [| child container | - ScrollPane useRetractableScrollBars: false. - container := ScrollPane new color: Color green; extent: 300 @ 300; showVScrollBarOnlyWhenNeeded; showHScrollBarOnlyWhenNeeded. - container scroller addMorphBack: (child := Morph new color: Color red; extent: 100 @ 100). - - self ensureLayout: container. - self assert: container hScrollBar owner isNil. - self assert: container vScrollBar owner isNil. - - child extent: 400 @ 100. - self ensureLayout: container. - self assert: container hScrollBar owner notNil. - self assert: container vScrollBar owner isNil. - - child extent: 400 @ 400. - self ensureLayout: container. - self assert: container hScrollBar owner notNil. - self assert: container hScrollBar owner notNil] - ensure: [ScrollPane useRetractableScrollBars: oldPreferences]! Item was added: + ----- Method: MorphicEventTests>>test02MouseOver (in category 'tests') ----- + test02MouseOver + + | m1 m2 | + m1 := MorphForEventTests new. + m2 := MorphForEventTests new. + + m1 extent: 20 at 20; topLeft: 0 at 0. + m2 extent: 20 at 20; topLeft: 40 at 0. + + m1 openInWorld: world. + m2 openInWorld: world. + + hand handleEvent: (self redMouseDownAt: m1 center). + hand handleEvent: (self redMouseUpAt: m1 center). + hand handleEvent: (self redMouseDownAt: m2 center). + hand handleEvent: (self redMouseUpAt: m2 center). + + self + checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp mouseLeave) + forEvents: m1 eventsDuringBubble + ignoreMouseOver: true. + + self + checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp) + forEvents: m2 eventsDuringBubble + ignoreMouseOver: true. + ! Item was removed: - ----- Method: MorphicEventTests>>test02MouserOver (in category 'tests') ----- - test02MouserOver - - | m1 m2 | - m1 := MorphForEventTests new. - m2 := MorphForEventTests new. - - m1 extent: 20 at 20; topLeft: 0 at 0. - m2 extent: 20 at 20; topLeft: 40 at 0. - - m1 openInWorld: world. - m2 openInWorld: world. - - hand handleEvent: (self redMouseDownAt: m1 center). - hand handleEvent: (self redMouseUpAt: m1 center). - hand handleEvent: (self redMouseDownAt: m2 center). - hand handleEvent: (self redMouseUpAt: m2 center). - - self - checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp mouseLeave) - forEvents: m1 eventsDuringBubble - ignoreMouseOver: true. - - self - checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp) - forEvents: m2 eventsDuringBubble - ignoreMouseOver: true. - ! Item was changed: TestCase subclass: #TableLayoutTest + instanceVariableNames: 'container reset' - instanceVariableNames: 'container' classVariableNames: '' poolDictionaries: '' category: 'MorphicTests-Layouts'! Item was added: + ----- Method: TableLayoutTest>>setUp (in category 'running') ----- + setUp + + super setUp. + + reset := { + ([:enable | [self useRetractableScrollBars: enable]] + value: self useRetractableScrollBars) + in: [:block | self useRetractableScrollBars: false]. + }.! Item was added: + ----- Method: TableLayoutTest>>tearDown (in category 'running') ----- + tearDown + + reset do: #value. + super tearDown.! Item was changed: ----- Method: TableLayoutTest>>testPluggableTextMorph (in category 'tests') ----- testPluggableTextMorph + | ptm | - | oldPreferences | - oldPreferences := ScrollPane useRetractableScrollBars. - [| ptm | - ScrollPane useRetractableScrollBars: false. ptm := PluggableTextMorph new extent: 100 at 50; setText: 'Hello World!! Hello World!! Hello World!! Hello World!!'; wrapFlag: true. + - container := self newContainer addMorphBack: ptm. self ensureLayout: container. self assert: 100 at 50 equals: container extent. self assert: ptm vIsScrollbarShowing. self deny: ptm hIsScrollbarShowing. "Make it a one-liner." ptm wrapFlag: false. self ensureLayout: container. self deny: ptm vIsScrollbarShowing. self assert: ptm hIsScrollbarShowing. "Make it a one-liner without the horizontal scrollbar." ptm hideScrollBarsIndefinitely. self ensureLayout: container. self deny: ptm vIsScrollbarShowing. + self deny: ptm hIsScrollbarShowing. - self deny: ptm hIsScrollbarShowing] - ensure: [ScrollPane useRetractableScrollBars: oldPreferences]. ! Item was changed: ----- Method: TableLayoutTest>>testPluggableTextMorphScrollBarNotNeeded (in category 'tests') ----- testPluggableTextMorphScrollBarNotNeeded "The entire test might fit if the scroll bar would only disappear..." + | ptm | - | oldPreferences | - oldPreferences := ScrollPane useRetractableScrollBars. - [| ptm | - ScrollPane useRetractableScrollBars: false. ptm := PluggableTextMorph new extent: 100 at 50; setText: 'Hello World!! Hello World!! \\\ Hello World!! Hello World!!' withCRs. container := self newContainer addMorphBack: ptm. "Make it fit exactly first." ptm hResizing: #shrinkWrap; vResizing: #shrinkWrap. self ensureLayout: container. ptm hResizing: #rigid; vResizing: #rigid. ptm wrapFlag: true. "No scrollbars required." self ensureLayout: container. self deny: ptm vIsScrollbarShowing. "It wraps immediately." ptm width: ptm width - 5. self ensureLayout: container. self assert: ptm vIsScrollbarShowing. "No scrollbars required." ptm width: ptm width + 5. self ensureLayout: container. + self deny: ptm vIsScrollbarShowing.! - self deny: ptm vIsScrollbarShowing] - ensure: [ScrollPane useRetractableScrollBars: oldPreferences]! Item was added: + ----- Method: TableLayoutTest>>testScrollPaneBarUpdate (in category 'tests - scroll panes') ----- + testScrollPaneBarUpdate + + | child container | + container := ScrollPane new color: Color green; extent: 300 @ 300; showVScrollBarOnlyWhenNeeded; showHScrollBarOnlyWhenNeeded. + container scroller addMorphBack: (child := Morph new color: Color red; extent: 100 @ 100). + + self ensureLayout: container. + self assert: container hScrollBar owner isNil. + self assert: container vScrollBar owner isNil. + + child extent: 400 @ 100. + self ensureLayout: container. + self assert: container hScrollBar owner notNil. + self assert: container vScrollBar owner isNil. + + child extent: 400 @ 400. + self ensureLayout: container. + self assert: container hScrollBar owner notNil. + self assert: container hScrollBar owner notNil! Item was changed: ----- Method: TableLayoutTest>>testScrollPaneShrinkWrap (in category 'tests') ----- testScrollPaneShrinkWrap + | scroll scrollContent | - | oldPreferences | - oldPreferences := ScrollPane useRetractableScrollBars. - [| scroll scrollContent | - ScrollPane useRetractableScrollBars: false. container := self newContainer vResizing: #rigid; addMorphBack: (self newMorph extent: 50 @ 50); addMorphBack: (scroll := ScrollPane new hResizing: #shrinkWrap; vResizing: #spaceFill; showVScrollBarOnlyWhenNeeded; hideHScrollBarIndefinitely). " shrinkWrap the horizontal axis but scroll vertically " scroll scroller layoutPolicy: TableLayout new; addMorphBack: (scrollContent := self newMorph extent: 200 @ 500). container extent: 1 @ 300. self ensureLayout: container. self assert: container left = (container layoutChanged; fullBounds; left). "Do not be jumpy." self assert: (200 + scroll scrollBarThickness + scroll borderWidth) @ 300 equals: scroll extent. scrollContent extent: 300 @ 500. self ensureLayout: container. + self assert: (300 + scroll scrollBarThickness + scroll borderWidth) @ 300 equals: scroll extent! - self assert: (300 + scroll scrollBarThickness + scroll borderWidth) @ 300 equals: scroll extent] - ensure: [ScrollPane useRetractableScrollBars: oldPreferences]. - ! Item was added: + ----- Method: TableLayoutTest>>testScrollerFill (in category 'tests - scroll panes') ----- + testScrollerFill + "A scroll pane's scroller (i.e., the transform morph) has always #spaceFill behavior within the scroll pane's layout. Thus, submorphs (here: title and content) can themselves be #spaceFill. Embed a text morph to check height-for-width compatibility." + + | content title | + container := ScrollPane new. + + container scroller + layoutPolicy: TableLayout new; + color: Color random; + addMorphBack: (title := TextMorph new hResizing: #spaceFill; contents: 'Here comes an interesting title'); + addMorphBack: (content := self newMorph extent: 400 @ 400; hResizing: #spaceFill). + container extent: 50 @ 50. "Pick an extent so that the title must wrap!!" + self ensureLayout: container. + + "container openInHand." + + container extent: 500 @ 500. + self ensureLayout: container. + self assert: 500 @ 500 equals: container extent. + self assert: 500 - (container borderWidth * 2) @ 400 equals: content extent. + + container extent: 300 @ 300. + self ensureLayout: container. + self assert: 300 @ 300 equals: container extent. + self assert: 300 - container borderWidth - container scrollBarThickness @ 400 equals: content extent! Item was added: + ----- Method: TableLayoutTest>>testScrollerFillWithContainer (in category 'tests - scroll panes') ----- + testScrollerFillWithContainer + "A scroll pane's scroller (i.e., the transform morph) has always #spaceFill behavior within the scroll pane's layout. Thus, submorphs (here: title and content) can themselves be #spaceFill. Embed a text morph to check height-for-width compatibility. Add an extra container between scroller and title/content." + + | content title | + container := ScrollPane new. + + container scroller + layoutPolicy: TableLayout new; + addMorphBack: (self newContainer + hResizing: #spaceFill; + vResizing: #spaceFill; + listDirection: #topToBottom; + addMorphBack: (title := TextMorph new hResizing: #spaceFill; contents: 'Here comes an interesting title'); + addMorphBack: (content := self newMorph extent: 400 @ 400; hResizing: #spaceFill)). + container extent: 50 @ 50. "Pick an extent so that the title must wrap!!" + self ensureLayout: container. + + "container openInHand." + + container extent: 500 @ 500. + self ensureLayout: container. + self assert: 500 @ 500 equals: container extent. + self assert: 500 - (container borderWidth * 2) @ 400 equals: content extent. + + container extent: 300 @ 300. + self ensureLayout: container. + self assert: 300 @ 300 equals: container extent. + self assert: 300 - container borderWidth - container scrollBarThickness @ 400 equals: content extent! Item was changed: ----- Method: TableLayoutTest>>testShrinkWrapScrollPaneAlwaysShowBars (in category 'tests') ----- testShrinkWrapScrollPaneAlwaysShowBars + | scroll scrollContent | - | oldPreferences | - oldPreferences := ScrollPane useRetractableScrollBars. - [| scroll scrollContent | - ScrollPane useRetractableScrollBars: false. container := self newContainer vResizing: #shrinkWrap; hResizing: #shrinkWrap; addMorphBack: (scroll := ScrollPane new hResizing: #shrinkWrap; vResizing: #shrinkWrap; alwaysShowHScrollBar; alwaysShowVScrollBar). scroll scroller layoutPolicy: TableLayout new; addMorphBack: (scrollContent := self newMorph extent: 300 @ 300). self ensureLayout: container. + self assert: (300 @ 300) + scroll scrollBarThickness + scroll borderWidth equals: container extent! - self assert: (300 @ 300) + scroll scrollBarThickness + scroll borderWidth equals: container extent] - ensure: [ScrollPane useRetractableScrollBars: oldPreferences]. - ! Item was changed: ----- Method: TableLayoutTest>>testSidebarAndScrollingView (in category 'tests - example layouts') ----- testSidebarAndScrollingView " construct a container that has a fixed size sidebar on the left and a scrolling window that adapts flexibly to the container's size " + | scrolling sidebar content title | - | oldPreferences | - oldPreferences := ScrollPane useRetractableScrollBars. - [| scrolling sidebar content title | - ScrollPane useRetractableScrollBars: false. container := self newContainer addMorphBack: (sidebar := self newMorph width: 200; hResizing: #rigid; vResizing: #spaceFill); addMorphBack: (scrolling := ScrollPane new hResizing: #spaceFill; vResizing: #spaceFill). scrolling scroller layoutPolicy: TableLayout new; addMorphBack: (self newContainer hResizing: #spaceFill; vResizing: #spaceFill; listDirection: #topToBottom; addMorphBack: (title := TextMorph new hResizing: #spaceFill; contents: 'Here comes a title'); addMorphBack: (content := self newMorph extent: 400 @ 400; hResizing: #spaceFill)). + self ensureLayout: container. + "container openInHand." container extent: 500 @ 500. self ensureLayout: container. self assert: 200 @ 500 equals: sidebar extent. self assert: 300 @ 500 equals: scrolling extent. self assert: 300 - (scrolling borderWidth * 2) @ 400 equals: content extent. container extent: 300 @ 300. self ensureLayout: container. self assert: 200 @ 300 equals: sidebar extent. self assert: 100 @ 300 equals: scrolling extent. + self assert: 100 - scrolling borderWidth - scrolling scrollBarThickness @ 400 equals: content extent! - self assert: 100 - scrolling borderWidth - scrolling scrollBarThickness @ 400 equals: content extent] - ensure: [ScrollPane useRetractableScrollBars: oldPreferences]! Item was added: + ----- Method: TableLayoutTest>>useRetractableScrollBars (in category 'running') ----- + useRetractableScrollBars + + ^ ScrollPane classPool at: #UseRetractableScrollBars! Item was added: + ----- Method: TableLayoutTest>>useRetractableScrollBars: (in category 'running') ----- + useRetractableScrollBars: aBoolean + "Re-implemented to avoid triggering #allSubInstancesDo:." + + ScrollPane classPool at: #UseRetractableScrollBars put: aBoolean.! From commits at source.squeak.org Thu Sep 17 13:42:32 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 13:42:32 0000 Subject: [squeak-dev] The Trunk: MorphicTests-ct.61.mcz Message-ID: Marcel Taeumel uploaded a new version of MorphicTests to project The Trunk: http://source.squeak.org/trunk/MorphicTests-ct.61.mcz ==================== Summary ==================== Name: MorphicTests-ct.61 Author: ct Time: 20 March 2020, 8:07:43.181237 pm UUID: e1d09ce3-9d2a-6440-ad10-b469b1163316 Ancestors: MorphicTests-mt.60 Renames misspelled test selector. =============== Diff against MorphicTests-mt.60 =============== Item was added: + ----- Method: MorphicEventTests>>test02MouseOver (in category 'tests') ----- + test02MouseOver + + | m1 m2 | + m1 := MorphForEventTests new. + m2 := MorphForEventTests new. + + m1 extent: 20 at 20; topLeft: 0 at 0. + m2 extent: 20 at 20; topLeft: 40 at 0. + + m1 openInWorld: world. + m2 openInWorld: world. + + hand handleEvent: (self redMouseDownAt: m1 center). + hand handleEvent: (self redMouseUpAt: m1 center). + hand handleEvent: (self redMouseDownAt: m2 center). + hand handleEvent: (self redMouseUpAt: m2 center). + + self + checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp mouseLeave) + forEvents: m1 eventsDuringBubble + ignoreMouseOver: true. + + self + checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp) + forEvents: m2 eventsDuringBubble + ignoreMouseOver: true. + ! Item was removed: - ----- Method: MorphicEventTests>>test02MouserOver (in category 'tests') ----- - test02MouserOver - - | m1 m2 | - m1 := MorphForEventTests new. - m2 := MorphForEventTests new. - - m1 extent: 20 at 20; topLeft: 0 at 0. - m2 extent: 20 at 20; topLeft: 40 at 0. - - m1 openInWorld: world. - m2 openInWorld: world. - - hand handleEvent: (self redMouseDownAt: m1 center). - hand handleEvent: (self redMouseUpAt: m1 center). - hand handleEvent: (self redMouseDownAt: m2 center). - hand handleEvent: (self redMouseUpAt: m2 center). - - self - checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp mouseLeave) - forEvents: m1 eventsDuringBubble - ignoreMouseOver: true. - - self - checkEventOrder: #(mouseMove mouseEnter mouseDown mouseUp) - forEvents: m2 eventsDuringBubble - ignoreMouseOver: true. - ! From marcel.taeumel at hpi.de Thu Sep 17 14:19:39 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 16:19:39 +0200 Subject: [squeak-dev] The Inbox: ToolBuilder-Morphic-ct.264.mcz In-Reply-To: References: Message-ID: So, let's fix it in SystemWindow instead, not here. :-) Best, Marcel Am 17.09.2020 14:17:33 schrieb commits at source.squeak.org : A new version of ToolBuilder-Morphic was added to project The Inbox: http://source.squeak.org/inbox/ToolBuilder-Morphic-ct.264.mcz ==================== Summary ==================== Name: ToolBuilder-Morphic-ct.264 Author: ct Time: 17 September 2020, 2:17:24.709813 pm UUID: f0cb613b-9652-a146-9d77-016577d87700 Ancestors: ToolBuilder-Morphic-mt.263 Initially refresh window color when building a window. While this also happens later #openInWorld, the call is missing is something else is done with the window instead, e.g. #openInWindow or #imageForm. =============== Diff against ToolBuilder-Morphic-mt.263 =============== Item was changed: ----- Method: MorphicToolBuilder>>buildPluggableWindow: (in category 'widgets required') ----- buildPluggableWindow: aSpec | widget | aSpec layout == #proportional ifFalse:[ "This needs to be implemented - probably by adding a single pane and then the rest" ^self error: 'Not implemented'. ]. widget := (self windowClassFor: aSpec) new. self register: widget id: aSpec name. widget model: aSpec model. "Set child dependent layout properties." widget wantsPaneSplitters: (aSpec wantsResizeHandles ifNil: [true]). self setLayoutHintsFor: widget spec: aSpec. widget layoutInset: (aSpec padding ifNil: [ProportionalSplitterMorph gripThickness]). widget cellGap: (aSpec spacing ifNil: [ProportionalSplitterMorph gripThickness]). "Now create the children." panes := OrderedCollection new. aSpec children isSymbol ifTrue: [ widget getChildrenSelector: aSpec children. widget update: aSpec children] ifFalse: [ self buildAll: aSpec children in: widget]. widget setUpdatablePanesFrom: panes. aSpec label ifNotNil: [:label| label isSymbol ifTrue:[widget getLabelSelector: label] ifFalse:[widget setLabel: label]]. aSpec multiWindowStyle notNil ifTrue: [widget savedMultiWindowState: (SavedMultiWindowState on: aSpec model)]. widget closeWindowSelector: aSpec closeAction. self buildHelpFor: widget spec: aSpec. widget bounds: (RealEstateAgent initialFrameFor: widget initialExtent: (aSpec extent ifNil:[widget initialExtent]) world: self currentWorld). + widget refreshWindowColor. + ^ widget! -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Thu Sep 17 14:37:49 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Thu, 17 Sep 2020 10:37:49 -0400 Subject: [squeak-dev] The Trunk: ST80-mt.258.mcz In-Reply-To: References: Message-ID: <20200917143749.GA67623@shell.msen.com> Thanks Eliot for noticing and thanks Marcel for fixing it. Just to put the blame where it belongs - the multiple queue issue is an error that was introduced by me in ST80-dtl.54 more than 10 years ago. The mistake was mine, Marcel only fixed it. I simply can't imagine what I was thinking when I left the queue an Controller instance variable. Maybe it was a mindless copy from the old DeferredActionStandardSystemController but ugh, what was I thinking?!? Sorry, Dave On Thu, Sep 17, 2020 at 01:34:40PM +0200, Marcel Taeumel wrote: > Hi Eliot. > > See??ST80-mt.259. Both MVC and Morphic projects now have the same behavior regarding deferred messages. Each kind of project has its own, single queue. > > Best, > Marcel > Am 17.09.2020 13:06:52 schrieb Marcel Taeumel : > Hi Eliot. > > >??inside Controller>>processDeferredActions and have Controller>>controlActivity read > > This would produce an endless recursion. But one could check with #== to avoid that. :-) > > >??Shouldn???t there in fact be a single SharedQueue for deferred actions? > > Agreed. This would be a next step, now that we can see that this is actually happening. Might have been by accident so far. > > ??>??Is this bug in Morphic too? > > No, in Morphic, there is only single queue for all Morphic projects. It's a class variable in WorldState. > > Best, > Marcel > Am 17.09.2020 12:56:30 schrieb Eliot Miranda : > Hi Marcel, > > > On Sep 17, 2020, at 12:15 AM, commits at source.squeak.org wrote: > > > > ???Marcel Taeumel uploaded a new version of ST80 to project The Trunk: > > http://source.squeak.org/trunk/ST80-mt.258.mcz > > > > ==================== Summary ==================== > > > > Name: ST80-mt.258 > > Author: mt > > Time: 17 September 2020, 9:15:53.534726 am > > UUID: c7ff9de0-a71a-4646-a639-211d94848498 > > Ancestors: ST80-mt.257 > > > > Fixes processing of deferred actions in MVC. > > > > Note that windows in MVC delegate control to their sub-controllers, however, remaining the active controller from the project's world perspective (i.e. the controller manager). Thus, we have to process two deferred-action queues in for most cases. For example, the text field in a workspace involves a PluggableTextController, embedded in a StandardSystemController. Any do-it like "Project current addDeferredUIMessage: [...]" will add the message to the StandardSystemController's queue. > > > > =============== Diff against ST80-mt.257 =============== > > > > Item was changed: > > ----- Method: Controller>>controlActivity (in category 'control defaults') ----- > > controlActivity > > "Pass control to the next control level (that is, to the Controller of a > > subView of the receiver's view) if possible. It is sent by > > Controller|controlLoop each time through the main control loop. It should > > be redefined in a subclass if some other action is needed." > > > > + self processDeferredActions. > > + Project current world activeController processDeferredActions. > > - [self deferredActionQueue isEmpty] > > - whileFalse: [deferredActionQueue next value]. > > self controlToNextLevel! > > > > Item was added: > > + ----- Method: Controller>>processDeferredActions (in category 'control defaults') ----- > > + processDeferredActions > > + > > + [self deferredActionQueue isEmpty] > > + whileFalse: [deferredActionQueue next value].! > > > > Item was changed: > > ----- Method: ParagraphEditor>>normalActivity (in category 'controlling') ----- > > normalActivity > > self processKeyboard. > > + self processMouseButtons. > > + super normalActivity. > > + ! > > - self processMouseButtons! > > first regarding the fix, wouldn???t it make more sense to put > > Project current world activeController processDeferredActions. > > inside Controller>>processDeferredActions and have Controller>>controlActivity read > > Controller|controlLoop each time through the main control loop. It should > be redefined in a subclass if some other action is needed." > > self processDeferredActions. > self controlToNextLevel > > ? > > Second, doesn???t the two queue solution introduce a serious bug? Doesn???t it reorder deferred actions depending on which queue the action gets added to? Shouldn???t there in fact be a single SharedQueue for deferred actions? Is this bug in Morphic too? > > _,,,^..^,,,_ (phone) > > From marcel.taeumel at hpi.de Thu Sep 17 15:01:03 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Thu, 17 Sep 2020 17:01:03 +0200 Subject: [squeak-dev] The Trunk: ST80-mt.258.mcz In-Reply-To: <20200917143749.GA67623@shell.msen.com> References: <20200917143749.GA67623@shell.msen.com> Message-ID: Hi Dave, no worries. Makes one even think about having such synchronization queues for all processes in Squeak. At least, it is a nice thought experiment on inter-process communiction in Squeak. Best, Marcel Am 17.09.2020 16:37:57 schrieb David T. Lewis : Thanks Eliot for noticing and thanks Marcel for fixing it. Just to put the blame where it belongs - the multiple queue issue is an error that was introduced by me in ST80-dtl.54 more than 10 years ago. The mistake was mine, Marcel only fixed it. I simply can't imagine what I was thinking when I left the queue an Controller instance variable. Maybe it was a mindless copy from the old DeferredActionStandardSystemController but ugh, what was I thinking?!? Sorry, Dave On Thu, Sep 17, 2020 at 01:34:40PM +0200, Marcel Taeumel wrote: > Hi Eliot. > > See??ST80-mt.259. Both MVC and Morphic projects now have the same behavior regarding deferred messages. Each kind of project has its own, single queue. > > Best, > Marcel > Am 17.09.2020 13:06:52 schrieb Marcel Taeumel : > Hi Eliot. > > >??inside Controller>>processDeferredActions and have Controller>>controlActivity read > > This would produce an endless recursion. But one could check with #== to avoid that. :-) > > >??Shouldn???t there in fact be a single SharedQueue for deferred actions? > > Agreed. This would be a next step, now that we can see that this is actually happening. Might have been by accident so far. > > ??>??Is this bug in Morphic too? > > No, in Morphic, there is only single queue for all Morphic projects. It's a class variable in WorldState. > > Best, > Marcel > Am 17.09.2020 12:56:30 schrieb Eliot Miranda : > Hi Marcel, > > > On Sep 17, 2020, at 12:15 AM, commits at source.squeak.org wrote: > > > > ???Marcel Taeumel uploaded a new version of ST80 to project The Trunk: > > http://source.squeak.org/trunk/ST80-mt.258.mcz > > > > ==================== Summary ==================== > > > > Name: ST80-mt.258 > > Author: mt > > Time: 17 September 2020, 9:15:53.534726 am > > UUID: c7ff9de0-a71a-4646-a639-211d94848498 > > Ancestors: ST80-mt.257 > > > > Fixes processing of deferred actions in MVC. > > > > Note that windows in MVC delegate control to their sub-controllers, however, remaining the active controller from the project's world perspective (i.e. the controller manager). Thus, we have to process two deferred-action queues in for most cases. For example, the text field in a workspace involves a PluggableTextController, embedded in a StandardSystemController. Any do-it like "Project current addDeferredUIMessage: [...]" will add the message to the StandardSystemController's queue. > > > > =============== Diff against ST80-mt.257 =============== > > > > Item was changed: > > ----- Method: Controller>>controlActivity (in category 'control defaults') ----- > > controlActivity > > "Pass control to the next control level (that is, to the Controller of a > > subView of the receiver's view) if possible. It is sent by > > Controller|controlLoop each time through the main control loop. It should > > be redefined in a subclass if some other action is needed." > > > > + self processDeferredActions. > > + Project current world activeController processDeferredActions. > > - [self deferredActionQueue isEmpty] > > - whileFalse: [deferredActionQueue next value]. > > self controlToNextLevel! > > > > Item was added: > > + ----- Method: Controller>>processDeferredActions (in category 'control defaults') ----- > > + processDeferredActions > > + > > + [self deferredActionQueue isEmpty] > > + whileFalse: [deferredActionQueue next value].! > > > > Item was changed: > > ----- Method: ParagraphEditor>>normalActivity (in category 'controlling') ----- > > normalActivity > > self processKeyboard. > > + self processMouseButtons. > > + super normalActivity. > > + ! > > - self processMouseButtons! > > first regarding the fix, wouldn???t it make more sense to put > > Project current world activeController processDeferredActions. > > inside Controller>>processDeferredActions and have Controller>>controlActivity read > > Controller|controlLoop each time through the main control loop. It should > be redefined in a subclass if some other action is needed." > > self processDeferredActions. > self controlToNextLevel > > ? > > Second, doesn???t the two queue solution introduce a serious bug? Doesn???t it reorder deferred actions depending on which queue the action gets added to? Shouldn???t there in fact be a single SharedQueue for deferred actions? Is this bug in Morphic too? > > _,,,^..^,,,_ (phone) > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Thu Sep 17 15:20:36 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Thu, 17 Sep 2020 17:20:36 +0200 Subject: [squeak-dev] highdpi testing Message-ID: Hi all I have fiddled with the highdpi stuff and managed Linux/Unix to join the party. If you're interested, have a look at https://github.com/OpenSmalltalk/opensmalltalk-vm/tree/krono/highdpi-v2 The main thing that changes is that primitiveScreenScaleFactor now can dynamically determine what scale to use for a monitor. Per se, that does nothing, but the DpiAware -Changeset below allows Morphic and graphics to react to that. This should work on - OSX (CoreGraphics, OpenGL, Metal) - Windows - Linux (X11, fbdev) I think this might be especially interesting for Tony and Christoph, among others. Things to Note: - Windows needs a manifest. This can be tricky. See https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/scripts/ci/travis_build.sh#L21 https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-f0c6c15289ff7521735d7e74a350903dL15 - Mac needs a customized Info.plist https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-9bc015b0fdf6b3aa927c5e49b2d882b9R129 - Linux can be a mess. There are now a few Environment variables to help out: https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/a2690b1b4e5c4488c716613f6654a74e31fa018a/platforms/unix/vm/sqUnixDisplayHelpers.h Multimonitor people might want to set SQUEAK_DISPLAY_PER_MONITOR_SCALE=1 Tony on the Phone might want to set SQUEAK_DISPLAY_PREFER_PHYSICAL_SCALE=1 Have fun and play around :) Best regards -Tobias -------------- next part -------------- A non-text attachment was scrubbed... Name: DpiAware.1.cs Type: application/octet-stream Size: 15502 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: DisplayScreen class-actualScreenScaleFactor.st Type: application/octet-stream Size: 242 bytes Desc: not available URL: From commits at source.squeak.org Thu Sep 17 15:20:25 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 15:20:25 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1682.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1682.mcz ==================== Summary ==================== Name: Morphic-ct.1682 Author: ct Time: 17 September 2020, 5:20:17.764813 pm UUID: ae7e7e33-c789-ea41-a0d2-b81efd35d2da Ancestors: Morphic-dtl.1681 Adds new #repositionOnExpand flag to CollapsedMorph. If set, the wrapped morph is repositioned using RealEstateAgent when uncollapsed. Also adds some world-agnostic (someone knowing a good Descartes joke for this?) instantiation methods with examples. Will be used for Objectland. =============== Diff against Morphic-dtl.1681 =============== Item was added: + ----- Method: CollapsedMorph class>>createReplacementFor: (in category 'instance creation') ----- + createReplacementFor: aMorph + "Create an instance of the receiver that encapsulates aMorph. + + Usage: + (CollapsedMorph createReplacementFor: Morph new) openInHand + " + + ^ self new + basicBeReplacementFor: aMorph; + setProperty: #repositionOnExpand toValue: true; + yourself! Item was changed: ----- Method: CollapsedMorph class>>createReplacementFor:in: (in category 'instance creation') ----- createReplacementFor: aMorph in: anOwner + "Create an instance of the receiver that encapsulates aMorph and is opened in anOwner. + + Usage: + (CollapsedMorph createReplacementFor: Morph new in: World) centerWithWrappees: 100 @ 100 + " ^ self new beReplacementFor: aMorph in: anOwner; yourself! Item was added: + ----- Method: CollapsedMorph>>basicBeReplacementFor: (in category 'collapse/expand') ----- + basicBeReplacementFor: aMorph + "Encapsulate aMorph into the receiver." + + uncollapsedMorph := aMorph. + + self setLabel: aMorph externalName. + self refreshWindowColor. + self collapseOrExpand.! Item was changed: ----- Method: CollapsedMorph>>collapseOrExpand (in category 'resize/collapse') ----- collapseOrExpand "Toggle the expand/collapsd state of the receiver. If expanding, copy the window title back to the name of the expanded morph" | aWorld | + isCollapsed ifFalse: [^ super collapseOrExpand]. + + aWorld := self currentWorld. + uncollapsedMorph setProperty: #collapsedPosition toValue: self position. + self repositionOnExpand ifTrue: [ + uncollapsedMorph bounds: (RealEstateAgent + initialFrameFor: uncollapsedMorph + initialExtent: uncollapsedMorph extent + world: aWorld)]. + labelString ifNotNil: [uncollapsedMorph setNameTo: labelString]. + + mustNotClose := false. "We're not closing but expanding" + self delete. + + aWorld + addMorphFront: uncollapsedMorph; + startSteppingSubmorphsOf: uncollapsedMorph.! - isCollapsed - ifTrue: - [uncollapsedMorph setProperty: #collapsedPosition toValue: self position. - labelString ifNotNil: [uncollapsedMorph setNameTo: labelString]. - mustNotClose := false. "We're not closing but expanding" - self delete. - (aWorld := self currentWorld) addMorphFront: uncollapsedMorph. - aWorld startSteppingSubmorphsOf: uncollapsedMorph] - ifFalse: - [super collapseOrExpand]! Item was added: + ----- Method: CollapsedMorph>>repositionOnExpand (in category 'accessing') ----- + repositionOnExpand + "If true, the wrapped morph will be repositioned using RealEstateAgent when expanded the next time; otherwise, the former position will be kept (default). See #collapseOrExpand." + + ^ self valueOfProperty: #repositionOnExpand ifAbsent: [false]! Item was added: + ----- Method: CollapsedMorph>>repositionOnExpand: (in category 'accessing') ----- + repositionOnExpand: aBoolean + "If true, the wrapped morph will be repositioned using RealEstateAgent when expanded the next time; otherwise, the former position will be kept (default). See #collapseOrExpand." + + self setProperty: #repositionOnExpand toValue: aBoolean.! From commits at source.squeak.org Thu Sep 17 15:34:19 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 15:34:19 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1684.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1684.mcz ==================== Summary ==================== Name: Morphic-ct.1684 Author: ct Time: 17 September 2020, 5:33:51.858813 pm UUID: 439b6c83-ed50-784c-83f2-bda2edbd6a03 Ancestors: Morphic-mt.1683 Refactors halo handle creation logic, increasing reusability in subclass SelectionMorph. Adds lasso icon to #addOrRemoveItems: handle. Also includes minor refactorings of two other Morph methods. =============== Diff against Morphic-mt.1683 =============== Item was changed: ----- Method: Morph>>addHandlesTo:box: (in category 'halos and balloon help') ----- addHandlesTo: aHaloMorph box: box + "Add halo handles to the halo. Apply the halo filter if appropriate." - "Add halo handles to the halo. Apply the halo filter if appropriate" - aHaloMorph haloBox: box. + + Preferences haloSpecifications + select: [:spec | self + filterHaloSelector: spec addHandleSelector + inHalo: aHaloMorph] + thenDo: [:spec | + aHaloMorph + perform: spec addHandleSelector + with: spec]. + + aHaloMorph innerTarget addOptionalHandlesTo: aHaloMorph box: box.! - Preferences haloSpecifications do: - [:aSpec | | wantsIt aSelector | - aSelector := aSpec addHandleSelector. - wantsIt := Preferences selectiveHalos - ifTrue: - [self wantsHaloHandleWithSelector: aSelector inHalo: aHaloMorph] - ifFalse: - [true]. - wantsIt ifTrue: - [(#(addMakeSiblingHandle: addDupHandle:) includes: aSelector) ifTrue: - [wantsIt := self preferredDuplicationHandleSelector = aSelector]. - wantsIt ifTrue: - [aHaloMorph perform: aSelector with: aSpec]]]. - - aHaloMorph innerTarget addOptionalHandlesTo: aHaloMorph box: box! Item was changed: ----- Method: Morph>>changeProportionalLayout (in category 'layout-menu') ----- changeProportionalLayout + | layout | + ((layout := self layoutPolicy) notNil and: [layout isProportionalLayout]) + ifTrue: [^ self]. "already proportional layout" + self layoutPolicy: ProportionalLayout new.! - ((layout := self layoutPolicy) notNil and:[layout isProportionalLayout]) - ifTrue:[^self]. "already proportional layout" - self layoutPolicy: ProportionalLayout new. - self layoutChanged.! Item was added: + ----- Method: Morph>>filterHaloSelector:inHalo: (in category 'halos and balloon help') ----- + filterHaloSelector: aSelector inHalo: aHaloMorph + + ^ Preferences selectiveHalos ==> [ + self wantsHaloHandleWithSelector: aSelector inHalo: aHaloMorph] + and: [(#(addMakeSiblingHandle: addDupHandle:) includes: aSelector) ==> [ + self preferredDuplicationHandleSelector = aSelector]]! Item was changed: ----- Method: Morph>>referencePosition: (in category 'rotate scale and flex') ----- referencePosition: aPosition "Move the receiver to match its reference position with aPosition" + | newPos intPos | newPos := self position + (aPosition - self referencePosition). intPos := newPos asIntegerPoint. + self position: (newPos = intPos + ifTrue: [intPos] + ifFalse: [newPos]).! - newPos = intPos - ifTrue:[self position: intPos] - ifFalse:[self position: newPos].! Item was removed: - ----- Method: SelectionMorph>>addHandlesTo:box: (in category 'halos and balloon help') ----- - addHandlesTo: aHaloMorph box: box - | onlyThese | - aHaloMorph haloBox: box. - onlyThese := #(addDismissHandle: addMenuHandle: addGrabHandle: addDragHandle: addDupHandle: addHelpHandle: addGrowHandle: addFontSizeHandle: addFontStyleHandle: addFontEmphHandle: addRecolorHandle:). - Preferences haloSpecifications do: - [:aSpec | (onlyThese includes: aSpec addHandleSelector) ifTrue: - [aHaloMorph perform: aSpec addHandleSelector with: aSpec]]. - aHaloMorph innerTarget addOptionalHandlesTo: aHaloMorph box: box! Item was changed: ----- Method: SelectionMorph>>addOptionalHandlesTo:box: (in category 'halos and balloon help') ----- addOptionalHandlesTo: aHalo box: box + + aHalo + addHandleAt: box leftCenter + color: Color lightBlue + icon: #Lasso + on: #mouseUp + send: #addOrRemoveItems: + to: self.! - aHalo addHandleAt: box leftCenter color: Color blue icon: nil - on: #mouseUp send: #addOrRemoveItems: to: self.! Item was added: + ----- Method: SelectionMorph>>filterHaloSelector:inHalo: (in category 'halos and balloon help') ----- + filterHaloSelector: aSelector inHalo: aHaloMorph + + ^ self permittedHaloHandles includes: aSelector! Item was added: + ----- Method: SelectionMorph>>permittedHaloHandles (in category 'halos and balloon help') ----- + permittedHaloHandles + + ^ #(addDebugHandle: addDismissHandle: addMenuHandle: addGrabHandle: addDragHandle: addDupHandle: addHelpHandle: addGrowHandle: addFontSizeHandle: addFontStyleHandle: addFontEmphHandle: addRecolorHandle:)! From commits at source.squeak.org Thu Sep 17 15:37:01 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 15:37:01 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1685.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1685.mcz ==================== Summary ==================== Name: Morphic-ct.1685 Author: ct Time: 17 September 2020, 5:36:40.852813 pm UUID: b3443fef-7b83-134b-95b6-fdd7f6c504b4 Ancestors: Morphic-mt.1683 Complements Morphic-ct.1682. =============== Diff against Morphic-mt.1683 =============== Item was changed: ----- Method: Morph>>collapse (in category 'menus') ----- collapse + + ^ CollapsedMorph new beReplacementFor: self! - CollapsedMorph new beReplacementFor: self! Item was added: + ----- Method: Morph>>collapseInPlace (in category 'menus') ----- + collapseInPlace + + ^ CollapsedMorph createReplacementFor: self! From commits at source.squeak.org Thu Sep 17 15:41:17 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 15:41:17 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1686.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1686.mcz ==================== Summary ==================== Name: Morphic-ct.1686 Author: ct Time: 17 September 2020, 5:41:03.546813 pm UUID: 412bd6fc-041c-e74e-959c-498a12f39817 Ancestors: Morphic-mt.1683 Fix: Don't defer construction of main docking bar when initializing a new MorphicProject. Such lazy construction can be really disturbing when preparing a project from another one, as it will be done in Objectland. =============== Diff against Morphic-mt.1683 =============== Item was changed: ----- Method: MorphicProject>>initialize (in category 'initialize') ----- initialize "Initialize a new Morphic Project" + super initialize. + world := PasteUpMorph newWorldForProject: self. self setWorldBackground: true. + Locale switchToID: CurrentProject localeID. + Preferences useVectorVocabulary ifTrue: [world installVectorVocabulary]. + + self assureMainDockingBarPresenceMatchesPreference.! - Preferences useVectorVocabulary ifTrue: [world installVectorVocabulary]! From commits at source.squeak.org Thu Sep 17 16:07:08 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 16:07:08 0000 Subject: [squeak-dev] The Inbox: Graphics-ct.440.mcz Message-ID: A new version of Graphics was added to project The Inbox: http://source.squeak.org/inbox/Graphics-ct.440.mcz ==================== Summary ==================== Name: Graphics-ct.440 Author: ct Time: 17 September 2020, 6:06:52.462813 pm UUID: 1d3c2261-5196-cb41-abf7-beccef7625b4 Ancestors: Graphics-tonyg.438 Proposal: Include convenience method to create a Form from a base64 string into Trunk. Method by Maximilian Stiede (ms), [1]. Fore more information about base64-encoding in Squeak, see also [2]. [1] https://github.com/ShirleyNekoDev/Squeak-ToolIconsPlus/blob/master/ToolIconsPlus.package/Form.extension/class/fromBase64..st [2] https://wiki.squeak.org/squeak/3992 =============== Diff against Graphics-tonyg.438 =============== Item was added: + ----- Method: Form class>>fromBase64: (in category 'instance creation') ----- + fromBase64: aString + + ^ self fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: aString readStream)! From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 16:25:09 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 16:25:09 +0000 Subject: [squeak-dev] The Inbox: ToolBuilder-Morphic-ct.264.mcz In-Reply-To: References: , Message-ID: Hi Marcel, #refreshWindowColor depends on #paneColor which in turn depends on the model of the SystemWindow. If we call #refereshWindowColor directly in SystemWindow >> #initialize, the pane color will not match to the model at the end of #buildPluggableWindow:. Is this what we want? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Donnerstag, 17. September 2020 16:19:39 An: squeak-dev Betreff: Re: [squeak-dev] The Inbox: ToolBuilder-Morphic-ct.264.mcz So, let's fix it in SystemWindow instead, not here. :-) Best, Marcel Am 17.09.2020 14:17:33 schrieb commits at source.squeak.org : A new version of ToolBuilder-Morphic was added to project The Inbox: http://source.squeak.org/inbox/ToolBuilder-Morphic-ct.264.mcz ==================== Summary ==================== Name: ToolBuilder-Morphic-ct.264 Author: ct Time: 17 September 2020, 2:17:24.709813 pm UUID: f0cb613b-9652-a146-9d77-016577d87700 Ancestors: ToolBuilder-Morphic-mt.263 Initially refresh window color when building a window. While this also happens later #openInWorld, the call is missing is something else is done with the window instead, e.g. #openInWindow or #imageForm. =============== Diff against ToolBuilder-Morphic-mt.263 =============== Item was changed: ----- Method: MorphicToolBuilder>>buildPluggableWindow: (in category 'widgets required') ----- buildPluggableWindow: aSpec | widget | aSpec layout == #proportional ifFalse:[ "This needs to be implemented - probably by adding a single pane and then the rest" ^self error: 'Not implemented'. ]. widget := (self windowClassFor: aSpec) new. self register: widget id: aSpec name. widget model: aSpec model. "Set child dependent layout properties." widget wantsPaneSplitters: (aSpec wantsResizeHandles ifNil: [true]). self setLayoutHintsFor: widget spec: aSpec. widget layoutInset: (aSpec padding ifNil: [ProportionalSplitterMorph gripThickness]). widget cellGap: (aSpec spacing ifNil: [ProportionalSplitterMorph gripThickness]). "Now create the children." panes := OrderedCollection new. aSpec children isSymbol ifTrue: [ widget getChildrenSelector: aSpec children. widget update: aSpec children] ifFalse: [ self buildAll: aSpec children in: widget]. widget setUpdatablePanesFrom: panes. aSpec label ifNotNil: [:label| label isSymbol ifTrue:[widget getLabelSelector: label] ifFalse:[widget setLabel: label]]. aSpec multiWindowStyle notNil ifTrue: [widget savedMultiWindowState: (SavedMultiWindowState on: aSpec model)]. widget closeWindowSelector: aSpec closeAction. self buildHelpFor: widget spec: aSpec. widget bounds: (RealEstateAgent initialFrameFor: widget initialExtent: (aSpec extent ifNil:[widget initialExtent]) world: self currentWorld). + widget refreshWindowColor. + ^ widget! -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 17 17:04:57 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 17:04:57 0000 Subject: [squeak-dev] The Inbox: Monticello-ct.729.mcz Message-ID: A new version of Monticello was added to project The Inbox: http://source.squeak.org/inbox/Monticello-ct.729.mcz ==================== Summary ==================== Name: Monticello-ct.729 Author: ct Time: 17 September 2020, 7:04:53.385813 pm UUID: 5eea47cc-405e-7841-9544-0f57a9b92fa0 Ancestors: Monticello-cmm.726 Proposal: Enhance truncation of "newer versions avaiable" warning (and make it multilingual-ready) =============== Diff against Monticello-cmm.726 =============== Item was changed: ----- Method: MCWorkingCopyBrowser>>checkForNewerVersions (in category 'actions') ----- checkForNewerVersions + "Answer true if there are no newer versions preventing us from saving a version." + | newer | newer := workingCopy possiblyNewerVersionsIn: self repository. + + newer ifEmpty: [^ true]. + + ^ self confirm: ('CAUTION!! {1}:\{2}\Do you really want to save this version?' withCRs translated format: { + newer size = 1 + ifTrue: ['This version in the repository may be newer' translated] + ifFalse: ['These {1} versions in the repository may be newer' translated format: {newer size}]. + (newer size > 3 + ifFalse: [newer] + ifTrue: [(newer first: 3) , {'...'} , {newer last}]) asCommaString withNoLineLongerThan: 150 + })! - ^ newer isEmpty or: [ - self confirm: 'CAUTION!! These versions in the repository may be newer:', - String cr, ((newer asCommaString withNoLineLongerThan: 150) truncateWithElipsisTo: 5 * 149), String cr, - 'Do you really want to save this version?'].! From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 17:08:58 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 17:08:58 +0000 Subject: [squeak-dev] The Inbox: Monticello-ct.729.mcz In-Reply-To: References: Message-ID: <20e88b615b344893bf96e0cc9125ebc2@student.hpi.uni-potsdam.de> Before: [cid:e853f811-9aac-4450-8e0c-f4cb0a622e50] After: [cid:fabc3777-170e-4222-abf9-35c599634f15] However, I have follow-up questions: A. Why do clients need to care about #withNoLineLongerThan:/word-wrapping stuff? Shouldn't this be a responsibility of UserDialogBoxMorph instead? B. Should we maybe turn off this warning for commits to the MCRepository inbox? In this particular case, our declared workflow says that you should ignore this message anyway, doesn't it? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Donnerstag, 17. September 2020 19:04:57 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: Monticello-ct.729.mcz A new version of Monticello was added to project The Inbox: http://source.squeak.org/inbox/Monticello-ct.729.mcz ==================== Summary ==================== Name: Monticello-ct.729 Author: ct Time: 17 September 2020, 7:04:53.385813 pm UUID: 5eea47cc-405e-7841-9544-0f57a9b92fa0 Ancestors: Monticello-cmm.726 Proposal: Enhance truncation of "newer versions avaiable" warning (and make it multilingual-ready) =============== Diff against Monticello-cmm.726 =============== Item was changed: ----- Method: MCWorkingCopyBrowser>>checkForNewerVersions (in category 'actions') ----- checkForNewerVersions + "Answer true if there are no newer versions preventing us from saving a version." + | newer | newer := workingCopy possiblyNewerVersionsIn: self repository. + + newer ifEmpty: [^ true]. + + ^ self confirm: ('CAUTION!! {1}:\{2}\Do you really want to save this version?' withCRs translated format: { + newer size = 1 + ifTrue: ['This version in the repository may be newer' translated] + ifFalse: ['These {1} versions in the repository may be newer' translated format: {newer size}]. + (newer size > 3 + ifFalse: [newer] + ifTrue: [(newer first: 3) , {'...'} , {newer last}]) asCommaString withNoLineLongerThan: 150 + })! - ^ newer isEmpty or: [ - self confirm: 'CAUTION!! These versions in the repository may be newer:', - String cr, ((newer asCommaString withNoLineLongerThan: 150) truncateWithElipsisTo: 5 * 149), String cr, - 'Do you really want to save this version?'].! -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 58970 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 27934 bytes Desc: pastedImage.png URL: From karlramberg at gmail.com Thu Sep 17 17:26:48 2020 From: karlramberg at gmail.com (karl ramberg) Date: Thu, 17 Sep 2020 19:26:48 +0200 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> Message-ID: A note about something to do: I noticed that quite a few of the Etoys icons are not added to the ScriptingSystem formDictionary. So when opening a ScriptEditorMorph for example, icons like 'RoundGoldBox' are missing so the UI is crippled. The icons are found in ReleaseBuilderSqueakland but not initialized Best, Karl On Thu, Sep 17, 2020 at 12:42 PM Stéphane Rollandin wrote: > > This commit is part of reconstruction of Objectland (also known as "The > Worlds of Squeak"). > > Cool! > > Stef > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From craig at blackpagedigital.com Thu Sep 17 17:34:49 2020 From: craig at blackpagedigital.com (Craig Latta) Date: Thu, 17 Sep 2020 10:34:49 -0700 Subject: [squeak-dev] highdpi testing In-Reply-To: References: Message-ID: Hooray! Thanks, Tobias! -C *** On 17/9/20 08:20, Tobias Pape wrote: > Hi all > > I have fiddled with the highdpi stuff and managed Linux/Unix to join the party. > > If you're interested, have a look at > https://github.com/OpenSmalltalk/opensmalltalk-vm/tree/krono/highdpi-v2 > > > The main thing that changes is that primitiveScreenScaleFactor now can dynamically determine what > scale to use for a monitor. Per se, that does nothing, but the DpiAware -Changeset below allows > Morphic and graphics to react to that. > > This should work on > - OSX (CoreGraphics, OpenGL, Metal) > - Windows > - Linux (X11, fbdev) > > I think this might be especially interesting for Tony and Christoph, among others. > > Things to Note: > - Windows needs a manifest. This can be tricky. See > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/scripts/ci/travis_build.sh#L21 > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-f0c6c15289ff7521735d7e74a350903dL15 > - Mac needs a customized Info.plist > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-9bc015b0fdf6b3aa927c5e49b2d882b9R129 > - Linux can be a mess. > There are now a few Environment variables to help out: > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/a2690b1b4e5c4488c716613f6654a74e31fa018a/platforms/unix/vm/sqUnixDisplayHelpers.h > Multimonitor people might want to set > SQUEAK_DISPLAY_PER_MONITOR_SCALE=1 > Tony on the Phone might want to set > SQUEAK_DISPLAY_PREFER_PHYSICAL_SCALE=1 > > > Have fun and play around :) > > Best regards > -Tobias -- Craig Latta :: research computer scientist Black Page Digital :: Berkeley, California 663137D7940BF5C0AFC 1349FB2ADA32C4D5314CE From karlramberg at gmail.com Thu Sep 17 18:31:31 2020 From: karlramberg at gmail.com (karl ramberg) Date: Thu, 17 Sep 2020 20:31:31 +0200 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> Message-ID: On Thu, Sep 17, 2020 at 7:26 PM karl ramberg wrote: > A note about something to do: > I noticed that quite a few of the Etoys icons are not added to the > ScriptingSystem formDictionary. > So when opening a ScriptEditorMorph for example, icons like > 'RoundGoldBox' are missing so the UI is crippled. > The icons are found in ReleaseBuilderSqueakland but not initialized > You just need to do it: ReleaseBuilderSqueakland loadDefaultForms Best, Karl > Best, > Karl > > > On Thu, Sep 17, 2020 at 12:42 PM Stéphane Rollandin < > lecteur at zogotounga.net> wrote: > >> > This commit is part of reconstruction of Objectland (also known as "The >> Worlds of Squeak"). >> >> Cool! >> >> Stef >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Thu Sep 17 18:42:15 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Thu, 17 Sep 2020 14:42:15 -0400 Subject: [squeak-dev] Not getting all mail? In-Reply-To: <143C0EDA-0401-4AA9-8ECA-F8785EC61142@gmx.de> References: <143C0EDA-0401-4AA9-8ECA-F8785EC61142@gmx.de> Message-ID: <20200917184215.GA5001@shell.msen.com> I received three messages with subject line "autocomplete exit", including the one from Christoph. The archives are a reliable way to check what mail messages were distributed, and I see the same thres messages there: http://lists.squeakfoundation.org/pipermail/squeak-dev/2020-September/subject.html Dave On Thu, Sep 17, 2020 at 01:23:33PM +0200, Tobias Pape wrote: > > > On 17.09.2020, at 07:54, Jakob Reschke wrote: > > > > Hello, > > > > Over the past week I have observed people reply to some messages that I didn't receive from the list. For example, I do not have Christoph's message to which Eliot replied below. In one instance it even looked like I did not get the original post, but only a reply. I checked my spam folder, there are no Squeak mails in there. > > > > Do you experience the same? Or do I just always see cases where one person did not reply to the list, but the next one does again? > > Jep, same here. > -t > > > > > Kind regards, > > Jakob > > > > > > Eliot Miranda schrieb am Mo., 14. Sep. 2020, 21:49: > > > > > > On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph wrote: > > Which completion are you using? Autocompletion already behaves like this. > > > > > > OComplete > > > > Best, > > > > Christoph > > > > Von: Squeak-dev im Auftrag von Eliot Miranda > > Gesendet: Montag, 14. September 2020 04:45:30 > > An: The general-purpose Squeak developers list > > Betreff: [squeak-dev] autocomplete exit > > > > Hi, > > > > would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? > > > > _,,,^..^,,,_ > > best, Eliot > > > > > > > > > From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 19:04:39 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 19:04:39 +0000 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> , Message-ID: <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> Nice finding, Karl! I guess this has not been made part of the usual ReleaseBuilder for performance (storage) considerations. The question is: Is this still relevant? Should we lazily load all forms if any is missing in the ScriptingSystem? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Donnerstag, 17. September 2020 20:31:31 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: EToys-ct.406.mcz On Thu, Sep 17, 2020 at 7:26 PM karl ramberg > wrote: A note about something to do: I noticed that quite a few of the Etoys icons are not added to the ScriptingSystem formDictionary. So when opening a ScriptEditorMorph for example, icons like 'RoundGoldBox' are missing so the UI is crippled. The icons are found in ReleaseBuilderSqueakland but not initialized You just need to do it: ReleaseBuilderSqueakland loadDefaultForms Best, Karl Best, Karl On Thu, Sep 17, 2020 at 12:42 PM Stéphane Rollandin > wrote: > This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). Cool! Stef -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Thu Sep 17 19:04:39 2020 From: gettimothy at zoho.com (gettimothy) Date: Thu, 17 Sep 2020 15:04:39 -0400 Subject: [squeak-dev] Levente do you have PG3 running on 6.0Alpha? In-Reply-To: References: <174922b1b51.fa8febbc51549.1123089187633474927@zoho.com> Message-ID: <1749d745076.11464aa3824895.4339457474792953053@zoho.com> Hi Levente, The connection works fine when installed and coded on a pristine 6.0 alpha. It is on my 6.0 alpha image where I am importing my Parser work that I get the following error: 'Missing transition' (MyConnection default executeQuery: 'select 1 + 6 ')  then callstack... PG3ConnectionPool>>getConnection PG3ConnectionPool>>createNewConnection PG3Connection>>startup PG3Connection >>processStates PG3ServerState >> recieve:from Error is thrown in: receive: aPG3TypedMessage from: connection | action | action := transitions at: aPG3TypedMessage class ifAbsent: nil. action ifNil: [ self error: 'Missing transition' ]. (action at: 2) ifNotNil: [ :actionSelector | connection perform: actionSelector with: aPG3TypedMessage ]. ^action at:  I have both images open side-by-side, same connection args,etc.\ trying to debug now.... ---- On Tue, 15 Sep 2020 11:50:37 -0400 Levente Uzonyi wrote ---- Hi Tim, Yes, I do. On Tue, 15 Sep 2020, gettimothy via Squeak-dev wrote: > Hi Levente, > > I am migrating my work to 6.0 alpha. > > Getting some glitches and wondering if you have gotten it running there. Which versions of the packages do you have in your image? Can you describe what problems you see? Levente > > thx > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Thu Sep 17 19:20:12 2020 From: gettimothy at zoho.com (gettimothy) Date: Thu, 17 Sep 2020 15:20:12 -0400 Subject: [squeak-dev] Xtreams ConfigurationOfXTreams tries to access non existent 'http://www.squeaksource.com/MetacelloRepository/ConfigurationOfMetacello-dkh.674.mcz` Message-ID: <1749d828a4d.c17be4a425108.4231844776180238079@zoho.com> Good afternoon. The culprit appears to be ConfigurationOfXtreams >> ensureMetacello I vaguely rememeber a discussion here some months ago about the 'Ensure Recent Metacello' . to recreate: Installer ss        project: 'MetacelloRepository';        install: 'ConfigurationOfXtreams'. (Smalltalk at: #ConfigurationOfXtreams) loadDevelopment Let me know how I can /should help. thx. -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Thu Sep 17 19:50:56 2020 From: karlramberg at gmail.com (karl ramberg) Date: Thu, 17 Sep 2020 21:50:56 +0200 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> Message-ID: I'm not sure it is for space reasons. There could be other conflicts. It seems to change some icons for the handles when you do ReleaseBuilderSqueakland loadDefaultForms Lazy loading of just the missing icons would be awesome. Best, Karl On Thu, Sep 17, 2020 at 9:04 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Nice finding, Karl! I guess this has not been made part of the usual > ReleaseBuilder for performance (storage) considerations. The question is: > Is this still relevant? Should we lazily load all forms if any is missing > in the ScriptingSystem? > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von karl ramberg > *Gesendet:* Donnerstag, 17. September 2020 20:31:31 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] The Inbox: EToys-ct.406.mcz > > > > On Thu, Sep 17, 2020 at 7:26 PM karl ramberg > wrote: > >> A note about something to do: >> I noticed that quite a few of the Etoys icons are not added to the >> ScriptingSystem formDictionary. >> So when opening a ScriptEditorMorph for example, icons like >> 'RoundGoldBox' are missing so the UI is crippled. >> The icons are found in ReleaseBuilderSqueakland but not initialized >> > > You just need to do it: > ReleaseBuilderSqueakland loadDefaultForms > Best, > Karl > > >> Best, >> Karl >> >> >> On Thu, Sep 17, 2020 at 12:42 PM Stéphane Rollandin < >> lecteur at zogotounga.net> wrote: >> >>> > This commit is part of reconstruction of Objectland (also known as >>> "The Worlds of Squeak"). >>> >>> Cool! >>> >>> Stef >>> >>> >>> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 17 19:56:22 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 17 Sep 2020 19:56:22 +0000 Subject: [squeak-dev] The Inbox: Sound-ct.70.mcz In-Reply-To: <2ad81ea635c04521a6b1d99c415d9569@student.hpi.uni-potsdam.de> References: , , <2ad81ea635c04521a6b1d99c415d9569@student.hpi.uni-potsdam.de> Message-ID: Hi all, I'd like to finally get this done. I propose to use the following cover version instead, which is flagged with a CC-BY license on YouTube: https://www.youtube.com/watch?v=tcQqXLwHeXw If there any objections, please let me know soon, otherwise I will upload this version. Best, Christoph ________________________________ Von: Thiede, Christoph Gesendet: Mittwoch, 2. Oktober 2019 13:57:45 An: Squeak Dev Betreff: AW: [squeak-dev] The Inbox: Sound-ct.70.mcz Good point. It's at least not the original Beatles version with lyrics, but rather a cover version. Google does not recognize it. Can't we assume that a copyright check has been made before it was released within the old Worlds of Squeak? Otherwise, hypothetically I could replace it with a license-free song without the same melancholic touch ... ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Mittwoch, 2. Oktober 2019 08:53:32 An: Alan Grimes via Squeak-dev Betreff: Re: [squeak-dev] The Inbox: Sound-ct.70.mcz What about the copyright here? Is it the Beatles song? Best, Marcel Am 01.10.2019 23:25:13 schrieb commits at source.squeak.org : A new version of Sound was added to project The Inbox: http://source.squeak.org/inbox/Sound-ct.70.mcz ==================== Summary ==================== Name: Sound-ct.70 Author: ct Time: 1 October 2019, 11:24:29.223339 pm UUID: 2f133210-3aa3-7140-96bf-52dc03b1dea6 Ancestors: Sound-eem.66 Add example "Yesterday" for SampledSound This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against Sound-eem.66 =============== Item was added: + ----- Method: SampledSound class>>exampleYesterday (in category 'examples') ----- + exampleYesterday + "SampledSound exampleYesterday play" + + ^ SampledSound + samples: (SoundBuffer fromByteArray: (Base64MimeConverter mimeDecodeToBytes: + self yesterdaySamples readStream) contents) + samplingRate: 22050! Item was added: + ----- Method: SampledSound class>>yesterdaySamples (in category 'examples') ----- (excessive size, no diff calculated) -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 17 20:33:25 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 17 Sep 2020 20:33:25 0000 Subject: [squeak-dev] The Trunk: Monticello-eem.727.mcz Message-ID: Eliot Miranda uploaded a new version of Monticello to project The Trunk: http://source.squeak.org/trunk/Monticello-eem.727.mcz ==================== Summary ==================== Name: Monticello-eem.727 Author: eem Time: 17 September 2020, 1:33:24.024802 pm UUID: 5415d5d8-f2cd-49ac-974d-54d74f352585 Ancestors: Monticello-cmm.726 Have the snapshot browser file-out the selected method definition(s), not the installed definition(s). =============== Diff against Monticello-cmm.726 =============== Item was added: + ----- Method: MCSnapshotBrowser>>fileOutMessage (in category 'menus') ----- + fileOutMessage + "Put a description of the selected message on a file" + + | definitions wildcard | + wildcard := protocolSelection = '-- all --'. + definitions := methodSelection + ifNotNil: [{methodSelection}] + ifNil: [items select: + [:item| + item isMethodDefinition + and: [item className = classSelection + and: [wildcard or: [item protocol = protocolSelection]]]]]. + definitions isEmpty ifTrue: + [^self]. + FileStream + writeSourceCodeFrom: ((MCStWriter on: (WriteStream on: (String new: 100))) + writeDefinitions: (definitions); + stream) + baseName: (methodSelection + ifNil: [categorySelection, '-', classSelection, (wildcard ifTrue: [''] ifFalse: ['-', protocolSelection])] + ifNotNil: [methodSelection actualClass name, '-', (methodSelection selector copyReplaceAll: ':' with: '')]) + isSt: true + useHtml: false! From lewis at mail.msen.com Thu Sep 17 20:36:07 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Thu, 17 Sep 2020 16:36:07 -0400 Subject: [squeak-dev] The Inbox: Sound-ct.70.mcz In-Reply-To: References: <2ad81ea635c04521a6b1d99c415d9569@student.hpi.uni-potsdam.de> Message-ID: <20200917203607.GA20660@shell.msen.com> I love the idea of having more examples like this, although the data size is rather large. Maybe someone can suggest a good strategy for handling this? We don't want to start adding 7MB sound files to the image, and we know from long experience that web links tend to go stale in embarassing ways. I Squeakland Etoys, projects are stored externally and served from a reliable well-known site. Maybe something similar could work for multimedia referenced from the image. We certainly have plenty of spare capacity on the squeak.org at the moment, just as long as somebody doesn't come along and make us start paying for it. I don't know the answer but it would be nice to have a working solution that didn't go stale every time the intenet gets updated ;-) Dave On Thu, Sep 17, 2020 at 07:56:22PM +0000, Thiede, Christoph wrote: > Hi all, I'd like to finally get this done. > > > I propose to use the following cover version instead, which is flagged with a CC-BY license on YouTube: https://www.youtube.com/watch?v=tcQqXLwHeXw > > > If there any objections, please let me know soon, otherwise I will upload this version. > > > Best, > > Christoph > > ________________________________ > Von: Thiede, Christoph > Gesendet: Mittwoch, 2. Oktober 2019 13:57:45 > An: Squeak Dev > Betreff: AW: [squeak-dev] The Inbox: Sound-ct.70.mcz > > > Good point. It's at least not the original Beatles version with lyrics, but rather a cover version. Google does not recognize it. > > Can't we assume that a copyright check has been made before it was released within the old Worlds of Squeak? > > > Otherwise, hypothetically I could replace it with a license-free song without the same melancholic touch ... > > ________________________________ > Von: Squeak-dev im Auftrag von Taeumel, Marcel > Gesendet: Mittwoch, 2. Oktober 2019 08:53:32 > An: Alan Grimes via Squeak-dev > Betreff: Re: [squeak-dev] The Inbox: Sound-ct.70.mcz > > What about the copyright here? Is it the Beatles song? > > Best, > Marcel > > Am 01.10.2019 23:25:13 schrieb commits at source.squeak.org : > > A new version of Sound was added to project The Inbox: > http://source.squeak.org/inbox/Sound-ct.70.mcz > > ==================== Summary ==================== > > Name: Sound-ct.70 > Author: ct > Time: 1 October 2019, 11:24:29.223339 pm > UUID: 2f133210-3aa3-7140-96bf-52dc03b1dea6 > Ancestors: Sound-eem.66 > > Add example "Yesterday" for SampledSound > > This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html > > =============== Diff against Sound-eem.66 =============== > > Item was added: > + ----- Method: SampledSound class>>exampleYesterday (in category 'examples') ----- > + exampleYesterday > + "SampledSound exampleYesterday play" > + > + ^ SampledSound > + samples: (SoundBuffer fromByteArray: (Base64MimeConverter mimeDecodeToBytes: > + self yesterdaySamples readStream) contents) > + samplingRate: 22050! > > Item was added: > + ----- Method: SampledSound class>>yesterdaySamples (in category 'examples') ----- > (excessive size, no diff calculated) > > > From eric.gade at gmail.com Thu Sep 17 21:06:02 2020 From: eric.gade at gmail.com (Eric Gade) Date: Thu, 17 Sep 2020 17:06:02 -0400 Subject: [squeak-dev] The Inbox: Sound-ct.70.mcz In-Reply-To: <20200917203607.GA20660@shell.msen.com> References: <2ad81ea635c04521a6b1d99c415d9569@student.hpi.uni-potsdam.de> <20200917203607.GA20660@shell.msen.com> Message-ID: On Thu, Sep 17, 2020 at 4:36 PM David T. Lewis wrote: > Maybe someone can suggest a good strategy for handling this? We don't > want to start adding 7MB sound files to the image, and we know from > long experience that web links tend to go stale in embarassing ways. > How about some audio from the Wikimedia Commons? It's unlikely the URLs would go stale for those. -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Thu Sep 17 21:23:38 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Thu, 17 Sep 2020 23:23:38 +0200 (CEST) Subject: [squeak-dev] Not getting all mail? In-Reply-To: References: Message-ID: Hi Jakob, Your mail server considered that email to be spam. It happened to many other emails on that day and also today. And it also happend to a few others using the same email provider. Levente On Thu, 17 Sep 2020, Jakob Reschke wrote: > Hello, > Over the past week I have observed people reply to some messages that I didn't receive from the list. For example, I do not have Christoph's message to which Eliot replied below. In one instance it even looked like I did not > get the original post, but only a reply. I checked my spam folder, there are no Squeak mails in there. > > Do you experience the same? Or do I just always see cases where one person did not reply to the list, but the next one does again? > > Kind regards, > Jakob > > > Eliot Miranda schrieb am Mo., 14. Sep. 2020, 21:49: > > > On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph wrote: > > Which completion are you using? Autocompletion already behaves like this. > > > OComplete > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Eliot Miranda > Gesendet: Montag, 14. September 2020 04:45:30 > An: The general-purpose Squeak developers list > Betreff: [squeak-dev] autocomplete exit   > Hi, >    would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? > > _,,,^..^,,,_ > best, Eliot > > > > From asqueaker at gmail.com Fri Sep 18 04:42:38 2020 From: asqueaker at gmail.com (Chris Muller) Date: Thu, 17 Sep 2020 23:42:38 -0500 Subject: [squeak-dev] The Inbox: Monticello-ct.728.mcz In-Reply-To: References: Message-ID: Hi Christoph, +1 for the multilingual change. However, the UI does already support the ability to diff between _any_ two versions via the existing browsers. Simply opening the History browser on the descendant, and then select whichever ancestor you wish to compare to. Best, Chris On Thu, Sep 17, 2020 at 6:47 AM wrote: > A new version of Monticello was added to project The Inbox: > http://source.squeak.org/inbox/Monticello-ct.728.mcz > > ==================== Summary ==================== > > Name: Monticello-ct.728 > Author: ct > Time: 17 September 2020, 1:47:00.019813 pm > UUID: 7d44995a-f19b-ee49-b16b-c710c9f2bce6 > Ancestors: Monticello-cmm.726 > > Implements comparing a Monticello version against any of its ancestors. > Not absolutely sure about the code quality, but it's a relevant feature for > me (until now I always used to consult the Nabble archive). Also improves > multilingual support. > > =============== Diff against Monticello-cmm.726 =============== > > Item was changed: > ----- Method: MCHttpRepository>>httpGet:arguments: (in category > 'private') ----- > httpGet: url arguments: arguments > > | progress urlString client response result | > progress := [ :total :amount | > HTTPProgress new > total: total; > amount: amount; > + signal: 'Downloading...' translated ]. > - signal: 'Downloading...' ]. > urlString := arguments > ifNil: [ url ] > ifNotNil: [ > | queryString | > queryString := WebUtils encodeUrlEncodedForm: > arguments. > (url includes: $?) > ifTrue: [ url, '&', queryString ] > ifFalse: [ url, '?', queryString ] ]. > self class useSharedWebClientInstance ifTrue: [ > "Acquire webClient by atomically storing it in the client > variable and setting its value to nil." > client := webClient. > webClient := nil ]. > client > ifNil: [ client := WebClient new ] > ifNotNil: [ > "Attempt to avoid an error on windows by > recreating the underlying stream." > client isConnected ifFalse: [ client close ] ]. > response := client > username: self user; > password: self password; > httpGet: urlString do: [ :request | > request > headerAt: 'Authorization' put: 'Basic ', > (self user, ':', self password) base64Encoded; > headerAt: 'Connection' put: 'Keep-Alive'; > headerAt: 'Accept' put: '*/*' ]. > result := (response code between: 200 and: 299) > ifFalse: [ > response content. "Make sure content is read." > nil ] > ifTrue: [ (RWBinaryOrTextStream with: (response > contentWithProgress: progress)) reset ]. > self class useSharedWebClientInstance > ifTrue: [ > "Save the WebClient instance for reuse, but only > if there is no client cached." > webClient > ifNil: [ webClient := client ] > ifNotNil: [ client close ] ] > ifFalse: [ client close ]. > + result ifNil: [ NetworkError signal: ('Could not access {1} (Code > {2})' translated format: {location. response code}) ]. > - result ifNil: [ NetworkError signal: 'Could not access ', location > ]. > ^result! > > Item was changed: > ----- Method: MCRepositoryInspector>>versionListMenu: (in category > 'morphic ui') ----- > versionListMenu: aMenu > + > 1 to: self orderSpecs size do: [ :index | > aMenu addUpdating: #orderString: target: self selector: > #order: argumentList: { index } ]. > aMenu addLine. > + aMenu add: 'Changes against ...' translated action: [ > + | ri versions seen | > - aMenu add: 'Changes against ...' action: [| ri | > ri := aMenu defaultTarget. > + versions := ri versionList > + collect: [:name | MCVersionName on: name] > + as: OrderedCollection. > + seen := versions asSet. > + self version info breadthFirstAncestors do: [:ancestor | > + (seen includes: ancestor name) ifFalse: [ > + versions add: ancestor. > + seen add: ancestor name]]. > (UIManager default > + chooseFrom: (versions collect: [:version | version > name]) > + values: versions > + title: 'Select version to show patch against ...' > translated) ifNotNil: [:name | > + | target base | > - chooseFrom: ri versionList > - values: ri versionList > - title: 'Select version to show patch against ...') > ifNotNil: [:name | > - | versionName target base | > - versionName := MCVersionName on: name. > target := ri repository versionNamed: ri > versionInfo name. > + base := name isString > + ifTrue: [ri repository versionNamed: name] > + ifFalse: [ri version workingCopy > repositoryGroup versionWithInfo: name]. > - base := aMenu defaultTarget repository > versionNamed: versionName. > (MCPatchBrowser > forPatch: (target snapshot > patchRelativeToBase: base snapshot)) > + showLabelled: ('Changes from {1} to {2}' > translated format: {name. ri versionInfo name})]]. > - showLabelled: 'Changes from ', versionName, ' to > ', ri versionInfo name]]. > ^aMenu! > > Item was added: > + ----- Method: MCVersionName>>name (in category 'as yet unclassified') > ----- > + name > + > + ^ self! > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Fri Sep 18 05:12:51 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Fri, 18 Sep 2020 07:12:51 +0200 Subject: [squeak-dev] Not getting all mail? In-Reply-To: References: Message-ID: <6B354F73-D65D-4674-A13C-61E7C2B2921A@gmx.de> > On 17.09.2020, at 23:23, Levente Uzonyi wrote: > > Hi Jakob, > > Your mail server considered that email to be spam. It happened to many other emails on that day and also today. And it also happend to a few others using the same email provider. I have taken action. I have requested to remove our mailserver from the barracuda spam detection list. I have purged all deferred mails, retried mails, removed all bounces and pending messages to all lists. Let's hope that gets us back to normal. We need someone watch the mailserver once in a while… -t > > > Levente > > On Thu, 17 Sep 2020, Jakob Reschke wrote: > >> Hello, >> Over the past week I have observed people reply to some messages that I didn't receive from the list. For example, I do not have Christoph's message to which Eliot replied below. In one instance it even looked like I did not >> get the original post, but only a reply. I checked my spam folder, there are no Squeak mails in there. >> Do you experience the same? Or do I just always see cases where one person did not reply to the list, but the next one does again? >> Kind regards, >> Jakob >> Eliot Miranda schrieb am Mo., 14. Sep. 2020, 21:49: >> On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph wrote: >> >> Which completion are you using? Autocompletion already behaves like this. >> OComplete >> >> Best, >> >> Christoph >> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ >> Von: Squeak-dev im Auftrag von Eliot Miranda >> Gesendet: Montag, 14. September 2020 04:45:30 >> An: The general-purpose Squeak developers list >> Betreff: [squeak-dev] autocomplete exit >> Hi, >> would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? >> _,,,^..^,,,_ >> best, Eliot > From gettimothy at zoho.com Fri Sep 18 12:19:12 2020 From: gettimothy at zoho.com (gettimothy) Date: Fri, 18 Sep 2020 08:19:12 -0400 Subject: [squeak-dev] Levente do you have PG3 running on 6.0Alpha? In-Reply-To: <1749d745076.11464aa3824895.4339457474792953053@zoho.com> References: <174922b1b51.fa8febbc51549.1123089187633474927@zoho.com> <1749d745076.11464aa3824895.4339457474792953053@zoho.com> Message-ID: <174a1277674.cd8a11c735873.451723395333825370@zoho.com> Levente, Disregard. Password issue on the db. I haven't used squeak/PG3 against a db since my last workstation died, this machine's db install does not match up exactly with what I had. The issue on XTreams remains. ---- On Thu, 17 Sep 2020 15:04:39 -0400 gettimothy via Squeak-dev wrote ---- Hi Levente, The connection works fine when installed and coded on a pristine 6.0 alpha. It is on my 6.0 alpha image where I am importing my Parser work that I get the following error: 'Missing transition' (MyConnection default executeQuery: 'select 1 + 6 ')  then callstack... PG3ConnectionPool>>getConnection PG3ConnectionPool>>createNewConnection PG3Connection>>startup PG3Connection >>processStates PG3ServerState >> recieve:from Error is thrown in: receive: aPG3TypedMessage from: connection | action | action := transitions at: aPG3TypedMessage class ifAbsent: nil. action ifNil: [ self error: 'Missing transition' ]. (action at: 2) ifNotNil: [ :actionSelector | connection perform: actionSelector with: aPG3TypedMessage ]. ^action at:  I have both images open side-by-side, same connection args,etc.\ trying to debug now.... ---- On Tue, 15 Sep 2020 11:50:37 -0400 Levente Uzonyi wrote ---- Hi Tim, Yes, I do. On Tue, 15 Sep 2020, gettimothy via Squeak-dev wrote: > Hi Levente, > > I am migrating my work to 6.0 alpha. > > Getting some glitches and wondering if you have gotten it running there. Which versions of the packages do you have in your image? Can you describe what problems you see? Levente > > thx > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Fri Sep 18 12:21:49 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 18 Sep 2020 12:21:49 0000 Subject: [squeak-dev] The Inbox: MorphicExtras-ct.279.mcz Message-ID: A new version of MorphicExtras was added to project The Inbox: http://source.squeak.org/inbox/MorphicExtras-ct.279.mcz ==================== Summary ==================== Name: MorphicExtras-ct.279 Author: ct Time: 18 September 2020, 2:21:45.836093 pm UUID: d8c7d976-398d-49a1-96de-16a8bbb1609c Ancestors: MorphicExtras-kfr.276 Adds SketchMorph examples. Replaces MorphicExtras-ct.267, which can be moved into the treated inbox. Updated with compressed forms (which significantly speeds up Shout). This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against MorphicExtras-kfr.276 =============== Item was added: + ----- Method: Form class>>extraCook (in category '*MorphicExtras-examples') ----- (excessive size, no diff calculated) Item was added: + ----- Method: Form class>>extraWizard (in category '*MorphicExtras-examples') ----- + extraWizard + + ^ Form fromBinaryStream: (Base64MimeConverter mimeDecodeToBytes: 'iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAA4UklEQVR4XuWdB1gT6fb//d3733vvdsvaUOkoVZGekJCGnY6Iiljo2HvDQhFUioIi9k4HsaAIKihN3XVXd9e19wZY1oJAIJTzP+8ALqEJmAA653nOE8hMMpPMZ06+7zlv6UJ/o7/R3+hvn4X5BmUbevtlKtD/m6C/fZbmE5w5CiEO8wvOSVkTkvsEH/9e4X/2mcf8ozPp/+3Q3z4PiIMyLXyDcgJqIC7wCcwuX+J9BmYtOg4uMw/BNM8EmOIW995mcqQ//b8t+lvnlRMBiRzvdZmbEOJnfsG5FSsDzsECr1RwnZUEU9zjG/hkt7iyqa4HJ9L/m6O/dRrzXnt2EEbiLQjxW3RY5psObrMPw1SPhEYhbuBuceX2Dvv49P8m6W8dbkO03eUXr0q55L/hPCDUsHj1aXCakdgykOu4o2vsUWuPpB/p/43S3zrM9A3mC0xH+5wjUdl7fRalj1sLch3pUTTJOWY4/b9V+luH2GDGPBsGe+ETt9lR4LXmLLjNOdxmmGt9kmtsMv2/Wfpbu5sBc+5mI/bCd6Mt12KDLwWcpie2RlqA7cQdMM5xd0OgneLW0//bpb+1q+kazA9jmCwC4mNsAsHOcRdMRkhRMrREVoDF2M3Ua1n85Q1hd4vdQf9vmP7WfpqZOW8FRubSWqBrnWO6CsxsQmH8lH3NAk0iszF3aTXQvGWNRe/Do62Pf0v/b5r+JlXT1nO3QgizRlr4l9s7RlSNc9wOYx12gP2UvTBh6n6wm7QLbCZsAzPbULAcF06BXT9iE4lBICYwG5ksLDFiLVwx2S32oevMRJi5MLk2gv880SlSnv7fOP1NauYxOzrEfU5M8cqADFjpnwGe8442KymIRp7kEi0G9CTnKOAN9/4Q 0XUMPV5++32/GTL9tZbNXZJQigUYmL88lbzmhoNLlCb9v3X6m8TNOyBrDKbjHpCUHKn2uc850qbMhaNLDHCHrxaTKCpqllF4CEP0SSNHzApa4Xe6ihxn+vxjxQ7OccPo/+3T3yQLc1DmDATsFYFsqfeZVmUx6ruV/RaEePEHmA2M55X+2E3hEh7GE32UspLh5NlzYkTkWPOxRG7vcpD+FUP6m+TMJzB7BVb9Skjlb6lPesvL143llZ2jwcR0pVh01jOafenf//7fbDyULPqWrl272lhaLr/lE5hVtQSrjChZFtL/KtDfJGJ+QTnr14RUw0xkhksTHYpa6qSBWBdmzJCUDdGZOwUPpYVOoO6PPn2ottmoJSuOv8ObiRzzGM9hc1f6Xw362yfZqoAsZ/zZF1V3Lsposndca1wwyk8MaB0Djxtduyvr4eGGoG9C/w59HDm+q9uetb6BWaKlPmcKHKYd0af/FaG/tU1iBGWZ+YXkXq+Nym1t/DUK9Ei/utH5ep9++gTUfejzag7vK3ZT+ad7r16b/swvJGeXr+85+kdp+lsro/L6THPSb5nA7BuU88kSo76TvHQt0KoatrF4yAASjEljEP0bdOv659Sjl6zVyoCz6vS/OvS3Vln88WsChPl5bbfPT+kp11ROerj5ug9AKw0a/QQPW5uSM0P3auS0fkKnf487+lvrjD384Y8+ITlH/4E5WaIwE58w9QAwuUs+AK2mZfcAD21ZcwpK6LMaObUhNbDT3+hvLdTMIb8YrwnJiUeYq1avy4QZC45JHGZH1xjgj6zfIHS/goe3RXdEn9bE6ZEGozn9rxL9rUW2fHWqwprgnDO1mpkMk5I0zFS6zk48XVfdf2NRGp6CBnocunYTp+iAbkf/K0V/a5H5BWUHom6uJECTqpw0YCY+2npDA6B1jTzP4ykEEcXT1PkpyslO6dXzp4V8Ltve0FCvH/2vGP2t6ejsnUoyGkUEZlLAcJVwRkOsMWi2r gHQqKEf4mmMaOzctLQ0RuhoD46Xl5MtkJHp+05DQ/2FsrLiNZm+vffiZlX6Xz36WwNDmC8SmEnhZO7SFKnATLSz9fiID32e67oBc+6hJk5tQK9ePQ999dVXVZMnT4aAgADYtWsXdO/eHf7zn68e43b6p/Dob+LmG3TerhbmBSvSJA6yg3MUmI/d1CjIdQorJ5o4vUE12Y8AhHpO36+/8v3q/7q44f8CokLof/Xobw2BDs4mWQ1qZLakiydEYgwbE0B1ETWu6cDfBNCnmznFPuh96X+l6G8taQjaYWTOI0AvWnVKKpqZjFipLqIsbg7oX4bqz5aj/xWhv31adF6b4eofcr6SjAppbrRJ24GOxZyzLzW8igx8JSNTTAQrUX4sqQ/0LT3jeUz6XxH62yfZEu+Th0h0XrX2XPUEiVKI0A5OkZQTuGunKrCfvIfS1Wy+Vy3Qbw1Y813of0Xob222vgOWDnCbHX2b6qzvfUZqeefm+0Rv+RClh+jNH0v/q0J/a7NpDpnhPHHatmIC9OzFJzoEaNKno3a0twFz/hr6XxX6W5vNkLXgqLltIJa5s6k5mTsCaCJDTAQrqoFmLThM/6tCf2uT6RjNm4G6tchEsBzmL0/uEJhr3XTUmlodfVXbePZA+l8d+lurTFt3/mQj9oLrtdp1+Bj/Fk3VJS23sNv8oWGorOpJ/+6h9LdWSg3M+RqxF1URiJicJTDSIrBDgZ4w7QBmO5ZTUOsz5y2i/xWiv7XYVNVn6GMkrCDwmI72g7EOO6nJFDtScpCbieSqyTmhrqf/lLn0t5aZorJDD0Pj+RmkqDF+ylaY5hHfoSCLdSm1CqmVHfe0jebr0v9q0d+atX79Fn1jYDwvkEAz1W2PVEaifIpXz6BEAS3UNZzjSf8rRn9r0vQMZ/XGn/IUAsw4x3CJTkUgKbdz3A0MTnVfD0P2gks9ZfS/p/+Vo781sL4DxvVAQMIJKDbjN8IynzOdDubqhuFBYAs+lM FfKw0yG0X/q0d/a2AunntWWNtvqHCdGQkr/M+C84y2FFDiYKp7LDh5xqDujqH+Js9JumFYdyYlPaO5K+l/9ehvYjZnRbKyX0jub7V9nV3bMOjV2TMKtq1fDD/HjIEnqfrw4IQRnI+ygAOhM2HxonAJ9+v4Z/AsSqTL9L+C9LcP5hec7ecXnHN2TUhulZdfBrjPbZ1uJtF4T8g8eHDcCIqzVECYI+4l2SpQmMOA+9lLIT05GhISsyA65jwEBR0Ct5kxbQJ6olMkcIatqpUdRfS/ivQ3ynxDsidWLz2cA/OWHgFzm1AQjFgDxuxlYGC0EDS13ClXVp4EigoTKFfXcKWe0xrsAfqG8yFgoUOjIIv5LxwQ3V4BFQVHoUr4DKpKHoPwQRQ8z50FPyfMhIObfGC1V0Rbq4ZV+oy5Y+h/NWluXMESV1v7kPsskwUwUGUCyPQxh5+6D4fuPwpa7D278WG/95DmYUYv/XUkiO6vg4pX6VAl+hsqC29AxYPtIPptMoh+sYGyn23gbfY4iESwXWdEtThK184ZPVR/7lr6X1Ga2n//w2L/+B33KAL5tjXwNubyMlx4cHSQGLwkWufs0oDfo9TgdTp5biCUXrEB0aPNUPHmIgL9Firf/AbldzaA6NJ4CuhaL75gC9kxc8CtBVBTS7vVRGnU0VH0v7I0tR++5e7q9oOg8lNhJm5qYNwgGqdu0gT5vjxQlecAT48F8x0M4fg2e3h8eQuUv7sKVWWvofJlJpTf8EOIx4oBTVx40RaStnu1bGZ/l2gq42HEWviU/leWhvbjN9z13X7gl0gC5h5d+RA4W6cB0A6jjBrs27enKRgOHQvOk/0gZG0CbA8Og2Nb3eFaoiW8yrBCyWEtBvXrLHvw8QpvUUeo8VP3k45TZVjdpH+nf1pJja80dRHm95KAmbgcRuHzu9XFYH6cPAgGynEb3b93j1GgozUDhpn4gZryVHzOFJQHCGAkczgkBI6B0oviUCeGzgF r6y1g77iPWmOlObjNx26qGsqYHUT/q0wT+9e/FL5D3ZyGQFdJCughA03gTbp4dE4J1cJozGt0/369LcFIZyHwWN6gMGCc2DbG4GGYu7YSAzpzjxPwjDE1p7cImIylMHzUOhg3aQ+1vFtjetrWYWeuunr4v+h/tWlg337NnSIpkGt9mrlhA7mxxlO3yf0JxBzGCmAZLEW4LcS2MYcMg2dp4kBn7JwGbMNloDt4FmgMdAYluQkg398OhurMwsJKBDXLUj2oi3UN3L+j/9WmgWFkfiRZoPmwY7l2A6DHsJhN6O1hlMwwZfuC3pDZ0Kv7CLHtk81GiEkOksbb7+8ODN2FMETdAwYpTga5fmOhz0+jq9+v2zBQUXEE3jBfavBsNdDxlVZjt+nQ/2p/6Q3Br02cJR2d1RQ4cDNBVQxmkqbr35vXpH4mkdbUxBdUlaY02H48zEwsOr/NsgUXu7mgh69RU5mG0d0eZHqZ441h2uj7yw6wBTZ3ZZWVffhi+l/xL9i+/g9bq+sP/IeSBnqYoTFV0q4L9N5V2vATZj4a21+mtzkwUQtzmaso2VB32yB5U7iZZCkG9JPUccBlLARtDU8YqDgJZGVsoFePkS05t5fffs0LpP+V/1Kj8/e8/ZhzrpI00HamzAZyY5GjftMZkX62CPNKqoHXFyNt3W2jWcPhTaZ4hiNtqyMYDJ0LmoNcQFG2+ehc31FeVaIX0//qf2kNwf+xJ0oq51zfbXgNgd6yGCN0t8YjNGnQmbJ9QEdzJvSsp5+XOY0Sy0OXXrSB9fNdqBQfkSetiM5iTn8C6ln+6b5Gn+u5//At3xNhfiwNmGtTdvVL3q/TB8LhIC0ImK4LLlaGGHm5oKo4HGWIKZWlECDQ9fVzr+4CSN9mLhadX2aMhXGjPUFL1ZW6Efr2MqNy1q05v+//y/OnNbxnOV3+L3aunHzt/8/tuvz7WZr8pM/xs9g7hHG5/CU3eYJlYGYVAKPM1oDpyIAPPmzU WjC33QwWY8MpH2MVKradw18NTPYyMDJeAtpDZ4KauhOVUVBQGA8D+ttCn55jEERTCGqkSviPD4RX2aiN0+dBSuxG8F+9G6aOD0Sg7cXAMxrcMF13OcYeI7knqCqT6GyLUX94q2+4bv/l0aezUvr6QT3OrlUTW5Tx5vHew7JClM22L//JpOCU/OnHqf35tUDnn5AdmX9SdkJn/ky+QTmjfQKznfyCcxPR73mvz6r0WpMBC7xS2zjypF7hwjUWJk6LhHGOe8HafiuY22wCh4l+8FcStwmg1aD0CoJ6zx8qXqZBRckTeHnnNFw65gPbV9nCFEzTcfWGQey6MSCsVyHc4+OI0dkFVBQcavLVpq0GuiuW+GkB89OU/prkMSNIayt5zEvs0/fpyQEDC9LkjlyIUFx1IUJBUJAmX/o0TW7cs1S5BRTQqXLx6Ps64+exc/RT9w3O3r8mJPcJgly1KuActe6Jy8ykdhnfR0ai3DnGagj0eS0o/cMBRA9DoeJ1LlQi0BUFKSC6teZDD7u3WdYN+nAUZtuA5/hpmKqbQhViSLqvLZLoh285G794mO8c/MEkP03+8qvkXoyzwVrbf907wDg/VXYzwpqAj1vuHZKLuBCu6Pn8lILw+SlZt2cn5aixagh47o1YxRMJy1UX5h/r0+EzxxubOv13issWVb/g7GAE+RHpnL9izVlYsCwJps+NAreZkeC1cgt4ztlPjSYh4JGxfR6zD0p8fB/x5UtC4WLMGMg/pQOFmTW6+oIBlF1zh/Inu6Dy3e9QWfwQyp8lguj6skZ72NX6hQP2oD+4OjoP6GvdJrlBRehvuaG0iNAFqfJpCOit9GAFnYxgrdgnR+VmPU+Tf5eXKncGPe/wioHT81Ply56lDFhKgC5I6aeL22+djxj46NAKjScYyd/kpfRe2hHnvtgrtavP+vOzEOIXZNEeMvZv3rKTMM0jFkLXBkDk6iXwZPEoeLeEA2+sOfD3PB5cn28DN/ys4 f5WC0ib4w6p26eDF0bVlSvDpRa1583bDrtDV0P2sfVQcOsQFL+5BxWFN6D84Q4Q/eHZJMz5p+2AqetWUxW0hd41VcG2Ac2jB9B5JwY4IdBFl2P6qGYED466ul/FGf8vxCh9tuCUQnlWmMquvJMKlQhuKEbtVXkpA2xx+9P0kEF5x7zVn5HX3j/x06S3UT/2keZ56uoG/3vxyowfVgXc6L3K9yzDOyjHEyNxCsqKcrI2IFmBynlGAkbkfRAbvgj+9B0Hb2cjyOPROXWcW/34dioHCjfh3w7oW/lw2WU8THWT/rReTtPjYdWaFIiLSYObWVvg/c/OVH9nUtquLXEXXxgLN5LdwGfxRmAxlsAgbAz272tFlcvbCnS377hbadMoRFjT8lJkV59Zp+6QHaYZhZBmIbw7n5+Sr/xlu9Lpu4mK5fj3NoR8B/r8vJPyL04FqpamBKg+xH2fPUvpJ9H1PVavXv2v6eHH/73a74+BKwLP2KwKyVqM0XeXX0jOZYzE5avXZsJSn3RqxVYyi5HzjEQKlrVrQyBy9zLIH20Kb4bVA7kZf+vJgRebhtVIkPadU2Pm7EjYHLAZjm73hrMHl0BixBoI8t4Bs2YehhkzjlLu4hILZqODQHuwR5vyzzVAb6MN0IfW9hyBmYx7f+xTZp8NGXzmXpJCdH6abBzq68c3YxWzL+9WFhWkysUgvHvQtz05rlBxMkCtIjNs4DsE/AwCPfJTjr8y4KyuT1D2uiWrUrf7BuZsxaibjl6AEFd4+WVULlp5qoo07DzmHoGpHk2vZTLTeQ/8NmocvBnRcpgpR/h32nth9IzukIlipqK7eaLen34EAT7yAeS6TrYRn+SwF5iMRSDbz4b+QNe32PkqtvHLFPTnTvzv1wjqa4zSPtg43Hhlj+oR1Mn3qhuHcnczN6r8nX9S7hgBGiWH+4MjilXJfupVl/coV+WdlNv4LLnPR2fqmbvocFefoCwzn6Cc9d7B2WGYVtuBjb hMlA15KBmKMKVWtmhlWtWcJSfAc/6x6tRaKyZE9HA+APHWc+Fns4lQMAUjtCWCOqplQD8NGAXr1gR3+AxILh6JNVAfbdbJPu7uiWBjvQl0MS/ep+do+gNdawmLVZft8RwwOj+1/1wE9t2NKCXPzA2Drz88ongfc807MMMBOZsGvnt0TOEibt+HgIfejFUCBBruH1aEvFTZxWcj7cx8g3Nd/YKytyGgYX4h2Qcxwl7Avx+vXpf5aplPeinCCtWwHqVgJatGNRdt2+oko0FmKAoOCoQF83bCrrBVsG/1ckjz94Ac68nwfJYpvODyoTCiGuZ3qzmQGz0NXDooOjc6QQ2C7Tn98EfBrnUPjySYOHE3FbnlsNBDa6B3eMgNTFiqtv1OxE+KCOtV1Me52aGaUTdiBpZjliMBIa64skcZrh5QuoTbj6GnXtmnAmnr1QD3rXyULLfBzoo9Y4y5/R37ibOFzp6BVc7TtyKwe/EnPBY9EcHtKDjiKHedGUVlPlauCIc1qzeAl1s4RM1ZBOljnODqKBvwnxuM5xjbqearIzLEFWVIS6GujdzTpkXByOH+1BQMtNXQsUtVhh9aouL05Hj/eQhw8dWDKlv/PKBajn/fxQbjX7fjlSB3s8ot/Ps4Pnfj4taBgGk7wL9fFaTK+svIyHyvpqapb2LCmc3j8X/m84dVCkzHgMDUGoaPmgqWdt4wblIoODjt7dDZ7muhnVqTeyZZjenOmJt2jemUkzDWyhDPFsiQ+mA7Ou4DtvFSkK2J2LQCmljiUrWkcyHKUxDSq09PKFz7eavGtYI0hQqE+Cw2AqvSAlQxRSeXS7Ia50IHwV9RygToh4+Oy86p+z6ysvKqJiYm4xHsRD5fUICPVQg6sNg84HAR8mETsUy8DOwdsVTstK9DAf9cXLzR2Dqwp06NBB5nJZEiW2kF9C7XvuzEpar7f92htBezG69+36eafztepQolxp9Edpxep1qCDcOHz1Lk32VsUIW HRxUBt11GDT29qffkcrkOfD4/DaHOQrgruVyEmsMFNtuEAtyEYwYmPDvgD3eC0ZaLwHZCMEyYuhMcXaPoD3IjPs09AcE+REXs6a0Am7irS/xfbm6H5w0dGtGVPtJjgRIjabnab9ciFW/filMRng/XRGjl8xHososRKvAkWQGwsQg54QPJ8yKM2L+35H0R5D0EaIHAFIjj3xituWBoZAh6+nqgo6sD2kO10YeCvqEJsLm2MNJsLgX3JBf6w90o2B6H2hCxjwo9PQ8HKCiE0GdC9MjFKnYnfFULsZgC2WGD4W6iMuSnyb3866Ay3I5XhDsJSnBljwpQ1cQ0udsfez82m62DAItqYa4FGqM2sE1MgMFggBHDCAwMDUBXTxeG6gytgVsb/2cAi2ONcM+GsQ5hMMn5IP0lSj2w3VspRXDfMnf3w2sVFdO+pQ3USUtUN/y8VaXyyh41+HWHOonGQgL4pR0qcCNGCW7FU3KjCCP0pebex8DAoA+PJ0iuC3N9J3CbELCZ1WCTqF0Ldy3Ytc5kjYEJ4+fBnNkR1NzL0uhY9Dm6c03DsaVg434id/dDAbQBertzjz5HV6hdxGwHnNswBB4nY745RQEyggfB1UhleHRMEUh+GqGO/IjUmCMQiEfnppxEbBabTUFd6wRufQP9OpJEG0Ln6cG1GDac22UFO9fNhXlzt9Ef6to0X40MmT6jRcWZYg7nwgDaQB23RGXJoWXq5WcCNeGX7SRKK0Cytxr8tpvIDQUiOcqxauja1Osx6upxOLxIjMBPWwJ0fTlSH27ii6cx4fVpPjxP5WJRhw034ljwy/7hkLp1HMQFTIUdK2ZByBIvWDp3I160XXihY2kLt0cLCjOenkeeurnF29ECaOvB3/4ncbGqb5KXBpwJ1MKshhIc91GDP/crU9GZ9LjLPznAqrn30NTU/J++PrsHk8kcjqm7lQhqMgL7qKVgk4wI09j4A9Cbl7Gh/PwwykW5w6A4UwB5Jzhw M5oJ1zbrwV+b9OAq+h9hBvB7mBFc2siC9PWjId7XEcKXLwD/Rb6weE4YZgoO0AJsJ4+PaewjVQh1Jm2i9H7Lb747ulL10pEVGnB5tyolOe4nVcuNglS5i9d2fvNDa95PQ0Pje4RbXltbh2liwgtAwFMR3GcIcGVTUGPaD9N7HArs3B0cqLgw/IOXZmMZm0Tr/UZwI1yPgropJ7D/iaBfCWVQoKeutYBIb2fYtGwR+CwIgPmzt3yxmvxD47HxzEfF1KkJ9NHTv2+R90xerVae4qcOuRGKcGW/PDw8Jg9kRMunvreurm4PDoevbWjIMEd47zcXrR2s+VCYbioGNPHyLFMoSWRBcawxvMVIXbDXCB7sMIBbEfrNAl7X/wgzhF8R8nOBIyDBzwG2LJ+HkK+FOTO3fVGQk8ZjYxEb/3/r7p40gzZQX96jmLvJRREWWsjAAnMZ8BorAyEuMssleQwOhzMDI3Z+41ALYNsyPiUz6gMtOsOHskS2mAsTWFAUZwzvoo3hBUbvRzsN4DYCfr2FgNdCfnEDByO5Jexf7QrrF6+i5MrUL0CXVxdokuo0Hon0SEqiDdDTh/U55cHvW+5pKgPzRveDRZYyMHd0X4kveSArK9sbq4uzMSLn1QV6zAgB/J0qaABzBWrpsuOcBkA35aUIekm8MRTGYCTfZwQPMZLf2ar/UblS169u0qdAT1tnAXtXu4PfwjXYANv32cONab9Kd/cEX1oA7crt5bLCTub9wQUqELVgEGRtViy9Gi17J/+krJk0jofR2gLz17m1QC9zaTw6l6OGLjts0mKgGwechYAbw8sDDLi/Xb/VkZwAnhE4Cg6udgbfBWtg5oydn2UUJ5kRZ/fEQktLry+/ipge2M3wWoz822OopZOWa8DJNaoPn56Qu4KVwuvSOqa6uoY8Qv07yo3KuAA+ldloIDdO89oMc1OAF6NUeRtVHcEJ4De3tBxu0vDMCRbA3lUesGD2ZtSskR80+BJMJ RJN7uTRuUv59i7Ri/rJ6f/3iwY6P3VA9INDSnmHlquXHfdWBwL2rXiqs/95rBaGSOu4ampqA4cJuNt+2y8or6gHNAG8LJkjUaAbA/w9Ru9XBxnweFfrdPiVUCNIDrCFwMUrUJLshZXzAzGFOAoOrxkP61CPe0zf0ymBnuwWJ5zgHLVQc2j4lxmp/z7Re33eSYWi37YPepO8Sr0yN0wNjqxQJ/2j0zHTMRuhTpTm8YuijfqXnjMtaRCdzwmg7BBbqkA3BvjrSCY2NBmQj9mUx7sMKcg/BndW0HDYuXImpggXUtqbRPJMfO6gtytEeM3DaL4Fo3nnKeUj1BWObrEJdo4Rml8UzIVR38thASXvTqzyk+yNapVHlqveyggYVJWwULUsdtGggOepfaVeOi0/zBA0aAyil53ktivMH4P86e6Pw02KPYf8JsA5hLn+ttPrzVGD+6OWjek0YDu6xmVOmBSj+sVF6Quzu7NO+qnG7nVVnrXJWvHNJhvFkjBrhQXtcezSFI5Xg8YgNhDLjph0CqDrw00yKM8Q7uYyJ7+HGlKPjTmJ4IGLV4KLx0EKqo72Sa7xEV2kYR1t/gZdvtpu1WMgAv1ws40SbDRvuh+HpAyC+/6n7JTJvgZAd4DcaC3YJBf+JpIBTxDuW800LAn4b6MYVL787tbqCE/K9+eDuVTflI5uRE52i71n5xTJ6fKl2iYrxQsE6DArealPLQVHDXuL0vmXGujnU7xOC3NTqUGiue9ta1ySkOeFuA/x19gAJYUgAjrR2umYEty4dCm4dlz/kypH19ir5nYHh3yRQIdZyvsToDFS/7ZuTK9e0jxW5Sk1JSxtlzfIbnRCudFSJ5H7PZbpn9aTJaTAUxLHErsRinA/kl0h+12lUoJ82LB0GUbt6A6I1HGFDi5RPl8c0D7GvWwR5gr0JxstBlhI81hlh41MyusVVMozsZiSxP5sgRbT29HVertWkpDcd1Opw2d7qvcjJfmTa62wQu lXk+duV6iFDi6RX9Y809gYnIkwv95srShE+eEnzWOVnuS4tqTvxucONonYBFjSqaoECzvN7Uci+/XwarBT1lrDGiy9tyfYCLVoknOs0xcD9EazAdoI9E2qYWghN12qQJ/mb+is6Tpp+N8HqxuHBN7mbgDSq/DRjmopQsCO8naCWTN2QHul+xDq1/bOMR7q6pe//UIahgrJ1TpaIUqaxxFl8JO/JP3cEie9BJsDui7Y76KrS/REf2cHD4Non2mwZpEfuHvubw+oSx1dYmNtx++coDTU4uvPG2hLheUEaJQdN6R5nPJMwdX6fZ87c7quo7wYG5N5KFlIvxPSWer0OjNYPCdUrC+JFMF+7+Ac5f1ZA73BQs4DiyuVKD3ypBqhswSvxfRzBp/+ADfjRGM/2VWtscnonBNrbaisyPQZu2Gau3SnO5voEjNTU+czXSg/zEJ+EsL8Ar1Uajlos75fke6h4r3r6A90y6qVxmLFnMuhTIjycYIlczZSUVsa3Vup/h+usTGsEV7yn2HDsD9pGP5FZEeIRV9FaRzj/UGDnvV72H1OBZUOz3djkYb0Enyw3UCsiHM+hE9p7YBF3jX57DiJyhJHt7hHthN22X5+UdpKIYkAHWop7yWVHHQyU6tByi6N/kC3NlpTfbxRipDGI0n51VYrSRXyl41sSA6wg+CFM8HMaiGYWy8G2wnrYPyUCHB0jf6UaF2Gft3BKXrO5yQ75leXwOWkstJ/aRrXpAHQqfQH+pP7mGDkLophwsv91aV4ordTfPSBwWDWOAuYTBMwNuaCCWc0mA53hDGW88DKzhvsHEKo+QZbWjJHqN+Nm3ow6LMA2tu49zAKaEv5nVIpe6do6DcA+gSX/mBKWJaQ8vrlTUxgsdgIMasO2PXduBp0Fg84PHMQDJ8Eoy3mgvU4X7CbtBEcXSKbiNbxlaitr0+cEjuxs0sOa9TRzzEnfUEqETrZmN+gqCLlESp09RcH2TVzoFTPWkWmPSZ zetc6mfCHPFfXyXPUdnzkcE2BxzeD4SMdwMxyFtiM94Pxk7eIyRZSNp/kFDPf3iFmYOfMRZvLqSHQV0jqLtC8v5rEgT7OHt8A6C+8qNJhFcooNrRmurbm5k+pdVPTMTB8xFgYY+EK1nbLYdykDTDJJbLcxt43afDgwX07acVQfjsCXYINw3CJNwrTOCsaVAnpX1SRGtDDhwkkAnUduCvJvIZksnsej+eDkmYsh8OREViEdvwAXGHuQKWybJ6LKEewuuIcZ0xlrik11izUSm72ZhvFqmDzPvMlXlQ5wwtuMGVBB11w0p952xx98HXSpXytiy7snKcHu+frU37Sz5DyNPRfQxnw5+Zqv7OdCXd3VPvTvcbwbF+1vzpoDG+iWJST9+5ooN9Es2GcuUASEJcTiPl8/lkmkzWTzTax6nRR+H0GU1+UbfpLWSq3iprU5SS3UpTJ/1WUyRsV5azECbdVgjh31eMVZ1jDRBkcia0oK0rnRTYYpdJhFTgWzBo7FPSGalGuPUQLtLQ+7ixDLWAbDqbcnDcELPjVPnGUNkwx04ap6DPxfeeO06F85WQdCHTXpXzrHD1I9jZol8/3FoG2txB8CsR5XC5/r5aWtgPqavNmuzM8ku1edpFlIsrleYlyBf4VORyzdoMZ3Pr+u/w8P6LsGKdK7EsgP/2p3Nd39hkFXFqjDW8imaSPclXZUZPnZzZo20gE6HOCRDGgO7DsTTrlXw1nwLl1RpSf8DWEQyuqPWapASwYPxT0df4BmUDvbKkNjmOqnQCsq90QeF3cz0jvHyf76NQ42W4tGNJpgCarMKC/R3+F/pjHE2QixBFaWkMnsVisBisJw5Ve3YQ/s03Ls7n2f2cYKFHtonPGGqJMQYzoFLeUauCjizL4RaKLvPaZVw9yGV+XpfHymrzQR0zKGuQ6j7NPl98e8EkL05TFG/ZEoNPrTvkl6UllJOW3tjFgmPHgBrBOGq0NF0Oqu4Le2saEYayG+4xk D4a9C/XhyiYG5elrjeCUvxGkonSJXWYAx70N2wfoGHbVeAt+IUqFmwjpYTabE66pqRWL4L6tgfcaWdUM3YvL5X501eDKyz37o2Q8LjrJBcrTefcqNmpblaVy/iw7XO/4GBxFadxXZdmcWdIH+rj+f1FmXGll8v6xMJYp9ynHLYk3lC3PFOTWbRCKOmE/aALrkolDm5QcDhiZ38WwYLmjTpP7WGEUbkl3Uel+DnZhSQJDYnKx9BR3YYMGfDqvvMlzwF93vN7n2yVKi/aqz8aDVrbi5zlHlGjc41OOWXSYrY0/TX+JAd0JiyovsXFngdq4OR1NGoYC5uAmtxOZ8WSP8RcFdEmccVCrjp/ILhKmcttnaJfIstePwnjj8y0+uXj9Ty51osbSxqzGNbGU3bHOV1R5fsCYauwRMDE1BVu2bIGEhASwt7f/AGxOoBFwjaqBHjNmDCQmJkJ6ejpMnjyZeo7o5esRzC8K6MJoA2v81aloOTPGl8pX9+vebo1D0XFDb2zwiRo7mXdRTKqDOfV/Aut+2QVl3U++w5PZLIzQD8SAPtz5iipk/unxI4aAtrY2REVFwfPnz+HFixeQlJREwcrAht7jXQywEQwGHR0dSElJ+bBPcnIytY+hrha8OPBlAV26R7dXSbxxSgtl2yvhXlXT9sk/p2pxSjP450Qp3LKmRlrfC9eHPZNU4MCUgfByn+H2B5wu/5KABhtZnmX6vLMXVUj2I36ZLrCMmZCVlUWBSvzixYswFFN7oR7aVKf7xOW6wGQYwvnz5z/sQ/42wMzIRtxHGP9laWgqPRen79CiYx/n3MtPNtkkfZjPaH+H+eZjH5sy4O/9DNg2ThnCbZTgWpje30WnTXifDPQZBDq7DtA5pp22yvYGf6G8HIZAoN9SuH//PjwvKIADOzbAjtlDPgx4JdPzek0aAut9l8H169fh8ePHsDNsDe6jTe3T8Y1byQNdkshoEdBUoDrJrRDlmkZJN8NxQdAbc 833P3ZChfizG+2sCqTnXe6qIVgAYX/yjEqlp7mjEOiXH3rZZQk6dZfM+9sMIMNfH7I220DutolwKYwLz/eJj94m+6Sv0YP0DWaQtcUOU3rVfZQ7OsMhLaDL4rWXtuo80njvy7L40us/Lczh9ShL4fzRki6IJ+ZqUEAfmakGpckm70uyuAsqbqv80PZGIc8BJYdIbOrcTt7PmIzAJtGWQEpu8ub2IbCTYVKd5/wlC7Qoi2NYegJzzq05hyR2WXm2IFJ6EXqK/P8rPsxqUfrlPEZmAvRe1NKUHjzOKUO5srcym6vTlmNXnOZPrTuekP6DYz8foMtz2byyM7zfWzu7FabuhGXp3DDpa+l440BMqzzCCCNq6mQebzWkgCZesLuOJkzGzEQG/6Yom2sEZ416thjoswh0zjCpLT1Bf2/w6/GuKEpb+ZOici7bAa/11TZ28a0siWfGt1va7u2egUNKE4wTUIJcbSzbQFJ3pGFIgP4zUOcdpu+qxER/GmZJsgRnRBfYEypvq8q0Gmj6D72SdoR+3aZfcacB/32fyx1Sls0LRjYq6rJB8tDo5R+5kcg+b4RxzH0d0vMO78BTjQFdFEMahoMooKNt5faWxjFPNPgw5CcoBat95/jPyrM568ouD1IqOqv/9UeBJlXC1E5Y9kZpVYr591JMywkxj1y0WR/erR0Kb1F+NebvN+pBUYQBCPHmL41lUa//XIEuSWd8W5qixRFl8Q6K0vmFWKOoD2op/rJnoIc2edzjJsXoB3Bfsw7rSlp2inuoiYZhxcn5GkXV890p+lP77tWYiVH970Y/zDETatJFbAT8VpEtWFfxq+LwpoCmctCdqOz9PlQPXmHjN99aAR4bysB9xZ/gzo8/ttx7dIUH6r0gz0weXi/ShOKdhp8N0O9zOGoVv+tOw6zTXVEar6pBR6NqmB+JVmuMI/sXx+lxmwaaUyE6b+rVoX2j38XoW+EJ/yV+cqwqYYLx6WgnlZXUoFlzue APX0CM/lhRtK4/vkbY5Ac7alKFEbgcZUle2WnekYoMwWHMamR1BqCFUcbwZsVgeIG/PgV2SvBwcB+40+3H1gHcnHdF7/4jPNTqTb3/qxlqULzNsCM09L2GjTv+ZMwNp4iyTV+UnWQG4fU5jxAXi05wKxp/D+PzeXs0lr2P1TD4kH7drdULn/+tiazGY8yEjOnwDv+vojXlhXFGi7GheAa/iN/KEpih5dED+gWM7qlRPa2BwpG1I7uKTQn1PtbAFve92+IvGX/CxIBu534cREq8CxgKjxn9KOAkBnALAL/XvzvkmctD4QbddstRCxNZN0TnBRGiC/xwUY4gpfycIA9/Qd+hRBS1pMsB6evzdL9eo5Pflyaqm+DnKK53A70pizPoXKPAC/cP6iWMN1Co/X+DhWzPTdaK78OsFa9uMu/bp8H+kQaaWN/fXxLHKv3ol4x6m0zM2BFAF2Ep/9kYObjbp1v7gdyI35XpDvljFSmNLvXPncQuxyJaCTUi6TC7xd0MEMxCDG5rSvcO6d9soeygxggMgEdwf9TVzLCKeM2hXTq77eDKfLPJSiEHoRat5/dsNAVUeKRvt6e79O3/Psh40Gz/hfpAH22fjkmvF2vCfZWeHQpy/Yj9bLQcFG837GzzeVQVxTKvlCYxTcGiR4sGvUJov2/f7x/Su8vnYqt7dvl/YVZyu6pXyJJptj/Ho+06ox7vMsh5eYBRTiY6afDTWh/ow+3Q2AvTg7u9unYemOtATTT2u7U6naIzVmEMs+Lpbj3/tzs0v8xFhOqaz6iuYzdbK1WEWbRsueS7vjLqdyP0ZqJffbLLsJwsYEkAFx5qX6BfL9WCOz+1EuZu3eAvfLz03Xdw6ptv4NjXX8Nh9KQaP1THk/73P8rJdrJfGu5/EV939fvv4dYPP7QY7Kf8ASA8yGzP7Ac1ATsGnioMQE8fbTfwf7hNT78LXWyu8fdkHcNybBge9R/+XYv7ctzerKF1PVQ78E6E/sv 72wyqnu01gtKzgn8W2ZTiIkEkh9wamXFXRQUe2NvDk5074bS+PiQgqKcR0PPffgu/IaR/IKTXENLrdZz8/zs+/ytuz8H9yA1wGF93CD0ZAc/C58jrbnft+lGoX7qrSrWxKKyzgD9Ziu7+doNXd7fqbbkbqsTuQkdDoN+h395gNWBQa197Y6vusFtb9ELubTd4WHKGX/EBaCl2Kvp7rkZ1+qwZkG4jkLd694YHEyZA/qVLHzrp3/byooBti5S4ga+7jICfQ7iPItSJCPS95cvh7pAhmKvu0eTrHqj1guK9DMnOO1IDMEZiasJ0hJgsc1F8N0L//O0tOlO70NnCrBXOI9DCFXq91Nv6HvkxbL2yTNN7UgcaG6bPRsg1C/I1BC0TocsaPfpD53wCdMHDh/A4NBRvBsno7uujRkHB06dQcOcOPAkPh3sY/e90797ovvm2ilRqUVKygkTiO1sNKL+9Vf/1rS36sbfDB7t2ob916bLRUm5ndT5ats1rGAqTGCqiLNNL7QH0Y6N+jUJDtC2REUQapCPQf7FYFMj5v/8Oj9euhXsmJpSOllTj73FQ0IfIT900eMNcd3WFm439AnTvSpXRJfQrVfVst2HZrQiDvD/CGdsubTIaRn+KxSXHewpoa4U2D6spitfvU55telbaQFMjTlYMEav+EZAzEeQUlAFE1/4DEUbLPn2kmtG4x+fDs+RkeJ6fDwV//AE3tbTgaI1Grw924UY9SQFdjIWR9fQntwnbZKP4ggCNOem0zaN69OjSBiv9q//XWP5OljDQUNbII6Wj52nAfeWfKJgvI8R/tiD7cF+hGzxj94IX42TgtYcsvJktB4VLFcT83WIFaturKf0hf3gfeGLQEx4odW+++og3zv3hw+GhkxP1PwGZNCRTEeprNedEdLQwUjLZDtJ1tDiBKdX1Jj9rwwh9GVN3ZIWsW6EW8tptfZ/yTNP49gCaeBEWLkgnI6KZb38E5KcI8ZuZslDkrwQl QcogDFEBYeigat+sJu6bVKufx31KgpWhZL0yFAcoQeFKBXg5oR881ukJd3t+XIPfrpMpeaDRC4pQ60oq04Hv83dJrJEZ/cltwsIs5YNJoxC9LGj0T4ZtfZ+yM7wt7QV0nrXCR6F6OKgHvJzcH0oCEeIwBHXbYBDu04PSGAY20IyrPb6e1z4fy4TSgwYg3KtT/bpwdSjZMBCK1yHcKxTguZ0MPNLo8dFsS21vvberJTfnHQL9oijGUJv+5DZhXoN+ILloSnZssJSb3Nb3EZ3hrWovoEklrtGcc0+MyMxelGQoCsBIvFULSg/oVwPa1ghJFvIhsEfjjbBPF4Q7hoAwQhPhVoG/XQdQMuZe/+ajNum2WiqhMYn4OZ7Sn9qPRWkrhRwCND5ubXOEPs3zaC+g8837N4T5px/hxViZalmxDUGONqJglMo5EMgx0gu3D4YSlCfvFsnDE6OeTWrtB4N6SkxDYyHlZ/oT+1HZIRdKNQytFc8FmfVtU4cUUQrXst009NpBKCn+yfne69cVXjr2Q7gGYUTWkx7IjYF9wICSJMVrleEFSpHGZMizYTIS64UnjGem0J/Yj9hGc9n1CDRpGN4JNZdjtOU9hEdMuO0FtHCvLhQuV4CHaj3gse5PVKOvBBtzRPe2C8j1ZQDqbuFuHdTZg6hGaF2o7/buio1KFUq2SAjoPfQn9uMNw2UIc+EmG6XSIEFPQZuyHOcEo9oLaBIZiT6mMhbEN6tXS4wOgFkM7EhDzKYow1OTXlRPQCKDCsz7gnCntsSyHCVxjAD6E9uy9N1DIjs2Wsi2aTYc4WmOQbsBXetxqEvjO88EMBTUsQzqBvtws6HOlmTnpHf7h/rRn9aWNQxP1zQM2zRnWWmKiQoFdDv1h+7MTqX+SIoQpRHJsEgwZVeGVcKD9Ke1JTraUj6opmGY05bXQxKjF5nCgP5AS/FGSWAVlsQZ+9Cf1hZY0JgByzfVNAzbXC3MpT/QUgb6ZUk8cyL9a W2BrTPry0a5kRRqKR/SZqCzTYuqB8nSfwVZqQCdyHpSFG88lP60tpMh0FcooOm/xrd00pUJrKv0p6wdTZQpOEkBfZz+QEsF6Hjjk/SnrD2BPsvfTQGdwqU/gFIBmrWT/pS1o5Wmc71I6o7+s49Kx0viGSvpT1l7Ruh0EwsK6FP0B1oaLkpgrqA/Ze1o70/qamDqrpL+M/hLx4vjjX3pT1k7mjCdpyTKNi0QdfI1Vj7btB39c9Dta4UprJ6Y6chFqOkPoFQahfr0z0G3p0Fw3/+I0rn7qEwH/QGUsLOq6E9YRzQMz3Cd23MGUhqVvV/Tn66OqBYeZ7II0CL6F1ckXfa+R3+6OgLo1ardUUOXdMaFgz7zCJ1Bf7o6wIqOc3/AhuGvZJEh+oMo0bI3/UeqdJiOzuCFlWcKqugPosSic8n7WMZU+pPVUbIjg8tHHV3Z0rU/6O8fA9r4ekkc/VN2HZe+W9T9m/Jc01f0z3RITG7Qf9hVh0fpXH6U6AT9G4YSkBuil1Hq5vQnqqN1dK7pMVEar5z+UH6y3MitOKAvT3+iOoGVpXLv0x/KT8o9FwrjmXPoT1JnidKpnDP0B/OT5MaZ53u0fqI/SZ0F6CzTM9Sqp/SHs61APy5drWFPf5I6iVWcVZAtPWZym/5wftK0Z+XCBOMY+tPUSUx4iJVOfzA/FWp2Jf21dCew0njjX+kPpCSkB7uiOM7Qnf5EdWRkTmBlYiu9gv5Atmn+jfMYDO7X6GgSnVOKt8r3oz9VHRmdE1jPUP9VfWKjqJis+vQZQllFpu3CHPLF4jhmMP5d3lSnfdTHdwi04pM9MqeIIg31SmKNnEpi9MbfDlKWoz9RHWzFscyJeKFe1YHzOV68K41Vwpop+R4XHRw4sijGwBnhSMF9b+JN8pwA09mqeej56H+UxRtvLo7UH18ax3B4u0uzf83NfbOJ190vjGaY4Oc8Vgs4PvewOF5nAP0J6pRRmrkDL5AQ/VdhAtOpMM 5wBBW5/2nBF5QlaLjhc0WNFBWKX+/VdKn7fkUxRrolCczJ+LoIBPwkWUCnKNIgsTjeKBx/omPwhjmNz13B7dcILOgPqBsAnawehf4e37eI8gRWYe22Rjyv5vX3sEF2Df//A6E7h+9/CD/H7sKYoSTqluIxb5QlGCfg8+vxefv3kYaKjcqvRKOVKCPS8TVva6J3Kb7f6bLIgVRKrjDWUJNK0SUYv0KtTP8ljj8nK4xirKyB67fimOpRzAipP4JRUBt5yXAjYTxjSVuPkRLc939lSUxNYSJjaEkiYwJxhH6qMI7pgyD5Ey+JZ66s3VbfixKMzMjrSxOM1CTarsDP9C5Gz604gbmY/iR8QdbYwpGlCYb2ojijRcI4xqo3cUOs6f8t0d/ob/S3z8L+P1PYPpP1IB7GAAAAAElFTkSuQmCC' readStream)! Item was added: + ----- Method: SketchMorph class>>extraExampleCook (in category '*MorphicExtras-examples') ----- + extraExampleCook + "SketchMorph extraExampleCook openInWorld" + + ^ (self withForm: Form extraCook) + addMouseUpActionWith: 'Form toothpaste: 30'; + balloonText: 'Click me and then drag the cursor over the screen'; + yourself! Item was added: + ----- Method: SketchMorph class>>extraExampleWizard (in category '*MorphicExtras-examples') ----- + extraExampleWizard + "SketchMorph extraExampleWizard openInWorld" + + ^ (self withForm: Form extraWizard) + addMouseUpActionWith: + (MessageSend receiver: Pen new selector: #web); + balloonText: 'Click me and then drag the cursor over the screen\(Trust me, I won''t turn you into a toad!!)' withCRs; + yourself! From commits at source.squeak.org Fri Sep 18 12:56:28 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 18 Sep 2020 12:56:28 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1687.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1687.mcz ==================== Summary ==================== Name: Morphic-ct.1687 Author: ct Time: 18 September 2020, 2:56:19.743093 pm UUID: 60f81d52-6f24-4952-be9d-291a806074bc Ancestors: Morphic-mt.1683 Fixes SystemWindow >> #collapseOrExpand if the window being collapsed is not in world. Relates to Morphic-ct.1682. =============== Diff against Morphic-mt.1683 =============== Item was changed: ----- Method: SystemWindow>>collapseOrExpand (in category 'resize/collapse') ----- collapseOrExpand "Collapse or expand the window, depending on existing state" | cf | isCollapsed ifTrue: ["Expand -- restore panes to morphics structure" isCollapsed := false. self beKeyWindow. "Bring to frint first" Preferences collapseWindowsInPlace ifTrue: [fullFrame := fullFrame align: fullFrame topLeft with: self getBoundsWithFlex topLeft] ifFalse: [collapsedFrame := self getBoundsWithFlex]. collapseBox ifNotNil: [collapseBox setBalloonText: 'collapse this window' translated]. self setBoundsWithFlex: fullFrame. paneMorphs reverseDo: [:m | self addMorph: m unlock. self world startSteppingSubmorphsOf: m]. self addPaneSplitters. (self hasProperty: #applyTheme) ifTrue: [ self removeProperty: #applyTheme. self userInterfaceTheme applyTo: self allMorphs]] ifFalse: ["Collapse -- remove panes from morphics structure" isCollapsed := true. fullFrame := self getBoundsWithFlex. "First save latest fullFrame" paneMorphs do: [:m | m delete; releaseCachedState]. self removePaneSplitters. self removeCornerGrips. model modelSleep. cf := self getCollapsedFrame. (collapsedFrame isNil and: [Preferences collapseWindowsInPlace not]) ifTrue: [collapsedFrame := cf]. self setBoundsWithFlex: cf. collapseBox ifNotNil: [collapseBox setBalloonText: 'expand this window' translated ]. expandBox ifNotNil: [expandBox setBalloonText: 'expand this window' translated ]. + self isInWorld ifTrue: [self sendToBack]]. + self layoutChanged.! - self sendToBack]. - self layoutChanged! From commits at source.squeak.org Fri Sep 18 13:03:45 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 18 Sep 2020 13:03:45 0000 Subject: [squeak-dev] The Inbox: MorphicExtras-ct.280.mcz Message-ID: A new version of MorphicExtras was added to project The Inbox: http://source.squeak.org/inbox/MorphicExtras-ct.280.mcz ==================== Summary ==================== Name: MorphicExtras-ct.280 Author: ct Time: 18 September 2020, 3:03:41.759788 pm UUID: 05c39684-111e-485f-a27b-03597f1a1a97 Ancestors: MorphicExtras-kfr.276 Add BannerMorph example to demonstrate ScreeningMorph, BackgroundMorph and other Morph techniques. Reuploaded to fix bad dependency in example (it has to be #asTextFromHtml instead of #asHtmlText). Replaces MorphicExtras-ct.271, which can be moved to the treated inbox. This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against MorphicExtras-kfr.276 =============== Item was added: + EllipseMorph subclass: #BannerMorph + instanceVariableNames: 'header contents' + classVariableNames: '' + poolDictionaries: '' + category: 'MorphicExtras-Demo'! + + !BannerMorph commentStamp: 'ct 9/7/2019 10:43' prior: 0! + I display a header and a contents text, arranged in an EllipseMorph in a flamboyant way. I use a ScreeningMorph and a BackgroundMorph to hinder the user to avert his gaze from me.! Item was added: + ----- Method: BannerMorph class>>example (in category 'examples') ----- + example + "BannerMorph example openInWorld" + + ^ self + header: 'Yes, you are ...' withCRs asTextFromHtml + contents: 'Live in Morphic' asUppercase asText! Item was added: + ----- Method: BannerMorph class>>header:contents: (in category 'instance creation') ----- + header: aStringOrText contents: anotherStringOrText + + ^ self basicNew + header: aStringOrText contents: anotherStringOrText; + initialize; + yourself! Item was added: + ----- Method: BannerMorph>>contents (in category 'accessing') ----- + contents + + ^ contents! Item was added: + ----- Method: BannerMorph>>createBackground (in category 'initialize-release') ----- + createBackground + + | fillMorph fillStyle | + fillStyle := GradientFillStyle colors: + ({Color red. Color green. Color blue} + in: [:colors | colors, colors reverse]). + fillStyle + origin: 0 @ 0; + direction: 150 @ 50. + fillMorph := (Morph new + fillStyle: fillStyle; + yourself). + ^ BackgroundMorph new + extent: 300 @ 130; + addMorph: fillMorph; + yourself! Item was added: + ----- Method: BannerMorph>>createContents (in category 'initialize-release') ----- + createContents + + | text | + text := self contents asText + addAttribute: TextEmphasis bold; + addAttribute: (TextFontReference toFont: + (StrikeFont familyName: #ComicPlain size: 39)); + asMorph. + text readOnly: true; flag: #ct. "We're no *that* life, yet :(" + ^ ScreeningMorph new + addMorph: (self createBackground + extent: text extent; + yourself); + addMorph: text; + showScreened; + cellPositioning: #center; + yourself! Item was added: + ----- Method: BannerMorph>>createHeader (in category 'initialize-release') ----- + createHeader + + | text | + text := (self header copyWithFirst: Character cr) asText + addAttribute: TextEmphasis bold; + addAttribute: (TextFontReference toFont: + (StrikeFont familyName: #Accula size: 29)); + yourself. + ^ text asMorph + centered; + fillsOwner: true; + yourself! Item was added: + ----- Method: BannerMorph>>header (in category 'accessing') ----- + header + + ^ header! Item was added: + ----- Method: BannerMorph>>header:contents: (in category 'accessing') ----- + header: aStringOrText contents: anotherStringOrText + + header := aStringOrText. + contents := anotherStringOrText.! Item was added: + ----- Method: BannerMorph>>initialize (in category 'initialize-release') ----- + initialize + + super initialize. + self extent: 300 @ 200. + self + changeProportionalLayout; + addMorph: (Morph new + color: Color transparent; + changeTableLayout; + listCentering: #center; wrapCentering: #center; + addMorph: self createContents; + yourself) + fullFrame: LayoutFrame fullFrame; + addMorph: self createHeader.! Item was added: + ----- Method: BannerMorph>>initializeToStandAlone (in category 'initialize-release') ----- + initializeToStandAlone + + self header: 'Introducing' contents: self class name. + super initializeToStandAlone.! From commits at source.squeak.org Fri Sep 18 13:08:09 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 18 Sep 2020 13:08:09 0000 Subject: [squeak-dev] The Inbox: MorphicExtras-ct.281.mcz Message-ID: A new version of MorphicExtras was added to project The Inbox: http://source.squeak.org/inbox/MorphicExtras-ct.281.mcz ==================== Summary ==================== Name: MorphicExtras-ct.281 Author: ct Time: 18 September 2020, 3:08:01.172788 pm UUID: 6e19610b-d812-471d-83d3-6292915e452c Ancestors: MorphicExtras-kfr.276 Adds PolygonMorph examples. Reuploaded to fix bad dependency in #extraExampleTrapeze (it has to be #asTextFromHtml instead of #asHtmlText). Replaces MorphicExtras-ct.268, which can be moved to the treated inbox. Still depends on ST80-ct.240. This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against MorphicExtras-kfr.276 =============== Item was added: + ----- Method: PolygonMorph class>>extraCircularVertices (in category '*MorphicExtras-examples') ----- + extraCircularVertices + + ^ ((Circle center: 400 @ 300 radius: 200 quadrant: 1) computeVertices: 5) reverse + , ((Circle center: 400 @ 300 radius: 200 quadrant: 4) computeVertices: 5) reverse + , ((Circle center: 400 @ 400 radius: 100 quadrant: 4) computeVertices: 5) + , ((Circle center: 400 @ 400 radius: 100 quadrant: 1) computeVertices: 5) + , ((Circle center: 400 @ 200 radius: 100 quadrant: 3) computeVertices: 5) reverse + , ((Circle center: 400 @ 200 radius: 100 quadrant: 2) computeVertices: 5) reverse! Item was added: + ----- Method: PolygonMorph class>>extraExampleTextFlow (in category '*MorphicExtras-examples') ----- + extraExampleTextFlow + "PolygonMorph extraExampleTextFlow openInHand" + + | polygon text obstacle | + polygon := self new. + polygon + setVertices: self extraCircularVertices; + extent: 309 asPoint; + beSmoothCurve; + color: Color lightGray; + addHandles. + + text := (TextMorph + string: 'TextMorphs can be chained together, causing their contents to flow between containers as either the contents or the containers change. If a TextMorph is embedded in another Morph, you can ask it to have fill the shape of that Morph. Moreover, you can ask it to avoid occlusions, in which case it will do its best to avoid collisions with siblings being in front of it. If a TextMorph is embedded in a CurveMorph, you can ask it to have the text follow the curve, as illustrated here.' asTextMorph + fontName: #BitstreamVeraSans + size: 14) + textColor: Color white; + fillsOwner: true; + yourself. + obstacle := StarMorph new + center: polygon center - (50 @ 25); + extent: 81 asPoint; + color: Color orchid; + yourself. + + polygon + addMorph: text; + addMorph: obstacle. + text centered. + text container avoidsOcclusions: true. + ^ polygon! Item was added: + ----- Method: PolygonMorph class>>extraExampleTrapeze (in category '*MorphicExtras-examples') ----- + extraExampleTrapeze + "PolygonMorph extraExampleTrapeze openInHand" + + | polygon text | + polygon := self new. + polygon + setVertices: {0 @ 100. 275 @ 100. 200 @ 0. 75 @ 0}; + addHandles ; + balloonText: 'Click and drag the handles to change my shape'. + text := 'Polygons can be closed or open, filled or empty as well as lined or convex and can have directed arrows, bevelled borders and last but not least adapted handles.' asTextFromHtml asMorph + beAllFont: (TextStyle default fontOfSize: 14); + fillsOwner: true; + yourself. + polygon addMorph: text. + text centered. + ^ polygon! Item was added: + ----- Method: PolygonMorph class>>extraExampleTrapezePlus (in category '*MorphicExtras-examples') ----- + extraExampleTrapezePlus + "PolygonMorph extraExampleTrapezePlus openInHand" + "Some additional decoration" + + ^ self extraExampleTrapeze + fillStyle: ((GradientFillStyle + ramp: { 0.0 -> Color orange. 0.7 -> Color magenta twiceLighter. 1.0 -> Color red muchLighter }) + origin: 0 @ 0; direction: 275 @ 100; + yourself); + borderWidth: 2; + borderColor: Color blue; + dashedBorder: {35. 20. Color yellow}; + yourself! From marcel.taeumel at hpi.de Fri Sep 18 13:11:14 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Fri, 18 Sep 2020 15:11:14 +0200 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> Message-ID: For the 5.3 release, we decided to put that all behind "ReleaseBuilderSqueakland class >> #setEtoysMode" to be triggered from the "Extras > Themes & Colors" docking-bar menu. Best, Marcel Am 17.09.2020 21:51:16 schrieb karl ramberg : I'm not sure it is for space reasons. There could be other conflicts. It seems to change some icons for the handles when you do ReleaseBuilderSqueakland loadDefaultForms Lazy loading of just the missing icons would be awesome. Best, Karl On Thu, Sep 17, 2020 at 9:04 PM Thiede, Christoph wrote: Nice finding, Karl! I guess this has not been made part of the usual ReleaseBuilder for performance (storage) considerations. The question is: Is this still relevant? Should we lazily load all forms if any is missing in the ScriptingSystem? Best, Christoph [http://www.hpi.de/] Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Donnerstag, 17. September 2020 20:31:31 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: EToys-ct.406.mcz   On Thu, Sep 17, 2020 at 7:26 PM karl ramberg wrote: A  note about something to do: I noticed that quite a few of the Etoys icons are not added to the ScriptingSystem formDictionary. So when opening a  ScriptEditorMorph for example, icons like 'RoundGoldBox' are missing so the UI is crippled. The icons are found in ReleaseBuilderSqueakland but not initialized You just need to do it: ReleaseBuilderSqueakland loadDefaultForms Best, Karl Best, Karl On Thu, Sep 17, 2020 at 12:42 PM Stéphane Rollandin wrote: > This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). Cool! Stef -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Fri Sep 18 13:15:05 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 18 Sep 2020 13:15:05 0000 Subject: =?utf-8?q?_=5Bsqueak-dev=5D_The_Inbox=3A_MorphicExtras-ct=2E2=7F?= =?utf-8?q?82=2Emcz?= Message-ID: A new version of MorphicExtras was added to project The Inbox: http://source.squeak.org/inbox/MorphicExtras-ct.282.mcz ==================== Summary ==================== Name: MorphicExtras-ct.282 Author: ct Time: 18 September 2020, 3:15:00.754788 pm UUID: 83d0d7e0-6e0e-4293-93ec-0f06402bc888 Ancestors: MorphicExtras-kfr.276 Adds Workspace examples for demonstration of Editor and Text.Reuploaded to fix bad dependency in #extraExample1 (it has to be #asTextFromHtml instead of #asHtmlText). Replaces MorphicExtras-ct.276, which can be moved to the treated inbox. This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against MorphicExtras-kfr.276 =============== Item was added: + ----- Method: Workspace class>>extraExample1 (in category '*MorphicExtras-examples') ----- + extraExample1 + "ToolBuilder open: Workspace extraExample1" + + | quoteAttributes | + quoteAttributes := {TextEmphasis bold. TextColor color: Color mocha}. + ^ Workspace new + contents: (Text streamContents: [:stream | ({ + '"When in doubt, try it out!!"' asTextFromHtml + addAllAttributes: quoteAttributes; + yourself. + '-- Dan Ingalls' asText + addAllAttributes: quoteAttributes; + addAttribute: TextAlignment rightFlush; + yourself. + String empty. } + , ((self class sourceCodeAt: #extraExampleContents1) asString lines allButFirst: 3) + do: [:line | stream nextPutAll: line] + separatedBy: [stream cr])]); + shouldStyle: false; + yourself! Item was added: + ----- Method: Workspace class>>extraExample2 (in category '*MorphicExtras-examples') ----- + extraExample2 + "ToolBuilder open: Workspace extraExample2" + + ^ self new + contents: (((self class sourceCodeAt: #extraExampleContents2) + asString lines allButFirst: 3) + joinSeparatedBy: Character cr); + yourself! Item was added: + ----- Method: Workspace class>>extraExampleContents1 (in category '*MorphicExtras-examples') ----- + extraExampleContents1 + "This is example code for #extraExample1" + + "Run the following lines by pressing d:" + Transcript showln: 'Hello world!!'. + self inform: 'This is a dialog box. Quite easy, isn''t it?'. + + "Print the result of an expression using p" + 6 * 7. + Float pi i exp stringForReadout. + (16 factorial + 1) isPrime. + (Smalltalk allClasses gather: #selectors) size.! Item was added: + ----- Method: Workspace class>>extraExampleContents2 (in category '*MorphicExtras-examples') ----- + extraExampleContents2 + "This is example code for #extraExample2" + + "Inspect any (sub)expression result by pressing i" + (20 to: 40 by: 2) asOrderedCollection + addFirst: 16; + addLast: 42; + sort: [:x | x \\ 3] descending; + yourself. + + "Explore any (sub)expression result by pressing I" + Project current world. + + "Debug any (sub)expression using D" + (1 to: 9) join asNumber sqrt truncateTo: 1e-3. + (SystemWindow windowsIn: ActiveWorld) + select: [:window | window bounds isWide] + thenDo: [:window | window color: window color negated].! From marcel.taeumel at hpi.de Fri Sep 18 13:20:44 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Fri, 18 Sep 2020 15:20:44 +0200 Subject: [squeak-dev] The Inbox: Monticello-ct.729.mcz In-Reply-To: <20e88b615b344893bf96e0cc9125ebc2@student.hpi.uni-potsdam.de> References: <20e88b615b344893bf96e0cc9125ebc2@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, yeah, that warning is not helpful for inbox commits.  It would be nice to have a version of text-only dialog that uses a pluggable text morph to scroll longer text messages. Maybe with a fixed  width of "60 ex" and height for 5-7 lines. -1 for this trunction of information, though, because the user has no easy way to look at those 16 version names in that situation. Best, Marcel Am 17.09.2020 19:09:07 schrieb Thiede, Christoph : Before: After: However, I have follow-up questions: A. Why do clients need to care about #withNoLineLongerThan:/word-wrapping stuff? Shouldn't this be a responsibility of UserDialogBoxMorph instead? B. Should we maybe turn off this warning for commits to the MCRepository inbox? In this particular case, our declared workflow says that you should ignore this message anyway, doesn't it? Best, Christoph Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Donnerstag, 17. September 2020 19:04:57 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: Monticello-ct.729.mcz   A new version of Monticello was added to project The Inbox: http://source.squeak.org/inbox/Monticello-ct.729.mcz [http://source.squeak.org/inbox/Monticello-ct.729.mcz] ==================== Summary ==================== Name: Monticello-ct.729 Author: ct Time: 17 September 2020, 7:04:53.385813 pm UUID: 5eea47cc-405e-7841-9544-0f57a9b92fa0 Ancestors: Monticello-cmm.726 Proposal: Enhance truncation of "newer versions avaiable" warning (and make it multilingual-ready) =============== Diff against Monticello-cmm.726 =============== Item was changed:   ----- Method: MCWorkingCopyBrowser>>checkForNewerVersions (in category 'actions') -----   checkForNewerVersions +        "Answer true if there are no newer versions preventing us from saving a version." +          | newer |          newer := workingCopy possiblyNewerVersionsIn: self repository. +        +        newer ifEmpty: [^ true]. +        +        ^ self confirm: ('CAUTION!! {1}:\{2}\Do you really want to save this version?' withCRs translated format: { +                newer size = 1 +                        ifTrue: ['This version in the repository may be newer' translated] +                        ifFalse: ['These {1} versions in the repository may be newer' translated format: {newer size}]. +                (newer size > 3 +                        ifFalse: [newer] +                        ifTrue: [(newer first: 3) , {'...'} , {newer last}]) asCommaString withNoLineLongerThan: 150 +        })! -        ^ newer isEmpty or: [ -                self confirm: 'CAUTION!! These versions in the repository may be newer:', -                        String cr, ((newer asCommaString withNoLineLongerThan:  150) truncateWithElipsisTo: 5 * 149), String cr, -                        'Do you really want to save this version?'].! -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 58970 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 27934 bytes Desc: not available URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 18 13:39:37 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 18 Sep 2020 13:39:37 +0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1540.mcz In-Reply-To: <9ca405d0-4a33-78ac-721f-a919bdf61eae@zogotounga.net> References: <6e8f6302-01fc-c1d6-ab95-90c49a13c2a9@zogotounga.net> <47daea28e0484853bb5aa769040b79ae@student.hpi.uni-potsdam.de>, <9ca405d0-4a33-78ac-721f-a919bdf61eae@zogotounga.net> Message-ID: <70032ace219344e69802ef2f34891ac7@student.hpi.uni-potsdam.de> Hi Stéphane, hi all! Was that enough time for seeing what it's worth? ;-) I use the #rememberProvenance utility from time to time and cannot see any harm, because when disabled as by default, the total overhead makes up tiny 12 byte code steps (for comparison, the bounds initialization costs 13 steps). If you still feel worried about this, we could also lower the overhead to 2 steps by inlining the class variable access. I'd love to see this in Trunk soon, IMHO it also makes the new Objectland example projects which will be ready soon much better explorable. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Stéphane Rollandin Gesendet: Montag, 23. September 2019 21:01:56 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1540.mcz > Do you also refer to Marcel's proposal, which I implemented in > Morphic-ct.1541? This does not affect normal Morphic use at all, unless > you use explicitly call #rememberProvenanceDuring:. Well I missed that one... give me some time to see what it is :) Stef -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Fri Sep 18 14:57:03 2020 From: karlramberg at gmail.com (karl ramberg) Date: Fri, 18 Sep 2020 16:57:03 +0200 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> Message-ID: Ah, I have never seen that option :-D Best, Karl On Fri, Sep 18, 2020 at 3:11 PM Marcel Taeumel wrote: > For the 5.3 release, we decided to put that all behind > "ReleaseBuilderSqueakland class >> #setEtoysMode" to be triggered from the > "Extras > Themes & Colors" docking-bar menu. > > Best, > Marcel > > Am 17.09.2020 21:51:16 schrieb karl ramberg : > I'm not sure it is for space reasons. > There could be other conflicts. > It seems to change some icons for the handles when you do > ReleaseBuilderSqueakland loadDefaultForms > > Lazy loading of just the missing icons would be awesome. > > Best, > Karl > > > On Thu, Sep 17, 2020 at 9:04 PM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Nice finding, Karl! I guess this has not been made part of the usual >> ReleaseBuilder for performance (storage) considerations. The question is: >> Is this still relevant? Should we lazily load all forms if any is missing >> in the ScriptingSystem? >> >> >> Best, >> >> Christoph >> >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von karl ramberg >> *Gesendet:* Donnerstag, 17. September 2020 20:31:31 >> *An:* The general-purpose Squeak developers list >> *Betreff:* Re: [squeak-dev] The Inbox: EToys-ct.406.mcz >> >> >> >> On Thu, Sep 17, 2020 at 7:26 PM karl ramberg >> wrote: >> >>> A note about something to do: >>> I noticed that quite a few of the Etoys icons are not added to the >>> ScriptingSystem formDictionary. >>> So when opening a ScriptEditorMorph for example, icons like >>> 'RoundGoldBox' are missing so the UI is crippled. >>> The icons are found in ReleaseBuilderSqueakland but not initialized >>> >> >> You just need to do it: >> ReleaseBuilderSqueakland loadDefaultForms >> Best, >> Karl >> >> >>> Best, >>> Karl >>> >>> >>> On Thu, Sep 17, 2020 at 12:42 PM Stéphane Rollandin < >>> lecteur at zogotounga.net> wrote: >>> >>>> > This commit is part of reconstruction of Objectland (also known as >>>> "The Worlds of Squeak"). >>>> >>>> Cool! >>>> >>>> Stef >>>> >>>> >>>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lecteur at zogotounga.net Fri Sep 18 16:56:00 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Fri, 18 Sep 2020 18:56:00 +0200 Subject: [squeak-dev] The Inbox: Morphic-ct.1540.mcz In-Reply-To: <70032ace219344e69802ef2f34891ac7@student.hpi.uni-potsdam.de> References: <6e8f6302-01fc-c1d6-ab95-90c49a13c2a9@zogotounga.net> <47daea28e0484853bb5aa769040b79ae@student.hpi.uni-potsdam.de> <9ca405d0-4a33-78ac-721f-a919bdf61eae@zogotounga.net> <70032ace219344e69802ef2f34891ac7@student.hpi.uni-potsdam.de> Message-ID: <046fa55d-0bc5-e7d0-a9bc-f13b153af425@zogotounga.net> > Hi Stéphane, hi all! > > > Was that enough time for seeing what it's worth? ;-) I guess so :) Go on! Stef From lewis at mail.msen.com Fri Sep 18 21:50:50 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Fri, 18 Sep 2020 17:50:50 -0400 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> Message-ID: <20200918215050.GA59541@shell.msen.com> Oh, that's great! I had not noticed it either :-D Dave On Fri, Sep 18, 2020 at 04:57:03PM +0200, karl ramberg wrote: > Ah, > I have never seen that option :-D > > Best, > Karl > > > On Fri, Sep 18, 2020 at 3:11 PM Marcel Taeumel > wrote: > > > For the 5.3 release, we decided to put that all behind > > "ReleaseBuilderSqueakland class >> #setEtoysMode" to be triggered from the > > "Extras > Themes & Colors" docking-bar menu. > > > > Best, > > Marcel > > > > Am 17.09.2020 21:51:16 schrieb karl ramberg : > > I'm not sure it is for space reasons. > > There could be other conflicts. > > It seems to change some icons for the handles when you do > > ReleaseBuilderSqueakland loadDefaultForms > > > > Lazy loading of just the missing icons would be awesome. > > > > Best, > > Karl > > > > > > On Thu, Sep 17, 2020 at 9:04 PM Thiede, Christoph < > > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > >> Nice finding, Karl! I guess this has not been made part of the usual > >> ReleaseBuilder for performance (storage) considerations. The question is: > >> Is this still relevant? Should we lazily load all forms if any is missing > >> in the ScriptingSystem? > >> > >> > >> Best, > >> > >> Christoph > >> > >> ------------------------------ > >> *Von:* Squeak-dev im > >> Auftrag von karl ramberg > >> *Gesendet:* Donnerstag, 17. September 2020 20:31:31 > >> *An:* The general-purpose Squeak developers list > >> *Betreff:* Re: [squeak-dev] The Inbox: EToys-ct.406.mcz > >> > >> > >> > >> On Thu, Sep 17, 2020 at 7:26 PM karl ramberg > >> wrote: > >> > >>> A note about something to do: > >>> I noticed that quite a few of the Etoys icons are not added to the > >>> ScriptingSystem formDictionary. > >>> So when opening a ScriptEditorMorph for example, icons like > >>> 'RoundGoldBox' are missing so the UI is crippled. > >>> The icons are found in ReleaseBuilderSqueakland but not initialized > >>> > >> > >> You just need to do it: > >> ReleaseBuilderSqueakland loadDefaultForms > >> Best, > >> Karl > >> > >> > >>> Best, > >>> Karl > >>> > >>> > >>> On Thu, Sep 17, 2020 at 12:42 PM St??phane Rollandin < > >>> lecteur at zogotounga.net> wrote: > >>> > >>>> > This commit is part of reconstruction of Objectland (also known as > >>>> "The Worlds of Squeak"). > >>>> > >>>> Cool! > >>>> > >>>> Stef > >>>> > >>>> > >>>> > >> > > > From eliot.miranda at gmail.com Sat Sep 19 02:34:18 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Fri, 18 Sep 2020 19:34:18 -0700 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions Message-ID: Hi All, the SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: method looks to be badly broken. I don't understand the index scaling (yet) so I thought I'd throw this out there and see if anyone can spot when and why this broke. Here's a simple example that generates a second long A below middle C (220Hz), smoothing start and stop, and then plays it, so far so good: | samples sineTable sound | "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" sineTable := SoundPlayer sineTable: 73. sineTable doWithIndex: "And let's not deafen anyone..." [:sample :index| sineTable at: index put: sample // 4]. samples := SoundBuffer new: 16000. 1 to: samples size by: sineTable size do: [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. 1 to: 146 do: "smooth start and end of the sound" [:i| samples at: i put: ((samples at: i) * i / 146) asInteger. samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. (SampledSound samples: samples samplingRate: 16000) play Now let's create a stereo sound image and try and play that: | samples sineTable sound | "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" sineTable := SoundPlayer sineTable: 73. sineTable doWithIndex: [:sample :index| sineTable at: index put: sample // 4]. samples := SoundBuffer new: 16000. 1 to: samples size by: sineTable size do: [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. 1 to: 146 do: [:i| samples at: i put: ((samples at: i) * i / 146) asInteger. samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. sound := SampledSound samples: samples samplingRate: 16000. sound := MixedSound new add: sound pan: 0.25; add: sound pan: 0.75; yourself. sound play Eek!! You can examine the mixing without hurting your ears by replacing "sound play" with "sound computeSamplesForSeconds: sound duration" which invokes the mixing directly. Any help making sense of this gratefully received. Some comments around the scaledIndex := scaledIndex + scaledIncrement. scaledIndex >= ScaledIndexOverflow ifTrue: [ overflow := scaledIndex >> IncrementFractionBits. indexHighBits := indexHighBits + overflow. scaledIndex := scaledIndex - (overflow << IncrementFractionBits)]. in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: would be most welcome. And if you simply comment out the primitive teh Smaklltalk code produces garbage also, so the problem does not seem to be Slang but the algorithm itself. _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Tom.Beckmann at student.hpi.uni-potsdam.de Sat Sep 19 06:15:05 2020 From: Tom.Beckmann at student.hpi.uni-potsdam.de (Beckmann, Tom) Date: Sat, 19 Sep 2020 06:15:05 +0000 Subject: [squeak-dev] FFI type for 'char[12]'? In-Reply-To: References: , Message-ID: <029d51a873cb42b695cca5611e93ff06@student.hpi.uni-potsdam.de> Hi Elliot, hi all, > It is much better to have a new facility "out in the wild" available to look at b others and test than it is to sit on something until it is perfect. Please do release it asap. Then we can all collaborate on writing tests for it (we would need tests in the FFI test suite), reviewing the code, etc. Wholeheartedly agree! The code in question was mixed with a bunch of other experiments for FFI that mostly turned out to not be interesting. I went ahead and isolated it into the attached changeset now. As of right now, it does the bare minimum required to generate accessors for returning appropriately typed ExternalData instances for arrays. It does not support type aliases and I'm sure there are some incorrectly implemented overrides in the ExternalArrayType class. To test, just add a definition like (myArray 'char[12]') to a struct's #fields and try `MyStruct new myArray at: 3`. Please feel free to take this as a base or disregard it completely :) If no one else picks the topic up in the next weeks I'll eventually get time to return to it and investigate adding fixes and tests. Best, Tom ________________________________________ From: Squeak-dev on behalf of Eliot Miranda Sent: Wednesday, September 16, 2020 5:08:24 PM To: The general-purpose Squeak developers list Subject: Re: [squeak-dev] FFI type for 'char[12]'? Hi Tom, On Wed, Sep 16, 2020 at 1:12 AM Beckmann, Tom > wrote: Hi, for ExternalStructure fields definitions I worked around this by exploding the array, as in #((str1 'char') (str2 'char') (str3 'char') (str4 'char') (str5 'char')) The same works for other ExternalTypes like Foo of course. I also have an experimental set of changes that adds an ExternalArrayType to support this, however it still has some untested corner cases. I'll investigate getting it merged to FFI soon. It is much better to have a new facility "out in the wild" available to look at b others and test than it is to sit on something until it is perfect. Please do release it asap. Then we can all collaborate on writing tests for it (we would need tests in the FFI test suite), reviewing the code, etc. Best, Tom ________________________________________ From: Squeak-dev > on behalf of Taeumel, Marcel Sent: Wednesday, September 16, 2020 9:58:45 AM To: squeak-dev Subject: Re: [squeak-dev] FFI type for 'char[12]'? Hi Tony, such container types are still work-in-progress. :-/ For char[12], you can either use "string" or "char*", I suppose. "Foo x[5]" can be expressed through ExternalData, too, which would be "Foo*". Best, Marcel Am 16.09.2020 09:49:20 schrieb Tony Garnock-Jones >: Hi all, Is there some way in the FFI to specify that an ExternalStructure has a member whose type is like 'char x[12]'? Similarly, given some ExternalStructure Foo, can I have another structure that has a member like 'Foo x[5]'? Tony -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- A non-text attachment was scrubbed... Name: FFIExternalArrayType.1.cs Type: text/x-csharp Size: 4076 bytes Desc: FFIExternalArrayType.1.cs URL: From lecteur at zogotounga.net Sat Sep 19 07:46:28 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 09:46:28 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: Message-ID: As a first and fast answer, let me say that I did not notice anything broken there in years of doing sound and music in Squeak. Did you check if the way you populate the samples buffers is the same as in #fromWaveStream: which is used to load wav files and works correctly AFAIK? There are some useful tools in muO. For example, fetch the (32 bit) image in the rogue352.zip here: http://www.zogotounga.net/comp/squeak/rogue.htm , then copy the attached sound file in the root folder and do (SampledSound fromWaveFileNamed: 'sound.wav') edit (SampledSound fromWaveFileNamed: 'sound.wav') play Works fine here. Stef -------------- next part -------------- A non-text attachment was scrubbed... Name: sound.wav Type: audio/wav Size: 335132 bytes Desc: not available URL: From lecteur at zogotounga.net Sat Sep 19 07:52:26 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 09:52:26 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: Message-ID: <727187c0-4bdd-12cc-85fb-7416ee2f650d@zogotounga.net> > (SampledSound fromWaveFileNamed: 'sound.wav') edit A note about the editor: Left-click in one of the two displayed channel and select 'edit [element]' to see the waveform and spectrogram (which is enabled via the 'spectrogram' checkbox in the contextual menu). You can scale the display smoothly to an arbitrary precision by holding SHIFT and click-dragging in the window. Stef From eliot.miranda at gmail.com Sat Sep 19 07:52:29 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 00:52:29 -0700 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: Message-ID: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> Stef, if you play the example you’ll see that the first version works fine. The second version, which adds MixedSound yo create stereo doesn’t. That proves that the issue is in MixedSound. Please just try the two examples. _,,,^..^,,,_ (phone) > On Sep 19, 2020, at 12:46 AM, Stéphane Rollandin wrote: > > As a first and fast answer, let me say that I did not notice anything broken there in years of doing sound and music in Squeak. > > Did you check if the way you populate the samples buffers is the same as in #fromWaveStream: which is used to load wav files and works correctly AFAIK? > > > There are some useful tools in muO. For example, fetch the (32 bit) image in the rogue352.zip here: http://www.zogotounga.net/comp/squeak/rogue.htm , then copy the attached sound file in the root folder and do > > (SampledSound fromWaveFileNamed: 'sound.wav') edit > (SampledSound fromWaveFileNamed: 'sound.wav') play > > > Works fine here. > > Stef > > From lecteur at zogotounga.net Sat Sep 19 08:17:53 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 10:17:53 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> Message-ID: <2937ccb4-2970-7733-a142-07809a68b684@zogotounga.net> > if you play the example you’ll see that the first version works fine. The second version, which adds MixedSound yo create stereo doesn’t. That proves that the issue is in MixedSound. Please just try the two examples. Right. What I can see If I do | samples sineTable sound | "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" sineTable := SoundPlayer sineTable: 73. sineTable doWithIndex: [:sample :index| sineTable at: index put: sample // 4]. samples := SoundBuffer new: 16000. 1 to: samples size by: sineTable size do: [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. 1 to: 146 do: [:i| samples at: i put: ((samples at: i) * i / 146) asInteger. samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. sound := SampledSound samples: samples samplingRate: 16000. sound := MixedSound new add: sound pan: 0.25; add: sound pan: 0.75; yourself. (SampledSound samples: sound monoSamples samplingRate: sound originalSamplingRate) edit in the muO image I pointed to in my previous message, is a phasing problem leading to periodic glitches (the attached image is directly taken from the muO editor) I'm looking at this.. Stef -------------- next part -------------- A non-text attachment was scrubbed... Name: SoundElementEditor.png Type: image/png Size: 11682 bytes Desc: not available URL: From lecteur at zogotounga.net Sat Sep 19 09:27:25 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 11:27:25 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> Message-ID: Ok, it looks like your choice of frequency let a bug surface. In SampledSound>>#reset, the computation of scaledIncrement depends on the sampling rate. But scaledIncrement is also supposed to be somewhat commensurate with ScaledIndexOverflow which has a fixed value set in the class-side initialize. So I guess the whole LargePositiveIntegers avoidance scheme is bogus. If you get rid of it altogether, which in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: translates as follow (primitive uncommented): ----- mixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol "Mix the given number of samples with the samples already in the given buffer starting at the given index. Assume that the buffer size is at least (index + count) - 1." | lastIndex outIndex sample i s | " " lastIndex := (startIndex + n) - 1. outIndex := startIndex. "index of next stereo output sample pair" [(outIndex <= samplesSize) and: [outIndex <= lastIndex]] whileTrue: [ sample := ((samples at: outIndex) * scaledVol) // ScaleFactor. leftVol > 0 ifTrue: [ i := (2 * outIndex) - 1. s := (aSoundBuffer at: i) + ((sample * leftVol) // ScaleFactor). s > 32767 ifTrue: [s := 32767]. "clipping!" s < -32767 ifTrue: [s := -32767]. "clipping!" aSoundBuffer at: i put: s]. rightVol > 0 ifTrue: [ i := 2 * outIndex. s := (aSoundBuffer at: i) + ((sample * rightVol) // ScaleFactor). s > 32767 ifTrue: [s := 32767]. "clipping!" s < -32767 ifTrue: [s := -32767]. "clipping!" aSoundBuffer at: i put: s]. scaledVolIncr ~= 0 ifTrue: [ scaledVol := scaledVol + scaledVolIncr. ((scaledVolIncr > 0 and: [scaledVol >= scaledVolLimit]) or: [scaledVolIncr < 0 and: [scaledVol <= scaledVolLimit]]) ifTrue: [ "reached the limit; stop incrementing" scaledVol := scaledVolLimit. scaledVolIncr := 0]]. outIndex := outIndex + 1]. count := count - n. ----- then you get a nice sine wave (which still play badly in my Squeak image but now only because it is too slow to generate without a primitive). Stef From lecteur at zogotounga.net Sat Sep 19 09:28:16 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 11:28:16 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> Message-ID: <744a7a3f-c062-0048-f5d6-b0e92fbca005@zogotounga.net> > Ok, it looks like your choice of frequency let a bug surface. I meant choice of *sampling rate*, sorry. Stef From lecteur at zogotounga.net Sat Sep 19 09:34:54 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 11:34:54 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> Message-ID: > SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: > translates as follow (primitive uncommented): ...and here I meant "primitive commented out", sorry again. Stef From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 11:33:41 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 11:33:41 +0000 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: <20200918215050.GA59541@shell.msen.com> References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> , <20200918215050.GA59541@shell.msen.com> Message-ID: <65d6053df3734f509937e33b32052f2a@student.hpi.uni-potsdam.de> This is good to know, but I wonder whether this is a perfect solution when you want to play a little bit around with some EToys morphs (such as Viewers) without converting your whole image into an EToys optimized one. And you get nowhere informed about the need to activate this EToys mode. Should we modify the ScriptingSystem so that if an icon is not found, the Squeakland resources are looked up before the default icon is answered? Then you could use all the EToys tools without missing any icon, but still would have your default halo icons etc. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Freitag, 18. September 2020 23:50:50 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: EToys-ct.406.mcz Oh, that's great! I had not noticed it either :-D Dave On Fri, Sep 18, 2020 at 04:57:03PM +0200, karl ramberg wrote: > Ah, > I have never seen that option :-D > > Best, > Karl > > > On Fri, Sep 18, 2020 at 3:11 PM Marcel Taeumel > wrote: > > > For the 5.3 release, we decided to put that all behind > > "ReleaseBuilderSqueakland class >> #setEtoysMode" to be triggered from the > > "Extras > Themes & Colors" docking-bar menu. > > > > Best, > > Marcel > > > > Am 17.09.2020 21:51:16 schrieb karl ramberg : > > I'm not sure it is for space reasons. > > There could be other conflicts. > > It seems to change some icons for the handles when you do > > ReleaseBuilderSqueakland loadDefaultForms > > > > Lazy loading of just the missing icons would be awesome. > > > > Best, > > Karl > > > > > > On Thu, Sep 17, 2020 at 9:04 PM Thiede, Christoph < > > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > >> Nice finding, Karl! I guess this has not been made part of the usual > >> ReleaseBuilder for performance (storage) considerations. The question is: > >> Is this still relevant? Should we lazily load all forms if any is missing > >> in the ScriptingSystem? > >> > >> > >> Best, > >> > >> Christoph > >> > >> ------------------------------ > >> *Von:* Squeak-dev im > >> Auftrag von karl ramberg > >> *Gesendet:* Donnerstag, 17. September 2020 20:31:31 > >> *An:* The general-purpose Squeak developers list > >> *Betreff:* Re: [squeak-dev] The Inbox: EToys-ct.406.mcz > >> > >> > >> > >> On Thu, Sep 17, 2020 at 7:26 PM karl ramberg > >> wrote: > >> > >>> A note about something to do: > >>> I noticed that quite a few of the Etoys icons are not added to the > >>> ScriptingSystem formDictionary. > >>> So when opening a ScriptEditorMorph for example, icons like > >>> 'RoundGoldBox' are missing so the UI is crippled. > >>> The icons are found in ReleaseBuilderSqueakland but not initialized > >>> > >> > >> You just need to do it: > >> ReleaseBuilderSqueakland loadDefaultForms > >> Best, > >> Karl > >> > >> > >>> Best, > >>> Karl > >>> > >>> > >>> On Thu, Sep 17, 2020 at 12:42 PM St??phane Rollandin < > >>> lecteur at zogotounga.net> wrote: > >>> > >>>> > This commit is part of reconstruction of Objectland (also known as > >>>> "The Worlds of Squeak"). > >>>> > >>>> Cool! > >>>> > >>>> Stef > >>>> > >>>> > >>>> > >> > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 11:37:16 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 11:37:16 +0000 Subject: [squeak-dev] The Inbox: Monticello-ct.729.mcz In-Reply-To: References: <20e88b615b344893bf96e0cc9125ebc2@student.hpi.uni-potsdam.de>, Message-ID: <9bab35821f924d86bd573e660a6fb7fb@student.hpi.uni-potsdam.de> Hi Marcel, > It would be nice to have a version of text-only dialog that uses a pluggable text morph to scroll longer text messages. Maybe with a fixed width of "60 ex" and height for 5-7 lines. Hm, would this be a reason for a new "NestedTextAttribute" that is converted into a nested pluggable text morph before displaying? The advantage of this solution would be the free positioning of the scrollable text inside of the whole message text. The disadvantage would be increased complexity ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Freitag, 18. September 2020 15:20:44 An: squeak-dev Betreff: Re: [squeak-dev] The Inbox: Monticello-ct.729.mcz Hi Christoph, yeah, that warning is not helpful for inbox commits. It would be nice to have a version of text-only dialog that uses a pluggable text morph to scroll longer text messages. Maybe with a fixed width of "60 ex" and height for 5-7 lines. -1 for this trunction of information, though, because the user has no easy way to look at those 16 version names in that situation. Best, Marcel Am 17.09.2020 19:09:07 schrieb Thiede, Christoph : Before: [cid:e853f811-9aac-4450-8e0c-f4cb0a622e50] After: [cid:fabc3777-170e-4222-abf9-35c599634f15] However, I have follow-up questions: A. Why do clients need to care about #withNoLineLongerThan:/word-wrapping stuff? Shouldn't this be a responsibility of UserDialogBoxMorph instead? B. Should we maybe turn off this warning for commits to the MCRepository inbox? In this particular case, our declared workflow says that you should ignore this message anyway, doesn't it? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Donnerstag, 17. September 2020 19:04:57 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: Monticello-ct.729.mcz A new version of Monticello was added to project The Inbox: http://source.squeak.org/inbox/Monticello-ct.729.mcz ==================== Summary ==================== Name: Monticello-ct.729 Author: ct Time: 17 September 2020, 7:04:53.385813 pm UUID: 5eea47cc-405e-7841-9544-0f57a9b92fa0 Ancestors: Monticello-cmm.726 Proposal: Enhance truncation of "newer versions avaiable" warning (and make it multilingual-ready) =============== Diff against Monticello-cmm.726 =============== Item was changed: ----- Method: MCWorkingCopyBrowser>>checkForNewerVersions (in category 'actions') ----- checkForNewerVersions + "Answer true if there are no newer versions preventing us from saving a version." + | newer | newer := workingCopy possiblyNewerVersionsIn: self repository. + + newer ifEmpty: [^ true]. + + ^ self confirm: ('CAUTION!! {1}:\{2}\Do you really want to save this version?' withCRs translated format: { + newer size = 1 + ifTrue: ['This version in the repository may be newer' translated] + ifFalse: ['These {1} versions in the repository may be newer' translated format: {newer size}]. + (newer size > 3 + ifFalse: [newer] + ifTrue: [(newer first: 3) , {'...'} , {newer last}]) asCommaString withNoLineLongerThan: 150 + })! - ^ newer isEmpty or: [ - self confirm: 'CAUTION!! These versions in the repository may be newer:', - String cr, ((newer asCommaString withNoLineLongerThan: 150) truncateWithElipsisTo: 5 * 149), String cr, - 'Do you really want to save this version?'].! -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 58970 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 27934 bytes Desc: pastedImage.png URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 11:39:36 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 11:39:36 +0000 Subject: [squeak-dev] The Inbox: Monticello-ct.728.mcz In-Reply-To: References: , Message-ID: <940c2ba54df340d08601d7a2b19cd982@student.hpi.uni-potsdam.de> Hi Chris, thanks for the hint to the history browser! Still, this is some clicks further away. How do you think about offering all ancestor versions directly into the "changes against" menu as proposed below? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Chris Muller Gesendet: Freitag, 18. September 2020 06:42:38 An: squeak dev Betreff: Re: [squeak-dev] The Inbox: Monticello-ct.728.mcz Hi Christoph, +1 for the multilingual change. However, the UI does already support the ability to diff between _any_ two versions via the existing browsers. Simply opening the History browser on the descendant, and then select whichever ancestor you wish to compare to. Best, Chris On Thu, Sep 17, 2020 at 6:47 AM > wrote: A new version of Monticello was added to project The Inbox: http://source.squeak.org/inbox/Monticello-ct.728.mcz ==================== Summary ==================== Name: Monticello-ct.728 Author: ct Time: 17 September 2020, 1:47:00.019813 pm UUID: 7d44995a-f19b-ee49-b16b-c710c9f2bce6 Ancestors: Monticello-cmm.726 Implements comparing a Monticello version against any of its ancestors. Not absolutely sure about the code quality, but it's a relevant feature for me (until now I always used to consult the Nabble archive). Also improves multilingual support. =============== Diff against Monticello-cmm.726 =============== Item was changed: ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- httpGet: url arguments: arguments | progress urlString client response result | progress := [ :total :amount | HTTPProgress new total: total; amount: amount; + signal: 'Downloading...' translated ]. - signal: 'Downloading...' ]. urlString := arguments ifNil: [ url ] ifNotNil: [ | queryString | queryString := WebUtils encodeUrlEncodedForm: arguments. (url includes: $?) ifTrue: [ url, '&', queryString ] ifFalse: [ url, '?', queryString ] ]. self class useSharedWebClientInstance ifTrue: [ "Acquire webClient by atomically storing it in the client variable and setting its value to nil." client := webClient. webClient := nil ]. client ifNil: [ client := WebClient new ] ifNotNil: [ "Attempt to avoid an error on windows by recreating the underlying stream." client isConnected ifFalse: [ client close ] ]. response := client username: self user; password: self password; httpGet: urlString do: [ :request | request headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; headerAt: 'Connection' put: 'Keep-Alive'; headerAt: 'Accept' put: '*/*' ]. result := (response code between: 200 and: 299) ifFalse: [ response content. "Make sure content is read." nil ] ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. self class useSharedWebClientInstance ifTrue: [ "Save the WebClient instance for reuse, but only if there is no client cached." webClient ifNil: [ webClient := client ] ifNotNil: [ client close ] ] ifFalse: [ client close ]. + result ifNil: [ NetworkError signal: ('Could not access {1} (Code {2})' translated format: {location. response code}) ]. - result ifNil: [ NetworkError signal: 'Could not access ', location ]. ^result! Item was changed: ----- Method: MCRepositoryInspector>>versionListMenu: (in category 'morphic ui') ----- versionListMenu: aMenu + 1 to: self orderSpecs size do: [ :index | aMenu addUpdating: #orderString: target: self selector: #order: argumentList: { index } ]. aMenu addLine. + aMenu add: 'Changes against ...' translated action: [ + | ri versions seen | - aMenu add: 'Changes against ...' action: [| ri | ri := aMenu defaultTarget. + versions := ri versionList + collect: [:name | MCVersionName on: name] + as: OrderedCollection. + seen := versions asSet. + self version info breadthFirstAncestors do: [:ancestor | + (seen includes: ancestor name) ifFalse: [ + versions add: ancestor. + seen add: ancestor name]]. (UIManager default + chooseFrom: (versions collect: [:version | version name]) + values: versions + title: 'Select version to show patch against ...' translated) ifNotNil: [:name | + | target base | - chooseFrom: ri versionList - values: ri versionList - title: 'Select version to show patch against ...') ifNotNil: [:name | - | versionName target base | - versionName := MCVersionName on: name. target := ri repository versionNamed: ri versionInfo name. + base := name isString + ifTrue: [ri repository versionNamed: name] + ifFalse: [ri version workingCopy repositoryGroup versionWithInfo: name]. - base := aMenu defaultTarget repository versionNamed: versionName. (MCPatchBrowser forPatch: (target snapshot patchRelativeToBase: base snapshot)) + showLabelled: ('Changes from {1} to {2}' translated format: {name. ri versionInfo name})]]. - showLabelled: 'Changes from ', versionName, ' to ', ri versionInfo name]]. ^aMenu! Item was added: + ----- Method: MCVersionName>>name (in category 'as yet unclassified') ----- + name + + ^ self! -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Sat Sep 19 11:47:08 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sat, 19 Sep 2020 13:47:08 +0200 (CEST) Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: Message-ID: Hi Eliot, On Fri, 18 Sep 2020, Eliot Miranda wrote: > Hi All, > >      the SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: method looks to be badly broken.  I don't understand the index scaling (yet) so I thought I'd throw this out there and see if anyone can spot when and > why this broke. > > Here's a simple example that generates a second long A below middle C (220Hz), smoothing start and stop, and then plays it, so far so good: > > | samples sineTable sound | > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > sineTable := SoundPlayer sineTable: 73. > sineTable doWithIndex: "And let's not deafen anyone..." > [:sample :index| sineTable at: index put: sample // 4]. > samples := SoundBuffer new: 16000. > 1 to: samples size by: sineTable size do: > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. > 1 to: 146 do: "smooth start and end of the sound" > [:i| > samples at: i put: ((samples at: i) * i / 146) asInteger. > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. > (SampledSound samples: samples samplingRate: 16000) play > > Now let's create a stereo sound image and try and play that: > > | samples sineTable sound | > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > sineTable := SoundPlayer sineTable: 73. > sineTable doWithIndex: > [:sample :index| sineTable at: index put: sample // 4]. > samples := SoundBuffer new: 16000. > 1 to: samples size by: sineTable size do: > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. > 1 to: 146 do: > [:i| > samples at: i put: ((samples at: i) * i / 146) asInteger. > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. > sound := SampledSound samples: samples samplingRate: 16000. > sound := MixedSound new > add: sound pan: 0.25; > add: sound pan: 0.75; If you replace the above line with > add: sound copy pan: 0.75; then the produced sound should be okay. Sound objects have state, and if you pass the same object twice, the state will be messed up. Levente > yourself. > sound play > > Eek!! > > You can examine the mixing without hurting your ears by replacing >     "sound play" > with >     "sound computeSamplesForSeconds: sound duration" > which invokes the mixing directly. > > Any help making sense of this gratefully received.  Some comments around the > > scaledIndex := scaledIndex + scaledIncrement. > scaledIndex >= ScaledIndexOverflow ifTrue: [ > overflow := scaledIndex >> IncrementFractionBits. > indexHighBits := indexHighBits + overflow. > scaledIndex := scaledIndex - (overflow << IncrementFractionBits)]. > > in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: would be most welcome. > > And if you simply comment out the primitive teh Smaklltalk code produces garbage also, so the problem does not seem to be Slang but the algorithm itself. > > _,,,^..^,,,_ > best, Eliot > > From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 12:11:36 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 12:11:36 +0000 Subject: [squeak-dev] The Inbox: Sound-ct.70.mcz In-Reply-To: References: <2ad81ea635c04521a6b1d99c415d9569@student.hpi.uni-potsdam.de> <20200917203607.GA20660@shell.msen.com>, Message-ID: Hi Dave, Hi Eric, thanks for the pointers. First, I don't know whether we want to have every image automatically download a file when you open Objectland, and because it should be easily explorable, I would not like to make an extra button "Click here to download a sound file" for it. If we decide we can make automatical downloads, Eric, can we upload any CC-free audio file to Wikimedia Commons and be sure it will never be deleted, or do we use any song that is already there? This takes about 3 seconds for a 30 seconds wave file on my machine: SampledSound fromWaveStream: (HTTPSocket httpGet: 'http://upload.wikimedia.org/wikipedia/commons/4/4e/Bach_Brandenburg_4_coda_to_the_3rd_movement.wav') Opening Objectland should not be terribly slow ... Hm, maybe we should just use the existing bachFugue sound, but I wanted to demonstrate Squeak's capability of playing wave files beside midi files, too ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eric Gade Gesendet: Donnerstag, 17. September 2020 23:06:02 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: Sound-ct.70.mcz On Thu, Sep 17, 2020 at 4:36 PM David T. Lewis > wrote: Maybe someone can suggest a good strategy for handling this? We don't want to start adding 7MB sound files to the image, and we know from long experience that web links tend to go stale in embarassing ways. How about some audio from the Wikimedia Commons? It's unlikely the URLs would go stale for those. -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 12:12:47 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 12:12:47 +0000 Subject: [squeak-dev] The Inbox: Monticello-ul.727.mcz In-Reply-To: References: <1ae1c3850d3548dab48fe0da68bffac4@student.hpi.uni-potsdam.de>, Message-ID: <8330c5bc89b5482db98e249b35110361@student.hpi.uni-potsdam.de> Hi Levente, alright, then please go ahead, I'm looking forward to seeing this in the Trunk! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Donnerstag, 17. September 2020 12:49:28 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: Monticello-ul.727.mcz Hi Christoph, On Thu, 17 Sep 2020, Thiede, Christoph wrote: > > Very nice idea! :-) If I wanted to hijack an image, these URLRewriteRules would probably be my first approach - but security has never been an issue for Squeak, so I guess this is not a problem. If you can manipulate objects like URLRewriteRules, you have already hijacked the image. > > > Does this also fix the problem with the classic HTTP URLs returned by the update map or will we still need to patch them on the server side? The urls in the update map are used to create repositories. That's why simply changing the existing http repository urls to https doesn't suffice, because the updater will see the http urls and create new repositores with them if they are absent. So, this rewrite trick of Eliot works around the problem of the update maps as well. > > > And one last question regarding to your tests in the method comment of #rewriteUrl:forDownload:: Couldn't you put them into a real test case? I'm pretty sure that not everyone will run these out-commented tests manually, and > it would be a pity not to automate them. I left the possibility to change the rewrite rules to whatever you want. If there were a test case with those asserts in the comment, the test would start failing as soon as you changed the rules. Thought it's possible to create a test case which temporarily resets to the default rules and tests them. Levente > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von commits at source.squeak.org > Gesendet: Donnerstag, 17. September 2020 01:55:41 > An: squeak-dev at lists.squeakfoundation.org > Betreff: [squeak-dev] The Inbox: Monticello-ul.727.mcz > Levente Uzonyi uploaded a new version of Monticello to project The Inbox: > http://source.squeak.org/inbox/Monticello-ul.727.mcz > > ==================== Summary ==================== > > Name: Monticello-ul.727 > Author: ul > Time: 17 September 2020, 1:54:51.056164 am > UUID: ad776836-42eb-4aa2-b788-f10dd9e07da2 > Ancestors: Monticello-cmm.726 > > MCHttpRepository changes: > - before up- or downloading files, transform the urls using #rewriteUrl:forDownload:. The default rules (see #urlRewriteRules) switch from http to https for source.squeak.org and squeaksource.com, and switch to the the static > smalltalkhub site for downloads. The url rewriting is Eliot's idea, but this implementation uses a list of rewrite rules instead of a dictionary-based mapping. > - use WebClient (and the shared webclient instance) for uploads too > - retry down/uploading with WebClient at most 3 times. This should work around the case where the underlying socket was closed but the state of the socket has not been updated in Squeak. > - use https in #creationTemplate > > =============== Diff against Monticello-cmm.726 =============== > > Item was changed: > MCFileBasedRepository subclass: #MCHttpRepository > instanceVariableNames: 'location user password readerCache indexed webClient' > + classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' > - classVariableNames: 'UseSharedWebClientInstance' > poolDictionaries: '' > category: 'Monticello-Repositories'! > > Item was changed: > ----- Method: MCHttpRepository class>>creationTemplate (in category 'ui-support') ----- > creationTemplate > + ^self creationTemplateLocation: 'https://www.squeaksource.com/ProjectName' > - ^self creationTemplateLocation: 'http://www.squeaksource.com/ProjectName' > user: 'squeak' > password: 'squeak' > ! > > Item was added: > + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in category 'url rewrite') ----- > + rewriteUrl: aString forDownload: forDownload > + > + | result | > + result := aString. > + self urlRewriteRules groupsDo: [ :regexString :replacement :downloadOnly | > + (forDownload or: [ downloadOnly not ]) ifTrue: [ > + result := result copyWithRegex: regexString matchesReplacedWith: replacement ] ]. > + ^result > + > + " > + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). > + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). > + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). > + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). > + self assert: 'http://static.smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). > + self assert: 'http://smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). > + "! > > Item was added: > + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url rewrite') ----- > + urlRewriteRules > + > + ^URLRewriteRules ifNil: [ > + URLRewriteRules := #( > + "Regex to be replaced" "static replacement string" "download only" > + '^http\://source\.squeak\.org/' 'https://source.squeak.org/' false > + '^http\://squeaksource\.com/' 'https://squeaksource.com/' false > + '^http\://www.squeaksource\.com/' 'https://www.squeaksource.com/' false > + '^http\://smalltalkhub.com/' 'http://static.smalltalkhub.com/' true > + ) asOrderedCollection ]! > > Item was changed: > ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- > httpGet: url arguments: arguments > > + | urlString | > - | progress urlString client response result | > - progress := [ :total :amount | > - HTTPProgress new > - total: total; > - amount: amount; > - signal: 'Downloading...' ]. > urlString := arguments > ifNil: [ url ] > ifNotNil: [ > | queryString | > queryString := WebUtils encodeUrlEncodedForm: arguments. > (url includes: $?) > ifTrue: [ url, '&', queryString ] > ifFalse: [ url, '?', queryString ] ]. > + urlString := self class rewriteUrl: urlString forDownload: true. > + ^self webClientDo: [ :client | > + client > + username: self user; > + password: self password; > + httpGet: urlString do: [ :request | > + request > + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > + headerAt: 'Connection' put: 'Keep-Alive'; > + headerAt: 'Accept' put: '*/*' ] ]! > - self class useSharedWebClientInstance ifTrue: [ > - "Acquire webClient by atomically storing it in the client variable and setting its value to nil." > - client := webClient. > - webClient := nil ]. > - client > - ifNil: [ client := WebClient new ] > - ifNotNil: [ > - "Attempt to avoid an error on windows by recreating the underlying stream." > - client isConnected ifFalse: [ client close ] ]. > - response := client > - username: self user; > - password: self password; > - httpGet: urlString do: [ :request | > - request > - headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > - headerAt: 'Connection' put: 'Keep-Alive'; > - headerAt: 'Accept' put: '*/*' ]. > - result := (response code between: 200 and: 299) > - ifFalse: [ > - response content. "Make sure content is read." > - nil ] > - ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. > - self class useSharedWebClientInstance > - ifTrue: [ > - "Save the WebClient instance for reuse, but only if there is no client cached." > - webClient > - ifNil: [ webClient := client ] > - ifNotNil: [ client close ] ] > - ifFalse: [ client close ]. > - result ifNil: [ NetworkError signal: 'Could not access ', location ]. > - ^result! > > Item was added: > + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') ----- > + webClientDo: aBlock > + > + | client attemptsLeft response result | > + self class useSharedWebClientInstance ifTrue: [ > + "Acquire webClient by atomically storing it in the client variable and setting its value to nil." > + client := webClient. > + webClient := nil ]. > + > + client > + ifNil: [ client := WebClient new ] > + ifNotNil: [ > + "Attempt to avoid an error by recreating the underlying stream." > + client isConnected ifFalse: [ client close ] ]. > + > + attemptsLeft := 3. > + response := nil. > + [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ > + response := [ aBlock value: client ] > + on: NetworkError > + do: [ :error | > + attemptsLeft = 0 ifTrue: [ error pass ]. > + (3 - attemptsLeft) seconds asDelay wait. > + attemptsLeft := attemptsLeft - 1. > + nil "The response" ] ]. > + > + result := (response code between: 200 and: 299) > + ifFalse: [ > + response content. "Make sure content is read." > + nil ] > + ifTrue: [ > + (RWBinaryOrTextStream with: ( > + response contentWithProgress: [ :total :amount | > + HTTPProgress new > + total: total; > + amount: amount; > + signal ])) reset ]. > + > + self class useSharedWebClientInstance > + ifTrue: [ > + "Save the WebClient instance for reuse, but only if there is no client cached." > + webClient > + ifNil: [ webClient := client ] > + ifNotNil: [ client close ] ] > + ifFalse: [ client close ]. > + > + result ifNil: [ NetworkError signal: 'Could not access ', location ]. > + ^result! > > Item was changed: > ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in category 'private') ----- > writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock > + > + | stream urlString | > - | stream response statusLine code | > stream := RWBinaryOrTextStream on: String new. > aBlock value: stream. > + urlString := self urlForFileNamed: aString. > + urlString := self class rewriteUrl: urlString forDownload: false. > + ^self displayProgress: 'Uploading ', aString during: [ > + self webClientDo: [ :client | > + client > + username: self user; > + password: self password; > + httpPut: urlString > + content: stream contents > + type: nil > + do: [ :request | > + request > + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > + headerAt: 'Connection' put: 'Keep-Alive'; > + headerAt: 'Accept' put: '*/*' ] ] ]! > - self displayProgress: 'Uploading ', aString during:[ > - response := HTTPSocket > - httpPut: stream contents > - to: (self urlForFileNamed: aString) > - user: self user > - passwd: self password. > - ]. > - "More robust handling of HTTP responses. Instead of enumerating > - all possible return codes and http versions, do a quick parse" > - (response beginsWith: 'HTTP/') ifTrue:[ > - "Looks like an HTTP header, not some error message" > - statusLine := response copyUpTo: Character cr. > - code := [(statusLine findTokens: ' ') second asInteger] on: Error do:[]. > - ]. > - (code isInteger and:[code between: 200 and: 299]) > - ifFalse:[self error: response].! > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 12:15:18 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 12:15:18 +0000 Subject: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <7b0ad9f0e7154058be3f75247f929212@student.hpi.uni-potsdam.de> <20200913203422.GA85590@shell.msen.com> <62b5f1793b424e09881789f7c19fc56e@student.hpi.uni-potsdam.de> <44397F75-848B-4D1F-AC48-3A5F2C0542A6@gmx.de> <4e7855a126c44ccfbee961acc3a0b331@student.hpi.uni-potsdam.de> <9B0DB8D2-198B-4681-A315-03D7BDB42335@gmx.de> <20200914150112.GA80288@shell.msen.com> <20200914202934.GA25753@shell.msen.com> <02bd205c43974b2b8fce90ed05e7cea1@student.hpi.uni-potsdam.de>, Message-ID: <9e70027793ba42129db76dce5d5e74a2@student.hpi.uni-potsdam.de> Okay, I see now, but it's indeed a weak dictionary so I guess we cannot use this for reliably deprecating the World binding. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Vanessa Freudenberg Gesendet: Mittwoch, 16. September 2020 22:26:32 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) See page 41 "The Origin of Smalltalk SymbolTables" in Dan's Smalltalk Evolution paper: https://smalltalkzoo.computerhistory.org/papers/EvolutionOfSmalltalk.pdf There's likely others but this one I remembered ;) Vanessa On Wed, Sep 16, 2020 at 4:35 AM Thiede, Christoph > wrote: Hi Vanessa, > Wouldn't the binding still live in Undeclared? Are there any pointers to the concept of Undeclared? It seems to be a weak dictionary, so this does not look like a durable compatibility solution to me. Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Vanessa Freudenberg > Gesendet: Montag, 14. September 2020 23:30:59 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Etoys help needed loading e.g. CarAndPen.014.pr (was: Changeset: Eliminating global state from Morphic) On Mon, Sep 14, 2020 at 1:29 PM David T. Lewis > wrote: Karl, Thank you very much! Indeed, great sleuthing! Clearly we have introduced a bug in trunk since the 5.3 release, so we'll need to fix that. But to my original question - I was able to start with a fresh 5.3 image, and I can load CarAndPen.014.pr without problems. Then I removed the #World binding, and tried loading CarAndPen.014.pr to find out what the errors would look like. Big surprise - There was no error. The project still loads and runs as before. That is not what I expected, so I'm glad we checked. Dave Wouldn't the binding still live in Undeclared? - Vanessa - -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 12:16:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 12:16:19 +0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1540.mcz In-Reply-To: <046fa55d-0bc5-e7d0-a9bc-f13b153af425@zogotounga.net> References: <6e8f6302-01fc-c1d6-ab95-90c49a13c2a9@zogotounga.net> <47daea28e0484853bb5aa769040b79ae@student.hpi.uni-potsdam.de> <9ca405d0-4a33-78ac-721f-a919bdf61eae@zogotounga.net> <70032ace219344e69802ef2f34891ac7@student.hpi.uni-potsdam.de>, <046fa55d-0bc5-e7d0-a9bc-f13b153af425@zogotounga.net> Message-ID: Great! But it's not up to me to go ahead, someone else would need to review + merge this into the Trunk! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Stéphane Rollandin Gesendet: Freitag, 18. September 2020 18:56:00 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1540.mcz > Hi Stéphane, hi all! > > > Was that enough time for seeing what it's worth? ;-) I guess so :) Go on! Stef -------------- next part -------------- An HTML attachment was scrubbed... URL: From lecteur at zogotounga.net Sat Sep 19 12:17:02 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 14:17:02 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: Message-ID: <74203272-d930-2500-fcb9-84e462da34b4@zogotounga.net> > If you replace the above line with > >> add: sound copy pan: 0.75; > > then the produced sound should be okay. Sound objects have state, and if > you pass the same object twice, the state will be messed up. Hmm... this is much simpler than my "fix" :) Stef From karlramberg at gmail.com Sat Sep 19 12:32:38 2020 From: karlramberg at gmail.com (karl ramberg) Date: Sat, 19 Sep 2020 14:32:38 +0200 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: <65d6053df3734f509937e33b32052f2a@student.hpi.uni-potsdam.de> References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> <20200918215050.GA59541@shell.msen.com> <65d6053df3734f509937e33b32052f2a@student.hpi.uni-potsdam.de> Message-ID: On Sat, Sep 19, 2020 at 1:33 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > This is good to know, but I wonder whether this is a perfect solution when > you want to play a little bit around with some EToys morphs (such as > Viewers) without converting your whole image into an EToys optimized one. > And you get nowhere informed about the need to activate this EToys mode. > > > Should we modify the ScriptingSystem so that if an icon is not found, the > Squeakland resources are looked up before the default icon is answered? > Then you could use all the EToys tools without missing any icon, but still > would have your default halo icons etc. > I think we can just add the most obvious ones. It's not that many. This should cover the most of them: assoc := ReleaseBuilderSqueakland scriptingSystemImage109."RoundGoldBox" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage127."Try It Pressed" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage064."Try It" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage038."MenuIcon" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage165."AddCategoryViewer" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage171."AddInstanceVariable" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage014."TanOPressed" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage085."TanO" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage053."Gets" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage080."RightCaret" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage044."DownCaret" ScriptingSystem saveForm: assoc value atKey: assoc key. Best, Karl > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von David T. Lewis > *Gesendet:* Freitag, 18. September 2020 23:50:50 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] The Inbox: EToys-ct.406.mcz > > Oh, that's great! I had not noticed it either :-D > > Dave > > On Fri, Sep 18, 2020 at 04:57:03PM +0200, karl ramberg wrote: > > Ah, > > I have never seen that option :-D > > > > Best, > > Karl > > > > > > On Fri, Sep 18, 2020 at 3:11 PM Marcel Taeumel > > wrote: > > > > > For the 5.3 release, we decided to put that all behind > > > "ReleaseBuilderSqueakland class >> #setEtoysMode" to be triggered from > the > > > "Extras > Themes & Colors" docking-bar menu. > > > > > > Best, > > > Marcel > > > > > > Am 17.09.2020 21:51:16 schrieb karl ramberg : > > > I'm not sure it is for space reasons. > > > There could be other conflicts. > > > It seems to change some icons for the handles when you do > > > ReleaseBuilderSqueakland loadDefaultForms > > > > > > Lazy loading of just the missing icons would be awesome. > > > > > > Best, > > > Karl > > > > > > > > > On Thu, Sep 17, 2020 at 9:04 PM Thiede, Christoph < > > > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > > > >> Nice finding, Karl! I guess this has not been made part of the usual > > >> ReleaseBuilder for performance (storage) considerations. The question > is: > > >> Is this still relevant? Should we lazily load all forms if any is > missing > > >> in the ScriptingSystem? > > >> > > >> > > >> Best, > > >> > > >> Christoph > > >> > > >> ------------------------------ > > >> *Von:* Squeak-dev im > > >> Auftrag von karl ramberg > > >> *Gesendet:* Donnerstag, 17. September 2020 20:31:31 > > >> *An:* The general-purpose Squeak developers list > > >> *Betreff:* Re: [squeak-dev] The Inbox: EToys-ct.406.mcz > > >> > > >> > > >> > > >> On Thu, Sep 17, 2020 at 7:26 PM karl ramberg > > >> wrote: > > >> > > >>> A note about something to do: > > >>> I noticed that quite a few of the Etoys icons are not added to the > > >>> ScriptingSystem formDictionary. > > >>> So when opening a ScriptEditorMorph for example, icons like > > >>> 'RoundGoldBox' are missing so the UI is crippled. > > >>> The icons are found in ReleaseBuilderSqueakland but not initialized > > >>> > > >> > > >> You just need to do it: > > >> ReleaseBuilderSqueakland loadDefaultForms > > >> Best, > > >> Karl > > >> > > >> > > >>> Best, > > >>> Karl > > >>> > > >>> > > >>> On Thu, Sep 17, 2020 at 12:42 PM St??phane Rollandin < > > >>> lecteur at zogotounga.net> wrote: > > >>> > > >>>> > This commit is part of reconstruction of Objectland (also known as > > >>>> "The Worlds of Squeak"). > > >>>> > > >>>> Cool! > > >>>> > > >>>> Stef > > >>>> > > >>>> > > >>>> > > >> > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 12:41:56 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 12:41:56 +0000 Subject: [squeak-dev] The Inbox: EToys-ct.406.mcz In-Reply-To: References: <4f9934fe-3284-043a-c445-fed2c3e55cd3@zogotounga.net> <51858a0282ad45389bb1847790304359@student.hpi.uni-potsdam.de> <20200918215050.GA59541@shell.msen.com> <65d6053df3734f509937e33b32052f2a@student.hpi.uni-potsdam.de>, Message-ID: Karl, great finding! This small number of icons should not matter IMO. Can you put this somewhere into the ReleaseBuilder? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Samstag, 19. September 2020 14:32:38 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: EToys-ct.406.mcz On Sat, Sep 19, 2020 at 1:33 PM Thiede, Christoph > wrote: This is good to know, but I wonder whether this is a perfect solution when you want to play a little bit around with some EToys morphs (such as Viewers) without converting your whole image into an EToys optimized one. And you get nowhere informed about the need to activate this EToys mode. Should we modify the ScriptingSystem so that if an icon is not found, the Squeakland resources are looked up before the default icon is answered? Then you could use all the EToys tools without missing any icon, but still would have your default halo icons etc. I think we can just add the most obvious ones. It's not that many. This should cover the most of them: assoc := ReleaseBuilderSqueakland scriptingSystemImage109."RoundGoldBox" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage127."Try It Pressed" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage064."Try It" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage038."MenuIcon" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage165."AddCategoryViewer" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage171."AddInstanceVariable" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage014."TanOPressed" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage085."TanO" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage053."Gets" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage080."RightCaret" ScriptingSystem saveForm: assoc value atKey: assoc key. assoc := ReleaseBuilderSqueakland scriptingSystemImage044."DownCaret" ScriptingSystem saveForm: assoc value atKey: assoc key. Best, Karl Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von David T. Lewis > Gesendet: Freitag, 18. September 2020 23:50:50 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: EToys-ct.406.mcz Oh, that's great! I had not noticed it either :-D Dave On Fri, Sep 18, 2020 at 04:57:03PM +0200, karl ramberg wrote: > Ah, > I have never seen that option :-D > > Best, > Karl > > > On Fri, Sep 18, 2020 at 3:11 PM Marcel Taeumel > > wrote: > > > For the 5.3 release, we decided to put that all behind > > "ReleaseBuilderSqueakland class >> #setEtoysMode" to be triggered from the > > "Extras > Themes & Colors" docking-bar menu. > > > > Best, > > Marcel > > > > Am 17.09.2020 21:51:16 schrieb karl ramberg >: > > I'm not sure it is for space reasons. > > There could be other conflicts. > > It seems to change some icons for the handles when you do > > ReleaseBuilderSqueakland loadDefaultForms > > > > Lazy loading of just the missing icons would be awesome. > > > > Best, > > Karl > > > > > > On Thu, Sep 17, 2020 at 9:04 PM Thiede, Christoph < > > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > >> Nice finding, Karl! I guess this has not been made part of the usual > >> ReleaseBuilder for performance (storage) considerations. The question is: > >> Is this still relevant? Should we lazily load all forms if any is missing > >> in the ScriptingSystem? > >> > >> > >> Best, > >> > >> Christoph > >> > >> ------------------------------ > >> *Von:* Squeak-dev > im > >> Auftrag von karl ramberg > > >> *Gesendet:* Donnerstag, 17. September 2020 20:31:31 > >> *An:* The general-purpose Squeak developers list > >> *Betreff:* Re: [squeak-dev] The Inbox: EToys-ct.406.mcz > >> > >> > >> > >> On Thu, Sep 17, 2020 at 7:26 PM karl ramberg > > >> wrote: > >> > >>> A note about something to do: > >>> I noticed that quite a few of the Etoys icons are not added to the > >>> ScriptingSystem formDictionary. > >>> So when opening a ScriptEditorMorph for example, icons like > >>> 'RoundGoldBox' are missing so the UI is crippled. > >>> The icons are found in ReleaseBuilderSqueakland but not initialized > >>> > >> > >> You just need to do it: > >> ReleaseBuilderSqueakland loadDefaultForms > >> Best, > >> Karl > >> > >> > >>> Best, > >>> Karl > >>> > >>> > >>> On Thu, Sep 17, 2020 at 12:42 PM St??phane Rollandin < > >>> lecteur at zogotounga.net> wrote: > >>> > >>>> > This commit is part of reconstruction of Objectland (also known as > >>>> "The Worlds of Squeak"). > >>>> > >>>> Cool! > >>>> > >>>> Stef > >>>> > >>>> > >>>> > >> > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sat Sep 19 12:50:34 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 12:50:34 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1688.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1688.mcz ==================== Summary ==================== Name: Morphic-ct.1688 Author: ct Time: 19 September 2020, 2:50:27.564788 pm UUID: b93e1861-055a-4822-8856-8bd82d43201d Ancestors: Morphic-mt.1683 Adds large Squeak icon, made by Bert Freudenberg. Replaces Graphics-ct.422, which can be moved into the treated inbox. Reuploaded into MenuIcons instead of Form and base64-compressed. This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against Morphic-mt.1683 =============== Item was added: + ----- Method: MenuIcons class>>largeSqueakLogoIcon (in category 'accessing - icons') ----- (excessive size, no diff calculated) From commits at source.squeak.org Sat Sep 19 12:54:16 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 12:54:16 0000 Subject: [squeak-dev] The Inbox: MorphicExtras-ct.282.mcz Message-ID: A new version of MorphicExtras was added to project The Inbox: http://source.squeak.org/inbox/MorphicExtras-ct.282.mcz ==================== Summary ==================== Name: MorphicExtras-ct.282 Author: ct Time: 19 September 2020, 2:54:12.200788 pm UUID: 6fe94ab1-2bf7-4c51-9bf3-dc6089fb8b64 Ancestors: MorphicExtras-kfr.276 Adds MovingEyeMorph examples. Replaces MorphicExtras-ct.272, which can be moved into the treated inbox. Uses updated squeak icon resource. Depends on Morphic-ct.1688 and Graphics-ct.437. This commit is part of reconstruction of Objectland (also known as "The Worlds of Squeak"). For more information, see: http://forum.world.st/The-Inbox-MorphicExtras-ct-267-mcz-td5104764.html =============== Diff against MorphicExtras-kfr.276 =============== Item was added: + ----- Method: MovingEyeMorph class>>extraExampleSqueakGhostIsWatchingYou (in category '*MorphicExtras-examples') ----- + extraExampleSqueakGhostIsWatchingYou + "MovingEyeMorph extraExampleSqueakGhostIsWatchingYou openInHand" + + ^ (MenuIcons largeSqueakLogoIcon collectColors: #negated) asMorph + changeProportionalLayout; + addMorph: (self color: Color white irisColor: Color black) + fullFrame: (LayoutFrame fractions: (0.1 @ 0.15 exactCenter: 0.39 @ 0.55)); + addMorph: (self color: Color white irisColor: Color black) + fullFrame: (LayoutFrame fractions: (0.1 @ 0.15 exactCenter: 0.59 @ 0.56)); + yourself! Item was added: + ----- Method: MovingEyeMorph class>>extraExampleSqueakIsWatchingYou (in category '*MorphicExtras-examples') ----- + extraExampleSqueakIsWatchingYou + "MovingEyeMorph extraExampleSqueakIsWatchingYou openInHand" + + ^ MenuIcons largeSqueakLogoIcon asMorph + changeProportionalLayout; + addMorph: self new fullFrame: (LayoutFrame fractions: ( + 0.1 @ 0.15 exactCenter: 0.39 @ 0.55)); + addMorph: self new fullFrame: (LayoutFrame fractions: ( + 0.1 @ 0.15 exactCenter: 0.59 @ 0.56)); + yourself! From eliot.miranda at gmail.com Sat Sep 19 13:13:53 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 06:13:53 -0700 Subject: [squeak-dev] find class is broken... Message-ID: Hi All, whenever I hit Command-f in the browser's System Category pane to invoke FInd Class, instead the Find In Method dialog comes up. This is driving me nuts because to get back to the class dialog I often lose focus and end up deselecting the class. (and while we're at it, how easy is it to implement Form>>copyToClipboard ? this was tedious to produce; here are my "focus" preferences, I also have multi-window browsers turned on) [image: image.png] _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 320190 bytes Desc: not available URL: From christoph.thiede at student.hpi.uni-potsdam.de Sat Sep 19 13:22:27 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Sat, 19 Sep 2020 08:22:27 -0500 (CDT) Subject: [squeak-dev] TranslucentColor for World: Rendering issue In-Reply-To: References: <422ff7a6-886e-427c-91a8-148e3a282911@getmailbird.com> <0849af1602d84be9b3aa03d5b9d0690b@student.hpi.uni-potsdam.de> Message-ID: <1600521747957-0.post@n4.nabble.com> Hi Marcel, I just stumbled upon the same issue again. It would be great if you could review this changeset. :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From eliot.miranda at gmail.com Sat Sep 19 13:27:20 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 06:27:20 -0700 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: Message-ID: Hi Levente, Hi Stef, On Sat, Sep 19, 2020 at 4:47 AM Levente Uzonyi wrote: > Hi Eliot, > > On Fri, 18 Sep 2020, Eliot Miranda wrote: > > > Hi All, > > > > the SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: > method looks to be badly broken. I don't understand the index scaling > (yet) so I thought I'd throw this out there and see if anyone can spot when > and > > why this broke. > > > > Here's a simple example that generates a second long A below middle C > (220Hz), smoothing start and stop, and then plays it, so far so good: > > > > | samples sineTable sound | > > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > > sineTable := SoundPlayer sineTable: 73. > > sineTable doWithIndex: "And let's not deafen anyone..." > > [:sample :index| sineTable at: index put: sample // 4]. > > samples := SoundBuffer new: 16000. > > 1 to: samples size by: sineTable size do: > > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) > with: sineTable startingAt: 1]. > > 1 to: 146 do: "smooth start and end of the sound" > > [:i| > > samples at: i put: ((samples at: i) * i / 146) asInteger. > > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) > asInteger]. > > (SampledSound samples: samples samplingRate: 16000) play > > > > Now let's create a stereo sound image and try and play that: > > > > | samples sineTable sound | > > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > > sineTable := SoundPlayer sineTable: 73. > > sineTable doWithIndex: > > [:sample :index| sineTable at: index put: sample // 4]. > > samples := SoundBuffer new: 16000. > > 1 to: samples size by: sineTable size do: > > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) > with: sineTable startingAt: 1]. > > 1 to: 146 do: > > [:i| > > samples at: i put: ((samples at: i) * i / 146) asInteger. > > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) > asInteger]. > > sound := SampledSound samples: samples samplingRate: 16000. > > sound := MixedSound new > > add: sound pan: 0.25; > > add: sound pan: 0.75; > > If you replace the above line with > > > add: sound copy pan: 0.75; > > then the produced sound should be okay. Sound objects have state, and if > you pass the same object twice, the state will be messed up. > Thank you! This has cost me a day. I'd say that it fails the principle of least astonishment. I'm going to see if I can add a check to add:pan: or the mixdown step that would warn of this requirement. Levente > > > yourself. > > sound play > > > > Eek!! > > > > You can examine the mixing without hurting your ears by replacing > > "sound play" > > with > > "sound computeSamplesForSeconds: sound duration" > > which invokes the mixing directly. > > > > Any help making sense of this gratefully received. Some comments around > the > > > > scaledIndex := scaledIndex + scaledIncrement. > > scaledIndex >= ScaledIndexOverflow ifTrue: [ > > overflow := scaledIndex >> IncrementFractionBits. > > indexHighBits := indexHighBits + overflow. > > scaledIndex := scaledIndex - (overflow << IncrementFractionBits)]. > > > > in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: would > be most welcome. > > > > And if you simply comment out the primitive teh Smaklltalk code produces > garbage also, so the problem does not seem to be Slang but the algorithm > itself. > > > > _,,,^..^,,,_ > > best, Eliot > > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 19 13:28:09 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 06:28:09 -0700 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> Message-ID: Hi Stef, Hi Levente, On Sat, Sep 19, 2020 at 2:27 AM Stéphane Rollandin wrote: > Ok, it looks like your choice of frequency let a bug surface. > > In SampledSound>>#reset, the computation of scaledIncrement depends on > the sampling rate. But scaledIncrement is also supposed to be somewhat > commensurate with ScaledIndexOverflow which has a fixed value set in the > class-side initialize. > > So I guess the whole LargePositiveIntegers avoidance scheme is bogus. If > you get rid of it altogether, which in > SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: > translates as follow (primitive uncommented): > Stef, thanks for looking at the overflow arithmetic. In 64-bits we may not need it at all, and so I could add an ifTrue:ifFalse: based on the value of SmallInteger maxVal that would avoid the complex arithmetic on 64-bits. What do you think? > ----- > > mixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: > leftVol rightVol: rightVol > "Mix the given number of samples with the samples already in the > given > buffer starting at the given index. Assume that the buffer size is at > least (index + count) - 1." > > | lastIndex outIndex sample i s | > " module:'SoundGenerationPlugin'> > > > " > lastIndex := (startIndex + n) - 1. > outIndex := startIndex. "index of next stereo output sample > pair" > > [(outIndex <= samplesSize) and: [outIndex <= lastIndex]] > whileTrue: [ > sample := ((samples at: outIndex) * scaledVol) // > ScaleFactor. > leftVol > 0 ifTrue: [ > i := (2 * outIndex) - 1. > s := (aSoundBuffer at: i) + ((sample * leftVol) // > ScaleFactor). > s > 32767 ifTrue: [s := 32767]. "clipping!" > s < -32767 ifTrue: [s := -32767]. "clipping!" > aSoundBuffer at: i put: s]. > rightVol > 0 ifTrue: [ > i := 2 * outIndex. > s := (aSoundBuffer at: i) + ((sample * rightVol) > // ScaleFactor). > s > 32767 ifTrue: [s := 32767]. "clipping!" > s < -32767 ifTrue: [s := -32767]. "clipping!" > aSoundBuffer at: i put: s]. > > scaledVolIncr ~= 0 ifTrue: [ > scaledVol := scaledVol + scaledVolIncr. > ((scaledVolIncr > 0 and: [scaledVol >= > scaledVolLimit]) or: > [scaledVolIncr < 0 and: [scaledVol <= > scaledVolLimit]]) > ifTrue: [ "reached the limit; stop > incrementing" > scaledVol := scaledVolLimit. > scaledVolIncr := 0]]. > > outIndex := outIndex + 1]. > count := count - n. > > ----- > > then you get a nice sine wave (which still play badly in my Squeak image > but now only because it is too slow to generate without a primitive). > > Stef > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 13:28:55 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 13:28:55 +0000 Subject: [squeak-dev] highdpi testing In-Reply-To: References: , Message-ID: Hi Tobias, this sounds very promising, but unfortunately, I cannot try it out yet. I checked out your branch and built the VM for Win32, but while loading your changeset, my image gets damaged because it does not understand #displayScaled. I used a fresh #19860 image. Am I missing any dependency? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Craig Latta Gesendet: Donnerstag, 17. September 2020 19:34:49 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] highdpi testing Hooray! Thanks, Tobias! -C *** On 17/9/20 08:20, Tobias Pape wrote: > Hi all > > I have fiddled with the highdpi stuff and managed Linux/Unix to join the party. > > If you're interested, have a look at > https://github.com/OpenSmalltalk/opensmalltalk-vm/tree/krono/highdpi-v2 > > > The main thing that changes is that primitiveScreenScaleFactor now can dynamically determine what > scale to use for a monitor. Per se, that does nothing, but the DpiAware -Changeset below allows > Morphic and graphics to react to that. > > This should work on > - OSX (CoreGraphics, OpenGL, Metal) > - Windows > - Linux (X11, fbdev) > > I think this might be especially interesting for Tony and Christoph, among others. > > Things to Note: > - Windows needs a manifest. This can be tricky. See > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/scripts/ci/travis_build.sh#L21 > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-f0c6c15289ff7521735d7e74a350903dL15 > - Mac needs a customized Info.plist > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-9bc015b0fdf6b3aa927c5e49b2d882b9R129 > - Linux can be a mess. > There are now a few Environment variables to help out: > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/a2690b1b4e5c4488c716613f6654a74e31fa018a/platforms/unix/vm/sqUnixDisplayHelpers.h > Multimonitor people might want to set > SQUEAK_DISPLAY_PER_MONITOR_SCALE=1 > Tony on the Phone might want to set > SQUEAK_DISPLAY_PREFER_PHYSICAL_SCALE=1 > > > Have fun and play around :) > > Best regards > -Tobias -- Craig Latta :: research computer scientist Black Page Digital :: Berkeley, California 663137D7940BF5C0AFC 1349FB2ADA32C4D5314CE -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sat Sep 19 13:41:06 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 13:41:06 0000 Subject: [squeak-dev] The Trunk: Sound-eem.72.mcz Message-ID: Eliot Miranda uploaded a new version of Sound to project The Trunk: http://source.squeak.org/trunk/Sound-eem.72.mcz ==================== Summary ==================== Name: Sound-eem.72 Author: eem Time: 19 September 2020, 6:41:04.690078 am UUID: 993decea-486a-4d07-badb-dda290a3194e Ancestors: Sound-ct.71 Fix an annopying behaviour in MixedSound. If the same SampledSound is added more than once to a MixedSound then mix-down will produce weird results. Fix by checking for duplicates in MixedSound>>add:pan:volume: and silently taking a copy. Levente, if this is the wrong approach LMK and I can commit a version that raises an error instead. =============== Diff against Sound-ct.71 =============== Item was changed: ----- Method: MixedSound>>add:pan:volume: (in category 'composition') ----- add: aSound pan: leftRightPan volume: volume "Add the given sound with the given left-right pan, where 0.0 is full left, 1.0 is full right, and 0.5 is centered. The loudness of the sound will be scaled by volume, which ranges from 0 to 1.0." | pan vol | pan := ((leftRightPan * ScaleFactor) asInteger max: 0) min: ScaleFactor. vol := ((volume * ScaleFactor) asInteger max: 0) min: ScaleFactor. + "Sounds have state. If a sound occurs more than once in sounds then mixing can produce some very strange results" + sounds := sounds copyWith: ((sounds includes: aSound) ifTrue: [aSound copy] ifFalse: [aSound]). - sounds := sounds copyWith: aSound. leftVols := leftVols copyWith: ((ScaleFactor - pan) * vol) // ScaleFactor. + rightVols := rightVols copyWith: (pan * vol) // ScaleFactor! - rightVols := rightVols copyWith: (pan * vol) // ScaleFactor. - ! From Das.Linux at gmx.de Sat Sep 19 13:54:06 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sat, 19 Sep 2020 15:54:06 +0200 Subject: [squeak-dev] highdpi testing In-Reply-To: References: Message-ID: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> > On 19.09.2020, at 15:28, Thiede, Christoph wrote: > > Hi Tobias, > > this sounds very promising, but unfortunately, I cannot try it out yet. I checked out your branch and built the VM for Win32, but while loading your changeset, my image gets damaged because it does not understand #displayScaled. That is part of the Change set. Which class misses that? Also, one might have to reset all the UI themes. -t > I used a fresh #19860 image. Am I missing any dependency? :-) > > Best, > Christoph > Von: Squeak-dev im Auftrag von Craig Latta > Gesendet: Donnerstag, 17. September 2020 19:34:49 > An: squeak-dev at lists.squeakfoundation.org > Betreff: Re: [squeak-dev] highdpi testing > > > Hooray! Thanks, Tobias! > > > -C > > *** > > On 17/9/20 08:20, Tobias Pape wrote: > > > Hi all > > > > I have fiddled with the highdpi stuff and managed Linux/Unix to join > the party. > > > > If you're interested, have a look at > > https://github.com/OpenSmalltalk/opensmalltalk-vm/tree/krono/highdpi-v2 > > > > > > The main thing that changes is that primitiveScreenScaleFactor now can > dynamically determine what > > scale to use for a monitor. Per se, that does nothing, but the > DpiAware -Changeset below allows > > Morphic and graphics to react to that. > > > > This should work on > > - OSX (CoreGraphics, OpenGL, Metal) > > - Windows > > - Linux (X11, fbdev) > > > > I think this might be especially interesting for Tony and Christoph, > among others. > > > > Things to Note: > > - Windows needs a manifest. This can be tricky. See > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/scripts/ci/travis_build.sh#L21 > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-f0c6c15289ff7521735d7e74a350903dL15 > > - Mac needs a customized Info.plist > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-9bc015b0fdf6b3aa927c5e49b2d882b9R129 > > - Linux can be a mess. > > There are now a few Environment variables to help out: > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/a2690b1b4e5c4488c716613f6654a74e31fa018a/platforms/unix/vm/sqUnixDisplayHelpers.h > > Multimonitor people might want to set > > SQUEAK_DISPLAY_PER_MONITOR_SCALE=1 > > Tony on the Phone might want to set > > SQUEAK_DISPLAY_PREFER_PHYSICAL_SCALE=1 > > > > > > Have fun and play around :) > > > > Best regards > > -Tobias From gettimothy at zoho.com Sat Sep 19 13:55:20 2020 From: gettimothy at zoho.com (gettimothy) Date: Sat, 19 Sep 2020 09:55:20 -0400 Subject: [squeak-dev] ConfigurationOfXYZ ensureRecentMetacello pattern. Message-ID: <174a6a5d657.115efdfce48303.3718468625167681844@zoho.com> Hi Folks, I am "fixing" the ConfigurationOfXTreams , ConfigurationOfXMLParser...etc so I can load the packages for a project I am working on into Squeak6.0 alpha. There is a pattern on the ensureMetacello:' that I think you should be aware of. For example in ConfigurationOfXTreams, there is all these contortions. ensureMetacello "Bootstrap Gofer (if necessary), load latest mcz file for ConfigurationOfMetacello (using old Gofer API), then load the latest version of Metacello itself." | meta | meta := 'ConfigurationOfMetacello-dkh.674'. Smalltalk at: #MetacelloProject ifAbsent: [ "list of repositories to try, in case primary repository is not accessible" self bootstrapPackage: meta from: 'http://www.squeaksource.com/MetacelloRepository'. Smalltalk at: #ConfigurationOfMetacello ifPresent: [:config | config load]. Smalltalk at: #MetacelloProject ifAbsent: [ self bootstrapPackage: meta from: 'http://seaside.gemstone.com/ss/metacello'. Smalltalk at: #ConfigurationOfMetacello ifPresent: [:config | config load]. Smalltalk at: #MetacelloProject ifAbsent: [self error: 'Couldn''t l......] That stuff failed. However to get it to work, In a Workspace I run  Installer ensureRecentMetacello . And then replace the class side method in the ConfigurationOfXYZ with the same. ensureMetacello  Installer ensureRecentMetacello ConfigurationofXtreams has another method with some contortions that I just comment out. ConfigurationofXTreams class > ensureMetacelloBaseConfiguration  comment out. At that point the installs are going swimmingly. My point is that IIRC there was a lengthy discussion about ensureRecentMetacello on new Squeak a few months back. Is it possible that ConfigurationOfXYZ --all of them--have the gobbledy-gook to be replaced? thanks for your time. -------------- next part -------------- An HTML attachment was scrubbed... URL: From christoph.thiede at student.hpi.uni-potsdam.de Sat Sep 19 13:59:27 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Sat, 19 Sep 2020 08:59:27 -0500 (CDT) Subject: [squeak-dev] find class is broken... In-Reply-To: References: Message-ID: <1600523967143-0.post@n4.nabble.com> Hi Eliot, if you see the Find Method dialog, I think you do not have focused the system category pane. Did you make a click in it before (or hover it, provided that #mouseOverToKeyboardFocus is enabled)? I'm using the Find Class dialog everyday and it always works fine. > how easy is it to implement Form>>copyToClipboardYou could open an > Inspector on the Window and say `self lookFocused exportAsPNG` or > something like this. For clipboard support, we would need to implement > this on VM side first. This was already one point of my "possible VM > improvements" list ... :-)If you really want to, here is a hack that > already works today:morph := .code := (Base64MimeConverter mimeEncode: > (ByteArray streamContents: [:stream | PNGReadWriter putForm: morph > imageForm onStream: stream]) readStream multiLine: false) contents.html := > (' ' format: {code}).Clipboard clipboardText: > htmlIf Nabble wasn't so stubborn, you could watch the result here: > > Best,Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html -------------- next part -------------- An HTML attachment was scrubbed... URL: From christoph.thiede at student.hpi.uni-potsdam.de Sat Sep 19 14:01:12 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Sat, 19 Sep 2020 09:01:12 -0500 (CDT) Subject: [squeak-dev] find class is broken... In-Reply-To: References: Message-ID: <1600524072137-0.post@n4.nabble.com> Hi Eliot, if you see the Find Method dialog, I think you do not have focused the system category pane. Did you make a click in it before (or hover it, provided that #mouseOverToKeyboardFocus is enabled)? I'm using the Find Class dialog everyday and it always works fine. > how easy is it to implement Form>>copyToClipboard You could open an Inspector on the Window and say `self lookFocused exportAsPNG` or something like this. For clipboard support, we would need to implement this on VM side first. This was already one point of my "possible VM improvements" list ... :-) If you really want to, here is a hack that already works today: morph := .code := (Base64MimeConverter mimeEncode: (ByteArray streamContents: [:stream | PNGReadWriter putForm: morph imageForm onStream: stream]) readStream multiLine: false) contents.html := (' ' format: {code}).Clipboard clipboardText: HTML If Nabble wasn't so stubborn, you could watch the result here: Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html -------------- next part -------------- An HTML attachment was scrubbed... URL: From Tom.Beckmann at student.hpi.uni-potsdam.de Sat Sep 19 14:03:58 2020 From: Tom.Beckmann at student.hpi.uni-potsdam.de (Beckmann, Tom) Date: Sat, 19 Sep 2020 14:03:58 +0000 Subject: [squeak-dev] highdpi testing In-Reply-To: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> References: , <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> Message-ID: <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> Hi, turns out, we try drawing things while we load things :) In the attached, updated changeset I moved the #displayScaled definitions first so that we can happily resume drawing. Also, I removed the usage of the deprecated #anyOpenWindowLikeMe, which caused my image to freeze. Best, Tom ________________________________________ From: Squeak-dev on behalf of Tobias Pape Sent: Saturday, September 19, 2020 3:54:06 PM To: The general-purpose Squeak developers list Subject: Re: [squeak-dev] highdpi testing > On 19.09.2020, at 15:28, Thiede, Christoph wrote: > > Hi Tobias, > > this sounds very promising, but unfortunately, I cannot try it out yet. I checked out your branch and built the VM for Win32, but while loading your changeset, my image gets damaged because it does not understand #displayScaled. That is part of the Change set. Which class misses that? Also, one might have to reset all the UI themes. -t > I used a fresh #19860 image. Am I missing any dependency? :-) > > Best, > Christoph > Von: Squeak-dev im Auftrag von Craig Latta > Gesendet: Donnerstag, 17. September 2020 19:34:49 > An: squeak-dev at lists.squeakfoundation.org > Betreff: Re: [squeak-dev] highdpi testing > > > Hooray! Thanks, Tobias! > > > -C > > *** > > On 17/9/20 08:20, Tobias Pape wrote: > > > Hi all > > > > I have fiddled with the highdpi stuff and managed Linux/Unix to join > the party. > > > > If you're interested, have a look at > > https://github.com/OpenSmalltalk/opensmalltalk-vm/tree/krono/highdpi-v2 > > > > > > The main thing that changes is that primitiveScreenScaleFactor now can > dynamically determine what > > scale to use for a monitor. Per se, that does nothing, but the > DpiAware -Changeset below allows > > Morphic and graphics to react to that. > > > > This should work on > > - OSX (CoreGraphics, OpenGL, Metal) > > - Windows > > - Linux (X11, fbdev) > > > > I think this might be especially interesting for Tony and Christoph, > among others. > > > > Things to Note: > > - Windows needs a manifest. This can be tricky. See > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/scripts/ci/travis_build.sh#L21 > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-f0c6c15289ff7521735d7e74a350903dL15 > > - Mac needs a customized Info.plist > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-9bc015b0fdf6b3aa927c5e49b2d882b9R129 > > - Linux can be a mess. > > There are now a few Environment variables to help out: > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/a2690b1b4e5c4488c716613f6654a74e31fa018a/platforms/unix/vm/sqUnixDisplayHelpers.h > > Multimonitor people might want to set > > SQUEAK_DISPLAY_PER_MONITOR_SCALE=1 > > Tony on the Phone might want to set > > SQUEAK_DISPLAY_PREFER_PHYSICAL_SCALE=1 > > > > > > Have fun and play around :) > > > > Best regards > > -Tobias -------------- next part -------------- A non-text attachment was scrubbed... Name: DpiAware.1.cs Type: text/x-csharp Size: 15513 bytes Desc: DpiAware.1.cs URL: From commits at source.squeak.org Sat Sep 19 14:16:53 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 14:16:53 0000 Subject: [squeak-dev] The Trunk: Kernel-eem.1341.mcz Message-ID: Eliot Miranda uploaded a new version of Kernel to project The Trunk: http://source.squeak.org/trunk/Kernel-eem.1341.mcz ==================== Summary ==================== Name: Kernel-eem.1341 Author: eem Time: 19 September 2020, 7:16:49.354509 am UUID: facfc991-969e-4997-acb1-42478416023b Ancestors: Kernel-dtl.1340 Add two useful CompiledMethod testing methods... that sounds clumsy :-) =============== Diff against Kernel-dtl.1340 =============== Item was added: + ----- Method: CompiledMethod>>isLinkedNamedPrimitive (in category 'testing') ----- + isLinkedNamedPrimitive + "Answer if the receiver invokes a named primitive, and the method is linked to an actual primitive. + For example if the method hasn;t yet been used in the current session, it won't be linked" + ^self isNamedPrimitive and: [(self literalAt: 1) fourth ~= 0] + + "self systemNavigation browseAllSelect: [:m| m isLinkedNamedPrimitive]"! Item was added: + ----- Method: CompiledMethod>>isNamedPrimitive (in category 'testing') ----- + isNamedPrimitive + "Answer if the receiver invokes a named primitive." + ^self primitive == 117 + + "self systemNavigation browseAllSelect: [:m| m isNamedPrimitive]"! From commits at source.squeak.org Sat Sep 19 14:20:32 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 14:20:32 0000 Subject: [squeak-dev] The Trunk: Sound-eem.73.mcz Message-ID: Eliot Miranda uploaded a new version of Sound to project The Trunk: http://source.squeak.org/trunk/Sound-eem.73.mcz ==================== Summary ==================== Name: Sound-eem.73 Author: eem Time: 19 September 2020, 7:20:30.790279 am UUID: 4e7631d7-a5fd-4c91-8efa-c9f792aaee06 Ancestors: Sound-eem.72 Add error codes to the relevant SoundPlugin primitives. Avoid disturbing the sound system when shutting down and the sound system is inactive (requires Kernel-eem.1341!!). =============== Diff against Sound-eem.72 =============== Item was changed: ----- Method: SoundPlayer class>>primGetDefaultSoundPlayer (in category 'private') ----- primGetDefaultSoundPlayer "Answer the name of the default output device." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primGetDefaultSoundRecorder (in category 'private') ----- primGetDefaultSoundRecorder "Answer the name of the default input device." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primGetNumberOfSoundPlayerDevices (in category 'private') ----- primGetNumberOfSoundPlayerDevices "Answer the number of output devices." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primGetNumberOfSoundRecorderDevices (in category 'private') ----- primGetNumberOfSoundRecorderDevices "Answer the number of input devices." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primGetSoundPlayerDeviceName: (in category 'private') ----- primGetSoundPlayerDeviceName: n "Answer the name of the n'th output device." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primGetSoundRecorderDeviceName: (in category 'private') ----- primGetSoundRecorderDeviceName: n "Answer the name of the n'th input device." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primSetDefaultSoundPlayer: (in category 'private') ----- primSetDefaultSoundPlayer: deviceName "Set the default output device by supplying its name." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primSetDefaultSoundRecorder: (in category 'private') ----- primSetDefaultSoundRecorder: deviceName "Set the default input device by supplying its name." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primSoundAvailableBytes (in category 'private') ----- primSoundAvailableBytes + "Answer the number of bytes of available space in the sound output buffer." - "Return the number of bytes of available space in the sound output buffer." "Note: Squeak always uses buffers containing 4-bytes per sample (2 channels at 2 bytes per channel) regardless of the state of the Stereo flag." + + ^ self primitiveFailed! - - ^ self primitiveFailed - ! Item was changed: ----- Method: SoundPlayer class>>primSoundEnableAEC: (in category 'private') ----- primSoundEnableAEC: aBooleanInteger "Enable or disable acoustic echo-cancellation (AEC). aBooleanInteger should be 0 for false, and 1 for true." + + ^self primitiveFailed! - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primSoundGetRecordLevel (in category 'private') ----- primSoundGetRecordLevel + "Answer sound as array of doubles left then right channel, range is 0.0 to 1.0 but may be overdriven" + + ^self primitiveFailed! - "Return sound as array of doubles left then right channel, range is 0.0 to 1.0 but may be overdriven" - - self primitiveFailed! Item was changed: ----- Method: SoundPlayer class>>primSoundGetVolume (in category 'private') ----- primSoundGetVolume + "Answer sound as an array of doubles left then right channel, range is 0.0 to 1.0 but may be overdriven" + - "Return sound as array of doubles left then right channel, range is 0.0 to 1.0 but may be overdriven" - ^Array with: 1.0 with: 1.0! Item was changed: ----- Method: SoundPlayer class>>primSoundInsertSamples:from:samplesOfLeadTime: (in category 'private') ----- primSoundInsertSamples: count from: aSoundBuffer samplesOfLeadTime: anInteger + "Mix the given number of sample frames from the given sound buffer into the queue of samples that has already been submitted to the sound driver. This primitive is used to start a sound playing with minimum latency, even if large sound output buffers are being used to ensure smooth sound output. Answers the number of samples consumed, or zero if the primitive is not implemented or fails." - "Mix the given number of sample frames from the given sound buffer into the queue of samples that has already been submitted to the sound driver. This primitive is used to start a sound playing with minimum latency, even if large sound output buffers are being used to ensure smooth sound output. Returns the number of samples consumed, or zero if the primitive is not implemented or fails." + + ^ 0! - - ^ 0 - ! Item was changed: ----- Method: SoundPlayer class>>primSoundPlaySamples:from:startingAt: (in category 'private') ----- primSoundPlaySamples: count from: aSampleBuffer startingAt: index + "Copy count frames (pairs) of stereo sound samples into the current sound + output buffer from the given sample buffer starting at the given index." - "Copy count bytes into the current sound output buffer from the given sample buffer starting at the given index." + + ^self primitiveFailed - - ^ self primitiveFailed ! Item was changed: ----- Method: SoundPlayer class>>primSoundSetVolumeLeft:volumeRight: (in category 'private') ----- primSoundSetVolumeLeft: aLeftVolume volumeRight: aRightVolume "Set sound pass in float 0.0-1.0 for left and right channel, with possible 2.0 or higher to overdrive sound channel " + + ^self! - - ! Item was changed: ----- Method: SoundPlayer class>>primSoundStartBufferSize:rate:stereo: (in category 'private') ----- primSoundStartBufferSize: bufferSize rate: samplesPerSecond stereo: stereoFlag "Start double-buffered sound output with the given buffer size and sampling rate. This version has been superceded by primitive 171 (primSoundStartBufferSize:rate:stereo:semaIndex:)." "ar 12/5/1998 Turn off the sound if not supported" + + SoundSupported := false! - - SoundSupported := false.! Item was changed: ----- Method: SoundPlayer class>>primSoundStartBufferSize:rate:stereo:semaIndex: (in category 'private') ----- primSoundStartBufferSize: bufferSize rate: samplesPerSecond stereo: stereoFlag semaIndex: anInteger "Start double-buffered sound output with the given buffer size and sampling rate. If the given semaphore index is > 0, it is taken to be the index of a Semaphore in the external objects array to be signalled when the sound driver is ready to accept another buffer of samples." "Details: If this primitive fails, this method tries to use the older version instead." + - UseReadySemaphore := false. + self primSoundStartBufferSize: bufferSize rate: samplesPerSecond stereo: stereoFlag! - self primSoundStartBufferSize: bufferSize rate: samplesPerSecond stereo: stereoFlag. - ! Item was changed: ----- Method: SoundPlayer class>>primSoundStop (in category 'private') ----- primSoundStop "Stop double-buffered sound output. Must not raise an error because it is used inside error handling and at system shutdown" + + ^self! - ! Item was changed: ----- Method: SoundPlayer class>>primSoundSupportsAEC (in category 'private') ----- primSoundSupportsAEC "Answer if the plugin supports echo-cancellation on this OS/hardware." + + ^self primitiveFailed! - - self primitiveFailed! Item was added: + ----- Method: SoundPlayer class>>soundPluginActive (in category 'private') ----- + soundPluginActive + "Answer quickly if the SoundPlugin is actually active." + "self soundPluginActive" + ^SoundSupported + and: [(self class compiledMethodAt: (UseReadySemaphore + ifTrue: [#primSoundStartBufferSize:rate:stereo:semaIndex:] + ifFalse: [#primSoundStartBufferSize:rate:stereo:])) isLinkedNamedPrimitive]! Item was changed: ----- Method: SoundPlayer class>>stopPlayerProcess (in category 'player process') ----- stopPlayerProcess "Stop the sound player process." "SoundPlayer stopPlayerProcess" + PlayerProcess ifNotNil: + [PlayerProcess ~~ Processor activeProcess ifTrue: + [PlayerProcess terminate]. + PlayerProcess := nil]. + "Don't load the SoundPlugin if it is not in use..." + self soundPluginActive ifTrue: [self primSoundStop]. + ActiveSounds isEmpty ifFalse: + [ActiveSounds := OrderedCollection new]. - (PlayerProcess == nil or:[PlayerProcess == Processor activeProcess]) - ifFalse:[PlayerProcess terminate]. - PlayerProcess := nil. - self primSoundStop. - ActiveSounds := OrderedCollection new. Buffer := nil. + PlayerSemaphore isEmpty ifFalse: + [PlayerSemaphore := Semaphore forMutualExclusion]. - PlayerSemaphore := Semaphore forMutualExclusion. ReadyForBuffer ifNotNil: + [Smalltalk unregisterExternalObject: ReadyForBuffer. + ReadyForBuffer := nil]! - [Smalltalk unregisterExternalObject: ReadyForBuffer]. - ReadyForBuffer := nil. - ! Item was changed: ----- Method: SoundRecorder>>primGetActualRecordingSampleRate (in category 'primitives') ----- primGetActualRecordingSampleRate + "Answer the actual sample rate being used for recording. This primitive fails unless sound recording is currently in progress." - "Return the actual sample rate being used for recording. This primitive fails unless sound recording is currently in progress." + + ^self primitiveFailed! - - self primitiveFailed - ! Item was changed: ----- Method: SoundRecorder>>primRecordSamplesInto:startingAt: (in category 'primitives') ----- primRecordSamplesInto: aWordArray startingAt: index + "Record a sequence of 16-bit sound samples into the given array starting at the given sample index. Answer the number of samples recorded, which may be zero if no samples are currently available." - "Record a sequence of 16-bit sound samples into the given array starting at the given sample index. Return the number of samples recorded, which may be zero if no samples are currently available." + + ^self primitiveFailed! - - self primitiveFailed - ! Item was changed: ----- Method: SoundRecorder>>primSetRecordLevel: (in category 'primitives') ----- primSetRecordLevel: anInteger "Set the desired recording level to the given value in the range 0-1000, where 0 is the lowest recording level and 1000 is the maximum. Do nothing if the sound input hardware does not support changing the recording level." + + ^self primitiveFailed! - - self primitiveFailed - ! From commits at source.squeak.org Sat Sep 19 14:23:52 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 14:23:52 0000 Subject: [squeak-dev] The Trunk: EToys-eem.401.mcz Message-ID: Eliot Miranda uploaded a new version of EToys to project The Trunk: http://source.squeak.org/trunk/EToys-eem.401.mcz ==================== Summary ==================== Name: EToys-eem.401 Author: eem Time: 19 September 2020, 7:23:47.860928 am UUID: 1288d313-8a58-4c91-bddc-ce3def738399 Ancestors: EToys-eem.400 Add error codes to relevant SoundPlugin primitives. =============== Diff against EToys-eem.400 =============== Item was changed: ----- Method: SoundRecorder>>primGetSwitch:captureFlag:channel: (in category '*Etoys-Squeakland-primitives') ----- primGetSwitch: id captureFlag: capture channel: channel + - ^ -1! Item was changed: ----- Method: SoundRecorder>>primSetDevice:name: (in category '*Etoys-Squeakland-primitives') ----- primSetDevice: anInteger name: aString + + ^ -1! - - ^ -1. - ! Item was changed: ----- Method: SoundRecorder>>primSetSwitch:captureFlag:parameter: (in category '*Etoys-Squeakland-primitives') ----- primSetSwitch: id captureFlag: capture parameter: parameter + - ^ -1! From lecteur at zogotounga.net Sat Sep 19 14:32:02 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 16:32:02 +0200 Subject: [squeak-dev] The Trunk: Sound-eem.72.mcz In-Reply-To: References: Message-ID: <5db13b22-9c17-d749-0362-30404295b1ff@zogotounga.net> > Eliot Miranda uploaded a new version of Sound to project The Trunk: > http://source.squeak.org/trunk/Sound-eem.72.mcz > > ==================== Summary ==================== > > Name: Sound-eem.72 > Author: eem > Time: 19 September 2020, 6:41:04.690078 am > UUID: 993decea-486a-4d07-badb-dda290a3194e > Ancestors: Sound-ct.71 > > Fix an annopying behaviour in MixedSound. If the same SampledSound is added more than once to a MixedSound then mix-down will produce weird results. Fix by checking for duplicates in MixedSound>>add:pan:volume: and silently taking a copy. Levente, if this is the wrong approach LMK and I can commit a version that raises an error instead. Actually there is another conceptual problem with MixedSound>>add:pan:volume: It does not check whether the added sound is mono or stereo. As a consequence, when using MixedSound>>add: (which applies a pan of 0.5) with a stereo sound as argument, that sound overall volume gets decreased because the corresponding leftVols and rightVols values are set to obey the pan law. But I would expect the values added to leftVols and rightVols to be ScaleFactor so that the newly added sound is mixed as is, with no change in volume. To see (well, hear) the problem, use the attached stereo sound as follow: snd := SampledSound fromWaveFileNamed: 'sound.wav'. ms := MixedSound new. ms add: snd. snd play. "correct volume" ms play. "lower volume" Stef -------------- next part -------------- A non-text attachment was scrubbed... Name: sound.wav Type: audio/wav Size: 335132 bytes Desc: not available URL: From lecteur at zogotounga.net Sat Sep 19 14:41:34 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 16:41:34 +0200 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> Message-ID: > Stef, thanks for looking at the overflow arithmetic.  In 64-bits we may > not need it at all, and so I could add an ifTrue:ifFalse: based on the > value of SmallInteger maxVal that would avoid the complex arithmetic on > 64-bits.  What do you think? Well my analysis of the problem was way off, as Levente showed, so I will pass on this one:) Stef From lecteur at zogotounga.net Sat Sep 19 15:09:59 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 17:09:59 +0200 Subject: [squeak-dev] The Trunk: Sound-eem.72.mcz In-Reply-To: <5db13b22-9c17-d749-0362-30404295b1ff@zogotounga.net> References: <5db13b22-9c17-d749-0362-30404295b1ff@zogotounga.net> Message-ID: <81d57f83-168e-f410-6724-9249a0971020@zogotounga.net> > Actually there is another conceptual problem with > MixedSound>>add:pan:volume: > > It does not check whether the added sound is mono or stereo. > > As a consequence, when using MixedSound>>add: (which applies a pan of > 0.5) with a stereo sound as argument, that sound overall volume gets > decreased because the corresponding leftVols and rightVols values are > set to obey the pan law. > > But I would expect the values added to leftVols and rightVols to be > ScaleFactor so that the newly added sound is mixed as is, with no change > in volume. > > To see (well, hear) the problem, use the attached stereo sound as follow: > > snd := SampledSound fromWaveFileNamed: 'sound.wav'. > > ms := MixedSound new. > ms add: snd. > > snd play. "correct volume" > ms play. "lower volume" Fix attached. It keeps the same behaviour as before if the added sound is mono, or if the pan argument is not 0.5 In the other cases, that is for adding a stereo sound centered in the mix, it just bypasses any panning calculation. Note that I am not proposing this fix to be included as is, only tested and examined, because it also changes the way panning is computed by implementing a -4.5db power law (rationale here: http://www.cs.cmu.edu/~music/icm-online/readings/panlaws/index.html) which is the one I use in muO. Stef -------------- next part -------------- 'From Squeak5.3 of 3 March 2020 [latest update: #19431] on 19 September 2020 at 5:03:18 pm'! !MixedSound methodsFor: '*MuO-override' stamp: 'spfa 9/19/2020 15:21'! add: aSound "Add the given sound with a pan setting of centered and no attenuation." aSound isStereo ifFalse: [ ^ self add: aSound pan: 0.5 volume: 1.0.]. "If aSound is already stereo, there is no point in messing with its panning" sounds := sounds copyWith: aSound. leftVols := leftVols copyWith: ScaleFactor. rightVols := rightVols copyWith: ScaleFactor. ^ aSound ! ! !MixedSound methodsFor: '*MuO-override' stamp: 'spfa 9/19/2020 15:22'! add: aSound pan: leftRightPan "Add the given sound with the given left-right panning and no attenuation." (leftRightPan closeTo: 0.5) ifTrue: [^ self add: aSound; yourself]. self add: aSound pan: leftRightPan volume: 1.0. ! ! !MixedSound methodsFor: '*MuO-override' stamp: 'spfa 9/19/2020 17:03'! add: aSound pan: leftRightPan volume: volume "Add the given sound with the given left-right pan, where 0.0 is full left, 1.0 is full right, and 0.5 is centered. The loudness of the sound will be scaled by volume, which ranges from 0 to 1.0." "Note that MuO uses a -4.5 dB pan law" | vol pvs | (aSound isStereo and: [leftRightPan closeTo: 0.5]) ifTrue: [ "0.5 for pan is interpreted as 'do nothing' if aSound is already stereo (see senders)" vol := (ScaleFactor * volume) rounded. sounds := sounds copyWith: aSound. leftVols := leftVols copyWith: vol. rightVols := rightVols copyWith: vol.. ^ self].. vol := (volume max: 0.0) min: 1.0. sounds := sounds copyWith: aSound. pvs := self class panValuesFor: leftRightPan. leftVols := leftVols copyWith: (pvs first * vol) rounded. rightVols := rightVols copyWith: (pvs second * vol) rounded ! ! !MixedSound class methodsFor: '*MuO' stamp: 'spfa 9/19/2020 17:00'! db45PanAt: pNumber "pNumber ranges from -1 (45 degrees left) to 1 (45 degrees right)" "Uses -4.5 dB pan law ref: http://www.cs.cmu.edu/~music/icm-online/readings/panlaws/index.html" | theta | theta := (pNumber + 1) * Float pi / 4. ^ { "left" (theta cos * (Float halfPi - theta) / Float halfPi) sqrt. "right" (theta sin * theta / Float halfPi) sqrt }! ! !MixedSound class methodsFor: '*MuO' stamp: 'spfa 9/19/2020 17:00'! panValuesFor: aPos "aPos ranges from 0.0 to 1.0 (Squeak convention)" "MuO uses a -4.5 dB pan law" ^ (self db45PanAt: (aPos * 2) - 1) collect: [:ea | ((ea * ScaleFactor) rounded max: 0) min: ScaleFactor]! ! !MixedSound reorganize! ('accessing' duration sounds) ('composition' +) ('copying' copySounds postCopy) ('initialization' initialize) ('sound generation' doControl mixSampleCount:into:startingAt:leftVol:rightVol: reset samplesRemaining stopGracefully) ('*MuO-override' add: add:pan: add:pan:volume: isStereo) ('*MuO' addSansPanning: asSoundElementsWithDelay:forEdition: musicalNotesWithDelay: nominalDuration pitch: soundEditorClass |) ! From commits at source.squeak.org Sat Sep 19 16:11:42 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 16:11:42 0000 Subject: [squeak-dev] The Trunk: Sound-eem.74.mcz Message-ID: Eliot Miranda uploaded a new version of Sound to project The Trunk: http://source.squeak.org/trunk/Sound-eem.74.mcz ==================== Summary ==================== Name: Sound-eem.74 Author: eem Time: 19 September 2020, 9:11:40.666386 am UUID: 42a3c2d9-0bed-4310-bcf1-cbe0f3d0653b Ancestors: Sound-eem.73 Oops! stopPlayerProcess *must* stop the sound system when sent from startPlayerProcessBufferSize:rate:stereo:sound:. Spo refactor a bit, renaming stopPlayerProcess to stopPlayerProcess: to take a hardStop boolean. When quitting the argument is false. Add a 64-bit specific, integer-overflow agnostic version of mixSampleCount:into:startingAt:leftVol:rightVol:, for creating a simpler inline primitive in the 64-bit VM. =============== Diff against Sound-eem.73 =============== Item was changed: ----- Method: AbstractSound class>>translatedPrimitives (in category 'primitive generation') ----- translatedPrimitives ^#( (FMSound mixSampleCount:into:startingAt:leftVol:rightVol:) (PluckedSound mixSampleCount:into:startingAt:leftVol:rightVol:) (LoopedSampledSound mixSampleCount:into:startingAt:leftVol:rightVol:) (SampledSound mixSampleCount:into:startingAt:leftVol:rightVol:) + (SampledSound _64bitMixSampleCount:into:startingAt:leftVol:rightVol:) (ReverbSound applyReverbTo:startingAt:count:) ). ! Item was added: + ----- Method: SampledSound>>_64bitMixSampleCount:into:startingAt:leftVol:rightVol: (in category 'playing') ----- + _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol + "Mix the given number of samples with the samples already in the given buffer starting at the given index. + Assume that the buffer size is at least (index + count) - 1." + + | lastIndex outIndex sampleIndex sample i s | + + + + + lastIndex := (startIndex + n) - 1. + outIndex := startIndex. "index of next stereo output sample pair" + sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). + [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] whileTrue: + [sample := ((samples at: sampleIndex) * scaledVol) // ScaleFactor. + leftVol > 0 ifTrue: + [i := (2 * outIndex) - 1. + s := (aSoundBuffer at: i) + ((sample * leftVol) // ScaleFactor). + s > 32767 ifTrue: [s := 32767]. "clipping!!" + s < -32767 ifTrue: [s := -32767]. "clipping!!" + aSoundBuffer at: i put: s]. + rightVol > 0 ifTrue: + [i := 2 * outIndex. + s := (aSoundBuffer at: i) + ((sample * rightVol) // ScaleFactor). + s > 32767 ifTrue: [s := 32767]. "clipping!!" + s < -32767 ifTrue: [s := -32767]. "clipping!!" + aSoundBuffer at: i put: s]. + + scaledVolIncr ~= 0 ifTrue: + [scaledVol := scaledVol + scaledVolIncr. + ((scaledVolIncr > 0 and: [scaledVol >= scaledVolLimit]) or: + [scaledVolIncr < 0 and: [scaledVol <= scaledVolLimit]]) + ifTrue: "reached the limit; stop incrementing" + [scaledVol := scaledVolLimit. + scaledVolIncr := 0]]. + + scaledIndex := scaledIndex + scaledIncrement. + + sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). + outIndex := outIndex + 1]. + count := count - n + ! Item was changed: ----- Method: SampledSound>>mixSampleCount:into:startingAt:leftVol:rightVol: (in category 'playing') ----- mixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol + "Mix the given number of samples with the samples already in the given buffer starting at the given index. + Assume that the buffer size is at least (index + count) - 1." - "Mix the given number of samples with the samples already in the given buffer starting at the given index. Assume that the buffer size is at least (index + count) - 1." | lastIndex outIndex sampleIndex sample i s overflow | + + + - - - + SmallInteger maxVal > 16r3FFFFFFF ifTrue: "In 64-bits we don't have to worry about 2^15 * 2^15 overflow" + [^self _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol]. lastIndex := (startIndex + n) - 1. outIndex := startIndex. "index of next stereo output sample pair" sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] whileTrue: [ sample := ((samples at: sampleIndex) * scaledVol) // ScaleFactor. leftVol > 0 ifTrue: [ i := (2 * outIndex) - 1. s := (aSoundBuffer at: i) + ((sample * leftVol) // ScaleFactor). s > 32767 ifTrue: [s := 32767]. "clipping!!" s < -32767 ifTrue: [s := -32767]. "clipping!!" aSoundBuffer at: i put: s]. rightVol > 0 ifTrue: [ i := 2 * outIndex. s := (aSoundBuffer at: i) + ((sample * rightVol) // ScaleFactor). s > 32767 ifTrue: [s := 32767]. "clipping!!" s < -32767 ifTrue: [s := -32767]. "clipping!!" aSoundBuffer at: i put: s]. scaledVolIncr ~= 0 ifTrue: [ scaledVol := scaledVol + scaledVolIncr. ((scaledVolIncr > 0 and: [scaledVol >= scaledVolLimit]) or: [scaledVolIncr < 0 and: [scaledVol <= scaledVolLimit]]) ifTrue: [ "reached the limit; stop incrementing" scaledVol := scaledVolLimit. scaledVolIncr := 0]]. scaledIndex := scaledIndex + scaledIncrement. scaledIndex >= ScaledIndexOverflow ifTrue: [ overflow := scaledIndex >> IncrementFractionBits. indexHighBits := indexHighBits + overflow. scaledIndex := scaledIndex - (overflow << IncrementFractionBits)]. sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). outIndex := outIndex + 1]. + count := count - n! - count := count - n. - ! Item was changed: ----- Method: SoundPlayer class>>shutDown: (in category 'snapshotting') ----- shutDown: quitting "Stop player process, for example before snapshotting." + quitting ifTrue: + [self stopPlayerProcess: false. + ReverbState := nil]! - quitting ifTrue: [ - self stopPlayerProcess. - ReverbState := nil].! Item was changed: ----- Method: SoundPlayer class>>startPlayerProcessBufferSize:rate:stereo:sound: (in category 'player process') ----- startPlayerProcessBufferSize: bufferSize rate: samplesPerSecond stereo: stereoFlag sound: aSound "Start the sound player process. Terminate the old process, if any." "SoundPlayer startPlayerProcessBufferSize: 1000 rate: 11025 stereo: false" + self stopPlayerProcess: true. + aSound ifNotNil: "stopPlayerProcess: ensures ActiveSounds are empty..." + [ActiveSounds add: aSound]. - self stopPlayerProcess. - aSound - ifNil:[ActiveSounds := OrderedCollection new] - ifNotNil:[ActiveSounds := OrderedCollection with: aSound]. Buffer := SoundBuffer newStereoSampleCount: (bufferSize // 4) * 4. + LastBuffer ifNotNil: + [LastBuffer := SoundBuffer basicNew: Buffer basicSize]. - LastBuffer ifNotNil:[LastBuffer := SoundBuffer basicNew: Buffer basicSize]. PlayerSemaphore := Semaphore forMutualExclusion. SamplingRate := samplesPerSecond. Stereo := stereoFlag. SoundSupported := true. "Assume so" UseReadySemaphore := true. "set to false if ready semaphore not supported by VM" Smalltalk newExternalSemaphoreDo: [ :semaphore :index | ReadyForBuffer := semaphore. self primSoundStartBufferSize: Buffer stereoSampleCount rate: samplesPerSecond stereo: Stereo semaIndex: index ]. "Check if sound start prim was successful" SoundSupported ifFalse:[ Smalltalk unregisterExternalObject: ReadyForBuffer. ReadyForBuffer := nil. ^self ]. UseReadySemaphore ifTrue: [PlayerProcess := [SoundPlayer playLoop] newProcess] ifFalse: [PlayerProcess := [SoundPlayer oldStylePlayLoop] newProcess]. UseReverb ifTrue: [self startReverb]. PlayerProcess name: 'Sound Player (', ActiveSounds size asString, ')'; priority: Processor userInterruptPriority; resume! Item was removed: - ----- Method: SoundPlayer class>>stopPlayerProcess (in category 'player process') ----- - stopPlayerProcess - "Stop the sound player process." - "SoundPlayer stopPlayerProcess" - - PlayerProcess ifNotNil: - [PlayerProcess ~~ Processor activeProcess ifTrue: - [PlayerProcess terminate]. - PlayerProcess := nil]. - "Don't load the SoundPlugin if it is not in use..." - self soundPluginActive ifTrue: [self primSoundStop]. - ActiveSounds isEmpty ifFalse: - [ActiveSounds := OrderedCollection new]. - Buffer := nil. - PlayerSemaphore isEmpty ifFalse: - [PlayerSemaphore := Semaphore forMutualExclusion]. - ReadyForBuffer ifNotNil: - [Smalltalk unregisterExternalObject: ReadyForBuffer. - ReadyForBuffer := nil]! Item was added: + ----- Method: SoundPlayer class>>stopPlayerProcess: (in category 'player process') ----- + stopPlayerProcess: hardStop + "Stop the sound player process." + "SoundPlayer stopPlayerProcess" + + PlayerProcess ifNotNil: + [PlayerProcess ~~ Processor activeProcess ifTrue: + [PlayerProcess terminate]. + PlayerProcess := nil]. + (hardStop or: [self soundPluginActive]) ifTrue: [self primSoundStop]. + ActiveSounds isEmpty ifFalse: + [ActiveSounds := OrderedCollection new]. + Buffer := nil. + PlayerSemaphore isEmpty ifFalse: + [PlayerSemaphore := Semaphore forMutualExclusion]. + ReadyForBuffer ifNotNil: + [Smalltalk unregisterExternalObject: ReadyForBuffer. + ReadyForBuffer := nil]! From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 17:26:59 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 17:26:59 +0000 Subject: [squeak-dev] highdpi testing In-Reply-To: <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> References: , <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de>, <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> Message-ID: <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> Hi, ah, I had assumed something like this, thanks for fixing, Tom! :-) #actualScreenScaleFactor is a very helpful tool, but I do not yet see the whole idea of the changeset. In what sense is this meant to be a complement of RealEstateAgent >> #scaleFactor or rather an orthogonal concept? After loading the changeset and resetting my UI theme, the background image of the world disappeared and all new windows are huge, but they still have a tiny font - am I supposed to set the #scaleFactor manually (to 12, in my example)? Where exactly can I see any components in the image that are upscaled by the new changeset? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Beckmann, Tom Gesendet: Samstag, 19. September 2020 16:03:58 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] highdpi testing Hi, turns out, we try drawing things while we load things :) In the attached, updated changeset I moved the #displayScaled definitions first so that we can happily resume drawing. Also, I removed the usage of the deprecated #anyOpenWindowLikeMe, which caused my image to freeze. Best, Tom ________________________________________ From: Squeak-dev on behalf of Tobias Pape Sent: Saturday, September 19, 2020 3:54:06 PM To: The general-purpose Squeak developers list Subject: Re: [squeak-dev] highdpi testing > On 19.09.2020, at 15:28, Thiede, Christoph wrote: > > Hi Tobias, > > this sounds very promising, but unfortunately, I cannot try it out yet. I checked out your branch and built the VM for Win32, but while loading your changeset, my image gets damaged because it does not understand #displayScaled. That is part of the Change set. Which class misses that? Also, one might have to reset all the UI themes. -t > I used a fresh #19860 image. Am I missing any dependency? :-) > > Best, > Christoph > Von: Squeak-dev im Auftrag von Craig Latta > Gesendet: Donnerstag, 17. September 2020 19:34:49 > An: squeak-dev at lists.squeakfoundation.org > Betreff: Re: [squeak-dev] highdpi testing > > > Hooray! Thanks, Tobias! > > > -C > > *** > > On 17/9/20 08:20, Tobias Pape wrote: > > > Hi all > > > > I have fiddled with the highdpi stuff and managed Linux/Unix to join > the party. > > > > If you're interested, have a look at > > https://github.com/OpenSmalltalk/opensmalltalk-vm/tree/krono/highdpi-v2 > > > > > > The main thing that changes is that primitiveScreenScaleFactor now can > dynamically determine what > > scale to use for a monitor. Per se, that does nothing, but the > DpiAware -Changeset below allows > > Morphic and graphics to react to that. > > > > This should work on > > - OSX (CoreGraphics, OpenGL, Metal) > > - Windows > > - Linux (X11, fbdev) > > > > I think this might be especially interesting for Tony and Christoph, > among others. > > > > Things to Note: > > - Windows needs a manifest. This can be tricky. See > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/scripts/ci/travis_build.sh#L21 > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-f0c6c15289ff7521735d7e74a350903dL15 > > - Mac needs a customized Info.plist > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/Cog...krono/highdpi-v2#diff-9bc015b0fdf6b3aa927c5e49b2d882b9R129 > > - Linux can be a mess. > > There are now a few Environment variables to help out: > > > https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/a2690b1b4e5c4488c716613f6654a74e31fa018a/platforms/unix/vm/sqUnixDisplayHelpers.h > > Multimonitor people might want to set > > SQUEAK_DISPLAY_PER_MONITOR_SCALE=1 > > Tony on the Phone might want to set > > SQUEAK_DISPLAY_PREFER_PHYSICAL_SCALE=1 > > > > > > Have fun and play around :) > > > > Best regards > > -Tobias -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 19 17:56:47 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 10:56:47 -0700 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: <2937ccb4-2970-7733-a142-07809a68b684@zogotounga.net> References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> <2937ccb4-2970-7733-a142-07809a68b684@zogotounga.net> Message-ID: Hi Stef, On Sat, Sep 19, 2020 at 1:17 AM Stéphane Rollandin wrote: > > if you play the example you’ll see that the first version works > fine. The second version, which adds MixedSound yo create stereo doesn’t. > That proves that the issue is in MixedSound. Please just try the two > examples. > > Right. What I can see If I do > > | samples sineTable sound | > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > sineTable := SoundPlayer sineTable: 73. > sineTable doWithIndex: > [:sample :index| sineTable at: index put: sample // 4]. > samples := SoundBuffer new: 16000. > 1 to: samples size by: sineTable size do: > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: > 16000) > with: sineTable startingAt: 1]. > 1 to: 146 do: > [:i| > samples at: i put: ((samples at: i) * i / 146) asInteger. > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) > asInteger]. > sound := SampledSound samples: samples samplingRate: 16000. > sound := MixedSound new > add: sound pan: 0.25; > add: sound pan: 0.75; > yourself. > (SampledSound samples: sound monoSamples samplingRate: sound > originalSamplingRate) edit > > in the muO image I pointed to in my previous message, is a phasing > problem leading to periodic glitches (the attached image is directly > taken from the muO editor) > [image: image.png] Interesting!! I don't see this in my data, but I do see evidence of an off-by-one error in misdown. Here's the start, first zero crossing, and end of the mono sound (the original samples sound buffer) { samples first: 8. samples copyFrom: 70 to: 77. samples last: 8 } {a SoundBuffer(4 19 42 75 117 166 222 285) . a SoundBuffer(-1003 -682 -347 0 356 720 1088 1457) . a SoundBuffer(221 222 213 196 170 136 96 50)} So these look fine and smooth. But here's the corresponding data from the computeSamplesForSeconds: result of the MixedSound: { self first: 16. self copyFrom: 70 * 2 to: 77 * 2 + 1. self last: 16 } {a SoundBuffer(4 4 4 4 18 18 41 41 41 41 74 74 116 116 165 165) . a SoundBuffer(-2712 -2712 -2712 -2836 -2836 -2940 -2940 -2940 -2940 -3022 -3022 -3084 -3084 -3124 -3124 -3214) . a SoundBuffer(212 212 212 212 196 196 169 169 169 169 136 136 96 96 49 49)} The data *should* start with a SoundBuffer(4 4 18 18... The data ends well. But the first crossing is completely wrong. Strange, because it sounds ok... Let me count the crossings using: | sign crossings | crossings := 0. sign := self first sign. self do: [:each| each sign = sign negated ifTrue: [crossings := crossings + 1. sign := each sign]]. crossings Both give 438 as expected. Strange. Why is there no crossing between 140 and 154 in the mixed down sound? Ah, ok, the sample rate has changed: | sign crossings ncrossings | ncrossings := 0. crossings := OrderedCollection new. sign := self first sign. self doWithIndex: [:each :idx| each sign = sign negated ifTrue: [crossings add: idx - 1 -> idx. ncrossings := ncrossings + 1. sign := each sign]]. { crossings first: 4. ncrossings } {an OrderedCollection(100->101 202->203 302->303 404->405) . 438} So everything is fine *except for* the extra sample at the beginning. That's clearly an error, right? I'm looking at this.. > > Stef > _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 19346 bytes Desc: not available URL: From eliot.miranda at gmail.com Sat Sep 19 18:17:12 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 11:17:12 -0700 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? Message-ID: Hi Marcel, can we try and reduce the frequency at which we compute the variables in the context inspector in the debugger? It is a noticeable performance hit. I really like the user interface, but the performance hit is making debugging difficult. As an example use this: | samples sineTable sound | "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" sineTable := SoundPlayer sineTable: 73. sineTable doWithIndex: "And let's not deafen anyone..." [:sample :index| sineTable at: index put: sample // 4]. samples := SoundBuffer new: 16000. 1 to: samples size by: sineTable size do: [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. 1 to: 146 do: [:i| samples at: i put: ((samples at: i) * i / 146) asInteger. samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. sound := SampledSound samples: samples samplingRate: 16000. sound := MixedSound new add: sound pan: 0.25; add: sound pan: 0.75; yourself. sound computeSamplesForSeconds: sound duration Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an image containing Tools-mt.942). Debug the above in the workspace. Position the cursor at "sound computeSamplesForSeconds: sound duration" and do "run to here". It is essentially instantaneous. Now do the same in a contemporary trunk image. It takes almost 6 seconds. I used to be able to click step as fast as I could and the system would keep up with me. Now I find that if I click too fast I can accumulate excess clicks and when I stp clicking the system will continue stepping and go too far. I don't want to lose the feedback the new variables list gives us. But I do find the performance hit tedious. I wonder could we cache the list and only update it - when Morphic updates, and - when the context changes? _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 18:36:53 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 18:36:53 +0000 Subject: [squeak-dev] ColorForm with transparency not working properly Message-ID: <618f98fce9dd44cbb9b603a6eca2e463@student.hpi.uni-potsdam.de> Hi all, try out this: m := Morph new color: (Color red alpha: 0.5). m openInHand. m imageForm openAsMorph. Output: [cid:b8fe602f-7ca6-47d9-bd34-b5b52440cad7] And for completeness' sake, "m imageForm asMorph imageForm openAsMorph" appears just a bit darker again. I traced the issue down to two methods: For the regular Morph, FormCanvas >> #setFillColor:, and for the ImageMorph, Canvas >> #translucentImage:at:sourceRect:. In both methods, at the end, the rule Form blend is used to draw the image. If I change it to Form over, the colors are the same and the issue seems to be fixed. But unfortunately, I could not find any documentation about the different Form rules such as #blend, #over, #paint etc. Can you give me some pointers? Also, I noted the comment in #translucentImage:at:sourceRect: which states: "Draw a translucent image using the best available way of representing translucency. Note: This will be fixed in the future." Which workaround is this comment referring to? Looking forward to your help! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 8995 bytes Desc: pastedImage.png URL: From eliot.miranda at gmail.com Sat Sep 19 18:43:34 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 11:43:34 -0700 Subject: [squeak-dev] MixedSound (& consequently stereo) is badly broken in both 32-bit and 64-bit versions In-Reply-To: References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> <2937ccb4-2970-7733-a142-07809a68b684@zogotounga.net> Message-ID: Hi Dan, Hi Stef, Hi Levente, Hi Christoph, this is not a bug. I'm just noting something for our collective understanding and pointing to a potential improvement in the sound system. This is concerning the code in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol:. If you look at the results of mixing down a 16KHz sound into e.g. a 22.1KHz sound (the default sampling rate for a mix down) then if the input is a 220Hz sine wave at 16KHz (A below middle C, computed in the example below) then the first few samples look like this a SoundBuffer(4 19 42 75 117 166 222 285 353 425 ... When we mix this down the corresponding range of samples in the stereo 22.1Khz mixed-down sound is a SoundBuffer(4 4 4 4 18 18 41 41 41 41 74 74 116 116 165 165 165 165 221 221 284 284 284 284 352 352 424 424 500 500 ... Now this comes because of the scaled indexing in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: and is to be expected. The input sample rate is less than the output sample rate so the algorithm smears mixed output samples until the next matching input sample is reached. The improvement would be not to smear, but to interpolate. If we added (say, simple linear) interpolation then the algorithm would instead produce a SoundBuffer(4 4 11 11 18 18 41 41 57 57 74 74 116 116 165 165 193 193 221 221 284 284 318 318 352 352 424 424 500 500 ... Now I (we?) just have to find time to work on this ;-) On Sat, Sep 19, 2020 at 10:56 AM Eliot Miranda wrote: > Hi Stef, > > On Sat, Sep 19, 2020 at 1:17 AM Stéphane Rollandin > wrote: > >> > if you play the example you’ll see that the first version works >> fine. The second version, which adds MixedSound yo create stereo doesn’t. >> That proves that the issue is in MixedSound. Please just try the two >> examples. >> >> Right. What I can see If I do >> >> | samples sineTable sound | >> "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" >> sineTable := SoundPlayer sineTable: 73. >> sineTable doWithIndex: >> [:sample :index| sineTable at: index put: sample // 4]. >> samples := SoundBuffer new: 16000. >> 1 to: samples size by: sineTable size do: >> [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: >> 16000) >> with: sineTable startingAt: 1]. >> 1 to: 146 do: >> [:i| >> samples at: i put: ((samples at: i) * i / 146) asInteger. >> samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) >> asInteger]. >> sound := SampledSound samples: samples samplingRate: 16000. >> sound := MixedSound new >> add: sound pan: 0.25; >> add: sound pan: 0.75; >> yourself. >> (SampledSound samples: sound monoSamples samplingRate: sound >> originalSamplingRate) edit >> >> in the muO image I pointed to in my previous message, is a phasing >> problem leading to periodic glitches (the attached image is directly >> taken from the muO editor) >> > > [image: image.png] > > Interesting!! I don't see this in my data, but I do see evidence of an > off-by-one error in misdown. Here's the start, first zero crossing, and > end of the mono sound (the original samples sound buffer) > > { samples first: 8. samples copyFrom: 70 to: 77. samples last: 8 } > {a SoundBuffer(4 19 42 75 117 166 222 285) . > a SoundBuffer(-1003 -682 -347 0 356 720 1088 1457) . > a SoundBuffer(221 222 213 196 170 136 96 50)} > > So these look fine and smooth. > > But here's the corresponding data from the computeSamplesForSeconds: > result of the MixedSound: > { self first: 16. self copyFrom: 70 * 2 to: 77 * 2 + 1. self last: 16 } > {a SoundBuffer(4 4 4 4 18 18 41 41 41 41 74 74 116 116 165 165) . > a SoundBuffer(-2712 -2712 -2712 -2836 -2836 -2940 -2940 -2940 -2940 > -3022 -3022 -3084 -3084 -3124 -3124 -3214) . > a SoundBuffer(212 212 212 212 196 196 169 169 169 169 136 136 96 96 49 > 49)} > > The data *should* start with a SoundBuffer(4 4 18 18... > The data ends well. But the first crossing is completely wrong. > > Strange, because it sounds ok... > > Let me count the crossings using: > | sign crossings | > crossings := 0. > sign := self first sign. > self do: > [:each| > each sign = sign negated ifTrue: > [crossings := crossings + 1. > sign := each sign]]. > crossings > > Both give 438 as expected. Strange. Why is there no crossing between 140 > and 154 in the mixed down sound? > > > Ah, ok, the sample rate has changed: > > > | sign crossings ncrossings | > ncrossings := 0. > crossings := OrderedCollection new. > sign := self first sign. > self doWithIndex: > [:each :idx| > each sign = sign negated ifTrue: > [crossings add: idx - 1 -> idx. > ncrossings := ncrossings + 1. > sign := each sign]]. > { crossings first: 4. ncrossings } {an OrderedCollection(100->101 202->203 > 302->303 404->405) . 438} > > > So everything is fine *except for* the extra sample at the beginning. > That's clearly an error, right? > > I'm looking at this.. >> >> Stef >> > > _,,,^..^,,,_ > best, Eliot > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 19346 bytes Desc: not available URL: From eliot.miranda at gmail.com Sat Sep 19 18:45:21 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 11:45:21 -0700 Subject: [squeak-dev] Interpolation on mix down in MixedSound In-Reply-To: References: <15B25848-5500-45C1-B81F-3E0015CB60E9@gmail.com> <2937ccb4-2970-7733-a142-07809a68b684@zogotounga.net> Message-ID: Hi Dan, Hi Stef, Hi Levente, Hi Christoph, (duplicating to change the subject line) this is not a bug. I'm just noting something for our collective understanding and pointing to a potential improvement in the sound system. This is concerning the code in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol:. If you look at the results of mixing down a 16KHz sound into e.g. a 22.1KHz sound (the default sampling rate for a mix down) then if the input is a 220Hz sine wave at 16KHz (A below middle C, computed in the example below) then the first few samples look like this a SoundBuffer(4 19 42 75 117 166 222 285 353 425 ... When we mix this down the corresponding range of samples in the stereo 22.1Khz mixed-down sound is a SoundBuffer(4 4 4 4 18 18 41 41 41 41 74 74 116 116 165 165 165 165 221 221 284 284 284 284 352 352 424 424 500 500 ... Now this comes because of the scaled indexing in SampledSound>>#mixSampleCount:into:startingAt:leftVol:rightVol: and is to be expected. The input sample rate is less than the output sample rate so the algorithm smears mixed output samples until the next matching input sample is reached. The improvement would be not to smear, but to interpolate. If we added (say, simple linear) interpolation then the algorithm would instead produce a SoundBuffer(4 4 11 11 18 18 41 41 57 57 74 74 116 116 165 165 193 193 221 221 284 284 318 318 352 352 424 424 500 500 ... Now I (we?) just have to find time to work on this ;-) On Sat, Sep 19, 2020 at 10:56 AM Eliot Miranda wrote: > Hi Stef, > > On Sat, Sep 19, 2020 at 1:17 AM Stéphane Rollandin > wrote: > >> > if you play the example you’ll see that the first version works >> fine. The second version, which adds MixedSound yo create stereo doesn’t. >> That proves that the issue is in MixedSound. Please just try the two >> examples. >> >> Right. What I can see If I do >> >> | samples sineTable sound | >> "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" >> sineTable := SoundPlayer sineTable: 73. >> sineTable doWithIndex: >> [:sample :index| sineTable at: index put: sample // 4]. >> samples := SoundBuffer new: 16000. >> 1 to: samples size by: sineTable size do: >> [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: >> 16000) >> with: sineTable startingAt: 1]. >> 1 to: 146 do: >> [:i| >> samples at: i put: ((samples at: i) * i / 146) asInteger. >> samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) >> asInteger]. >> sound := SampledSound samples: samples samplingRate: 16000. >> sound := MixedSound new >> add: sound pan: 0.25; >> add: sound pan: 0.75; >> yourself. >> (SampledSound samples: sound monoSamples samplingRate: sound >> originalSamplingRate) edit >> >> in the muO image I pointed to in my previous message, is a phasing >> problem leading to periodic glitches (the attached image is directly >> taken from the muO editor) >> > > [image: image.png] > > Interesting!! I don't see this in my data, but I do see evidence of an > off-by-one error in misdown. Here's the start, first zero crossing, and > end of the mono sound (the original samples sound buffer) > > { samples first: 8. samples copyFrom: 70 to: 77. samples last: 8 } > {a SoundBuffer(4 19 42 75 117 166 222 285) . > a SoundBuffer(-1003 -682 -347 0 356 720 1088 1457) . > a SoundBuffer(221 222 213 196 170 136 96 50)} > > So these look fine and smooth. > > But here's the corresponding data from the computeSamplesForSeconds: > result of the MixedSound: > { self first: 16. self copyFrom: 70 * 2 to: 77 * 2 + 1. self last: 16 } > {a SoundBuffer(4 4 4 4 18 18 41 41 41 41 74 74 116 116 165 165) . > a SoundBuffer(-2712 -2712 -2712 -2836 -2836 -2940 -2940 -2940 -2940 > -3022 -3022 -3084 -3084 -3124 -3124 -3214) . > a SoundBuffer(212 212 212 212 196 196 169 169 169 169 136 136 96 96 49 > 49)} > > The data *should* start with a SoundBuffer(4 4 18 18... > The data ends well. But the first crossing is completely wrong. > > Strange, because it sounds ok... > > Let me count the crossings using: > | sign crossings | > crossings := 0. > sign := self first sign. > self do: > [:each| > each sign = sign negated ifTrue: > [crossings := crossings + 1. > sign := each sign]]. > crossings > > Both give 438 as expected. Strange. Why is there no crossing between 140 > and 154 in the mixed down sound? > > > Ah, ok, the sample rate has changed: > > > | sign crossings ncrossings | > ncrossings := 0. > crossings := OrderedCollection new. > sign := self first sign. > self doWithIndex: > [:each :idx| > each sign = sign negated ifTrue: > [crossings add: idx - 1 -> idx. > ncrossings := ncrossings + 1. > sign := each sign]]. > { crossings first: 4. ncrossings } {an OrderedCollection(100->101 202->203 > 302->303 404->405) . 438} > > > So everything is fine *except for* the extra sample at the beginning. > That's clearly an error, right? > > I'm looking at this.. >> >> Stef >> > > _,,,^..^,,,_ > best, Eliot > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 19346 bytes Desc: not available URL: From eliot.miranda at gmail.com Sat Sep 19 18:53:00 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 11:53:00 -0700 Subject: [squeak-dev] whitewash, sound documentation Message-ID: Hi All, if anyone is looking for a documentation project, most of the classes in system category Sound-Synthesis are lacking class comments. It would be really nice to have good quality class comments, including specifications of class variables here. _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 18:57:09 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 18:57:09 +0000 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: References: Message-ID: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> Hi Eliot, very nice finding once again! I #timeProfile'd the menu button action and as I expected, the most expensive operation is the shout styling by the new inspectors, including the decompilation of every method: [cid:1fdde244-d227-4d8d-9bfb-6b7176b7bc6f] First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but probably that's rather a problem with my sources file, see the thread about this commit). Second, we do not redraw the world while running to the selection, so we do not need to update the inspectors at all. I think we could split up #doStep into some #basicDoStep (which would perform the actual stepping logic) + some #updateContextDuring: (which would update the stack list and the inspectors), then we would need to trigger the updates only once from #runToSelection:. As an alternative, we could make this method a bit more complex but responsive by applying the same updating logic from #runUntil. What do you think? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Samstag, 19. September 2020 20:17:12 An: The general-purpose Squeak developers list; Taeumel, Marcel Betreff: [squeak-dev] Can we make computing the local variables in the debugger lazy? Hi Marcel, can we try and reduce the frequency at which we compute the variables in the context inspector in the debugger? It is a noticeable performance hit. I really like the user interface, but the performance hit is making debugging difficult. As an example use this: | samples sineTable sound | "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" sineTable := SoundPlayer sineTable: 73. sineTable doWithIndex: "And let's not deafen anyone..." [:sample :index| sineTable at: index put: sample // 4]. samples := SoundBuffer new: 16000. 1 to: samples size by: sineTable size do: [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. 1 to: 146 do: [:i| samples at: i put: ((samples at: i) * i / 146) asInteger. samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. sound := SampledSound samples: samples samplingRate: 16000. sound := MixedSound new add: sound pan: 0.25; add: sound pan: 0.75; yourself. sound computeSamplesForSeconds: sound duration Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an image containing Tools-mt.942). Debug the above in the workspace. Position the cursor at "sound computeSamplesForSeconds: sound duration" and do "run to here". It is essentially instantaneous. Now do the same in a contemporary trunk image. It takes almost 6 seconds. I used to be able to click step as fast as I could and the system would keep up with me. Now I find that if I click too fast I can accumulate excess clicks and when I stp clicking the system will continue stepping and go too far. I don't want to lose the feedback the new variables list gives us. But I do find the performance hit tedious. I wonder could we cache the list and only update it - when Morphic updates, and - when the context changes? _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 399308 bytes Desc: pastedImage.png URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 19:04:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 19:04:19 +0000 Subject: [squeak-dev] Drop shadows during grabbing Message-ID: <840b04d3e6334b40a967906bd5513b05@student.hpi.uni-potsdam.de> Hi all, I wonder whether we should turn off drop shadows during grabbing a morph? [cid:2b0e9a8e-fc25-455b-9db5-3187ed1fe59f] And it looks even more strange when grabbing menu items (citing from Squeak by Example): [cid:258a12de-bd97-46fe-ac1d-7471c6e04160] This is related to the #menuAppearance3d preference, but I never met a physics system were shadows can throw their own shadows 🤔 Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 6693 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 61116 bytes Desc: pastedImage.png URL: From eliot.miranda at gmail.com Sat Sep 19 19:20:28 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 12:20:28 -0700 Subject: [squeak-dev] find class is broken... In-Reply-To: <1600523967143-0.post@n4.nabble.com> References: <1600523967143-0.post@n4.nabble.com> Message-ID: On Sat, Sep 19, 2020 at 6:59 AM Christoph Thiede < christoph.thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, > > if you see the Find Method dialog, I think you do not have focused the > system category pane. Did you make a click in it before (or hover it, > provided that #mouseOverToKeyboardFocus is enabled)? I'm using the Find > Class dialog everyday and it always works fine > I am hovering over the system category window of course. (Selecting isn't an option; it changes the selection). There is definitely a bug. This is new behaviour. I have the same settings I've had for several years and hovering in the wsyste, categories window and hitting command F always brought up the FInd Class dialog. Now it doesn't. _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 19:35:48 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 19:35:48 +0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1641.mcz In-Reply-To: References: <,> , Message-ID: Hi Marcel, hi all, What's the strategy here? Which events do you want to catch? I think that only mouse-over (incl. enter/leave) events are problematic. For a generic solution, benchmarks are mandatory. Where are we standing at that front? I think the big question is how generic we would like to be at this point. Beside mouse over/enter/leave events, dragMove events could be a problem, too (though we are planning not to expose them to any morph). Mouse wheel events, too, maybe, especially when they originally come from a touchpad or touch screen input. What is about window events? #windowMetricChange and #windowPaint can be signaled with a high frequency, and when a host system such as Windows is mad again, #windowActivated, too. So in my inbox proposal, I covered all kinds of events and overwrote a #isHighFrequentEvent getter for certain event classes that are known not to be recorded very high-frequently (so basically, an allow list instead of a ban list). I have been testing these changes for a few months in my image now, and while I did not experience any further debugger chains, there was a quite small number of sporadic false positives, for instance, after switching a project, but unfortunately, I did not take notes about them and probably there is a relationship to any other change in my working copy. :-( In my personal doIt list, I have added the two following lines for quickly fixing the problem: World allMorphsDo: #resumeAfterEventError. World allMorphsDo: #resumeAfterDrawError. However, I'm still wondering about a frequency-based debugger detection that could record the last let's say 5 debugger invocation times and if they have been too close, another warning could be displayed. This approach would be less technical but better aligned with the actual purpose of this proposal, which is to prevent a long chain of debuggers overflowing the user. Here is a short, hacked implementation of such a feature for the StandardToolSet: initialize + RecentDebuggersMutex := Semaphore forMutualExclusion. + RecentDebuggers := OrderedCollection new. + 5 timesRepeat: [RecentDebuggers addFirst: DateAndTime new]. ToolSet register: self. debugProcess: aProcess context: aContext label: aString contents: contents fullView: aBool + | now | (aProcess isTerminated and: [aString beginsWith: 'Debug it']) ifTrue: [ ^ Project uiManager inform: 'Nothing to debug. Process has terminated.\Expression optimized.' withCRs translated]. + now := DateAndTime now. + (RecentDebuggersMutex critical: [RecentDebuggers notEmpty ifTrue: [RecentDebuggers removeLast]]) ifNotNil: [:time | + now - time < 1 second ifTrue: [ + DebuggerOverflow + ifNil: [ + [DebuggerOverflow := Semaphore new. + (Project uiManager confirm: 'A lot of debuggers has been detected in recent past. Would you like to suppress further debuggers?' translated) + ifTrue: [self suppressDebuggers]] + ensure: [ + DebuggerOverflow signal. + DebuggerOverflow := nil]] + ifNotNil: [:sem | + sem wait] + ]]. + RecentDebuggersMutex critical: [RecentDebuggers addFirst: now]. + self flag: #todo. "'Mutex' is not always signaled correctly" + + (self shouldSuppressDebuggers ==> [Project uiManager + confirm: ('This will open a Debugger:\\{1}\{2}\{3}\\Proceed?' withCRs translated format: { + aProcess. aContext. contents }) + title: aString]) + ifFalse: [aProcess terminate. ^ self]. ^ Debugger openOn: aProcess context: aContext label: aString contents: contents fullView: aBool This has saved my image two or three times, too, but on the contrary, the quality of such a heuristic may be questionary ... Looking forward to your opinions! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 8. Juni 2020 12:07 Uhr An: squeak-dev Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1641.mcz > Everything: http://forum.world.st/template/NamlServlet.jtp?macro=search_page&node=45488&query=doSafely&sort=date ;-) Are those just pointers to your commit messages? Best, Marcel Am 08.06.2020 12:01:54 schrieb Thiede, Christoph : Hi Marcel, thank you! :-) Discussion: http://forum.world.st/bug-in-a-ToolBuilder-Squeak5-3rc2-td5112536.html#a5112551 Inbox: Morphic-ct.1636, Morphic-ct.1638, Morphic-ct.1641 Everything: http://forum.world.st/template/NamlServlet.jtp?macro=search_page&node=45488&query=doSafely&sort=date ;-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 8. Juni 2020 11:52:32 An: squeak-dev Betreff: Re: [squeak-dev] The Inbox: Morphic-ct.1641.mcz Hi Christoph, can you point me to the prior inbox artifacts and list discussions/comments about this "safe event processing"? I would really like to take a look at it. Having some safety net for broken #mouseOver: handlers similar to #displayWorldSafely: would really help debugging. Best, Marcel Am 07.06.2020 14:46:14 schrieb commits at source.squeak.org : Christoph Thiede uploaded a new version of Morphic to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1641.mcz ==================== Summary ==================== Name: Morphic-ct.1641 Author: ct Time: 7 June 2020, 2:45:17.536284 pm UUID: b90e23e1-ce70-3d49-a8e4-f09af148ef97 Ancestors: Morphic-ct.1638 Refine #isHighFrequentEvent implementation for further event types. Fixes one possible cause of the "color depth = 0" bug. See http://forum.world.st/Image-not-startable-after-save-td5117084.html. Depends indeed on Morphic-ct.1638. =============== Diff against Morphic-ct.1638 =============== Item was added: + ----- Method: DropEvent>>isHighFrequentEvent (in category 'testing') ----- + isHighFrequentEvent + + ^ false! Item was added: + ----- Method: WindowEvent>>isHighFrequentEvent (in category 'testing') ----- + isHighFrequentEvent + + ^ (#(windowClose) includes: self type) not! Item was changed: ----- Method: WorldState>>doSafely:onErrorThat:setErrorFlag:ifFatal:afterErrorDo: (in category 'update cycle') ----- doSafely: aBlock onErrorThat: errorPredicate setErrorFlag: errorFlag ifFatal: fatalErrorBlock afterErrorDo: postErrorBlock "Evaluate aBlock and keep track of errors during morph invocations." | finished classesWithErrors | finished := false. classesWithErrors := IdentitySet new. [finished] whileFalse: [ [aBlock value. finished := true] on: Error, Halt, Warning do: [:ex | | err rcvr errCtxt errMorph | (errorPredicate cull: ex) ifFalse: [ex pass]. err := ex description. rcvr := ex receiver. errCtxt := thisContext. [ errCtxt := errCtxt sender. "Search the sender chain to find the morph causing the problem" [errCtxt notNil and: [(errCtxt receiver isMorph) not]] whileTrue: [errCtxt := errCtxt sender]. "If we're at the root of the context chain then we have a fatal problem" errCtxt ifNil: [^ fatalErrorBlock cull: err]. + errMorph := errCtxt receiver + ] doWhileTrue: [ - errMorph := errCtxt receiver. "If the morph causing the problem has already the error flag set, then search for the next morph above in the caller chain." errMorph hasProperty: errorFlag + ]. - ] whileTrue. errMorph setProperty: errorFlag toValue: true; changed. "Catch all errors, one for each receiver class." (classesWithErrors includes: rcvr class) ifFalse: [ classesWithErrors add: rcvr class. ToolSet debugException: ex]. postErrorBlock cull: err. ]].! Item was changed: ----- Method: WorldState>>processEventsSafely: (in category 'update cycle') ----- processEventsSafely: aHandMorph ^ self doSafely: [aHandMorph processEvents] + onErrorThat: [:error | self currentEvent ifNil: [true] ifNotNil: [:evt | evt isHighFrequentEvent]] - onErrorThat: [:error | ActiveEvent isNil or: [ActiveEvent isHighFrequentEvent]] setErrorFlag: #errorOnEvent ifFatal: [:error | Project current fatalEventHandlingError: error] afterErrorDo: []! -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 19 19:36:57 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 12:36:57 -0700 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> References: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Sat, Sep 19, 2020 at 11:57 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, > > > very nice finding once again! I #timeProfile'd the menu button action and > as I expected, the most expensive operation is the shout styling by the new > inspectors, including the decompilation of every method: > > > > > > First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but > probably that's rather a problem with my sources file, see the thread about > this commit). > > Second, we do not redraw the world while running to the selection, so we > do not need to update the inspectors at all. I think we could split up > #doStep into some #basicDoStep (which would perform the actual stepping > logic) + some #updateContextDuring: (which would update the stack list and > the inspectors), then we would need to trigger the updates only once from > #runToSelection:. > As an alternative, we could make this method a bit more complex but > responsive by applying the same updating logic from #runUntil. > Nice. What ever is TSTTCPW :-). But it strikes me that one way is to use addDeferredUIMessage:. The only tricky thing is not creating lots of these unnecessarily. But that could be done by checking if the deferred action queue is empty or not. E.g. rewrite this in doStep: self contextStackIndex > 1 ifTrue: [self resetContext: newContext] ifFalse: [newContext == currentContext ifTrue: [self changed: #contentsSelection. self updateInspectors] ifFalse: [self resetContext: newContext]]. as, say, self contextStackIndex > 1 ifTrue: [self resetContext: newContext] ifFalse: [newContext == currentContext ifTrue: [self scheduleUIUpdate] ifFalse: [self resetContext: newContext]]. add an inst var to hold the last deferred action, and then do something like Debugger>>scheduleUIUpdate (lastScheduledUpdate notNil and: [WorldState lastDeferredUIMessage == lastScheduledUpdate]) ifTrue: [^self]. lastScheduledUpdate := [self changed: #contentsSelection. self updateInspectors]. WorldState addDeferredUIMessage: lastScheduledUpdate For this we have to add the lastDeferredUIMessage accessor to all the relevant places, but it seems a nice pattern to me. And of course, there is no need to refresh lastScheduledUpdate. It could be Debugger>>scheduleUIUpdate lastScheduledUpdate ifNil: [lastScheduledUpdate := [self changed: #contentsSelection. self updateInspectors]]. WorldState lastDeferredUIMessage ~~ lastScheduledUpdate ifTrue: [WorldState addDeferredUIMessage: lastScheduledUpdate] What do you think? :-) > What do you like? > Best, > Christoph > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Eliot Miranda > *Gesendet:* Samstag, 19. September 2020 20:17:12 > *An:* The general-purpose Squeak developers list; Taeumel, Marcel > *Betreff:* [squeak-dev] Can we make computing the local variables in the > debugger lazy? > > Hi Marcel, > > can we try and reduce the frequency at which we compute the variables > in the context inspector in the debugger? It is a noticeable > performance hit. I really like the user interface, but the performance hit > is making debugging difficult. > > As an example use this: > > | samples sineTable sound | > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > sineTable := SoundPlayer sineTable: 73. > sineTable doWithIndex: "And let's not deafen anyone..." > [:sample :index| sineTable at: index put: sample // 4]. > samples := SoundBuffer new: 16000. > 1 to: samples size by: sineTable size do: > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: > sineTable startingAt: 1]. > 1 to: 146 do: > [:i| > samples at: i put: ((samples at: i) * i / 146) asInteger. > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. > sound := SampledSound > samples: samples > samplingRate: 16000. > sound := MixedSound new > add: sound pan: 0.25; > add: sound pan: 0.75; > yourself. > sound computeSamplesForSeconds: sound duration > > > Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an > image containing Tools-mt.942). Debug the above in the workspace. > Position the cursor at "sound computeSamplesForSeconds: sound duration" > and do "run to here". It is essentially instantaneous. > > Now do the same in a contemporary trunk image. It takes almost 6 seconds. > > I used to be able to click step as fast as I could and the system would > keep up with me. Now I find that if I click too fast I can accumulate > excess clicks and when I stp clicking the system will continue stepping and > go too far. > > I don't want to lose the feedback the new variables list gives us. But I > do find the performance hit tedious. I wonder could we cache the list and > only update it > - when Morphic updates, and > - when the context changes? > > > _,,,^..^,,,_ > best, Eliot > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 399308 bytes Desc: not available URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 19:40:41 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 19:40:41 +0000 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com>, Message-ID: Can you reproduce this in a fresh trunk image? Which preferences do you have changed? I cannot reproduce this; the only related experience I am aware of is when you use a hierarchy browser instead of a normal one. I got a few times irritated when hovering the left pane as usual and pressing f, ignoring that a hierarchy browser has no system category pane ... But that's probably not what you are describing. :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Samstag, 19. September 2020 21:20:28 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] find class is broken... On Sat, Sep 19, 2020 at 6:59 AM Christoph Thiede > wrote: Hi Eliot, if you see the Find Method dialog, I think you do not have focused the system category pane. Did you make a click in it before (or hover it, provided that #mouseOverToKeyboardFocus is enabled)? I'm using the Find Class dialog everyday and it always works fine I am hovering over the system category window of course. (Selecting isn't an option; it changes the selection). There is definitely a bug. This is new behaviour. I have the same settings I've had for several years and hovering in the wsyste, categories window and hitting command F always brought up the FInd Class dialog. Now it doesn't. _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sat Sep 19 19:41:46 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 19:41:46 0000 Subject: [squeak-dev] The Inbox: EToys-kfr.392.mcz Message-ID: A new version of EToys was added to project The Inbox: http://source.squeak.org/inbox/EToys-kfr.392.mcz ==================== Summary ==================== Name: EToys-kfr.392 Author: kfr Time: 19 September 2020, 9:41:26.748531 pm UUID: 0cba87d8-dec4-304e-be2d-dc783945f18d Ancestors: EToys-kfr.378, EToys-mt.391 add most used Etoy icons to ScriptingSystem formDictionary =============== Diff against EToys-kfr.378 =============== Item was added: + ----- Method: ReleaseBuilderSqueakland class>>loadMostUsedEtoysForms (in category 'preparing') ----- + loadMostUsedEtoysForms + "add must used Etoy icons to ScriptingSystem formDictionary" + "RoundGoldBox, TryItPressed, TryIt, MenuIcon, AddCategoryViewer, AddInstanceVariable, TanOPressed, TanO, Gets, RightCaret, DownCaret" + | assoc | + {#scriptingSystemImage109. #scriptingSystemImage127. #scriptingSystemImage064. #scriptingSystemImage038. #scriptingSystemImage165. #scriptingSystemImage171. #scriptingSystemImage014. #scriptingSystemImage053. #scriptingSystemImage080. #scriptingSystemImage044.} + do: [:sym | + assoc := (self perform: sym). + ScriptingSystem saveForm: assoc value atKey: assoc key].! From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 19:43:16 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 19:43:16 +0000 Subject: [squeak-dev] The Inbox: EToys-kfr.392.mcz In-Reply-To: References: Message-ID: <0f9e25f00c3a47389aaa1feb16243729@student.hpi.uni-potsdam.de> Great, now we only have to call this method from anywhere within the ReleaseBuilder, without explicitly referencing the Etoys class :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Samstag, 19. September 2020 21:41:46 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: EToys-kfr.392.mcz A new version of EToys was added to project The Inbox: http://source.squeak.org/inbox/EToys-kfr.392.mcz ==================== Summary ==================== Name: EToys-kfr.392 Author: kfr Time: 19 September 2020, 9:41:26.748531 pm UUID: 0cba87d8-dec4-304e-be2d-dc783945f18d Ancestors: EToys-kfr.378, EToys-mt.391 add most used Etoy icons to ScriptingSystem formDictionary =============== Diff against EToys-kfr.378 =============== Item was added: + ----- Method: ReleaseBuilderSqueakland class>>loadMostUsedEtoysForms (in category 'preparing') ----- + loadMostUsedEtoysForms + "add must used Etoy icons to ScriptingSystem formDictionary" + "RoundGoldBox, TryItPressed, TryIt, MenuIcon, AddCategoryViewer, AddInstanceVariable, TanOPressed, TanO, Gets, RightCaret, DownCaret" + | assoc | + {#scriptingSystemImage109. #scriptingSystemImage127. #scriptingSystemImage064. #scriptingSystemImage038. #scriptingSystemImage165. #scriptingSystemImage171. #scriptingSystemImage014. #scriptingSystemImage053. #scriptingSystemImage080. #scriptingSystemImage044.} + do: [:sym | + assoc := (self perform: sym). + ScriptingSystem saveForm: assoc value atKey: assoc key].! -------------- next part -------------- An HTML attachment was scrubbed... URL: From ma.chris.m at gmail.com Sat Sep 19 19:59:28 2020 From: ma.chris.m at gmail.com (Chris Muller) Date: Sat, 19 Sep 2020 14:59:28 -0500 Subject: [squeak-dev] The Inbox: Monticello-ct.728.mcz In-Reply-To: <940c2ba54df340d08601d7a2b19cd982@student.hpi.uni-potsdam.de> References: <940c2ba54df340d08601d7a2b19cd982@student.hpi.uni-potsdam.de> Message-ID: I must admit, I don't quite grasp how your change affected the IDE but... just as a matter of philosophy, it's object-centric approach is what attracts me to Smalltalk and it's IDE. I always want to start with an object, and THEN send a message to it. But command-driven UI's, like this "Changes against...", are about, "WHAT do I want to 'do'", and THEN select the object to do it on, often via a series of modal pop ups, which requires the user to have all the information gathered, else must abort. It's two rather opposite paradigms that, together, IMO, manifest some dissonance within the UI. It might be refined to choose a philosophy for the IDE and stick with it. OTOH, Squeak's ability to appeal to so many different tastes can also be regarded as attractive and an impressive accomplishment. - Chris On Sat, Sep 19, 2020 at 6:39 AM Thiede, Christoph wrote: > > Hi Chris, > > > thanks for the hint to the history browser! Still, this is some clicks further away. How do you think about offering all ancestor versions directly into the "changes against" menu as proposed below? > > > Best, > > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von Chris Muller > Gesendet: Freitag, 18. September 2020 06:42:38 > An: squeak dev > Betreff: Re: [squeak-dev] The Inbox: Monticello-ct.728.mcz > > Hi Christoph, > > +1 for the multilingual change. > > However, the UI does already support the ability to diff between _any_ two versions via the existing browsers. Simply opening the History browser on the descendant, and then select whichever ancestor you wish to compare to. > > Best, > Chris > > On Thu, Sep 17, 2020 at 6:47 AM wrote: >> >> A new version of Monticello was added to project The Inbox: >> http://source.squeak.org/inbox/Monticello-ct.728.mcz >> >> ==================== Summary ==================== >> >> Name: Monticello-ct.728 >> Author: ct >> Time: 17 September 2020, 1:47:00.019813 pm >> UUID: 7d44995a-f19b-ee49-b16b-c710c9f2bce6 >> Ancestors: Monticello-cmm.726 >> >> Implements comparing a Monticello version against any of its ancestors. Not absolutely sure about the code quality, but it's a relevant feature for me (until now I always used to consult the Nabble archive). Also improves multilingual support. >> >> =============== Diff against Monticello-cmm.726 =============== >> >> Item was changed: >> ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- >> httpGet: url arguments: arguments >> >> | progress urlString client response result | >> progress := [ :total :amount | >> HTTPProgress new >> total: total; >> amount: amount; >> + signal: 'Downloading...' translated ]. >> - signal: 'Downloading...' ]. >> urlString := arguments >> ifNil: [ url ] >> ifNotNil: [ >> | queryString | >> queryString := WebUtils encodeUrlEncodedForm: arguments. >> (url includes: $?) >> ifTrue: [ url, '&', queryString ] >> ifFalse: [ url, '?', queryString ] ]. >> self class useSharedWebClientInstance ifTrue: [ >> "Acquire webClient by atomically storing it in the client variable and setting its value to nil." >> client := webClient. >> webClient := nil ]. >> client >> ifNil: [ client := WebClient new ] >> ifNotNil: [ >> "Attempt to avoid an error on windows by recreating the underlying stream." >> client isConnected ifFalse: [ client close ] ]. >> response := client >> username: self user; >> password: self password; >> httpGet: urlString do: [ :request | >> request >> headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; >> headerAt: 'Connection' put: 'Keep-Alive'; >> headerAt: 'Accept' put: '*/*' ]. >> result := (response code between: 200 and: 299) >> ifFalse: [ >> response content. "Make sure content is read." >> nil ] >> ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. >> self class useSharedWebClientInstance >> ifTrue: [ >> "Save the WebClient instance for reuse, but only if there is no client cached." >> webClient >> ifNil: [ webClient := client ] >> ifNotNil: [ client close ] ] >> ifFalse: [ client close ]. >> + result ifNil: [ NetworkError signal: ('Could not access {1} (Code {2})' translated format: {location. response code}) ]. >> - result ifNil: [ NetworkError signal: 'Could not access ', location ]. >> ^result! >> >> Item was changed: >> ----- Method: MCRepositoryInspector>>versionListMenu: (in category 'morphic ui') ----- >> versionListMenu: aMenu >> + >> 1 to: self orderSpecs size do: [ :index | >> aMenu addUpdating: #orderString: target: self selector: #order: argumentList: { index } ]. >> aMenu addLine. >> + aMenu add: 'Changes against ...' translated action: [ >> + | ri versions seen | >> - aMenu add: 'Changes against ...' action: [| ri | >> ri := aMenu defaultTarget. >> + versions := ri versionList >> + collect: [:name | MCVersionName on: name] >> + as: OrderedCollection. >> + seen := versions asSet. >> + self version info breadthFirstAncestors do: [:ancestor | >> + (seen includes: ancestor name) ifFalse: [ >> + versions add: ancestor. >> + seen add: ancestor name]]. >> (UIManager default >> + chooseFrom: (versions collect: [:version | version name]) >> + values: versions >> + title: 'Select version to show patch against ...' translated) ifNotNil: [:name | >> + | target base | >> - chooseFrom: ri versionList >> - values: ri versionList >> - title: 'Select version to show patch against ...') ifNotNil: [:name | >> - | versionName target base | >> - versionName := MCVersionName on: name. >> target := ri repository versionNamed: ri versionInfo name. >> + base := name isString >> + ifTrue: [ri repository versionNamed: name] >> + ifFalse: [ri version workingCopy repositoryGroup versionWithInfo: name]. >> - base := aMenu defaultTarget repository versionNamed: versionName. >> (MCPatchBrowser >> forPatch: (target snapshot patchRelativeToBase: base snapshot)) >> + showLabelled: ('Changes from {1} to {2}' translated format: {name. ri versionInfo name})]]. >> - showLabelled: 'Changes from ', versionName, ' to ', ri versionInfo name]]. >> ^aMenu! >> >> Item was added: >> + ----- Method: MCVersionName>>name (in category 'as yet unclassified') ----- >> + name >> + >> + ^ self! >> >> From commits at source.squeak.org Sat Sep 19 20:10:22 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:10:22 0000 Subject: [squeak-dev] The Trunk: Collections-eem.912.mcz Message-ID: Eliot Miranda uploaded a new version of Collections to project The Trunk: http://source.squeak.org/trunk/Collections-eem.912.mcz ==================== Summary ==================== Name: Collections-eem.912 Author: eem Time: 19 September 2020, 1:10:19.845777 pm UUID: 04ad0d49-19ab-4e40-8570-e63c00cc49ca Ancestors: Collections-eem.911 Add peekLast to the SharedQueues to complement peek =============== Diff against Collections-eem.911 =============== Item was added: + ----- Method: SharedQueue>>peekLast (in category 'accessing') ----- + peekLast + "Answer the object that was sent through the receiver last and has not + yet been received by anyone. If no object has been sent, answer nil" + + "SharedQueue new nextPut: 1; nextPut: 2; peekLast" + + ^readSynch + critical: + [accessProtect critical: + [writePosition > 1 ifTrue: + [contentsArray at: writePosition - 1]]] + ifLocked: [nil]! Item was added: + ----- Method: SharedQueue2>>peekLast (in category 'accessing') ----- + peekLast + "Answer the object that was sent through the receiver last and has not + yet been received by anyone. If no object has been sent, answer nil" + + "SharedQueue2 new nextPut: 1; nextPut: 2; peekLast" + ^monitor critical: + [items isEmpty ifFalse: + [items last]]! From commits at source.squeak.org Sat Sep 19 20:11:17 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:11:17 0000 Subject: [squeak-dev] The Trunk: CollectionsTests-eem.342.mcz Message-ID: Eliot Miranda uploaded a new version of CollectionsTests to project The Trunk: http://source.squeak.org/trunk/CollectionsTests-eem.342.mcz ==================== Summary ==================== Name: CollectionsTests-eem.342 Author: eem Time: 19 September 2020, 1:11:15.902536 pm UUID: ba823c3b-42f7-4276-8a25-8c59d26a3346 Ancestors: CollectionsTests-ul.341 Refator SHaredQueue2Test so that now we also test SharedQueue. Add a test for peek and peekLast =============== Diff against CollectionsTests-ul.341 =============== Item was added: + TestCase subclass: #AbstractSharedQueueTest + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Sequenceable'! Item was added: + ----- Method: AbstractSharedQueueTest class>>isAbstract (in category 'testing') ----- + isAbstract + ^self class == thisContext methodClass! Item was added: + ----- Method: AbstractSharedQueueTest>>queueClass (in category 'private') ----- + queueClass + ^self subclassResponsibility! Item was added: + ----- Method: AbstractSharedQueueTest>>testBasics (in category 'tests') ----- + testBasics + | q | + q := self queueClass new. + + self assert: nil equals: q nextOrNil. + + q nextPut: 5. + self assert: 5 equals: q nextOrNil. + self assert: nil equals: q nextOrNil! Item was added: + ----- Method: AbstractSharedQueueTest>>testContention1 (in category 'tests') ----- + testContention1 + "here is a test case that breaks the standard SharedQueue from Squeak 3.8" + + | q r1 r2 | + q := self queueClass new. + q nextPut: 5. + q nextPut: 10. + + self assert: 5 equals: q nextOrNil. + + [ r1 := q next ] fork. + [ r2 := q next ] fork. + Processor yield. "let the above two threads block" + + q nextPut: 10. + Processor yield. + + self assert: 10 equals: r1. + self assert: 10 equals: r2. + self assert: nil equals: q nextOrNil! Item was added: + ----- Method: AbstractSharedQueueTest>>testNextOrNilSuchThat (in category 'tests') ----- + testNextOrNilSuchThat + | q item | + q := self queueClass new. + q nextPut: 5. + q nextPut: 6. + + item := q nextOrNilSuchThat: [ :x | x even ]. + self assert: 6 equals: item. + + self assert: 5 equals: q nextOrNil. + self assert: nil equals: q nextOrNil! Item was added: + ----- Method: AbstractSharedQueueTest>>testPeeks (in category 'tests') ----- + testPeeks + | q | + q := self queueClass new. + + self assert: nil equals: q peek. + self assert: nil equals: q peekLast. + + q nextPut: #first; nextPut: #last. + + self assert: #first equals: q peek. + self assert: #last equals: q peekLast. + + self assert: #first equals: q next. + + self assert: #last equals: q peek. + self assert: #last equals: q peekLast! Item was changed: + AbstractSharedQueueTest subclass: #SharedQueue2Test - TestCase subclass: #SharedQueue2Test instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'CollectionsTests-Sequenceable'! Item was added: + ----- Method: SharedQueue2Test>>queueClass (in category 'private') ----- + queueClass + ^SharedQueue2! Item was removed: - ----- Method: SharedQueue2Test>>testBasics (in category 'tests') ----- - testBasics - | q | - q := SharedQueue2 new. - - self should: [ q nextOrNil = nil ]. - - q nextPut: 5. - self should: [ q nextOrNil = 5 ]. - self should: [ q nextOrNil = nil ]. - - ! Item was removed: - ----- Method: SharedQueue2Test>>testContention1 (in category 'tests') ----- - testContention1 - "here is a test case that breaks the standard SharedQueue from Squeak 3.8" - - | q r1 r2 | - q := SharedQueue2 new. - q nextPut: 5. - q nextPut: 10. - - self should: [ q nextOrNil = 5 ]. - - [ r1 := q next ] fork. - [ r2 := q next ] fork. - Processor yield. "let the above two threads block" - - q nextPut: 10. - Processor yield. - - self should: [ r1 = 10 ]. - self should: [ r2 = 10 ]. - self should: [ q nextOrNil = nil ]. - ! Item was removed: - ----- Method: SharedQueue2Test>>testNextOrNilSuchThat (in category 'tests') ----- - testNextOrNilSuchThat - | q item | - q := SharedQueue2 new. - q nextPut: 5. - q nextPut: 6. - - item := q nextOrNilSuchThat: [ :x | x even ]. - self should: [ item = 6 ]. - - self should: [ q nextOrNil = 5 ]. - self should: [ q nextOrNil = nil ]. - ! Item was added: + AbstractSharedQueueTest subclass: #SharedQueueTest + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Sequenceable'! Item was added: + ----- Method: SharedQueueTest>>queueClass (in category 'private') ----- + queueClass + ^SharedQueue! From Christoph.Thiede at student.hpi.uni-potsdam.de Sat Sep 19 20:17:25 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Sat, 19 Sep 2020 20:17:25 +0000 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: References: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de>, Message-ID: Hi Eliot, these are interesting ideas! :-) Your pattern is cool, it reminds me a little bit of the approach we are using for Shout, see SHTextStyler >> #styleInBackgroundProcess:. It even more reminds me of Collection >> #do:displayingProgress:every:. And last but not least, at a fundamental level, aren't we replicating the usual stepping logic from WorldState here? In your approach, where do you process the scheduled messages? The problem is that we are running the simulation/execution code on the UI process. See this thread, too: http://forum.world.st/Debugging-of-other-processes-with-wait-tp5119965p5120049.html I would not use #lastDeferredUIMessage unless we can rule out that any other client is communicating with the WorldState. Which we, afaik, never can rule out since the idea of the deferred messages is that you can have them scheduled from any background process. It would be cool to develop some reusable logic that is applicable to different domains without expensive adaption. Maybe we should just add some static generic helper method for this? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Samstag, 19. September 2020 21:36:57 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Can we make computing the local variables in the debugger lazy? Hi Christoph, On Sat, Sep 19, 2020 at 11:57 AM Thiede, Christoph > wrote: Hi Eliot, very nice finding once again! I #timeProfile'd the menu button action and as I expected, the most expensive operation is the shout styling by the new inspectors, including the decompilation of every method: [cid:174a7d2f503f456b1e51] First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but probably that's rather a problem with my sources file, see the thread about this commit). Second, we do not redraw the world while running to the selection, so we do not need to update the inspectors at all. I think we could split up #doStep into some #basicDoStep (which would perform the actual stepping logic) + some #updateContextDuring: (which would update the stack list and the inspectors), then we would need to trigger the updates only once from #runToSelection:. As an alternative, we could make this method a bit more complex but responsive by applying the same updating logic from #runUntil. Nice. What ever is TSTTCPW :-). But it strikes me that one way is to use addDeferredUIMessage:. The only tricky thing is not creating lots of these unnecessarily. But that could be done by checking if the deferred action queue is empty or not. E.g. rewrite this in doStep: self contextStackIndex > 1 ifTrue: [self resetContext: newContext] ifFalse: [newContext == currentContext ifTrue: [self changed: #contentsSelection. self updateInspectors] ifFalse: [self resetContext: newContext]]. as, say, self contextStackIndex > 1 ifTrue: [self resetContext: newContext] ifFalse: [newContext == currentContext ifTrue: [self scheduleUIUpdate] ifFalse: [self resetContext: newContext]]. add an inst var to hold the last deferred action, and then do something like Debugger>>scheduleUIUpdate (lastScheduledUpdate notNil and: [WorldState lastDeferredUIMessage == lastScheduledUpdate]) ifTrue: [^self]. lastScheduledUpdate := [self changed: #contentsSelection. self updateInspectors]. WorldState addDeferredUIMessage: lastScheduledUpdate For this we have to add the lastDeferredUIMessage accessor to all the relevant places, but it seems a nice pattern to me. And of course, there is no need to refresh lastScheduledUpdate. It could be Debugger>>scheduleUIUpdate lastScheduledUpdate ifNil: [lastScheduledUpdate := [self changed: #contentsSelection. self updateInspectors]]. WorldState lastDeferredUIMessage ~~ lastScheduledUpdate ifTrue: [WorldState addDeferredUIMessage: lastScheduledUpdate] What do you think? :-) What do you like? Best, Christoph ________________________________ Von: Squeak-dev > im Auftrag von Eliot Miranda > Gesendet: Samstag, 19. September 2020 20:17:12 An: The general-purpose Squeak developers list; Taeumel, Marcel Betreff: [squeak-dev] Can we make computing the local variables in the debugger lazy? Hi Marcel, can we try and reduce the frequency at which we compute the variables in the context inspector in the debugger? It is a noticeable performance hit. I really like the user interface, but the performance hit is making debugging difficult. As an example use this: | samples sineTable sound | "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" sineTable := SoundPlayer sineTable: 73. sineTable doWithIndex: "And let's not deafen anyone..." [:sample :index| sineTable at: index put: sample // 4]. samples := SoundBuffer new: 16000. 1 to: samples size by: sineTable size do: [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. 1 to: 146 do: [:i| samples at: i put: ((samples at: i) * i / 146) asInteger. samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. sound := SampledSound samples: samples samplingRate: 16000. sound := MixedSound new add: sound pan: 0.25; add: sound pan: 0.75; yourself. sound computeSamplesForSeconds: sound duration Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an image containing Tools-mt.942). Debug the above in the workspace. Position the cursor at "sound computeSamplesForSeconds: sound duration" and do "run to here". It is essentially instantaneous. Now do the same in a contemporary trunk image. It takes almost 6 seconds. I used to be able to click step as fast as I could and the system would keep up with me. Now I find that if I click too fast I can accumulate excess clicks and when I stp clicking the system will continue stepping and go too far. I don't want to lose the feedback the new variables list gives us. But I do find the performance hit tedious. I wonder could we cache the list and only update it - when Morphic updates, and - when the context changes? _,,,^..^,,,_ best, Eliot -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 399308 bytes Desc: pastedImage.png URL: From commits at source.squeak.org Sat Sep 19 20:24:55 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:24:55 0000 Subject: [squeak-dev] The Inbox: MorphicExtras-kfr.277.mcz Message-ID: A new version of MorphicExtras was added to project The Inbox: http://source.squeak.org/inbox/MorphicExtras-kfr.277.mcz ==================== Summary ==================== Name: MorphicExtras-kfr.277 Author: kfr Time: 19 September 2020, 10:24:34.566601 pm UUID: d39cb295-7978-d240-889f-6f5388354762 Ancestors: MorphicExtras-kfr.276 Width 3000 was a little excessive for some images =============== Diff against MorphicExtras-kfr.276 =============== Item was changed: ----- Method: GraphicalDictionaryMenu>>initializeFor:fromDictionary: (in category 'initialization') ----- initializeFor: aTarget fromDictionary: aDictionary "Initialize me for a target and a dictionary." | anIndex aButton | self baseDictionary: aDictionary. target := aTarget. coexistWithOriginal := true. self extent: 210 @ 210. self clipSubmorphs: true. self layoutPolicy: ProportionalLayout new. aButton := (IconicButton new) borderWidth: 0; labelGraphic: (ScriptingSystem formAtKey: 'TinyMenu'); color: Color transparent; actWhen: #buttonDown; actionSelector: #showMenu; target: self; setBalloonText: 'menu'. self addMorph: aButton fullFrame: (LayoutFrame fractions: (0.5 @ 0 extent: 0 @ 0) offsets: (-50 @ 6 extent: aButton extent)). aButton := (SimpleButtonMorph new) target: self; borderColor: Color black; label: 'Prev'; actionSelector: #downArrowHit; actWhen: #whilePressed; setBalloonText: 'show previous picture'; yourself. self addMorph: aButton fullFrame: (LayoutFrame fractions: (0.5 @ 0 extent: 0 @ 0) offsets: (-24 @ 4 extent: aButton extent)). aButton := (SimpleButtonMorph new) target: self; borderColor: Color black; label: 'Next'; actionSelector: #upArrowHit; actWhen: #whilePressed; setBalloonText: 'show next pictutre'. self addMorph: aButton fullFrame: (LayoutFrame fractions: (0.5 @ 0 extent: 0 @ 0) offsets: (24 @ 4 extent: aButton extent)). self addMorph: ((UpdatingStringMorph new) contents: ' '; target: self; putSelector: #renameGraphicTo:; getSelector: #truncatedNameOfGraphic; useStringFormat; setBalloonText: 'The name of the current graphic'; yourself) fullFrame: (LayoutFrame fractions: (0 @ 0 extent: 1 @ 0) offsets: (10 @ 40 corner: -10 @ 60)). self addMorph: ((Morph new) extent: 100 @ 4; color: Color black) fullFrame: (LayoutFrame fractions: (0 @ 0 extent: 1 @ 0) offsets: (0 @ 60 corner: 0 @ 64)). formDisplayMorph := (Thumbnail new) extent: 100 @ 100; useInterpolation: true; + maxWidth: 300 minHeight: 100; - maxWidth: 3000 minHeight: 100; yourself. formDisplayMorph layoutFrame: (LayoutFrame fractions: (0 @ 0 extent: 0 at 0) offsets: (8 @ 72 corner: 108 @ 172)). self addMorph: formDisplayMorph. self minimumExtent: 116 at 180. target ifNotNil: [(anIndex := formChoices indexOf: target form ifAbsent: []) ifNotNil: [currentIndex := anIndex]]. self updateThumbnail! From commits at source.squeak.org Sat Sep 19 20:29:18 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:29:18 0000 Subject: [squeak-dev] The Trunk: ST80-eem.260.mcz Message-ID: Eliot Miranda uploaded a new version of ST80 to project The Trunk: http://source.squeak.org/trunk/ST80-eem.260.mcz ==================== Summary ==================== Name: ST80-eem.260 Author: eem Time: 19 September 2020, 1:29:16.901184 pm UUID: 5da36bad-2b6f-4f3f-8cd2-bc339dd22b4f Ancestors: ST80-mt.259 Add an accessor for the last deferred ui message. =============== Diff against ST80-mt.259 =============== Item was added: + ----- Method: ControlManager class>>lastDeferredUIMessage (in category 'class initialization') ----- + lastDeferredUIMessage + "Answer the most recently scheduled deferredUIMessage." + + ^self deferredActionQueue peekLast! Item was added: + ----- Method: MVCProject>>lastDeferredUIMessage (in category 'scheduling & debugging') ----- + lastDeferredUIMessage + "Answer the most recently scheduled deferredUIMessage." + + ^ControlManager lastDeferredUIMessage! From commits at source.squeak.org Sat Sep 19 20:29:57 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:29:57 0000 Subject: [squeak-dev] The Trunk: Morphic-eem.1684.mcz Message-ID: Eliot Miranda uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-eem.1684.mcz ==================== Summary ==================== Name: Morphic-eem.1684 Author: eem Time: 19 September 2020, 1:29:54.075008 pm UUID: 80c69f47-1bc8-4764-9b48-4aa3ff8b40c9 Ancestors: Morphic-mt.1683 Add an accessor for the last deferred ui message. =============== Diff against Morphic-mt.1683 =============== Item was added: + ----- Method: WorldState class>>lastDeferredUIMessage (in category 'class initialization') ----- + lastDeferredUIMessage + "Answer the most recently scheduled deferredUIMessage." + + ^self deferredUIMessages peekLast! From commits at source.squeak.org Sat Sep 19 20:31:55 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:31:55 0000 Subject: [squeak-dev] The Trunk: Morphic-eem.1685.mcz Message-ID: Eliot Miranda uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-eem.1685.mcz ==================== Summary ==================== Name: Morphic-eem.1685 Author: eem Time: 19 September 2020, 1:31:52.065024 pm UUID: 6f1a278a-cc16-4e8a-bf3d-487a98cb4b68 Ancestors: Morphic-eem.1684 Add an accessor for the last deferred ui message. =============== Diff against Morphic-eem.1684 =============== Item was added: + ----- Method: MorphicProject>>lastDeferredUIMessage (in category 'scheduling & debugging') ----- + lastDeferredUIMessage + "Answer the most recently scheduled deferredUIMessage." + + ^WorldState lastDeferredUIMessage! From commits at source.squeak.org Sat Sep 19 20:33:08 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:33:08 0000 Subject: [squeak-dev] The Trunk: System-eem.1173.mcz Message-ID: Eliot Miranda uploaded a new version of System to project The Trunk: http://source.squeak.org/trunk/System-eem.1173.mcz ==================== Summary ==================== Name: System-eem.1173 Author: eem Time: 19 September 2020, 1:33:05.549281 pm UUID: 9d302aad-a895-4450-aa3d-e71424ed4916 Ancestors: System-dtl.1172 Add an accessor for the last deferred ui message. =============== Diff against System-dtl.1172 =============== Item was added: + ----- Method: Project>>lastDeferredUIMessage (in category 'scheduling & debugging') ----- + lastDeferredUIMessage + "Answer the most recently scheduled deferredUIMessage." + + self subclassResponsibility! From lecteur at zogotounga.net Sat Sep 19 20:34:21 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 19 Sep 2020 22:34:21 +0200 Subject: [squeak-dev] ColorForm with transparency not working properly In-Reply-To: <618f98fce9dd44cbb9b603a6eca2e463@student.hpi.uni-potsdam.de> References: <618f98fce9dd44cbb9b603a6eca2e463@student.hpi.uni-potsdam.de> Message-ID: > But unfortunately, I could not find any documentation about the > different Form rules such as #blend, #over, #paint etc. Can you give me > some pointers? Related thread: http://forum.world.st/ImageForm-color-with-alpha-td5090574.html Stef From commits at source.squeak.org Sat Sep 19 20:35:37 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 19 Sep 2020 20:35:37 0000 Subject: [squeak-dev] The Inbox: Tools-eem.986.mcz Message-ID: A new version of Tools was added to project The Inbox: http://source.squeak.org/inbox/Tools-eem.986.mcz ==================== Summary ==================== Name: Tools-eem.986 Author: eem Time: 19 September 2020, 1:35:34.572026 pm UUID: b33a2312-24e5-4cfa-a4e3-7f409ce8f5d4 Ancestors: Tools-ct.985 Use the lastDeferredUIMessage mechanism for avoiding updating the debugger UI too frequently. Alas this only solves the run to here case; it doesn't fix the rapid clicking issue. Hence this is in the inbox. Christoph, this may be useful to you. =============== Diff against Tools-ct.985 =============== Item was changed: CodeHolder subclass: #Debugger + instanceVariableNames: 'interruptedProcess contextStack contextStackIndex contextStackList receiverInspector receiverInspectorState contextVariablesInspector contextVariablesInspectorState externalInterrupt proceedValue selectingPC savedCursor isolationHead failedProject labelString message untilExpression uiUpdateBlock' - instanceVariableNames: 'interruptedProcess contextStack contextStackIndex contextStackList receiverInspector receiverInspectorState contextVariablesInspector contextVariablesInspectorState externalInterrupt proceedValue selectingPC savedCursor isolationHead failedProject labelString message untilExpression' classVariableNames: 'ContextStackKeystrokes ErrorReportServer FullStackSize InterruptUIProcessIfBlockedOnErrorInBackgroundProcess NotifierStackSize SavedExtent StackSizeLimit WantsAnnotationPane' poolDictionaries: '' category: 'Tools-Debugger'! !Debugger commentStamp: 'mt 12/17/2019 12:19' prior: 0! I represent the machine state at the time of an interrupted process. I also represent a query path into the state of the process. The debugger is typically viewed through a window that views the stack of suspended contexts, the code for, and execution point in, the currently selected message, and inspectors on both the receiver of the currently selected message, and the variables in the current context. Special note on recursive errors: Some errors affect Squeak's ability to present a debugger. This is normally an unrecoverable situation. However, if such an error occurs in an isolation layer, Squeak will attempt to exit from the isolation layer and then present a debugger. Here is the chain of events in such a recovery. * A recursive error is detected. * The current project is queried for an isolationHead * Changes in the isolationHead are revoked * The parent project of isolated project is returned to * The debugger is opened there and execution resumes. If the user closes that debugger, execution continues in the outer project and layer. If, after repairing some damage, the user proceeds from the debugger, then the isolationHead is re-invoked, the failed project is re-entered, and execution resumes in that world. --- In September 2019, we added MorphicDebugger and MVCDebugger to untangle framework-specific features in our debugger infrastructure. However, this is just an intermediate step. The overall goal would be to remove those two subclasses again while preserving their functionality. Mostly, MVC and Morphic differ in their GUI-process management. This means that "proceed" and "close" work differently depending on the process that is being debugged. --- One idea is to attach that framework-specific information to the process objects. See Process >> #environmentAt: and #environmentAt:put:. Also see ToolSet's #handle* and #debug* methods.! Item was changed: ----- Method: Debugger>>doStep (in category 'context stack menu') ----- doStep "Send the selected message in the accessed method, and regain control after the invoked method returns." | currentContext newContext | self okToChange ifFalse: [^ self]. self checkContextSelection. currentContext := self selectedContext. newContext := self handleLabelUpdatesIn: [interruptedProcess completeStep: currentContext] whenExecuting: currentContext. newContext == currentContext ifTrue: [newContext := interruptedProcess stepToSendOrReturn]. self contextStackIndex > 1 ifTrue: [self resetContext: newContext] ifFalse: [newContext == currentContext + ifTrue: [self scheduleUIUpdate] - ifTrue: [self changed: #contentsSelection. - self updateInspectors] ifFalse: [self resetContext: newContext]]. ! Item was added: + ----- Method: Debugger>>scheduleUIUpdate (in category 'context stack menu') ----- + scheduleUIUpdate + uiUpdateBlock ifNil: + [uiUpdateBlock := [self changed: #contentsSelection. + self updateInspectors]]. + WorldState lastDeferredUIMessage ~~ uiUpdateBlock ifTrue: + [WorldState addDeferredUIMessage: uiUpdateBlock]! From eliot.miranda at gmail.com Sat Sep 19 20:41:29 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sat, 19 Sep 2020 13:41:29 -0700 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: References: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Sat, Sep 19, 2020 at 1:17 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, these are interesting ideas! :-) > > > Your pattern is cool, it reminds me a little bit of the approach we are > using for Shout, see SHTextStyler >> #styleInBackgroundProcess:. > > It even more reminds me of Collection >> #do:displayingProgress:every:. > > And last but not least, at a fundamental level, aren't we replicating the > usual stepping logic from WorldState here? > I don't think so. What we're doing is decouplng from it. But you could be right. It seems to me like an MVC/MVP pattern. The model is changing, but it only makes sense to change the display when the observer can represent the model. If one is in an animation system such as Morphic then the observer (V or V&P) only needs display the model when rendering the next frame, if the model has changed. So what we'd like is automatic filtering or buffering of the state the odel uses to announce it has changed. But we don't have that. We've just got SharedQueue. So a poor man's solution is to use it. > > In your approach, where do you process the scheduled messages? The problem > is that we are running the simulation/execution code on the UI process. See > this thread, too: > http://forum.world.st/Debugging-of-other-processes-with-wait-tp5119965p5120049.html > > I would not use #lastDeferredUIMessage unless we can rule out that any > other client is communicating with the WorldState. Which we, afaik, never > can rule out since the idea of the deferred messages is that you can have > them scheduled from any background process. > > It would be cool to develop some reusable logic that is applicable to > different domains without expensive adaption. Maybe we should just add some > static generic helper method for this? > Well, take a look at what I commited to inbox. It soles the run to here problem, but does not fix fast clicking. So I'm just pointing you to it because it may be useful. I think you can focus on the problem better than I. But I'm here if you need to bounce ideas off me. Thanks for your energy here!! > Best, > > Christoph > > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Eliot Miranda > *Gesendet:* Samstag, 19. September 2020 21:36:57 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] Can we make computing the local variables in > the debugger lazy? > > Hi Christoph, > > On Sat, Sep 19, 2020 at 11:57 AM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Hi Eliot, >> >> >> very nice finding once again! I #timeProfile'd the menu button action and >> as I expected, the most expensive operation is the shout styling by the new >> inspectors, including the decompilation of every method: >> >> >> >> >> >> First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but >> probably that's rather a problem with my sources file, see the thread about >> this commit). >> >> Second, we do not redraw the world while running to the selection, so we >> do not need to update the inspectors at all. I think we could split up >> #doStep into some #basicDoStep (which would perform the actual stepping >> logic) + some #updateContextDuring: (which would update the stack list and >> the inspectors), then we would need to trigger the updates only once from >> #runToSelection:. >> As an alternative, we could make this method a bit more complex but >> responsive by applying the same updating logic from #runUntil. >> > > Nice. What ever is TSTTCPW :-). But it strikes me that one way is to use > addDeferredUIMessage:. The only tricky thing is not creating lots of these > unnecessarily. But that could be done by checking if the deferred > action queue is empty or not. E.g. rewrite this in doStep: > > self contextStackIndex > 1 > ifTrue: [self resetContext: newContext] > ifFalse: > [newContext == currentContext > ifTrue: [self changed: #contentsSelection. > self updateInspectors] > ifFalse: [self resetContext: newContext]]. > > as, say, > > self contextStackIndex > 1 > ifTrue: [self resetContext: newContext] > ifFalse: > [newContext == currentContext > ifTrue: [self scheduleUIUpdate] > ifFalse: [self resetContext: newContext]]. > > add an inst var to hold the last deferred action, and then do something > like > > Debugger>>scheduleUIUpdate > (lastScheduledUpdate notNil > and: [WorldState lastDeferredUIMessage == lastScheduledUpdate]) > ifTrue: > [^self]. > lastScheduledUpdate := [self changed: #contentsSelection. self > updateInspectors]. > WorldState addDeferredUIMessage: lastScheduledUpdate > > For this we have to add the lastDeferredUIMessage accessor to all the > relevant places, but it seems a nice pattern to me. > > And of course, there is no need to refresh lastScheduledUpdate. It could > be > > Debugger>>scheduleUIUpdate > lastScheduledUpdate ifNil: [lastScheduledUpdate := [self changed: > #contentsSelection. self updateInspectors]]. > WorldState lastDeferredUIMessage ~~ lastScheduledUpdate ifTrue: > [WorldState addDeferredUIMessage: lastScheduledUpdate] > > What do you think? :-) >> > > What do you like? > >> Best, >> Christoph >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von Eliot Miranda >> *Gesendet:* Samstag, 19. September 2020 20:17:12 >> *An:* The general-purpose Squeak developers list; Taeumel, Marcel >> *Betreff:* [squeak-dev] Can we make computing the local variables in the >> debugger lazy? >> >> Hi Marcel, >> >> can we try and reduce the frequency at which we compute the variables >> in the context inspector in the debugger? It is a noticeable >> performance hit. I really like the user interface, but the performance hit >> is making debugging difficult. >> >> As an example use this: >> >> | samples sineTable sound | >> "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" >> sineTable := SoundPlayer sineTable: 73. >> sineTable doWithIndex: "And let's not deafen anyone..." >> [:sample :index| sineTable at: index put: sample // 4]. >> samples := SoundBuffer new: 16000. >> 1 to: samples size by: sineTable size do: >> [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: >> sineTable startingAt: 1]. >> 1 to: 146 do: >> [:i| >> samples at: i put: ((samples at: i) * i / 146) asInteger. >> samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. >> sound := SampledSound >> samples: samples >> samplingRate: 16000. >> sound := MixedSound new >> add: sound pan: 0.25; >> add: sound pan: 0.75; >> yourself. >> sound computeSamplesForSeconds: sound duration >> >> >> Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an >> image containing Tools-mt.942). Debug the above in the workspace. >> Position the cursor at "sound computeSamplesForSeconds: sound duration" >> and do "run to here". It is essentially instantaneous. >> >> Now do the same in a contemporary trunk image. It takes almost 6 seconds. >> >> I used to be able to click step as fast as I could and the system would >> keep up with me. Now I find that if I click too fast I can accumulate >> excess clicks and when I stp clicking the system will continue stepping and >> go too far. >> >> I don't want to lose the feedback the new variables list gives us. But I >> do find the performance hit tedious. I wonder could we cache the list and >> only update it >> - when Morphic updates, and >> - when the context changes? >> >> >> _,,,^..^,,,_ >> best, Eliot >> >> > > -- > _,,,^..^,,,_ > best, Eliot > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 399308 bytes Desc: not available URL: From leves at caesar.elte.hu Sat Sep 19 21:00:25 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sat, 19 Sep 2020 23:00:25 +0200 (CEST) Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> References: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Sat, 19 Sep 2020, Thiede, Christoph wrote: > > Hi Eliot, > > > very nice finding once again! I #timeProfile'd the menu button action and as I expected, the most expensive operation is the shout styling by the new inspectors, including the decompilation of every method: What was it exactly that you profiled? The screenshot shows that 76.9% was spent in #initializeVariablesFromContext, of which 52.5% of the time was spent in CompiledMethod(CompiledCode) >> #getSource. The rest of the tree is not visible, but these methods have nothing to do with parsing or styling, they initialize the parser and normally should take <1 ms. Also, why is the decompiler producing the source code? > > > [IMAGE] > > > First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but probably that's rather a problem with my sources file, see the thread about this commit). You may want to try compiling a VM where FilePlugin's CreateFile does not set the FILE_FLAG_SEQUENTIAL_SCAN flag, and see if it helps with file reading performance. Levente > > Second, we do not redraw the world while running to the selection, so we do not need to update the inspectors at all. I think we could split up #doStep into some #basicDoStep (which would perform the actual stepping logic) + > some #updateContextDuring: (which would update the stack list and the inspectors), then we would need to trigger the updates only once from #runToSelection:. > As an alternative, we could make this method a bit more complex but responsive by applying the same updating logic from #runUntil. > > What do you think? :-) > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Eliot Miranda > Gesendet: Samstag, 19. September 2020 20:17:12 > An: The general-purpose Squeak developers list; Taeumel, Marcel > Betreff: [squeak-dev] Can we make computing the local variables in the debugger lazy?   > Hi Marcel, > >     can we try and reduce the frequency at which we compute the variables in the context inspector in the debugger?  It is a noticeable performance hit.  I really like the user interface, but the performance hit is making > debugging difficult. > > As an example use this: > > | samples sineTable sound | > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > sineTable := SoundPlayer sineTable: 73. > sineTable doWithIndex: "And let's not deafen anyone..." > [:sample :index| sineTable at: index put: sample // 4]. > samples := SoundBuffer new: 16000. > 1 to: samples size by: sineTable size do: > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. > 1 to: 146 do: > [:i| > samples at: i put: ((samples at: i) * i / 146) asInteger. > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. > sound := SampledSound > samples: samples > samplingRate: 16000. > sound := MixedSound new > add: sound pan: 0.25; > add: sound pan: 0.75; > yourself. > sound computeSamplesForSeconds: sound duration > > > Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an image containing Tools-mt.942).  Debug the above in the workspace.  Position the cursor at "sound computeSamplesForSeconds: sound duration" and do "run > to here".  It is essentially instantaneous. > > Now do the same in a contemporary trunk image.  It takes almost 6 seconds. > > I used to be able to click step as fast as I could and the system would keep up with me.  Now I find that if I click too fast I can accumulate excess clicks and when I stp clicking the system will continue stepping and go too > far. > > I don't want to lose the feedback the new variables list gives us.  But I do find the performance hit tedious.  I wonder could we cache the list and only update it > - when Morphic updates, and > - when the context changes? > > > _,,,^..^,,,_ > best, Eliot > > From asqueaker at gmail.com Sat Sep 19 21:24:40 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sat, 19 Sep 2020 16:24:40 -0500 Subject: [squeak-dev] find class is broken... In-Reply-To: References: Message-ID: Hi Eliot, I'm familiar with Squeak's focus settings.. > whenever I hit Command-f in the browser's System Category pane to > invoke FInd Class, instead the Find In Method dialog comes up. This is > driving me nuts because to get back to the class dialog I often lose focus > and end up deselecting the class. > Since you're looking to Find Class (which navigates to a new class selection), is it a big deal that the class is deselected? By "in" the System Category pane, do you mean the mouse pointer? Cmd+f is going to invoke on whatever pane has *keyboard focus*. As Christoph mentioned, *Mouse over for keyboard focus* will make it follow the mouse, which I would highly recommend. Functional pointing increases input leverage by an order of magnitude, the most powerful way to use Squeak, while also partially unwinding the dreadful coupling between widget focus, application-function, window z-ordering! Squeak's is the only IDE (with the right settings) capable of delineating these functions with independent, unambiguous gestures. Regards, Chris > > (and while we're at it, how easy is it to implement Form>>copyToClipboard > ? this was tedious to produce; here are my "focus" preferences, I also have > multi-window browsers turned on) > > [image: image.png] > _,,,^..^,,,_ > best, Eliot > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image.png Type: image/png Size: 320190 bytes Desc: not available URL: From tim at rowledge.org Sat Sep 19 22:18:30 2020 From: tim at rowledge.org (tim Rowledge) Date: Sat, 19 Sep 2020 15:18:30 -0700 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: > On 2020-09-19, at 12:40 PM, Thiede, Christoph wrote: > > Can you reproduce this in a fresh trunk image? Which preferences do you have changed? I can see the problem in a 5.3 image with no updates. There is some weird interaction between where you last selected anything and what cmd-f offers. I'd have to guess at some relationship to the focus follows mouse stuff - except the preferences don't show me any pref that seems to have anything to do with 'focus follows mouse', which I'd swear used to be there. Select a class and then a method; move the cursor back to the category list and cmd-f - you'll get the dialogue for the method list. Select in the method text pane, move the cursor to the category list and cmd-f will get you a text find dialogue (with a truly terrible pair of title & query strings). We seem to be making a rather convoluted set of UI choices these days. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Rules for Optimization: 1. Don't; 2. (for experts only) Don't Yet From gettimothy at zoho.com Sat Sep 19 22:28:48 2020 From: gettimothy at zoho.com (gettimothy) Date: Sat, 19 Sep 2020 18:28:48 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp Message-ID: <174a87bed5a.b1d436d749724.103768498038659580@zoho.com> Hi Folks, I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. (XMLElement name:'br' ) printString '
' vs (XMLElement name:'br' ) printString '
' Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. Checking the Monticello, the 6.0  XML-Parser (monty.428) while the 5.3 is XML-Parser (monty.428 , ul.44) So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. Being new to this, who knows how I managed that.   before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. Which should I use?  Should I bring in the ul.44? and how do I do that? Should I revise my tests (there are not too many) to insert that extra space. thank you for your time. -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Sat Sep 19 23:47:40 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sat, 19 Sep 2020 18:47:40 -0500 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: On Sat, Sep 19, 2020 at 5:18 PM tim Rowledge wrote: > > > > On 2020-09-19, at 12:40 PM, Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > > > > Can you reproduce this in a fresh trunk image? Which preferences do you > have changed? > > I can see the problem in a 5.3 image with no updates. There is some weird > interaction between where you last selected anything and what cmd-f offers. > I'd have to guess at some relationship to the focus follows mouse stuff - > except the preferences don't show me any pref that seems to have anything > to do with 'focus follows mouse', which I'd swear used to be there. > "*Focus Follows Mouse" *is the original name for the setting, "*Windows' Contents Are Always Active*". Marcel did quite a bit of work which included renaming that. > Select a class and then a method; move the cursor back to the category > list and cmd-f - you'll get the dialogue for the method list. Select in the > method text pane, move the cursor to the category list and cmd-f will get > you a text find dialogue (with a truly terrible pair of title & query > strings). > > We seem to be making a rather convoluted set of UI choices these days. > The above is the correct behavior when *Mouse over for keyboard focus* is disabled. The last-clicked widget got keyboard focus and keyboard input. I made a video several years ago explaining all the related settings in detail. Insane input leverage is available, if you're willing to crank up the settings. For example, imagine the power of having EVERY PIXEL on your desktop being ready for ANY INPUT, regardless of window z-ordering, simply by moving pointer there and pressing a mouse or keyboard button. https://www.youtube.com/watch?v=q0WqBjtIbK0 I believe *Click for focus *is such a naturally painful (mal)design for users, that it was instrumental in the death of PC's for personal computing. It's still around because people often prefer familiarity, even if it hurts. - Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From tim at rowledge.org Sun Sep 20 00:52:58 2020 From: tim at rowledge.org (tim Rowledge) Date: Sat, 19 Sep 2020 17:52:58 -0700 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: UI preferences are so personal it makes my head spin. We have almost had global nuclear wars over arguments about text editor choices, let alone actually interesting UI aspects. > On 2020-09-19, at 4:47 PM, Chris Muller wrote: > For example, imagine the power of having EVERY PIXEL on your desktop being ready for ANY INPUT, regardless of window z-ordering, simply by moving pointer there and pressing a mouse or keyboard button. > Here you see, we differ. It's likely partly due to growing up using RISC OS and the original ST-80 UI (similar but not the same, which makes for 'interesting' habit/reflexes) where basically the keyboard focus was set by clicking in a text field and mouse focus what where ever the cursor sat. RISC OS permits windows to be active (for mouse or keyboard) at any point in the Z stack but you do still have to click in that text field. Very-old ST-80 MVC was mouse focus within the active window and IIRC keyboard focus in the text view under the mouse. I *think* I remember work at PPS to actually formalise some idea of a text focus for ObjectWorks 4.0 - but that's so long ago who knows if I'm right. The thing that would drive me nuts - and I have experience of trying to use a workstation set up that way in the past - is the keyboard focus sliding around as the mouse drifts across because of the cable being stiff or pulled by gravity etc. Yes, ok, a bluetooth mouse wouldn't usually do that but half the time they don't do *anything* right. Similarly I really, really, dislike the 'menu button selects a list item' thing that we currently have (and that Apple do, too) because I want the damn thing I selected to be selected until I actually select something else. Grrrrr. Damn; this is all reminding me how much I miss arguing UI with Jef Raskin. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim You never really learn to swear until you learn to drive in Silicon Valley From karlramberg at gmail.com Sun Sep 20 07:37:17 2020 From: karlramberg at gmail.com (karl ramberg) Date: Sun, 20 Sep 2020 09:37:17 +0200 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: Relevant xkce https://xkcd.com/1172/ Best, Karl On Sun, Sep 20, 2020 at 2:53 AM tim Rowledge wrote: > UI preferences are so personal it makes my head spin. We have almost had > global nuclear wars over arguments about text editor choices, let alone > actually interesting UI aspects. > > > On 2020-09-19, at 4:47 PM, Chris Muller wrote: > > For example, imagine the power of having EVERY PIXEL on your desktop > being ready for ANY INPUT, regardless of window z-ordering, simply by > moving pointer there and pressing a mouse or keyboard button. > > > > Here you see, we differ. It's likely partly due to growing up using RISC > OS and the original ST-80 UI (similar but not the same, which makes for > 'interesting' habit/reflexes) where basically the keyboard focus was set by > clicking in a text field and mouse focus what where ever the cursor sat. > RISC OS permits windows to be active (for mouse or keyboard) at any point > in the Z stack but you do still have to click in that text field. Very-old > ST-80 MVC was mouse focus within the active window and IIRC keyboard focus > in the text view under the mouse. > > I *think* I remember work at PPS to actually formalise some idea of a text > focus for ObjectWorks 4.0 - but that's so long ago who knows if I'm right. > The thing that would drive me nuts - and I have experience of trying to > use a workstation set up that way in the past - is the keyboard focus > sliding around as the mouse drifts across because of the cable being stiff > or pulled by gravity etc. Yes, ok, a bluetooth mouse wouldn't usually do > that but half the time they don't do *anything* right. > > Similarly I really, really, dislike the 'menu button selects a list item' > thing that we currently have (and that Apple do, too) because I want the > damn thing I selected to be selected until I actually select something > else. Grrrrr. Damn; this is all reminding me how much I miss arguing UI > with Jef Raskin. > > > tim > -- > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > You never really learn to swear until you learn to drive in Silicon Valley > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 20 09:33:11 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 20 Sep 2020 05:33:11 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp Message-ID: <174aadc318a.f6f06e2b54545.8472782805438890616@zoho.com> Regarding the space, no space, per https://www.tutorialspoint.com/xhtml/xhtml_tips_tricks.htm the space is required, so I am going to go with that and modify my tests. cheers. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davide.grandi at email.it Sun Sep 20 09:55:04 2020 From: davide.grandi at email.it (Davide Grandi) Date: Sun, 20 Sep 2020 11:55:04 +0200 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <174aadc318a.f6f06e2b54545.8472782805438890616@zoho.com> References: <174aadc318a.f6f06e2b54545.8472782805438890616@zoho.com> Message-ID: <5797e19a-b512-81d5-5a2b-edd2c893bce7@email.it> I was curious and, via ubiquitous stackoverflow, I found a precise standard reference : https://www.w3.org/TR/xhtml1/#C_2 bye,     Davide On 20/09/2020 11:33, gettimothy via Squeak-dev wrote: > Regarding the space, no space, per > > https://www.tutorialspoint.com/xhtml/xhtml_tips_tricks.htm > > the space is required, so I am going to go with that and modify my tests. > > > cheers. > -- Ing. Davide Grandi email : davide.grandi at email.it mobile : +39 339 7468 778 linkedin : http://linkedin.com/in/davidegrandi -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 20 10:02:10 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 20 Sep 2020 06:02:10 -0400 Subject: [squeak-dev] ReadWriteStream inspect self contents ctl-p is immediately available for cut-n-paste and that is a great convenience, thanks to however did it. Message-ID: <174aaf6baa5.c0e269d554636.7017114569999735143@zoho.com> Its a small thing, but I have about 100 SUnit tests that I have to edit and the workflow is very easy due to that feature. -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 20 10:26:59 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 20 Sep 2020 06:26:59 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <5797e19a-b512-81d5-5a2b-edd2c893bce7@email.it> References: <174aadc318a.f6f06e2b54545.8472782805438890616@zoho.com> <5797e19a-b512-81d5-5a2b-edd2c893bce7@email.it> Message-ID: <174ab0d71df.b6adfdc354741.8955706299915959932@zoho.com> Thank you! ---- On Sun, 20 Sep 2020 05:55:04 -0400 Davide Grandi wrote ---- I was curious and, via ubiquitous stackoverflow, I found a precise standard reference : https://www.w3.org/TR/xhtml1/#C_2 bye,     Davide On 20/09/2020 11:33, gettimothy via Squeak-dev wrote: Regarding the space, no space, per https://www.tutorialspoint.com/xhtml/xhtml_tips_tricks.htm the space is required, so I am going to go with that and modify my tests. cheers. -- Ing. Davide Grandi email : mailto:davide.grandi at email.it mobile : +39 339 7468 778 linkedin : http://linkedin.com/in/davidegrandi -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Sun Sep 20 13:33:53 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 20 Sep 2020 09:33:53 -0400 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: <20200920133353.GA47020@shell.msen.com> Thats perfect! Dave On Sun, Sep 20, 2020 at 09:37:17AM +0200, karl ramberg wrote: > Relevant xkce > https://xkcd.com/1172/ > > Best, > Karl > > On Sun, Sep 20, 2020 at 2:53 AM tim Rowledge wrote: > > > UI preferences are so personal it makes my head spin. We have almost had > > global nuclear wars over arguments about text editor choices, let alone > > actually interesting UI aspects. > > > > > On 2020-09-19, at 4:47 PM, Chris Muller wrote: > > > For example, imagine the power of having EVERY PIXEL on your desktop > > being ready for ANY INPUT, regardless of window z-ordering, simply by > > moving pointer there and pressing a mouse or keyboard button. > > > > > > > Here you see, we differ. It's likely partly due to growing up using RISC > > OS and the original ST-80 UI (similar but not the same, which makes for > > 'interesting' habit/reflexes) where basically the keyboard focus was set by > > clicking in a text field and mouse focus what where ever the cursor sat. > > RISC OS permits windows to be active (for mouse or keyboard) at any point > > in the Z stack but you do still have to click in that text field. Very-old > > ST-80 MVC was mouse focus within the active window and IIRC keyboard focus > > in the text view under the mouse. > > > > I *think* I remember work at PPS to actually formalise some idea of a text > > focus for ObjectWorks 4.0 - but that's so long ago who knows if I'm right. > > The thing that would drive me nuts - and I have experience of trying to > > use a workstation set up that way in the past - is the keyboard focus > > sliding around as the mouse drifts across because of the cable being stiff > > or pulled by gravity etc. Yes, ok, a bluetooth mouse wouldn't usually do > > that but half the time they don't do *anything* right. > > > > Similarly I really, really, dislike the 'menu button selects a list item' > > thing that we currently have (and that Apple do, too) because I want the > > damn thing I selected to be selected until I actually select something > > else. Grrrrr. Damn; this is all reminding me how much I miss arguing UI > > with Jef Raskin. > > > > > > tim > > -- > > tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim > > You never really learn to swear until you learn to drive in Silicon Valley > > > > > > > > > From forums.jakob at resfarm.de Sun Sep 20 14:36:27 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Sun, 20 Sep 2020 16:36:27 +0200 Subject: [squeak-dev] Not getting all mail? In-Reply-To: References: Message-ID: Thank you Levente. Well, I still do not receive all mail and the missing messages do not appear in my Strato inbox (to which this address belongs) in the spam folder either. Whom should I contact, the Strato support? @Tobias Pape Did your action already improve the situation for you? Correspondingly, I have received absolutely no replies to this thread via the list, only Levente's direct message. *sigh* Kind regards, Jakob Am Do., 17. Sept. 2020 um 23:36 Uhr schrieb Levente Uzonyi : > > Hi Jakob, > > Your mail server considered that email to be spam. It happened to many > other emails on that day and also today. And it also happend to a few > others using the same email provider. > > > Levente > > P.S.: I saw that your mail server rejected my mail on the list as well, so > I'm sending this directly to you. > > On Thu, 17 Sep 2020, Jakob Reschke wrote: > > > Hello, > > Over the past week I have observed people reply to some messages that I didn't receive from the list. For example, I do not have Christoph's message to which Eliot replied below. In one instance it even looked like I did not > > get the original post, but only a reply. I checked my spam folder, there are no Squeak mails in there. > > > > Do you experience the same? Or do I just always see cases where one person did not reply to the list, but the next one does again? > > > > Kind regards, > > Jakob > > > > > > Eliot Miranda schrieb am Mo., 14. Sep. 2020, 21:49: > > > > > > On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph wrote: > > > > Which completion are you using? Autocompletion already behaves like this. > > > > > > OComplete > > > > > > Best, > > > > Christoph > > > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > > Von: Squeak-dev im Auftrag von Eliot Miranda > > Gesendet: Montag, 14. September 2020 04:45:30 > > An: The general-purpose Squeak developers list > > Betreff: [squeak-dev] autocomplete exit > > Hi, > > would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? > > > > _,,,^..^,,,_ > > best, Eliot > > > > > > > > From leves at caesar.elte.hu Sun Sep 20 15:10:16 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 20 Sep 2020 17:10:16 +0200 (CEST) Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <174a87bed5a.b1d436d749724.103768498038659580@zoho.com> References: <174a87bed5a.b1d436d749724.103768498038659580@zoho.com> Message-ID: Hi Tim, Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. That Xtreams dependency on that library is pretty much pointless: - only one example grammar (PEGWikiGenerator) uses Monty's XML package - the example uses it to produce web content as XHTML which is rather unusual for two reasons: - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) - the example does not use any features to justify using the not-so-Squeak-compatible XML package - that example grammar only uses it for the sake of making it Pharo-compatible Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo version of the example, making the example use Squeak's XML package in Squeak. The latter also involves more work, and a more compilcated Metacello configuration. On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: > Hi Folks, > > I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 > > Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. > > (XMLElement name:'br' ) printString '
' > vs > (XMLElement name:'br' ) printString '
' > > Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. > > > Checking the Monticello, the 6.0  > > XML-Parser (monty.428) > > > while the 5.3 is > > XML-Parser (monty.428 , ul.44) You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. > > > > So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. > Being new to this, who knows how I managed that.   No. The two packages are not compatible with each other and you have "both" in your image at the same time. > > > before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. > > > FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. > > Which should I use?  The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. > > Should I bring in the ul.44? and how do I do that? > Should I revise my tests (there are not too many) to insert that extra space. If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). Levente > > > thank you for your time. > > > > > > > > > > > > From leves at caesar.elte.hu Sun Sep 20 15:22:01 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 20 Sep 2020 17:22:01 +0200 (CEST) Subject: [squeak-dev] Not getting all mail? In-Reply-To: References: Message-ID: Hi Jakob, On Sun, 20 Sep 2020, Jakob Reschke wrote: > Thank you Levente. > > Well, I still do not receive all mail and the missing messages do not > appear in my Strato inbox (to which this address belongs) in the spam > folder either. Whom should I contact, the Strato support? Hard to answer. Your mail provider straight out rejected the emails, so the mails had no chance to appear in your spam folder. They very likely use barracudacentral's blocklist, but the mail server did not mention why it blocks the messages. This is the response of the mail server: 550 5.7.1 Refused by local policy. No SPAM please! (B-EX 149500::1600587459-000088D1-6E70BDDC/10/35787913868) see https://www.strato.com/faq/en_us/article/2420 The link there is super unhelpful. It says: Your email violates the filter and/or spam rules of the respective recipient. Please check if your email contains content which could be identified as spam. Perhaps that identifier (B-EX 149500::1600587459-000088D1-6E70BDDC/10/35787913868) may help you get answer from your provider why they are blocking the emails. > > @Tobias Pape Did your action already improve the situation for you? > > Correspondingly, I have received absolutely no replies to this thread > via the list, only Levente's direct message. *sigh* Yes, they are still blocking mails to your address (and others' addresses). Levente > > Kind regards, > Jakob > > Am Do., 17. Sept. 2020 um 23:36 Uhr schrieb Levente Uzonyi > : >> >> Hi Jakob, >> >> Your mail server considered that email to be spam. It happened to many >> other emails on that day and also today. And it also happend to a few >> others using the same email provider. >> >> >> Levente >> >> P.S.: I saw that your mail server rejected my mail on the list as well, so >> I'm sending this directly to you. >> >> On Thu, 17 Sep 2020, Jakob Reschke wrote: >> >>> Hello, >>> Over the past week I have observed people reply to some messages that I didn't receive from the list. For example, I do not have Christoph's message to which Eliot replied below. In one instance it even looked like I did not >>> get the original post, but only a reply. I checked my spam folder, there are no Squeak mails in there. >>> >>> Do you experience the same? Or do I just always see cases where one person did not reply to the list, but the next one does again? >>> >>> Kind regards, >>> Jakob >>> >>> >>> Eliot Miranda schrieb am Mo., 14. Sep. 2020, 21:49: >>> >>> >>> On Mon, Sep 14, 2020 at 4:54 AM Thiede, Christoph wrote: >>> >>> Which completion are you using? Autocompletion already behaves like this. >>> >>> >>> OComplete >>> >>> >>> Best, >>> >>> Christoph >>> >>> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ >>> Von: Squeak-dev im Auftrag von Eliot Miranda >>> Gesendet: Montag, 14. September 2020 04:45:30 >>> An: The general-purpose Squeak developers list >>> Betreff: [squeak-dev] autocomplete exit >>> Hi, >>> would it be possible to make autocomplete exit whenever a non-selector character was typed, or at least when an arrow key was typed? >>> >>> _,,,^..^,,,_ >>> best, Eliot >>> >>> >>> >>> > From lewis at mail.msen.com Sun Sep 20 15:50:46 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Sun, 20 Sep 2020 11:50:46 -0400 Subject: [squeak-dev] ActiveGlobals explained by Andreas Raab Message-ID: <20200920155046.GA78466@shell.msen.com> In the course of trying to better understand the role of the ActiveHand global, I found an excellent explanation by Andreas Raab in his original change set. I put this on the swiki and linked it to several related pages: http://wiki.squeak.org/squeak/6647 It is amusing to note how I found this. I was trying to figure out what object should properly own the active hand, and I had conviced myself that it should probably be the WolrdState for a Morphic world. So as an experiment, it tried adding the instance variable to WorldState, and I discovered to my surprise that it was already there. It was completely unreferenced, but one of my Morphic worlds still had it set to point to a HandMorph, which seemed odd. Looking back at old images, I found that the activeHand instance var in WorldState was last used in Squeak 3.0, and that the global variable that replaced it was in use as of Squeak 3.10.2. A bit of digging though the files.squeak.org archives led me to the original change set in which the change was made. Andreas provided a preamble with explanation of the rationale for the change, this this is what I copied to the swiki. Despite the fact that a WorldState instance still point to an activeHand, that reference has apparently been hanging around for the last 20 years without actually being used for anything. Dave From gettimothy at zoho.com Sun Sep 20 16:17:25 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 20 Sep 2020 12:17:25 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: References: <174a87bed5a.b1d436d749724.103768498038659580@zoho.com> Message-ID: <174ac4e46c7.10117c19457296.7639518019265384967@zoho.com> Levente Thank you for your detailed reply. I will get back to you throughout the week as I am very tired at the moment and the brain is sub-par. Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. Can the Trunk XML stuff handle the XPath and SAX?  I will be iterating over many gigabytes of XML document. If so, I would love to use the Squeak stuff and begin documenting it (there is no documenation that I am aware of) IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo version of the example, making the example use Squeak's XML package in Squeak. The latter also involves more work, and a more compilcated Metacello configuration. I prefer the latter approach. Surprisingly, I chatted with the pharo team...and I tried to import XTreams into Pharo at one point...and it does not load. So having a pharo dependency on something that does not work in Pharo is silly.  It has been years since I wrote a Metacello configuration, but ramp up time should not be too much. If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). I do. I have some Postgres XPath and XSLT functions written that depend on XHTML.  The strict document stuff makes data extraction and manipulation easier in the long-run. So, if I am reading you correctly, the "right thing to do" is decouple the Monty XML stuff from Xtreams,  Modify the existing XML stuff in the package to use the Trunk XML stuff, then provide a squeak and pharo ConfigurationOfXTreamsPharo and ConfigurationOfXTreamsSqueak. That's a big job. My project is about 2/3 done and I do not think I will be using anything in the image that will hit the dangers you describe. I do have a "pristine6.0" image for testing loading stuff and getting it running on Squeak6  That porting project would be perfect to run on it. I will need guidance on the porting project as source control stuff is a big weak spot with me. I usually "putter" code on Sunday's . I have added :  DECOUPLE Project per Levente on the Pristine on "down time putter" to my TODO list in my image. cheers. t ---- On Sun, 20 Sep 2020 11:10:16 -0400 Levente Uzonyi wrote ---- Hi Tim, Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. That Xtreams dependency on that library is pretty much pointless: - only one example grammar (PEGWikiGenerator) uses Monty's XML package - the example uses it to produce web content as XHTML which is rather unusual for two reasons: - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) - the example does not use any features to justify using the not-so-Squeak-compatible XML package - that example grammar only uses it for the sake of making it Pharo-compatible Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo version of the example, making the example use Squeak's XML package in Squeak. The latter also involves more work, and a more compilcated Metacello configuration. On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: > Hi Folks, > > I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 > > Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. > > (XMLElement name:'br' ) printString '
' > vs > (XMLElement name:'br' ) printString '
' > > Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. > > > Checking the Monticello, the 6.0  > > XML-Parser (monty.428) > > > while the 5.3 is > > XML-Parser (monty.428 , ul.44) You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. > > > > So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. > Being new to this, who knows how I managed that.   No. The two packages are not compatible with each other and you have "both" in your image at the same time. > > > before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. > > > FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. > > Which should I use?  possibly in Postgres The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. > > Should I bring in the ul.44? and how do I do that? > Should I revise my tests (there are not too many) to insert that extra space. If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). Levente > > > thank you for your time. > > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sun Sep 20 19:33:20 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 20 Sep 2020 19:33:20 0000 Subject: [squeak-dev] The Trunk: Monticello-ul.728.mcz Message-ID: Levente Uzonyi uploaded a new version of Monticello to project The Trunk: http://source.squeak.org/trunk/Monticello-ul.728.mcz ==================== Summary ==================== Name: Monticello-ul.728 Author: ul Time: 20 September 2020, 9:32:46.305579 pm UUID: 8e7d9744-9b5a-4868-b645-2f817295ad7b Ancestors: Monticello-ul.727, Monticello-eem.727, Monticello-ct.727, Monticello-ul.726 Merged Monticello-ul.727, Monticello-ct.727, Monticello-ul.726. =============== Diff against Monticello-eem.727 =============== Item was added: + ----- Method: MCDirectoryRepository>>includesVersionNamed: (in category 'versions') ----- + includesVersionNamed: aString + + | comparable | + comparable := ((aString endsWith: '.mcz') and: [ aString size > 4 ]) + ifTrue: [ aString allButLast: 4 ] + ifFalse: [ aString ]. + allVersionNamesCache ifNil: [ + "Instead of reading the contents of the entire directory in #allVersionNames, look up a single .mcz file. + This is just an optimization. If the file does not exist, the version may still be there as an mcd." + (directory fileExists: comparable, '.mcz') ifTrue: [ ^true ] ]. + ^ self allVersionNames includes: comparable! Item was changed: MCFileBasedRepository subclass: #MCHttpRepository instanceVariableNames: 'location user password readerCache indexed webClient' + classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' - classVariableNames: 'UseSharedWebClientInstance' poolDictionaries: '' category: 'Monticello-Repositories'! Item was changed: ----- Method: MCHttpRepository class>>creationTemplate (in category 'ui-support') ----- creationTemplate + ^self creationTemplateLocation: 'https://www.squeaksource.com/ProjectName' - ^self creationTemplateLocation: 'http://www.squeaksource.com/ProjectName' user: 'squeak' password: 'squeak' ! Item was added: + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in category 'url rewrite') ----- + rewriteUrl: aString forDownload: forDownload + + | result | + result := aString. + self urlRewriteRules groupsDo: [ :regexString :replacement :downloadOnly | + (forDownload or: [ downloadOnly not ]) ifTrue: [ + result := result copyWithRegex: regexString matchesReplacedWith: replacement ] ]. + ^result + + " + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'http://static.smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). + self assert: 'http://smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). + "! Item was added: + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url rewrite') ----- + urlRewriteRules + + ^URLRewriteRules ifNil: [ + URLRewriteRules := #( + "Regex to be replaced" "static replacement string" "download only" + '^http\://source\.squeak\.org/' 'https://source.squeak.org/' false + '^http\://squeaksource\.com/' 'https://squeaksource.com/' false + '^http\://www.squeaksource\.com/' 'https://www.squeaksource.com/' false + '^http\://smalltalkhub.com/' 'http://static.smalltalkhub.com/' true + ) asOrderedCollection ]! Item was changed: ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- httpGet: url arguments: arguments + | urlString | - | progress urlString client response result | - progress := [ :total :amount | - HTTPProgress new - total: total; - amount: amount; - signal: 'Downloading...' ]. urlString := arguments ifNil: [ url ] ifNotNil: [ | queryString | queryString := WebUtils encodeUrlEncodedForm: arguments. (url includes: $?) ifTrue: [ url, '&', queryString ] ifFalse: [ url, '?', queryString ] ]. + urlString := self class rewriteUrl: urlString forDownload: true. + ^self webClientDo: [ :client | + client + username: self user; + password: self password; + httpGet: urlString do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ]! - self class useSharedWebClientInstance ifTrue: [ - "Acquire webClient by atomically storing it in the client variable and setting its value to nil." - client := webClient. - webClient := nil ]. - client - ifNil: [ client := WebClient new ] - ifNotNil: [ - "Attempt to avoid an error on windows by recreating the underlying stream." - client isConnected ifFalse: [ client close ] ]. - response := client - username: self user; - password: self password; - httpGet: urlString do: [ :request | - request - headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; - headerAt: 'Connection' put: 'Keep-Alive'; - headerAt: 'Accept' put: '*/*' ]. - result := (response code between: 200 and: 299) - ifFalse: [ - response content. "Make sure content is read." - nil ] - ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. - self class useSharedWebClientInstance - ifTrue: [ - "Save the WebClient instance for reuse, but only if there is no client cached." - webClient - ifNil: [ webClient := client ] - ifNotNil: [ client close ] ] - ifFalse: [ client close ]. - result ifNil: [ NetworkError signal: 'Could not access ', location ]. - ^result! Item was added: + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') ----- + webClientDo: aBlock + + | client attemptsLeft response result | + self class useSharedWebClientInstance ifTrue: [ + "Acquire webClient by atomically storing it in the client variable and setting its value to nil." + client := webClient. + webClient := nil ]. + + client + ifNil: [ client := WebClient new ] + ifNotNil: [ + "Attempt to avoid an error by recreating the underlying stream." + client isConnected ifFalse: [ client close ] ]. + + attemptsLeft := 3. + response := nil. + [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ + response := [ aBlock value: client ] + on: NetworkError + do: [ :error | + attemptsLeft = 0 ifTrue: [ error pass ]. + (3 - attemptsLeft) seconds asDelay wait. + attemptsLeft := attemptsLeft - 1. + nil "The response" ] ]. + + result := (response code between: 200 and: 299) + ifFalse: [ + response content. "Make sure content is read." + nil ] + ifTrue: [ + (RWBinaryOrTextStream with: ( + response contentWithProgress: [ :total :amount | + HTTPProgress new + total: total; + amount: amount; + signal ])) reset ]. + + self class useSharedWebClientInstance + ifTrue: [ + "Save the WebClient instance for reuse, but only if there is no client cached." + webClient + ifNil: [ webClient := client ] + ifNotNil: [ client close ] ] + ifFalse: [ client close ]. + + result ifNil: [ NetworkError signal: 'Could not access ', location ]. + ^result! Item was changed: ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in category 'private') ----- writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock + + | stream urlString | - | stream response statusLine code | stream := RWBinaryOrTextStream on: String new. aBlock value: stream. + urlString := self urlForFileNamed: aString. + urlString := self class rewriteUrl: urlString forDownload: false. + ^self displayProgress: 'Uploading ', aString during: [ + self webClientDo: [ :client | + client + username: self user; + password: self password; + httpPut: urlString + content: stream contents + type: nil + do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ] ]! - self displayProgress: 'Uploading ', aString during:[ - response := HTTPSocket - httpPut: stream contents - to: (self urlForFileNamed: aString) - user: self user - passwd: self password. - ]. - "More robust handling of HTTP responses. Instead of enumerating - all possible return codes and http versions, do a quick parse" - (response beginsWith: 'HTTP/') ifTrue:[ - "Looks like an HTTP header, not some error message" - statusLine := response copyUpTo: Character cr. - code := [(statusLine findTokens: ' ') second asInteger] on: Error do:[]. - ]. - (code isInteger and:[code between: 200 and: 299]) - ifFalse:[self error: response].! Item was changed: ----- Method: MCTool>>buildWith: (in category 'toolbuilder') ----- buildWith: builder | windowBuilder | windowBuilder := MCToolWindowBuilder builder: builder tool: self. self widgetSpecs do: [:spec | | send fractions offsets | send := spec first. fractions := (spec at: 2 ifAbsent: [#(0 0 1 1)]) copy. offsets := (spec at: 3 ifAbsent: [#(0 0 0 0)]) copy. + fractions withIndexDo: [:numberOrSymbol :index | - fractions doWithIndex: [:numberOrSymbol :index | numberOrSymbol isSymbol ifTrue: [fractions at: index put: (self perform: numberOrSymbol)]]. + offsets withIndexDo: [:numberOrSymbol :index | - offsets doWithIndex: [:numberOrSymbol :index | numberOrSymbol isSymbol ifTrue: [offsets at: index put: (self perform: numberOrSymbol)]]. windowBuilder frame: (LayoutFrame fractions: (fractions first @ fractions second corner: fractions third @ fractions fourth) offsets: (offsets first @ offsets second corner: offsets third @ offsets fourth)). windowBuilder perform: send first withArguments: send allButFirst]. ^ windowBuilder build ! Item was changed: ----- Method: MCVersionName>>versionName (in category 'accessing') ----- versionName "Answer my version name as a ByteString, without the file suffix or any ancestor-attributes." | end | self isEmpty ifTrue: [^ String empty]. end := self indexOf: $( ifAbsent: [ + | size | + size := self size. + (size > 4 + and: [ (self at: size - 3) == $. + and: [ (self at: size - 2) == $m + and: [ (self at: size - 1) == $c ] ] ]) + ifTrue: [size - 3] + ifFalse: [size + 1]]. - (self size > 4 - and: [ (self at: self size - 3) == $. - and: [ (self at: self size - 2) == $m - and: [ (self at: self size - 1) == $c ] ] ]) - ifTrue: [self size - 3] - ifFalse: [self size + 1]]. ^self first: end - 1! From commits at source.squeak.org Sun Sep 20 19:34:12 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 20 Sep 2020 19:34:12 0000 Subject: [squeak-dev] The Trunk: Monticello-ul.727.mcz Message-ID: Levente Uzonyi uploaded a new version of Monticello to project The Trunk: http://source.squeak.org/trunk/Monticello-ul.727.mcz ==================== Summary ==================== Name: Monticello-ul.727 Author: ul Time: 17 September 2020, 1:54:51.056164 am UUID: ad776836-42eb-4aa2-b788-f10dd9e07da2 Ancestors: Monticello-cmm.726 MCHttpRepository changes: - before up- or downloading files, transform the urls using #rewriteUrl:forDownload:. The default rules (see #urlRewriteRules) switch from http to https for source.squeak.org and squeaksource.com, and switch to the the static smalltalkhub site for downloads. The url rewriting is Eliot's idea, but this implementation uses a list of rewrite rules instead of a dictionary-based mapping. - use WebClient (and the shared webclient instance) for uploads too - retry down/uploading with WebClient at most 3 times. This should work around the case where the underlying socket was closed but the state of the socket has not been updated in Squeak. - use https in #creationTemplate =============== Diff against Monticello-cmm.726 =============== Item was changed: MCFileBasedRepository subclass: #MCHttpRepository instanceVariableNames: 'location user password readerCache indexed webClient' + classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' - classVariableNames: 'UseSharedWebClientInstance' poolDictionaries: '' category: 'Monticello-Repositories'! Item was changed: ----- Method: MCHttpRepository class>>creationTemplate (in category 'ui-support') ----- creationTemplate + ^self creationTemplateLocation: 'https://www.squeaksource.com/ProjectName' - ^self creationTemplateLocation: 'http://www.squeaksource.com/ProjectName' user: 'squeak' password: 'squeak' ! Item was added: + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in category 'url rewrite') ----- + rewriteUrl: aString forDownload: forDownload + + | result | + result := aString. + self urlRewriteRules groupsDo: [ :regexString :replacement :downloadOnly | + (forDownload or: [ downloadOnly not ]) ifTrue: [ + result := result copyWithRegex: regexString matchesReplacedWith: replacement ] ]. + ^result + + " + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). + self assert: 'http://static.smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). + self assert: 'http://smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). + "! Item was added: + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url rewrite') ----- + urlRewriteRules + + ^URLRewriteRules ifNil: [ + URLRewriteRules := #( + "Regex to be replaced" "static replacement string" "download only" + '^http\://source\.squeak\.org/' 'https://source.squeak.org/' false + '^http\://squeaksource\.com/' 'https://squeaksource.com/' false + '^http\://www.squeaksource\.com/' 'https://www.squeaksource.com/' false + '^http\://smalltalkhub.com/' 'http://static.smalltalkhub.com/' true + ) asOrderedCollection ]! Item was changed: ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- httpGet: url arguments: arguments + | urlString | - | progress urlString client response result | - progress := [ :total :amount | - HTTPProgress new - total: total; - amount: amount; - signal: 'Downloading...' ]. urlString := arguments ifNil: [ url ] ifNotNil: [ | queryString | queryString := WebUtils encodeUrlEncodedForm: arguments. (url includes: $?) ifTrue: [ url, '&', queryString ] ifFalse: [ url, '?', queryString ] ]. + urlString := self class rewriteUrl: urlString forDownload: true. + ^self webClientDo: [ :client | + client + username: self user; + password: self password; + httpGet: urlString do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ]! - self class useSharedWebClientInstance ifTrue: [ - "Acquire webClient by atomically storing it in the client variable and setting its value to nil." - client := webClient. - webClient := nil ]. - client - ifNil: [ client := WebClient new ] - ifNotNil: [ - "Attempt to avoid an error on windows by recreating the underlying stream." - client isConnected ifFalse: [ client close ] ]. - response := client - username: self user; - password: self password; - httpGet: urlString do: [ :request | - request - headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; - headerAt: 'Connection' put: 'Keep-Alive'; - headerAt: 'Accept' put: '*/*' ]. - result := (response code between: 200 and: 299) - ifFalse: [ - response content. "Make sure content is read." - nil ] - ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. - self class useSharedWebClientInstance - ifTrue: [ - "Save the WebClient instance for reuse, but only if there is no client cached." - webClient - ifNil: [ webClient := client ] - ifNotNil: [ client close ] ] - ifFalse: [ client close ]. - result ifNil: [ NetworkError signal: 'Could not access ', location ]. - ^result! Item was added: + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') ----- + webClientDo: aBlock + + | client attemptsLeft response result | + self class useSharedWebClientInstance ifTrue: [ + "Acquire webClient by atomically storing it in the client variable and setting its value to nil." + client := webClient. + webClient := nil ]. + + client + ifNil: [ client := WebClient new ] + ifNotNil: [ + "Attempt to avoid an error by recreating the underlying stream." + client isConnected ifFalse: [ client close ] ]. + + attemptsLeft := 3. + response := nil. + [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ + response := [ aBlock value: client ] + on: NetworkError + do: [ :error | + attemptsLeft = 0 ifTrue: [ error pass ]. + (3 - attemptsLeft) seconds asDelay wait. + attemptsLeft := attemptsLeft - 1. + nil "The response" ] ]. + + result := (response code between: 200 and: 299) + ifFalse: [ + response content. "Make sure content is read." + nil ] + ifTrue: [ + (RWBinaryOrTextStream with: ( + response contentWithProgress: [ :total :amount | + HTTPProgress new + total: total; + amount: amount; + signal ])) reset ]. + + self class useSharedWebClientInstance + ifTrue: [ + "Save the WebClient instance for reuse, but only if there is no client cached." + webClient + ifNil: [ webClient := client ] + ifNotNil: [ client close ] ] + ifFalse: [ client close ]. + + result ifNil: [ NetworkError signal: 'Could not access ', location ]. + ^result! Item was changed: ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in category 'private') ----- writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock + + | stream urlString | - | stream response statusLine code | stream := RWBinaryOrTextStream on: String new. aBlock value: stream. + urlString := self urlForFileNamed: aString. + urlString := self class rewriteUrl: urlString forDownload: false. + ^self displayProgress: 'Uploading ', aString during: [ + self webClientDo: [ :client | + client + username: self user; + password: self password; + httpPut: urlString + content: stream contents + type: nil + do: [ :request | + request + headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; + headerAt: 'Connection' put: 'Keep-Alive'; + headerAt: 'Accept' put: '*/*' ] ] ]! - self displayProgress: 'Uploading ', aString during:[ - response := HTTPSocket - httpPut: stream contents - to: (self urlForFileNamed: aString) - user: self user - passwd: self password. - ]. - "More robust handling of HTTP responses. Instead of enumerating - all possible return codes and http versions, do a quick parse" - (response beginsWith: 'HTTP/') ifTrue:[ - "Looks like an HTTP header, not some error message" - statusLine := response copyUpTo: Character cr. - code := [(statusLine findTokens: ' ') second asInteger] on: Error do:[]. - ]. - (code isInteger and:[code between: 200 and: 299]) - ifFalse:[self error: response].! From commits at source.squeak.org Sun Sep 20 19:34:37 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 20 Sep 2020 19:34:37 0000 Subject: [squeak-dev] The Trunk: Monticello-ct.727.mcz Message-ID: Levente Uzonyi uploaded a new version of Monticello to project The Trunk: http://source.squeak.org/trunk/Monticello-ct.727.mcz ==================== Summary ==================== Name: Monticello-ct.727 Author: ct Time: 20 August 2020, 2:38:27.66964 pm UUID: b81a07b6-cb93-6f48-847e-dbe86460d69f Ancestors: Monticello-cmm.726 Complements 60Deprecated-ct.80 (deprecation #doWithIndex: & Co.). =============== Diff against Monticello-cmm.726 =============== Item was changed: ----- Method: MCTool>>buildWith: (in category 'toolbuilder') ----- buildWith: builder | windowBuilder | windowBuilder := MCToolWindowBuilder builder: builder tool: self. self widgetSpecs do: [:spec | | send fractions offsets | send := spec first. fractions := (spec at: 2 ifAbsent: [#(0 0 1 1)]) copy. offsets := (spec at: 3 ifAbsent: [#(0 0 0 0)]) copy. + fractions withIndexDo: [:numberOrSymbol :index | - fractions doWithIndex: [:numberOrSymbol :index | numberOrSymbol isSymbol ifTrue: [fractions at: index put: (self perform: numberOrSymbol)]]. + offsets withIndexDo: [:numberOrSymbol :index | - offsets doWithIndex: [:numberOrSymbol :index | numberOrSymbol isSymbol ifTrue: [offsets at: index put: (self perform: numberOrSymbol)]]. windowBuilder frame: (LayoutFrame fractions: (fractions first @ fractions second corner: fractions third @ fractions fourth) offsets: (offsets first @ offsets second corner: offsets third @ offsets fourth)). windowBuilder perform: send first withArguments: send allButFirst]. ^ windowBuilder build ! From commits at source.squeak.org Sun Sep 20 19:34:49 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 20 Sep 2020 19:34:49 0000 Subject: [squeak-dev] The Trunk: Monticello-ul.726.mcz Message-ID: Levente Uzonyi uploaded a new version of Monticello to project The Trunk: http://source.squeak.org/trunk/Monticello-ul.726.mcz ==================== Summary ==================== Name: Monticello-ul.726 Author: ul Time: 29 June 2020, 1:39:36.986367 am UUID: b64f6b16-96de-40ed-aa9a-2c0f625bfcb1 Ancestors: Monticello-mt.725 - potentially speed up MCDirectoryRepository >> #includesVersionNamed: when the repository doesn't have version names cached - cache self size in MCVersionName >> #versionName =============== Diff against Monticello-mt.725 =============== Item was added: + ----- Method: MCDirectoryRepository>>includesVersionNamed: (in category 'versions') ----- + includesVersionNamed: aString + + | comparable | + comparable := ((aString endsWith: '.mcz') and: [ aString size > 4 ]) + ifTrue: [ aString allButLast: 4 ] + ifFalse: [ aString ]. + allVersionNamesCache ifNil: [ + "Instead of reading the contents of the entire directory in #allVersionNames, look up a single .mcz file. + This is just an optimization. If the file does not exist, the version may still be there as an mcd." + (directory fileExists: comparable, '.mcz') ifTrue: [ ^true ] ]. + ^ self allVersionNames includes: comparable! Item was changed: ----- Method: MCVersionName>>versionName (in category 'accessing') ----- versionName "Answer my version name as a ByteString, without the file suffix or any ancestor-attributes." | end | self isEmpty ifTrue: [^ String empty]. end := self indexOf: $( ifAbsent: [ + | size | + size := self size. + (size > 4 + and: [ (self at: size - 3) == $. + and: [ (self at: size - 2) == $m + and: [ (self at: size - 1) == $c ] ] ]) + ifTrue: [size - 3] + ifFalse: [size + 1]]. - (self size > 4 - and: [ (self at: self size - 3) == $. - and: [ (self at: self size - 2) == $m - and: [ (self at: self size - 1) == $c ] ] ]) - ifTrue: [self size - 3] - ifFalse: [self size + 1]]. ^self first: end - 1! From leves at caesar.elte.hu Sun Sep 20 19:36:29 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 20 Sep 2020 21:36:29 +0200 (CEST) Subject: [squeak-dev] The Inbox: Monticello-ul.727.mcz In-Reply-To: <8330c5bc89b5482db98e249b35110361@student.hpi.uni-potsdam.de> References: <1ae1c3850d3548dab48fe0da68bffac4@student.hpi.uni-potsdam.de>, <8330c5bc89b5482db98e249b35110361@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, Done. Along with Monticello-ct.726 and Monticello-ul.726. Levente On Sat, 19 Sep 2020, Thiede, Christoph wrote: > > Hi Levente, > > > alright, then please go ahead, I'm looking forward to seeing this in the Trunk! :-) > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Levente Uzonyi > Gesendet: Donnerstag, 17. September 2020 12:49:28 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] The Inbox: Monticello-ul.727.mcz   > Hi Christoph, > > On Thu, 17 Sep 2020, Thiede, Christoph wrote: > > > > > Very nice idea! :-) If I wanted to hijack an image, these URLRewriteRules would probably be my first approach - but security has never been an issue for Squeak, so I guess this is not a problem. > > If you can manipulate objects like URLRewriteRules, you have already > hijacked the image. > > > > > > > Does this also fix the problem with the classic HTTP URLs returned by the update map or will we still need to patch them on the server side? > > The urls in the update map are used to create repositories. That's why > simply changing the existing http repository urls to https doesn't > suffice, because the updater will see the http urls and create new > repositores with them if they are absent. > > So, this rewrite trick of Eliot works around the problem of the update > maps as well. > > > > > > > And one last question regarding to your tests in the method comment of #rewriteUrl:forDownload:: Couldn't you put them into a real test case? I'm pretty sure that not everyone will run these out-commented tests manually, > and > > it would be a pity not to automate them. > > I left the possibility to change the rewrite rules to whatever you want. > If there were a test case with those asserts in the comment, the test > would start failing as soon as you changed the rules. > Thought it's possible to create a test case which temporarily resets to > the default rules and tests them. > > > Levente > > > > > > > Best, > > > > Christoph > > > >________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > _ > > Von: Squeak-dev im Auftrag von commits at source.squeak.org > > Gesendet: Donnerstag, 17. September 2020 01:55:41 > > An: squeak-dev at lists.squeakfoundation.org > > Betreff: [squeak-dev] The Inbox: Monticello-ul.727.mcz   > > Levente Uzonyi uploaded a new version of Monticello to project The Inbox: > > http://source.squeak.org/inbox/Monticello-ul.727.mcz > > > > ==================== Summary ==================== > > > > Name: Monticello-ul.727 > > Author: ul > > Time: 17 September 2020, 1:54:51.056164 am > > UUID: ad776836-42eb-4aa2-b788-f10dd9e07da2 > > Ancestors: Monticello-cmm.726 > > > > MCHttpRepository changes: > > - before up- or downloading files, transform the urls using #rewriteUrl:forDownload:. The default rules (see #urlRewriteRules) switch from http to https for source.squeak.org and squeaksource.com, and switch to the the > static > > smalltalkhub site for downloads. The url rewriting is Eliot's idea, but this implementation uses a list of rewrite rules instead of a dictionary-based mapping. > > - use WebClient (and the shared webclient instance) for uploads too > > - retry down/uploading with WebClient at most 3 times. This should work around the case where the underlying socket was closed but the state of the socket has not been updated in Squeak. > > - use https in #creationTemplate > > > > =============== Diff against Monticello-cmm.726 =============== > > > > Item was changed: > >   MCFileBasedRepository subclass: #MCHttpRepository > >          instanceVariableNames: 'location user password readerCache indexed webClient' > > +        classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' > > -        classVariableNames: 'UseSharedWebClientInstance' > >          poolDictionaries: '' > >          category: 'Monticello-Repositories'! > > > > Item was changed: > >   ----- Method: MCHttpRepository class>>creationTemplate (in category 'ui-support') ----- > >   creationTemplate > > +        ^self creationTemplateLocation: 'https://www.squeaksource.com/ProjectName' > > -        ^self creationTemplateLocation: 'http://www.squeaksource.com/ProjectName' > >                  user: 'squeak' > >                  password: 'squeak' > >   ! > > > > Item was added: > > + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in category 'url rewrite') ----- > > + rewriteUrl: aString forDownload: forDownload > > + > > +        | result | > > +        result := aString. > > +        self urlRewriteRules groupsDo: [ :regexString :replacement :downloadOnly | > > +                (forDownload or: [ downloadOnly not ])  ifTrue: [ > > +                        result := result copyWithRegex: regexString matchesReplacedWith: replacement ] ]. > > +        ^result > > +        > > + " > > + self assert:  'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). > > + self assert:  'https://squeaksource.com/foo/bar?baz=1' = (self rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). > > + self assert:  'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). > > + self assert:  'https://source.squeak.org/foo/bar?baz=1' = (self rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). > > + self assert:  'http://static.smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). > > + self assert:  'http://smalltalkhub.com/foo/bar?baz=1' = (self rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). > > + "! > > > > Item was added: > > + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url rewrite') ----- > > + urlRewriteRules > > + > > +        ^URLRewriteRules ifNil: [ > > +                URLRewriteRules := #( > > +                        "Regex to be replaced"  "static replacement string"     "download only" > > +                        '^http\://source\.squeak\.org/' 'https://source.squeak.org/' false > > +                        '^http\://squeaksource\.com/' 'https://squeaksource.com/' false > > +                        '^http\://www.squeaksource\.com/' 'https://www.squeaksource.com/' false > > +                        '^http\://smalltalkhub.com/' 'http://static.smalltalkhub.com/' true     > > +                )  asOrderedCollection ]! > > > > Item was changed: > >   ----- Method: MCHttpRepository>>httpGet:arguments: (in category 'private') ----- > >   httpGet: url arguments: arguments > >   > > +        | urlString | > > -        | progress urlString client  response result | > > -        progress := [ :total :amount | > > -                HTTPProgress new > > -                        total: total; > > -                        amount: amount; > > -                        signal: 'Downloading...' ]. > >          urlString := arguments > >                  ifNil: [ url ] > >                  ifNotNil: [ > >                          | queryString | > >                          queryString := WebUtils encodeUrlEncodedForm: arguments. > >                          (url includes: $?) > >                                  ifTrue: [ url, '&', queryString ] > >                                  ifFalse: [ url, '?', queryString ] ]. > > +        urlString := self class rewriteUrl: urlString forDownload: true. > > +        ^self webClientDo: [ :client | > > +                client > > +                        username: self user; > > +                        password: self password; > > +                        httpGet: urlString do: [ :request | > > +                                request > > +                                        headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > > +                                        headerAt: 'Connection' put: 'Keep-Alive'; > > +                                        headerAt: 'Accept' put: '*/*' ] ]! > > -        self class useSharedWebClientInstance ifTrue: [ > > -                "Acquire webClient by atomically storing it in the client variable and setting its value to nil." > > -                client := webClient. > > -                webClient := nil ]. > > -        client > > -                ifNil: [ client := WebClient new ] > > -                ifNotNil: [ > > -                        "Attempt to avoid an error on windows by recreating the underlying stream." > > -                        client isConnected ifFalse: [ client close ] ]. > > -        response := client > > -                username: self user; > > -                password: self password; > > -                httpGet: urlString do: [ :request | > > -                        request > > -                                headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > > -                                headerAt: 'Connection' put: 'Keep-Alive'; > > -                                headerAt: 'Accept' put: '*/*' ]. > > -        result := (response code between: 200 and: 299) > > -                ifFalse: [ > > -                        response content. "Make sure content is read." > > -                        nil ] > > -                ifTrue: [ (RWBinaryOrTextStream with: (response contentWithProgress: progress)) reset ]. > > -        self class useSharedWebClientInstance > > -                ifTrue: [ > > -                        "Save the WebClient instance for reuse, but only if there is no client cached." > > -                        webClient  > > -                                ifNil: [ webClient := client ] > > -                                ifNotNil: [ client close ] ] > > -                ifFalse: [ client close ]. > > -        result ifNil: [ NetworkError signal: 'Could not access ', location ]. > > -        ^result! > > > > Item was added: > > + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') ----- > > + webClientDo: aBlock > > + > > +        | client attemptsLeft response result | > > +        self class useSharedWebClientInstance ifTrue: [ > > +                "Acquire webClient by atomically storing it in the client variable and setting its value to nil." > > +                client := webClient. > > +                webClient := nil ]. > > +        > > +        client > > +                ifNil: [ client := WebClient new ] > > +                ifNotNil: [ > > +                        "Attempt to avoid an error by recreating the underlying stream." > > +                        client isConnected ifFalse: [ client close ] ]. > > +                > > +        attemptsLeft := 3. > > +        response := nil. > > +        [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ > > +                response := [ aBlock value: client ] > > +                        on: NetworkError > > +                        do: [ :error | > > +                                attemptsLeft = 0 ifTrue: [ error pass ]. > > +                                (3 - attemptsLeft) seconds asDelay wait. > > +                                attemptsLeft := attemptsLeft - 1. > > +                                nil "The response" ] ]. > > +        > > +        result := (response code between: 200 and: 299) > > +                ifFalse: [ > > +                        response content. "Make sure content is read." > > +                        nil ] > > +                ifTrue: [ > > +                        (RWBinaryOrTextStream with: ( > > +                                response contentWithProgress:  [ :total :amount | > > +                                        HTTPProgress new > > +                                                total: total; > > +                                                amount: amount; > > +                                                signal ])) reset ]. > > + > > +        self class useSharedWebClientInstance > > +                ifTrue: [ > > +                        "Save the WebClient instance for reuse, but only if there is no client cached." > > +                        webClient  > > +                                ifNil: [ webClient := client ] > > +                                ifNotNil: [ client close ] ] > > +                ifFalse: [ client close ]. > > + > > +        result ifNil: [ NetworkError signal: 'Could not access ', location ]. > > +        ^result! > > > > Item was changed: > >   ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in category 'private') ----- > >   writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock > > + > > +        | stream urlString | > > -        | stream response statusLine code | > >          stream := RWBinaryOrTextStream on: String new. > >          aBlock value: stream. > > +        urlString := self urlForFileNamed: aString. > > +        urlString := self class rewriteUrl: urlString forDownload: false. > > +        ^self displayProgress: 'Uploading ', aString during: [ > > +                self webClientDo: [ :client | > > +                        client > > +                                username: self user; > > +                                password: self password; > > +                                httpPut: urlString > > +                                        content: stream contents > > +                                        type: nil > > +                                        do: [ :request | > > +                                                request > > +                                                        headerAt: 'Authorization' put: 'Basic ', (self user, ':', self password) base64Encoded; > > +                                                        headerAt: 'Connection' put: 'Keep-Alive'; > > +                                                        headerAt: 'Accept' put: '*/*'  ] ] ]! > > -        self displayProgress: 'Uploading ', aString during:[ > > -                response := HTTPSocket > > -                                        httpPut: stream contents > > -                                        to: (self urlForFileNamed: aString) > > -                                        user: self user > > -                                        passwd: self password. > > -        ]. > > -        "More robust handling of HTTP responses. Instead of enumerating > > -        all possible return codes and http versions, do a quick parse" > > -        (response beginsWith: 'HTTP/') ifTrue:[ > > -                "Looks like an HTTP header, not some error message" > > -                statusLine := response copyUpTo: Character cr. > > -                code := [(statusLine findTokens: ' ') second asInteger] on: Error do:[]. > > -        ]. > > -        (code isInteger and:[code between: 200 and: 299]) > > -                ifFalse:[self error: response].! > > > > > > > > > > From leves at caesar.elte.hu Sun Sep 20 19:36:57 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Sun, 20 Sep 2020 21:36:57 +0200 (CEST) Subject: [squeak-dev] The Inbox: Monticello-ul.727.mcz In-Reply-To: References: <1ae1c3850d3548dab48fe0da68bffac4@student.hpi.uni-potsdam.de>, <8330c5bc89b5482db98e249b35110361@student.hpi.uni-potsdam.de> Message-ID: On Sun, 20 Sep 2020, Levente Uzonyi wrote: > Hi Christoph, > > Done. Along with Monticello-ct.726 and Monticello-ul.726. I mean Monticello-ct.727... > > > Levente > > On Sat, 19 Sep 2020, Thiede, Christoph wrote: > >> >> Hi Levente, >> >> >> alright, then please go ahead, I'm looking forward to seeing this in the >> Trunk! :-) >> >> >> Best, >> >> Christoph >> >> _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ >> Von: Squeak-dev im Auftrag >> von Levente Uzonyi >> Gesendet: Donnerstag, 17. September 2020 12:49:28 >> An: The general-purpose Squeak developers list >> Betreff: Re: [squeak-dev] The Inbox: Monticello-ul.727.mcz   >> Hi Christoph, >> >> On Thu, 17 Sep 2020, Thiede, Christoph wrote: >> >> > >> > Very nice idea! :-) If I wanted to hijack an image, these URLRewriteRules >> would probably be my first approach - but security has never been an issue >> for Squeak, so I guess this is not a problem. >> >> If you can manipulate objects like URLRewriteRules, you have already >> hijacked the image. >> >> > >> > >> > Does this also fix the problem with the classic HTTP URLs returned by the >> update map or will we still need to patch them on the server side? >> >> The urls in the update map are used to create repositories. That's why >> simply changing the existing http repository urls to https doesn't >> suffice, because the updater will see the http urls and create new >> repositores with them if they are absent. >> >> So, this rewrite trick of Eliot works around the problem of the update >> maps as well. >> >> > >> > >> > And one last question regarding to your tests in the method comment of >> #rewriteUrl:forDownload:: Couldn't you put them into a real test case? I'm >> pretty sure that not everyone will run these out-commented tests manually, >> and >> > it would be a pity not to automate them. >> >> I left the possibility to change the rewrite rules to whatever you want. >> If there were a test case with those asserts in the comment, the test >> would start failing as soon as you changed the rules. >> Thought it's possible to create a test case which temporarily resets to >> the default rules and tests them. >> >> >> Levente >> >> > >> > >> > Best, >> > >> > Christoph >> > >> >________________________________________________________________________________________________________________________________________________________________________________________________________________________________ >> _ >> > Von: Squeak-dev im >> Auftrag von commits at source.squeak.org >> > Gesendet: Donnerstag, 17. September 2020 01:55:41 >> > An: squeak-dev at lists.squeakfoundation.org >> > Betreff: [squeak-dev] The Inbox: Monticello-ul.727.mcz   >> > Levente Uzonyi uploaded a new version of Monticello to project The Inbox: >> > http://source.squeak.org/inbox/Monticello-ul.727.mcz >> > >> > ==================== Summary ==================== >> > >> > Name: Monticello-ul.727 >> > Author: ul >> > Time: 17 September 2020, 1:54:51.056164 am >> > UUID: ad776836-42eb-4aa2-b788-f10dd9e07da2 >> > Ancestors: Monticello-cmm.726 >> > >> > MCHttpRepository changes: >> > - before up- or downloading files, transform the urls using >> #rewriteUrl:forDownload:. The default rules (see #urlRewriteRules) switch >> from http to https for source.squeak.org and squeaksource.com, and switch >> to the the >> static >> > smalltalkhub site for downloads. The url rewriting is Eliot's idea, but >> this implementation uses a list of rewrite rules instead of a >> dictionary-based mapping. >> > - use WebClient (and the shared webclient instance) for uploads too >> > - retry down/uploading with WebClient at most 3 times. This should work >> around the case where the underlying socket was closed but the state of the >> socket has not been updated in Squeak. >> > - use https in #creationTemplate >> > >> > =============== Diff against Monticello-cmm.726 =============== >> > >> > Item was changed: >> >   MCFileBasedRepository subclass: #MCHttpRepository >> >          instanceVariableNames: 'location user password readerCache >> indexed webClient' >> > +        classVariableNames: 'URLRewriteRules UseSharedWebClientInstance' >> > -        classVariableNames: 'UseSharedWebClientInstance' >> >          poolDictionaries: '' >> >          category: 'Monticello-Repositories'! >> > >> > Item was changed: >> >   ----- Method: MCHttpRepository class>>creationTemplate (in category >> 'ui-support') ----- >> >   creationTemplate >> > +        ^self creationTemplateLocation: >> 'https://www.squeaksource.com/ProjectName' >> > -        ^self creationTemplateLocation: >> 'http://www.squeaksource.com/ProjectName' >> >                  user: 'squeak' >> >                  password: 'squeak' >> >   ! >> > >> > Item was added: >> > + ----- Method: MCHttpRepository class>>rewriteUrl:forDownload: (in >> category 'url rewrite') ----- >> > + rewriteUrl: aString forDownload: forDownload >> > + >> > +        | result | >> > +        result := aString. >> > +        self urlRewriteRules groupsDo: [ :regexString :replacement >> :downloadOnly | >> > +                (forDownload or: [ downloadOnly not ])  ifTrue: [ >> > +                        result := result copyWithRegex: regexString >> matchesReplacedWith: replacement ] ]. >> > +        ^result >> > +        >> > + " >> > + self assert:  'https://squeaksource.com/foo/bar?baz=1' = (self >> rewriteUrl: 'http://squeaksource.com/foo/bar?baz=1' forDownload: true). >> > + self assert:  'https://squeaksource.com/foo/bar?baz=1' = (self >> rewriteUrl: 'https://squeaksource.com/foo/bar?baz=1' forDownload: true). >> > + self assert:  'https://source.squeak.org/foo/bar?baz=1' = (self >> rewriteUrl: 'http://source.squeak.org/foo/bar?baz=1' forDownload: true). >> > + self assert:  'https://source.squeak.org/foo/bar?baz=1' = (self >> rewriteUrl: 'https://source.squeak.org/foo/bar?baz=1' forDownload: true). >> > + self assert:  'http://static.smalltalkhub.com/foo/bar?baz=1' = (self >> rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: true). >> > + self assert:  'http://smalltalkhub.com/foo/bar?baz=1' = (self >> rewriteUrl: 'http://smalltalkhub.com/foo/bar?baz=1' forDownload: false). >> > + "! >> > >> > Item was added: >> > + ----- Method: MCHttpRepository class>>urlRewriteRules (in category 'url >> rewrite') ----- >> > + urlRewriteRules >> > + >> > +        ^URLRewriteRules ifNil: [ >> > +                URLRewriteRules := #( >> > +                        "Regex to be replaced"  "static replacement >> string"     "download only" >> > +                        '^http\://source\.squeak\.org/' >> 'https://source.squeak.org/' false >> > +                        '^http\://squeaksource\.com/' >> 'https://squeaksource.com/' false >> > +                        '^http\://www.squeaksource\.com/' >> 'https://www.squeaksource.com/' false >> > +                        '^http\://smalltalkhub.com/' >> 'http://static.smalltalkhub.com/' true     >> > +                )  asOrderedCollection ]! >> > >> > Item was changed: >> >   ----- Method: MCHttpRepository>>httpGet:arguments: (in category >> 'private') ----- >> >   httpGet: url arguments: arguments >> >   >> > +        | urlString | >> > -        | progress urlString client  response result | >> > -        progress := [ :total :amount | >> > -                HTTPProgress new >> > -                        total: total; >> > -                        amount: amount; >> > -                        signal: 'Downloading...' ]. >> >          urlString := arguments >> >                  ifNil: [ url ] >> >                  ifNotNil: [ >> >                          | queryString | >> >                          queryString := WebUtils encodeUrlEncodedForm: >> arguments. >> >                          (url includes: $?) >> >                                  ifTrue: [ url, '&', queryString ] >> >                                  ifFalse: [ url, '?', queryString ] ]. >> > +        urlString := self class rewriteUrl: urlString forDownload: true. >> > +        ^self webClientDo: [ :client | >> > +                client >> > +                        username: self user; >> > +                        password: self password; >> > +                        httpGet: urlString do: [ :request | >> > +                                request >> > +                                        headerAt: 'Authorization' put: >> 'Basic ', (self user, ':', self password) base64Encoded; >> > +                                        headerAt: 'Connection' put: >> 'Keep-Alive'; >> > +                                        headerAt: 'Accept' put: '*/*' ] >> ]! >> > -        self class useSharedWebClientInstance ifTrue: [ >> > -                "Acquire webClient by atomically storing it in the >> client variable and setting its value to nil." >> > -                client := webClient. >> > -                webClient := nil ]. >> > -        client >> > -                ifNil: [ client := WebClient new ] >> > -                ifNotNil: [ >> > -                        "Attempt to avoid an error on windows by >> recreating the underlying stream." >> > -                        client isConnected ifFalse: [ client close ] ]. >> > -        response := client >> > -                username: self user; >> > -                password: self password; >> > -                httpGet: urlString do: [ :request | >> > -                        request >> > -                                headerAt: 'Authorization' put: 'Basic ', >> (self user, ':', self password) base64Encoded; >> > -                                headerAt: 'Connection' put: >> 'Keep-Alive'; >> > -                                headerAt: 'Accept' put: '*/*' ]. >> > -        result := (response code between: 200 and: 299) >> > -                ifFalse: [ >> > -                        response content. "Make sure content is read." >> > -                        nil ] >> > -                ifTrue: [ (RWBinaryOrTextStream with: (response >> contentWithProgress: progress)) reset ]. >> > -        self class useSharedWebClientInstance >> > -                ifTrue: [ >> > -                        "Save the WebClient instance for reuse, but only >> if there is no client cached." >> > -                        webClient  >> > -                                ifNil: [ webClient := client ] >> > -                                ifNotNil: [ client close ] ] >> > -                ifFalse: [ client close ]. >> > -        result ifNil: [ NetworkError signal: 'Could not access ', >> location ]. >> > -        ^result! >> > >> > Item was added: >> > + ----- Method: MCHttpRepository>>webClientDo: (in category 'private') >> ----- >> > + webClientDo: aBlock >> > + >> > +        | client attemptsLeft response result | >> > +        self class useSharedWebClientInstance ifTrue: [ >> > +                "Acquire webClient by atomically storing it in the >> client variable and setting its value to nil." >> > +                client := webClient. >> > +                webClient := nil ]. >> > +        >> > +        client >> > +                ifNil: [ client := WebClient new ] >> > +                ifNotNil: [ >> > +                        "Attempt to avoid an error by recreating the >> underlying stream." >> > +                        client isConnected ifFalse: [ client close ] ]. >> > +                >> > +        attemptsLeft := 3. >> > +        response := nil. >> > +        [ response isNil and: [ attemptsLeft > 0 ] ] whileTrue: [ >> > +                response := [ aBlock value: client ] >> > +                        on: NetworkError >> > +                        do: [ :error | >> > +                                attemptsLeft = 0 ifTrue: [ error pass ]. >> > +                                (3 - attemptsLeft) seconds asDelay wait. >> > +                                attemptsLeft := attemptsLeft - 1. >> > +                                nil "The response" ] ]. >> > +        >> > +        result := (response code between: 200 and: 299) >> > +                ifFalse: [ >> > +                        response content. "Make sure content is read." >> > +                        nil ] >> > +                ifTrue: [ >> > +                        (RWBinaryOrTextStream with: ( >> > +                                response contentWithProgress:  [ :total >> :amount | >> > +                                        HTTPProgress new >> > +                                                total: total; >> > +                                                amount: amount; >> > +                                                signal ])) reset ]. >> > + >> > +        self class useSharedWebClientInstance >> > +                ifTrue: [ >> > +                        "Save the WebClient instance for reuse, but only >> if there is no client cached." >> > +                        webClient  >> > +                                ifNil: [ webClient := client ] >> > +                                ifNotNil: [ client close ] ] >> > +                ifFalse: [ client close ]. >> > + >> > +        result ifNil: [ NetworkError signal: 'Could not access ', >> location ]. >> > +        ^result! >> > >> > Item was changed: >> >   ----- Method: MCHttpRepository>>writeStreamForFileNamed:replace:do: (in >> category 'private') ----- >> >   writeStreamForFileNamed: aString replace: ignoreBoolean do: aBlock >> > + >> > +        | stream urlString | >> > -        | stream response statusLine code | >> >          stream := RWBinaryOrTextStream on: String new. >> >          aBlock value: stream. >> > +        urlString := self urlForFileNamed: aString. >> > +        urlString := self class rewriteUrl: urlString forDownload: >> false. >> > +        ^self displayProgress: 'Uploading ', aString during: [ >> > +                self webClientDo: [ :client | >> > +                        client >> > +                                username: self user; >> > +                                password: self password; >> > +                                httpPut: urlString >> > +                                        content: stream contents >> > +                                        type: nil >> > +                                        do: [ :request | >> > +                                                request >> > +                                                        headerAt: >> 'Authorization' put: 'Basic ', (self user, ':', self password) >> base64Encoded; >> > +                                                        headerAt: >> 'Connection' put: 'Keep-Alive'; >> > +                                                        headerAt: >> 'Accept' put: '*/*'  ] ] ]! >> > -        self displayProgress: 'Uploading ', aString during:[ >> > -                response := HTTPSocket >> > -                                        httpPut: stream contents >> > -                                        to: (self urlForFileNamed: >> aString) >> > -                                        user: self user >> > -                                        passwd: self password. >> > -        ]. >> > -        "More robust handling of HTTP responses. Instead of enumerating >> > -        all possible return codes and http versions, do a quick parse" >> > -        (response beginsWith: 'HTTP/') ifTrue:[ >> > -                "Looks like an HTTP header, not some error message" >> > -                statusLine := response copyUpTo: Character cr. >> > -                code := [(statusLine findTokens: ' ') second asInteger] >> on: Error do:[]. >> > -        ]. >> > -        (code isInteger and:[code between: 200 and: 299]) >> > -                ifFalse:[self error: response].! >> > >> > >> > >> > >> >> > From asqueaker at gmail.com Sun Sep 20 21:14:05 2020 From: asqueaker at gmail.com (Chris Muller) Date: Sun, 20 Sep 2020 16:14:05 -0500 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: > > > For example, imagine the power of having EVERY PIXEL on your desktop > being ready for ANY INPUT, regardless of window z-ordering, simply by > moving pointer there and pressing a mouse or keyboard button. > > > > Here you see, we differ. It's likely partly due to growing up using RISC > OS and the original ST-80 UI ... I grew up using MS Windows, the beast that made "click for focus" the defacto disaster it is today. Not everyone is willing to change how they work, but a few years ago I got a tendonitis that *really* made me notice that I was doing, literally, hundreds upon hundreds, of extra clicks every day for no other purpose than reassigning keyboard focus, which, as you know, have to even be "strategic clicks" (window title bars, borders, etc.) to avoid suffering unwanted selection change, otherwise having to click yet again just to "get back" to what I as looking at. Not only the pain, but I realized what a total time sink it was. The only way to escape was I had to be willing to change, which actually turned out a lot easier than I thought... -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sun Sep 20 21:35:09 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 20 Sep 2020 14:35:09 -0700 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: References: Message-ID: <17DE76DF-54DD-4826-BF26-65CA143F7404@gmail.com> Hi Levente, Hi Tty, > On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: > > Hi Tim, > > Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. > > That Xtreams dependency on that library is pretty much pointless: > - only one example grammar (PEGWikiGenerator) uses Monty's XML package > - the example uses it to produce web content as XHTML which is rather unusual for two reasons: > - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. > - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) > - the example does not use any features to justify using the not-so-Squeak-compatible XML package > - that example grammar only uses it for the sake of making it Pharo-compatible > > Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. > So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. > > IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo version of the example, making the example use Squeak's XML package in Squeak. > The latter also involves more work, and a more compilcated Metacello configuration. I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework. Cc’ing Ron as he knows the reality far better than I (hi Ron). > >> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: >> >> Hi Folks, >> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 >> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. >> >> (XMLElement name:'br' ) printString '
' >> vs >> (XMLElement name:'br' ) printString '
' >> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. >> Checking the Monticello, the 6.0 >> >> XML-Parser (monty.428) >> while the 5.3 is >> >> XML-Parser (monty.428 , ul.44) > > You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. > >> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. >> Being new to this, who knows how I managed that. > > No. The two packages are not compatible with each other and you have "both" in your image at the same time. > >> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. >> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. >> Which should I use? > > The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. > >> Should I bring in the ul.44? and how do I do that? >> Should I revise my tests (there are not too many) to insert that extra space. > > If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). > > > Levente > >> thank you for your time. > From leves at caesar.elte.hu Sun Sep 20 22:12:08 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Mon, 21 Sep 2020 00:12:08 +0200 (CEST) Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <17DE76DF-54DD-4826-BF26-65CA143F7404@gmail.com> References: <17DE76DF-54DD-4826-BF26-65CA143F7404@gmail.com> Message-ID: H Eliot, On Sun, 20 Sep 2020, Eliot Miranda wrote: > Hi Levente, Hi Tty, > >> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: >> >> Hi Tim, >> >> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. >> >> That Xtreams dependency on that library is pretty much pointless: >> - only one example grammar (PEGWikiGenerator) uses Monty's XML package >> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: >> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. >> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) >> - the example does not use any features to justify using the not-so-Squeak-compatible XML package >> - that example grammar only uses it for the sake of making it Pharo-compatible >> >> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. >> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. >> >> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo version of the example, making the example use Squeak's XML package in Squeak. >> The latter also involves more work, and a more compilcated Metacello configuration. > > I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework. Cc’ing Ron as he knows the reality far better than I (hi Ron). Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. Levente > >> >>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: >>> >>> Hi Folks, >>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 >>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. >>> >>> (XMLElement name:'br' ) printString '
' >>> vs >>> (XMLElement name:'br' ) printString '
' >>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. >>> Checking the Monticello, the 6.0 >>> >>> XML-Parser (monty.428) >>> while the 5.3 is >>> >>> XML-Parser (monty.428 , ul.44) >> >> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. >> >>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. >>> Being new to this, who knows how I managed that. >> >> No. The two packages are not compatible with each other and you have "both" in your image at the same time. >> >>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. >>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. >>> Which should I use? >> >> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. >> >>> Should I bring in the ul.44? and how do I do that? >>> Should I revise my tests (there are not too many) to insert that extra space. >> >> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). >> >> >> Levente >> >>> thank you for your time. >> From eliot.miranda at gmail.com Sun Sep 20 22:30:56 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Sun, 20 Sep 2020 15:30:56 -0700 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: References: Message-ID: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi wrote: > > H Eliot, > >> On Sun, 20 Sep 2020, Eliot Miranda wrote: >> >> Hi Levente, Hi Tty, >> >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: >>> Hi Tim, >>> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. >>> That Xtreams dependency on that library is pretty much pointless: >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package >>> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: >>> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. >>> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) >>> - the example does not use any features to justify using the not-so-Squeak-compatible XML package >>> - that example grammar only uses it for the sake of making it Pharo-compatible >>> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. >>> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. >>> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo version of the example, making the example use Squeak's XML package in Squeak. >>> The latter also involves more work, and a more compilcated Metacello configuration. >> >> I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework. Cc’ing Ron as he knows the reality far better than I (hi Ron). > > Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. Terf is built in Squeak 5.3. We want to move forward to trunk soon (we want to stay on trunk). I have zero desire to develop on Pharo. > > Levente > >> >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: >>>> Hi Folks, >>>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. >>>> >>>> (XMLElement name:'br' ) printString '
' >>>> vs >>>> (XMLElement name:'br' ) printString '
' >>>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. >>>> Checking the Monticello, the 6.0 >>>> XML-Parser (monty.428) >>>> while the 5.3 is >>>> >>>> XML-Parser (monty.428 , ul.44) >>> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. >>>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. >>>> Being new to this, who knows how I managed that. >>> No. The two packages are not compatible with each other and you have "both" in your image at the same time. >>>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. >>>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. >>>> Which should I use? >>> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. >>>> Should I bring in the ul.44? and how do I do that? >>>> Should I revise my tests (there are not too many) to insert that extra space. >>> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). >>> Levente >>>> thank you for your time. > From ron at usmedrec.com Mon Sep 21 13:12:24 2020 From: ron at usmedrec.com (Ron Teitelbaum) Date: Mon, 21 Sep 2020 09:12:24 -0400 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: On Sun, Sep 20, 2020 at 5:14 PM Chris Muller wrote: > > For example, imagine the power of having EVERY PIXEL on your desktop >> being ready for ANY INPUT, regardless of window z-ordering, simply by >> moving pointer there and pressing a mouse or keyboard button. >> > >> >> Here you see, we differ. It's likely partly due to growing up using RISC >> OS and the original ST-80 UI ... > > > I grew up using MS Windows, the beast that made "click for focus" the > defacto disaster it is today. Not everyone is willing to change how they > work, but a few years ago I got a tendonitis that *really* made me notice > that I was doing, literally, hundreds upon hundreds, of extra clicks every > day for no other purpose than reassigning keyboard focus, which, as you > know, have to even be "strategic clicks" (window title bars, borders, etc.) > to avoid suffering unwanted selection change, otherwise having to click yet > again just to "get back" to what I as looking at. Not only the pain, but I > realized what a total time sink it was. The only way to escape was I had > to be willing to change, which actually turned out a lot easier than I > thought... > > > Hover to select is one of the greatest causes of user confusion that I encounter. Most windows users are used to clicking and moving the cursor out of the way so they can see. Now imagine a text box. As a user you click on it so that you can type into it. You move your mouse away to type into the box BUT NOTHING HAPPENS. Why because your moving the mouse away from the text field took focus away from the text field. Or you click and start typing but your mouse pointer is in the way so you move it and now nothing you type goes into the field. We worked around this issue by locking some of the important fields when a user selects it but not all of them so it's not consistent. We also have the issue that if your mouse happens to be hovering above a button things work for a minute but then stop working because the button is capturing keystrokes. The user only moved their mouse. They didn't mean to hover over a button or they clicked a button and got the result they expected but had no reason to move their mouse cursor off the button afterwards. While I understand the unnecessary click argument and the harm that clicking does to your wrist, there is still a lot to be said for consistency, user understanding, and user experience. Just my 2c. Ron Teitelbaum -------------- next part -------------- An HTML attachment was scrubbed... URL: From nicolas.cellier.aka.nice at gmail.com Mon Sep 21 13:24:03 2020 From: nicolas.cellier.aka.nice at gmail.com (Nicolas Cellier) Date: Mon, 21 Sep 2020 15:24:03 +0200 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: Plus, moving away from host feel is disruptive... Unless we deliver a SqueakNOS. Le lun. 21 sept. 2020 à 15:12, Ron Teitelbaum a écrit : > > On Sun, Sep 20, 2020 at 5:14 PM Chris Muller wrote: > >> > For example, imagine the power of having EVERY PIXEL on your desktop >>> being ready for ANY INPUT, regardless of window z-ordering, simply by >>> moving pointer there and pressing a mouse or keyboard button. >>> > >>> >>> Here you see, we differ. It's likely partly due to growing up using RISC >>> OS and the original ST-80 UI ... >> >> >> I grew up using MS Windows, the beast that made "click for focus" the >> defacto disaster it is today. Not everyone is willing to change how they >> work, but a few years ago I got a tendonitis that *really* made me >> notice that I was doing, literally, hundreds upon hundreds, of extra clicks >> every day for no other purpose than reassigning keyboard focus, which, as >> you know, have to even be "strategic clicks" (window title bars, borders, >> etc.) to avoid suffering unwanted selection change, otherwise having to >> click yet again just to "get back" to what I as looking at. Not only the >> pain, but I realized what a total time sink it was. The only way to escape >> was I had to be willing to change, which actually turned out a lot easier >> than I thought... >> >> >> Hover to select is one of the greatest causes of user confusion that I > encounter. Most windows users are used to clicking and moving the cursor > out of the way so they can see. > > Now imagine a text box. As a user you click on it so that you can type > into it. You move your mouse away to type into the box BUT NOTHING > HAPPENS. Why because your moving the mouse away from the text field took > focus away from the text field. > > Or you click and start typing but your mouse pointer is in the way so you > move it and now nothing you type goes into the field. We worked around > this issue by locking some of the important fields when a user selects it > but not all of them so it's not consistent. > > We also have the issue that if your mouse happens to be hovering above a > button things work for a minute but then stop working because the button is > capturing keystrokes. The user only moved their mouse. They didn't mean to > hover over a button or they clicked a button and got the result they > expected but had no reason to move their mouse cursor off the button > afterwards. > > While I understand the unnecessary click argument and the harm that > clicking does to your wrist, there is still a lot to be said for > consistency, user understanding, and user experience. > > Just my 2c. > > Ron Teitelbaum > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Mon Sep 21 13:50:30 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Mon, 21 Sep 2020 15:50:30 +0200 Subject: [squeak-dev] highdpi testing In-Reply-To: <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> Message-ID: <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> Hi > On 19.09.2020, at 19:26, Thiede, Christoph wrote: > > Hi, > > ah, I had assumed something like this, thanks for fixing, Tom! :-) > > #actualScreenScaleFactor is a very helpful tool, but I do not yet see the whole idea of the changeset. > In what sense is this meant to be a complement of RealEstateAgent >> #scaleFactor or rather an orthogonal concept? > After loading the changeset and resetting my UI theme, the background image of the world disappeared and all new windows are huge, but they still have a tiny font - am I supposed to set the #scaleFactor manually (to 12, in my example)? > Where exactly can I see any components in the image that are upscaled by the new changeset? > These do something. Load in order: DpiAware-Kernel DpiAware-Morpic DpiAware-Display First two are harmless, third will do something. If nothing changes, do UserInterfaceTheme cleanUpAndReset. Issues ensue. - Layouting borks. The assert after the potential relayout self assert: (self hasProperty: #doLayoutAgain) not fails already in the docking bar… - Fonts with UI-Themes do not work. These are "Deep-referenced" to a pixel size (for Strike fonts) on creation, (so also during UserInterfaceTheme cleanUpAndReset). However, when the scale factor changes, the pixel-sized fonts don't match the point sizes anymore… This used to work pre-UI-Themes, as everyone requesting a Font did so by directly asking StrikeFont or TextStyle at the time the Font was used. Hummm.. Hope you enjoy. -t -------------- next part -------------- A non-text attachment was scrubbed... Name: DpiAware-Display.1.cs Type: application/octet-stream Size: 778 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: DpiAware-Kernel.1.cs Type: application/octet-stream Size: 4348 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: DpiAware-Morphic.1.cs Type: application/octet-stream Size: 5789 bytes Desc: not available URL: From ron at usmedrec.com Mon Sep 21 14:03:29 2020 From: ron at usmedrec.com (Ron Teitelbaum) Date: Mon, 21 Sep 2020 10:03:29 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> Message-ID: Hi All, Terf uses a modified version of XML-Parser based on trunk XML-Parser-ul.44 and XML-Explorer-topa.1. Levente Uzonyi >> Monty's XML implementation is way more complete than the one in the Trunk. How so? Ron Tetielbaum On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda wrote: > > > > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi > wrote: > > > > H Eliot, > > > >> On Sun, 20 Sep 2020, Eliot Miranda wrote: > >> > >> Hi Levente, Hi Tty, > >> > >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi > wrote: > >>> Hi Tim, > >>> Be very careful here. The Xtreams Metacello configuration pulls in > Monty's XML implementation along with the whole kitchen sink it depends on, > and leaves your image with a bunch of undeclared variables. > >>> That Xtreams dependency on that library is pretty much pointless: > >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package > >>> - the example uses it to produce web content as XHTML which is rather > unusual for two reasons: > >>> - the majority of web content is HTML5 nowadays. Some stats on the > internet say XHTML is still at 10% but without proof. > >>> - it uses an XML library to produce XHTML, but the two are > surprisingly not as compatible as you would think (see below) > >>> - the example does not use any features to justify using the > not-so-Squeak-compatible XML package > >>> - that example grammar only uses it for the sake of making it > Pharo-compatible > >>> Monty's XML implementation is way more complete than the one in the > Trunk, but it's not compatible with the one in the Trunk. > >>> So, anything that depends on Squeak's XML implementation will be > broken if you load Xtreams. > >>> IMO, the right solution is to either drop Pharo support for Xtreams, > as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 > the latest) or split the package, and have a Squeak and a Pharo version of > the example, making the example use Squeak's XML package in Squeak. > >>> The latter also involves more work, and a more compilcated Metacello > configuration. > >> > >> I’m pretty sure Terf uses the Squeak XML framework and I know that > we’re heavily dependent on XML. So let me make a plea for the latter. I > fear we would be heavily impacted by a change of XML framework. Cc’ing Ron > as he knows the reality far better than I (hi Ron). > > > > Does Terf use Pharo (version 4 or earlier)? If not, this change would > make no difference. > > Terf is built in Squeak 5.3. We want to move forward to trunk soon (we > want to stay on trunk). I have zero desire to develop on Pharo. > > > > > Levente > > > >> > >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: > >>>> Hi Folks, > >>>> I have pretty much succesfully imported XTreams and the XML-Parser > stuff to Squeak 6alpha. from Squeak5.3 > >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are > failing for the following reason. > >>>> > >>>> (XMLElement name:'br' ) printString '
' > >>>> vs > >>>> (XMLElement name:'br' ) printString '
' > >>>> Notice the latter one inserts an extra space after the 'br'...that is > the behavior on my 6.0. > >>>> Checking the Monticello, the 6.0 > >>>> XML-Parser (monty.428) > >>>> while the 5.3 is > >>>> > >>>> XML-Parser (monty.428 , ul.44) > >>> You have probably updated that image after loading Xtreams, and the > update process merged in the XML-Parsing package from the Trunk. > >>>> So, presumably having the extra ul.44 removes the space, but I have > no idea what the ul.44 is, why it is there, how it got > there....yada,yada,yada. > >>>> Being new to this, who knows how I managed that. > >>> No. The two packages are not compatible with each other and you have > "both" in your image at the same time. > >>>> before I mailed this, I checked at the w3 schools on the
tag and > it asserts it should not have the slash in it. w3 schools is not the xHTML > standard, so this may or may not be true. > >>>> FWIW, the
stuff works fine for me as does the
, but I do > not know if it will cause problems when I move on to XPath stuff later on > the resulting xHTML docs. > >>>> Which should I use? > >>> The space is required for XHTML. It is not required for XML. You're > using an XML library to generate XHTML, which, as you see, may or may not > work. > >>>> Should I bring in the ul.44? and how do I do that? > >>>> Should I revise my tests (there are not too many) to insert that > extra space. > >>> If you want to produce specifically XHTML, then yes. Else you're > better off generating HTML and not bother with how an XML library produces > XHTML (which is not the job of an XML library IMO). > >>> Levente > >>>> thank you for your time. > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Mon Sep 21 15:56:16 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Mon, 21 Sep 2020 17:56:16 +0200 (CEST) Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> Message-ID: Hi Ron, On Mon, 21 Sep 2020, Ron Teitelbaum wrote: > Hi All, > > Terf uses a modified version of XML-Parser based on trunk XML-Parser-ul.44 and XML-Explorer-topa.1. > > Levente Uzonyi > > >> Monty's XML implementation is way more complete than the one in the Trunk. > > How so?   AFAIK, it supports namespaces, dtd and schema validation, and sax2. There may be other things too. I never really checked. Levente > > Ron Tetielbaum > > On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda wrote: > > > > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi wrote: > > > > H Eliot, > > > >> On Sun, 20 Sep 2020, Eliot Miranda wrote: > >> > >> Hi Levente, Hi Tty, > >> > >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: > >>> Hi Tim, > >>> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. > >>> That Xtreams dependency on that library is pretty much pointless: > >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package > >>> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: > >>> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. > >>> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) > >>> - the example does not use any features to justify using the not-so-Squeak-compatible XML package > >>> - that example grammar only uses it for the sake of making it Pharo-compatible > >>> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. > >>> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. > >>> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo > version of the example, making the example use Squeak's XML package in Squeak. > >>> The latter also involves more work, and a more compilcated Metacello configuration. > >> > >> I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML.  So let me make a plea for the latter.  I fear we would be heavily impacted by a change of XML framework.  > Cc’ing Ron as he knows the reality far better than I (hi Ron). > > > > Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. > > Terf is built in Squeak 5.3.  We want to move forward to trunk soon (we want to stay on trunk).  I have zero desire to develop on Pharo. > > > > > Levente > > > >> > >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: > >>>> Hi Folks, > >>>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 > >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. > >>>> > >>>>     (XMLElement name:'br' ) printString '
' > >>>> vs > >>>> (XMLElement name:'br' ) printString '
' > >>>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. > >>>> Checking the Monticello, the 6.0 > >>>>     XML-Parser (monty.428) > >>>> while the 5.3 is > >>>> > >>>>     XML-Parser (monty.428 , ul.44) > >>> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. > >>>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. > >>>> Being new to this, who knows how I managed that. > >>> No. The two packages are not compatible with each other and you have "both" in your image at the same time. > >>>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. > >>>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. > >>>> Which should I use? > >>> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. > >>>> Should I bring in the ul.44? and how do I do that? > >>>> Should I revise my tests (there are not too many) to insert that extra space. > >>> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). > >>> Levente > >>>> thank you for your time. > > > > > From Das.Linux at gmx.de Mon Sep 21 17:03:58 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Mon, 21 Sep 2020 19:03:58 +0200 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> Message-ID: <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> > On 21.09.2020, at 17:56, Levente Uzonyi wrote: > > Hi Ron, > > On Mon, 21 Sep 2020, Ron Teitelbaum wrote: > >> Hi All, >> Terf uses a modified version of XML-Parser based on trunk XML-Parser-ul.44 and XML-Explorer-topa.1. >> >> Levente Uzonyi >> >> Monty's XML implementation is way more complete than the one in the Trunk. >> How so? > > AFAIK, it supports namespaces, dtd and schema validation, and sax2. There may be other things too. I never really checked. > XLink support and the like. It's really impressive and quite useful. Had I had time, I'd incorporated it in 2017 or so. I hadn't had time and I forgot :/ 6.0 is a good Idea tho. -t > > Levente > >> Ron Tetielbaum >> On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda wrote: >> >> > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi wrote: >> > >> > H Eliot, >> > >> >> On Sun, 20 Sep 2020, Eliot Miranda wrote: >> >> >> >> Hi Levente, Hi Tty, >> >> >> >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: >> >>> Hi Tim, >> >>> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. >> >>> That Xtreams dependency on that library is pretty much pointless: >> >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package >> >>> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: >> >>> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. >> >>> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) >> >>> - the example does not use any features to justify using the not-so-Squeak-compatible XML package >> >>> - that example grammar only uses it for the sake of making it Pharo-compatible >> >>> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. >> >>> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. >> >>> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo >> version of the example, making the example use Squeak's XML package in Squeak. >> >>> The latter also involves more work, and a more compilcated Metacello configuration. >> >> >> >> I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework. >> Cc’ing Ron as he knows the reality far better than I (hi Ron). >> > >> > Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. >> >> Terf is built in Squeak 5.3. We want to move forward to trunk soon (we want to stay on trunk). I have zero desire to develop on Pharo. >> >> > >> > Levente >> > >> >> >> >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: >> >>>> Hi Folks, >> >>>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 >> >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. >> >>>> >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> vs >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. >> >>>> Checking the Monticello, the 6.0 >> >>>> XML-Parser (monty.428) >> >>>> while the 5.3 is >> >>>> >> >>>> XML-Parser (monty.428 , ul.44) >> >>> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. >> >>>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. >> >>>> Being new to this, who knows how I managed that. >> >>> No. The two packages are not compatible with each other and you have "both" in your image at the same time. >> >>>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. >> >>>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. >> >>>> Which should I use? >> >>> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. >> >>>> Should I bring in the ul.44? and how do I do that? >> >>>> Should I revise my tests (there are not too many) to insert that extra space. >> >>> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). >> >>> Levente >> >>>> thank you for your time. >> > > From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 21 19:31:28 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 21 Sep 2020 19:31:28 +0000 Subject: [squeak-dev] ColorForm with transparency not working properly In-Reply-To: References: <618f98fce9dd44cbb9b603a6eca2e463@student.hpi.uni-potsdam.de>, Message-ID: <25297d17b4f2447ea1cc43bb71e6eb84@student.hpi.uni-potsdam.de> Thanks for the pointer, Stef. I think we really need some kind of documentation of the form rules to decide whether we can apply Marcel's solution. Did anyone ever read something about it? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Stéphane Rollandin Gesendet: Samstag, 19. September 2020 22:34:21 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] ColorForm with transparency not working properly > But unfortunately, I could not find any documentation about the > different Form rules such as #blend, #over, #paint etc. Can you give me > some pointers? Related thread: http://forum.world.st/ImageForm-color-with-alpha-td5090574.html Stef -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 21 19:35:01 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 21 Sep 2020 19:35:01 +0000 Subject: [squeak-dev] The Trunk: Sound-eem.74.mcz In-Reply-To: References: Message-ID: <7f701d7baf4c4ae8902b05397d30dcc6@student.hpi.uni-potsdam.de> Underscore selectors in the Trunk? Is this really necessary? Depending on your preferences, you might not even be able to load this code, and I would not consider this Smalltalkish at all ... Is this a known idiom? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Samstag, 19. September 2020 18:11:42 An: squeak-dev at lists.squeakfoundation.org; packages at lists.squeakfoundation.org Betreff: [squeak-dev] The Trunk: Sound-eem.74.mcz Eliot Miranda uploaded a new version of Sound to project The Trunk: http://source.squeak.org/trunk/Sound-eem.74.mcz ==================== Summary ==================== Name: Sound-eem.74 Author: eem Time: 19 September 2020, 9:11:40.666386 am UUID: 42a3c2d9-0bed-4310-bcf1-cbe0f3d0653b Ancestors: Sound-eem.73 Oops! stopPlayerProcess *must* stop the sound system when sent from startPlayerProcessBufferSize:rate:stereo:sound:. Spo refactor a bit, renaming stopPlayerProcess to stopPlayerProcess: to take a hardStop boolean. When quitting the argument is false. Add a 64-bit specific, integer-overflow agnostic version of mixSampleCount:into:startingAt:leftVol:rightVol:, for creating a simpler inline primitive in the 64-bit VM. =============== Diff against Sound-eem.73 =============== Item was changed: ----- Method: AbstractSound class>>translatedPrimitives (in category 'primitive generation') ----- translatedPrimitives ^#( (FMSound mixSampleCount:into:startingAt:leftVol:rightVol:) (PluckedSound mixSampleCount:into:startingAt:leftVol:rightVol:) (LoopedSampledSound mixSampleCount:into:startingAt:leftVol:rightVol:) (SampledSound mixSampleCount:into:startingAt:leftVol:rightVol:) + (SampledSound _64bitMixSampleCount:into:startingAt:leftVol:rightVol:) (ReverbSound applyReverbTo:startingAt:count:) ). ! Item was added: + ----- Method: SampledSound>>_64bitMixSampleCount:into:startingAt:leftVol:rightVol: (in category 'playing') ----- + _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol + "Mix the given number of samples with the samples already in the given buffer starting at the given index. + Assume that the buffer size is at least (index + count) - 1." + + | lastIndex outIndex sampleIndex sample i s | + + + + + lastIndex := (startIndex + n) - 1. + outIndex := startIndex. "index of next stereo output sample pair" + sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). + [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] whileTrue: + [sample := ((samples at: sampleIndex) * scaledVol) // ScaleFactor. + leftVol > 0 ifTrue: + [i := (2 * outIndex) - 1. + s := (aSoundBuffer at: i) + ((sample * leftVol) // ScaleFactor). + s > 32767 ifTrue: [s := 32767]. "clipping!!" + s < -32767 ifTrue: [s := -32767]. "clipping!!" + aSoundBuffer at: i put: s]. + rightVol > 0 ifTrue: + [i := 2 * outIndex. + s := (aSoundBuffer at: i) + ((sample * rightVol) // ScaleFactor). + s > 32767 ifTrue: [s := 32767]. "clipping!!" + s < -32767 ifTrue: [s := -32767]. "clipping!!" + aSoundBuffer at: i put: s]. + + scaledVolIncr ~= 0 ifTrue: + [scaledVol := scaledVol + scaledVolIncr. + ((scaledVolIncr > 0 and: [scaledVol >= scaledVolLimit]) or: + [scaledVolIncr < 0 and: [scaledVol <= scaledVolLimit]]) + ifTrue: "reached the limit; stop incrementing" + [scaledVol := scaledVolLimit. + scaledVolIncr := 0]]. + + scaledIndex := scaledIndex + scaledIncrement. + + sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). + outIndex := outIndex + 1]. + count := count - n + ! Item was changed: ----- Method: SampledSound>>mixSampleCount:into:startingAt:leftVol:rightVol: (in category 'playing') ----- mixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol + "Mix the given number of samples with the samples already in the given buffer starting at the given index. + Assume that the buffer size is at least (index + count) - 1." - "Mix the given number of samples with the samples already in the given buffer starting at the given index. Assume that the buffer size is at least (index + count) - 1." | lastIndex outIndex sampleIndex sample i s overflow | + + + - - - + SmallInteger maxVal > 16r3FFFFFFF ifTrue: "In 64-bits we don't have to worry about 2^15 * 2^15 overflow" + [^self _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol]. lastIndex := (startIndex + n) - 1. outIndex := startIndex. "index of next stereo output sample pair" sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] whileTrue: [ sample := ((samples at: sampleIndex) * scaledVol) // ScaleFactor. leftVol > 0 ifTrue: [ i := (2 * outIndex) - 1. s := (aSoundBuffer at: i) + ((sample * leftVol) // ScaleFactor). s > 32767 ifTrue: [s := 32767]. "clipping!!" s < -32767 ifTrue: [s := -32767]. "clipping!!" aSoundBuffer at: i put: s]. rightVol > 0 ifTrue: [ i := 2 * outIndex. s := (aSoundBuffer at: i) + ((sample * rightVol) // ScaleFactor). s > 32767 ifTrue: [s := 32767]. "clipping!!" s < -32767 ifTrue: [s := -32767]. "clipping!!" aSoundBuffer at: i put: s]. scaledVolIncr ~= 0 ifTrue: [ scaledVol := scaledVol + scaledVolIncr. ((scaledVolIncr > 0 and: [scaledVol >= scaledVolLimit]) or: [scaledVolIncr < 0 and: [scaledVol <= scaledVolLimit]]) ifTrue: [ "reached the limit; stop incrementing" scaledVol := scaledVolLimit. scaledVolIncr := 0]]. scaledIndex := scaledIndex + scaledIncrement. scaledIndex >= ScaledIndexOverflow ifTrue: [ overflow := scaledIndex >> IncrementFractionBits. indexHighBits := indexHighBits + overflow. scaledIndex := scaledIndex - (overflow << IncrementFractionBits)]. sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). outIndex := outIndex + 1]. + count := count - n! - count := count - n. - ! Item was changed: ----- Method: SoundPlayer class>>shutDown: (in category 'snapshotting') ----- shutDown: quitting "Stop player process, for example before snapshotting." + quitting ifTrue: + [self stopPlayerProcess: false. + ReverbState := nil]! - quitting ifTrue: [ - self stopPlayerProcess. - ReverbState := nil].! Item was changed: ----- Method: SoundPlayer class>>startPlayerProcessBufferSize:rate:stereo:sound: (in category 'player process') ----- startPlayerProcessBufferSize: bufferSize rate: samplesPerSecond stereo: stereoFlag sound: aSound "Start the sound player process. Terminate the old process, if any." "SoundPlayer startPlayerProcessBufferSize: 1000 rate: 11025 stereo: false" + self stopPlayerProcess: true. + aSound ifNotNil: "stopPlayerProcess: ensures ActiveSounds are empty..." + [ActiveSounds add: aSound]. - self stopPlayerProcess. - aSound - ifNil:[ActiveSounds := OrderedCollection new] - ifNotNil:[ActiveSounds := OrderedCollection with: aSound]. Buffer := SoundBuffer newStereoSampleCount: (bufferSize // 4) * 4. + LastBuffer ifNotNil: + [LastBuffer := SoundBuffer basicNew: Buffer basicSize]. - LastBuffer ifNotNil:[LastBuffer := SoundBuffer basicNew: Buffer basicSize]. PlayerSemaphore := Semaphore forMutualExclusion. SamplingRate := samplesPerSecond. Stereo := stereoFlag. SoundSupported := true. "Assume so" UseReadySemaphore := true. "set to false if ready semaphore not supported by VM" Smalltalk newExternalSemaphoreDo: [ :semaphore :index | ReadyForBuffer := semaphore. self primSoundStartBufferSize: Buffer stereoSampleCount rate: samplesPerSecond stereo: Stereo semaIndex: index ]. "Check if sound start prim was successful" SoundSupported ifFalse:[ Smalltalk unregisterExternalObject: ReadyForBuffer. ReadyForBuffer := nil. ^self ]. UseReadySemaphore ifTrue: [PlayerProcess := [SoundPlayer playLoop] newProcess] ifFalse: [PlayerProcess := [SoundPlayer oldStylePlayLoop] newProcess]. UseReverb ifTrue: [self startReverb]. PlayerProcess name: 'Sound Player (', ActiveSounds size asString, ')'; priority: Processor userInterruptPriority; resume! Item was removed: - ----- Method: SoundPlayer class>>stopPlayerProcess (in category 'player process') ----- - stopPlayerProcess - "Stop the sound player process." - "SoundPlayer stopPlayerProcess" - - PlayerProcess ifNotNil: - [PlayerProcess ~~ Processor activeProcess ifTrue: - [PlayerProcess terminate]. - PlayerProcess := nil]. - "Don't load the SoundPlugin if it is not in use..." - self soundPluginActive ifTrue: [self primSoundStop]. - ActiveSounds isEmpty ifFalse: - [ActiveSounds := OrderedCollection new]. - Buffer := nil. - PlayerSemaphore isEmpty ifFalse: - [PlayerSemaphore := Semaphore forMutualExclusion]. - ReadyForBuffer ifNotNil: - [Smalltalk unregisterExternalObject: ReadyForBuffer. - ReadyForBuffer := nil]! Item was added: + ----- Method: SoundPlayer class>>stopPlayerProcess: (in category 'player process') ----- + stopPlayerProcess: hardStop + "Stop the sound player process." + "SoundPlayer stopPlayerProcess" + + PlayerProcess ifNotNil: + [PlayerProcess ~~ Processor activeProcess ifTrue: + [PlayerProcess terminate]. + PlayerProcess := nil]. + (hardStop or: [self soundPluginActive]) ifTrue: [self primSoundStop]. + ActiveSounds isEmpty ifFalse: + [ActiveSounds := OrderedCollection new]. + Buffer := nil. + PlayerSemaphore isEmpty ifFalse: + [PlayerSemaphore := Semaphore forMutualExclusion]. + ReadyForBuffer ifNotNil: + [Smalltalk unregisterExternalObject: ReadyForBuffer. + ReadyForBuffer := nil]! -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 21 19:37:54 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 21 Sep 2020 19:37:54 +0000 Subject: [squeak-dev] The Inbox: Tools-eem.986.mcz In-Reply-To: References: Message-ID: Hi Levente, I'm still not sure how this can reliably work if some other unknown process is scheduling deferred UI messages at the same time ... Also, maybe you could use a closure for the uiUpdateBlock variable instead of bloating the whole object's state, but that could be a subjective thing, too. 😊 Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Samstag, 19. September 2020 22:35:37 An: squeak-dev at lists.squeakfoundation.org Betreff: [squeak-dev] The Inbox: Tools-eem.986.mcz A new version of Tools was added to project The Inbox: http://source.squeak.org/inbox/Tools-eem.986.mcz ==================== Summary ==================== Name: Tools-eem.986 Author: eem Time: 19 September 2020, 1:35:34.572026 pm UUID: b33a2312-24e5-4cfa-a4e3-7f409ce8f5d4 Ancestors: Tools-ct.985 Use the lastDeferredUIMessage mechanism for avoiding updating the debugger UI too frequently. Alas this only solves the run to here case; it doesn't fix the rapid clicking issue. Hence this is in the inbox. Christoph, this may be useful to you. =============== Diff against Tools-ct.985 =============== Item was changed: CodeHolder subclass: #Debugger + instanceVariableNames: 'interruptedProcess contextStack contextStackIndex contextStackList receiverInspector receiverInspectorState contextVariablesInspector contextVariablesInspectorState externalInterrupt proceedValue selectingPC savedCursor isolationHead failedProject labelString message untilExpression uiUpdateBlock' - instanceVariableNames: 'interruptedProcess contextStack contextStackIndex contextStackList receiverInspector receiverInspectorState contextVariablesInspector contextVariablesInspectorState externalInterrupt proceedValue selectingPC savedCursor isolationHead failedProject labelString message untilExpression' classVariableNames: 'ContextStackKeystrokes ErrorReportServer FullStackSize InterruptUIProcessIfBlockedOnErrorInBackgroundProcess NotifierStackSize SavedExtent StackSizeLimit WantsAnnotationPane' poolDictionaries: '' category: 'Tools-Debugger'! !Debugger commentStamp: 'mt 12/17/2019 12:19' prior: 0! I represent the machine state at the time of an interrupted process. I also represent a query path into the state of the process. The debugger is typically viewed through a window that views the stack of suspended contexts, the code for, and execution point in, the currently selected message, and inspectors on both the receiver of the currently selected message, and the variables in the current context. Special note on recursive errors: Some errors affect Squeak's ability to present a debugger. This is normally an unrecoverable situation. However, if such an error occurs in an isolation layer, Squeak will attempt to exit from the isolation layer and then present a debugger. Here is the chain of events in such a recovery. * A recursive error is detected. * The current project is queried for an isolationHead * Changes in the isolationHead are revoked * The parent project of isolated project is returned to * The debugger is opened there and execution resumes. If the user closes that debugger, execution continues in the outer project and layer. If, after repairing some damage, the user proceeds from the debugger, then the isolationHead is re-invoked, the failed project is re-entered, and execution resumes in that world. --- In September 2019, we added MorphicDebugger and MVCDebugger to untangle framework-specific features in our debugger infrastructure. However, this is just an intermediate step. The overall goal would be to remove those two subclasses again while preserving their functionality. Mostly, MVC and Morphic differ in their GUI-process management. This means that "proceed" and "close" work differently depending on the process that is being debugged. --- One idea is to attach that framework-specific information to the process objects. See Process >> #environmentAt: and #environmentAt:put:. Also see ToolSet's #handle* and #debug* methods.! Item was changed: ----- Method: Debugger>>doStep (in category 'context stack menu') ----- doStep "Send the selected message in the accessed method, and regain control after the invoked method returns." | currentContext newContext | self okToChange ifFalse: [^ self]. self checkContextSelection. currentContext := self selectedContext. newContext := self handleLabelUpdatesIn: [interruptedProcess completeStep: currentContext] whenExecuting: currentContext. newContext == currentContext ifTrue: [newContext := interruptedProcess stepToSendOrReturn]. self contextStackIndex > 1 ifTrue: [self resetContext: newContext] ifFalse: [newContext == currentContext + ifTrue: [self scheduleUIUpdate] - ifTrue: [self changed: #contentsSelection. - self updateInspectors] ifFalse: [self resetContext: newContext]]. ! Item was added: + ----- Method: Debugger>>scheduleUIUpdate (in category 'context stack menu') ----- + scheduleUIUpdate + uiUpdateBlock ifNil: + [uiUpdateBlock := [self changed: #contentsSelection. + self updateInspectors]]. + WorldState lastDeferredUIMessage ~~ uiUpdateBlock ifTrue: + [WorldState addDeferredUIMessage: uiUpdateBlock]! -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Mon Sep 21 19:44:24 2020 From: karlramberg at gmail.com (karl ramberg) Date: Mon, 21 Sep 2020 21:44:24 +0200 Subject: [squeak-dev] ColorForm with transparency not working properly In-Reply-To: <25297d17b4f2447ea1cc43bb71e6eb84@student.hpi.uni-potsdam.de> References: <618f98fce9dd44cbb9b603a6eca2e463@student.hpi.uni-potsdam.de> <25297d17b4f2447ea1cc43bb71e6eb84@student.hpi.uni-potsdam.de> Message-ID: It is this code that apply the blendAlphaScaled. BalloonEngine>>bitBlt: aBitBlt | destWidth | bitBlt := aBitBlt. bitBlt isNil ifTrue:[^self]. destWidth := bitBlt destForm width. destWidth > span size ifTrue:[span := Bitmap new: destWidth]. self class primitiveSetBitBltPlugin: bitBlt getPluginName. self clipRect: bitBlt clipRect. bitBlt sourceForm: (Form extent: span size @ 1 depth: 32 bits: span); sourceRect: (0 at 0 extent: 1 at span size); colorMap: (Color colorMapIfNeededFrom: 32 to: bitBlt destForm depth); combinationRule: (bitBlt destForm depth >= 8 ifTrue:[34" blendAlphaScaled "] ifFalse:[Form paint]). Best, Karl On Mon, Sep 21, 2020 at 9:31 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Thanks for the pointer, Stef. I think we really need some kind of > documentation of the form rules to decide whether we can apply Marcel's > solution. Did anyone ever read something about it? > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Stéphane Rollandin > *Gesendet:* Samstag, 19. September 2020 22:34:21 > *An:* squeak-dev at lists.squeakfoundation.org > *Betreff:* Re: [squeak-dev] ColorForm with transparency not working > properly > > > But unfortunately, I could not find any documentation about the > > different Form rules such as #blend, #over, #paint etc. Can you give me > > some pointers? > Related thread: > http://forum.world.st/ImageForm-color-with-alpha-td5090574.html > > Stef > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Mon Sep 21 19:49:14 2020 From: karlramberg at gmail.com (karl ramberg) Date: Mon, 21 Sep 2020 21:49:14 +0200 Subject: [squeak-dev] ColorForm with transparency not working properly In-Reply-To: References: <618f98fce9dd44cbb9b603a6eca2e463@student.hpi.uni-potsdam.de> <25297d17b4f2447ea1cc43bb71e6eb84@student.hpi.uni-potsdam.de> Message-ID: See also mail from Juan Vuletich in vm-dev list:"Proposal for new BitBlt rules (#505)" which has fixes for alpha blending Best, Karl On Mon, Sep 21, 2020 at 9:44 PM karl ramberg wrote: > It is this code that apply the blendAlphaScaled. > > BalloonEngine>>bitBlt: aBitBlt > | destWidth | > bitBlt := aBitBlt. > bitBlt isNil ifTrue:[^self]. > destWidth := bitBlt destForm width. > destWidth > span size ifTrue:[span := Bitmap new: destWidth]. > self class primitiveSetBitBltPlugin: bitBlt getPluginName. > self clipRect: bitBlt clipRect. > bitBlt > sourceForm: (Form extent: span size @ 1 depth: 32 bits: span); > sourceRect: (0 at 0 extent: 1 at span size); > colorMap: (Color colorMapIfNeededFrom: 32 to: bitBlt destForm depth); > combinationRule: (bitBlt destForm depth >= 8 ifTrue:[34" blendAlphaScaled > "] ifFalse:[Form paint]). > > Best, > Karl > > On Mon, Sep 21, 2020 at 9:31 PM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Thanks for the pointer, Stef. I think we really need some kind of >> documentation of the form rules to decide whether we can apply Marcel's >> solution. Did anyone ever read something about it? >> >> >> Best, >> >> Christoph >> >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von Stéphane Rollandin >> *Gesendet:* Samstag, 19. September 2020 22:34:21 >> *An:* squeak-dev at lists.squeakfoundation.org >> *Betreff:* Re: [squeak-dev] ColorForm with transparency not working >> properly >> >> > But unfortunately, I could not find any documentation about the >> > different Form rules such as #blend, #over, #paint etc. Can you give me >> > some pointers? >> Related thread: >> http://forum.world.st/ImageForm-color-with-alpha-td5090574.html >> >> Stef >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Mon Sep 21 19:50:12 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 21 Sep 2020 12:50:12 -0700 Subject: [squeak-dev] The Trunk: Sound-eem.74.mcz In-Reply-To: <7f701d7baf4c4ae8902b05397d30dcc6@student.hpi.uni-potsdam.de> References: <7f701d7baf4c4ae8902b05397d30dcc6@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Mon, Sep 21, 2020 at 12:35 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Underscore selectors in the Trunk? Is this really necessary? > No, but it leads to a much nicer sounding selector name than any of sixtyFourBitMixSampleCount:into:startingAt:leftVol:rightVol:, m ix64BitSampleCount:into:startingAt:leftVol:rightVol:, or mixSampleCount64: into:startingAt:leftVol:rightVol:. I can change it if it really offends you, but I did it for a reason. I wanted it to read as "this is the 64-bit version of mixSampleCount:into:startingAt:leftVol:rightVol:", and I didn't want it to be over long. Note that "mix64BitSampleCOunt:" et al are ambiguous. The samples are not 64-bits, the algorithm is for 64-bits. But remember that this is a strange method anyway, a translated primitive, with lots of C weirdness in it. Complaining about the selector is the least of the problems with it ;-) Depending on your preferences, you might not even be able to load this > code, and I would not consider this Smalltalkish at all ... Is this a known > idiom? :-) > Well, underscores have neen allowed in Smalltalk for a long time now. It does lead to some nice usage when interfacing with C. I certwinly do not use it for normal SMalltalk code. But I would miss it badly if it didn't exist. I use selectors such as strcpy:_:_: (which maps to strcpy(3)) routinely in the VMMaker. > Best, > > Christoph > ------------------------------ > *Von:* Squeak-dev im > Auftrag von commits at source.squeak.org > *Gesendet:* Samstag, 19. September 2020 18:11:42 > *An:* squeak-dev at lists.squeakfoundation.org; > packages at lists.squeakfoundation.org > *Betreff:* [squeak-dev] The Trunk: Sound-eem.74.mcz > > Eliot Miranda uploaded a new version of Sound to project The Trunk: > http://source.squeak.org/trunk/Sound-eem.74.mcz > > ==================== Summary ==================== > > Name: Sound-eem.74 > Author: eem > Time: 19 September 2020, 9:11:40.666386 am > UUID: 42a3c2d9-0bed-4310-bcf1-cbe0f3d0653b > Ancestors: Sound-eem.73 > > Oops! stopPlayerProcess *must* stop the sound system when sent from > startPlayerProcessBufferSize:rate:stereo:sound:. Spo refactor a bit, > renaming stopPlayerProcess to stopPlayerProcess: to take a hardStop > boolean. When quitting the argument is false. > > Add a 64-bit specific, integer-overflow agnostic version of > mixSampleCount:into:startingAt:leftVol:rightVol:, for creating a simpler > inline primitive in the 64-bit VM. > > =============== Diff against Sound-eem.73 =============== > > Item was changed: > ----- Method: AbstractSound class>>translatedPrimitives (in category > 'primitive generation') ----- > translatedPrimitives > ^#( > (FMSound mixSampleCount:into:startingAt:leftVol:rightVol:) > (PluckedSound > mixSampleCount:into:startingAt:leftVol:rightVol:) > (LoopedSampledSound > mixSampleCount:into:startingAt:leftVol:rightVol:) > (SampledSound > mixSampleCount:into:startingAt:leftVol:rightVol:) > + (SampledSound > _64bitMixSampleCount:into:startingAt:leftVol:rightVol:) > (ReverbSound applyReverbTo:startingAt:count:) > ). > ! > > Item was added: > + ----- Method: > SampledSound>>_64bitMixSampleCount:into:startingAt:leftVol:rightVol: (in > category 'playing') ----- > + _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex > leftVol: leftVol rightVol: rightVol > + "Mix the given number of samples with the samples already in the > given buffer starting at the given index. > + Assume that the buffer size is at least (index + count) - 1." > + > + | lastIndex outIndex sampleIndex sample i s | > + > + > + > + > + lastIndex := (startIndex + n) - 1. > + outIndex := startIndex. "index of next stereo output sample > pair" > + sampleIndex := indexHighBits + (scaledIndex >> > IncrementFractionBits). > + [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] > whileTrue: > + [sample := ((samples at: sampleIndex) * scaledVol) // > ScaleFactor. > + leftVol > 0 ifTrue: > + [i := (2 * outIndex) - 1. > + s := (aSoundBuffer at: i) + ((sample * leftVol) > // ScaleFactor). > + s > 32767 ifTrue: [s := 32767]. "clipping!!" > + s < -32767 ifTrue: [s := -32767]. "clipping!!" > + aSoundBuffer at: i put: s]. > + rightVol > 0 ifTrue: > + [i := 2 * outIndex. > + s := (aSoundBuffer at: i) + ((sample * rightVol) > // ScaleFactor). > + s > 32767 ifTrue: [s := 32767]. "clipping!!" > + s < -32767 ifTrue: [s := -32767]. "clipping!!" > + aSoundBuffer at: i put: s]. > + > + scaledVolIncr ~= 0 ifTrue: > + [scaledVol := scaledVol + scaledVolIncr. > + ((scaledVolIncr > 0 and: [scaledVol >= > scaledVolLimit]) or: > + [scaledVolIncr < 0 and: [scaledVol <= > scaledVolLimit]]) > + ifTrue: "reached the limit; stop > incrementing" > + [scaledVol := scaledVolLimit. > + scaledVolIncr := 0]]. > + > + scaledIndex := scaledIndex + scaledIncrement. > + > + sampleIndex := indexHighBits + (scaledIndex >> > IncrementFractionBits). > + outIndex := outIndex + 1]. > + count := count - n > + ! > > Item was changed: > ----- Method: > SampledSound>>mixSampleCount:into:startingAt:leftVol:rightVol: (in category > 'playing') ----- > mixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: > leftVol rightVol: rightVol > + "Mix the given number of samples with the samples already in the > given buffer starting at the given index. > + Assume that the buffer size is at least (index + count) - 1." > - "Mix the given number of samples with the samples already in the > given buffer starting at the given index. Assume that the buffer size is at > least (index + count) - 1." > > | lastIndex outIndex sampleIndex sample i s overflow | > + 'SoundGenerationPlugin'> > + > + > - module:'SoundGenerationPlugin'> > - > - > > + SmallInteger maxVal > 16r3FFFFFFF ifTrue: "In 64-bits we don't > have to worry about 2^15 * 2^15 overflow" > + [^self _64bitMixSampleCount: n into: aSoundBuffer > startingAt: startIndex leftVol: leftVol rightVol: rightVol]. > lastIndex := (startIndex + n) - 1. > outIndex := startIndex. "index of next stereo output sample > pair" > sampleIndex := indexHighBits + (scaledIndex >> > IncrementFractionBits). > [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] > whileTrue: [ > sample := ((samples at: sampleIndex) * scaledVol) // > ScaleFactor. > leftVol > 0 ifTrue: [ > i := (2 * outIndex) - 1. > s := (aSoundBuffer at: i) + ((sample * leftVol) > // ScaleFactor). > s > 32767 ifTrue: [s := 32767]. "clipping!!" > s < -32767 ifTrue: [s := -32767]. "clipping!!" > aSoundBuffer at: i put: s]. > rightVol > 0 ifTrue: [ > i := 2 * outIndex. > s := (aSoundBuffer at: i) + ((sample * rightVol) > // ScaleFactor). > s > 32767 ifTrue: [s := 32767]. "clipping!!" > s < -32767 ifTrue: [s := -32767]. "clipping!!" > aSoundBuffer at: i put: s]. > > scaledVolIncr ~= 0 ifTrue: [ > scaledVol := scaledVol + scaledVolIncr. > ((scaledVolIncr > 0 and: [scaledVol >= > scaledVolLimit]) or: > [scaledVolIncr < 0 and: [scaledVol <= > scaledVolLimit]]) > ifTrue: [ "reached the limit; stop > incrementing" > scaledVol := scaledVolLimit. > scaledVolIncr := 0]]. > > scaledIndex := scaledIndex + scaledIncrement. > scaledIndex >= ScaledIndexOverflow ifTrue: [ > overflow := scaledIndex >> IncrementFractionBits. > indexHighBits := indexHighBits + overflow. > scaledIndex := scaledIndex - (overflow << > IncrementFractionBits)]. > > sampleIndex := indexHighBits + (scaledIndex >> > IncrementFractionBits). > outIndex := outIndex + 1]. > + count := count - n! > - count := count - n. > - ! > > Item was changed: > ----- Method: SoundPlayer class>>shutDown: (in category 'snapshotting') > ----- > shutDown: quitting > "Stop player process, for example before snapshotting." > > + quitting ifTrue: > + [self stopPlayerProcess: false. > + ReverbState := nil]! > - quitting ifTrue: [ > - self stopPlayerProcess. > - ReverbState := nil].! > > Item was changed: > ----- Method: SoundPlayer > class>>startPlayerProcessBufferSize:rate:stereo:sound: (in category 'player > process') ----- > startPlayerProcessBufferSize: bufferSize rate: samplesPerSecond stereo: > stereoFlag sound: aSound > "Start the sound player process. Terminate the old process, if > any." > "SoundPlayer startPlayerProcessBufferSize: 1000 rate: 11025 > stereo: false" > > + self stopPlayerProcess: true. > + aSound ifNotNil: "stopPlayerProcess: ensures ActiveSounds are > empty..." > + [ActiveSounds add: aSound]. > - self stopPlayerProcess. > - aSound > - ifNil:[ActiveSounds := OrderedCollection new] > - ifNotNil:[ActiveSounds := OrderedCollection with: aSound]. > Buffer := SoundBuffer newStereoSampleCount: (bufferSize // 4) * 4. > + LastBuffer ifNotNil: > + [LastBuffer := SoundBuffer basicNew: Buffer basicSize]. > - LastBuffer ifNotNil:[LastBuffer := SoundBuffer basicNew: Buffer > basicSize]. > PlayerSemaphore := Semaphore forMutualExclusion. > SamplingRate := samplesPerSecond. > Stereo := stereoFlag. > SoundSupported := true. "Assume so" > UseReadySemaphore := true. "set to false if ready semaphore not > supported by VM" > Smalltalk newExternalSemaphoreDo: [ :semaphore :index | > ReadyForBuffer := semaphore. > self primSoundStartBufferSize: Buffer stereoSampleCount > rate: samplesPerSecond > stereo: Stereo > semaIndex: index ]. > "Check if sound start prim was successful" > SoundSupported ifFalse:[ > Smalltalk unregisterExternalObject: ReadyForBuffer. > ReadyForBuffer := nil. > ^self ]. > UseReadySemaphore > ifTrue: [PlayerProcess := [SoundPlayer playLoop] > newProcess] > ifFalse: [PlayerProcess := [SoundPlayer oldStylePlayLoop] > newProcess]. > UseReverb ifTrue: [self startReverb]. > > PlayerProcess > name: 'Sound Player (', ActiveSounds size asString, ')'; > priority: Processor userInterruptPriority; > resume! > > Item was removed: > - ----- Method: SoundPlayer class>>stopPlayerProcess (in category 'player > process') ----- > - stopPlayerProcess > - "Stop the sound player process." > - "SoundPlayer stopPlayerProcess" > - > - PlayerProcess ifNotNil: > - [PlayerProcess ~~ Processor activeProcess ifTrue: > - [PlayerProcess terminate]. > - PlayerProcess := nil]. > - "Don't load the SoundPlugin if it is not in use..." > - self soundPluginActive ifTrue: [self primSoundStop]. > - ActiveSounds isEmpty ifFalse: > - [ActiveSounds := OrderedCollection new]. > - Buffer := nil. > - PlayerSemaphore isEmpty ifFalse: > - [PlayerSemaphore := Semaphore forMutualExclusion]. > - ReadyForBuffer ifNotNil: > - [Smalltalk unregisterExternalObject: ReadyForBuffer. > - ReadyForBuffer := nil]! > > Item was added: > + ----- Method: SoundPlayer class>>stopPlayerProcess: (in category 'player > process') ----- > + stopPlayerProcess: hardStop > + "Stop the sound player process." > + "SoundPlayer stopPlayerProcess" > + > + PlayerProcess ifNotNil: > + [PlayerProcess ~~ Processor activeProcess ifTrue: > + [PlayerProcess terminate]. > + PlayerProcess := nil]. > + (hardStop or: [self soundPluginActive]) ifTrue: [self > primSoundStop]. > + ActiveSounds isEmpty ifFalse: > + [ActiveSounds := OrderedCollection new]. > + Buffer := nil. > + PlayerSemaphore isEmpty ifFalse: > + [PlayerSemaphore := Semaphore forMutualExclusion]. > + ReadyForBuffer ifNotNil: > + [Smalltalk unregisterExternalObject: ReadyForBuffer. > + ReadyForBuffer := nil]! > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From tim at rowledge.org Mon Sep 21 20:15:22 2020 From: tim at rowledge.org (tim Rowledge) Date: Mon, 21 Sep 2020 13:15:22 -0700 Subject: [squeak-dev] The Trunk: Sound-eem.74.mcz In-Reply-To: References: <7f701d7baf4c4ae8902b05397d30dcc6@student.hpi.uni-potsdam.de> Message-ID: <3DDCB66B-D450-4E0A-801D-CC08390D8319@rowledge.org> > On 2020-09-21, at 12:50 PM, Eliot Miranda wrote: > > Hi Christoph, > > On Mon, Sep 21, 2020 at 12:35 PM Thiede, Christoph wrote: > Underscore selectors in the Trunk? Is this really necessary? I have to say I really, really, don't like underscores in selectors myself. But there's a lot of stuff I don't like that I have to put up with, absent my invincible army of flying monkeys. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim A flash of light, a cloud of dust, and... What was the question? From karlramberg at gmail.com Mon Sep 21 20:32:28 2020 From: karlramberg at gmail.com (karl ramberg) Date: Mon, 21 Sep 2020 22:32:28 +0200 Subject: [squeak-dev] ColorForm with transparency not working properly In-Reply-To: <25297d17b4f2447ea1cc43bb71e6eb84@student.hpi.uni-potsdam.de> References: <618f98fce9dd44cbb9b603a6eca2e463@student.hpi.uni-potsdam.de> <25297d17b4f2447ea1cc43bb71e6eb84@student.hpi.uni-potsdam.de> Message-ID: Class comment for VMMaker-Interpreter/BitBltSimulation has some information. This information is not in the image currently. This class implements BitBlt, much as specified in the Blue Book spec. Performance has been enhanced through the use of pointer variables such as sourceIndex and destIndex, and by separating several special cases of the inner loop. Operation has been extended to color, with support for 1, 2, 4, 8, 16, and 32-bit pixel sizes. Conversion between different pixel sizes is facilitated by accepting an optional color map. In addition to the original 16 combination rules, this BitBlt supports 16 fail (for old paint mode) 17 fail (for old mask mode) 18 sourceWord + destinationWord 19 sourceWord - destinationWord 20 rgbAdd: sourceWord with: destinationWord 21 rgbSub: sourceWord with: destinationWord 22 OLDrgbDiff: sourceWord with: destinationWord 23 OLDtallyIntoMap: destinationWord -- old vers doesn't clip to bit boundary 24 alphaBlend: sourceWord with: destinationWord 25 pixPaint: sourceWord with: destinationWord 26 pixMask: sourceWord with: destinationWord 27 rgbMax: sourceWord with: destinationWord 28 rgbMin: sourceWord with: destinationWord 29 rgbMin: sourceWord bitInvert32 with: destinationWord 30 alphaBlendConst: sourceWord with: destinationWord -- alpha passed as an arg 31 alphaPaintConst: sourceWord with: destinationWord -- alpha passed as an arg 32 rgbDiff: sourceWord with: destinationWord 33 tallyIntoMap: destinationWord 34 alphaBlendScaled: sourceWord with: destinationWord 35 alphaBlendScaled: sourceWord with: "unused here - only used by FXBlt" 36 alphaBlendScaled: sourceWord with: "unused here - only used by FXBlt" 37 rgbMul: sourceWord with: destinationWord 38 pixSwap: sourceWord with: destinationWord 39 pixClear: sourceWord with: destinationWord 40 fixAlpha: sourceWord with: destinationWord 41 rgbComponentAlpha: sourceWord with: destinationWord This implementation has also been fitted with an experimental "warp drive" that allows abritrary scaling and rotation (and even limited affine deformations) with all BitBlt storage modes supported. To add a new rule to BitBlt... 1. add the new rule method or methods in the category 'combination rules' of BBSim 2. describe it in the class comment of BBSim and in the class comment for BitBlt 3. add refs to initializeRuleTable in proper positions 4. add refs to initBBOpTable, following the pattern XHTML | CSS | RSS On Mon, Sep 21, 2020 at 9:31 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Thanks for the pointer, Stef. I think we really need some kind of > documentation of the form rules to decide whether we can apply Marcel's > solution. Did anyone ever read something about it? > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Stéphane Rollandin > *Gesendet:* Samstag, 19. September 2020 22:34:21 > *An:* squeak-dev at lists.squeakfoundation.org > *Betreff:* Re: [squeak-dev] ColorForm with transparency not working > properly > > > But unfortunately, I could not find any documentation about the > > different Form rules such as #blend, #over, #paint etc. Can you give me > > some pointers? > Related thread: > http://forum.world.st/ImageForm-color-with-alpha-td5090574.html > > Stef > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Mon Sep 21 22:20:36 2020 From: gettimothy at zoho.com (gettimothy) Date: Mon, 21 Sep 2020 18:20:36 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> Message-ID: <174b2c124a7.b191b772176460.1790324482142433833@zoho.com> Sorry I don"t niw how to intersperse my comments as you all have done, so please bear with me. If I an reading this correctly, TERF has the same issue as XTreams, a dependency on an XML package that is not in Trunk. Terf is a featured app on squeak.org and XTreams parsing is the bees knees. The issue then becomes a corporate decision on upgrading the XML in Trunk for 6.0 and then retrofitting existing apps, or converting Terf , XTreams and others to the XML in Trunk. Do I have that right? My vote is to go with the best software, not marketing, solution. If the XML in trunk is not the best out there, go for the best. I will help as best I can with that. Furthermore, and I say this as a newbie who realized a few years ago that importing Omnibrowser(?) Via monticello, into an early squeak 3.x release, ended up converting my squeak to pharo after all the dependencies where satisfied, and that that model was dangerous. Keep it clean, keep it excellent, keep it elegant. Monticello is not meeting that because it is a facade for complexity...it makes some installs very easy, but then you hit this and the "ensure recent Monticello" contortions . Imho, the BSD ports or Slackware packages is the better model. Git stuff looking good too, but I am jyst an app guy, not a systems guy. . thanks all for your work, squeak is a great platform. ---- On Mon, 21 Sep 2020 13:03:43 -0400 Das.Linux at gmx.de wrote ---- > On 21.09.2020, at 17:56, Levente Uzonyi wrote: > > Hi Ron, > > On Mon, 21 Sep 2020, Ron Teitelbaum wrote: > >> Hi All, >> Terf uses a modified version of XML-Parser based on trunk XML-Parser-ul.44 and XML-Explorer-topa.1. >> >> Levente Uzonyi >> >> Monty's XML implementation is way more complete than the one in the Trunk. >> How so? > > AFAIK, it supports namespaces, dtd and schema validation, and sax2. There may be other things too. I never really checked. > XLink support and the like. It's really impressive and quite useful. Had I had time, I'd incorporated it in 2017 or so. I hadn't had time and I forgot :/ 6.0 is a good Idea tho. -t > > Levente > >> Ron Tetielbaum >> On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda wrote: >> >> > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi wrote: >> > >> > H Eliot, >> > >> >> On Sun, 20 Sep 2020, Eliot Miranda wrote: >> >> >> >> Hi Levente, Hi Tty, >> >> >> >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: >> >>> Hi Tim, >> >>> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. >> >>> That Xtreams dependency on that library is pretty much pointless: >> >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package >> >>> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: >> >>> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. >> >>> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) >> >>> - the example does not use any features to justify using the not-so-Squeak-compatible XML package >> >>> - that example grammar only uses it for the sake of making it Pharo-compatible >> >>> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. >> >>> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. >> >>> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo >> version of the example, making the example use Squeak's XML package in Squeak. >> >>> The latter also involves more work, and a more compilcated Metacello configuration. >> >> >> >> I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework. >> Cc’ing Ron as he knows the reality far better than I (hi Ron). >> > >> > Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. >> >> Terf is built in Squeak 5.3. We want to move forward to trunk soon (we want to stay on trunk). I have zero desire to develop on Pharo. >> >> > >> > Levente >> > >> >> >> >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: >> >>>> Hi Folks, >> >>>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 >> >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. >> >>>> >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> vs >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. >> >>>> Checking the Monticello, the 6.0 >> >>>> XML-Parser (monty.428) >> >>>> while the 5.3 is >> >>>> >> >>>> XML-Parser (monty.428 , ul.44) >> >>> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. >> >>>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. >> >>>> Being new to this, who knows how I managed that. >> >>> No. The two packages are not compatible with each other and you have "both" in your image at the same time. >> >>>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. >> >>>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. >> >>>> Which should I use? >> >>> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. >> >>>> Should I bring in the ul.44? and how do I do that? >> >>>> Should I revise my tests (there are not too many) to insert that extra space. >> >>> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). >> >>> Levente >> >>>> thank you for your time. >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From giovanni at corriga.net Mon Sep 21 22:27:40 2020 From: giovanni at corriga.net (Giovanni Corriga) Date: Mon, 21 Sep 2020 23:27:40 +0100 Subject: [squeak-dev] UK Smalltalk User Group meeting - Wednesday, September 30th Message-ID: The next meeting of the UK Smalltalk User Group will be on Wednesday, September 30th. For this meeting, Niall Ross and Vlad Degen from Cincom will present AppeX ( ), Cincom’s web application development framework. Together they will cover: - The advantages of the AppeX design philosophy - lightweight, plays well with others - How AppeX works in the Cincom Smalltalk IDE - The advantages of WYSIWYG AppeX JavaScript development through debugging of running AppeX applications - How you can use AppeX in the Cincom Smalltalk IDE to learn and improve your JavaScript, and to create and deploy JavaScript intensive modern web applications. - The advantages of AppeX web development, using the Chrome DevTools in conjunction with AppeX for debugging. Given the current COVID-19 restrictions, this will be an online meeting from home. If you'd like to join us, please sign up in advance on the meeting's Meetup page ( https://www.meetup.com/UKSTUG/events/cbklbrybcmbfc/ ) to receive the meeting details. Don’t forget to bring your laptop and drinks! -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron at usmedrec.com Mon Sep 21 23:14:35 2020 From: ron at usmedrec.com (Ron Teitelbaum) Date: Mon, 21 Sep 2020 19:14:35 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <174b2c124a7.b191b772176460.1790324482142433833@zoho.com> References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> <174b2c124a7.b191b772176460.1790324482142433833@zoho.com> Message-ID: Hi get, I was only asking what was available. The additional features sound very useful to me. I've had to use other software in the past because of some of the missing features. I think I ended up with python. I'm happy to have you believe that we have more power than we do but like everyone else we contribute what we find useful. There are no corporate cabals here. Please feel free to push for what is best for everyone. Ron Teitelbaum On Mon, Sep 21, 2020, 6:20 PM gettimothy via Squeak-dev < squeak-dev at lists.squeakfoundation.org> wrote: > Sorry I don"t niw how to intersperse my comments as you all have done, so > please bear with me. > > > If I an reading this correctly, TERF has the same issue as XTreams, a > dependency on an XML package that is not in Trunk. > > Terf is a featured app on squeak.org and XTreams parsing is the bees > knees. > > The issue then becomes a corporate decision on upgrading the XML in Trunk > for 6.0 and then retrofitting existing apps, or converting Terf , XTreams > and others to the XML in Trunk. > > > Do I have that right? > > > My vote is to go with the best software, not marketing, solution. If the > XML in trunk is not the best out there, go for the best. I will help as > best I can with that. > > Furthermore, and I say this as a newbie who realized a few years ago that > importing Omnibrowser(?) Via monticello, into an early squeak 3.x > release, ended up converting my squeak to pharo after all the dependencies > where satisfied, and that that model was dangerous. > > Keep it clean, keep it excellent, keep it elegant. Monticello is not > meeting that because it is a facade for complexity...it makes some installs > very easy, but then you hit this and the "ensure recent Monticello" > contortions . > > > Imho, the BSD ports or Slackware packages is the better model. Git stuff > looking good too, but I am jyst an app guy, not a systems guy. > . > > thanks all for your work, squeak is a great platform. > > > > > > ---- On Mon, 21 Sep 2020 13:03:43 -0400 * Das.Linux at gmx.de > * wrote ---- > > > > On 21.09.2020, at 17:56, Levente Uzonyi wrote: > > > > Hi Ron, > > > > On Mon, 21 Sep 2020, Ron Teitelbaum wrote: > > > >> Hi All, > >> Terf uses a modified version of XML-Parser based on trunk > XML-Parser-ul.44 and XML-Explorer-topa.1. > >> > >> Levente Uzonyi > >> >> Monty's XML implementation is way more complete than the one in the > Trunk. > >> How so? > > > > AFAIK, it supports namespaces, dtd and schema validation, and sax2. > There may be other things too. I never really checked. > > > XLink support and the like. > It's really impressive and quite useful. > Had I had time, I'd incorporated it in 2017 or so. > I hadn't had time and I forgot :/ > > 6.0 is a good Idea tho. > > -t > > > > > Levente > > > >> Ron Tetielbaum > >> On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda > wrote: > >> > >> > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi > wrote: > >> > > >> > H Eliot, > >> > > >> >> On Sun, 20 Sep 2020, Eliot Miranda wrote: > >> >> > >> >> Hi Levente, Hi Tty, > >> >> > >> >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi > wrote: > >> >>> Hi Tim, > >> >>> Be very careful here. The Xtreams Metacello configuration pulls in > Monty's XML implementation along with the whole kitchen sink it depends on, > and leaves your image with a bunch of undeclared variables. > >> >>> That Xtreams dependency on that library is pretty much pointless: > >> >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML > package > >> >>> - the example uses it to produce web content as XHTML which is > rather unusual for two reasons: > >> >>> - the majority of web content is HTML5 nowadays. Some stats on the > internet say XHTML is still at 10% but without proof. > >> >>> - it uses an XML library to produce XHTML, but the two are > surprisingly not as compatible as you would think (see below) > >> >>> - the example does not use any features to justify using the > not-so-Squeak-compatible XML package > >> >>> - that example grammar only uses it for the sake of making it > Pharo-compatible > >> >>> Monty's XML implementation is way more complete than the one in the > Trunk, but it's not compatible with the one in the Trunk. > >> >>> So, anything that depends on Squeak's XML implementation will be > broken if you load Xtreams. > >> >>> IMO, the right solution is to either drop Pharo support for > Xtreams, as Pharo does not use squeaksource (Metacello configuration > assumes Pharo 4 the latest) or split the package, and have a Squeak and a > Pharo > >> version of the example, making the example use Squeak's XML package in > Squeak. > >> >>> The latter also involves more work, and a more compilcated > Metacello configuration. > >> >> > >> >> I’m pretty sure Terf uses the Squeak XML framework and I know that > we’re heavily dependent on XML. So let me make a plea for the latter. I > fear we would be heavily impacted by a change of XML framework. > >> Cc’ing Ron as he knows the reality far better than I (hi Ron). > >> > > >> > Does Terf use Pharo (version 4 or earlier)? If not, this change would > make no difference. > >> > >> Terf is built in Squeak 5.3. We want to move forward to trunk soon (we > want to stay on trunk). I have zero desire to develop on Pharo. > >> > >> > > >> > Levente > >> > > >> >> > >> >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: > >> >>>> Hi Folks, > >> >>>> I have pretty much succesfully imported XTreams and the XML-Parser > stuff to Squeak 6alpha. from Squeak5.3 > >> >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha > are failing for the following reason. > >> >>>> > >> >>>> (XMLElement name:'br' ) printString '
' > >> >>>> vs > >> >>>> (XMLElement name:'br' ) printString '
' > >> >>>> Notice the latter one inserts an extra space after the 'br'...that > is the behavior on my 6.0. > >> >>>> Checking the Monticello, the 6.0 > >> >>>> XML-Parser (monty.428) > >> >>>> while the 5.3 is > >> >>>> > >> >>>> XML-Parser (monty.428 , ul.44) > >> >>> You have probably updated that image after loading Xtreams, and the > update process merged in the XML-Parsing package from the Trunk. > >> >>>> So, presumably having the extra ul.44 removes the space, but I > have no idea what the ul.44 is, why it is there, how it got > there....yada,yada,yada. > >> >>>> Being new to this, who knows how I managed that. > >> >>> No. The two packages are not compatible with each other and you > have "both" in your image at the same time. > >> >>>> before I mailed this, I checked at the w3 schools on the
tag > and it asserts it should not have the slash in it. w3 schools is not the > xHTML standard, so this may or may not be true. > >> >>>> FWIW, the
stuff works fine for me as does the
, but I > do not know if it will cause problems when I move on to XPath stuff later > on the resulting xHTML docs. > >> >>>> Which should I use? > >> >>> The space is required for XHTML. It is not required for XML. You're > using an XML library to generate XHTML, which, as you see, may or may not > work. > >> >>>> Should I bring in the ul.44? and how do I do that? > >> >>>> Should I revise my tests (there are not too many) to insert that > extra space. > >> >>> If you want to produce specifically XHTML, then yes. Else you're > better off generating HTML and not bother with how an XML library produces > XHTML (which is not the job of an XML library IMO). > >> >>> Levente > >> >>>> thank you for your time. > >> > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ma.chris.m at gmail.com Mon Sep 21 23:19:45 2020 From: ma.chris.m at gmail.com (Chris Muller) Date: Mon, 21 Sep 2020 18:19:45 -0500 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: I believe you're describing defacto habits ingrained into everyone from years of using Microsoft's original UI design than a qualitative critique of this particular design aspect. Yes, familiarity can matter (see below), but the concept of *"Point to what you want to interact with,"* is such a natural way of thinking and working for a human user. Having keyboard focus decoupled and *way off* somewhere else than where the users' attention has since moved, often leads users to a similar confusion that you described, but just from the other direction -- the user wondering why *nothing is happening* when typing into a field -- but this time because the list in which they last clicked just to check something real-quick still has the keyboard focus. This is basically what happened to Eliot.. Coupling it to the mouse pointer helps remove all confusion about where keyboard focus is, and is a natural concept that can be assimilated in about a day. Dump Windows for Linux, and the behavior can be extended into the host system, too. Now, maybe for a single-use Form with a few fields used by external users for something simple, designing it click-for-focus may be worth aligning to Microsoft's defacto. But for all-day developer work involving multiple, multi-paned windows, allocating power to pointing makes the system feel "light and responsive" instead of heavy and cumbersome. For me, anyway... :) Best, Chris On Mon, Sep 21, 2020 at 8:12 AM Ron Teitelbaum wrote: > > > On Sun, Sep 20, 2020 at 5:14 PM Chris Muller wrote: >>> >>> > For example, imagine the power of having EVERY PIXEL on your desktop being ready for ANY INPUT, regardless of window z-ordering, simply by moving pointer there and pressing a mouse or keyboard button. >>> > >>> >>> Here you see, we differ. It's likely partly due to growing up using RISC OS and the original ST-80 UI ... >> >> >> I grew up using MS Windows, the beast that made "click for focus" the defacto disaster it is today. Not everyone is willing to change how they work, but a few years ago I got a tendonitis that really made me notice that I was doing, literally, hundreds upon hundreds, of extra clicks every day for no other purpose than reassigning keyboard focus, which, as you know, have to even be "strategic clicks" (window title bars, borders, etc.) to avoid suffering unwanted selection change, otherwise having to click yet again just to "get back" to what I as looking at. Not only the pain, but I realized what a total time sink it was. The only way to escape was I had to be willing to change, which actually turned out a lot easier than I thought... >> >> > Hover to select is one of the greatest causes of user confusion that I encounter. Most windows users are used to clicking and moving the cursor out of the way so they can see. > > Now imagine a text box. As a user you click on it so that you can type into it. You move your mouse away to type into the box BUT NOTHING HAPPENS. Why because your moving the mouse away from the text field took focus away from the text field. > > Or you click and start typing but your mouse pointer is in the way so you move it and now nothing you type goes into the field. We worked around this issue by locking some of the important fields when a user selects it but not all of them so it's not consistent. > > We also have the issue that if your mouse happens to be hovering above a button things work for a minute but then stop working because the button is capturing keystrokes. The user only moved their mouse. They didn't mean to hover over a button or they clicked a button and got the result they expected but had no reason to move their mouse cursor off the button afterwards. > > While I understand the unnecessary click argument and the harm that clicking does to your wrist, there is still a lot to be said for consistency, user understanding, and user experience. > > Just my 2c. > > Ron Teitelbaum -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Mon Sep 21 23:42:12 2020 From: asqueaker at gmail.com (Chris Muller) Date: Mon, 21 Sep 2020 18:42:12 -0500 Subject: [squeak-dev] The Trunk: Sound-eem.74.mcz In-Reply-To: References: <7f701d7baf4c4ae8902b05397d30dcc6@student.hpi.uni-potsdam.de> Message-ID: > > Underscore selectors in the Trunk? Is this really necessary? >> > No, but it leads to a much nicer sounding selector name than any of > sixtyFourBitMixSampleCount:into:startingAt:leftVol:rightVol:, m > ix64BitSampleCount:into:startingAt:leftVol:rightVol:, or mixSampleCount64: > into:startingAt:leftVol:rightVol:. I can change it if it really > IMO, underscores should NOT be any part of the OOTB API! All three of those alternatives are better Smalltalk convention than the underscore name. Underscore selectors should be reserved for external frameworks that need it to do their meta stuff. This blurs any chance for such a convention, while moving the entire trunk community across this line into needing to set Allow Underscore Selectors, and yet, leaves the Preference in the image, extraneous, I guess..? All for the aesthetic of one selector? offends you, but I did it for a reason. I wanted it to read as "this is > the 64-bit version of mixSampleCount:into:startingAt:leftVol:rightVol:", > and I didn't want it to be over long. Note that "mix64BitSampleCOunt:" et > al are ambiguous. The samples are not 64-bits, the algorithm is for > 64-bits. But remember that this is a strange method anyway, a translated > primitive, with lots of C weirdness in it. Complaining about the selector > is the least of the problems with it ;-) > > Depending on your preferences, you might not even be able to load this >> code, and I would not consider this Smalltalkish at all ... Is this a known >> idiom? :-) >> > > Well, underscores have neen allowed in Smalltalk for a long time now. It > does lead to some nice usage when interfacing with C. I certwinly do not > use it for normal SMalltalk code. But I would miss it badly if it > didn't exist. I use selectors such as strcpy:_:_: (which maps to > strcpy(3)) routinely in the VMMaker. > >> Best, >> >> Christoph >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von commits at source.squeak.org >> *Gesendet:* Samstag, 19. September 2020 18:11:42 >> *An:* squeak-dev at lists.squeakfoundation.org; >> packages at lists.squeakfoundation.org >> *Betreff:* [squeak-dev] The Trunk: Sound-eem.74.mcz >> >> Eliot Miranda uploaded a new version of Sound to project The Trunk: >> http://source.squeak.org/trunk/Sound-eem.74.mcz >> >> ==================== Summary ==================== >> >> Name: Sound-eem.74 >> Author: eem >> Time: 19 September 2020, 9:11:40.666386 am >> UUID: 42a3c2d9-0bed-4310-bcf1-cbe0f3d0653b >> Ancestors: Sound-eem.73 >> >> Oops! stopPlayerProcess *must* stop the sound system when sent from >> startPlayerProcessBufferSize:rate:stereo:sound:. Spo refactor a bit, >> renaming stopPlayerProcess to stopPlayerProcess: to take a hardStop >> boolean. When quitting the argument is false. >> >> Add a 64-bit specific, integer-overflow agnostic version of >> mixSampleCount:into:startingAt:leftVol:rightVol:, for creating a simpler >> inline primitive in the 64-bit VM. >> >> =============== Diff against Sound-eem.73 =============== >> >> Item was changed: >> ----- Method: AbstractSound class>>translatedPrimitives (in category >> 'primitive generation') ----- >> translatedPrimitives >> ^#( >> (FMSound >> mixSampleCount:into:startingAt:leftVol:rightVol:) >> (PluckedSound >> mixSampleCount:into:startingAt:leftVol:rightVol:) >> (LoopedSampledSound >> mixSampleCount:into:startingAt:leftVol:rightVol:) >> (SampledSound >> mixSampleCount:into:startingAt:leftVol:rightVol:) >> + (SampledSound >> _64bitMixSampleCount:into:startingAt:leftVol:rightVol:) >> (ReverbSound applyReverbTo:startingAt:count:) >> ). >> ! >> >> Item was added: >> + ----- Method: >> SampledSound>>_64bitMixSampleCount:into:startingAt:leftVol:rightVol: (in >> category 'playing') ----- >> + _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex >> leftVol: leftVol rightVol: rightVol >> + "Mix the given number of samples with the samples already in the >> given buffer starting at the given index. >> + Assume that the buffer size is at least (index + count) - 1." >> + >> + | lastIndex outIndex sampleIndex sample i s | >> + >> + >> + >> + >> + lastIndex := (startIndex + n) - 1. >> + outIndex := startIndex. "index of next stereo output sample >> pair" >> + sampleIndex := indexHighBits + (scaledIndex >> >> IncrementFractionBits). >> + [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] >> whileTrue: >> + [sample := ((samples at: sampleIndex) * scaledVol) // >> ScaleFactor. >> + leftVol > 0 ifTrue: >> + [i := (2 * outIndex) - 1. >> + s := (aSoundBuffer at: i) + ((sample * leftVol) >> // ScaleFactor). >> + s > 32767 ifTrue: [s := 32767]. "clipping!!" >> + s < -32767 ifTrue: [s := -32767]. "clipping!!" >> + aSoundBuffer at: i put: s]. >> + rightVol > 0 ifTrue: >> + [i := 2 * outIndex. >> + s := (aSoundBuffer at: i) + ((sample * rightVol) >> // ScaleFactor). >> + s > 32767 ifTrue: [s := 32767]. "clipping!!" >> + s < -32767 ifTrue: [s := -32767]. "clipping!!" >> + aSoundBuffer at: i put: s]. >> + >> + scaledVolIncr ~= 0 ifTrue: >> + [scaledVol := scaledVol + scaledVolIncr. >> + ((scaledVolIncr > 0 and: [scaledVol >= >> scaledVolLimit]) or: >> + [scaledVolIncr < 0 and: [scaledVol <= >> scaledVolLimit]]) >> + ifTrue: "reached the limit; stop >> incrementing" >> + [scaledVol := scaledVolLimit. >> + scaledVolIncr := 0]]. >> + >> + scaledIndex := scaledIndex + scaledIncrement. >> + >> + sampleIndex := indexHighBits + (scaledIndex >> >> IncrementFractionBits). >> + outIndex := outIndex + 1]. >> + count := count - n >> + ! >> >> Item was changed: >> ----- Method: >> SampledSound>>mixSampleCount:into:startingAt:leftVol:rightVol: (in category >> 'playing') ----- >> mixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: >> leftVol rightVol: rightVol >> + "Mix the given number of samples with the samples already in the >> given buffer starting at the given index. >> + Assume that the buffer size is at least (index + count) - 1." >> - "Mix the given number of samples with the samples already in the >> given buffer starting at the given index. Assume that the buffer size is at >> least (index + count) - 1." >> >> | lastIndex outIndex sampleIndex sample i s overflow | >> + > 'SoundGenerationPlugin'> >> + >> + >> - > module:'SoundGenerationPlugin'> >> - >> - >> >> + SmallInteger maxVal > 16r3FFFFFFF ifTrue: "In 64-bits we don't >> have to worry about 2^15 * 2^15 overflow" >> + [^self _64bitMixSampleCount: n into: aSoundBuffer >> startingAt: startIndex leftVol: leftVol rightVol: rightVol]. >> lastIndex := (startIndex + n) - 1. >> outIndex := startIndex. "index of next stereo output sample >> pair" >> sampleIndex := indexHighBits + (scaledIndex >> >> IncrementFractionBits). >> [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] >> whileTrue: [ >> sample := ((samples at: sampleIndex) * scaledVol) // >> ScaleFactor. >> leftVol > 0 ifTrue: [ >> i := (2 * outIndex) - 1. >> s := (aSoundBuffer at: i) + ((sample * leftVol) >> // ScaleFactor). >> s > 32767 ifTrue: [s := 32767]. "clipping!!" >> s < -32767 ifTrue: [s := -32767]. "clipping!!" >> aSoundBuffer at: i put: s]. >> rightVol > 0 ifTrue: [ >> i := 2 * outIndex. >> s := (aSoundBuffer at: i) + ((sample * rightVol) >> // ScaleFactor). >> s > 32767 ifTrue: [s := 32767]. "clipping!!" >> s < -32767 ifTrue: [s := -32767]. "clipping!!" >> aSoundBuffer at: i put: s]. >> >> scaledVolIncr ~= 0 ifTrue: [ >> scaledVol := scaledVol + scaledVolIncr. >> ((scaledVolIncr > 0 and: [scaledVol >= >> scaledVolLimit]) or: >> [scaledVolIncr < 0 and: [scaledVol <= >> scaledVolLimit]]) >> ifTrue: [ "reached the limit; stop >> incrementing" >> scaledVol := scaledVolLimit. >> scaledVolIncr := 0]]. >> >> scaledIndex := scaledIndex + scaledIncrement. >> scaledIndex >= ScaledIndexOverflow ifTrue: [ >> overflow := scaledIndex >> IncrementFractionBits. >> indexHighBits := indexHighBits + overflow. >> scaledIndex := scaledIndex - (overflow << >> IncrementFractionBits)]. >> >> sampleIndex := indexHighBits + (scaledIndex >> >> IncrementFractionBits). >> outIndex := outIndex + 1]. >> + count := count - n! >> - count := count - n. >> - ! >> >> Item was changed: >> ----- Method: SoundPlayer class>>shutDown: (in category 'snapshotting') >> ----- >> shutDown: quitting >> "Stop player process, for example before snapshotting." >> >> + quitting ifTrue: >> + [self stopPlayerProcess: false. >> + ReverbState := nil]! >> - quitting ifTrue: [ >> - self stopPlayerProcess. >> - ReverbState := nil].! >> >> Item was changed: >> ----- Method: SoundPlayer >> class>>startPlayerProcessBufferSize:rate:stereo:sound: (in category 'player >> process') ----- >> startPlayerProcessBufferSize: bufferSize rate: samplesPerSecond stereo: >> stereoFlag sound: aSound >> "Start the sound player process. Terminate the old process, if >> any." >> "SoundPlayer startPlayerProcessBufferSize: 1000 rate: 11025 >> stereo: false" >> >> + self stopPlayerProcess: true. >> + aSound ifNotNil: "stopPlayerProcess: ensures ActiveSounds are >> empty..." >> + [ActiveSounds add: aSound]. >> - self stopPlayerProcess. >> - aSound >> - ifNil:[ActiveSounds := OrderedCollection new] >> - ifNotNil:[ActiveSounds := OrderedCollection with: >> aSound]. >> Buffer := SoundBuffer newStereoSampleCount: (bufferSize // 4) * >> 4. >> + LastBuffer ifNotNil: >> + [LastBuffer := SoundBuffer basicNew: Buffer basicSize]. >> - LastBuffer ifNotNil:[LastBuffer := SoundBuffer basicNew: Buffer >> basicSize]. >> PlayerSemaphore := Semaphore forMutualExclusion. >> SamplingRate := samplesPerSecond. >> Stereo := stereoFlag. >> SoundSupported := true. "Assume so" >> UseReadySemaphore := true. "set to false if ready semaphore not >> supported by VM" >> Smalltalk newExternalSemaphoreDo: [ :semaphore :index | >> ReadyForBuffer := semaphore. >> self primSoundStartBufferSize: Buffer stereoSampleCount >> rate: samplesPerSecond >> stereo: Stereo >> semaIndex: index ]. >> "Check if sound start prim was successful" >> SoundSupported ifFalse:[ >> Smalltalk unregisterExternalObject: ReadyForBuffer. >> ReadyForBuffer := nil. >> ^self ]. >> UseReadySemaphore >> ifTrue: [PlayerProcess := [SoundPlayer playLoop] >> newProcess] >> ifFalse: [PlayerProcess := [SoundPlayer >> oldStylePlayLoop] newProcess]. >> UseReverb ifTrue: [self startReverb]. >> >> PlayerProcess >> name: 'Sound Player (', ActiveSounds size asString, ')'; >> priority: Processor userInterruptPriority; >> resume! >> >> Item was removed: >> - ----- Method: SoundPlayer class>>stopPlayerProcess (in category 'player >> process') ----- >> - stopPlayerProcess >> - "Stop the sound player process." >> - "SoundPlayer stopPlayerProcess" >> - >> - PlayerProcess ifNotNil: >> - [PlayerProcess ~~ Processor activeProcess ifTrue: >> - [PlayerProcess terminate]. >> - PlayerProcess := nil]. >> - "Don't load the SoundPlugin if it is not in use..." >> - self soundPluginActive ifTrue: [self primSoundStop]. >> - ActiveSounds isEmpty ifFalse: >> - [ActiveSounds := OrderedCollection new]. >> - Buffer := nil. >> - PlayerSemaphore isEmpty ifFalse: >> - [PlayerSemaphore := Semaphore forMutualExclusion]. >> - ReadyForBuffer ifNotNil: >> - [Smalltalk unregisterExternalObject: ReadyForBuffer. >> - ReadyForBuffer := nil]! >> >> Item was added: >> + ----- Method: SoundPlayer class>>stopPlayerProcess: (in category >> 'player process') ----- >> + stopPlayerProcess: hardStop >> + "Stop the sound player process." >> + "SoundPlayer stopPlayerProcess" >> + >> + PlayerProcess ifNotNil: >> + [PlayerProcess ~~ Processor activeProcess ifTrue: >> + [PlayerProcess terminate]. >> + PlayerProcess := nil]. >> + (hardStop or: [self soundPluginActive]) ifTrue: [self >> primSoundStop]. >> + ActiveSounds isEmpty ifFalse: >> + [ActiveSounds := OrderedCollection new]. >> + Buffer := nil. >> + PlayerSemaphore isEmpty ifFalse: >> + [PlayerSemaphore := Semaphore forMutualExclusion]. >> + ReadyForBuffer ifNotNil: >> + [Smalltalk unregisterExternalObject: ReadyForBuffer. >> + ReadyForBuffer := nil]! >> >> >> >> > > -- > _,,,^..^,,,_ > best, Eliot > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From karlramberg at gmail.com Tue Sep 22 05:11:37 2020 From: karlramberg at gmail.com (karl ramberg) Date: Tue, 22 Sep 2020 07:11:37 +0200 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: On Tue, Sep 22, 2020 at 1:20 AM Chris Muller wrote: > I believe you're describing defacto habits ingrained into everyone from > years of using Microsoft's original UI design than a qualitative critique > of this particular design aspect. Yes, familiarity can matter (see below), > but the concept of *"Point to what you want to interact with,"* is such a > natural way of thinking and working for a human user. > Then explain how "mouse over to focus" can work on a multi touch interface? Every time you touch something the focus moves to where you touch. Multi touch is the most used interface in the world, and it is click to focus. Best, Karl > Having keyboard focus decoupled and *way off* somewhere else than where > the users' attention has since moved, often leads users to a similar > confusion that you described, but just from the other direction -- the user > wondering why *nothing is happening* when typing into a field -- but this > time because the list in which they last clicked just to check something > real-quick still has the keyboard focus. This is basically what happened > to Eliot.. > > Coupling it to the mouse pointer helps remove all confusion about where > keyboard focus is, and is a natural concept that can be assimilated in > about a day. Dump Windows for Linux, and the behavior can be extended into > the host system, too. > > Now, maybe for a single-use Form with a few fields used by external users > for something simple, designing it click-for-focus may be worth aligning to > Microsoft's defacto. But for all-day developer work involving multiple, > multi-paned windows, allocating power to pointing makes the system feel > "light and responsive" instead of heavy and cumbersome. For me, anyway... > :) > > Best, > Chris > > > > On Mon, Sep 21, 2020 at 8:12 AM Ron Teitelbaum wrote: > > > > > > On Sun, Sep 20, 2020 at 5:14 PM Chris Muller > wrote: > >>> > >>> > For example, imagine the power of having EVERY PIXEL on your > desktop being ready for ANY INPUT, regardless of window z-ordering, simply > by moving pointer there and pressing a mouse or keyboard button. > >>> > > >>> > >>> Here you see, we differ. It's likely partly due to growing up using > RISC OS and the original ST-80 UI ... > >> > >> > >> I grew up using MS Windows, the beast that made "click for focus" the > defacto disaster it is today. Not everyone is willing to change how they > work, but a few years ago I got a tendonitis that really made me notice > that I was doing, literally, hundreds upon hundreds, of extra clicks every > day for no other purpose than reassigning keyboard focus, which, as you > know, have to even be "strategic clicks" (window title bars, borders, etc.) > to avoid suffering unwanted selection change, otherwise having to click yet > again just to "get back" to what I as looking at. Not only the pain, but I > realized what a total time sink it was. The only way to escape was I had > to be willing to change, which actually turned out a lot easier than I > thought... > >> > >> > > Hover to select is one of the greatest causes of user confusion that I > encounter. Most windows users are used to clicking and moving the cursor > out of the way so they can see. > > > > Now imagine a text box. As a user you click on it so that you can type > into it. You move your mouse away to type into the box BUT NOTHING > HAPPENS. Why because your moving the mouse away from the text field took > focus away from the text field. > > > > Or you click and start typing but your mouse pointer is in the way so > you move it and now nothing you type goes into the field. We worked around > this issue by locking some of the important fields when a user selects it > but not all of them so it's not consistent. > > > > We also have the issue that if your mouse happens to be hovering above a > button things work for a minute but then stop working because the button is > capturing keystrokes. The user only moved their mouse. They didn't mean to > hover over a button or they clicked a button and got the result they > expected but had no reason to move their mouse cursor off the button > afterwards. > > > > While I understand the unnecessary click argument and the harm that > clicking does to your wrist, there is still a lot to be said for > consistency, user understanding, and user experience. > > > > Just my 2c. > > > > Ron Teitelbaum > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Tue Sep 22 06:54:11 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Tue, 22 Sep 2020 08:54:11 +0200 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: > On 22.09.2020, at 07:11, karl ramberg wrote: > > > > On Tue, Sep 22, 2020 at 1:20 AM Chris Muller wrote: > I believe you're describing defacto habits ingrained into everyone from years of using Microsoft's original UI design than a qualitative critique of this particular design aspect. Yes, familiarity can matter (see below), but the concept of "Point to what you want to interact with," is such a natural way of thinking and working for a human user. > > Then explain how "mouse over to focus" can work on a multi touch interface? > Every time you touch something the focus moves to where you touch. > Multi touch is the most used interface in the world, and it is click to focus. I don't know, but my phone distinguishes lightly touched, touched, and long touched and has dedicated effects for each of these (namely hover, click, and every now and then, context menu). -tobias > > Best, > Karl > > > > Having keyboard focus decoupled and way off somewhere else than where the users' attention has since moved, often leads users to a similar confusion that you described, but just from the other direction -- the user wondering why nothing is happening when typing into a field -- but this time because the list in which they last clicked just to check something real-quick still has the keyboard focus. This is basically what happened to Eliot.. > > Coupling it to the mouse pointer helps remove all confusion about where keyboard focus is, and is a natural concept that can be assimilated in about a day. Dump Windows for Linux, and the behavior can be extended into the host system, too. > > Now, maybe for a single-use Form with a few fields used by external users for something simple, designing it click-for-focus may be worth aligning to Microsoft's defacto. But for all-day developer work involving multiple, multi-paned windows, allocating power to pointing makes the system feel "light and responsive" instead of heavy and cumbersome. For me, anyway... :) > > Best, > Chris > > > > On Mon, Sep 21, 2020 at 8:12 AM Ron Teitelbaum wrote: > > > > > > On Sun, Sep 20, 2020 at 5:14 PM Chris Muller wrote: > >>> > >>> > For example, imagine the power of having EVERY PIXEL on your desktop being ready for ANY INPUT, regardless of window z-ordering, simply by moving pointer there and pressing a mouse or keyboard button. > >>> > > >>> > >>> Here you see, we differ. It's likely partly due to growing up using RISC OS and the original ST-80 UI ... > >> > >> > >> I grew up using MS Windows, the beast that made "click for focus" the defacto disaster it is today. Not everyone is willing to change how they work, but a few years ago I got a tendonitis that really made me notice that I was doing, literally, hundreds upon hundreds, of extra clicks every day for no other purpose than reassigning keyboard focus, which, as you know, have to even be "strategic clicks" (window title bars, borders, etc.) to avoid suffering unwanted selection change, otherwise having to click yet again just to "get back" to what I as looking at. Not only the pain, but I realized what a total time sink it was. The only way to escape was I had to be willing to change, which actually turned out a lot easier than I thought... > >> > >> > > Hover to select is one of the greatest causes of user confusion that I encounter. Most windows users are used to clicking and moving the cursor out of the way so they can see. > > > > Now imagine a text box. As a user you click on it so that you can type into it. You move your mouse away to type into the box BUT NOTHING HAPPENS. Why because your moving the mouse away from the text field took focus away from the text field. > > > > Or you click and start typing but your mouse pointer is in the way so you move it and now nothing you type goes into the field. We worked around this issue by locking some of the important fields when a user selects it but not all of them so it's not consistent. > > > > We also have the issue that if your mouse happens to be hovering above a button things work for a minute but then stop working because the button is capturing keystrokes. The user only moved their mouse. They didn't mean to hover over a button or they clicked a button and got the result they expected but had no reason to move their mouse cursor off the button afterwards. > > > > While I understand the unnecessary click argument and the harm that clicking does to your wrist, there is still a lot to be said for consistency, user understanding, and user experience. > > > > Just my 2c. > > > > Ron Teitelbaum > > From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 22 11:12:53 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 22 Sep 2020 11:12:53 +0000 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> , Message-ID: <3afbe0f1ba864bf69910ef6459354d64@student.hpi.uni-potsdam.de> > Then explain how "mouse over to focus" can work on a multi touch interface? > Every time you touch something the focus moves to where you touch. > Multi touch is the most used interface in the world, and it is click to focus. I believe the difference is that on touch screens, you have kind of random access to every point of the screen, whereas when using a mouse, every click requires you to move the cursor until you reach the desired point, which introduces an additional level of indirection. Probably you cannot find an ultimative design concept for different input methods - just remember how successful Microsoft was with its Universal Windows Platform ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Dienstag, 22. September 2020 07:11:37 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] find class is broken... On Tue, Sep 22, 2020 at 1:20 AM Chris Muller > wrote: I believe you're describing defacto habits ingrained into everyone from years of using Microsoft's original UI design than a qualitative critique of this particular design aspect. Yes, familiarity can matter (see below), but the concept of "Point to what you want to interact with," is such a natural way of thinking and working for a human user. Then explain how "mouse over to focus" can work on a multi touch interface? Every time you touch something the focus moves to where you touch. Multi touch is the most used interface in the world, and it is click to focus. Best, Karl Having keyboard focus decoupled and way off somewhere else than where the users' attention has since moved, often leads users to a similar confusion that you described, but just from the other direction -- the user wondering why nothing is happening when typing into a field -- but this time because the list in which they last clicked just to check something real-quick still has the keyboard focus. This is basically what happened to Eliot.. Coupling it to the mouse pointer helps remove all confusion about where keyboard focus is, and is a natural concept that can be assimilated in about a day. Dump Windows for Linux, and the behavior can be extended into the host system, too. Now, maybe for a single-use Form with a few fields used by external users for something simple, designing it click-for-focus may be worth aligning to Microsoft's defacto. But for all-day developer work involving multiple, multi-paned windows, allocating power to pointing makes the system feel "light and responsive" instead of heavy and cumbersome. For me, anyway... :) Best, Chris On Mon, Sep 21, 2020 at 8:12 AM Ron Teitelbaum > wrote: > > > On Sun, Sep 20, 2020 at 5:14 PM Chris Muller > wrote: >>> >>> > For example, imagine the power of having EVERY PIXEL on your desktop being ready for ANY INPUT, regardless of window z-ordering, simply by moving pointer there and pressing a mouse or keyboard button. >>> > >>> >>> Here you see, we differ. It's likely partly due to growing up using RISC OS and the original ST-80 UI ... >> >> >> I grew up using MS Windows, the beast that made "click for focus" the defacto disaster it is today. Not everyone is willing to change how they work, but a few years ago I got a tendonitis that really made me notice that I was doing, literally, hundreds upon hundreds, of extra clicks every day for no other purpose than reassigning keyboard focus, which, as you know, have to even be "strategic clicks" (window title bars, borders, etc.) to avoid suffering unwanted selection change, otherwise having to click yet again just to "get back" to what I as looking at. Not only the pain, but I realized what a total time sink it was. The only way to escape was I had to be willing to change, which actually turned out a lot easier than I thought... >> >> > Hover to select is one of the greatest causes of user confusion that I encounter. Most windows users are used to clicking and moving the cursor out of the way so they can see. > > Now imagine a text box. As a user you click on it so that you can type into it. You move your mouse away to type into the box BUT NOTHING HAPPENS. Why because your moving the mouse away from the text field took focus away from the text field. > > Or you click and start typing but your mouse pointer is in the way so you move it and now nothing you type goes into the field. We worked around this issue by locking some of the important fields when a user selects it but not all of them so it's not consistent. > > We also have the issue that if your mouse happens to be hovering above a button things work for a minute but then stop working because the button is capturing keystrokes. The user only moved their mouse. They didn't mean to hover over a button or they clicked a button and got the result they expected but had no reason to move their mouse cursor off the button afterwards. > > While I understand the unnecessary click argument and the harm that clicking does to your wrist, there is still a lot to be said for consistency, user understanding, and user experience. > > Just my 2c. > > Ron Teitelbaum -------------- next part -------------- An HTML attachment was scrubbed... URL: From tonyg at leastfixedpoint.com Tue Sep 22 11:33:55 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Tue, 22 Sep 2020 13:33:55 +0200 Subject: [squeak-dev] The Trunk: ST80-mt.258.mcz In-Reply-To: References: <20200917143749.GA67623@shell.msen.com> Message-ID: On 9/17/20 5:01 PM, Marcel Taeumel wrote: > no worries. Makes one even think about having such synchronization > queues for all processes in Squeak. At least, it is a nice thought > experiment on inter-process communiction in Squeak. It works well (being essentially the Actor model) :-) I've been really enjoying using my Actors package for my Squeak-cellphone work recently. Though, of course, I *would* say that, wouldn't I? Tony From nicolas.cellier.aka.nice at gmail.com Tue Sep 22 17:12:03 2020 From: nicolas.cellier.aka.nice at gmail.com (Nicolas Cellier) Date: Tue, 22 Sep 2020 19:12:03 +0200 Subject: [squeak-dev] find class is broken... In-Reply-To: <3afbe0f1ba864bf69910ef6459354d64@student.hpi.uni-potsdam.de> References: <1600523967143-0.post@n4.nabble.com> <3afbe0f1ba864bf69910ef6459354d64@student.hpi.uni-potsdam.de> Message-ID: Also consider that focus is decoupled from mouse position so as to enable focus shift thru keyboard. What i most hate like Tim is that operate menu (pop up) is coupled with select (it's a select and operate). Operate is already requiring some motion to pick the right menu item, so i find the coupling really tiring, focus on position accurately twice, once for where we press the button, and once where we release the button... We already focused once when selecting, so doing it twice is just too many. Le mar. 22 sept. 2020 à 13:12, Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> a écrit : > > Then explain how "mouse over to focus" can work on a multi touch > interface? > > Every time you touch something the focus moves to where you touch. > > Multi touch is the most used interface in the world, and it is click to > focus. > > I believe the difference is that on touch screens, you have kind of random > access to every point of the screen, whereas when using a mouse, every > click requires you to move the cursor until you reach the desired point, > which introduces an additional level of indirection. Probably you cannot > find an ultimative design concept for different input methods - just > remember how successful Microsoft was with its Universal Windows Platform > ... > > Best, > Christoph > ------------------------------ > *Von:* Squeak-dev im > Auftrag von karl ramberg > *Gesendet:* Dienstag, 22. September 2020 07:11:37 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] find class is broken... > > > > On Tue, Sep 22, 2020 at 1:20 AM Chris Muller wrote: > >> I believe you're describing defacto habits ingrained into everyone from >> years of using Microsoft's original UI design than a qualitative critique >> of this particular design aspect. Yes, familiarity can matter (see below), >> but the concept of *"Point to what you want to interact with,"* is such >> a natural way of thinking and working for a human user. >> > > Then explain how "mouse over to focus" can work on a multi touch > interface? > Every time you touch something the focus moves to where you touch. > Multi touch is the most used interface in the world, and it is click to > focus. > > Best, > Karl > > > >> Having keyboard focus decoupled and *way off* somewhere else than where >> the users' attention has since moved, often leads users to a similar >> confusion that you described, but just from the other direction -- the user >> wondering why *nothing is happening* when typing into a field -- but >> this time because the list in which they last clicked just to check >> something real-quick still has the keyboard focus. This is basically >> what happened to Eliot.. >> >> Coupling it to the mouse pointer helps remove all confusion about where >> keyboard focus is, and is a natural concept that can be assimilated in >> about a day. Dump Windows for Linux, and the behavior can be extended into >> the host system, too. >> >> Now, maybe for a single-use Form with a few fields used by external users >> for something simple, designing it click-for-focus may be worth aligning to >> Microsoft's defacto. But for all-day developer work involving multiple, >> multi-paned windows, allocating power to pointing makes the system feel >> "light and responsive" instead of heavy and cumbersome. For me, anyway... >> :) >> >> Best, >> Chris >> >> >> >> On Mon, Sep 21, 2020 at 8:12 AM Ron Teitelbaum wrote: >> > >> > >> > On Sun, Sep 20, 2020 at 5:14 PM Chris Muller >> wrote: >> >>> >> >>> > For example, imagine the power of having EVERY PIXEL on your >> desktop being ready for ANY INPUT, regardless of window z-ordering, simply >> by moving pointer there and pressing a mouse or keyboard button. >> >>> > >> >>> >> >>> Here you see, we differ. It's likely partly due to growing up using >> RISC OS and the original ST-80 UI ... >> >> >> >> >> >> I grew up using MS Windows, the beast that made "click for focus" the >> defacto disaster it is today. Not everyone is willing to change how they >> work, but a few years ago I got a tendonitis that really made me notice >> that I was doing, literally, hundreds upon hundreds, of extra clicks every >> day for no other purpose than reassigning keyboard focus, which, as you >> know, have to even be "strategic clicks" (window title bars, borders, etc.) >> to avoid suffering unwanted selection change, otherwise having to click yet >> again just to "get back" to what I as looking at. Not only the pain, but I >> realized what a total time sink it was. The only way to escape was I had >> to be willing to change, which actually turned out a lot easier than I >> thought... >> >> >> >> >> > Hover to select is one of the greatest causes of user confusion that I >> encounter. Most windows users are used to clicking and moving the cursor >> out of the way so they can see. >> > >> > Now imagine a text box. As a user you click on it so that you can type >> into it. You move your mouse away to type into the box BUT NOTHING >> HAPPENS. Why because your moving the mouse away from the text field took >> focus away from the text field. >> > >> > Or you click and start typing but your mouse pointer is in the way so >> you move it and now nothing you type goes into the field. We worked around >> this issue by locking some of the important fields when a user selects it >> but not all of them so it's not consistent. >> > >> > We also have the issue that if your mouse happens to be hovering above >> a button things work for a minute but then stop working because the button >> is capturing keystrokes. The user only moved their mouse. They didn't mean >> to hover over a button or they clicked a button and got the result they >> expected but had no reason to move their mouse cursor off the button >> afterwards. >> > >> > While I understand the unnecessary click argument and the harm that >> clicking does to your wrist, there is still a lot to be said for >> consistency, user understanding, and user experience. >> > >> > Just my 2c. >> > >> > Ron Teitelbaum >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 22 18:15:43 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 22 Sep 2020 18:15:43 0000 Subject: [squeak-dev] The Trunk: Tools-ct.987.mcz Message-ID: Eliot Miranda uploaded a new version of Tools to project The Trunk: http://source.squeak.org/trunk/Tools-ct.987.mcz ==================== Summary ==================== Name: Tools-ct.987 Author: ct Time: 3 September 2020, 2:45:32.786083 pm UUID: e07c870e-186e-f54a-9dbc-b766b1cb2bbb Ancestors: Tools-ct.985 Fixes several issues with accepting source in debuggers. Applies overall refactoring to Debugger>>#contents:notifying:. With this version, two concrete regressions are fixed that were introduced with the SistaV1 bytecode set (see [1]): - When compiling a method from a block context and answering subsequent parser notifications, make sure the source code is not lost. A variant of this issue was filed by Eliot (emm) in [2]. - When removing a method from a block context, make sure the stack is unwinded correctly. Further adjustments: - Don't restart the current context if a different selector is changed. - Update contentsSelection correctly without morphic hack. Works now in MVC, again. [1] http://forum.world.st/The-Inbox-Kernel-dtl-1310-mcz-td5113032.html [2] http://forum.world.st/tedious-programming-in-the-debugger-error-needs-fixing-td5109568.html =============== Diff against Tools-ct.985 =============== Item was changed: ----- Method: Debugger>>contents:notifying: (in category 'accessing') ----- contents: aText notifying: aController + "Accept new method source of the selected context." - "The retrieved information has changed and its source must now be updated. - In this case, the retrieved information is the method of the selected context." - | result selector classOfMethod category h ctxt newMethod | - contextStackIndex = 0 ifTrue: - [^false]. - self selectedContext isExecutingBlock ifTrue: - [h := self selectedContext activeHome. - h ifNil: - [self inform: 'Method for block not found on stack, can''t edit and continue'. - ^false]. - (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs) ifFalse: - [^false]. - self resetContext: h changeContents: false. - "N.B. Only reset the contents if the compilation succeeds. If contents are reset - when compilation fails both compiler error message and modifications are lost." - (result := self contents: aText notifying: aController) ifTrue: - [self contentsChanged]. - ^result]. + | selector classOfMethod category ctxt newMethod | + contextStackIndex = 0 ifTrue: [^ false]. + + "First, handle some edge cases" + selector := self selectedClass newParser parseSelector: aText. + "selector isDoIt ifTrue: [ + currentCompiledMethod := self compileDoIt: aText]." + self flag: #todo. "ct: Recompile doIt method *without* creating method litters!! See Compiler>>#evaluateCue:ifFail:." + selector = self selectedMessageName ifFalse: [ + "Different message compiled, delegating to super" + ^ super contents: aText notifying: aController]. + + self selectedContext isExecutingBlock ifTrue: [ + "If we are in a block context, we need to rewind the stack before ." + | home | + home := self selectedContext activeHome. + home ifNil: [ + self inform: 'Method for block not found on stack, can''t edit and continue' translated. + ^ false]. + (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs translated) ifFalse: [ + ^ false]. + + self resetContext: home changeContents: false. + "N.B. Only reset the contents if the compilation succeeds. If contents would be reset when compilation fails, both compiler error message and modifications were lost." + ^ (self contents: aText notifying: aController) + ifTrue: [self contentsChanged]; + yourself]. + classOfMethod := self selectedClass. category := self selectedMessageCategoryName. + + "Do the actual compilation" - selector := self selectedClass newParser parseSelector: aText. - (selector == self selectedMessageName - or: [(self selectedMessageName beginsWith: 'DoIt') - and: [selector numArgs = self selectedMessageName numArgs]]) ifFalse: - [self inform: 'can''t change selector'. - ^false]. selector := classOfMethod + compile: aText + classified: category + notifying: aController. + selector ifNil: [^ false]. "compilation cancelled" + + "Update views" - compile: aText - classified: category - notifying: aController. - selector ifNil: [^false]. "compile cancelled" contents := aText. newMethod := classOfMethod compiledMethodAt: selector. + newMethod isQuick ifTrue: [ + self cutBackExecutionToSenderContext]. - newMethod isQuick ifTrue: - [self cutBackExecutionToSenderContext]. ctxt := interruptedProcess popTo: self selectedContext. ctxt == self selectedContext + ifFalse: [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs translated] + ifTrue: [ + newMethod isQuick ifFalse: [ + interruptedProcess restartTopWith: newMethod. + self stepToStatement]. - ifFalse: - [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs] - ifTrue: - [newMethod isQuick ifFalse: - [interruptedProcess - restartTopWith: newMethod; - stepToSendOrReturn]. contextVariablesInspector object: nil]. self resetContext: ctxt. + + Project current addDeferredUIMessage: [ + self changed: #contentsSelection]. + ^ true! - Smalltalk isMorphic ifTrue: - [Project current world - addAlarm: #changed: - withArguments: #(contentsSelection) - for: self - at: (Time millisecondClockValue + 200)]. - ^true! Item was changed: ----- Method: Debugger>>contextStackIndex:oldContextWas: (in category 'private') ----- contextStackIndex: anInteger oldContextWas: oldContext "Change the context stack index to anInteger, perhaps in response to user selection." | isNewMethod | self saveReceiverInspectorState. self saveContextVariablesInspectorState. contextStackIndex := anInteger. anInteger = 0 ifTrue: [currentCompiledMethod := contents := nil. self changed: #contextStackIndex. self decorateButtons. self contentsChanged. contextVariablesInspector object: nil. receiverInspector context: nil; inspect: self receiver. ^self]. isNewMethod := oldContext isNil + or: [oldContext home method ~= (currentCompiledMethod := self selectedContext home method)]. - or: [oldContext method ~~ (currentCompiledMethod := self selectedContext method)]. isNewMethod ifTrue: [contents := self selectedMessage. self contentsChanged. self pcRange]. self changed: #contextStackIndex. self decorateButtons. contextVariablesInspector object: self selectedContext. self restoreContextVariablesInspectorState. receiverInspector context: self selectedContext; inspect: self receiver. self restoreReceiverInspectorState. isNewMethod ifFalse: [self changed: #contentsSelection]! Item was changed: ----- Method: Debugger>>findCleanHomeBelow: (in category 'context stack (message list)') ----- findCleanHomeBelow: method | dirtyIndex | dirtyIndex := contextStack size + 1. contextStack reverse detect: [:context | dirtyIndex := dirtyIndex - 1. + context home method = method homeMethod]. - context method = method]. ^ dirtyIndex + 1! Item was added: + ----- Method: Debugger>>tallyMenu: (in category 'controls') ----- + tallyMenu: aMenu + + ^ aMenu + "title: 'Tally' translated;" flag: #todo; "ct: Implement on PluggableMenuSpec" + addTranslatedList: #( + ('Tally selection' tallyIt 'evaluate current selection and measure the time') + ('Record send' doRecord 'record next message send')); + yourself! From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 22 18:19:02 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 22 Sep 2020 18:19:02 +0000 Subject: [squeak-dev] The Trunk: Tools-ct.987.mcz In-Reply-To: References: Message-ID: <5513b027b5cf496a887b88229a3738f3@student.hpi.uni-potsdam.de> Oh, thank you for merging, Eliot! Meanwhile, I almost had forgotten these fixes myself ... ;-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Dienstag, 22. September 2020 20:15:43 An: squeak-dev at lists.squeakfoundation.org; packages at lists.squeakfoundation.org Betreff: [squeak-dev] The Trunk: Tools-ct.987.mcz Eliot Miranda uploaded a new version of Tools to project The Trunk: http://source.squeak.org/trunk/Tools-ct.987.mcz ==================== Summary ==================== Name: Tools-ct.987 Author: ct Time: 3 September 2020, 2:45:32.786083 pm UUID: e07c870e-186e-f54a-9dbc-b766b1cb2bbb Ancestors: Tools-ct.985 Fixes several issues with accepting source in debuggers. Applies overall refactoring to Debugger>>#contents:notifying:. With this version, two concrete regressions are fixed that were introduced with the SistaV1 bytecode set (see [1]): - When compiling a method from a block context and answering subsequent parser notifications, make sure the source code is not lost. A variant of this issue was filed by Eliot (emm) in [2]. - When removing a method from a block context, make sure the stack is unwinded correctly. Further adjustments: - Don't restart the current context if a different selector is changed. - Update contentsSelection correctly without morphic hack. Works now in MVC, again. [1] http://forum.world.st/The-Inbox-Kernel-dtl-1310-mcz-td5113032.html [2] http://forum.world.st/tedious-programming-in-the-debugger-error-needs-fixing-td5109568.html =============== Diff against Tools-ct.985 =============== Item was changed: ----- Method: Debugger>>contents:notifying: (in category 'accessing') ----- contents: aText notifying: aController + "Accept new method source of the selected context." - "The retrieved information has changed and its source must now be updated. - In this case, the retrieved information is the method of the selected context." - | result selector classOfMethod category h ctxt newMethod | - contextStackIndex = 0 ifTrue: - [^false]. - self selectedContext isExecutingBlock ifTrue: - [h := self selectedContext activeHome. - h ifNil: - [self inform: 'Method for block not found on stack, can''t edit and continue'. - ^false]. - (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs) ifFalse: - [^false]. - self resetContext: h changeContents: false. - "N.B. Only reset the contents if the compilation succeeds. If contents are reset - when compilation fails both compiler error message and modifications are lost." - (result := self contents: aText notifying: aController) ifTrue: - [self contentsChanged]. - ^result]. + | selector classOfMethod category ctxt newMethod | + contextStackIndex = 0 ifTrue: [^ false]. + + "First, handle some edge cases" + selector := self selectedClass newParser parseSelector: aText. + "selector isDoIt ifTrue: [ + currentCompiledMethod := self compileDoIt: aText]." + self flag: #todo. "ct: Recompile doIt method *without* creating method litters!! See Compiler>>#evaluateCue:ifFail:." + selector = self selectedMessageName ifFalse: [ + "Different message compiled, delegating to super" + ^ super contents: aText notifying: aController]. + + self selectedContext isExecutingBlock ifTrue: [ + "If we are in a block context, we need to rewind the stack before ." + | home | + home := self selectedContext activeHome. + home ifNil: [ + self inform: 'Method for block not found on stack, can''t edit and continue' translated. + ^ false]. + (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs translated) ifFalse: [ + ^ false]. + + self resetContext: home changeContents: false. + "N.B. Only reset the contents if the compilation succeeds. If contents would be reset when compilation fails, both compiler error message and modifications were lost." + ^ (self contents: aText notifying: aController) + ifTrue: [self contentsChanged]; + yourself]. + classOfMethod := self selectedClass. category := self selectedMessageCategoryName. + + "Do the actual compilation" - selector := self selectedClass newParser parseSelector: aText. - (selector == self selectedMessageName - or: [(self selectedMessageName beginsWith: 'DoIt') - and: [selector numArgs = self selectedMessageName numArgs]]) ifFalse: - [self inform: 'can''t change selector'. - ^false]. selector := classOfMethod + compile: aText + classified: category + notifying: aController. + selector ifNil: [^ false]. "compilation cancelled" + + "Update views" - compile: aText - classified: category - notifying: aController. - selector ifNil: [^false]. "compile cancelled" contents := aText. newMethod := classOfMethod compiledMethodAt: selector. + newMethod isQuick ifTrue: [ + self cutBackExecutionToSenderContext]. - newMethod isQuick ifTrue: - [self cutBackExecutionToSenderContext]. ctxt := interruptedProcess popTo: self selectedContext. ctxt == self selectedContext + ifFalse: [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs translated] + ifTrue: [ + newMethod isQuick ifFalse: [ + interruptedProcess restartTopWith: newMethod. + self stepToStatement]. - ifFalse: - [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs] - ifTrue: - [newMethod isQuick ifFalse: - [interruptedProcess - restartTopWith: newMethod; - stepToSendOrReturn]. contextVariablesInspector object: nil]. self resetContext: ctxt. + + Project current addDeferredUIMessage: [ + self changed: #contentsSelection]. + ^ true! - Smalltalk isMorphic ifTrue: - [Project current world - addAlarm: #changed: - withArguments: #(contentsSelection) - for: self - at: (Time millisecondClockValue + 200)]. - ^true! Item was changed: ----- Method: Debugger>>contextStackIndex:oldContextWas: (in category 'private') ----- contextStackIndex: anInteger oldContextWas: oldContext "Change the context stack index to anInteger, perhaps in response to user selection." | isNewMethod | self saveReceiverInspectorState. self saveContextVariablesInspectorState. contextStackIndex := anInteger. anInteger = 0 ifTrue: [currentCompiledMethod := contents := nil. self changed: #contextStackIndex. self decorateButtons. self contentsChanged. contextVariablesInspector object: nil. receiverInspector context: nil; inspect: self receiver. ^self]. isNewMethod := oldContext isNil + or: [oldContext home method ~= (currentCompiledMethod := self selectedContext home method)]. - or: [oldContext method ~~ (currentCompiledMethod := self selectedContext method)]. isNewMethod ifTrue: [contents := self selectedMessage. self contentsChanged. self pcRange]. self changed: #contextStackIndex. self decorateButtons. contextVariablesInspector object: self selectedContext. self restoreContextVariablesInspectorState. receiverInspector context: self selectedContext; inspect: self receiver. self restoreReceiverInspectorState. isNewMethod ifFalse: [self changed: #contentsSelection]! Item was changed: ----- Method: Debugger>>findCleanHomeBelow: (in category 'context stack (message list)') ----- findCleanHomeBelow: method | dirtyIndex | dirtyIndex := contextStack size + 1. contextStack reverse detect: [:context | dirtyIndex := dirtyIndex - 1. + context home method = method homeMethod]. - context method = method]. ^ dirtyIndex + 1! Item was added: + ----- Method: Debugger>>tallyMenu: (in category 'controls') ----- + tallyMenu: aMenu + + ^ aMenu + "title: 'Tally' translated;" flag: #todo; "ct: Implement on PluggableMenuSpec" + addTranslatedList: #( + ('Tally selection' tallyIt 'evaluate current selection and measure the time') + ('Record send' doRecord 'record next message send')); + yourself! -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 22 18:47:08 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 22 Sep 2020 18:47:08 0000 Subject: [squeak-dev] The Trunk: Sound-eem.75.mcz Message-ID: Eliot Miranda uploaded a new version of Sound to project The Trunk: http://source.squeak.org/trunk/Sound-eem.75.mcz ==================== Summary ==================== Name: Sound-eem.75 Author: eem Time: 22 September 2020, 11:47:06.322437 am UUID: a4d7e0e8-269c-432f-ac18-25d2d7d1ff44 Ancestors: Sound-eem.74 In deference to strong opinions voice on squeak-dev reimplement teh 64-bit alternative for SampledSound>>mixSampleCount:into:startingAt:leftVol:rightVol:, and consequently fix a non-obvious regression in the 64-bit alternative. =============== Diff against Sound-eem.74 =============== Item was changed: ----- Method: AbstractSound class>>translatedPrimitives (in category 'primitive generation') ----- translatedPrimitives ^#( (FMSound mixSampleCount:into:startingAt:leftVol:rightVol:) (PluckedSound mixSampleCount:into:startingAt:leftVol:rightVol:) (LoopedSampledSound mixSampleCount:into:startingAt:leftVol:rightVol:) (SampledSound mixSampleCount:into:startingAt:leftVol:rightVol:) - (SampledSound _64bitMixSampleCount:into:startingAt:leftVol:rightVol:) (ReverbSound applyReverbTo:startingAt:count:) ). ! Item was removed: - ----- Method: SampledSound>>_64bitMixSampleCount:into:startingAt:leftVol:rightVol: (in category 'playing') ----- - _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol - "Mix the given number of samples with the samples already in the given buffer starting at the given index. - Assume that the buffer size is at least (index + count) - 1." - - | lastIndex outIndex sampleIndex sample i s | - - - - - lastIndex := (startIndex + n) - 1. - outIndex := startIndex. "index of next stereo output sample pair" - sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). - [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] whileTrue: - [sample := ((samples at: sampleIndex) * scaledVol) // ScaleFactor. - leftVol > 0 ifTrue: - [i := (2 * outIndex) - 1. - s := (aSoundBuffer at: i) + ((sample * leftVol) // ScaleFactor). - s > 32767 ifTrue: [s := 32767]. "clipping!!" - s < -32767 ifTrue: [s := -32767]. "clipping!!" - aSoundBuffer at: i put: s]. - rightVol > 0 ifTrue: - [i := 2 * outIndex. - s := (aSoundBuffer at: i) + ((sample * rightVol) // ScaleFactor). - s > 32767 ifTrue: [s := 32767]. "clipping!!" - s < -32767 ifTrue: [s := -32767]. "clipping!!" - aSoundBuffer at: i put: s]. - - scaledVolIncr ~= 0 ifTrue: - [scaledVol := scaledVol + scaledVolIncr. - ((scaledVolIncr > 0 and: [scaledVol >= scaledVolLimit]) or: - [scaledVolIncr < 0 and: [scaledVol <= scaledVolLimit]]) - ifTrue: "reached the limit; stop incrementing" - [scaledVol := scaledVolLimit. - scaledVolIncr := 0]]. - - scaledIndex := scaledIndex + scaledIncrement. - - sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). - outIndex := outIndex + 1]. - count := count - n - ! Item was changed: ----- Method: SampledSound>>mixSampleCount:into:startingAt:leftVol:rightVol: (in category 'playing') ----- mixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol "Mix the given number of samples with the samples already in the given buffer starting at the given index. Assume that the buffer size is at least (index + count) - 1." | lastIndex outIndex sampleIndex sample i s overflow | + lastIndex := startIndex + n - 1. - SmallInteger maxVal > 16r3FFFFFFF ifTrue: "In 64-bits we don't have to worry about 2^15 * 2^15 overflow" - [^self _64bitMixSampleCount: n into: aSoundBuffer startingAt: startIndex leftVol: leftVol rightVol: rightVol]. - lastIndex := (startIndex + n) - 1. outIndex := startIndex. "index of next stereo output sample pair" sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). + [sampleIndex <= samplesSize and: [outIndex <= lastIndex]] whileTrue: + [sample := (samples at: sampleIndex) * scaledVol // ScaleFactor. + leftVol > 0 ifTrue: + [i := 2 * outIndex - 1. + s := (aSoundBuffer at: i) + (sample * leftVol // ScaleFactor). + "clip the result!!!!" + aSoundBuffer at: i put: (s > 32767 ifTrue: [32767] ifFalse: [s < -32767 ifTrue: [-32767] ifFalse: [s]])]. + rightVol > 0 ifTrue: + [i := 2 * outIndex. + s := (aSoundBuffer at: i) + (sample * rightVol // ScaleFactor). + "clip the result!!!!" + aSoundBuffer at: i put: (s > 32767 ifTrue: [32767] ifFalse: [s < -32767 ifTrue: [-32767] ifFalse: [s]])]. - [(sampleIndex <= samplesSize) and: [outIndex <= lastIndex]] whileTrue: [ - sample := ((samples at: sampleIndex) * scaledVol) // ScaleFactor. - leftVol > 0 ifTrue: [ - i := (2 * outIndex) - 1. - s := (aSoundBuffer at: i) + ((sample * leftVol) // ScaleFactor). - s > 32767 ifTrue: [s := 32767]. "clipping!!" - s < -32767 ifTrue: [s := -32767]. "clipping!!" - aSoundBuffer at: i put: s]. - rightVol > 0 ifTrue: [ - i := 2 * outIndex. - s := (aSoundBuffer at: i) + ((sample * rightVol) // ScaleFactor). - s > 32767 ifTrue: [s := 32767]. "clipping!!" - s < -32767 ifTrue: [s := -32767]. "clipping!!" - aSoundBuffer at: i put: s]. + scaledVolIncr ~= 0 ifTrue: + [scaledVol := scaledVol + scaledVolIncr. + (scaledVolIncr > 0 + ifTrue: [scaledVol >= scaledVolLimit] + ifFalse: [scaledVol <= scaledVolLimit]) + ifTrue: "reached the limit; stop incrementing" + [scaledVol := scaledVolLimit. - scaledVolIncr ~= 0 ifTrue: [ - scaledVol := scaledVol + scaledVolIncr. - ((scaledVolIncr > 0 and: [scaledVol >= scaledVolLimit]) or: - [scaledVolIncr < 0 and: [scaledVol <= scaledVolLimit]]) - ifTrue: [ "reached the limit; stop incrementing" - scaledVol := scaledVolLimit. scaledVolIncr := 0]]. scaledIndex := scaledIndex + scaledIncrement. + "In 64-bits we don't have to worry about scaled indexes overflowing, + and in the primitive this is guard evaluated at compile time." + (SmallInteger maxVal <= 16r3FFFFFFF + and: [scaledIndex >= ScaledIndexOverflow]) ifTrue: + [overflow := scaledIndex >> IncrementFractionBits. - scaledIndex >= ScaledIndexOverflow ifTrue: [ - overflow := scaledIndex >> IncrementFractionBits. indexHighBits := indexHighBits + overflow. scaledIndex := scaledIndex - (overflow << IncrementFractionBits)]. sampleIndex := indexHighBits + (scaledIndex >> IncrementFractionBits). outIndex := outIndex + 1]. + "But we still have to update indexHighBits and scaledIndex correctly outside the loop..." + SmallInteger maxVal > 16r3FFFFFFF ifTrue: + [overflow := scaledIndex >> IncrementFractionBits. + indexHighBits := indexHighBits + overflow. + scaledIndex := scaledIndex - (overflow << IncrementFractionBits)]. count := count - n! From asqueaker at gmail.com Wed Sep 23 21:30:46 2020 From: asqueaker at gmail.com (Chris Muller) Date: Wed, 23 Sep 2020 16:30:46 -0500 Subject: [squeak-dev] find class is broken... In-Reply-To: References: <1600523967143-0.post@n4.nabble.com> Message-ID: > > On Tue, Sep 22, 2020 at 1:20 AM Chris Muller wrote: > >> I believe you're describing defacto habits ingrained into everyone from >> years of using Microsoft's original UI design than a qualitative critique >> of this particular design aspect. Yes, familiarity can matter (see below), >> but the concept of *"Point to what you want to interact with,"* is such >> a natural way of thinking and working for a human user. >> > > Then explain how "mouse over to focus" can work on a multi touch > interface? > By reversing the touch vs. tap+touch for accessing the meta-layer. But this would not be enough to make touch interfaces useful for authoring use-cases, while complicating its primary use-case as a consumption kiosk. > Every time you touch something the focus moves to where you touch. > Multi touch is the most used interface in the world, and it is click to > focus. > Not for complex IDE's. Only simple modal things like checking in to your flight or adding cherry syrup to your coke. :) Nicolas wrote: > Also consider that focus is decoupled from mouse position so as to enable focus shift thru keyboard. Good point. If the focus shift is done on #mouseEnter, it could still be shifted away, and then the next time the user "pointed" at something, would snap focus to whatever they pointed at. The mouse remains the main tool for setting focus, and the keyboard a sub input device. You could accidentally bump the mouse as long as it didn't knock it into a totally different widget. - Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 24 08:42:54 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 24 Sep 2020 08:42:54 0000 Subject: [squeak-dev] The Inbox: SUnit-ct.129.mcz Message-ID: Christoph Thiede uploaded a new version of SUnit to project The Inbox: http://source.squeak.org/inbox/SUnit-ct.129.mcz ==================== Summary ==================== Name: SUnit-ct.129 Author: ct Time: 24 September 2020, 10:42:52.868426 am UUID: 92e68d23-8472-5d48-96d3-8435bd56ac14 Ancestors: SUnit-pre.122 Proposal: Catch warnings and halts in test case execution as well as Errors. Catching (Error, Warning, Halt) is a common pattern to be (relatively) sure that no debugger will occur during an operation. For related usages, see Morph >> #fullBounds, WorldState >> #displayWorldSafely:, and many other places. IMO it is no desired behavior that the whole test execution, i.e. in a TestRunner, is interrupted because any method under test contains a halt or raises a DeprecationWarning, for example. Instead, the test should be listed as red. For a similar discussion, see https://github.com/hpi-swa/smalltalkCI/issues/470. I believe we already had talked about this on squeak-dev, but if I remember correctly, I cannot find the thread again. =============== Diff against SUnit-pre.122 =============== Item was changed: ----- Method: TestCase>>timeout:after: (in category 'private') ----- timeout: aBlock after: seconds "Evaluate the argument block. Time out if the evaluation is not complete after the given number of seconds. Handle the situation that a timeout may occur after a failure (during debug)" | theProcess delay watchdog | "the block will be executed in the current process" theProcess := Processor activeProcess. delay := Delay forSeconds: seconds. "make a watchdog process" watchdog := [ delay wait. "wait for timeout or completion" theProcess ifNotNil:[ theProcess signalException: (TestFailure new messageText: 'Test timed out') ] ] newProcess. "Watchdog needs to run at high priority to do its job (but not at timing priority)" watchdog priority: Processor timingPriority-1. "catch the timeout signal" watchdog resume. "start up the watchdog" + ^[aBlock on: TestFailure, (Error, Warning, Halt) do: [:ex| - ^[aBlock on: TestFailure, Error, Halt do:[:ex| theProcess := nil. ex pass. ]] ensure:[ "evaluate the receiver" theProcess := nil. "it has completed, so ..." delay delaySemaphore signal. "arrange for the watchdog to exit" ]! Item was added: + ----- Method: TestResult class>>exAllErrors (in category 'exceptions') ----- + exAllErrors + ^ self exError, Warning, Halt + ! Item was changed: ----- Method: TestResult>>runCase: (in category 'running') ----- runCase: aTestCase | testCasePassed timeToRun | testCasePassed := true. [timeToRun := [aTestCase runCase] timeToRunWithoutGC] on: self class failure do: [:signal | failures add: aTestCase. testCasePassed := false. signal return: false] + on: self class exAllErrors - on: self class error do: [:signal | errors add: aTestCase. testCasePassed := false. signal return: false]. testCasePassed ifTrue: [passed add: aTestCase]. self durations at: aTestCase put: timeToRun.! From lists at fniephaus.com Thu Sep 24 08:50:44 2020 From: lists at fniephaus.com (Fabio Niephaus) Date: Thu, 24 Sep 2020 10:50:44 +0200 Subject: [squeak-dev] The Inbox: SUnit-ct.129.mcz In-Reply-To: References: Message-ID: Hi Christoph, If you run all tests in your image without and with your change, does the number of test failures change? Fabio On Thu, Sep 24, 2020 at 10:42 AM wrote: > > Christoph Thiede uploaded a new version of SUnit to project The Inbox: > http://source.squeak.org/inbox/SUnit-ct.129.mcz > > ==================== Summary ==================== > > Name: SUnit-ct.129 > Author: ct > Time: 24 September 2020, 10:42:52.868426 am > UUID: 92e68d23-8472-5d48-96d3-8435bd56ac14 > Ancestors: SUnit-pre.122 > > Proposal: Catch warnings and halts in test case execution as well as Errors. > > Catching (Error, Warning, Halt) is a common pattern to be (relatively) sure that no debugger will occur during an operation. For related usages, see Morph >> #fullBounds, WorldState >> #displayWorldSafely:, and many other places. IMO it is no desired behavior that the whole test execution, i.e. in a TestRunner, is interrupted because any method under test contains a halt or raises a DeprecationWarning, for example. Instead, the test should be listed as red. > > For a similar discussion, see https://github.com/hpi-swa/smalltalkCI/issues/470. I believe we already had talked about this on squeak-dev, but if I remember correctly, I cannot find the thread again. > > =============== Diff against SUnit-pre.122 =============== > > Item was changed: > ----- Method: TestCase>>timeout:after: (in category 'private') ----- > timeout: aBlock after: seconds > "Evaluate the argument block. Time out if the evaluation is not > complete after the given number of seconds. Handle the situation > that a timeout may occur after a failure (during debug)" > > | theProcess delay watchdog | > > "the block will be executed in the current process" > theProcess := Processor activeProcess. > delay := Delay forSeconds: seconds. > > "make a watchdog process" > watchdog := [ > delay wait. "wait for timeout or completion" > theProcess ifNotNil:[ theProcess signalException: > (TestFailure new messageText: 'Test timed out') ] > ] newProcess. > > "Watchdog needs to run at high priority to do its job (but not at timing priority)" > watchdog priority: Processor timingPriority-1. > > "catch the timeout signal" > watchdog resume. "start up the watchdog" > + ^[aBlock on: TestFailure, (Error, Warning, Halt) do: [:ex| > - ^[aBlock on: TestFailure, Error, Halt do:[:ex| > theProcess := nil. > ex pass. > ]] ensure:[ "evaluate the receiver" > theProcess := nil. "it has completed, so ..." > delay delaySemaphore signal. "arrange for the watchdog to exit" > ]! > > Item was added: > + ----- Method: TestResult class>>exAllErrors (in category 'exceptions') ----- > + exAllErrors > + ^ self exError, Warning, Halt > + ! > > Item was changed: > ----- Method: TestResult>>runCase: (in category 'running') ----- > runCase: aTestCase > > | testCasePassed timeToRun | > testCasePassed := true. > > [timeToRun := [aTestCase runCase] timeToRunWithoutGC] > on: self class failure > do: [:signal | > failures add: aTestCase. > testCasePassed := false. > signal return: false] > + on: self class exAllErrors > - on: self class error > do: [:signal | > errors add: aTestCase. > testCasePassed := false. > signal return: false]. > > testCasePassed ifTrue: [passed add: aTestCase]. > self durations at: aTestCase put: timeToRun.! > > From christoph.thiede at student.hpi.uni-potsdam.de Thu Sep 24 09:09:12 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Thu, 24 Sep 2020 04:09:12 -0500 (CDT) Subject: [squeak-dev] Why is ModificationForbidden not an Error? In-Reply-To: References: <93CBEDF7-D881-4B30-9142-004918314878@rowledge.org> Message-ID: <1600938552895-0.post@n4.nabble.com> Hi Chris, hi all, after months of stagnation, I'd like to bring up this open discussion again. IMO the current situation in the Trunk is still buggy and the last thing we want developers to have to care about. For example, I recently crashed a number of smalltalkCI builds again because some method raised a ModificationForbidden, which the test runner, obviously, was not prepared for ... >From what I can see, the only problem with my proposal that resists still in the inbox, Kernel-ct.1321, is related to Chris' Magma implementation. I want to respect this use case (and actually, learning a bit more about Magma is already on my long, long list of nice-to-do stuff), but I have to agree with other people saying that ModificationForbidden is a very low-level error and should not need any special treatment. For this reason, I also think that Marcel's changeset is a bit over-engineered for the Trunk, given the particular fact that we do not have plans for any user of ManagedObjects in the Trunk. Chris, did anything happen on your end regarding the handling of ModificationForbidden in Magma? Iirc you mentioned recently that you have reached some kind of feature completeness in your project. Can't we just merge Kernel-ct.1321 for now and resolve this discussion until some new use case arises? For Magma as an external project (still without being familiar at all with its design), I don't understand what would be wrong with handling such exceptions using #on:do:, this would totally suffice to circumvent the #defaultAction implementation. If you do not have the possibility to install an exception handler or a ToolSet, you could still place an extension override method in ModificationForbidden and (conditionally) inject your custom handling logic there. What do you think? I would like to finally get ahead with this issue! :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From commits at source.squeak.org Thu Sep 24 09:28:33 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 24 Sep 2020 09:28:33 0000 Subject: [squeak-dev] The Inbox: SUnit-ct.130.mcz Message-ID: Christoph Thiede uploaded a new version of SUnit to project The Inbox: http://source.squeak.org/inbox/SUnit-ct.130.mcz ==================== Summary ==================== Name: SUnit-ct.130 Author: ct Time: 24 September 2020, 11:28:31.626426 am UUID: 43df5072-3a69-634c-946a-ec59903c2519 Ancestors: SUnit-pre.122 Deprecates TestResult class >> #error and replaces sends to it with #exError. It is a bad and erroneous practice to override #error in a way that does not signal an error but returns a class object. =============== Diff against SUnit-pre.122 =============== Item was changed: ----- Method: SUnitTest>>testDialectLocalizedException (in category 'tests') ----- testDialectLocalizedException self should: [TestResult signalFailureWith: 'Foo'] raise: TestResult failure. self should: [TestResult signalErrorWith: 'Foo'] + raise: TestResult exError.! - raise: TestResult error. - - ! Item was changed: ----- Method: SUnitTest>>testException (in category 'tests') ----- testException self should: [self error: 'foo'] + raise: TestResult exError! - raise: TestResult error - ! Item was changed: ----- Method: SUnitTest>>testWithExceptionDo (in category 'tests') ----- testWithExceptionDo self should: [self error: 'foo'] + raise: TestResult exError - raise: TestResult error withExceptionDo: [:exception | self assert: (exception description includesSubstring: 'foo') + ]! - ] - ! Item was changed: ----- Method: TestResult class>>error (in category 'exceptions') ----- error + + self deprecated: 'ct: Send #exError to retrieve an exception class or #error: to signal an error, depending on what you need.'. + ^ self exError! - ^self exError - ! Item was changed: ----- Method: TestResult class>>signalErrorWith: (in category 'exceptions') ----- signalErrorWith: aString + ^ self exError signal: aString! - self error signal: aString - ! From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 24 10:22:19 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 24 Sep 2020 10:22:19 +0000 Subject: [squeak-dev] The Inbox: SUnit-ct.129.mcz In-Reply-To: References: , Message-ID: <64f7d89110c041f49cf0bdf17a021383@student.hpi.uni-potsdam.de> Hi Fabio, hi all, very good question (and after having waited about twenty minutes for two test executions and comparing them manually, I once again wish we had CI for every inbox commits ...)! And actually, I broke the DecompilerTests, e.g. #testBlockNumbering, because somewhere during compilation, an UndeclaredVariableWarning is raised. All other tests, however, did not change their result after loading this inbox commit. This behavior leads me to the question of whether it is justified to derive UndeclaredVariableWarning from Warning, given its #defaultAction implementation making it behave like a Notification. Alternatively, what could we do else if it is not acceptable to interrupt a test once it raises a warning? As I proposed somewhere else, we could catch UnhandledError instead of all these classes eventually open a debugger, but Marcel told me that this would be an implementation detail of the EHS and should not be exposed. Still, I cannot imagine any other way to write an exception handler that detects whether an exception will "raise a problem" eventually or whether it won't. Hmm ... What do you think? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Fabio Niephaus Gesendet: Donnerstag, 24. September 2020 10:50:44 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] The Inbox: SUnit-ct.129.mcz Hi Christoph, If you run all tests in your image without and with your change, does the number of test failures change? Fabio On Thu, Sep 24, 2020 at 10:42 AM wrote: > > Christoph Thiede uploaded a new version of SUnit to project The Inbox: > http://source.squeak.org/inbox/SUnit-ct.129.mcz > > ==================== Summary ==================== > > Name: SUnit-ct.129 > Author: ct > Time: 24 September 2020, 10:42:52.868426 am > UUID: 92e68d23-8472-5d48-96d3-8435bd56ac14 > Ancestors: SUnit-pre.122 > > Proposal: Catch warnings and halts in test case execution as well as Errors. > > Catching (Error, Warning, Halt) is a common pattern to be (relatively) sure that no debugger will occur during an operation. For related usages, see Morph >> #fullBounds, WorldState >> #displayWorldSafely:, and many other places. IMO it is no desired behavior that the whole test execution, i.e. in a TestRunner, is interrupted because any method under test contains a halt or raises a DeprecationWarning, for example. Instead, the test should be listed as red. > > For a similar discussion, see https://github.com/hpi-swa/smalltalkCI/issues/470. I believe we already had talked about this on squeak-dev, but if I remember correctly, I cannot find the thread again. > > =============== Diff against SUnit-pre.122 =============== > > Item was changed: > ----- Method: TestCase>>timeout:after: (in category 'private') ----- > timeout: aBlock after: seconds > "Evaluate the argument block. Time out if the evaluation is not > complete after the given number of seconds. Handle the situation > that a timeout may occur after a failure (during debug)" > > | theProcess delay watchdog | > > "the block will be executed in the current process" > theProcess := Processor activeProcess. > delay := Delay forSeconds: seconds. > > "make a watchdog process" > watchdog := [ > delay wait. "wait for timeout or completion" > theProcess ifNotNil:[ theProcess signalException: > (TestFailure new messageText: 'Test timed out') ] > ] newProcess. > > "Watchdog needs to run at high priority to do its job (but not at timing priority)" > watchdog priority: Processor timingPriority-1. > > "catch the timeout signal" > watchdog resume. "start up the watchdog" > + ^[aBlock on: TestFailure, (Error, Warning, Halt) do: [:ex| > - ^[aBlock on: TestFailure, Error, Halt do:[:ex| > theProcess := nil. > ex pass. > ]] ensure:[ "evaluate the receiver" > theProcess := nil. "it has completed, so ..." > delay delaySemaphore signal. "arrange for the watchdog to exit" > ]! > > Item was added: > + ----- Method: TestResult class>>exAllErrors (in category 'exceptions') ----- > + exAllErrors > + ^ self exError, Warning, Halt > + ! > > Item was changed: > ----- Method: TestResult>>runCase: (in category 'running') ----- > runCase: aTestCase > > | testCasePassed timeToRun | > testCasePassed := true. > > [timeToRun := [aTestCase runCase] timeToRunWithoutGC] > on: self class failure > do: [:signal | > failures add: aTestCase. > testCasePassed := false. > signal return: false] > + on: self class exAllErrors > - on: self class error > do: [:signal | > errors add: aTestCase. > testCasePassed := false. > signal return: false]. > > testCasePassed ifTrue: [passed add: aTestCase]. > self durations at: aTestCase put: timeToRun.! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 24 10:23:29 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 24 Sep 2020 10:23:29 0000 Subject: [squeak-dev] The Inbox: ToolsTests-ct.99.mcz Message-ID: A new version of ToolsTests was added to project The Inbox: http://source.squeak.org/inbox/ToolsTests-ct.99.mcz ==================== Summary ==================== Name: ToolsTests-ct.99 Author: ct Time: 24 September 2020, 12:23:28.743708 pm UUID: 3344a54d-786b-f948-b7c3-dc44fc21fe50 Ancestors: ToolsTests-mt.98 Increases timeout for WeakSetInspectorTest >> #testSymbolTableM6812 which includes a call to the GC. =============== Diff against ToolsTests-mt.98 =============== Item was changed: ----- Method: WeakSetInspectorTest>>testSymbolTableM6812 (in category 'tests') ----- testSymbolTableM6812 "This test is related to http://bugs.squeak.org/view.php?id=6812. Check whether field selection and garbage collection are somehow interfering." + "#garbageCollect can be an expensive operation" | getRandomSymbol symbols priorContents currentContents currentIndex | self object removeAll. getRandomSymbol := [ | token | token := (1 to: 10) collect: [:ea | ($a to: $z) atRandom] as: String. (Symbol lookup: token) ifNil: [token asSymbol] ifNotNil: [nil]]. symbols := OrderedCollection new. 10 timesRepeat: [ getRandomSymbol value ifNotNil: [:symbol | symbols add: symbol]]. self object addAll: symbols. Smalltalk garbageCollect. self assert: symbols size equals: self object size. self assert: symbols notEmpty. 1 to: symbols size do: [:round | currentIndex := 1. symbols removeLast. [(currentIndex := currentIndex + 1) <= self inspector fieldList size] whileTrue: [ self inspector selectionIndex: currentIndex. self assert: priorContents ~= (currentContents := self inspector contents). priorContents := currentContents. Smalltalk garbageCollect. "Removes symbol from weak set" self simulateStepping. "Removes field from weak-set inspector"]]. self assert: symbols isEmpty. self assert: self object isEmpty.! From Christoph.Thiede at student.hpi.uni-potsdam.de Thu Sep 24 12:08:54 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Thu, 24 Sep 2020 12:08:54 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <20200914154934.GA85344@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> , <20200914154934.GA85344@shell.msen.com> Message-ID: Hi all, sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: * Use DynamicVariables for storing the active variable states * Refactor and reduce the visibility of active variable accessors on Object * Clean up implementations of #activeHand and #primaryHand @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. PS: Thanks for the hint to the new wiki page! Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Montag, 14. September 2020 17:49:34 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong? Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: Hide activeVariables.3.cs URL: From lists at fniephaus.com Thu Sep 24 12:36:51 2020 From: lists at fniephaus.com (Fabio Niephaus) Date: Thu, 24 Sep 2020 14:36:51 +0200 Subject: [squeak-dev] Fwd: [CRON] Errored: squeak-smalltalk/squeak-app#1837 (squeak-5.1 - b3d176f) In-Reply-To: <5f6bdbbe7b877_13ff229eb4e603052f0@travis-tasks-74bf4bd6d6-2z5tn.mail> References: <5f6bdbbe7b877_13ff229eb4e603052f0@travis-tasks-74bf4bd6d6-2z5tn.mail> Message-ID: Hi all, I've disabled monthly rebuilds for Squeak 5.1 and 5.2 as they are failing due to an outdated version of XCode. Fabio ---------- Forwarded message --------- From: Travis CI Date: Thu, Sep 24, 2020 at 1:35 AM Subject: [CRON] Errored: squeak-smalltalk/squeak-app#1837 (squeak-5.1 - b3d176f) To: squeak-smalltalk / squeak-app [image: branch icon]squeak-5.1 [image: build has errored] Build #1837 has errored [image: arrow to build time] [image: clock icon]30 mins and 54 secs [image: Fabio Niephaus avatar]Fabio Niephaus b3d176f CHANGESET → Build and deploy Squeak(64)-5.1 bundles only Want to know about upcoming build environment updates? Would you like to stay up-to-date with the upcoming Travis CI build environment updates? We set up a mailing list for you! SIGN UP HERE [image: book icon] Documentation about Travis CI Have any questions? We're here to help. Unsubscribe from build emails from the squeak-smalltalk/squeak-app repository. To unsubscribe from *all* build emails, please update your settings . [image: black and white travis ci logo] Travis CI GmbH, Rigaer Str. 8, 10427 Berlin, Germany | GF/CEO: Randy Jacops | Contact: contact at travis-ci.com | Amtsgericht Charlottenburg, Berlin, HRB 140133 B | Umsatzsteuer-ID gemäß §27 a Umsatzsteuergesetz: DE282002648 -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Thu Sep 24 15:03:59 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Thu, 24 Sep 2020 11:03:59 -0400 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <20200914154934.GA85344@shell.msen.com> Message-ID: <20200924150359.GA88607@shell.msen.com> Hi Christoph, I am using an up to date trunk image. My image locks up about half way through loading the change set. I cannot interrupt it at that point so I'm not sure what the issue may be. Is there some prerequisite that I need? BTW, Marcel explained to me during a recent board meeting that the process local variables are intended to address issues related to the debugger, so now I understand the reason for it. Thanks, I'm looking forward to trying this. Dave On Thu, Sep 24, 2020 at 12:08:54PM +0000, Thiede, Christoph wrote: > Hi all, > > > sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: > > > * Use DynamicVariables for storing the active variable states > > * Refactor and reduce the visibility of active variable accessors on Object > > * Clean up implementations of #activeHand and #primaryHand > > > @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. > > > > > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. > > I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. > A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. > > PS: Thanks for the hint to the new wiki page! > > Best, > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Montag, 14. September 2020 17:49:34 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > > Hi Dave, > > > > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > > > > What do you think? > > > > Hi Christoph, > > The thing that I like (a lot) about your changes is that they make it > much easier to see and understand the accesses to the global variables. > The references now all go through a small number of methods in Object, > so that you can see what they do now. > > Clearly we would want to leave this logic in Object, since we don't > want to add more clutter to its protocol. But you have collected the logic > in one place now, which makes it easier to think about where it ultimately > should go, and that's great. > > But where do these things actually belong? Thinking in terms of the > responsiblities of objects in the system [1], I don't think that associating > them with a Process seems to be a good fit. It's true that in Morphic there > is one distinct Process associated with the user interface for each Morphic > world. So you can make things work by associating these variables with > a Process, but it seems like an unnatural fit to me. A Morphic world has > a process, but a Process is not responsible for knowing the state of > the Morphic world. > > Dave > > [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares. > > Content-Description: Hide activeVariables.3.cs > 'From Squeak6.0alpha of 6 September 2020 [latest update: #19838] on 24 September 2020 at 1:53:22 pm'! DynamicVariable subclass: #ActiveEventVariable instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Worlds'! DynamicVariable subclass: #ActiveHandVariable instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Worlds'! DynamicVariable subclass: #ActiveWorldVariable instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Worlds'! !Object methodsFor: 'user interface' stamp: 'ct 9/12/2020 14:13'! launchPartVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins" | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph openInHand.! ! !Object methodsFor: '*Protocols' stamp: 'ct 9/12/2020 14:14'! haveFullProtocolBrowsedShowingSelector: aSelector "Open up a Lexicon on the receiver, having it open up showing aSelector, which may be nil" "(2 at 3) haveFullProtocolBrowsed" | aBrowser | aBrowser := (Smalltalk at: #InstanceBrowser ifAbsent: [^ nil]) new useVocabulary: Vocabulary fullVocabulary. aBrowser openOnObject: self inWorld: Project current world showingSelector: aSelector! ! !Object methodsFor: '*Etoys-Squeakland-user interface' stamp: 'ct 9/12/2020 14:13'! launchPartOffsetVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins. This variant makes the morph offset from the hand position by an amount suitable for tile-scripting in some circumstances." | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph setProperty: #offsetForAttachingToHand toValue: 10@ -10. aMorph fullBounds. aMorph openInHand! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/24/2020 13:46'! activeHand "Answer the active hand for the current world. Fallback implementation for non-Morphic worlds. Private!! Usually, you want to send #currentHand instead." self == self currentWorld ifTrue: [ ^ nil]. ^ self currentHand! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/16/2020 20:28'! currentEvent "Answer the current MorphicEvent. Provided that a morphic project is loaded, this method never returns nil." ^ ActiveEventVariable value! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/24/2020 13:44'! currentHand "Answer the current HandMorph. Provided that a morphic project is loaded, this method will never return nil." ^ ActiveHandVariable value ifNil: [self currentWorld activeHand]! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/16/2020 20:27'! currentWorld "Answer the current world. This method will never return nil." ^ ActiveWorldVariable value! ! !ActiveEventVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:09'! default ^ self currentHand ifNotNil: [:hand | hand lastEvent]! ! !ActiveEventVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 19:42'! value: anObject during: aBlock "For backword compatibility <6.0, still maintain the ActiveEvent global." | priorEvent | priorEvent := self value. ActiveEvent := anObject. ^ [super value: anObject during: aBlock] ensure: [ ActiveEvent := priorEvent]! ! !ActiveHandVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:14'! default ^ self currentWorld activeHand! ! !ActiveHandVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 19:43'! value: anObject during: aBlock "For backword compatibility <6.0, still maintain the ActiveHand global." | priorHand | priorHand := self value. ActiveHand := anObject. ^ [super value: anObject during: aBlock] ensure: [ ActiveHand := priorHand]! ! !ActiveWorldVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 18:14'! default ^ Project current world! ! !ActiveWorldVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 18:31'! value: anObject during: aBlock "For backword compatibility <6.0, still maintain the ActiveWorld global." | priorWorld | priorWorld := self value. ActiveWorld := anObject. ^ [super value: anObject during: aBlock] ensure: [ ActiveWorld := priorWorld]! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:05'! currentBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: Browser]]) asSet! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:04'! currentHierarchyBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: HierarchyBrowser]]) asSet! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'Your name?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint onCancelReturn: cancelResponse! ! !Flaps class methodsFor: 'construction support' stamp: 'ct 9/11/2020 20:09'! possiblyReplaceEToyFlaps "If in eToyFriendly mode, and if it's ok to reinitialize flaps, replace the existing flaps with up-too-date etoy flaps. Caution: this is destructive of existing flaps. If preserving the contents of existing flaps is important, set the preference 'okToReinitializeFlaps' to true" PartsBin thumbnailForPartsDescription: StickyPadMorph descriptionForPartsBin. "Puts StickyPadMorph's custom icon back in the cache which typically will have been called" (Preferences eToyFriendly and: [Preferences okToReinitializeFlaps]) ifTrue: [Flaps disableGlobalFlaps: false. Flaps addAndEnableEToyFlaps. Smalltalk isMorphic ifTrue: [Project current world enableGlobalFlaps]]. "PartsBin clearThumbnailCache" "Flaps possiblyReplaceEToyFlaps"! ! !Flaps class methodsFor: 'menu commands' stamp: 'ct 9/11/2020 19:49'! disableGlobalFlaps: interactive "Clobber all the shared flaps structures. First read the user her Miranda rights." interactive ifTrue: [(self confirm: 'CAUTION!! This will destroy all the shared flaps, so that they will not be present in *any* project. If, later, you want them back, you will have to reenable them, from this same menu, whereupon the standard default set of shared flaps will be created. Do you really want to go ahead and clobber all shared flaps at this time?' translated) ifFalse: [^ self]]. self globalFlapTabsIfAny do: [:aFlapTab | self removeFlapTab: aFlapTab keepInList: false. aFlapTab isInWorld ifTrue: [self error: 'Flap problem' translated]]. self clobberFlapTabList. self initializeFlapsQuads. SharedFlapsAllowed := false. Smalltalk isMorphic ifTrue: [ Project current world restoreMorphicDisplay; reformulateUpdatingMenus]. "The following reduces the risk that flaps will be created with variant IDs such as 'Stack Tools2', potentially causing some shared flap logic to fail." "Smalltalk garbageCollect." "-- see if we are OK without this"! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:08'! enableGlobalFlaps "Start using global flaps, given that they were not present." Cursor wait showWhile: [ SharedFlapsAllowed := true. self globalFlapTabs. "This will create them" Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. FlapTab allInstancesDo: [:tab | tab computeEdgeFraction]. Project current world reformulateUpdatingMenus]]! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:09'! setUpSuppliesFlapOnly "Set up the Supplies flap as the only shared flap. A special version formulated for this stand-alone use is used, defined in #newLoneSuppliesFlap" | supplies | SharedFlapTabs isEmptyOrNil ifFalse: "get rid of pre-existing guys if any" [SharedFlapTabs do: [:t | t referent delete. t delete]]. SharedFlapsAllowed := true. SharedFlapTabs := OrderedCollection new. SharedFlapTabs add: (supplies := self newLoneSuppliesFlap). self enableGlobalFlapWithID: 'Supplies' translated. supplies setToPopOutOnMouseOver: false. Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps; reformulateUpdatingMenus].! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:08'! enableClassicNavigatorChanged "The #classicNavigatorEnabled preference has changed. No senders in easily traceable in the image, but this is really sent by a Preference object!!" Preferences classicNavigatorEnabled ifTrue: [Flaps disableGlobalFlapWithID: 'Navigator' translated. Preferences enable: #showProjectNavigator. self disableGlobalFlapWithID: 'Navigator' translated.] ifFalse: [self enableGlobalFlapWithID: 'Navigator' translated. Project current world addGlobalFlaps]. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. Project current world reformulateUpdatingMenus.! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:09'! makeNavigatorFlapResembleGoldenBar "At explicit request, make the flap-based navigator resemble the golden bar. No senders in the image, but sendable from a doit" "Flaps makeNavigatorFlapResembleGoldenBar" Preferences setPreference: #classicNavigatorEnabled toValue: false. Preferences setPreference: #showProjectNavigator toValue: false. (self globalFlapTabWithID: 'Navigator' translated) ifNil: [SharedFlapTabs add: self newNavigatorFlap delete]. self enableGlobalFlapWithID: 'Navigator' translated. Preferences setPreference: #navigatorOnLeftEdge toValue: true. (self globalFlapTabWithID: 'Navigator' translated) arrangeToPopOutOnMouseOver: true. Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. ! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:58'! addLocalFlap ^ self addLocalFlap: self currentEvent! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent "Menu command -- let the user add a new project-local flap. Once the new flap is born, the user can tell it to become a shared flap. Obtain an initial name and edge for the flap, launch the flap, and also launch a menu governing the flap, so that the user can get started right away with customizing it." | title edge | edge := self askForEdgeOfNewFlap. edge ifNil: [^ self]. title := UIManager default request: 'Wording for this flap:' translated initialAnswer: 'Flap' translated. title isEmptyOrNil ifTrue: [^ self]. ^ self addLocalFlap: anEvent titled: title onEdge: edge! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent titled: title onEdge: edge | flapTab menu world | flapTab := self newFlapTitled: title onEdge: edge. (world := anEvent hand world) addMorphFront: flapTab. flapTab adaptToWorld: world. menu := flapTab buildHandleMenu: anEvent hand. flapTab addTitleForHaloMenu: menu. flapTab computeEdgeFraction. menu popUpEvent: anEvent in: world.! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! enableOnlyGlobalFlapsWithIDs: survivorList "In the current project, suppress all global flaps other than those with ids in the survivorList" self globalFlapTabsIfAny do: [:flapTab | (survivorList includes: flapTab flapID) ifTrue: [self enableGlobalFlapWithID: flapTab flapID] ifFalse: [self disableGlobalFlapWithID: flapTab flapID]]. Project current world addGlobalFlaps "Flaps enableOnlyGlobalFlapsWithIDs: #('Supplies')"! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! positionVisibleFlapsRightToLeftOnEdge: edgeSymbol butPlaceAtLeftFlapsWithIDs: idList "Lay out flaps along the designated edge right-to-left, while laying left-to-right any flaps found in the exception list Flaps positionVisibleFlapsRightToLeftOnEdge: #bottom butPlaceAtLeftFlapWithIDs: {'Navigator' translated. 'Supplies' translated} Flaps sharedFlapsAlongBottom" | leftX flapList flapsOnRight flapsOnLeft | flapList := self globalFlapTabsIfAny select: [:aFlapTab | aFlapTab isInWorld and: [aFlapTab edgeToAdhereTo == edgeSymbol]]. flapsOnLeft := OrderedCollection new. flapsOnRight := OrderedCollection new. flapList do: [:fl | (idList includes: fl flapID) ifTrue: [ flapsOnLeft addLast: fl ] ifFalse: [ flapsOnRight addLast: fl ] ]. leftX := Project current world width - 15. flapsOnRight sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab right: leftX - 3. leftX := aFlapTab left]. leftX := Project current world left. flapsOnLeft sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab left: leftX + 3. leftX := aFlapTab right]. flapList do: [:ft | ft computeEdgeFraction. ft flapID = 'Navigator' translated ifTrue: [ft referent left: (ft center x - (ft referent width//2) max: 0)]]! ! !Flaps class methodsFor: '*Etoys-Squeakland-predefined flaps' stamp: 'ct 9/12/2020 14:29'! newSuppliesFlapFromQuads: quads positioning: positionSymbol withPreviousEntries: aCollection "Answer a fully-instantiated flap named 'Supplies' to be placed at the bottom of the screen. Use #center as the positionSymbol to have it centered at the bottom of the screen, or #right to have it placed off near the right edge." | aFlapTab aStrip aWidth sugarNavigator | sugarNavigator := SugarNavigatorBar showSugarNavigator. aStrip := PartsBin newPartsBinWithOrientation: #leftToRight andColor: Color gray muchLighter from: quads withPreviousEntries: aCollection. self twiddleSuppliesButtonsIn: aStrip. aFlapTab := (sugarNavigator ifTrue: [SolidSugarSuppliesTab] ifFalse: [FlapTab]) new referent: aStrip beSticky. aFlapTab setName: 'Supplies' translated edge: (sugarNavigator ifTrue: [#top] ifFalse: [#bottom]) color: Color red lighter. aFlapTab position: (0 @ Project current world sugarAllowance). aFlapTab setBalloonText: aFlapTab balloonTextForFlapsMenu. aFlapTab applyThickness: 20. aWidth := self currentWorld width. aStrip extent: aWidth @ (76 * (1 + (1350 // aWidth))). aStrip beFlap: true. aStrip autoLineLayout: true. aStrip vResizeToFit: true. sugarNavigator ifTrue: [ aFlapTab useSolidTab. aFlapTab height: 20; color: (Color r: 0.804 g: 0.804 b: 0.804)] ifFalse: [ aFlapTab color: Color red lighter]. ^ aFlapTab "Flaps replaceGlobalFlapwithID: 'Supplies' translated"! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleColorSees "Form exampleColorSees" "First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area - where red touches blue - superimposed on the original scene. Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" | formA formB maskA offset tally map intersection left top dCanvas sensitiveColor soughtColor index | formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. sensitiveColor := Color red. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: sensitiveColor. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map at: (index := sensitiveColor indexInMap: map) put: 1. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map at: index put: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((sensitiveColor pixelValueForDepth: formA depth) ) to: ((soughtColor pixelValueForDepth: formB depth) ) test: (Form compareMatchColor bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchTest "Form exampleTouchTest" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a non-transparent pixel of the background upon which it is displayed. First column shows a form with a red block in the midst of transparent area sneaking up on a form with a transparent LHS and blue RHS. The green frame shows the intersection area. Second column shows in grey the part of the red that is within the intersection. Third column shows in black the blue that is within the intersection. Fourth column shows just the A touching B area. Fifth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA maskB offset tally map intersection left top dCanvas| formA := formB := maskA := maskB := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := Form extent: 100 at 50 depth: 32. formB := Form extent: 100 at 50 depth: 16. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color yellow. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color red. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: Color blue. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 2. formA displayOn: maskA at: offset - intersection origin rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskB := Form extent: intersection extent depth: 2. formB displayOn: maskB at: intersection origin negated rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. map := Bitmap new: 4 withAll: 1. map at: 1 put: 0. "transparent" maskA copyBits: maskA boundingBox from: maskA at: 0 at 0 colorMap: map. "maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB copyBits: maskB boundingBox from: maskB at: 0 at 0 colorMap: map. "maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB displayOn: maskA at: 0 at 0 rule: Form and. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA boundingBox area -( maskA tallyPixelValues at: 1)) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((Color transparent pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((Color transparent pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorANotColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchingColor "Form exampleTouchingColor" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a particular color pixel of the background upon which it is displayed. First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area (black) superimposed on the original scene Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA offset tally map intersection left top dCanvas ignoreColor soughtColor| formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. ignoreColor := Color transparent. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color red. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map atAllPut: 1. map at: ( ignoreColor indexInMap: map) put: 0. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map atAllPut: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((ignoreColor pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((soughtColor pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorAMatchColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !HandBugs methodsFor: 'tests' stamp: 'ct 9/12/2020 14:41'! testTargetPoint "self new testTargetPoint" "self run: #testTargetPoint" "This should not throw an exception." self currentHand targetPoint ! ! !Lexicon methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:16'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'Lexicon' translated. aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var assignments (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !InstanceBrowser methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:12'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: ('Messages of {1}' translated format: {objectViewed nameForViewer}). aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var defs (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('viewer on me' viewViewee) ('inspector on me' inspectViewee) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !ListChooser methodsFor: 'actions' stamp: 'ct 9/12/2020 14:42'! accept "if the user submits with no valid entry, make them start over" | choice | self canAccept ifFalse: [ self canAdd ifTrue: [^ self add]. ^ self changed: #textSelection]. choice := self selectedItem. self canAdd ifTrue: [ "Ask the user whether to add the new item or choose the list selection." (UserDialogBoxMorph confirm: 'You can either choose an existing item or add a new one.\What do you want?' translated withCRs title: 'Choose or Add' translated trueChoice: choice asString falseChoice: self searchText asString at: self currentHand position) ifNil: ["Cancelled" self result: nil. ^ self] ifNotNil: [:answer | answer ifTrue: [self result: choice] ifFalse: [self result: self searchText asString]] ] ifFalse: [self result: choice]. self changed: #close.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! setUp previousID := Locale current localeID. previousKeyboardInterpreter := self currentHand instVarNamed: 'keyboardInterpreter'. previousClipboardInterpreter := Clipboard default instVarNamed: 'interpreter'. self currentHand clearKeyboardInterpreter. Clipboard default clearInterpreter.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! tearDown self currentHand instVarNamed: 'keyboardInterpreter' put: previousKeyboardInterpreter. Clipboard default instVarNamed: 'interpreter' put: previousClipboardInterpreter. Locale switchToID: (LocaleID isoLanguage: previousID).! ! !LocaleTest methodsFor: 'tests' stamp: 'ct 9/12/2020 14:43'! testLocaleChanged "self debug: #testLocaleChanged" "LanguageEnvironment >> startUp is called from Prject >> localeChanged" "takes quite a while" Project current updateLocaleDependents. self assert: (self currentHand instVarNamed: 'keyboardInterpreter') isNil description: 'non-nil keyboardInterpreter'. self assert: (Clipboard default instVarNamed: 'interpreter') isNil description: 'non-nil interpreter'. Locale switchToID: (LocaleID isoLanguage: 'ja'). self assert: 'ja' equals: Locale current localeID isoLanguage. Locale switchToID: (LocaleID isoLanguage: 'en'). self assert: 'en' equals: Locale current localeID isoLanguage.! ! !MCCodeTool methodsFor: 'menus' stamp: 'ct 9/11/2020 20:17'! browseFullProtocol "Open up a protocol-category browser on the value of the receiver's current selection. If in mvc, an old-style protocol browser is opened instead. Someone who still uses mvc might wish to make the protocol-category-browser work there too, thanks." (Smalltalk isMorphic and: [Smalltalk hasClassNamed: #Lexicon]) ifFalse: [^ self spawnFullProtocol]. self selectedClassOrMetaClass ifNotNil: [:class | ^ (Smalltalk at: #Lexicon) new openOnClass: class inWorld: self currentWorld showingSelector: self selectedMessageName]. ^ nil! ! !Morph methodsFor: 'copying' stamp: 'ct 9/12/2020 14:20'! duplicate "Make and return a duplicate of the receiver" | newMorph aName w aPlayer topRend | ((topRend := self topRendererOrSelf) ~~ self) ifTrue: [^ topRend duplicate]. self okayToDuplicate ifFalse: [^ self]. aName := (w := self world) ifNotNil: [w nameForCopyIfAlreadyNamed: self]. newMorph := self veryDeepCopy. aName ifNotNil: [newMorph setNameTo: aName]. newMorph arrangeToStartStepping. newMorph privateOwner: nil. "no longer in world" newMorph isPartsDonor: false. "no longer parts donor" (aPlayer := newMorph player) belongsToUniClass ifTrue: [aPlayer class bringScriptsUpToDate]. aPlayer ifNotNil: [self currentWorld presenter flushPlayerListCache]. ^ newMorph! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:20'! justDroppedInto: aMorph event: anEvent "This message is sent to a dropped morph after it has been dropped on -- and been accepted by -- a drop-sensitive morph" | partsBinCase cmd | (self formerOwner notNil and: [self formerOwner ~~ aMorph]) ifTrue: [self removeHalo]. self formerOwner: nil. self formerPosition: nil. cmd := self valueOfProperty: #undoGrabCommand. cmd ifNotNil:[aMorph rememberCommand: cmd. self removeProperty: #undoGrabCommand]. (partsBinCase := aMorph isPartsBin) ifFalse: [self isPartsDonor: false]. (self isInWorld and: [partsBinCase not]) ifTrue: [self world startSteppingSubmorphsOf: self]. "Note an unhappy inefficiency here: the startStepping... call will often have already been called in the sequence leading up to entry to this method, but unfortunately the isPartsDonor: call often will not have already happened, with the result that the startStepping... call will not have resulted in the startage of the steppage." "An object launched by certain parts-launcher mechanisms should end up fully visible..." (self hasProperty: #beFullyVisibleAfterDrop) ifTrue: [aMorph == self currentWorld ifTrue: [self goHome]. self removeProperty: #beFullyVisibleAfterDrop].! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:19'! slideToTrash: evt "Perhaps slide the receiver across the screen to a trash can and make it disappear into it. In any case, remove the receiver from the screen." | aForm trash startPoint endPoint morphToSlide | ((self renderedMorph == ScrapBook default scrapBook) or: [self renderedMorph isKindOf: TrashCanMorph]) ifTrue: [self dismissMorph. ^ self]. TrashCanMorph slideDismissalsToTrash ifTrue: [morphToSlide := self representativeNoTallerThan: 200 norWiderThan: 200 thumbnailHeight: 100. aForm := morphToSlide imageForm offset: (0 at 0). trash := self currentWorld findDeepSubmorphThat: [:aMorph | (aMorph isKindOf: TrashCanMorph) and: [aMorph topRendererOrSelf owner == self currentWorld]] ifAbsent: [trash := TrashCanMorph new. trash position: self currentWorld bottomLeft - (0 @ (trash extent y + 26)). trash openInWorld. trash]. endPoint := trash fullBoundsInWorld center. startPoint := self topRendererOrSelf fullBoundsInWorld center - (aForm extent // 2)]. self dismissMorph. self currentWorld displayWorld. TrashCanMorph slideDismissalsToTrash ifTrue: [aForm slideFrom: startPoint to: endPoint nSteps: 12 delay: 15]. ScrapBook default addToTrash: self! ! !Morph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:45'! yellowButtonActivity: shiftState "Find me or my outermost owner that has items to add to a yellow button menu. shiftState is true if the shift was pressed. Otherwise, build a menu that contains the contributions from myself and my interested submorphs, and present it to the user." | menu | self isWorldMorph ifFalse: [| outerOwner | outerOwner := self outermostOwnerWithYellowButtonMenu. outerOwner ifNil: [^ self]. outerOwner == self ifFalse: [^ outerOwner yellowButtonActivity: shiftState]]. menu := self buildYellowButtonMenu: self currentHand. menu addTitle: self externalName icon: (self iconOrThumbnailOfSize: (Preferences tinyDisplay ifTrue: [16] ifFalse: [28])). menu popUpInWorld: self currentWorld! ! !Morph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:00'! buildYellowButtonMenu: aHand "Build the morph menu for the yellow button." | menu | menu := MenuMorph new defaultTarget: self. self addNestedYellowButtonItemsTo: menu event: self currentEvent. MenuIcons decorateMenu: menu. ^ menu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:44'! addMiscExtrasTo: aMenu "Add a submenu of miscellaneous extra items to the menu." | realOwner realMorph subMenu | subMenu := MenuMorph new defaultTarget: self. (self isWorldMorph not and: [(self renderedMorph isSystemWindow) not]) ifTrue: [subMenu add: 'put in a window' translated action: #embedInWindow]. self isWorldMorph ifFalse: [subMenu add: 'adhere to edge...' translated action: #adhereToEdge. subMenu addLine]. realOwner := (realMorph := self topRendererOrSelf) owner. (realOwner isKindOf: TextPlusPasteUpMorph) ifTrue: [subMenu add: 'GeeMail stuff...' translated subMenu: (realOwner textPlusMenuFor: realMorph)]. subMenu add: 'add mouse up action' translated action: #addMouseUpAction; add: 'remove mouse up action' translated action: #removeMouseUpAction; add: 'hand me tiles to fire this button' translated action: #handMeTilesToFire. subMenu addLine. subMenu add: 'arrowheads on pen trails...' translated action: #setArrowheads. subMenu addLine. subMenu defaultTarget: self topRendererOrSelf. subMenu add: 'draw new path' translated action: #definePath. subMenu add: 'follow existing path' translated action: #followPath. subMenu add: 'delete existing path' translated action: #deletePath. subMenu addLine. self addGestureMenuItems: subMenu hand: self currentHand. aMenu add: 'extras...' translated subMenu: subMenu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:21'! chooseNewGraphicCoexisting: aBoolean "Allow the user to choose a different form for her form-based morph" | replacee aGraphicalMenu | self isInWorld ifFalse: "menu must have persisted for a not-in-world object." [aGraphicalMenu := Project current world submorphThat: [:m | (m isKindOf: GraphicalMenu) and: [m target == self]] ifNone: [^ self]. ^ aGraphicalMenu show; flashBounds]. aGraphicalMenu := GraphicalMenu new initializeFor: self withForms: self reasonableForms coexist: aBoolean. aBoolean ifTrue: [self primaryHand attachMorph: aGraphicalMenu] ifFalse: [replacee := self topRendererOrSelf. replacee owner replaceSubmorph: replacee by: aGraphicalMenu]! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/12/2020 14:20'! indicateAllSiblings "Indicate all the receiver and all its siblings by flashing momentarily." | aPlayer allBoxes | (aPlayer := self topRendererOrSelf player) belongsToUniClass ifFalse: [^ self "error: 'not uniclass'"]. allBoxes := aPlayer class allInstances select: [:m | m costume world == self currentWorld] thenCollect: [:m | m costume boundsInWorld]. 5 timesRepeat: [Display flashAll: allBoxes andWait: 120].! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/11/2020 18:00'! resizeFromMenu "Commence an interaction that will resize the receiver" ^ self resizeMorph: self currentEvent! ! !Morph methodsFor: 'structure' stamp: 'ct 9/24/2020 13:37'! activeHand "Alias for #currentHand. Maybe we want to deprecate this at some point" ... ^ self currentHand! ! !Morph methodsFor: 'structure' stamp: 'ct 9/16/2020 19:06'! primaryHand ^ self activeHand! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:45'! deleteUnlessHasFocus "Runs on a step timer because we cannot be guaranteed to get focus change events." (self currentHand keyboardFocus ~= self and: [ self isInWorld ]) ifTrue: [ self stopSteppingSelector: #deleteUnlessHasFocus ; delete ]! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:20'! dismissViaHalo "The user has clicked in the delete halo-handle. This provides a hook in case some concomitant action should be taken, or if the particular morph is not one which should be put in the trash can, for example." | cmd | self setProperty: #lastPosition toValue: self positionInWorld. self dismissMorph. TrashCanMorph preserveTrash ifTrue: [ TrashCanMorph slideDismissalsToTrash ifTrue:[self slideToTrash: nil] ifFalse:[TrashCanMorph moveToTrash: self]. ]. cmd := Command new cmdWording: 'dismiss ' translated, self externalName. cmd undoTarget: Project current world selector: #reintroduceIntoWorld: argument: self. cmd redoTarget: Project current world selector: #onceAgainDismiss: argument: self. Project current world rememberCommand: cmd.! ! !Morph methodsFor: 'e-toy support' stamp: 'ct 9/12/2020 14:19'! referencePlayfield "Answer the PasteUpMorph to be used for cartesian-coordinate reference" | former | owner ifNotNil: [(self topRendererOrSelf owner isHandMorph and: [(former := self formerOwner) notNil]) ifTrue: [former := former renderedMorph. ^ former isPlayfieldLike ifTrue: [former] ifFalse: [former referencePlayfield]]]. self allOwnersDo: [:o | o isPlayfieldLike ifTrue: [^ o]]. ^ Project current world! ! !Morph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:45'! handMeTilesToFire "Construct a phrase of tiles comprising a line of code that will 'fire' this object, and hand it to the user" self currentHand attachMorph: (self assuredPlayer tilesToCall: MethodInterface firingInterface)! ! !Morph methodsFor: '*Etoys-Squeakland-geometry' stamp: 'ct 9/12/2020 14:19'! stagingArea "Answer a containing Worldlet, or the World if none." ^ (self ownerThatIsA: Worldlet) ifNil: [self currentWorld]! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:22'! changeColorTarget: anObject selector: aSymbol originalColor: aColor hand: aHand showPalette: showPalette "Put up a color picker for changing some kind of color. May be modal or modeless, depending on #modalColorPickers setting" | c aRectangle | self flag: #arNote. "Simplify this due to anObject == self for almost all cases" c := ColorPickerMorph new. c choseModalityFromPreference; sourceHand: aHand; target: anObject; selector: aSymbol; originalColor: aColor. showPalette ifFalse: [c initializeForJustCursor]. aRectangle := (anObject == self currentWorld) ifTrue: [self currentHand position extent: (20 at 20)] ifFalse: [anObject isMorph ifFalse: [Rectangle center: self position extent: (20 at 20)] ifTrue: [anObject fullBoundsInWorld]]. c putUpFor: anObject near: aRectangle.! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:45'! showEmbedMenu "Put up a menu offering embed targets. Emphasize the current position. Theoretically this method will only be called when there are at least two alternatives." | aMenu | aMenu := self addEmbeddingMenuItemsTo: nil hand: self currentHand. aMenu title: ('embed {1} in...' translated format: {self externalName }). aMenu popUpInWorld! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:20'! hideWillingnessToAcceptDropFeedback "Make the receiver stop looking ready to show some welcoming feedback" self currentWorld removeHighlightFeedback ! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:19'! showWillingnessToAcceptDropFeedback "Make the receiver look ready to show show some welcoming feedback" | aMorph | aMorph := RectangleMorph new bounds: self bounds.. aMorph beTransparent; borderWidth: 4; borderColor: (Color green); lock. aMorph setProperty: #affilliatedPad toValue: (self ownerThatIsA: TilePadMorph). self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !Morph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 15:17'! openInWorldOrWorldlet "Open in the world-like creature affiliated with the active Hand." | aRecorder aWorldlet | (self currentHand isKindOf: HandMorphForReplay) ifTrue: [((aRecorder := self currentHand recorder) isKindOf: MentoringEventRecorder) ifTrue: [aWorldlet := aRecorder contentArea. self center: aWorldlet center. aWorldlet addMorphFront: self. ^ self]]. self openInWorld.! ! !AllPlayersTool methodsFor: 'reinvigoration' stamp: 'ct 9/12/2020 14:25'! reinvigorate "Referesh the contents of the receiver" (submorphs copyFrom: 3 to: submorphs size) do: [:m | m delete]. self currentWorld doOneCycleNow. self playSoundNamed: 'scritch'. (Delay forMilliseconds: 700) wait. self currentWorld presenter reinvigoratePlayersTool: self. self playSoundNamed: 'scratch'.! ! !AllScriptsTool methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:26'! addSecondLineOfControls "Add the second line of controls" | aRow outerButton aButton worldToUse | aRow := AlignmentMorph newRow listCentering: #center; color: Color transparent. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingOnlyActiveScripts; getSelector: #showingOnlyActiveScripts. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'tickers only' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then only scripts that are paused or ticking will be shown' translated. aRow addMorphBack: outerButton. aRow addTransparentSpacerOfSize: 20 at 0. aRow addMorphBack: self helpButton. aRow addTransparentSpacerOfSize: 20 at 0. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingAllInstances; getSelector: #showingAllInstances. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'all instances' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then entries for all instances will be shown, but if not checked, scripts for only one representative of each different kind of object will be shown. Consult the help available by clicking on the purple ? for more information.' translated. aRow addMorphBack: outerButton. self addMorphBack: aRow. worldToUse := self isInWorld ifTrue: [self world] ifFalse: [self currentWorld]. worldToUse presenter reinvigorateAllScriptsTool: self. self layoutChanged.! ! !BookMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:39'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer pageNum: pageNum "Call once to search a page of the book. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container wasIn strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [(pages at: pageNum) isInMemory ifFalse: [rawStrings] ifTrue: [(pages at: pageNum) allStringsAfter: oldContainer]]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findString: searchString startingAt: start caseSensitive: false) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" wasIn := (pages at: pageNum) isInMemory. self goToPage: pageNum. wasIn ifFalse: ["search again, on the real current text. Know page is in." ^self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) startAt: startIndex container: oldContainer pageNum: pageNum "recompute"]]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !BookMorph methodsFor: 'navigation' stamp: 'ct 9/12/2020 14:26'! goToPageMorph: newPage transitionSpec: transitionSpec "Go to a page, which is assumed to be an element of my pages array (if it is not, this method returns quickly. Apply the transitionSpec provided." | pageIndex aWorld oldPageIndex ascending tSpec readIn | pages isEmpty ifTrue: [^self]. self setProperty: #searchContainer toValue: nil. "forget previous search" self setProperty: #searchOffset toValue: nil. self setProperty: #searchKey toValue: nil. pageIndex := pages identityIndexOf: newPage ifAbsent: [^self "abort"]. readIn := newPage isInMemory not. oldPageIndex := pages identityIndexOf: currentPage ifAbsent: [nil]. ascending := (oldPageIndex isNil or: [newPage == currentPage]) ifTrue: [nil] ifFalse: [oldPageIndex < pageIndex]. tSpec := transitionSpec ifNil: ["If transition not specified by requestor..." newPage valueOfProperty: #transitionSpec ifAbsent: [" ... then consult new page" self transitionSpecFor: self " ... otherwise this is the default"]]. self flag: #arNote. "Probably unnecessary" (aWorld := self world) ifNotNil: [self primaryHand releaseKeyboardFocus]. currentPage ifNotNil: [currentPage updateCachedThumbnail]. self currentPage notNil ifTrue: [(((pages at: pageIndex) owner isKindOf: TransitionMorph) and: [(pages at: pageIndex) isInWorld]) ifTrue: [^self "In the process of a prior pageTurn"]. self currentPlayerDo: [:aPlayer | aPlayer runAllClosingScripts]. self removeViewersOnSubsIn: self currentWorld presenter. ascending ifNotNil: ["Show appropriate page transition and start new page when done" currentPage stopStepping. (pages at: pageIndex) position: currentPage position. ^(TransitionMorph effect: tSpec second direction: tSpec third inverse: (ascending or: [transitionSpec notNil]) not) showTransitionFrom: currentPage to: (pages at: pageIndex) in: self whenStart: [self playPageFlipSound: tSpec first] whenDone: [currentPage delete; fullReleaseCachedState. self insertPageMorphInCorrectSpot: (pages at: pageIndex). self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrlInBook: self url. currentPage sqkPage computeThumbnail "just store it"]]]. "No transition, but at least decommission current page" currentPage delete; fullReleaseCachedState]. self insertPageMorphInCorrectSpot: (pages at: pageIndex). "sets currentPage" self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrl. currentPage sqkPage computeThumbnail "just store it"]. self currentWorld presenter flushPlayerListCache.! ! !BookMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:38'! addAdvancedItemsTo: aMenu "Add advanced items to a menu which allow the user to affect all the pages of the book. NB balloon help msgs still pending." | subMenu | subMenu := MenuMorph new defaultTarget: self. subMenu addTranslatedList: #( ('make all pages the same size as this page' makeUniformPageSize 'Make all the pages of this book be the same size as the page currently showing.') ('set background color for all pages' #setPageColor 'Choose a color to assign as the background color for all of this book''s pages') - ('uncache page sorter' uncachePageSorter) ('make a thread of projects in this book' buildThreadOfProjects) - ('make this the template for new pages' setNewPagePrototype)) translatedNoop. "NB The following 2 items do not get auto-updated in a persistent menu." newPagePrototype ifNotNil: [ subMenu add: 'clear new-page template' translated action: #clearNewPagePrototype]. self isInFullScreenMode ifTrue: [ subMenu add: 'exit full screen' translated action: #exitFullScreen] ifFalse: [ subMenu add: 'show full screen' translated action: #goFullScreen]. (self currentHand pasteBuffer isKindOf: PasteUpMorph) ifTrue: [ subMenu addLine. subMenu add: 'paste book page' translated action: #pasteBookPage]. aMenu add: 'advanced...' translated subMenu: subMenu.! ! !CategoryViewer methodsFor: 'get/set slots' stamp: 'ct 9/12/2020 14:39'! makeUniversalTilesGetter: aMethodInterface event: evt from: aMorph "Button in viewer performs this to make a universal-tiles getter and attach it to hand." | newTiles | newTiles := self newGetterTilesFor: scriptedPlayer methodInterface: aMethodInterface. newTiles setProperty: #beScript toValue: true. owner ifNil: [^ newTiles]. self currentHand attachMorph: newTiles. newTiles align: newTiles topLeft with: evt hand position + (7 at 14).! ! !CategoryViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 19:42'! currentVocabulary "Answer the vocabulary currently installed in the viewer. The outer StandardViewer object holds this information." ^ self outerViewer ifNotNil: [:viewer | viewer currentVocabulary] ifNil: [(self world ifNil: [self currentWorld]) currentVocabularyFor: scriptedPlayer]! ! !CategoryViewer methodsFor: '*Etoys-Squeakland-categories' stamp: 'ct 9/11/2020 19:42'! assureCategoryFullyVisible "Keep deleting categoryviewers other than the receiver until the receiver is fully visible." | ready toDelete | ready := false. [(self bounds bottom > self world bottom) and: [ready not]] whileTrue: [ owner submorphs size > 2 ifTrue: [ toDelete := owner submorphs allButFirst reversed detect: [:cv | cv ~~ self] ifNone: [^ self]. toDelete delete. self world doOneCycleNow] ifFalse: [ ready := true]].! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: self bounds; beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock; yourself. self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! removeHighlightFeedback "Remove any existing highlight feedback" self world removeHighlightFeedback. ! ! !DialogWindow methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:44'! initialize super initialize. self changeTableLayout; listDirection: #topToBottom; hResizing: #shrinkWrap; vResizing: #shrinkWrap; rubberBandCells: true; setProperty: #indicateKeyboardFocus toValue: #never. self createTitle: 'Dialog'. self createBody. self setDefaultParameters. keyMap := Dictionary new. exclusive := true. autoCancel := false. preferredPosition := self currentWorld center.! ! !DockingBarMorph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:40'! delete self currentHand removeKeyboardListener: self. activeSubMenu ifNotNil: [ activeSubMenu delete]. ^ super delete! ! !EtoyDAVLoginMorph methodsFor: 'private' stamp: 'ct 9/11/2020 19:45'! loginAndDo: aBlock ifCanceled: cb "EtoyDAVLoginMorph loginAndDo:[:n :p | true] ifCanceled:[]" self name: '' actionBlock: aBlock cancelBlock: cb; fullBounds; position: Display extent - self extent // 2. self position: self position + (0 at 40). self currentWorld addMorphInLayer: self.! ! !EtoyDAVLoginMorph methodsFor: 'actions' stamp: 'ct 9/11/2020 19:45'! launchBrowser self currentWorld addMorph: self buildPanel centeredNear: Sensor cursorPoint. (Smalltalk classNamed: #ScratchPlugin) ifNotNil: [:sp | sp primOpenURL: self url].! ! !EventMorph methodsFor: 'drag and drop' stamp: 'ct 9/11/2020 19:45'! brownDragConcluded "After the user has manually repositioned the receiver via brown-halo-drag, this is invoked." self currentWorld abandonAllHalos. self eventRoll ifNotNil: [:evtRoll | evtRoll pushChangesBackToEventTheatre]! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! abandonReplayHandsAndHalos "Cleanup after playback." self currentWorld abandonReplayHandsAndHalosFor: eventRecorder! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! dismantlePaintBoxArtifacts "Cleanup after playback -- if a paint-box has been left up, take it down." (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/12/2020 14:28'! makeHorizontalRoll "Create a horizontal roll viewer for this recording space" state = #readyToRecord ifTrue: [ ^ self inform: 'Nothing recorded yet' translated]. "self convertToCanonicalForm." "Would prefer to do this but there are still issues." eventRoll ifNil: [ eventRoll := EventRollMorph new. eventRoll eventTheatre: self]. eventRoll formulate. eventRoll isInWorld ifFalse: [eventRoll openInWorld; setExtentFromHalo: (self currentWorld width - 10) @ eventRoll height; top: self bottom; bottom: (eventRoll bottom min: self currentWorld bottom); left: self currentWorld left + 2] "presumably zero" ifTrue: [eventRoll comeToFront].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! pausePlayback "Pause the playback. Sender responsible for setting state to #suspendedPlayback" eventRecorder pausePlayback. (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting. ^ self rewind]. self borderColor: Color orange. self setProperty: #suspendedContentArea toValue: contentArea veryDeepCopy. self populateControlsPanel! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! record "Commence event recording..." self currentWorld abandonAllHalos. self comeToFront. initialContentArea := contentArea veryDeepCopy. self forgetPriorPaintBoxSettings. initialPicture := contentArea imageForm. self state: #recording. self borderColor: Color red. self populateControlsPanel. self currentWorld doOneCycleNow. eventRecorder record! ! !EventRecordingSpace methodsFor: 'processing' stamp: 'ct 9/11/2020 19:45'! assureContentAreaStaysAt: aPoint "selbst-verst?????ndlich" self currentWorld doOneCycleNow. self topLeft: ((self topLeft - contentArea topLeft ) + aPoint)! ! !EventRecordingSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:46'! initializeFromPlaybackButton: anEventPlaybackButton "Initialize my content area, caption, and tape from a playback button." | soundEvent | initialContentArea := anEventPlaybackButton contentArea veryDeepCopy. eventRecorder tape: anEventPlaybackButton tape veryDeepCopy. eventRecorder caption: anEventPlaybackButton caption. soundEvent := eventRecorder tape detect: [:evt | evt type = #startSound] ifNone: [nil]. soundEvent ifNotNil: "For benefit of possible re-record of voiceover" [eventRecorder startSoundEvent: soundEvent]. initialPicture := anEventPlaybackButton initialPicture veryDeepCopy ifNil: [self inform: 'caution - old playback; button lacks vital data.' translated. ^ nil]. finalPicture := anEventPlaybackButton finalPicture veryDeepCopy. eventRecorder saved: true. self rewind. self center: self currentWorld center.! ! !EventPlaybackSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:45'! launchFrom: aButton "Initialize the receiver from an invoker button, and launch it." | where | self setProperty: #originatingButton toValue: aButton. self contentArea: aButton contentArea veryDeepCopy tape: aButton tape veryDeepCopy. self captionString: aButton caption. self rewind. autoStart := aButton autoStart. autoDismiss := aButton autoDismiss. "showChrome := aButton showChrome." where := aButton whereToAppear. self openInWorld. where = #screenCenter ifTrue: [self center: self currentWorld center]. where = #buttonPosition ifTrue: [self position: aButton position]. where = #containerOrigin ifTrue: [self position: aButton owner position]. self goHome. self addStopper. autoStart ifTrue: [self play]! ! !FlapTab methodsFor: 'globalness' stamp: 'ct 9/11/2020 19:47'! toggleIsGlobalFlap "Toggle whether the receiver is currently a global flap or not" | oldWorld | self hideFlap. oldWorld := self currentWorld. self isGlobalFlap ifTrue: [Flaps removeFromGlobalFlapTabList: self. oldWorld addMorphFront: self] ifFalse: [self delete. Flaps addGlobalFlap: self. self currentWorld addGlobalFlaps]. self currentWorld reformulateUpdatingMenus.! ! !GoldBoxMenu methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:41'! initializeFor: aScriptor "Answer a graphical menu to be put up in conjunction with the Gold Box" | aButton goldBox aReceiver boxBounds example toScale | scriptor := aScriptor. lastItemMousedOver := nil. self removeAllMorphs. self setProperty: #goldBox toValue: true. self listDirection: #topToBottom; hResizing: #spaceFill; extent: 1 at 1; vResizing: #spaceFill. "standard #newColumn stuff" self setNameTo: 'Gold Box' translated. self useRoundedCorners. self color: Color white. self borderColor: (Color r: 1.0 g: 0.839 b: 0.065). self hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4. { {ScriptingSystem. #yesNoComplexOfTiles. 'test' translated. 'Test/Yes/No panes for testing a condition.' translated}. {ScriptingSystem. #timesRepeatComplexOfTiles. 'repeat' translated. 'TimesRepeat panes for running a section of code repeatedly.' translated}. { ScriptingSystem. #randomNumberTile. 'random' translated. 'A tile that will produce a random number in a given range.' translated}. { ScriptingSystem. #seminalFunctionTile. 'function' translated. 'A tile representing a function call. Click on the function name or the arrows to change functions.' translated}. {ScriptingSystem. #buttonUpTile. 'button up?' translated. 'Reports whether the mouse button is up' translated}. {ScriptingSystem. #buttonDownTile. 'button down?' translated. 'Reports whether the mouse button is down' translated}. {ScriptingSystem. #randomColorTile. 'random color' translated. 'A tile returning a random color' translated}. {scriptor playerScripted. #tileToRefer. 'tile for me' translated. 'A tile representing the object being scripted' translated}. {self. #numericConstantTile. 'number' translated. 'A tile holding a plain number' translated}. } do: [:tuple | aReceiver := tuple first. example := aReceiver perform: tuple second. aButton := IconicButton new target: aReceiver. aButton borderWidth: 0; color: Color transparent. toScale := tuple size >= 5 ifTrue: [tuple first perform: tuple fifth] "bail-out for intractable images." ifFalse: [example imageForm]. aButton labelGraphic: (toScale copy scaledToHeight: 40). aButton actionSelector: #launchPartOffsetVia:label:. aButton arguments: {tuple second. tuple third}. (tuple size > 3 and: [tuple fourth isEmptyOrNil not]) ifTrue: [aButton setBalloonText: tuple fourth]. aButton actWhen: #buttonDown. aButton on: #mouseEnter send: #mousedOverEvent:button: to: self. aButton on: #click send: #delete to: self. self addMorphBack: aButton]. goldBox := aScriptor submorphs first submorphThat: [:m | (m isKindOf: SimpleButtonMorph) and: [m actionSelector == #offerGoldBoxMenu]] ifNone: [nil]. goldBox ifNil: [self position: self currentHand position] ifNotNil: [boxBounds := goldBox boundsInWorld. self center: boxBounds center. self left: (boxBounds center x - (self width // 2)). self top: boxBounds bottom]. lastItemMousedOver := nil. self on: #mouseLeave send: #mouseLeftMenuWithEvent: to: self. self on: #mouseLeaveDragging send: #delete to: self.! ! !GrabPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:41'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair.! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! doDirection: anEvent with: directionHandle "The mouse went down on the forward-direction halo handle; respond appropriately." anEvent hand obtainHalo: self. anEvent shiftPressed ifTrue: [directionArrowAnchor := (target point: target referencePosition in: self world) rounded. self positionDirectionShaft: directionHandle. self removeAllHandlesBut: directionHandle. directionHandle setProperty: #trackDirectionArrow toValue: true] ifFalse: [self currentHand spawnBalloonFor: directionHandle]! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:10'! maybeDismiss: evt with: dismissHandle "Ask hand to dismiss my target if mouse comes up in it." evt hand obtainHalo: self. (dismissHandle containsPoint: evt cursorPoint) ifFalse: [ self delete. target addHalo: evt] ifTrue: [ target resistsRemoval ifTrue: [(UIManager default chooseFrom: { 'Yes' translated. 'Um, no, let me reconsider' translated. } title: 'Really throw this away?' translated) = 1 ifFalse: [^ self]]. evt hand removeHalo. self delete. target dismissViaHalo. self currentWorld presenter flushPlayerListCache].! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! prepareToTrackCenterOfRotation: evt with: rotationHandle "The mouse went down on the center of rotation." evt hand obtainHalo: self. evt shiftPressed ifTrue: [self removeAllHandlesBut: rotationHandle. rotationHandle setProperty: #trackCenterOfRotation toValue: true. evt hand showTemporaryCursor: Cursor blank] ifFalse: [self currentHand spawnBalloonFor: rotationHandle]! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/16/2020 18:24'! cursorPoint "Implemented for allowing embedded worlds in an event cycle to query a hand's position and get it in its coordinates. The same can be achieved by #point:from: but this is simply much more convenient since it will look as if the hand is in the lower world." ^ self currentWorld point: self position from: owner! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/16/2020 19:47'! processEvents "Process user input events from the local input devices." | evt evtBuf type hadAny | self currentEvent ~= lastMouseEvent ifTrue: [ "Meaning that we were invoked from within an event response. Make sure z-order is up to date." self mouseOverHandler processMouseOver: lastMouseEvent]. hadAny := false. [(evtBuf := Sensor nextEvent) isNil] whileFalse: [evt := nil. "for unknown event types" type := evtBuf first. type = EventTypeMouse ifTrue: [evt := self generateMouseEvent: evtBuf]. type = EventTypeMouseWheel ifTrue: [evt := self generateMouseWheelEvent: evtBuf]. type = EventTypeKeyboard ifTrue: [evt := self generateKeyboardEvent: evtBuf]. type = EventTypeDragDropFiles ifTrue: [evt := self generateDropFilesEvent: evtBuf]. type = EventTypeWindow ifTrue:[evt := self generateWindowEvent: evtBuf]. "All other events are ignored" (type ~= EventTypeDragDropFiles and: [evt isNil]) ifTrue: [^self]. evt ifNotNil: ["Finally, handle it." self handleEvent: evt. hadAny := true. "For better user feedback, return immediately after a mouse event has been processed." evt isMouse ifTrue: [^ self]]]. "note: if we come here we didn't have any mouse events" mouseClickState ifNotNil: [ "No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: [ "No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent].! ! !HandMorph methodsFor: 'initialization' stamp: 'ct 9/16/2020 19:44'! becomeActiveDuring: aBlock "Make the receiver the active hand during the evaluation of aBlock." ^ ActiveHandVariable value: self during: aBlock! ! !HandMorphForReplay methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:30'! processEvents "Play back the next event" | evt hadMouse hadAny tracker | suspended == true ifTrue: [^ self]. hadMouse := hadAny := false. tracker := recorder objectTrackingEvents. [(evt := recorder nextEventToPlay) isNil] whileFalse: [ ((evt isMemberOf: MouseMoveEvent) and: [evt trail isNil]) ifTrue: [^ self]. tracker ifNotNil: [tracker currentEventTimeStamp: evt timeStamp]. evt type == #EOF ifTrue: [recorder pauseIn: self currentWorld. ^ self]. evt type == #startSound ifTrue: [recorder perhapsPlaySound: evt argument. recorder synchronize. ^ self]. evt type == #startEventPlayback ifTrue: [evt argument launchPlayback. recorder synchronize. ^ self]. evt type == #noteTheatreBounds ifTrue: ["The argument holds the content rect --for now we don't make any use of that info in this form." ^ self]. evt isMouse ifTrue: [hadMouse := true]. (evt isMouse or: [evt isKeyboard]) ifTrue: [self handleEvent: (evt setHand: self) resetHandlerFields. hadAny := true]]. (mouseClickState notNil and: [hadMouse not]) ifTrue: ["No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: ["No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent]! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:12'! destroyThread "Manually destroy the thread" (self confirm: ('Destroy thread <{1}> ?' translated format:{threadName})) ifFalse: [^ self]. self class knownThreads removeKey: threadName ifAbsent: []. self setProperty: #moribund toValue: true. "In case pointed to in some other project" self currentWorld keyboardNavigationHandler == self ifTrue: [self stopKeyboardNavigation]. self delete.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:13'! moreCommands "Put up a menu of options" | allThreads aMenu others target | allThreads := self class knownThreads. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'navigation' translated. Preferences noviceMode ifFalse:[ self flag: #deferred. "Probably don't want that stay-up item, not least because the navigation-keystroke stuff is not dynamically handled" aMenu addStayUpItem ]. others := (allThreads keys reject: [ :each | each = threadName]) asArray sort. others do: [ :each | aMenu add: ('switch to <{1}>' translated format:{each}) selector: #switchToThread: argument: each ]. aMenu addList: { {'switch to recent projects' translated. #getRecentThread}. #-. {'create a new thread' translated. #threadOfNoProjects}. {'edit this thread' translated. #editThisThread}. {'create thread of all projects' translated. #threadOfAllProjects}. #-. {'First project in thread' translated. #firstPage}. {'Last project in thread' translated. #lastPage} }. (target := self currentIndex + 2) > listOfPages size ifFalse: [ aMenu add: ('skip over next project ({1})' translated format:{(listOfPages at: target - 1) first}) action: #skipOverNext ]. aMenu addList: { {'jump within this thread' translated. #jumpWithinThread}. {'insert new project' translated. #insertNewProject}. #-. {'simply close this navigator' translated. #delete}. {'destroy this thread' translated. #destroyThread}. #- }. (self currentWorld keyboardNavigationHandler == self) ifFalse:[ aMenu add: 'start keyboard navigation with this thread' translated action: #startKeyboardNavigation ] ifTrue: [ aMenu add: 'stop keyboard navigation with this thread' translated action: #stopKeyboardNavigation ]. aMenu popUpInWorld.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! positionAppropriately | others world otherRects overlaps bottomRight | (self ownerThatIsA: HandMorph) ifNotNil: [^self]. others := (world := Project currentWorld) submorphs select: [ :each | each ~~ self and: [each isKindOf: self class]]. otherRects := others collect: [ :each | each bounds]. bottomRight := (world hasProperty: #threadNavigatorPosition) ifTrue: [world valueOfProperty: #threadNavigatorPosition] ifFalse: [world bottomRight]. self align: self fullBounds bottomRight with: bottomRight. self setProperty: #previousWorldBounds toValue: self world bounds. [ overlaps := false. otherRects do: [ :r | (r intersects: bounds) ifTrue: [overlaps := true. self bottom: r top]. ]. self top < self world top ifTrue: [ self bottom: bottomRight y. self right: self left - 1. ]. overlaps ] whileTrue.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! startKeyboardNavigation "Tell the active world to starting navigating via desktop keyboard navigation via me" self currentWorld keyboardNavigationHandler: self! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:15'! stopKeyboardNavigation "Cease navigating via the receiver in response to desktop keystrokes" self currentWorld removeProperty: #keyboardNavigationHandler! ! !InternalThreadNavigationMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:13'! loadPageWithProgress "Load the desired page, showing a progress indicator as we go" | projectInfo projectName beSpaceHandler | projectInfo := listOfPages at: currentIndex. projectName := projectInfo first. loadedProject := Project named: projectName. self class know: listOfPages as: threadName. beSpaceHandler := (Project current world keyboardNavigationHandler == self). self currentWorld addDeferredUIMessage: [InternalThreadNavigationMorph openThreadNamed: threadName atIndex: currentIndex beKeyboardHandler: beSpaceHandler]. loadedProject ifNil: [ ComplexProgressIndicator new targetMorph: self; historyCategory: 'project loading' translated; withProgressDo: [ [ loadedProject := Project current fromMyServerLoad: projectName ] on: ProjectViewOpenNotification do: [ :ex | ex resume: false] "we probably don't want a project view morph in this case" ]. ]. loadedProject ifNil: [ ^self inform: 'I cannot find that project' translated ]. self delete. loadedProject enter.! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! resetBottomRightPosition self currentWorld removeProperty: #threadNavigatorPosition. ! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! setBottomRightPosition self currentWorld setProperty: #threadNavigatorPosition toValue: self bottomRight. ! ! !LassoPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:42'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! play "Play the movie, as it were." tape ifNil: [^ self]. tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! record "Commence recording or re-recording." tapeStream := WriteStream on: (Array new: 10000). self resumeRecordIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! resumePlayingWithoutPassingStop "Like play, but avoids the stop step that does more than we'd like." tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/12/2020 14:24'! stop "Stop recording or playing." tapeStream ifNotNil: [(#(recording recordingWithSound) includes: self state) ifTrue: [tape := tapeStream contents. saved := false]]. self terminateVoiceRecording. "In case doing" journalFile ifNotNil: [journalFile close]. self pauseIn: self currentWorld. tapeStream := nil. self state: #atEndOfPlayback. recordingSpace abandonReplayHandsAndHalos. recordMeter ifNotNil: [recordMeter width: 1].! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:43'! popUpEvent: evt in: aWorld "Present this menu in response to the given event." | aHand aPosition | aHand := evt ifNotNil: [evt hand] ifNil: [self currentHand]. aPosition := aHand position truncated. ^ self popUpAt: aPosition forHand: aHand in: aWorld! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:23'! popUpNoKeyboard "Present this menu in the current World, *not* allowing keyboard input into the menu" ^ self popUpAt: self currentHand position forHand: self currentHand in: self currentWorld allowKeyboard: false! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title world | title := self allMorphs detect: [ :ea | ea hasProperty: #titleString ]. title := title submorphs first. self visible: false. world := self currentWorld. aBlock value: [:string| self visible ifFalse:[ world addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: self currentHand cursorPoint hangOut: false. self changed. world displayWorld "show myself"]. self delete. world displayWorld.! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! invokeModal: allowKeyboardControl "Invoke this menu and don't return until the user has chosen a value. If the allowKeyboarControl boolean is true, permit keyboard control of the menu" ^ self invokeModalAt: self currentHand position in: self currentWorld allowKeyboard: allowKeyboardControl! ! !MenuMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:43'! positionAt: aPoint relativeTo: aMenuItem inWorld: aWorld "Note: items may not be laid out yet (I found them all to be at 0 at 0), so we have to add up heights of items above the selected item." | i yOffset sub delta | self fullBounds. "force layout" i := 0. yOffset := 0. [(sub := self submorphs at: (i := i + 1)) == aMenuItem] whileFalse: [yOffset := yOffset + sub height]. self position: aPoint - (2 @ (yOffset + 8)). "If it doesn't fit, show it to the left, not to the right of the hand." self right > aWorld worldBounds right ifTrue: [self right: aPoint x + 1]. "Make sure that the menu fits in the world." delta := self bounds amountToTranslateWithin: (aWorld worldBounds withHeight: ((aWorld worldBounds height - 18) max: (self currentHand position y) + 1)). delta isZero ifFalse: [self position: self position + delta].! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:17'! displayAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." Smalltalk isMorphic ifFalse: [^ self]. [self currentWorld addMorph: self centeredNear: aPoint. self world displayWorld. "show myself" aBlock value] ensure: [self delete]! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:18'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title w | Smalltalk isMorphic ifFalse: [^ self]. title := self allMorphs detect: [:ea | ea hasProperty: #titleString]. title := title submorphs first. self visible: false. w := self currentWorld. aBlock value: [:string| self visible ifFalse: [ w addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: Sensor cursorPoint hangOut: false. self changed. w displayWorld "show myself" ]. self delete. w displayWorld.! ! !Morph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:18'! fromFileName: fullName "Reconstitute a Morph from the file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | aFileStream morphOrList | aFileStream := (MultiByteBinaryOrTextStream with: ((FileStream readOnlyFileNamed: fullName) binary contentsOfEntireFile)) binary reset. morphOrList := aFileStream fileInObjectAndCode. (morphOrList isKindOf: SqueakPage) ifTrue: [morphOrList := morphOrList contentsMorph]. Smalltalk isMorphic ifTrue: [Project current world addMorphsAndModel: morphOrList] ifFalse: [morphOrList isMorph ifFalse: [self inform: 'Can only load a single morph into an mvc project via this mechanism.' translated]. morphOrList openInWorld]! ! !AllPlayersTool class methodsFor: '*Etoys-Squeakland-parts bin' stamp: 'ct 9/12/2020 14:26'! allPlayersToolForActiveWorld "Launch an AllPlayersTool to view the scripted objects of the active world" | aTool | aTool := self newStandAlone. aTool center: self currentWorld center. ^ aTool " AllPlayersTool allPlayersToolForActiveWorld "! ! !AllScriptsTool class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:26'! allScriptsToolForActiveWorld "Launch an AllScriptsTool to view scripts of the active world" | aTool | aTool := self newColumn. aTool initializeFor: self currentWorld presenter. ^ aTool! ! !AnonymousSoundMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:26'! fromFileName: fullName "Create an instance of the receiver from the given file path." | newPlayer aSound ext aName | newPlayer := self new initialize. ('*aif*' match: fullName) ifTrue: [aSound := SampledSound fromAIFFfileNamed: fullName]. ('*wav' match: fullName) ifTrue: [aSound := SampledSound fromWaveFileNamed: fullName]. newPlayer := self new. ext := FileDirectory extensionFor: fullName. aName := (FileDirectory on: fullName) pathParts last. ext size > 0 ifTrue: [aName := aName copyFrom: 1 to: (aName size - (ext size + 1))]. newPlayer sound: aSound interimName: aName. newPlayer openInWorld; position: self currentWorld center.! ! !BookMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:27'! openFromFile: fullName "Reconstitute a Morph from the selected file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | book aFileStream | Smalltalk verifyMorphicAvailability ifFalse: [^ self]. aFileStream := FileStream readOnlyFileNamed: fullName. book := BookMorph new. book setProperty: #url toValue: aFileStream url. book fromRemoteStream: aFileStream. aFileStream close. Smalltalk isMorphic ifTrue: [self currentWorld addMorphsAndModel: book] ifFalse: [book isMorph ifFalse: [^self inform: 'Can only load a single morph\into an mvc project via this mechanism.' withCRs translated]. book openInWorld]. book goToPage: 1! ! !EventRecordingSpace class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:46'! openFromPlaybackButton: aButton "Open an EventRecordingSpace derived from a playback button. The primary reason for doing this would be to re-record voiceover." | aSpace | aSpace := EventRecordingSpace new. aSpace initializeFromPlaybackButton: aButton. aSpace center: self currentWorld center. aSpace openInWorld! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer centerAt: aPoint "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels. This variant is only for calling from within a Morphic project." "FillInTheBlankMorph request: 'Type something, then type CR.' initialAnswer: 'yo ho ho!!' centerAt: Display center" ^ self request: queryString initialAnswer: defaultAnswer centerAt: aPoint inWorld: self currentWorld! ! !FillInTheBlankMorph class methodsFor: '*Etoys-Squeakland-instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: self currentHand cursorPoint inWorld: self currentWorld onCancelReturn: cancelResponse! ! !HandMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:11'! showEvents: aBool "HandMorph showEvents: true" "HandMorph showEvents: false" ShowEvents := aBool. aBool ifFalse: [ Project current world invalidRect: (0 at 0 extent: 250 at 120)].! ! !InternalThreadNavigationMorph class methodsFor: 'known threads' stamp: 'ct 9/11/2020 20:15'! openThreadNamed: nameOfThread atIndex: anInteger beKeyboardHandler: aBoolean "Activate the thread of the given name, from the given index; set it up to be navigated via desktop keys if indicated" | coll nav | coll := self knownThreads at: nameOfThread ifAbsent: [^self]. nav := Project current world submorphThat: [ :each | (each isKindOf: self) and: [each threadName = nameOfThread]] ifNone: [nav := self basicNew. nav listOfPages: coll; threadName: nameOfThread index: anInteger; initialize; openInWorld; positionAppropriately. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav]. ^ self]. nav listOfPages: coll; threadName: nameOfThread index: anInteger; removeAllMorphs; addButtons. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav].! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! chooseFrom: aList lines: linesArray title: queryString "Choose an item from the given list. Answer the index of the selected item." | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString. 1 to: aList size do: [:i| menu add: (aList at: i) asString target: aBlock selector: #value: argument: i. (linesArray includes: i) ifTrue:[menu addLine]]. MenuIcons decorateMenu: menu. result := 0. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! confirm: queryString trueChoice: trueChoice falseChoice: falseChoice "Put up a yes/no menu with caption queryString. The actual wording for the two choices will be as provided in the trueChoice and falseChoice parameters. Answer true if the response is the true-choice, false if it's the false-choice. This is a modal question -- the user must respond one way or the other." "MenuMorph confirm: 'Are you hungry?' trueChoice: 'yes, I''m famished' falseChoice: 'no, I just ate'" | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: trueChoice target: aBlock selector: #value: argument: true. menu add: falseChoice target: aBlock selector: #value: argument: false. MenuIcons decorateMenu: menu. [menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. result == nil] whileTrue. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:22'! inform: queryString "MenuMorph inform: 'I like Squeak'" | menu | menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: 'OK' translated target: self selector: #yourself. MenuIcons decorateMenu: menu. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true.! ! !MorphHierarchy class methodsFor: 'opening' stamp: 'ct 9/12/2020 14:45'! openOrDelete | oldMorph | oldMorph := Project current world submorphs detect: [:each | each hasProperty: #morphHierarchy] ifNone: [| newMorph | newMorph := self new asMorph. newMorph bottomLeft: self currentHand position. newMorph openInWorld. newMorph isFullOnScreen ifFalse: [newMorph goHome]. ^ self]. "" oldMorph delete! ! !MorphWorldController methodsFor: 'basic control sequence' stamp: 'ct 9/16/2020 19:42'! controlTerminate "This window is becoming inactive; restore the normal cursor." Cursor normal show. super controlTerminate.! ! !MorphicEvent methodsFor: 'initialize' stamp: 'ct 9/16/2020 19:43'! becomeActiveDuring: aBlock "Make the receiver the active event during the evaluation of aBlock." ^ ActiveEventVariable value: self during: aBlock! ! !MultiWindowLabelButtonMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:16'! performAction "Override to interpret the actionSelector as a menu accessor and to activate that menu." actionSelector ifNil: [^ self]- (model perform: actionSelector) ifNotNil: [:menu | menu invokeModalAt: self position - (0 at 5) in: self currentWorld allowKeyboard: Preferences menuKeyboardControl].! ! !NativeImageSegment methodsFor: 'read/write segment' stamp: 'ct 9/12/2020 14:15'! smartFillRoots: dummy | refs known ours ww blockers | "Put all traced objects into my arrayOfRoots. Remove some that want to be in outPointers. Return blockers, an IdentityDictionary of objects to replace in outPointers." blockers := dummy blockers. known := (refs := dummy references) size. refs keys do: [:obj | "copy keys to be OK with removing items" (obj isSymbol) ifTrue: [refs removeKey: obj. known := known-1]. (obj class == PasteUpMorph) ifTrue: [ obj isWorldMorph & (obj owner == nil) ifTrue: [ (dummy project ~~ nil and: [obj == dummy project world]) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: 'The worldMorph of a different world')]]]. "Make a ProjectViewMorph here" "obj class == Project ifTrue: [Transcript show: obj; cr]." (blockers includesKey: obj) ifTrue: [ refs removeKey: obj ifAbsent: [known := known+1]. known := known-1]. ]. ours := (dummy project ifNil: [Project current]) world. refs keysDo: [:obj | obj isMorph ifTrue: [ ww := obj world. (ww == ours) | (ww == nil) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: obj printString, ' from another world')]]]. "keep original roots on the front of the list" dummy rootObject do: [:rr | refs removeKey: rr ifAbsent: []]. (self respondsTo: #classOrganizersBeRoots:) ifTrue: "an EToys extension" [self classOrganizersBeRoots: dummy]. ^dummy rootObject, refs keys asArray! ! !NebraskaSenderMorph methodsFor: 'parts bin' stamp: 'ct 9/12/2020 14:14'! initializeToStandAlone super initializeToStandAlone. self installModelIn: Project current world.! ! !NebraskaServerMorph class methodsFor: 'as yet unclassified' stamp: 'ct 9/12/2020 14:14'! serveWorld ^ self serveWorld: self currentWorld ! ! !NewVariableDialogMorph methodsFor: 'build' stamp: 'ct 9/12/2020 14:14'! rebuild | buttonColor itsName enableDecimalPlaces | self removeAllMorphs. self addAColumn: { self lockedString: self title. }. self addSeparator. self addARow: { self inAColumn: { (self addARow: { self lockedString: 'Name:' translated. self spacer. varNameText := self newTextMorph contentsWrapped: self varName; selectAll; crAction: (MessageSend receiver: self selector: #doAccept); yourself }) cellPositioning: #center. self inAColumn: { (self addARow: { self lockedString: 'Type:' translated. self spacer. varTypeButton := self buildVarTypeButton }) cellPositioning: #center. } named: #varType. } }. self currentHand newKeyboardFocus: varNameText. self addSeparator. self addDecimalPlaces. enableDecimalPlaces := false. (#(#Number #Point) includes: self varType) ifTrue: [ enableDecimalPlaces := true]. self allMorphsDo: [ :each | itsName := each knownName. (#(decimalPlaces) includes: itsName) ifTrue: [self enable: each when: enableDecimalPlaces]]. buttonColor := self color lighter. self addARow: { self inAColumn: { (self addARow: { self buttonNamed: 'Accept' translated action: #doAccept color: buttonColor help: 'keep changes made and close panel' translated. self buttonNamed: 'Cancel' translated action: #doCancel color: buttonColor help: 'cancel changes made and close panel' translated. }) listCentering: #center } }! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! step "Let all views know that some of my objects need to be updated." self monitorList do: [ :object | object ifNotNil: [self changed: #objectChanged with: object]]. self monitorList ifEmpty: [ self world stopStepping: self selector: #step ].! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! world ^ Project current world! ! !ObjectExplorer methodsFor: 'accessing - view' stamp: 'ct 9/12/2020 14:12'! views ^ self findDeepSubmorphsIn: self world that: [:morph | morph modelOrNil = self]! ! !ObjectsTool methodsFor: 'search' stamp: 'ct 9/12/2020 14:45'! showSearchPane "Set the receiver up so that it shows the search pane" | tabsPane aPane | modeSymbol == #search ifTrue: [ ^self ]. self partsBin removeAllMorphs. tabsPane := self tabsPane. aPane := self newSearchPane. self replaceSubmorph: tabsPane by: aPane. self modeSymbol: #search. self showMorphsMatchingSearchString. self currentHand newKeyboardFocus: aPane! ! !ParagraphEditor methodsFor: 'nonediting/nontyping keys' stamp: 'ct 9/12/2020 14:12'! escapeToDesktop: characterStream "Pop up a morph to field keyboard input in the context of the desktop" Smalltalk isMorphic ifTrue: [ Project current world putUpWorldMenuFromEscapeKey]. ^ true! ! !ParagraphEditor methodsFor: '*Etoys-Squeakland-editing keys' stamp: 'ct 9/12/2020 14:07'! shiftEnclose: characterStream "Insert or remove bracket characters around the current selection. Flushes typeahead." | char left right startIndex stopIndex oldSelection which text | char := sensor keyboard. char = $9 ifTrue: [ char := $( ]. char = $, ifTrue: "[ char := $< ]" [self closeTypeIn. Project current world showSourceKeyHit. ^ true]. char = $[ ifTrue: [ char := ${ ]. char = $' ifTrue: [ char := $" ]. char asciiValue = 27 ifTrue: [ char := ${ ]. "ctrl-[" self closeTypeIn. startIndex := self startIndex. stopIndex := self stopIndex. oldSelection := self selection. which := '([<{"''' indexOf: char ifAbsent: [1]. left := '([<{"''' at: which. right := ')]>}"''' at: which. text := paragraph text. ((startIndex > 1 and: [stopIndex <= text size]) and: [(text at: startIndex-1) = left and: [(text at: stopIndex) = right]]) ifTrue: ["already enclosed; strip off brackets" self selectFrom: startIndex-1 to: stopIndex. self replaceSelectionWith: oldSelection] ifFalse: ["not enclosed; enclose by matching brackets" self replaceSelectionWith: (Text string: (String with: left), oldSelection string ,(String with: right) emphasis: emphasisHere). self selectFrom: startIndex+1 to: stopIndex]. ^true! ! !PasteUpMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:10'! flapTab "Answer the tab affilitated with the receiver. Normally every flap tab is expected to have a PasteUpMorph which serves as its 'referent.'" | ww | self isFlap ifFalse: [^ nil]. ww := self presenter associatedMorph ifNil: [self]. ^ ww flapTabs detect: [:any| any referent == self] ifNone: [nil]! ! !PasteUpMorph methodsFor: 'events-processing' stamp: 'ct 9/16/2020 18:26'! processEvent: anEvent using: defaultDispatcher "Reimplemented to install the receiver as the new active world if it is one" self isWorldMorph ifFalse: [ ^ super processEvent: anEvent using: defaultDispatcher]. ^ self becomeActiveDuring: [ super processEvent: anEvent using: defaultDispatcher]! ! !PasteUpMorph methodsFor: 'flaps' stamp: 'ct 9/12/2020 14:11'! correspondingFlapTab "If there is a flap tab whose referent is me, return it, else return nil. Will also work for flaps on the edge of embedded subareas such as within scripting-areas, but more slowly." self currentWorld flapTabs do: [:aTab | aTab referent == self ifTrue: [^ aTab]]. "Catch guys in embedded worldlets" self currentWorld allMorphs do: [:aTab | ((aTab isKindOf: FlapTab) and: [aTab referent == self]) ifTrue: [^ aTab]]. ^ nil! ! !PasteUpMorph methodsFor: 'initialization' stamp: 'ct 9/16/2020 19:44'! becomeActiveDuring: aBlock "Make the receiver the active world during the evaluation of aBlock." ^ ActiveWorldVariable value: self during: aBlock! ! !PasteUpMorph methodsFor: 'menu & halo' stamp: 'ct 9/12/2020 14:07'! putUpPenTrailsSubmenu "Put up the pen trails menu" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'pen trails' translated. aMenu addStayUpItem. self addPenTrailsMenuItemsTo: aMenu. ^ aMenu popUpInWorld: self! ! !PasteUpMorph methodsFor: 'structure' stamp: 'ct 9/16/2020 19:39'! primaryHand self == self currentWorld ifFalse: [ ^ super primaryHand]. ^ self hands at: 1 ifAbsent: [nil]! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/12/2020 14:45'! extractScreenRegion: poly andPutSketchInHand: hand "The user has specified a polygonal area of the Display. Now capture the pixels from that region, and put in the hand as a Sketch." | screenForm outline topLeft innerForm exterior | outline := poly shadowForm. topLeft := outline offset. exterior := (outline offset: 0 at 0) anyShapeFill reverse. screenForm := Form fromDisplay: (topLeft extent: outline extent). screenForm eraseShape: exterior. innerForm := screenForm trimBordersOfColor: Color transparent. self currentHand showTemporaryCursor: nil. innerForm isAllWhite ifFalse: [hand attachMorph: (self drawingClass withForm: innerForm)]! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 20:07'! initializeDesktopCommandKeySelectors "Provide the starting settings for desktop command key selectors. Answer the dictionary." "ActiveWorld initializeDesktopCommandKeySelectors" | dict | dict := IdentityDictionary new. self defaultDesktopCommandKeyTriplets do: [:trip | | messageSend | messageSend := MessageSend receiver: trip second selector: trip third. dict at: trip first put: messageSend]. self setProperty: #commandKeySelectors toValue: dict. ^ dict! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 18:00'! putUpWorldMenuFromEscapeKey Preferences noviceMode ifFalse: [self putUpWorldMenu: self currentEvent]! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/16/2020 19:45'! install owner := nil. "since we may have been inside another world previously" submorphs do: [:ss | ss owner isNil ifTrue: [ss privateOwner: self]]. "Transcript that was in outPointers and then got deleted." self viewBox: Display boundingBox. EventSensor default flushEvents. worldState handsDo: [:h | h initForEvents]. self installFlaps. self borderWidth: 0. "default" (Preferences showSecurityStatus and: [SecurityManager default isInRestrictedMode]) ifTrue: [self borderWidth: 2; borderColor: Color red]. self presenter allExtantPlayers do: [:player | player prepareToBeRunning]. SystemWindow noteTopWindowIn: self.! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/12/2020 14:06'! repositionFlapsAfterScreenSizeChange "Reposition flaps after screen size change" (Flaps globalFlapTabsIfAny, self localFlapTabs) do: [:aFlapTab | aFlapTab applyEdgeFractionWithin: self bounds]. Flaps doAutomaticLayoutOfFlapsIfAppropriate! ! !PasteUpMorph methodsFor: '*Tools' stamp: 'ct 9/11/2020 20:07'! defaultDesktopCommandKeyTriplets "Answer a list of triplets of the form [+ optional fourth element, a for use in desktop-command-key-help] that will provide the default desktop command key handlers. If the selector takes an argument, that argument will be the command-key event" "World initializeDesktopCommandKeySelectors" | noviceKeys expertKeys | noviceKeys := { {$o. self. #activateObjectsTool. 'Activate the "Objects Tool"' translated}. {$r. self. #restoreMorphicDisplay. 'Redraw the screen' translated}. {$z. self. #undoOrRedoCommand. 'Undo or redo the last undoable command' translated}. {$F. Project current. #toggleFlapsSuppressed. 'Toggle the display of flaps' translated}. {$N. self. #toggleClassicNavigatorIfAppropriate. 'Show/Hide the classic Navigator, if appropriate' translated}. {$M. self. #toggleShowWorldMainDockingBar. 'Show/Hide the Main Docking Bar' translated}. {$]. Smalltalk. #saveSession. 'Save the image.' translated}. }. Preferences noviceMode ifTrue: [^ noviceKeys]. expertKeys := { {$b. SystemBrowser. #defaultOpenBrowser. 'Open a new System Browser' translated}. {$k. Workspace. #open. 'Open a new Workspace' translated}. {$m. self. #putUpNewMorphMenu. 'Put up the "New Morph" menu' translated}. {$O. self. #findAMonticelloBrowser. 'Bring a Monticello window into focus.' translated}. {$t. self. #findATranscript:. 'Make a System Transcript visible' translated}. {$w. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {Character escape. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {$C. self. #findAChangeSorter:. 'Make a Change Sorter visible' translated}. {$L. self. #findAFileList:. 'Make a File List visible' translated}. {$P. self. #findAPreferencesPanel:. 'Activate the Preferences tool' translated}. {$R. Utilities. #browseRecentSubmissions. 'Make a Recent Submissions browser visible' translated}. {$W. self. #findAMessageNamesWindow:. 'Make a MessageNames tool visible' translated}. {$Z. ChangeList. #browseRecentLog. 'Browse recently-logged changes' translated}. {$\. SystemWindow. #sendTopWindowToBack. 'Send the top window to the back' translated}. {$_. Smalltalk. #quitPrimitive. 'Quit the image immediately.' translated}. {$-. Preferences. #decreaseFontSize. 'Decrease all font sizes' translated}. {$+. Preferences. #increaseFontSize. 'Increase all font sizes' translated}. }. ^ noviceKeys, expertKeys! ! !PasteUpMorph methodsFor: '*Etoys-playfield' stamp: 'ct 9/12/2020 14:10'! galleryOfPlayers "Put up a tool showing all the players in the project" (Project current world findA: AllPlayersTool) ifNotNil: [:aTool | ^ aTool comeToFront]. AllPlayersTool newStandAlone openInHand "ActiveWorld galleryOfPlayers"! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:36'! attemptCleanupReporting: whetherToReport "Try to fix up some bad things that are known to occur in some etoy projects we've seen. If the whetherToReport parameter is true, an informer is presented after the cleanups" | fixes faultyStatusControls | fixes := 0. self world ifNotNil: [:world | world submorphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m submorphs isEmpty]] thenDo: [:m | m delete. fixes := fixes + 1]]. TransformationMorph allSubInstancesDo: [:m | (m player notNil and: [m renderedMorph ~~ m]) ifTrue: [m renderedMorph visible ifFalse: [m renderedMorph visible: true. fixes := fixes + 1]]]. (Player class allSubInstances select: [:cl | cl isUniClass and: [cl instanceCount > 0]]) do: [:aUniclass | fixes := fixes + aUniclass cleanseScripts]. self presenter flushPlayerListCache; allExtantPlayers. faultyStatusControls := ScriptStatusControl allInstances select: [:m |m fixUpScriptInstantiation]. fixes := fixes + faultyStatusControls size. ScriptNameTile allInstancesDo: [:aTile | aTile submorphs isEmpty ifTrue: [aTile setLiteral: aTile literal. fixes := fixes + 1]]. whetherToReport ifTrue: [self inform: ('{1} [or more] repair(s) made' translated format: {fixes printString})] ifFalse: [fixes > 0 ifTrue: [Transcript cr; show: fixes printString, ' repairs made to existing content.']] " ActiveWorld attemptCleanupReporting: true. ActiveWorld attemptCleanupReporting: false. "! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:09'! hideAllPlayers "Remove all Viewers belonging to scripted players associated with the receiver or any of its subjects from the screen." | a | a := OrderedCollection new. self allMorphsDo: [ :x | (self presenter currentlyViewing: x player) ifTrue: [a add: x player viewerFlapTab]]. a do: [ :each | each dismissViaHalo].! ! !PasteUpMorph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:08'! modernizeBJProject "Prepare a kids' project from the BJ fork of September 2000 -- a once-off thing for converting such projects forward to a modern 3.1a image, in July 2001. Except for the #enableOnlyGlobalFlapsWithIDs: call, this could conceivably be called upon reloading *any* project, just for safety." "ActiveWorld modernizeBJProject" self flag: #deprecate "ct: No senders". ScriptEditorMorph allInstancesDo: [:m | m userScriptObject]. Flaps enableOnlyGlobalFlapsWithIDs: {'Supplies' translated}. self abandonOldReferenceScheme. self relaunchAllViewers.! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:11'! abandonUnsituatedPlayers "If any objects in the project have references, in player-valued variables, to other objects otherwise not present in the project, abandon them and replace former references to them by references to Dot" | aList dot slotInfo varName ref allPlayers count | count := 0. allPlayers := self presenter reallyAllExtantPlayersNoSort. aList := allPlayers select: [:m | m belongsToUniClass]. dot := self presenter standardPlayer. aList do: [:p | p class slotInfo associationsDo: [:assoc | slotInfo := assoc value. varName := assoc key. (slotInfo type = #Player) ifTrue: [ref := p instVarNamed: varName. (allPlayers includes: ref) ifFalse: [p instVarNamed: varName put: dot. count := count + 1. Transcript cr; show: ('Variable named "{1}" in player named "{2}" changed to point to Dot' translated format: {varName. ref externalName})]]]]. aList := nil. "Increases chance of the next line having desired effect." self inform: ('{1} item(s) fixed up' translated format: {count}). WorldState addDeferredUIMessage: [Smalltalk garbageCollect]! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/12/2020 14:07'! putUpShowSourceMenu: evt title: aTitle "Put up a menu in response to the show-source button being hit" | menu | self bringTopmostsToFront. "put up the show-source menu" menu := (TheWorldMenu new adaptToWorld: self) buildShowSourceMenu. menu addTitle: aTitle. menu popUpEvent: evt in: self. ^ menu! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/11/2020 18:01'! showSourceKeyHit "The user hit the 'show source' key on the XO. Our current take on this is simply to put up the world menu..." ^ self putUpShowSourceMenu: self currentEvent title: 'etoys source' translated! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menus' stamp: 'ct 9/12/2020 14:46'! presentDesktopColorMenu "Present the menu that governs the fill style of the squeak desktop." | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'desktop color' translated. self fillStyle addFillStyleMenuItems: aMenu hand: self currentHand from: self. aMenu addLine. aMenu add: 'solid fill' translated action: #useSolidFill. aMenu add: 'gradient fill' translated action: #useGradientFill. aMenu add: 'bitmap fill' translated action: #useBitmapFill. aMenu add: 'default fill' translated action: #useDefaultFill. ^ aMenu popUpInWorld! ! !PasteUpMorph methodsFor: 'as yet unclassified' stamp: 'ct 9/24/2020 13:50'! activeHand self == self currentWorld ifFalse: [ ^ super activeHand]. ^ worldState primaryHand! ! !EventTimeline methodsFor: 'dropping/grabbing' stamp: 'ct 9/11/2020 19:47'! acceptDroppingMorph: aMorph event: evt "Accept the drop of a morph." | aRect anEventRoll itsDuration itsWidthAfterDrop | self flag: #deferred. "This is a possible place for discovering whether the drop would have damaging effects on the mouse track..." (aMorph isKindOf: MouseEventSequenceMorph) ifTrue: [itsDuration := aMorph durationInMilliseconds. itsWidthAfterDrop := itsDuration // self eventRoll millisecondsPerPixel. super acceptDroppingMorph: aMorph event: evt. aMorph bounds: ((aMorph left @ 6) extent: (itsWidthAfterDrop @ aMorph height)). submorphs do: [:m | ((m ~~ aMorph) and: [m isKindOf: MouseEventSequenceMorph]) ifTrue: [(m bounds intersects: aMorph bounds) ifTrue: ["Eureka" aMorph delete. aMorph position: 100 at 100. aMorph openInWorld. aMorph flash. ^ self]]]] ifFalse: [super acceptDroppingMorph: aMorph event: evt] . aRect := (((aMorph left + 10) max: 10) @ 0) extent: 100@ 10. (anEventRoll := self eventRoll) pushChangesBackToEventTheatre. "Note that will ultimately result in replacement of the receiver by a new timeline" aMorph delete. self currentWorld abandonAllHalos. anEventRoll scrollPaneForRoll scrollHorizontallyToShow: aRect! ! !PasteUpMorph class methodsFor: '*Etoys-Squeakland-eToys-scripting' stamp: 'ct 9/12/2020 14:09'! putativeAdditionsToViewerCategoryPlayfieldOptions "Answer playfield options additions. Some of these are not yet underpinned by code in the current image; these will follow in due course." self flag: #deprecate. "ct: No senders" ^ #(#'playfield options' ( (command roundUpStrays 'Bring back all objects whose current coordinates keep them from being visible, so that at least a portion of each of my interior objects can be seen.') (command makeFitContents 'Adjust my bounds so that I fit precisely around all the objects within me') (command showAllPlayers 'Make visible the viewers for all players which have user-written scripts in this playfield.') (command hideAllPlayers 'Make invisible the viewers for all players in this playfield. This will save space before you publish this project') (command shuffleSubmorphs 'Rearranges my contents in random order') (command showAllObjectNames 'show names beneath all the objects currently in my interior, except for those for which showing such names is inappropriate.') (command hideAllObjectNames 'stop showing names beneath all the objects of my interior, If any of them is marked to "always show name", remove that designation')))! ! !PartsBin class methodsFor: '*Etoys-Squeakland-thumbnail cache' stamp: 'ct 9/12/2020 14:11'! rebuildIconsWithProgress "Put up an eye-catching progress morph while doing a complete rebuild of all the parts icons in the system." | fixBlock | fixBlock := Project current displayProgressWithJump: 'Building icons' translated. self clearThumbnailCache. self cacheAllThumbnails. fixBlock value. Project current world fullRepaintNeeded.! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: ((submorphs at: (2 max: submorphs size)) bottomRight + (2 at 1))). "inHotZone := evt ifNil: [true] ifNotNil: [rect containsPoint: evt cursorPoint]." aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/11/2020 20:47'! createMultipleTestScripts: aCount "Simulate the action of dropping a copy of the receiver to launch a new script -- for performance testing. To use: Open an Inspector on some tile command in a Viewer, e.g. on 'Car forward 5'. In the trash pane of that Inspector, then, evaluate expressions like: [self createMultipleTestScripts: 10] timeToRun. and MessageTally spyOn: [self createMultipleTestScripts: 4] " | aPosition | aPosition := 10 at 10. 1 to: aCount do: [:i | self forceScriptCreationAt: aPosition. aPosition := aPosition + (0 @ 50). "avoid dropping into existing scriptor" Project current world doOneCycle] "refresh viewer"! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/12/2020 14:47'! forceScriptCreationAt: aPosition "For performance testing." | dup | dup := self duplicate. dup eventHandler: nil. "Remove viewer-related evt mouseover feedback" dup formerPosition: self currentHand position. self currentHand attachMorph: dup; simulateMorphDropAt: aPosition.! ! !PhraseTileForTest methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTest methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := CompoundTileMorph new. guyToTake setNamePropertyTo: 'TestTile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !PhraseTileForTimesRepeat methodsFor: 'hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTimesRepeat methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := TimesRepeatTile new. guyToTake setNamePropertyTo: 'Repeat Tile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! adoptScriptsFrom "Let the user click on another object form which the receiver should obtain scripts and code" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ Beeper beep]. (aMorph renderedMorph isSketchMorph and: [aMorph player belongsToUniClass] and: [self belongsToUniClass not]) ifTrue: [costume acquirePlayerSimilarTo: aMorph player] ifFalse: [Beeper beep].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! beRevealedInActiveWorld "Reveal my corresponding morph in the active world" self revealPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! grabPlayerInActiveWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" ^ self grabPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/12/2020 14:47'! grabPlayerIn: aWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" | aMorph newPosition | self costume == aWorld ifTrue: [^ self]. self currentHand releaseMouseFocus. (aMorph := self costume) visible: true. newPosition := self currentHand position - (aMorph extent // 2). aMorph isInWorld ifTrue: [aMorph goHome. aMorph formerPosition: aMorph positionInWorld] ifFalse: [aMorph formerPosition: aWorld center]. aMorph formerOwner: Project current world. aMorph position: newPosition. self currentHand targetOffset: aMorph position - self currentHand position; addMorphBack: aMorph.! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! impartSketchScripts "Let the user designate another object to which my scripts and code should be imparted" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ self]. (aMorph renderedMorph isSketchMorph) ifTrue: [ aMorph acquirePlayerSimilarTo: self].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:44'! offerAlternateViewerMenuFor: aViewer event: evt "Put up an alternate Viewer menu on behalf of the receiver." | menu world | world := aViewer world. menu := MenuMorph new defaultTarget: self. (costumes notNil and: [ (costumes size > 1 or: [costumes size == 1 and: [costumes first ~~ costume renderedMorph]])]) ifTrue: [menu add: 'forget other costumes' translated target: self selector: #forgetOtherCostumes]. menu add: 'expunge empty scripts' translated target: self action: #expungeEmptyScripts. menu addLine. menu add: 'choose vocabulary...' translated target: aViewer action: #chooseVocabulary; balloonTextForLastItem: 'Choose a different vocabulary for this Viewer.' translated. menu add: 'choose limit class...' translated target: aViewer action: #chooseLimitClass; balloonTextForLastItem: 'Specify what the limitClass should be for this Viewer -- i.e., the most generic class whose methods and categories should be considered here.' translated. menu add: 'open standard lexicon' translated target: aViewer action: #openLexicon; balloonTextForLastItem: 'open a window that shows the code for this object in traditional programmer format' translated. menu add: 'open lexicon with search pane' translated target: aViewer action: #openSearchingProtocolBrowser; balloonTextForLastItem: 'open a lexicon that has a type-in pane for search (not recommended!!)' translated. menu addLine. menu add: 'inspect morph' translated target: costume selector: #inspect. menu add: 'inspect player' translated target: self selector: #inspect. self belongsToUniClass ifTrue: [ menu add: 'browse class' translated target: self action: #browsePlayerClass. menu add: 'inspect class' translated target: self class action: #inspect]. menu add: 'inspect this Viewer' translated target: aViewer selector: #inspect. menu add: 'inspect this Vocabulary' translated target: aViewer currentVocabulary selector: #inspect. menu addLine. menu add: 'relaunch this Viewer' translated target: aViewer action: #relaunchViewer. menu add: 'attempt repairs' translated target: Project current world action: #attemptCleanup. menu add: 'destroy all this object''s scripts' translated target: self action: #destroyAllScripts. menu add: 'view morph directly' translated target: aViewer action: #viewMorphDirectly. menu balloonTextForLastItem: 'opens a Viewer directly on the rendered morph.' translated. costume renderedMorph isSketchMorph ifTrue: [ menu addLine. menu add: 'impart scripts to...' translated target: self action: #impartSketchScripts]. ^ menu popUpEvent: evt in: world! ! !Player methodsFor: 'scripts-standard' stamp: 'ct 9/12/2020 14:48'! hide "Make the object be hidden, as opposed to visible" self currentHand ifNotNil: [:hand | (hand keyboardFocus == self costume renderedMorph) ifTrue: [ hand releaseKeyboardFocus]]. self costume hide.! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:45'! getLastKeystroke "Answer the last keystroke fielded" ^ Project current world lastKeystroke! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:41'! setLastKeystroke: aString "Set the last keystroke fielded" ^ self currentWorld lastKeystroke: aString! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/12/2020 14:49'! setSecondColor: aColor "Setter for costume's second color, if it's using gradient fill; if not, does nothing" | morph fillStyle colorToUse | morph := costume renderedMorph. fillStyle := morph fillStyle. fillStyle isGradientFill ifFalse: [^ self]. colorToUse := (costume isWorldMorph and: [aColor isColor]) ifTrue: [aColor alpha: 1.0] "reject any translucency" ifFalse: [aColor]. fillStyle lastColor: colorToUse forMorph: morph hand: self currentHand.! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:47'! addInstanceVariable "Offer the user the opportunity to add an instance variable, and if he goes through with it, actually add it." Project current world addMorphInLayer: (NewVariableDialogMorph on: self costume) centeredNear: (self currentHand ifNil:[Sensor]) cursorPoint! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:46'! allPossibleWatchersFromWorld "Answer a list of all UpdatingStringMorphs, PlayerReferenceReadouts, ThumbnailMorphs, and UpdatingReferenceMorphs in the Active world and its hidden book pages, etc., which have me or any of my siblings as targets" | a | a := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: a. ^ a select: [:e | e isEtoyReadout and: [e target class == self class]]! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/12/2020 14:48'! offerGetterTiles: slotName "For a player-type slot, offer to build convenient compound tiles that otherwise would be hard to get" | typeChoices typeChosen thePlayerThereNow slotChoices slotChosen getterTiles aCategoryViewer playerGetter | typeChoices := Vocabulary typeChoices. typeChosen := UIManager default chooseFrom: (typeChoices collect: [:t | t translated]) values: typeChoices title: ('Choose the TYPE of data to get from {1}''s {2}' translated format: {self externalName. slotName translated}). typeChosen isEmptyOrNil ifTrue: [^self]. thePlayerThereNow := self perform: slotName asGetterSelector. thePlayerThereNow ifNil: [thePlayerThereNow := self presenter standardPlayer]. slotChoices := thePlayerThereNow slotNamesOfType: typeChosen. slotChoices isEmpty ifTrue: [^self inform: 'sorry -- no slots of that type' translated]. slotChoices sort. slotChosen := UIManager default chooseFrom: (slotChoices collect: [:t | t translated]) values: slotChoices title: ('Choose the datum you want to extract from {1}''s {2}' translated format: {self externalName. slotName translated}). slotChosen isEmptyOrNil ifTrue: [^self]. "Now we want to tear off tiles of the form holder's valueAtCursor's foo" getterTiles := nil. aCategoryViewer := CategoryViewer new initializeFor: thePlayerThereNow categoryChoice: 'basic'. getterTiles := aCategoryViewer getterTilesFor: slotChosen asGetterSelector type: typeChosen. aCategoryViewer := CategoryViewer new initializeFor: self categoryChoice: 'basic'. playerGetter := aCategoryViewer getterTilesFor: slotName asGetterSelector type: #Player. getterTiles submorphs first acceptDroppingMorph: playerGetter event: nil. "the pad" "simulate a drop" getterTiles makeAllTilesGreen. getterTiles justGrabbedFromViewer: false. (getterTiles firstSubmorph) changeTableLayout; hResizing: #shrinkWrap; vResizing: #spaceFill. self currentHand attachMorph: getterTiles.! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:46'! addPatchVarNamed: nameSymbol | f | f := KedamaPatchMorph newExtent: self costume dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). self addInstanceVariable2Named: nameSymbol type: #Patch value: f player. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatch | f usedNames newName | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f kedamaWorld: self costume renderedMorph. usedNames := Project current world allKnownNames, self class instVarNames. newName := Utilities keyLike: f innocuousName satisfying: [:aName | (usedNames includes: aName) not]. f setNameTo: newName. self createSlotForPatch: f. self addToPatchDisplayList: f assuredPlayer. self costume world primaryHand attachMorph: f. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtle | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). self costume world primaryHand attachMorph: m. ^ m! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleSilently | m | m := KedamaTurtleMorph new openInWorld. self useTurtle: m player. m turtleCount: 0. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !Player methodsFor: '*Etoys-Squeakland-scripts-standard' stamp: 'ct 9/11/2020 20:41'! printInTranscript "Print a line representing the receiver in the Transcript" Project current world findATranscript: nil. Transcript cr; show: (Time now printString copyWithoutAll: '()'); space; show: self costume printString.! ! !Player methodsFor: '*Etoys-Squeakland-slots-user' stamp: 'ct 9/12/2020 14:47'! changeSlotInfo: aSymbol Project current world addMorphInLayer: (ModifyVariableDialogMorph on: self costume slot: aSymbol) centeredNear: (self currentHand ifNil: [Sensor]) cursorPoint.! ! !Player methodsFor: '*Etoys-Squeakland-slot getters/setters' stamp: 'ct 9/12/2020 14:47'! handUserPictureOfPenTrail "Called from the user-interface: hand the user a picture of the pen trail" self getHasPenTrails ifFalse: [ ^ self inform: 'no pen trails present' translated]. self currentHand attachMorph: (SketchMorph new form: self getPenTrailGraphic).! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! kedamaWorld ^ Project current world findDeeplyA: KedamaMorph ! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatchForSet | f | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). f kedamaWorld: self costume renderedMorph. self createSlotForPatch: f. ^ f! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleForSet | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !PlayerSurrogate methodsFor: 'menu' stamp: 'ct 9/11/2020 20:37'! revealThisObject "Reveal the object I represent" playerRepresented revealPlayerIn: Project current world! ! !PlayerSurrogate methodsFor: '*Etoys-Squeakland-as yet unclassified' stamp: 'ct 9/11/2020 20:40'! forciblyRenamePlayer "Allow the receiver to seize a name already nominally in use in the project." | current reply currentlyBearingName newNameForHim binding | current := playerRepresented knownName. reply := FillInTheBlank request: 'Type the name you insist upon' translated initialAnswer: current. reply isEmptyOrNil ifTrue: [^ self]. Preferences uniquePlayerNames ifFalse: [^ self costume renameTo: reply]. reply := (reply asIdentifier: true) asSymbol. reply = current ifTrue: [^ self inform: 'no change' translated]. binding := Project current world referencePool hasBindingOf: reply. binding ifNotNil: [ currentlyBearingName := binding value. newNameForHim := Utilities keyLike: reply satisfying: [:name | (Project current world referencePool includesKey: name) not]. currentlyBearingName renameTo: newNameForHim]. playerRepresented renameTo: reply. self inform: (binding ifNil: [('There was no conflict; this object is now named {1}' translated format: {reply})] ifNotNil: ['Okay, this object is now named\{1}\and the object formerly known by this name is now called\{2}' translated format: {reply. newNameForHim}]).! ! !PlayerType methodsFor: 'tiles' stamp: 'ct 9/11/2020 20:37'! defaultArgumentTile "Answer a tile to represent the type" ^ Project current world presenter standardPlayer tileToRefer! ! !KedamaPatchType methodsFor: 'tile protocol' stamp: 'ct 9/11/2020 20:15'! defaultArgumentTile "Answer a tile to represent the type" | patch ks k p | patch := KedamaPatchTile new typeColor: self typeColor. ks := self world allMorphs select: [:e | e isKindOf: KedamaMorph]. ks isEmpty ifFalse: [ k := ks first. p := k player getPatch. ] ifTrue: [ k := KedamaPatchMorph new. k assuredPlayer. p := k player. ]. patch usePatch: p. ^ patch! ! !PluggableFileList methodsFor: 'StandardFileMenu' stamp: 'ct 9/12/2020 14:50'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PluggableListMorph methodsFor: 'model access - keystroke' stamp: 'ct 9/11/2020 18:01'! specialKeyPressed: asciiValue "A special key with the given ascii-value was pressed; dispatch it" | oldSelection nextSelection max howManyItemsShowing | (#(8 13) includes: asciiValue) ifTrue: [ "backspace key - clear the filter, restore the list with the selection" model okToChange ifFalse: [^ self]. self removeFilter. priorSelection ifNotNil: [ | prior | prior := priorSelection. priorSelection := self getCurrentSelectionIndex. asciiValue = 8 ifTrue: [ self changeModelSelection: prior ] ]. ^ self ]. asciiValue = 27 ifTrue: [" escape key" ^ self currentEvent shiftPressed ifTrue: [self currentEvent putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]]. max := self maximumSelection. max > 0 ifFalse: [^ self]. nextSelection := oldSelection := self selectionIndex. asciiValue = 31 ifTrue: [" down arrow" nextSelection := oldSelection + 1. nextSelection > max ifTrue: [nextSelection := 1]]. asciiValue = 30 ifTrue: [" up arrow" nextSelection := oldSelection - 1. nextSelection < 1 ifTrue: [nextSelection := max]]. asciiValue = 1 ifTrue: [" home" nextSelection := 1]. asciiValue = 4 ifTrue: [" end" nextSelection := max]. howManyItemsShowing := self numSelectionsInView. asciiValue = 11 ifTrue: [" page up" nextSelection := 1 max: oldSelection - howManyItemsShowing]. asciiValue = 12 ifTrue: [" page down" nextSelection := oldSelection + howManyItemsShowing min: max]. model okToChange ifFalse: [^ self]. "No change if model is locked" oldSelection = nextSelection ifTrue: [^ self flash]. ^ self changeModelSelection: (self modelIndexFor: nextSelection)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:50'! startUpCenteredWithCaption: captionOrNil "Differs from startUpWithCaption: by appearing with cursor in the menu, and thus ready to act on mouseUp, without requiring user tweak to confirm" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint - (20 @ 0)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." self flag: #fix. "mt: Could we manage to open pop-up menus in Morphic without accessing self currentHand?" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil icon: aForm "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil icon: aForm at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithoutKeyboard "Display and make a selection from the receiver as long as the button is pressed. Answer the current selection. Do not allow keyboard input into the menu" ^ self startUpWithCaption: nil at: ((self currentHand ifNil: [Sensor]) cursorPoint) allowKeyboard: false! ! !PopUpMenu methodsFor: '*Morphic-Menus' stamp: 'ct 9/11/2020 20:37'! morphicStartUpWithCaption: captionOrNil icon: aForm at: location allowKeyboard: aBoolean "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard." selection := Cursor normal showWhile: [| menuMorph | menuMorph := MVCMenuMorph from: self title: nil. (captionOrNil notNil or: [aForm notNil]) ifTrue: [menuMorph addTitle: captionOrNil icon: aForm]. MenuIcons decorateMenu: menuMorph. menuMorph invokeAt: location in: self currentWorld allowKeyboard: aBoolean]. ^ selection! ! !PopUpMenu methodsFor: '*Etoys-Squeakland-basic control sequence' stamp: 'ct 9/11/2020 20:37'! startUpWithCaption: captionOrNil at: location allowKeyboard: allowKeyboard centered: centered "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard If centered is true, the menu items are displayed centered.." | maxHeight aMenu | (ProvideAnswerNotification signal: captionOrNil) ifNotNil: [:answer | ^ selection := answer ifTrue: [1] ifFalse: [2]]. maxHeight := Display height*3//4. self frameHeight > maxHeight ifTrue: [^ self startUpSegmented: maxHeight withCaption: captionOrNil at: location allowKeyboard: allowKeyboard]. Smalltalk isMorphic ifTrue:[ selection := Cursor normal showWhile: [aMenu := MVCMenuMorph from: self title: captionOrNil. centered ifTrue: [aMenu submorphs allButFirst do: [:m | m setProperty: #centered toValue: true]]. aMenu invokeAt: location in: self currentWorld allowKeyboard: allowKeyboard]. ^ selection]. frame ifNil: [self computeForm]. Cursor normal showWhile: [self displayAt: location withCaption: captionOrNil during: [self controlActivity]]. ^ selection! ! !PopUpMenu class methodsFor: '*Etoys-Squeakland-dialogs' stamp: 'ct 9/12/2020 14:52'! informCenteredAboveCursor: aString "Put up an informer showing the given string in a box, with the OK button for dismissing the informer having the cursor at its center." "PopUpMenu informCenteredAboveCursor: 'I like Squeak\how about you?' withCRs" | lines maxWid xCoor | lines := Array streamContents: [:aStream | aString linesDo: [:l | aStream nextPut: l]]. maxWid := (lines collect: [:l | Preferences standardMenuFont widthOfString: l]) max. xCoor := self currentHand cursorPoint x - (maxWid // 2). ((xCoor + maxWid) > self currentWorld right) ifTrue: [xCoor := self currentWorld right]. "Caters to problematic PopUpMenu boundary behavior" (PopUpMenu labels: 'OK' translated) startUpWithCaption: aString at: (xCoor @ self currentHand cursorPoint y) allowKeyboard: true centered: true.! ! !PreferenceWizardMorph methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:35'! initializePreviewWorld | w1 w2 w3 | previewWorld := PasteUpMorph new hResizing: #spaceFill; vResizing: #spaceFill; viewBox: (0 at 0 corner: 500 at 500); layoutFrame: (LayoutFrame fractions: (0.3 @ 0 corner: 1.0 @ 1.0) offsets: (0@ titleMorph height corner: 0 @ buttonRowMorph height negated)); fillStyle: Project current world fillStyle; borderWidth: 2; borderColor: Color white; cornerStyle: (self hasLowPerformance ifTrue: [#square] ifFalse: [#rounded]); yourself. w1 := (ToolSet browse: Morph selector: #drawOn:) dependents detect: [:ea | ea isSystemWindow]. w2 := ToolSet browseMessageSet: (SystemNavigation default allCallsOn: #negated) name: 'Senders' translated autoSelect: 'negated'. w3 := (Workspace new contents: '3+4 "Select and hit [CMD]+[P]."') openLabel: 'Workspace'. {w1. w2. w3} do: [:ea | ea makeUnclosable. previewWorld addMorph: ea]. self updateWindowBounds.! ! !PreferenceWizardMorph methodsFor: 'support' stamp: 'ct 9/12/2020 14:36'! adjustSettingsForLowPerformance self updateLowPerformanceLabel: 'Please wait, optimizing performance...' translated. self refreshWorld. self stateGradients "flat look" ifFalse: [self toggleGradients]. self stateBlinkingCursor ifTrue: [self toggleBlinkingCursor]. self stateFastDrag ifFalse: [self toggleFastDrag]. self stateSoftShadows ifTrue: [self toggleSoftShadows]. self stateHardShadows ifTrue: [self toggleHardShadows]. self stateRoundedWindowLook ifTrue: [self toggleRoundedWindowLook]. self stateRoundedButtonLook ifTrue: [self toggleRoundedButtonLook]. self stateAttachToolsToMouse ifTrue: [self toggleAttachToolsToMouse]. self stateToolAndMenuIcons ifTrue: [self toggleToolAndMenuIcons]. self stateSmartHorizontalSplitters ifTrue: [self toggleSmartHorizontalSplitters]. self stateSmartVerticalSplitters ifTrue: [self toggleSmartVerticalSplitters]. PluggableListMorph highlightHoveredRow: false; filterableLists: false; highlightPreSelection: true; "Feedback is important!!" flashOnErrors: false. TheWorldMainDockingBar showSecondsInClock: false. Preferences disable: #balloonHelpInMessageLists. "Set simple background." Project current world setAsBackground: MorphicProject defaultFill. previewWorld fillStyle: Project current world fillStyle. "Done." self updateLowPerformanceLabel: 'Settings were adjusted for optimal performance.' translated.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! roundedWindowCornersChanged "The user changed the value of the roundedWindowCorners preference. React" Project current world fullRepaintNeeded.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! vectorVocabularySettingChanged "The current value of the useVectorVocabulary flag has changed; now react. No senders, but invoked by the Preference object associated with the #useVectorVocabulary preference." Smalltalk isMorphic ifFalse: [^ self]. Project current world makeVectorUseConformToPreference.! ! !Project methodsFor: '*Etoys-Squeakland-file in/out' stamp: 'ct 9/16/2020 19:18'! storeOnServerWithNoInteractionInnards "Save to disk as an Export Segment. Then put that file on the server I came from, as a new version. Version is literal piece of file name. Mime encoded and http encoded." | newName primaryServerDirectory serverVersionPair localDirectory localVersionPair myVersionNumber warning maxNumber myDepth | self assureIntegerVersion. "Find out what version" primaryServerDirectory := self defaultFolderForAutoSaving ifNil: [^self]. localDirectory := self squeakletDirectory. serverVersionPair := self class mostRecent: self name onServer: primaryServerDirectory. localVersionPair := self class mostRecent: self name onServer: localDirectory. maxNumber := myVersionNumber := self currentVersionNumber. ProgressNotification signal: '2:versionsDetected'. warning := ''. myVersionNumber < serverVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) on the server' translated. maxNumber := maxNumber max: serverVersionPair second. ]. myVersionNumber < localVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) in the local directory' translated. maxNumber := maxNumber max: localVersionPair second. ]. version := self bumpVersion: maxNumber. "write locally - now zipped automatically" Display isVirtualScreen ifTrue: [ myDepth := displayDepth. displayDepth := OLPCVirtualScreen preferredScreenDepth.. ]. newName := self versionedFileName. lastSavedAtSeconds := Time totalSeconds. self exportSegmentFileName: newName directory: localDirectory withoutInteraction: true. (localDirectory readOnlyFileNamed: newName) setFileTypeToObject; close. Display isVirtualScreen ifTrue: [ displayDepth := myDepth. ]. ProgressNotification signal: '4:localSaveComplete'. "3 is deep in export logic" primaryServerDirectory ifNotNil: [ [ primaryServerDirectory writeProject: self inFileNamed: newName asFileName fromDirectory: localDirectory. ] on: ProjectPasswordNotification do: [ :ex | ex resume: '' ]. ]. ProgressNotification signal: '9999 save complete'.! ! !Project methodsFor: '*Etoys-Squeakland-language' stamp: 'ct 9/11/2020 20:34'! updateLocaleDependentsWithPreviousSupplies: aCollection gently: gentlyFlag "Set the project's natural language as indicated" | morphs scriptEditors | gentlyFlag ifTrue: [ LanguageEnvironment localeChangedGently. ] ifFalse: [ LanguageEnvironment localeChanged. ]. morphs := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: morphs. scriptEditors := morphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m topEditor == m]]. (morphs copyWithoutAll: scriptEditors) do: [:morph | morph localeChanged]. scriptEditors do: [:m | m localeChanged]. Flaps disableGlobalFlaps: false. SugarNavigatorBar showSugarNavigator ifTrue: [Flaps addAndEnableEToyFlapsWithPreviousEntries: aCollection. Project current world addGlobalFlaps] ifFalse: [Preferences eToyFriendly ifTrue: [Flaps addAndEnableEToyFlaps. Project current world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]]. (Project current isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ParagraphEditor initializeTextEditorMenus. MenuIcons initializeTranslations. #(PartsBin ParagraphEditor BitEditor FormEditor StandardSystemController) do: [ :key | Smalltalk at: key ifPresent: [ :class | class initialize ]]. Project current world reformulateUpdatingMenus. "self setFlaps. self setPaletteFor: aLanguageSymbol." ! ! !MorphicProject methodsFor: 'utilities' stamp: 'ct 9/12/2020 15:13'! createViewIfAppropriate "Create a project view for the receiver and place it appropriately on the screen." | aMorph requiredWidth existing proposedV proposedH despair | ProjectViewOpenNotification signal ifTrue: [Preferences projectViewsInWindows ifTrue: [(ProjectViewMorph newProjectViewInAWindowFor: self) openInWorld] ifFalse: [aMorph := ProjectViewMorph on: self. requiredWidth := aMorph width + 10. existing := self currentWorld submorphs select: [:m | m isKindOf: ProjectViewMorph] thenCollect: [:m | m fullBoundsInWorld]. proposedV := 85. proposedH := 10. despair := false. [despair not and: [((proposedH @ proposedV) extent: requiredWidth) intersectsAny: existing]] whileTrue: [proposedH := proposedH + requiredWidth. proposedH + requiredWidth > self currentWorld right ifTrue: [proposedH := 10. proposedV := proposedV + 90. proposedV > (self currentWorld bottom - 90) ifTrue: [proposedH := self currentWorld center x - 45. proposedV := self currentWorld center y - 30. despair := true]]]. aMorph position: (proposedH @ proposedV). aMorph openInWorld]]! ! !MorphicProject methodsFor: 'flaps support' stamp: 'ct 9/12/2020 14:34'! setFlaps | flapTabs flapIDs sharedFlapTabs navigationMorph | self flag: #toRemove. "check if this method still used by Etoys" flapTabs := self world flapTabs. flapIDs := flapTabs collect: [:tab | tab knownName]. flapTabs do: [:tab | (tab isMemberOf: ViewerFlapTab) ifFalse: [tab isGlobalFlap ifTrue: [Flaps removeFlapTab: tab keepInList: false. tab currentWorld reformulateUpdatingMenus] ifFalse: [| referent | referent := tab referent. referent isInWorld ifTrue: [referent delete]. tab delete]]]. sharedFlapTabs := Flaps classPool at: #SharedFlapTabs. flapIDs do: [:id | id = 'Navigator' translated ifTrue: [sharedFlapTabs add: Flaps newNavigatorFlap]. id = 'Widgets' translated ifTrue: [sharedFlapTabs add: Flaps newWidgetsFlap]. id = 'Tools' translated ifTrue: [sharedFlapTabs add: Flaps newToolsFlap]. id = 'Squeak' translated ifTrue: [sharedFlapTabs add: Flaps newSqueakFlap]. id = 'Supplies' translated ifTrue: [sharedFlapTabs add: Flaps newSuppliesFlap]. id = 'Stack Tools' translated ifTrue: [sharedFlapTabs add: Flaps newStackToolsFlap]. id = 'Painting' translated ifTrue: [sharedFlapTabs add: Flaps newPaintingFlap]. id = 'Objects' translated ifTrue: [sharedFlapTabs add: Flaps newObjectsFlap ]]. 2 timesRepeat: [flapIDs do: [:id | Flaps enableDisableGlobalFlapWithID: id]]. self world flapTabs do: [:flapTab | flapTab isCurrentlyTextual ifTrue: [flapTab changeTabText: flapTab knownName]]. Flaps positionNavigatorAndOtherFlapsAccordingToPreference. navigationMorph := self currentWorld findDeeplyA: ProjectNavigationMorph preferredNavigator. navigationMorph isNil ifTrue: [^ self]. navigationMorph allMorphs do: [:morph | morph class == SimpleButtonDelayedMenuMorph ifTrue: [(morph findA: ImageMorph) isNil ifTrue: [| label | label := morph label. label isNil ifFalse: [| name | name := morph knownName. name isNil ifTrue: [morph name: label. name := label]. morph label: name translated]]]]! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/16/2020 19:45'! clearGlobalState "Clean up global state. This method may be removed if the use of global state variables is eliminated." "If global World is defined, clear it now. The value is expected to be set again as a new project is entered." Smalltalk globals at: #World ifPresent: [:w | Smalltalk globals at: #World put: nil].! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/12/2020 14:45'! wakeUpTopWindow "Image has been restarted, and the startUp list has been processed. Perform any additional actions needed to restart the user interface." SystemWindow wakeUpTopWindowUponStartup. Preferences mouseOverForKeyboardFocus ifTrue: [ "Allow global command keys to work upon re-entry without having to cause a focus change first." self currentHand releaseKeyboardFocus ]! ! !MorphicProject methodsFor: 'language' stamp: 'ct 9/12/2020 14:17'! updateLocaleDependents "Set the project's natural language as indicated" (self world respondsTo: #isTileScriptingElement) ifTrue: "Etoys present" [ self world allTileScriptingElements do: [:viewerOrScriptor | viewerOrScriptor localeChanged]]. Flaps disableGlobalFlaps: false. (Preferences eToyFriendly or: [ (Smalltalk classNamed: #SugarNavigatorBar) ifNotNil: [:c | c showSugarNavigator] ifNil: [false]]) ifTrue: [ Flaps addAndEnableEToyFlaps. self world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]. (self isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ScrapBook default emptyScrapBook. MenuIcons initializeTranslations. super updateLocaleDependents. "self setFlaps. self setPaletteFor: aLanguageSymbol."! ! !MorphicProject methodsFor: 'protocols' stamp: 'ct 9/12/2020 14:18'! currentVocabulary ^ self world currentVocabulary! ! !MorphicProject methodsFor: 'scheduling & debugging' stamp: 'ct 9/16/2020 19:45'! interruptCleanUpFor: interruptedProcess "Clean up things in case of a process interrupt." super interruptCleanUpFor: interruptedProcess. self uiProcess == interruptedProcess ifTrue: [ self currentHand ifNotNil: [:hand | hand interrupted]. Preferences eToyFriendly ifTrue: [ Project current world stopRunningAll]].! ! !MorphicProject class methodsFor: 'shrinking' stamp: 'ct 9/12/2020 15:45'! unloadMorphic "MorphicProject unloadMorphic" Project current isMorphic ifTrue: [ ^ Error signal: 'You can only unload Morphic from within another kind of project.' translated]. MorphicProject removeProjectsFromSystem. #(ActiveEvent ActiveHand ActiveWorld World) do: [:ea | Smalltalk globals removeKey: ea]. Processor allInstancesDo: [:process | #(ActiveHand ActiveEvent ActiveWorld) do: [:ea | process environmentRemoveKey: ea ifAbsent: []]]. { 'ToolBuilder-Morphic' . 'MorphicTests' . 'MorphicExtras' . 'Morphic' } do: [ :package | (MCPackage named: package) unload ].! ! !ProjectNavigationMorph methodsFor: 'stepping and presenter' stamp: 'ct 9/11/2020 20:34'! undoButtonWording "Answer the wording for the Undo button." | wdng | wdng := Project current world commandHistory undoOrRedoMenuWording. (wdng endsWith: ' (z)') ifTrue: [ wdng := wdng copyFrom: 1to: wdng size - 4]. ^ wdng! ! !ProjectNavigationMorph methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:34'! undoOrRedoLastCommand "Undo or redo the last command, as approrpiate." ^ Project current world commandHistory undoOrRedoCommand! ! !EventRecordingSpaceNavigator methodsFor: 'the actions' stamp: 'ct 9/11/2020 19:47'! doNewPainting "Make a new painting" | worldlet | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. worldlet closeNavigatorFlap. worldlet makeNewDrawing: (self currentEvent copy setPosition: worldlet center).! ! !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/11/2020 20:34'! maximumUsableArea ^ self maximumUsableAreaInWorld: Project current world! ! !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:41'! maximumUsableAreaInWorld: aWorldOrNil | allowedArea | allowedArea := Display usableArea. aWorldOrNil ifNotNil: [ Smalltalk isMorphic ifTrue: [ allowedArea := allowedArea intersect: aWorldOrNil visibleClearArea. (((Smalltalk classNamed: 'Flaps') ifNil: [false] ifNotNil: [:cls | cls anyFlapsVisibleIn: aWorldOrNil]) and: [self respondsTo: #reduceByFlaps:]) ifTrue: [allowedArea := self reduceByFlaps: allowedArea]]]. ^allowedArea! ! !RecordingControls methodsFor: 'private' stamp: 'ct 9/12/2020 14:52'! makeSoundMorph "Hand the user an anonymous-sound object representing the receiver's sound." | m aName | recorder verifyExistenceOfRecordedSound ifFalse: [^ self]. recorder pause. recordingSaved := true. m := AnonymousSoundMorph new. m sound: recorder recordedSound interimName: (aName := 'Unnamed Sound'). m setNameTo: aName. self currentHand attachMorph: m.! ! !ReleaseBuilder class methodsFor: 'scripts - support' stamp: 'ct 9/11/2020 20:33'! setProjectBackground: aFormOrColorOrFillStyle | world | world := Project current world. world fillStyle: aFormOrColorOrFillStyle. MorphicProject defaultFill: world fillStyle. world removeProperty: #hasCustomBackground.! ! !SARInstaller methodsFor: 'client services' stamp: 'ct 9/11/2020 20:33'! fileInMorphsNamed: memberName addToWorld: aBoolean "This will load the Morph (or Morphs) from the given member. Answers a Morph, or a list of Morphs, or nil if no such member or error. If aBoolean is true, also adds them and their models to the World." | member morphOrList | member := self memberNamed: memberName. member ifNil: [^ self errorNoSuchMember: memberName]. self installed: member. morphOrList := member contentStream fileInObjectAndCode. morphOrList ifNil: [^ nil]. aBoolean ifTrue: [Project current world addMorphsAndModel: morphOrList]. ^ morphOrList! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/12/2020 14:52'! addYesNoToHand "Place a test/yes/no complex in the hand of the beloved user" | ms messageNodeMorph aMorph | Preferences universalTiles ifTrue: [ms := MessageSend receiver: true selector: #ifTrue:ifFalse: arguments: {['do nothing']. ['do nothing']}. messageNodeMorph := ms asTilesIn: playerScripted class globalNames: true. self primaryHand attachMorph: messageNodeMorph] ifFalse: [aMorph := CompoundTileMorph new. self currentHand attachMorph: aMorph. aMorph setNamePropertyTo: 'TestTile' translated. aMorph position: self currentHand position. aMorph formerPosition: self currentHand position. self startSteppingSelector: #trackDropZones].! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:32'! dismiss "Dismiss the scriptor, usually nondestructively. Possibly animate the dismissal." | endPoint aForm startPoint topRend | owner ifNil: [^ self]. scriptName ifNil: [^ self delete]. "ad hoc fixup for bkwrd compat" endPoint := self viewerTile ifNotNilDo: [:tile | tile topLeft] ifNil: [owner topRight]. aForm := (topRend := self topRendererOrSelf) imageForm offset: (0 at 0). handWithTile := nil. startPoint := topRend topLeft. topRend topRendererOrSelf delete. (playerScripted isExpendableScript: scriptName) ifTrue: [ ^ playerScripted removeScript: scriptName fromWorld: Project current world]. Project current world displayWorld. aForm slideFrom: startPoint to: endPoint nSteps: 4 delay: 30. "The OLPC Virtual Screen wouldn't notice the last update here." Display forceToScreen: (endPoint extent: aForm extent).! ! !ScriptEditorMorph methodsFor: 'other' stamp: 'ct 9/12/2020 14:52'! offerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer" | aMenu count | self modernize. self currentHand showTemporaryCursor: nil. Preferences eToyFriendly ifTrue: [^ self offerSimplerScriptorMenu]. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addStayUpItem. "NB: the kids version in #offerSimplerScriptorMenu does not deploy the stay-up item" aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: { {'button to fire this script' translatedNoop. #tearOfButtonToFireScript}. {'fires per tick...' translatedNoop. #chooseFrequency}. #- }]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. "aMenu addLine. self addGoldBoxItemsTo: aMenu." aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addTranslatedList: { #-. {'open viewer' translatedNoop. #openObjectsViewer. 'open the viewer of the object to which this script belongs' translatedNoop}. {'detached method pane' translatedNoop. #makeIsolatedCodePane. 'open a little window that shows the Smalltalk code underlying this script.' translatedNoop}. #-. {'destroy this script' translatedNoop. #destroyScript} }. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-menu commands' stamp: 'ct 9/11/2020 20:32'! findObject "Reveal the object bearing the code " playerScripted revealPlayerIn: Project current world.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:52'! handUserTimesRepeatTile "Hand the user a times-repeat tile, presumably to drop in the script" | aMorph | aMorph := TimesRepeatTile new. self currentHand attachMorph: aMorph. aMorph position: self currentHand position.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:53'! offerSimplerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer. This variant is used when eToyFriendly preference is true." | aMenu count | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: #( ('button to fire this script' tearOfButtonToFireScript) -) translatedNoop]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu addTranslatedList: #( - ('open viewer' openObjectsViewer 'open the viewer of the object to which this script belongs') - ('destroy this script' destroyScript)) translatedNoop. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:32'! goldBoxMenu "Answer a graphical menu to be put up in conjunction with the Gold Box" | aBox | aBox := Project current world findA: GoldBoxMenu. aBox ifNil: [aBox := GoldBoxMenu new]. aBox initializeFor: self. ^ aBox! ! !ScriptEncoder methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:31'! init: class notifying: parser super init: class notifying: parser. self referenceObject: Project current world referenceWorld.! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/12/2020 14:53'! offerMenuIn: aStatusViewer "Put up a menu." | aMenu | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu title: player knownName, ' ', selector. aMenu addStayUpItem. (player class instanceCount > 1) ifTrue: [aMenu add: 'propagate status to siblings' translated selector: #assignStatusToAllSiblingsIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'Make the status of this script in all of my sibling instances be the same as the status you see here' translated]. aMenu addLine. aMenu add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: player selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: player selector: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu add: 'open this script''s Scriptor' translated target: player selector: #grabScriptorForSelector:in: argumentList: {selector. aStatusViewer world}. aMenu balloonTextForLastItem: 'Open up the Scriptor for this script' translated. aMenu add: 'open this object''s Viewer' translated target: player selector: #beViewed. aMenu balloonTextForLastItem: 'Open up a Viewer for this object' translated. aMenu addLine. aMenu add: 'more...' translated target: self selector: #offerShiftedMenuIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'The "more..." branch offers you menu items that are less frequently used.' translated. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/11/2020 20:30'! offerShiftedMenuIn: aStatusViewer "Put up the shifted menu" ^ (MenuMorph new defaultTarget: self) title: player knownName, ' ', selector; add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld; balloonTextForLastItem: 'Wherever this object currently is, the "grab" command will rip it out, and place it in your "hand". This is a very drastic step, that can disassemble things that may be very hard to put back together!!' translated; add: 'destroy this script' translated target: player selector: #removeScriptWithSelector: argument: selector; balloonTextForLastItem: 'Caution!! This is irreversibly destructive -- it removes the script from the system.' translated; addLine; add: 'inspect morph' translated target: player costume selector: #inspect; add: 'inspect player' translated target: player selector: #inspect; popUpInWorld: self currentWorld! ! !ScriptNameType methodsFor: 'queries' stamp: 'ct 9/11/2020 20:29'! choices "Answer an alphabetized list of known script selectors in the current project" ^ Project current world presenter allKnownUnaryScriptSelectors ! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:29'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock "Answer a MethodNode for the argument, sourceStream, that is the root of a parse tree. Parsing is done with respect to the argument, class, to find instance, class, and pool variables; and with respect to the argument, ctxt, to find temporary variables. Errors in parsing are reported to the argument, req, if not nil; otherwise aBlock is evaluated. The argument noPattern is a Boolean that is true if the the sourceStream does not contain a method header (i.e., for DoIts)." "Copied from superclass, use ScriptEncoder and give it a referenceWorld. This assumes worldLoading has been set to the right world this player belongs to. --bf 5/4/2010" | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: Project current world referenceWorld )] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:28'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock for: anInstance | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: (anInstance costume ifNotNil: [anInstance costume referenceWorld] ifNil: [Project current world]))] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !SearchTopic methodsFor: 'private' stamp: 'ct 9/11/2020 20:28'! triggerUpdateContents self mutex critical: [ updatePending == true ifFalse: [ updatePending := true. Project current addDeferredUIMessage: [Project current world addAlarm: #updateContents withArguments: #() for: self at: Time millisecondClockValue + 250]]].! ! !SelectionMorph methodsFor: 'halo commands' stamp: 'ct 9/11/2020 20:28'! duplicate "Make a duplicate of the receiver and havbe the hand grab it" selectedItems := self duplicateMorphCollection: selectedItems. selectedItems reverseDo: [:m | (owner ifNil: [self currentWorld]) addMorph: m]. dupLoc := self position. self currentHand grabMorph: self. self currentWorld presenter flushPlayerListCache.! ! !SimpleHierarchicalListMorph methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:28'! specialKeyPressed: asciiValue (self arrowKey: asciiValue) ifTrue: [^ true]. asciiValue = 27 "escape" ifTrue: [ self currentEvent shiftPressed ifTrue: [self currentWorld putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]. ^ true]. ^ false! ! !SimpleSelectionMorph methodsFor: 'extending' stamp: 'ct 9/11/2020 20:27'! extendByHand: aHand "Assumes selection has just been created and added to some pasteUp or world" | startPoint handle m inner | startPoint := Sensor cursorPoint. handle := NewHandleMorph new followHand: aHand forEachPointDo: [:newPoint | | localPt | Cursor crossHair show. localPt := (self transformFrom: self world) globalPointToLocal: newPoint. self bounds: (startPoint rect: localPt)] lastPointDo: [:newPoint | inner := self bounds insetBy: 2 at 2. inner area >= 16 ifTrue: [m := SketchMorph new form: (Form fromDisplay: inner). aHand attachMorph: m. self currentWorld fullRepaintNeeded] "selection tracking can leave unwanted artifacts" ifFalse: [Beeper beep]. "throw minnows back" self delete]. handle visible: false. aHand attachMorph: handle. handle startStepping! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! cancelOutOfPainting "The user requested to back out of a painting session without saving" self deleteSelfAndSubordinates. emptyPicBlock ifNotNil: [emptyPicBlock value]. "note no args to block!!" hostView ifNotNil: [hostView changed]. Project current world resumeScriptsPausedByPainting. ^ nil! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! deliverPainting: result evt: evt "Done painting. May come from resume, or from original call. Execute user's post painting instructions in the block. Always use this standard one. 4/21/97 tk" | newBox newForm ans | palette ifNotNil: "nil happens" [palette setAction: #paint: evt: evt]. "Get out of odd modes" "rot := palette getRotations." "rotate with heading, or turn to and fro" "palette setRotation: #normal." result == #cancel ifTrue: [ ans := UIManager default chooseFrom: { 'throw it away' translated. 'keep painting it' translated. } title: 'Do you really want to throw away what you just painted?' translated. ^ ans = 1 ifTrue: [self cancelOutOfPainting] ifFalse: [nil]]. "cancelled out of cancelling." "hostView rotationStyle: rot." "rotate with heading, or turn to and fro" newBox := paintingForm rectangleEnclosingPixelsNotOfColor: Color transparent. registrationPoint ifNotNil: [registrationPoint := registrationPoint - newBox origin]. "relative to newForm origin" newForm := Form extent: newBox extent depth: paintingForm depth. newForm copyBits: newBox from: paintingForm at: 0 at 0 clippingBox: newForm boundingBox rule: Form over fillColor: nil. newForm isAllWhite ifTrue: [ (self valueOfProperty: #background) == true ifFalse: [^ self cancelOutOfPainting]]. newForm fixAlpha. "so alpha channel stays intact for 32bpp" self delete. "so won't find me again" dimForm ifNotNil: [dimForm delete]. newPicBlock value: newForm value: (newBox copy translateBy: bounds origin). Project current world resumeScriptsPausedByPainting.! ! !SketchMorph methodsFor: 'menus' stamp: 'ct 9/11/2020 20:27'! collapse "Replace the receiver with a collapsed rendition of itself." | w collapsedVersion a ht | (w := self world) ifNil: [^ self]. collapsedVersion := (self imageForm scaledToSize: 50 at 50) asMorph. collapsedVersion setProperty: #uncollapsedMorph toValue: self. collapsedVersion on: #mouseUp send: #uncollapseSketch to: collapsedVersion. collapsedVersion setBalloonText: ('A collapsed version of {1}. Click to open it back up.' translated format: {self externalName}). self delete. w addMorphFront: ( a := AlignmentMorph newRow hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4; borderColor: Color white; addMorph: collapsedVersion; yourself). a setNameTo: self externalName. ht := (Smalltalk at: #SugarNavTab ifPresent: [:c | Project current world findA: c]) ifNotNil: [:tab | tab height] ifNil: [80]. a position: 0 at ht. collapsedVersion setProperty: #collapsedMorphCarrier toValue: a. (self valueOfProperty: #collapsedPosition) ifNotNil: [:priorPosition | a position: priorPosition].! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-event handling' stamp: 'ct 9/12/2020 14:39'! deleteBoxHit "The delete box was hit..." self currentHand showTemporaryCursor: nil. self delete.! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:39'! openPropertySheet "Delete the receiver and open a property sheet on my target instead." self currentHand showTemporaryCursor: nil. target openAppropriatePropertySheet. self delete.! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer cardNum: cardNum "Call once to search a card of the stack. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [self currentPage allStringsAfter: oldContainer text]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findWordStart: searchString startingAt: start) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" "wasIn := (pages at: pageNum) isInMemory." self goToCardNumber: cardNum "wasIn ifFalse: ['search again, on the real current text. Know page is in.'. ^ self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) recompute it startAt: startIndex container: oldContainer pageNum: pageNum]"]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findViaTemplate | list pl cardInst | "Current card is the template. Only search cards in this background. Look at cards directly (not allText). Key must be found in the same field as in the template. HyperCard style (multiple starts of words). Put results in a list, outside the stack." list := self templateMatches. list isEmpty ifTrue: [^ self inform: 'No matches were found. Be sure the current card is mostly blank and only has text you want to match.' translated]. "put up a PluggableListMorph" cardInst := self currentCard. cardInst matchIndex: 0. "establish entries" cardInst results at: 1 put: list. self currentPage setProperty: #myStack toValue: self. "way to get back" pl := PluggableListMorph new on: cardInst list: #matchNames selected: #matchIndex changeSelected: #matchIndex: menu: nil "#matchMenu:shifted:" keystroke: nil. self currentHand attachMorph: (self formatList: pl). ! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! assureFlapOfLabel: aTitle withContents: aString "Answer an info flap with the given title and contents. If one exists in the project, use that, else create one & insert it in the world. Answer the flap tab." | allFlapTabs aTab | allFlapTabs := Project current world localFlapTabs, Project current world extantGlobalFlapTabs. aTab := allFlapTabs detect: [:ft | ft flapID = aTitle] ifNone: [nil]. aTab ifNotNil: [^ aTab]. "already present" aTab := self openInfoFlapWithLabel: aTitle helpContents: aString edge: #left. aTab bottom: Project current world bottom. self cleanUpFlapTabsOnLeft. aTab hideFlap. aTab referent show. aTab show. ^ aTab " ScriptingSystem assureFlapOfLabel: 'Egg Sample' withContents: EventRollMorph basicNew helpString "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! cleanUpFlapTabsOnLeft "Make sure the flap tabs on the left of the screen line up nicely, making best use of realestate." | tabsOnLeft current | tabsOnLeft := ((Project current world localFlapTabs, Project current world extantGlobalFlapTabs) select: [:f | f edgeToAdhereTo = #left]) sort: [:a :b | a top <= b top]. current := SugarNavigatorBar showSugarNavigator ifTrue: [75] ifFalse: [0]. tabsOnLeft do: [:aTab | aTab top: (current min: Project current world height - aTab height). current := aTab bottom + 2]. " ScriptingSystem cleanUpFlapTabsOnLeft "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:25'! openInfoFlapWithLabel: aTitle helpContents: aString edge: anEdge "Open an info flap with the given label, contents, and edge" | aPlug outer leftStrip rightStrip titleRow aDismissButton aFlapTab | Preferences enable: #scrollBarsOnRight. Preferences enable: #inboardScrollbars. aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab referentMargin: 0 @ Project current world sugarAllowance. outer := HelpFlap newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #morphicLayerNumber toValue: 26. leftStrip := Morph new beTransparent. leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip beTransparent. rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. outer addMorphBack: rightStrip. outer clipSubmorphs: true. titleRow := AlignmentMorph newRow. titleRow borderColor: Color veryVeryLightGray; borderWidth: 1. titleRow hResizing: #spaceFill; vResizing: #shrinkWrap. titleRow beTransparent. aDismissButton := aFlapTab tanOButton. aDismissButton actionSelector: #dismissViaHalo. titleRow addMorphFront: aDismissButton. titleRow addTransparentSpacerOfSize: 8 @ 0. titleRow addMorphBack: (StringMorph contents: aTitle font: Preferences standardEToysTitleFont). rightStrip addMorph: titleRow. aPlug := PluggableTextMorph new. aPlug width: 540. aPlug setText: aString. aPlug textMorph beAllFont: Preferences standardEToysFont. aPlug retractable: false; scrollBarOnLeft: false. aPlug hScrollBarPolicy: #never. aPlug borderColor: ScriptingSystem borderColor. aPlug setNameTo: aTitle. aPlug hResizing: #spaceFill. aPlug vResizing: #spaceFill. rightStrip addMorphBack: aPlug. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: aTitle edge: anEdge color: (Color r: 0.677 g: 0.935 b: 0.484). aFlapTab submorphs first beAllFont: Preferences standardEToysFont. Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. anEdge == #left ifTrue: [aFlapTab position: (outer left @ outer top). outer extent: (540 @ Project current world height)]. anEdge == #right ifTrue: [aFlapTab position: ((Project current world right - aFlapTab width) @ Project current world top). outer extent: (540 @ Project current world height)]. outer beFlap: true. outer color: Color green veryMuchLighter. aPlug textMorph lock. aFlapTab referent hide. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. Project current world doOneCycle. aPlug width: 540. aPlug setText: aString. "hmm, again" aPlug color: outer color. aPlug borderWidth: 0. aPlug textMorph contents: aString wrappedTo: 520. aFlapTab applyThickness: 560. aFlapTab fitOnScreen. aFlapTab referent show. ^ aFlapTab! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:24'! systemQueryPhraseWithActionString: aString labelled: anotherString "Answer a system-query-phrase with the give action-string and label." ^ Project current world presenter systemQueryPhraseWithActionString: aString labelled: anotherString! ! !StandardViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 20:24'! currentVocabulary "Answer the vocabulary currently associated with the receiver" | aSym aVocab | aSym := self valueOfProperty: #currentVocabularySymbol ifAbsent: [nil]. aSym ifNil: [aVocab := self valueOfProperty: #currentVocabulary ifAbsent: [nil]. aVocab ifNotNil: [aSym := aVocab vocabularyName. self removeProperty: #currentVocabulary. self setProperty: #currentVocabularySymbol toValue: aSym]]. ^ aSym ifNotNil: [Vocabulary vocabularyNamed: aSym] ifNil: [(self world ifNil: [Project current world]) currentVocabularyFor: scriptedPlayer]! ! !SugarLauncher methodsFor: 'commands' stamp: 'ct 9/12/2020 14:07'! viewSource Project current world addDeferredUIMessage: [ Project current world showSourceKeyHit].! ! !SugarNavTab methodsFor: 'positioning' stamp: 'ct 9/11/2020 20:24'! occupyTopRightCorner "Make the receiver be the correct size, and occupy the top-right corner of the screen." | worldBounds toUse | worldBounds := Project current world bounds. " toUse := Preferences useArtificialSweetenerBar ifFalse: [75] ifTrue: [(ActiveWorld extent >= (1200 @ 900)) ifTrue: [75] ifFalse: [40]]." toUse := 40. "Trying for the moment to use the smaller icon always when in this mode." referent height: toUse; resizeButtonsAndTabTo: toUse. self extent: toUse @ toUse. self topRight: worldBounds topRight! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelp " SugarNavigatorBar putUpInitialBalloonHelp " | suppliesButton b1 b2 p b | suppliesButton := paintButton owner submorphs detect: [:e | e isButton and: [e actionSelector = #toggleSupplies]]. b1 := BalloonMorph string: self paintButtonInitialExplanation for: paintButton corner: #topRight force: false. b2 := BalloonMorph string: self suppliesButtonInitialExplanation for: suppliesButton corner: #topLeft force: true. p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. p addMorph: b1. p addMorph: b2. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelpFor: quads "Given a list of quads of the form (see senders for examples), put up initial balloon help for them." " SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((doNewPainting 'make a new painting' topRight false) (toggleSupplies 'open the supplies bin' topLeft true)) SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((showNavBar 'show the tool bar' bottomLeft false) (hideNavBar 'hide the tool bar' bottomLeft false)) " | b1 p b | p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. quads do: [:aQuad | (submorphs first submorphs detect: [:e | e isButton and: [e actionSelector = aQuad first]] ifNone: [nil]) ifNotNil: [:aButton | b1 := BalloonMorph string: aQuad second for: aButton corner: aQuad third force: aQuad fourth. p addMorph: b1]]. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'help flap' stamp: 'ct 9/12/2020 14:37'! buildAndOpenHelpFlap "Called only when flaps are being created afresh." | aFlapTab outer leftStrip rightStrip aGuide | aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab setProperty: #rigidThickness toValue: true. outer := AlignmentMorph newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #wantsHaloFromClick toValue: false. leftStrip := Morph new beTransparent. "This provides space for tabs to be seen." leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip color: (Color green veryMuchLighter alpha: 0.2). rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. rightStrip setProperty: #wantsHaloFromClick toValue: false. outer addMorphBack: rightStrip. outer clipSubmorphs: true. aGuide := QuickGuideMorph new. aGuide initializeIndexPage. " aGuide order: QuickGuideMorph defaultOrder. " QuickGuideMorph loadIndexAndPeekOnDisk. aGuide loadPages. rightStrip addMorphBack: aGuide. aGuide beSticky. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: 'Help' translated edge: #left color: (Color r: 0.677 g: 0.935 b: 0.484). Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. aFlapTab position: outer left @ outer top. outer extent: 462 @ Project current world height. outer beFlap: true. outer beTransparent. aFlapTab referent hide. aFlapTab referentMargin: 0 at self height. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. aFlapTab applyThickness: 462. aFlapTab fitOnScreen. aFlapTab referent show. aFlapTab show. aFlapTab makeFlapCompact: true. aFlapTab setToPopOutOnDragOver: false. Flaps addGlobalFlap: aFlapTab. Project current world addGlobalFlaps. ScriptingSystem cleanUpFlapTabsOnLeft! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/12/2020 14:53'! gotoAnother EToyProjectHistoryMorph new position: self currentHand position; openInWorld ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! makeProjectNameLabel | t | projectNameField := SugarRoundedField new. t := UpdatingStringMorph new. t setProperty: #okToTextEdit toValue: true. t putSelector: #projectNameChanged:. t getSelector: #projectName. projectNameField backgroundColor: self color. t target: self. t useStringFormat. t beSticky. t label: Project current name font: (StrikeFont familyName: 'BitstreamVeraSans' size: 24). t color: Color black. t width: projectNameField width - 10. projectNameField label: t. projectNameField setBalloonText: self projectNameFieldBalloonHelp. projectNameField on: #mouseDown send: #mouseDown: to: t. projectNameField on: #mouseUp send: #mouseUp: to: t. self resizeProjectNameField. ^projectNameField.! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectName ^ Project current name ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectNameChanged: aString Project current renameTo: aString. ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! shareMenu | menu item ext | menu := MenuMorph new. ext := 200 at 50. #((stopSharing makePrivateLabelIn:) (startSharing makeMyNeighborhoodLabelIn:) "(shareThisWorld makeBadgeLabelIn:)") do: [:pair | item := MenuItemMorph new contents: ''; target: self; selector: pair first; arguments: #(). item color: Color black. item addMorph: (self perform: pair second with: ext). item setProperty: #minHeight toValue: ext y. item fitContents. item extent: ext. item setProperty: #selectionFillStyle toValue: (Color gray alpha: 0.5). menu addMorphBack: item. ]. menu color: Color black. menu borderColor: Color white. ^ menu invokeModalAt: shareButton position + (10 at 20) in: Project current world allowKeyboard: false.! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/12/2020 14:37'! startNebraska | nebraska | Project current world remoteServer: nil. Project current world submorphs do: [:e | (e isMemberOf: NebraskaServerMorph) ifTrue: [e delete]]. nebraska := NebraskaServerMorph serveWorld. SugarLauncher current offerStreamTube: 'sqk-nebraska' inBackgroundOnPort: [nebraska listeningPort]. ! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! startP2P listener ifNotNil: [listener stopListening]. listener ifNil: [listener := SugarListenerMorph new]. listener position: -200@ -200. Project current world addMorphBack: listener. listener startListening. SugarLauncher current offerStreamTube: 'sqk-etoy-p2p' inBackgroundOnPort: [listener listeningPort].! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! stopSharing SugarLauncher current leaveSharedActivity. listener ifNotNil: [listener stopListening. listener := nil]. Project current world remoteServer: nil. Project current world submorphs do: [:ea | (ea isMemberOf: NebraskaServerMorph) ifTrue: [ea delete]]. self sharingChanged.! ! !SugarNavigatorBar methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:22'! undoButtonAppearance | wording | undoButton ifNotNil: [ Project current world commandHistory undoEnabled ifTrue: [undoButton enabled] ifFalse: [undoButton disabled]. wording := self undoButtonWording. undoButton setBalloonText: wording. ]. ! ! !InteriorSugarNavBar methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:12'! doNewPainting "Make a new painting" | worldlet aRect | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. aRect := (worldlet topLeft + (0 @ self height)) corner: worldlet bottomRight. worldlet makeNewDrawing: (self currentEvent copy setPosition: aRect center).! ! !SugarNavigatorBar class methodsFor: 'utilitity' stamp: 'ct 9/11/2020 20:22'! findAnythingMorph ^ FileList2 morphicViewProjectLoader2InWorld: Project current world title: 'Find...' translated reallyLoad: true dirFilterType: #initialDirectoryList isGeneral: true.! ! !SugarRoundedField methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:22'! resizeLabel | small | (label notNil and: [label hasFocus not]) ifTrue: [ label width: self width - 10. small :=self height < 45. label label: Project current world project name font: (StrikeFont familyName: 'BitstreamVeraSans' size: (small ifTrue: [15] ifFalse: [24])). label center: self center. label left: self left + 10. self addMorph: label. ]. ! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerTilesMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'me, by name' target: self selector: #attachTileForCode:nodeType: argumentList: {''. aReceiver}. menu add: 'self' target: self selector: #attachTileForCode:nodeType: argumentList: {'self'. VariableNode}. menu add: '_ (assignment)' target: self selector: #attachTileForCode:nodeType: argumentList: {''. nil}. menu add: '"a Comment"' target: self selector: #attachTileForCode:nodeType: argumentList: {'"a comment"\' withCRs. CommentNode}. menu submorphs last color: Color blue. menu add: 'a Number' target: self selector: #attachTileForCode:nodeType: argumentList: {'5'. LiteralNode}. menu add: 'a Character' target: self selector: #attachTileForCode:nodeType: argumentList: {'$z'. LiteralNode}. menu add: '''abc''' target: self selector: #attachTileForCode:nodeType: argumentList: {'''abc'''. LiteralNode}. menu add: 'a Symbol constant' target: self selector: #attachTileForCode:nodeType: argumentList: {'#next'. LiteralNode}. menu add: 'true' target: self selector: #attachTileForCode:nodeType: argumentList: {'true'. VariableNode}. menu add: 'a Test' target: self selector: #attachTileForCode:nodeType: argumentList: {'true ifTrue: [self] ifFalse: [self]'. MessageNode}. menu add: 'a Loop' target: self selector: #attachTileForCode:nodeType: argumentList: {'1 to: 10 do: [:index | self]'. MessageNode}. menu add: 'a Block' target: self selector: #attachTileForCode:nodeType: argumentList: {'[self]'. BlockNode}. menu add: 'a Class or Global' target: self selector: #attachTileForCode:nodeType: argumentList: {'Character'. LiteralVariableNode}. menu add: 'a Reply' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. ReturnNode}. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerVarsMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu instVarList cls | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'new temp variable' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. TempVariableNode}. instVarList := OrderedCollection new. cls := aReceiver class. [instVarList addAllFirst: cls instVarNames. cls == aLexiconModel limitClass] whileFalse: [cls := cls superclass]. instVarList do: [:nn | menu add: nn target: self selector: #instVarTile: argument: nn]. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! attachToHand "Adjust my look and attach me to the hand" self roundedCorners. self currentHand attachMorph: self. Preferences tileTranslucentDrag ifTrue: [self lookTranslucent. self align: self center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ self align: self topLeft with: self currentHand position + self cursorBaseOffset].! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! instVarTile: aName "Make and put into hand a tile for an instance variable" | sm | sm := ((VariableNode new name: aName index: 1 type: 1 "LdInstType") asMorphicSyntaxIn: SyntaxMorph new). sm roundedCorners. self currentHand attachMorph: sm. Preferences tileTranslucentDrag ifTrue: [sm lookTranslucent. sm align: sm center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ sm align: sm topLeft with: self currentHand position + self cursorBaseOffset]! ! !SyntaxMorph methodsFor: 'scripting' stamp: 'ct 9/12/2020 14:55'! tearOffTile "For a SyntaxMorph, this means give a copy of me" | dup | dup := self duplicate. self currentHand attachMorph: dup. ^ Preferences tileTranslucentDrag ifTrue: [dup lookTranslucent] ifFalse: [dup align: dup topLeft with: self currentHand position + self cursorBaseOffset]! ! !SystemWindow methodsFor: 'events' stamp: 'ct 9/11/2020 20:22'! doFastFrameDrag: grabPoint "Do fast frame dragging from the given point" | offset newBounds outerWorldBounds clearArea | outerWorldBounds := self boundsIn: nil. offset := outerWorldBounds origin - grabPoint. clearArea := Project current world clearArea. newBounds := outerWorldBounds newRectFrom: [:f | | p selector | p := Sensor cursorPoint. (self class dragToEdges and: [(selector := self dragToEdgesSelectorFor: p in: clearArea) notNil]) ifTrue: [clearArea perform: selector] ifFalse: [p + offset extent: outerWorldBounds extent]]. self bounds: newBounds; comeToFront! ! !CollapsedMorph methodsFor: 'collapse/expand' stamp: 'ct 9/12/2020 14:39'! uncollapseToHand "Hand the uncollapsedMorph to the user, placing it in her hand, after remembering appropriate state for possible future use" | nakedMorph | nakedMorph := uncollapsedMorph. uncollapsedMorph := nil. nakedMorph setProperty: #collapsedPosition toValue: self position. mustNotClose := false. "so the delete will succeed" self delete. self currentHand attachMorph: nakedMorph.! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 18:01'! rotateWindows "Rotate the z-ordering of the windows." self currentEvent shiftPressed ifTrue: [self sendTopWindowBackOne] ifFalse: [self sendTopWindowToBack].! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 20:21'! sendTopWindowBackOne "Rotate the window-list one downward, i.e., make the bottommost one be the active one, pushing the receiver to next-to-topmost." | dows | dows := Project current world submorphs select: [:m | m isSystemWindow]. dows ifNotEmpty: [dows last expand; comeToFront]! ! !TextEditor methodsFor: 'menu commands' stamp: 'ct 9/11/2020 18:02'! offerMenuFromEsc: aKeyboardEvent "The escape key was hit while the receiver has the keyboard focus; take action." self currentEvent shiftPressed ifFalse: [ self raiseContextMenu: aKeyboardEvent]. ^ true! ! !ThreePhaseButtonMorph methodsFor: 'button' stamp: 'ct 9/11/2020 18:02'! doButtonAction "Perform the action of this button. Subclasses may override this method. The default behavior is to send the button's actionSelector to its target object with its arguments." | args | (target notNil and: [actionSelector notNil]) ifTrue: [ args := actionSelector numArgs > arguments size ifTrue: [arguments copyWith: self currentEvent] ifFalse: [arguments]. Cursor normal showWhile: [ target perform: actionSelector withArguments: args]. target isMorph ifTrue: [target changed]].! ! !TileMorph methodsFor: 'arrows' stamp: 'ct 9/11/2020 18:02'! showSuffixChoices "The suffix arrow has been hit, so respond appropriately" | plusPhrase phrase pad outer num | self currentEvent shiftPressed ifTrue: [^ self wrapPhraseInFunction]. (phrase := self ownerThatIsA: PhraseTileMorph orA: FunctionTile) ifNil: [nil]. (type == #literal) & (literal isNumber) ifTrue: ["Tile is a constant number" (phrase isNil or: [phrase finalTilePadSubmorph == owner]) "pad" ifTrue: ["we are adding the first time (at end of our phrase)" plusPhrase := self phraseForOp: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). owner acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. num := plusPhrase firstSubmorph firstSubmorph. num deleteSuffixArrow]]. (#(function expression parameter) includes: type) ifTrue: [pad := self ownerThatIsA: TilePadMorph. plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: self. pad topEditor scriptEdited "recompile"]. type = #operator ifTrue: ["Tile is accessor of an expression" phrase resultType == #Number ifTrue: [outer := phrase ownerThatIsA: PhraseTileMorph orA: TimesRepeatTile. pad := self ownerThatIsA: TilePadMorph. outer ifNotNil: [(outer lastSubmorph == pad or: [true]) ifTrue: [ "first time" plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: phrase. "car's heading" pad topEditor scriptEdited "recompile & deal with carets"]]]]. (self topEditor ifNil: [phrase ifNil: [^ self]]) enforceTileColorPolicy! ! !TileMorph methodsFor: 'code generation' stamp: 'ct 9/11/2020 20:21'! acceptNewLiteral "Tell the scriptEditor who I belong to that I have a new literal value." | topScript | topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript installWithNewLiteral]. (self ownerThatIsA: ViewerLine) ifNotNil: [:aLine | (self ownerThatIsA: PhraseTileMorph) ifNotNil: [aLine removeHighlightFeedback. self layoutChanged. Project current world doOneSubCycle. aLine addCommandFeedback: nil]]! ! !TileMorph methodsFor: 'misc' stamp: 'ct 9/12/2020 14:56'! handReferentMorph "Hand the user the actual morph referred to" | aMorph surrogate | ((aMorph := actualObject costume) isMorph and: [aMorph isWorldMorph not]) ifTrue: [ surrogate := CollapsedMorph collapsedMorphOrNilFor: aMorph. surrogate ifNotNil: [surrogate uncollapseToHand] ifNil: [self currentHand attachMorph: aMorph]].! ! !SymbolListTile methodsFor: 'user interface' stamp: 'ct 9/11/2020 20:22'! choices "Answer the list of current choices for the receiver's symbol" dataType == #ScriptName ifTrue: "Backward compatibility with old tiles" [^ Project current world presenter allKnownUnaryScriptSelectors]. ^ choices! ! !ScriptNameTile methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:29'! choices "Answer the current list of choices" ^ Project current world presenter allKnownUnaryScriptSelectors! ! !TileMorph class methodsFor: '*Etoys-Squeakland-utilities' stamp: 'ct 9/11/2020 20:21'! implicitSelfInTilesChanged "The implicitSelfInTiles preference changed. Caution: although this may appear to have no senders in the image, it is in fact invoked when the implicitSelfInTiles preference is toggled... so please do not delete it." Smalltalk isMorphic ifFalse: [^ self]. Project current world allScriptEditorsInProject do: [:scriptEditor | scriptEditor install]. Project current world allViewersInProject do: [:viewer | viewer enforceImplicitSelf]. " (Preferences buttonForPreference: #implicitSelfInTiles) openInHand. "! ! !TileMorphTest methodsFor: 'testing' stamp: 'ct 9/12/2020 14:56'! testAssignmentTile "self debug: #testAssignmentTile" | player viewer tile phrase | player := Morph new assuredPlayer. viewer := CategoryViewer new invisiblySetPlayer: player. viewer makeSetter: #(#getX #Number) event: nil from: player costume. phrase := self currentHand firstSubmorph. self currentHand removeAllMorphs. tile := phrase submorphs second. self assert: tile codeString = 'setX: '. tile arrowAction: 1. self assert: tile codeString = 'setX: self getX + '.! ! !TypeListTile methodsFor: 'mouse handling' stamp: 'ct 9/12/2020 14:56'! showOptions | topScript | suffixArrow ifNotNil: [(suffixArrow bounds containsPoint: self currentHand cursorPoint) ifTrue: [^ super showOptions]]. topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript handUserParameterTile]! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice at: aPointOrNil "UserDialogBoxMorph confirm: 'Make your choice carefully' withCRs title: 'Do you like chocolate?' trueChoice: 'Oh yessir!!' falseChoice: 'Not so much...'" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: 1; registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponse! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice default: default triggerAfter: seconds at: aPointOrNil "UserDialogBoxMorph confirm: 'I like hot java' title: 'What do you say?' trueChoice: 'You bet!!' falseChoice: 'Nope' default: false triggerAfter: 12 at: 121 at 212" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: (default ifTrue: [1] ifFalse: [2]); registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponseAfter: seconds! ! !UserText methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:56'! keyStroke: evt "Handle a keystroke event." | newSel | super keyStroke: evt. evt hand keyboardFocus == self ifFalse: [self releaseEditor. ^ self]. newSel := self editor selectionInterval. "restore editor state" self refreshParagraph. self editor selectFrom: newSel first to: newSel last. wrapFlag ifFalse: [self fullBounds right > owner right ifTrue: [self wrapFlag: true. self right: owner right. self refreshParagraph. self editor selectFrom: text string size + 1 to: text string size]].! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs fourth topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 0)). aMorph useRoundedCorners; beTransparent; borderWidth: 2; borderColor: (Color r: 1.0 g: 0.548 b: 0.452); lock. aMorph setProperty: #highlight toValue: true. ^ Project current world addMorphFront: aMorph! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addGetterFeedback "Add feedback during mouseover of a getter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: (self firstAlignmentMorph ifNil: [self submorphs last bottomRight] ifNotNil: [:m | m bottomLeft])). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem getterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil. " Color fromUser (Color r: 1.0 g: 0.355 b: 0.839) "! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addSetterFeedback "Add screen feedback showing what would be torn off to make a setter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: self bounds bottomRight). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem setterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !ViewerLine methodsFor: '*Etoys-Squeakland-slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs third topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !Vocabulary class methodsFor: '*Etoys-Squeakland-type vocabularies' stamp: 'ct 9/11/2020 20:19'! typeChoicesForUserVariables "Answer a list of all user-choosable value types for variables." | aList | aList := #(Boolean Color CustomEvents Graphic Number Patch Player Point ScriptName Sound String) copy. self currentWorld isKedamaPresent ifFalse: [ ^ aList copyWithout: #Patch]. ^ aList " Vocabulary typeChoicesForUserVariables "! ! !WorldState methodsFor: 'hands' stamp: 'ct 9/24/2020 13:50'! primaryHand ^ ActiveHand value ifNil: [self hands at: 1 ifAbsent: [nil]]! ! !WorldState methodsFor: 'hands' stamp: 'ct 9/24/2020 13:50'! removeHand: aHandMorph "Remove the given hand from the list of hands for this world." (hands includes: aHandMorph) ifFalse: [^ self]. self currentHand == aHandMorph ifTrue: [self halt: 'ct: Cannot remove the active hand because it is a dynamic variable. What should we do now? Panic!!!!1']. hands := hands copyWithout: aHandMorph.! ! !WorldState methodsFor: 'stepping' stamp: 'ct 9/16/2020 18:27'! runLocalStepMethodsIn: aWorld "Run morph 'step' methods (LOCAL TO THIS WORLD) whose time has come. Purge any morphs that are no longer in this world. ar 3/13/1999: Remove buggy morphs from the step list so that they don't raise repeated errors." | now morphToStep stepTime | now := Time millisecondClockValue. aWorld becomeActiveDuring: [ self triggerAlarmsBefore: now. stepList ifEmpty: [^ self]. (now < lastStepTime or: [now - lastStepTime > 5000]) ifTrue: [ self adjustWakeupTimes: now]. "clock slipped" [stepList notEmpty and: [stepList first scheduledTime < now]] whileTrue: [ lastStepMessage := stepList removeFirst. morphToStep := lastStepMessage receiver. (morphToStep shouldGetStepsFrom: aWorld) ifTrue: [ lastStepMessage value: now. lastStepMessage ifNotNil: [ stepTime := lastStepMessage stepTime ifNil: [morphToStep stepTime]. lastStepMessage scheduledTime: now + (stepTime max: 1). stepList add: lastStepMessage]]. lastStepMessage := nil]. lastStepTime := now].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/16/2020 18:44'! doOneCycleNowFor: aWorld "Immediately do one cycle of the interaction loop. This should not be called directly, but only via doOneCycleFor:" | capturingGesture | DisplayScreen checkForNewScreenSize. capturingGesture := false. "self flag: #bob. " "need to consider remote hands in lower worlds" "process user input events" LastCycleTime := Time millisecondClockValue. self handsDo: [:hand | hand becomeActiveDuring: [ hand processEvents. capturingGesture := capturingGesture or: [hand isCapturingGesturePoints]]]. "The gesture recognizer needs enough points to be accurate. Therefore morph stepping is disabled while capturing points for the recognizer" capturingGesture ifFalse: [ aWorld runStepMethods. "there are currently some variations here" self displayWorldSafely: aWorld].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/16/2020 18:47'! doOneSubCycleFor: aWorld self flag: #deprecate. "ct: Historically, global state was preserved here. Since the introduction of ActiveHandVariable, this is no longer necessary, so this is equivalent to #doOneCycleFor:. However, let's keep this possibly valuable hook for now." ^ self doOneCycleFor: aWorld! ! !WorldState methodsFor: '*MorphicExtras-update cycle' stamp: 'ct 9/12/2020 15:18'! doOneCycleInBackground "Do one cycle of the interactive loop. This method is called repeatedly when this world is not the active window but is running in the background." self halt. "not ready for prime time" "process user input events, but only for remote hands" self handsDo: [:hand | (hand isKindOf: RemoteHandMorph) ifTrue: [ hand becomeActiveDuring: [ hand processEvents]]]. self runStepMethods. self displayWorldSafely.! ! !ZASMCameraMarkMorph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:02'! setTransition "Set the transition" ^ self setTransition: self currentEvent! ! WorldState removeSelector: #activeHand! WorldState removeSelector: #activeHand:! !MorphicProject reorganize! ('display' invalidate noDisplayDuring: previewImageForm restore viewLocFor:) ('initialize' initMorphic initialize installPasteUpAsWorld: openProject: setWorldBackground:) ('testing' isMorphic) ('menu messages' assureNavigatorPresenceMatchesPreference makeThumbnail) ('utilities' addItem:toMenu:selection:color:thumbnail: composeDisplayTextIntoForm: createViewIfAppropriate do:withProgressInfoOn:label: findAFolderForProject:label: findProjectView: jumpToProject offerMenu:from:shifted: pointerMoved setAsBackground: showImage:named: textWindows) ('flaps support' assureFlapIntegrity cleanseDisabledGlobalFlapIDsList enableDisableGlobalFlap: flapsSuppressed: globalFlapEnabledString: globalFlapWithIDEnabledString: isFlapEnabled: isFlapIDEnabled: setFlaps suppressFlapsString toggleFlapsSuppressed) ('docking bars support' assureMainDockingBarPresenceMatchesPreference createOrUpdateMainDockingBar dockingBar dockingBar: removeMainDockingBar showWorldMainDockingBar showWorldMainDockingBar: showWorldMainDockingBarString toggleShowWorldMainDockingBar) ('file in/out' acceptProjectDetails: armsLengthCommand:withDescription: compressFilesIn:to:in: compressFilesIn:to:in:resources: exportSegmentInSexpWithChangeSet:fileName:directory:withoutInteraction: exportSegmentWithChangeSet:fileName:directory: exportSegmentWithChangeSet:fileName:directory:withoutInteraction: loadFromServer: noteManifestDetailsIn: storeSegment storeSegmentNoFile) ('enter' clearGlobalState enterAsActiveSubprojectWithin: finalEnterActions: finalExitActions: initializeMenus isIncompletelyLoaded resumeEventRecorder: scheduleProcessForEnter startUpActions suspendProcessForDebug terminateProcessForLeave wakeUpTopWindow) ('squeaklet on server' enterIfThereOrFind: openBlankProjectNamed:) ('release' deletingProject: myPlayerClasses prepareForDelete) ('language' chooseNaturalLanguage updateLocaleDependents) ('*Etoys-stack' currentStack currentStack:) ('editors' bitEdit: bitEdit:at:scale: editCharacter:ofFont: formEdit: formViewClass openImage:name:saveResource:) ('futures' future:do:at:args: future:send:at:args:) ('protocols' currentVocabulary) ('active process' spawnNewProcess spawnNewProcessAndTerminateOld: spawnNewProcessIfThisIsUI: uiProcess uiProcess:) ('transcripter' displayTranscripter: initializeParagraphForTranscripter:) ('*46Deprecated') ('*51Deprecated') ('subprojects' addProject: subProjects) ('accessing' color) ('updating' applyUserInterfaceTheme canApplyUserInterfaceTheme) ('*Etoys-Worlds' setWorld:) ('scheduling & debugging' addDeferredUIMessage: debuggerClass fatalDrawingError: interruptCleanUpFor: recursiveError: restoreDisplay syntaxError:) ! !Project reorganize! ('accessing' changeSet color displayDepth displayDepth: findProjectView: forgetExistingURL labelString lastDirectory: lastSavedAtSeconds name nameAdjustedForDepth nextProject previousProject projectChangeSet renameTo: storeNewPrimaryURL: thumbnail thumbnail: transcript uiManager urlList viewSize viewSize: world) ('active process' depth spawnNewProcessIfThisIsUI: uiProcess) ('displaying' displayDepthChanged displaySizeChanged displayZoom: imageForm imageFormOfSize:depth: invalidate noDisplayDuring: previewImageForm restore restoreDisplay showZoom shrinkDisplay viewLocFor:) ('file in/out' armsLengthCommand:withDescription: assureIntegerVersion bumpVersion: couldBeSwappedOut currentVersionNumber decideAboutCreatingBlank: doArmsLengthCommand: downloadUrl ensureChangeSetNameUnique exportSegmentFileName:directory: exportSegmentFileName:directory:withoutInteraction: exportSegmentInSexpWithChangeSet:fileName:directory:withoutInteraction: exportSegmentWithCatagories:classes:fileName:directory: exportSegmentWithChangeSet:fileName:directory: findAFolderToLoadProjectFrom findAFolderToStoreProjectIn fromMyServerLoad: hasBadNameForStoring htmlPagePrototype loadFromServer loadFromServer: objectForDataStream: primaryServer primaryServerIfNil: projectExtension restoreReferences revert saveAs saveForRevert serverList squeakletDirectory storeAttributeKey:value:on: storeAttributesOn: storeDataOn: storeHtmlPageIn: storeManifestFileIn: storeOnServer storeOnServerAssumingNameValid storeOnServerInnards storeOnServerShowProgressOn:forgetURL: storeOnServerWithProgressInfo storeOnServerWithProgressInfoOn: storeSegment storeSegmentNoFile storeSomeSegment storeToMakeRoom tryToFindAServerWithMe url urlForLoading versionForFileName versionFrom: versionedFileName writeFileNamed:fromDirectory:toServer: writeStackText:in:registerIn:) ('flaps support' flapsSuppressed flapsSuppressed: showSharedFlaps) ('initialization' initialExtent initialProject initialize installNewDisplay:depth: openProject: setChangeSet: setServer windowActiveOnFirstClick windowReqNewLabel:) ('language' chooseNaturalLanguage localeChanged localeID naturalLanguage updateLocaleDependents) ('menu messages' assureNavigatorPresenceMatchesPreference doWeWantToRename exit fileOut installProjectPreferences makeThumbnail saveProjectPreferences validateProjectNameIfOK:) ('printing' addSubProjectNamesTo:indentation: printOn:) ('project parameters' initializeProjectParameters initializeProjectPreferences noteThatParameter:justChangedTo: parameterAt: parameterAt:ifAbsent: projectParameterAt: projectParameterAt:ifAbsent: projectParameterAt:ifAbsentPut: projectParameterAt:put: projectParameters projectPreferenceAt: projectPreferenceAt:ifAbsent: projectPreferenceFlagDictionary rawParameters removeParameter:) ('release' addDependent: canDiscardEdits close delete deletingProject: forget okToChange prepareForDelete release removeChangeSetIfPossible windowIsClosing) ('resources' abortResourceLoading resourceDirectoryName resourceManager resourceManager: resourceUrl startResourceLoading storeResourceList:in:) ('SuperSwiki' tellAFriend tellAFriend:) ('*sound' beep) ('enter' enter enter: enter:revert:saveForRevert: finalEnterActions: finalExitActions: loadState saveState scheduleProcessForEnter startUpActions terminateProcessForLeave wakeUpTopWindow) ('utilities' addItem:toMenu:selection:color:thumbnail: buildJumpToMenu: composeDisplayTextIntoForm: do:withProgressInfoOn:label: findAFolderForProject:label: jumpToProject jumpToSelection: offerMenu:from:shifted: pointerMoved setAsBackground: showImage:named: textWindows) ('squeaklet on server' enterIfThereOrFind: openBlankProjectNamed:) ('futures' future:do:at:args: future:send:at:args:) ('editors' bitEdit: bitEdit:at:scale: editCharacter:ofFont: formEdit: formViewClass openImage:name:saveResource:) ('protocols' currentVocabulary) ('transcripter' displayTranscripter: initializeParagraphForTranscripter:) ('*51Deprecated') ('sub-projects & hierarchy' addProject: beTopProject children isTopProject liftSubProjects parent setParent: subProjects withChildrenDo:) ('enter - recovery' enterForEmergencyRecovery suspendProcessForDebug) ('testing' isCurrentProject isIncompletelyLoaded isMorphic) ('shrinking' removeAllOtherProjects) ('*ST80-Testing' isMVC) ('*Etoys-Squeakland-file in/out' defaultFolderForAutoSaving latestProjectVersionsFromFileEntries: storeOnServerWithNoInteraction storeOnServerWithNoInteractionInnards storeOnServerWithNoInteractionThenQuit storePngThumbnailIn: version: writeForExportInSexp:withSources:inDirectory:changeSet:) ('*Etoys-Squeakland-displaying' displayExtent) ('*Etoys-Squeakland-menu messages' displayProgressWithJump: displaySavingProgress) ('*Etoys-Squeakland-flaps support' helpGuideIfOpen) ('*Etoys-Squeakland-initialization' installVirtualDisplayIfNeededFor:) ('*Etoys-Squeakland-sugar' keepSugarProperties:monitor: sugarObjectId sugarObjectId: sugarProperties sugarProperties:) ('*Etoys-Squeakland-language' locales localesString toggleUseLocale updateLocaleDependentsGently updateLocaleDependentsWithPreviousSupplies:gently: useLocaleString) ('*Etoys-Squeakland-accessing' name:) ('*Etoys-Squeakland-release' okToChangeSilently) ('*60Deprecated-accessing' setThumbnail: setViewSize:) ('scheduling & debugging' addDeferredUIMessage: debuggerClass interruptCleanUpFor: interruptName: interruptName:message: interruptName:message:preemptedProcess: interruptName:preemptedProcess: primitiveError: recursiveError: syntaxError:) ('*Morphic-Kernel-accessing') ! PasteUpMorph removeSelector: #activeHand:! !Browser reorganize! ('*46Deprecated') ('*60Deprecated-multi-window support' classHierarchy) ('*Etoys-Squeakland-class functions' buildClassBrowser) ('*Etoys-Squeakland-drag and drop' overwriteDialogHierarchyChange:higher:sourceClassName:destinationClassName:methodSelector:) ('*Etoys-Squeakland-initialize-release' browserWindowActivated) ('*Etoys-Squeakland-message functions' buildMessageBrowser) ('*SUnitTools-class list functions' testRunTests) ('*SUnitTools-menus' testsClassListMenu: testsSystemCategoryMenu:) ('*SUnitTools-system category functions' hasSystemCategoryWithTestsSelected testRunTestsCategory) ('*services-base' browseReference: classCategoryMenuServices: classListMenuServices: messageCategoryMenuServices: methodReference optionalButtonRow selectReference:) ('accessing' contents contents:notifying: contentsSelection couldBrowseAnyClass doItReceiver editSelection editSelection: environment newClassContents noteSelectionIndex:for: request:initialAnswer: selectEnvironment: spawn: suggestCategoryToSpawnedBrowser:) ('annotation' annotation annotation:) ('class comment pane' annotationForClassCommentFor: annotationForClassDefinitionFor: noCommentNagString stripNaggingAttributeFromComment:) ('class functions' addAllMethodsToCurrentChangeSet classCommentText classDefinitionText classListMenu: classListMenu:shifted: classListMenuMore: copyClass createInstVarAccessors defineClass:notifying: editClass editComment explainSpecial: fileOutClass findMethod hierarchy makeNewSubclass plusButtonHit printOutClass removeClass renameClass shiftedClassListMenu: shiftedClassListMenuMore:) ('class list' classIconAt: classList classListIndex classListIndex: classListIndexOf: classListSingleton createHierarchyTreeOf: defaultClassList flattenHierarchyTree:on:indent: flattenHierarchyTree:on:indent:by: flattenHierarchyTree:on:indent:by:format: hasClassSelected hierarchicalClassList recent selectClass: selectClassNamed: selectedClass selectedClassName) ('code pane' aboutToStyle: compileMessage:notifying: showBytecodes) ('controls' decorateButtons) ('copying' veryDeepInner:) ('drag and drop' dragFromClassList: dragFromMessageList: dropOnMessageCategories:at: dropOnSystemCategories:at: wantsMessageCategoriesDrop: wantsSystemCategoriesDrop:) ('initialize-release' classListFrame: classListFrame:fromLeft:width: classListFrame:fromTop:fromLeft:width: defaultBrowserTitle frameOffsetFromTop:fromLeft:width:bottomFraction: labelString methodCategoryChanged setClass: setClass:selector: setSelector: switchesFrame: switchesFrame:fromLeft:width: systemCatSingletonKey:from: systemOrganizer: topConstantHeightFrame:fromLeft:width:) ('message category functions' addCategory alphabetizeMessageCategories buildMessageCategoryBrowser buildMessageCategoryBrowserEditString: canShowMultipleMessageCategories categoryOfCurrentMethod changeMessageCategories: editMessageCategories fileOutMessageCategories highlightMessageList:with: mainMessageCategoryMenu: messageCategoryMenu: printOutMessageCategories removeEmptyCategories removeMessageCategory renameCategory showHomeCategory) ('message category list' categorizeAllUncategorizedMethods hasMessageCategorySelected messageCatListSingleton messageCategoryList messageCategoryListIndex messageCategoryListIndex: messageCategoryListKey:from: messageCategoryListSelection rawMessageCategoryList recategorizeMethodSelector: selectMessageCategoryNamed: selectedMessageCategoryName setOriginalCategoryIndexForCurrentMethod toggleCategorySelectionForCurrentMethod) ('message functions' browseAllCommentsForClass defineMessageFrom:notifying: inspectInstances inspectSubInstances mainMessageListMenu: removeMessage removeMessageFromBrowser) ('message list' addExtraShiftedItemsTo: hasMessageSelected lastMessageName messageHelpAt: messageIconAt: messageIconFor: messageIconHelpFor: messageList messageListIndex messageListIndex: messageListIndexOf: messageListMenu:shifted: reformulateList selectMessageNamed: selectedMessage selectedMessageName selectedMessageName: shiftedMessageListMenu:) ('metaclass' classCommentIndicated classDefinitionIndicated classMessagesIndicated classOrMetaClassOrganizer indicateClassMessages indicateInstanceMessages instanceMessagesIndicated metaClassIndicated metaClassIndicated: selectedClassOrMetaClass selectedClassOrMetaClassName setClassDefinition setClassOrganizer) ('multi-window support' arrowKey:from: browseClassHierarchy isHierarchy isPackage multiWindowName multiWindowNameForState: okToClose restoreMultiWindowState: restoreToCategory:className:protocol:selector:mode:meta: saveMultiWindowState) ('pluggable menus - hooks' classListMenuHook:shifted: messageCategoryMenuHook:shifted: messageListMenuHook:shifted: systemCategoryMenuHook:shifted:) ('self-updating' didCodeChangeElsewhere) ('system category functions' addSystemCategory alphabetizeSystemCategories browseAllClasses buildSystemCategoryBrowser buildSystemCategoryBrowserEditString: changeSystemCategories: classNotFound editSystemCategories fileOutSystemCategory findClass mainSystemCategoryMenu: printOutSystemCategory removeSystemCategory renameSystemCategory systemCatSingletonMenu: systemCategoryMenu: updateSystemCategories) ('system category list' hasSystemCategorySelected indexIsOne indexIsOne: selectCategoryForClass: selectSystemCategory: selectedEnvironment selectedSystemCategory selectedSystemCategoryName systemCatListKey:from: systemCategoryList systemCategoryListIndex systemCategoryListIndex: systemCategorySingleton) ('toolbuilder' buildAndOpenCategoryBrowser buildAndOpenCategoryBrowserLabel: buildAndOpenClassBrowserLabel: buildAndOpenFullBrowser buildAndOpenMessageCategoryBrowserLabel: buildCategoryBrowserWith: buildClassListSingletonWith: buildClassListWith: buildDefaultBrowserWith: buildMessageCategoryListWith: buildMessageListCatSingletonWith: buildMessageListWith: buildSwitchesWith: buildSystemCatListSingletonWith: buildSystemCategoryListWith: buildWith: setMultiWindowFor:) ('traits' addSpecialMenu: addTrait defineTrait:notifying: newClass newTrait) ('user interface' addModelItemsToWindowMenu: defaultWindowColor) ('private' spawnOrNavigateTo:) ! !ActiveWorldVariable class reorganize! ('accessing' default value:during:) ! !ActiveHandVariable class reorganize! ('accessing' default value:during:) ! !ActiveEventVariable class reorganize! ('accessing' default value:during:) ! !Object reorganize! ('accessing' at: at:modify: at:put: basicAt: basicAt:put: basicSize bindWithTemp: enclosedSetElement ifNil:ifNotNilDo: ifNotNilDo: ifNotNilDo:ifNil: in: presenter readFromString: size yourself) ('associating' ->) ('binding' bindingOf:) ('casing' caseOf: caseOf:otherwise:) ('class membership' class inheritsFromAnyIn: isKindOf: isKindOf:orOf: isMemberOf: respondsTo: xxxClass) ('comparing' closeTo: hash identityHashPrintString literalEqual: = ~=) ('converting' adaptToFloat:andCompare: adaptToFloat:andSend: adaptToFraction:andCompare: adaptToFraction:andSend: adaptToInteger:andCompare: adaptToInteger:andSend: adaptToScaledDecimal:andCompare: asOrderedCollection asSetElement asString asStringOrText as: changeClassTo: complexContents mustBeBoolean mustBeBooleanIn: printDirectlyToDisplay withoutListWrapper) ('copying' copy copyAddedStateFrom: copyFrom: copySameFrom: copyTwoLevel deepCopy initialDeepCopierSize postCopy shallowCopy veryDeepCopy veryDeepCopySibling veryDeepCopyUsing: veryDeepCopyWith: veryDeepFixupWith: veryDeepInner:) ('debugging' haltIf: needsWork) ('debugging-haltOnce' checkHaltCountExpired clearHaltOnce decrementAndCheckHaltCount decrementHaltCount doExpiredHaltCount doExpiredHaltCount: doExpiredInspectCount haltOnCount: haltOnce haltOnceEnabled haltOnce: halt:onCount: hasHaltCount inspectOnCount: inspectOnce inspectUntilCount: removeHaltCount setHaltCountTo: setHaltOnce toggleHaltOnce) ('dependents access' addDependent: breakDependents canDiscardEdits dependents evaluate:wheneverChangeIn: hasUnacceptedEdits myDependents myDependents: release removeDependent:) ('drag and drop' acceptDroppingMorph:event:inMorph: dragPassengerFor:inMorph: dragStartedFor:transferMorph: dragTransferTypeForMorph: wantsDroppedMorph:event:inMorph:) ('error handling' assert: assert:descriptionBlock: assert:description: backwardCompatibilityOnly: caseError deprecated deprecated: deprecated:block: doesNotUnderstand: dpsTrace: dpsTrace:levels: dpsTrace:levels:withContext: error error: halt halt: handles: notify: notify:at: primitiveFailed primitiveFailed: shouldBeImplemented shouldNotImplement subclassResponsibility traitConflict) ('evaluating' value valueWithArguments:) ('filter streaming' byteEncode: drawOnCanvas: elementSeparator encodePostscriptOn: flattenOnStream: fullDrawPostscriptOn: putOn: writeOnFilterStream:) ('flagging' isThisEverCalled isThisEverCalled: logEntry logExecution logExit) ('graph model' addModelYellowButtonMenuItemsTo:forMorph:hand: hasModelYellowButtonMenuItems) ('macpal' codeStrippedOut: contentsChanged flash instanceVariableValues isUniversalTiles objectRepresented refusesToAcceptCode scriptPerformer slotInfo) ('message handling' executeMethod: perform: perform:orSendTo: perform:with:with:with:with: perform:with:with:with:with:with: perform:withArguments: perform:withArguments:inSuperclass: perform:withEnoughArguments: perform:with: perform:with:with: perform:with:with:with: withArgs:executeMethod: with:executeMethod: with:with:executeMethod: with:with:with:executeMethod: with:with:with:with:executeMethod:) ('objects from disk' comeFullyUpOnReload: convertToCurrentVersion:refStream: fixUponLoad:seg: objectForDataStream: readDataFrom:size: saveOnFile saveOnFileNamed: storeDataOn:) ('printing' fullPrintString isLiteral longPrintOn: longPrintOn:limitedTo:indent: longPrintString longPrintStringLimitedTo: nominallyUnsent: printOn: printString printStringLimitedTo: printWithClosureAnalysisOn: reportableSize storeOn: storeString stringForReadout stringRepresentation) ('scripting' adaptedToWorld: defaultFloatPrecisionFor: evaluateUnloggedForSelf: methodInterfacesForCategory:inVocabulary:limitClass: methodInterfacesForInstanceVariablesCategoryIn: methodInterfacesForScriptsCategoryIn: selfWrittenAsIll selfWrittenAsIm selfWrittenAsMe selfWrittenAsMy selfWrittenAsThis) ('system primitives' asOop become: becomeForward: becomeForward:copyHash: className creationStamp instVarAt: instVarAt:put: instVarNamed: instVarNamed:put: oopString primitiveChangeClassTo: rootStubInImageSegment: someObject) ('testing' beViewed belongsToUniClass costumes haltIfNil isArray isBehavior isBlock isBoolean isCharacter isClassReference isClosure isCodeReference isCollection isColor isColorForm isCompiledCode isCompiledMethod isComplex isContext isDictionary isFloat isForm isFraction isHeap isInteger isInterval isMessageSend isMethodContext isMethodProperties isMethodReference isMorph isMorphicEvent isMorphicModel isNumber isPlayer isPoint isPrimitiveCostume isPromise isRectangle isScriptEditorMorph isSketchMorph isStream isString isSymbol isSystemWindow isText isTextView isTrait isTransparent isVariableBinding isWebBrowser isWindowForModel: knownName name nameForViewer renameInternal: renameTo: shouldBePrintedAsLiteral shouldBePrintedAsLiteralVisiting: showDiffs stepAt:in: stepIn: stepTime stepTimeIn: vocabularyDemanded wantsDiffFeedback wantsSteps wantsStepsIn:) ('updating' changed changed: changed:with: handledListVerification noteSelectionIndex:for: okToChange okToClose updateListsAndCodeIn: update: update:with: windowIsClosing) ('user interface' addModelItemsToWindowMenu: addModelMenuItemsTo:forMorph:hand: asExplorerString defaultLabelForInspector launchPartVia: launchPartVia:label: launchTileToRefer modelSleep modelWakeUp modelWakeUpIn: mouseUpBalk: notYetImplemented windowActiveOnFirstClick windowReqNewLabel:) ('*monticello' isConflict) ('*services-base' requestor) ('*system-support' isPrimitiveError systemNavigation) ('*Tools-Explorer' explore exploreWithLabel:) ('*tools-browser' browseHierarchy) ('private' errorImproperStore errorNonIntegerIndex errorNotIndexable errorSubscriptBounds: setPinned: species storeAt:inTempFrame:) ('thumbnail' iconOrThumbnailOfSize:) ('*Morphic-Explorer' explorerContents hasContentsInExplorer) ('futures' future future: futureDo:at:args: futureSend:at:args:) ('*Etoys-viewer' assureUniClass browseOwnClassSubProtocol categoriesForViewer: categoriesForVocabulary:limitClass: chooseNewNameForReference defaultLimitClassForVocabulary: defaultNameStemForInstances elementTypeFor:vocabulary: externalName graphicForViewerTab hasUserDefinedSlots infoFor:inViewer: initialTypeForSlotNamed: isPlayerLike methodInterfacesInPresentationOrderFrom:forCategory: newScriptorAround: newTileMorphRepresentative offerViewerMenuFor:event: offerViewerMenuForEvt:morph: renameScript: tilePhrasesForCategory:inViewer: tilePhrasesForMethodInterfaces:inViewer: tilePhrasesForSelectorList:inViewer: tileToRefer uniqueInstanceVariableNameLike:excluding: uniqueNameForReference uniqueNameForReferenceFrom: uniqueNameForReferenceOrNil updateThresholdForGraphicInViewerTab usableMethodInterfacesIn:) ('*Protocols' currentVocabulary haveFullProtocolBrowsed haveFullProtocolBrowsedShowingSelector:) ('*Etoys-tiles' basicType tearOffTile) ('*Tools-MessageSets' browseAllCallsOn: browseAllImplementorsOf:) ('*Tools-multi-window support' canHaveUnacceptedEdits) ('*morphic' asDraggableMorph asMorph asStringMorph asTextMorph isPluggableListMorph openAsMorph) ('*MorphicExtras-Undo' capturedState commandHistory purgeAllCommands redoFromCapturedState: refineRedoTarget:selector:arguments:in: refineUndoTarget:selector:arguments:in: rememberCommand: rememberUndoableAction:named: undoFromCapturedState:) ('*MorphicExtras-PartsBin' descriptionForPartsBin) ('tracing' inboundPointers inboundPointersExcluding: outboundPointers outboundPointersDo:) ('*Tools-Debugger' canonicalArgumentName chasePointers explorePointers shouldFollowOutboundPointers) ('*System-Object Events-accessing' actionForEvent: actionForEvent:ifAbsent: actionMap actionSequenceForEvent: actionsDo: createActionMap hasActionForEvent: setActionSequence:forEvent: updateableActionMap) ('*System-Change Notification-events' actionsWithReceiver:forEvent: renameActionsWithReceiver:forEvent:toEvent:) ('*System-Change Notification-converting' asActionSequence asActionSequenceTrappingErrors) ('*System-Tools-breakpoint' break) ('*ToolBuilder-Kernel-error handling' confirm: confirm:orCancel:) ('*EToys-user interface' eToyStreamedRepresentationNotifying:) ('*ToolBuilder-Kernel-user interface' inform:) ('*System-Support-user interface' initialExtent) ('*System-Localization-locales' localeChanged localeChangedGently translatedNoop) ('*System-Object Events-accessing-removing' releaseActionMap removeAction:forEvent: removeActionsForEvent: removeActionsSatisfying: removeActionsSatisfying:forEvent: removeActionsWithReceiver: removeActionsWithReceiver:forEvent:) ('*System-Object Events-accessing-triggering' triggerEvent: triggerEvent:ifNotHandled: triggerEvent:with: triggerEvent:with:ifNotHandled: triggerEvent:withArguments: triggerEvent:withArguments:ifNotHandled:) ('*System-Object Events-accessing-registering' when:evaluate: when:send:to: when:send:to:with: when:send:to:withArguments:) ('*Tools-inspecting') ('*Traits' explicitRequirement requirement) ('*Graphics-KernelExtensions' fullScreenSize) ('*System-Finalization' actAsExecutor executor finalizationRegistry finalize hasMultipleExecutors retryWithGC:until: toFinalizeSend:to:with:) ('*collections' asLink compareSafely:) ('*Tools-Browsing' browse) ('*System-Recovery-error handling' primitiveError:) ('*SUnit-testing' isTestClass) ('*51Deprecated') ('*Morphic-Events-Filtering' filterEvent:for:) ('*System-Preferences' applyUserInterfaceTheme canApplyUserInterfaceTheme userInterfaceTheme) ('*Etoys-Squeakland-accessing' customizeExplorerContents) ('*Etoys-Squeakland-comparing' eToysEQ: eToysGE: eToysGT: eToysLE: eToysLT: eToysNE:) ('*Etoys-Squeakland-error handling' eToysError:) ('*Etoys-Squeakland-Etoys-viewer' hasUserDefinedScripts) ('*Etoys-Squeakland-translation support' inline:) ('*Etoys-Squeakland-testing' isReallyString) ('*Etoys-Squeakland-user interface' launchPartOffsetVia:label:) ('*Etoys-Squeakland-translating' literalStringsDo:) ('*Etoys-Squeakland-Tweak-Kedama' test:ifTrue:ifFalse: times:repeat:) ('*Tools' environment) ('*60Deprecated-copying' clone) ('pinning' isPinned pin unpin) ('*EToys-Scripting-accessing' addInstanceVarNamed:withValue: basicAddInstanceVarNamed:withValue:) ('*60Deprecated-Tools' exploreAndYourself notifyWithLabel:) ('literals' allLiteralsDo: hasLiteral: hasLiteralSuchThat:) ('write barrier' attemptToAssign:withIndex: beReadOnlyObject beWritableObject isReadOnlyObject setIsReadOnlyObject:) ('*Tools-Inspector' basicInspect inspect inspectWithLabel: inspectorClass) ('*Morphic-Kernel-accessing' activeHand currentEvent currentHand currentWorld) ! > From craig at blackpagedigital.com Thu Sep 24 15:56:50 2020 From: craig at blackpagedigital.com (Craig Latta) Date: Thu, 24 Sep 2020 08:56:50 -0700 Subject: [squeak-dev] Squeak 25th anniversary call for participation Message-ID: Hi all-- On 24 September 1996, Dan Ingalls and team announced the existence of Squeak, “another run at the fence” toward the joyful use of computation. Some would say this wonderful surprise was inevitable. Equally inevitable is the passage of time, and there is joy in that as well. Let’s amplify our fascination, appreciation, and ambition for Squeak and the community, by celebrating the 25th anniversary in 2021! What would you like to see, hear, and do? We of your board have a few ideas, including a new issue of Squeak News, and special interview episodes of the Smalltalk Reflections podcast. This is a good time to reflect, plan, and invent the future once more. Proceed for truth! -C -- Craig Latta :: research computer scientist Black Page Digital :: Berkeley, California 663137D7940BF5C0AFC 1349FB2ADA32C4D5314CE From asqueaker at gmail.com Thu Sep 24 19:33:27 2020 From: asqueaker at gmail.com (Chris Muller) Date: Thu, 24 Sep 2020 14:33:27 -0500 Subject: [squeak-dev] Why is ModificationForbidden not an Error? In-Reply-To: <1600938552895-0.post@n4.nabble.com> References: <93CBEDF7-D881-4B30-9142-004918314878@rowledge.org> <1600938552895-0.post@n4.nabble.com> Message-ID: Hi Christoph, Magma does not depend on ModificationForbidden. My participation in this discussion was merely for my own learning, to try to overcome my skepticism about ModificationForbidden. Changing its superclass shouldn't affect Magma at all. Whether Magma will be able to make use of it is still up in the air. Please feel free to integrate what you and the other folks interested in this decide is best. Thanks! Chris On Thu, Sep 24, 2020 at 4:09 AM Christoph Thiede < christoph.thiede at student.hpi.uni-potsdam.de> wrote: > Hi Chris, hi all, > > after months of stagnation, I'd like to bring up this open discussion > again. > IMO the current situation in the Trunk is still buggy and the last thing we > want developers to have to care about. For example, I recently crashed a > number of smalltalkCI builds again because some method raised a > ModificationForbidden, which the test runner, obviously, was not prepared > for ... > > From what I can see, the only problem with my proposal that resists still > in > the inbox, Kernel-ct.1321, is related to Chris' Magma implementation. I > want > to respect this use case (and actually, learning a bit more about Magma is > already on my long, long list of nice-to-do stuff), but I have to agree > with > other people saying that ModificationForbidden is a very low-level error > and > should not need any special treatment. For this reason, I also think that > Marcel's changeset is a bit over-engineered for the Trunk, given the > particular fact that we do not have plans for any user of ManagedObjects in > the Trunk. > > Chris, did anything happen on your end regarding the handling of > ModificationForbidden in Magma? Iirc you mentioned recently that you have > reached some kind of feature completeness in your project. Can't we just > merge Kernel-ct.1321 for now and resolve this discussion until some new use > case arises? For Magma as an external project (still without being familiar > at all with its design), I don't understand what would be wrong with > handling such exceptions using #on:do:, this would totally suffice to > circumvent the #defaultAction implementation. If you do not have the > possibility to install an exception handler or a ToolSet, you could still > place an extension override method in ModificationForbidden and > (conditionally) inject your custom handling logic there. > > What do you think? I would like to finally get ahead with this issue! :-) > > Best, > Christoph > > > > -- > Sent from: http://forum.world.st/Squeak-Dev-f45488.html > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Thu Sep 24 19:42:37 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Thu, 24 Sep 2020 12:42:37 -0700 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: <1599137423163-0.post@n4.nabble.com> References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> Message-ID: Hi Christoph, I didn't do my due dilligence in reviewing the changes. I've just hit a problem. When I accept as method in the debugger I get an MNU of stepToStatement. MorphicDebugger needs to implement stepToStatement, or contents:notifying: needs not to send it. Can you please fix ASAP? Apologies!! On Thu, Sep 3, 2020 at 5:50 AM Christoph Thiede < christoph.thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, > > when you reported this issue, I was not able to reproduce it. About two > months later, your new Sista bytecode set has arrived in the Trunk. Today, > I > encountered a very similar issue, could reproduce it, and trace it back to > a > comparison issue of Context >> #method which was affected by the > introduction of CompiledBlock instances. Is it possible that you already > used Sista in your image when reporting this bug? > > In case you were able to reproduce the bug: Can you confirm whether > Tools-ct.987 (inbox) fixes the issue for you? :-) > > Best, > Christoph > > > > -- > Sent from: http://forum.world.st/Squeak-Dev-f45488.html > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Thu Sep 24 19:49:47 2020 From: asqueaker at gmail.com (Chris Muller) Date: Thu, 24 Sep 2020 14:49:47 -0500 Subject: [squeak-dev] Squeak 25th anniversary call for participation In-Reply-To: References: Message-ID: Next year will be 25 years! Squeak has been a significant part of my life the last 20 or so of those. This is an excellent motivation for me to finish the SqueakMap server rewrite project, it would be neat if a modern "App Store Experience" could be available on the web and possibly the image by that time. On Thu, Sep 24, 2020 at 10:57 AM Craig Latta wrote: > > Hi all-- > > On 24 September 1996, Dan Ingalls and team announced the existence > of Squeak, “another run at the fence” toward the joyful use of > computation. Some would say this wonderful surprise was inevitable. > Equally inevitable is the passage of time, and there is joy in that as > well. Let’s amplify our fascination, appreciation, and ambition for > Squeak and the community, by celebrating the 25th anniversary in 2021! > > What would you like to see, hear, and do? We of your board have a > few ideas, including a new issue of Squeak News, and special interview > episodes of the Smalltalk Reflections podcast. This is a good time to > reflect, plan, and invent the future once more. > > Proceed for truth! > > > -C > > -- > Craig Latta :: research computer scientist > Black Page Digital :: Berkeley, California > 663137D7940BF5C0AFC 1349FB2ADA32C4D5314CE > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Thu Sep 24 21:13:45 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Thu, 24 Sep 2020 21:13:45 0000 Subject: [squeak-dev] The Trunk: System-topa.1174.mcz Message-ID: Tobias Pape uploaded a new version of System to project The Trunk: http://source.squeak.org/trunk/System-topa.1174.mcz ==================== Summary ==================== Name: System-topa.1174 Author: topa Time: 24 September 2020, 11:14:16.007835 pm UUID: 8f692d9f-4973-4aac-b181-9a2643089f8a Ancestors: System-eem.1173 Make it so that Theme fonts are looked up on usage, not on theme creation. =============== Diff against System-eem.1173 =============== Item was changed: ----- Method: CommunityTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: aUserInterfaceTheme "Set-up fonts." aUserInterfaceTheme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode)! Item was changed: ----- Method: CommunityTheme class>>addDarkSyntaxHighlighting: (in category 'instance creation') ----- addDarkSyntaxHighlighting: aUserInterfaceTheme "self createDark apply." + | normal bold italic underlined | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. - | normal bold italic underlined darkMap | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. darkMap := StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9. aUserInterfaceTheme set: #color for: #TextAction to: self dbBlue; set: #default for: #SHTextStylerST80 to: {self dbForeground}; set: #invalid for: #SHTextStylerST80 to: {self dbInvalid}; set: #excessCode for: #SHTextStylerST80 to: {self dbInvalid twiceDarker}; "Descriptive text for humans, italicized." set: #comment for: #SHTextStylerST80 to: {self dbComment. italic}; set: #unfinishedComment for: #SHTextStylerST80 to: {self dbComment darker. italic}; set: #'$' for: #SHTextStylerST80 to: {self dbConstant}; set: #character for: #SHTextStylerST80 to: {self dbConstant}; set: #integer for: #SHTextStylerST80 to: {self dbConstant}; set: #number for: #SHTextStylerST80 to: {self dbConstant}; set: #- for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #= for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #symbol for: #SHTextStylerST80 to: {self dbBedrock}; set: #stringSymbol for: #SHTextStylerST80 to: {self dbBedrock}; set: #literalArray for: #SHTextStylerST80 to: {self dbForeground}; set: #string for: #SHTextStylerST80 to: {self dbConstant}; set: #unfinishedString for: #SHTextStylerST80 to: {self dbConstant darker}; set: #assignment for: #SHTextStylerST80 to: {nil. bold}; set: #ansiAssignment for: #SHTextStylerST80 to: {nil. bold}; set: #literal for: #SHTextStylerST80 to: {nil. bold}; set: #keyword for: #SHTextStylerST80 to: {self dbMessage}; set: #binary for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #unary for: #SHTextStylerST80 to: {self dbMessage}; set: #incompleteKeyword for: #SHTextStylerST80 to: {self dbMessage darker. {underlined. bold}}; set: #incompleteBinary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; set: #incompleteUnary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; set: #undefinedKeyword for: #SHTextStylerST80 to: {self dbInvalid}; set: #undefinedBinary for: #SHTextStylerST80 to: {self dbInvalid}; set: #undefinedUnary for: #SHTextStylerST80 to: {self dbInvalid}; "Delineate the selector (good for new users), and make the method look like a mini-document with a title." set: #patternKeyword for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; set: #patternBinary for: #SHTextStylerST80 to: {nil. bold}; set: #patternUnary for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; set: #self for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #super for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #true for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #false for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #nil for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #thisContext for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #return for: #SHTextStylerST80 to: {self dbForeground. bold}; + set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; + set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; - set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; - set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; set: #blockPatternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #blockArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #argument for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #blockArgColon for: #SHTextStylerST80 to: {self dbBedrock}; set: #leftParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #rightParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #leftParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #rightParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #leftParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #rightParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #leftParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #rightParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #leftParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #rightParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #leftParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #rightParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #leftParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #rightParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #leftParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; set: #rightParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; set: #blockStart for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #blockEnd for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #blockStart1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #blockEnd1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #blockStart2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockEnd2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockStart3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockEnd3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockStart4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockEnd4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockStart5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockEnd5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockStart6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockEnd6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockStart7 for: #SHTextStylerST80 to: {Color yellow}; set: #blockEnd7 for: #SHTextStylerST80 to: {Color yellow}; set: #arrayStart for: #SHTextStylerST80 to: {self dbBedrock}; set: #arrayEnd for: #SHTextStylerST80 to: {self dbBedrock}; set: #arrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; set: #arrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayStart for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayEnd for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; set: #leftBrace for: #SHTextStylerST80 to: {self dbForeground}; set: #rightBrace for: #SHTextStylerST80 to: {self dbForeground}; set: #cascadeSeparator for: #SHTextStylerST80 to: {self dbForeground}; set: #statementSeparator for: #SHTextStylerST80 to: {self dbForeground}; set: #externalCallType for: #SHTextStylerST80 to: {self dbForeground}; set: #externalCallTypePointerIndicator for: #SHTextStylerST80 to: {self dbForeground}; set: #primitiveOrExternalCallStart for: #SHTextStylerST80 to: {self dbForeground}; set: #primitiveOrExternalCallEnd for: #SHTextStylerST80 to: {self dbForeground}; set: #methodTempBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockTempBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockArgsBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #primitive for: #SHTextStylerST80 to: {self dbGreen lighter. bold}; set: #pragmaKeyword for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #pragmaUnary for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #pragmaBinary for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #externalFunctionCallingConvention for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #module for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #blockTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #blockPatternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #instVar for: #SHTextStylerST80 to: {self dbYellow. normal }; set: #workspaceVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #undefinedIdentifier for: #SHTextStylerST80 to: {self dbInvalid}; set: #incompleteIdentifier for: #SHTextStylerST80 to: {self dbGray. underlined}; set: #tempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #patternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #poolConstant for: #SHTextStylerST80 to: {self dbConstant }; set: #classVar for: #SHTextStylerST80 to: {self dbReference}; set: #globalVar for: #SHTextStylerST80 to: {self dbClass. normal}. "And the text differ" aUserInterfaceTheme set: #insertTextAttributes for: #TextDiffBuilder to: { TextColor color: self dbRed }; set: #removeTextAttributes for: #TextDiffBuilder to: { TextEmphasis struckOut. TextColor color: self dbBlue }; set: #normalTextAttributes for: #TextDiffBuilder to: { TextEmphasis normal }.! Item was changed: ----- Method: MonokaiTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: ----- Method: SolarizedTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: ----- Method: SolarizedTheme class>>addLightFonts: (in category 'instance creation') ----- addLightFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode).! Item was changed: ----- Method: SqueakTheme class>>addFonts: (in category 'instance creation') ----- addFonts: theme theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFixedFont to: [TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]. - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFixedFont to: (TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode). ! Item was changed: ----- Method: TrimTheme class>>addFonts: (in category 'instance creation') ----- addFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: + (PackageInfo named: 'System') postscript: 'UserInterfaceTheme cleanUpAndReset.'! - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! From commits at source.squeak.org Fri Sep 25 07:45:01 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 25 Sep 2020 07:45:01 0000 Subject: [squeak-dev] The Trunk: MorphicTests-pre.66.mcz Message-ID: Patrick Rein uploaded a new version of MorphicTests to project The Trunk: http://source.squeak.org/trunk/MorphicTests-pre.66.mcz ==================== Summary ==================== Name: MorphicTests-pre.66 Author: pre Time: 25 September 2020, 9:44:57.768674 am UUID: 4785ae25-ad3c-ad4b-8341-0f3f4a4f3f1d Ancestors: MorphicTests-mt.65 Adds a test case for layouting morphs in text in case the TextAnchor attribute is applied to more than the SoH character (which is unusual but valid). =============== Diff against MorphicTests-mt.65 =============== Item was added: + ----- Method: TextAnchorTest>>testTwoTextAnchorsOneWithNestedInterval (in category 'tests') ----- + testTwoTextAnchorsOneWithNestedInterval + + | anchorAttribute2 anchoredMorph2 | + anchoredMorph2 := RectangleMorph new. + anchoredMorph height: 40. + anchoredMorph2 height: 40. + anchorAttribute2 := TextAnchor new anchoredMorph: anchoredMorph2. + text := Text streamContents: [:stream | + stream + nextPutAll: 'contrived '; + nextPutAll: Character startOfHeader asString asText; + nextPutAll: ' whose morph is in the center.'; + nextPutAll: Character startOfHeader asString asText; + nextPutAll: 'and some more text!!']. + text addAttribute: anchorAttribute from: 11 to: 61. + text addAttribute: anchorAttribute2 from: 42 to: 48. + textMorph hResizing: #shrinkWrap. + self prepareTextMorph. + + self assert: textMorph paragraph lines first lineHeight < 50. + self assert: anchoredMorph right < anchoredMorph2 left! From commits at source.squeak.org Fri Sep 25 07:48:45 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 25 Sep 2020 07:48:45 0000 Subject: [squeak-dev] The Trunk: Graphics-pre.439.mcz Message-ID: Patrick Rein uploaded a new version of Graphics to project The Trunk: http://source.squeak.org/trunk/Graphics-pre.439.mcz ==================== Summary ==================== Name: Graphics-pre.439 Author: pre Time: 25 September 2020, 9:48:35.481674 am UUID: e6d3328b-645a-e846-a3a9-c3c61b479c23 Ancestors: Graphics-tonyg.438 Adds code to handle the case in which a text anchor attribute is used on more than the SoH character AND another SoH character is within the range where the text anchor was applied. Without this, the first morph would be put at the location of the second SoH character. =============== Diff against Graphics-tonyg.438 =============== Item was changed: ----- Method: CharacterScanner>>embeddedObject (in category 'stop conditions') ----- embeddedObject + | previousAttributes newAttributes | pendingKernX := 0. + "If a text anchor was already at the previous index, it was already dealt with" + previousAttributes := lastIndex > 1 ifTrue: [text attributesAt: lastIndex - 1] ifFalse: [#()]. + newAttributes := (text attributesAt: lastIndex) copyWithoutAll: previousAttributes. + (newAttributes reject: [:each | each anchoredMorph isNil]) - ((text attributesAt: lastIndex) reject: [:each | each anchoredMorph isNil]) ifNotEmpty: [:attributes | (self placeEmbeddedObjectsFrom: attributes) ifTrue: [^ true]]. self setFont. "Note: if ever several objects are embedded on same character, only indent lastIndex once" lastIndex := lastIndex + 1. + ^ false! - ^false! From kksubbu.ml at gmail.com Fri Sep 25 13:37:37 2020 From: kksubbu.ml at gmail.com (K K Subbu) Date: Fri, 25 Sep 2020 19:07:37 +0530 Subject: [squeak-dev] Squeak 25th anniversary call for participation In-Reply-To: References: Message-ID: <77b11be0-8915-3854-ce34-cd7d810dcf01@gmail.com> On 24/09/20 9:26 pm, Craig Latta wrote: > What would you like to see, hear, and do? We of your board have a > few ideas, including a new issue of Squeak News, and special interview > episodes of the Smalltalk Reflections podcast. This is a good time to > reflect, plan, and invent the future once more. Craig, Thank you for reaching out. That Squeak remains a fascinating platform for inventing the future even after 25 years is a testimony to the visions of its creators and the commitment of its community. My proposals for the home page : * a scrolling banner "25th year of Squeak" or something similar on squeak.org that links to a page on its history. * a screenshot of the email announcement that links to the actual email in https://files.squeak.org/history/ The next iteration of Squeak 5.3 needs an image (greeting card!) to highlight this milestone. https://files.squeak.org/mail/index.html has mail records only from Nov 1996. Mails from Sep to Nov can be recovered/reconstructed for a complete record. BTW, contents of mail/ folder may be merged into history for ease of access and (search) indexing. Regards .. Subbu From tonyg at leastfixedpoint.com Fri Sep 25 14:55:23 2020 From: tonyg at leastfixedpoint.com (Tony Garnock-Jones) Date: Fri, 25 Sep 2020 16:55:23 +0200 Subject: [squeak-dev] Multiple processes and morphic state (was Re: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <20200914154934.GA85344@shell.msen.com> Message-ID: <495a43a9-69fa-1758-7f03-9e712067d98b@leastfixedpoint.com> Hi Christoph, On 9/24/20 2:08 PM, Thiede, Christoph wrote: >> It's true that in Morphic there is one distinct Process associated > with the user interface for each Morphic world. > > I'm not so sure about this. You can always do forks within UI code, so > sometimes there are also multiple Processes within one Morphic world. > Just think about Shout's background process or debugging processes, and > please keep in mind that Jakob justifiably proposed to renew the process > handling for non-modal dialogs, which maybe could also result in > multiple processes being active at the same time. > A world can have multiple hands, for example, RemoteHandMorphs > (Nebraska), event-simulating hands (EventRecorder), or just > multiple plain HandMorphs as demonstrated by Tony recently in > his PostmarketOS implementation. There is no reason for these hands to > run on the same process. Analogously, I don't see why we should restrict > a hand not to be thread-safe, so the hand-event relation is 1:n again. I've found that while, yes, there are lots of opportunities for concurrency in Morphic, actually *taking* those opportunities results in all sorts of problems. Morphic state is I think not thread-safe enough to allow multiple processes "inside" of it at once. So I've ended up being *very* careful to serialize all Morphic access within the one, main UI process. Where other processes (Actors) exist, I send messages from the UI process to the others, and when Morphic state change code has to run, I package it up into a block and enqueue it for later execution by the UI process rather than doing it in each Actor. (It's painful, actually, having to be so careful about it, in such a C-style "you do all the work yourself and the system won't help you" manner. Maybe there's something we can improve here?) So I think it's *morally* true that "there is one distinct Process associated with the user interface for each Morphic world," even if as a rule it can be bent a little :-) Cheers, Tony From commits at source.squeak.org Fri Sep 25 14:56:34 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 25 Sep 2020 14:56:34 0000 Subject: [squeak-dev] The Inbox: Tools-ct.988.mcz Message-ID: A new version of Tools was added to project The Inbox: http://source.squeak.org/inbox/Tools-ct.988.mcz ==================== Summary ==================== Name: Tools-ct.988 Author: ct Time: 25 September 2020, 4:56:31.616748 pm UUID: 31bde789-95f1-2745-90b1-4b8ff66adcbb Ancestors: Tools-ct.987 Hotfix for regression introduced in Tools-ct.987 (merge error). Please apologize any inconveniences! =============== Diff against Tools-ct.987 =============== Item was changed: ----- Method: Debugger>>contents:notifying: (in category 'accessing') ----- contents: aText notifying: aController "Accept new method source of the selected context." | selector classOfMethod category ctxt newMethod | contextStackIndex = 0 ifTrue: [^ false]. "First, handle some edge cases" selector := self selectedClass newParser parseSelector: aText. "selector isDoIt ifTrue: [ currentCompiledMethod := self compileDoIt: aText]." self flag: #todo. "ct: Recompile doIt method *without* creating method litters!! See Compiler>>#evaluateCue:ifFail:." selector = self selectedMessageName ifFalse: [ "Different message compiled, delegating to super" ^ super contents: aText notifying: aController]. self selectedContext isExecutingBlock ifTrue: [ "If we are in a block context, we need to rewind the stack before ." | home | home := self selectedContext activeHome. home ifNil: [ self inform: 'Method for block not found on stack, can''t edit and continue' translated. ^ false]. (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs translated) ifFalse: [ ^ false]. self resetContext: home changeContents: false. "N.B. Only reset the contents if the compilation succeeds. If contents would be reset when compilation fails, both compiler error message and modifications were lost." ^ (self contents: aText notifying: aController) ifTrue: [self contentsChanged]; yourself]. classOfMethod := self selectedClass. category := self selectedMessageCategoryName. "Do the actual compilation" selector := classOfMethod compile: aText classified: category notifying: aController. selector ifNil: [^ false]. "compilation cancelled" "Update views" contents := aText. newMethod := classOfMethod compiledMethodAt: selector. newMethod isQuick ifTrue: [ self cutBackExecutionToSenderContext]. ctxt := interruptedProcess popTo: self selectedContext. ctxt == self selectedContext ifFalse: [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs translated] ifTrue: [ newMethod isQuick ifFalse: [ interruptedProcess restartTopWith: newMethod. + interruptedProcess stepToSendOrReturn]. - self stepToStatement]. contextVariablesInspector object: nil]. self resetContext: ctxt. Project current addDeferredUIMessage: [ self changed: #contentsSelection]. ^ true! From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 25 14:58:31 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 25 Sep 2020 14:58:31 +0000 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com>, Message-ID: Hi Eliot, I'm very sorry for this bug, which was so unnecessary because my image has still a gigantic working copy ...! Tools-ct.988 should fix the issue, I tested it in a fresh trunk image. But it would be probably better if you could test it yourself, too. ;-) Best, Christoph PS: #stepToStatement gets relevant with the BytecodeDebugger changeset I proposed somewhen earlier in this year. I still would appreciate any review of it! :-) ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Donnerstag, 24. September 2020 21:42:37 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] tedious programming-in-the-debugger error needs fixing Hi Christoph, I didn't do my due dilligence in reviewing the changes. I've just hit a problem. When I accept as method in the debugger I get an MNU of stepToStatement. MorphicDebugger needs to implement stepToStatement, or contents:notifying: needs not to send it. Can you please fix ASAP? Apologies!! On Thu, Sep 3, 2020 at 5:50 AM Christoph Thiede > wrote: Hi Eliot, when you reported this issue, I was not able to reproduce it. About two months later, your new Sista bytecode set has arrived in the Trunk. Today, I encountered a very similar issue, could reproduce it, and trace it back to a comparison issue of Context >> #method which was affected by the introduction of CompiledBlock instances. Is it possible that you already used Sista in your image when reporting this bug? In case you were able to reproduce the bug: Can you confirm whether Tools-ct.987 (inbox) fixes the issue for you? :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 25 15:19:10 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 25 Sep 2020 15:19:10 +0000 Subject: [squeak-dev] Trunk broken (was: The Trunk: System-topa.1174.mcz) In-Reply-To: References: Message-ID: <25cbd26a894f461fa6627a2a18a6e667@student.hpi.uni-potsdam.de> Hi Tobias, hi all, this update just broke my image functionality sensitively! Right after loading System-topa.1174, the following happens to my fresh (!) trunk image the only adjustment to which I have applied being doing "Preferences changeFontSize: 12" once: - The background image disappears, I'm seeing a single dull gray tone as a background instead - Shout in all tools is broken (no syntax highlight any longer) - Balloons are semi-transparent - Dialog buttons have lost their color - Also, the fonts look a bit larger now, but I cannot say whether I like the change or not. I'm using a 202003021730 VM on Windows 10 64 bit. None of these effects occurs if my font size is set to zero when loading the update. I do not yet completely understand the changes you made, but IMO it would be a very good idea to remove this commit again from the trunk, if possible, and re-add it with an appropriate preamble and postload. This screenshot shows the full extent of the crisis: [cid:1ff543dd-1790-488a-b262-d7a614543dc5] Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Donnerstag, 24. September 2020 23:13 Uhr An: squeak-dev at lists.squeakfoundation.org; packages at lists.squeakfoundation.org Betreff: [squeak-dev] The Trunk: System-topa.1174.mcz Tobias Pape uploaded a new version of System to project The Trunk: http://source.squeak.org/trunk/System-topa.1174.mcz ==================== Summary ==================== Name: System-topa.1174 Author: topa Time: 24 September 2020, 11:14:16.007835 pm UUID: 8f692d9f-4973-4aac-b181-9a2643089f8a Ancestors: System-eem.1173 Make it so that Theme fonts are looked up on usage, not on theme creation. =============== Diff against System-eem.1173 =============== Item was changed: ----- Method: CommunityTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: aUserInterfaceTheme "Set-up fonts." aUserInterfaceTheme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode)! Item was changed: ----- Method: CommunityTheme class>>addDarkSyntaxHighlighting: (in category 'instance creation') ----- addDarkSyntaxHighlighting: aUserInterfaceTheme "self createDark apply." + | normal bold italic underlined | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. - | normal bold italic underlined darkMap | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. darkMap := StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9. aUserInterfaceTheme set: #color for: #TextAction to: self dbBlue; set: #default for: #SHTextStylerST80 to: {self dbForeground}; set: #invalid for: #SHTextStylerST80 to: {self dbInvalid}; set: #excessCode for: #SHTextStylerST80 to: {self dbInvalid twiceDarker}; "Descriptive text for humans, italicized." set: #comment for: #SHTextStylerST80 to: {self dbComment. italic}; set: #unfinishedComment for: #SHTextStylerST80 to: {self dbComment darker. italic}; set: #'$' for: #SHTextStylerST80 to: {self dbConstant}; set: #character for: #SHTextStylerST80 to: {self dbConstant}; set: #integer for: #SHTextStylerST80 to: {self dbConstant}; set: #number for: #SHTextStylerST80 to: {self dbConstant}; set: #- for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #= for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #symbol for: #SHTextStylerST80 to: {self dbBedrock}; set: #stringSymbol for: #SHTextStylerST80 to: {self dbBedrock}; set: #literalArray for: #SHTextStylerST80 to: {self dbForeground}; set: #string for: #SHTextStylerST80 to: {self dbConstant}; set: #unfinishedString for: #SHTextStylerST80 to: {self dbConstant darker}; set: #assignment for: #SHTextStylerST80 to: {nil. bold}; set: #ansiAssignment for: #SHTextStylerST80 to: {nil. bold}; set: #literal for: #SHTextStylerST80 to: {nil. bold}; set: #keyword for: #SHTextStylerST80 to: {self dbMessage}; set: #binary for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #unary for: #SHTextStylerST80 to: {self dbMessage}; set: #incompleteKeyword for: #SHTextStylerST80 to: {self dbMessage darker. {underlined. bold}}; set: #incompleteBinary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; set: #incompleteUnary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; set: #undefinedKeyword for: #SHTextStylerST80 to: {self dbInvalid}; set: #undefinedBinary for: #SHTextStylerST80 to: {self dbInvalid}; set: #undefinedUnary for: #SHTextStylerST80 to: {self dbInvalid}; "Delineate the selector (good for new users), and make the method look like a mini-document with a title." set: #patternKeyword for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; set: #patternBinary for: #SHTextStylerST80 to: {nil. bold}; set: #patternUnary for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; set: #self for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #super for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #true for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #false for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #nil for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #thisContext for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #return for: #SHTextStylerST80 to: {self dbForeground. bold}; + set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; + set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; - set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; - set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; set: #blockPatternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #blockArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #argument for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #blockArgColon for: #SHTextStylerST80 to: {self dbBedrock}; set: #leftParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #rightParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #leftParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #rightParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #leftParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #rightParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #leftParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #rightParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #leftParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #rightParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #leftParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #rightParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #leftParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #rightParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #leftParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; set: #rightParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; set: #blockStart for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #blockEnd for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #blockStart1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #blockEnd1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #blockStart2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockEnd2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockStart3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockEnd3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockStart4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockEnd4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockStart5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockEnd5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockStart6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockEnd6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockStart7 for: #SHTextStylerST80 to: {Color yellow}; set: #blockEnd7 for: #SHTextStylerST80 to: {Color yellow}; set: #arrayStart for: #SHTextStylerST80 to: {self dbBedrock}; set: #arrayEnd for: #SHTextStylerST80 to: {self dbBedrock}; set: #arrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; set: #arrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayStart for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayEnd for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; set: #leftBrace for: #SHTextStylerST80 to: {self dbForeground}; set: #rightBrace for: #SHTextStylerST80 to: {self dbForeground}; set: #cascadeSeparator for: #SHTextStylerST80 to: {self dbForeground}; set: #statementSeparator for: #SHTextStylerST80 to: {self dbForeground}; set: #externalCallType for: #SHTextStylerST80 to: {self dbForeground}; set: #externalCallTypePointerIndicator for: #SHTextStylerST80 to: {self dbForeground}; set: #primitiveOrExternalCallStart for: #SHTextStylerST80 to: {self dbForeground}; set: #primitiveOrExternalCallEnd for: #SHTextStylerST80 to: {self dbForeground}; set: #methodTempBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockTempBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockArgsBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #primitive for: #SHTextStylerST80 to: {self dbGreen lighter. bold}; set: #pragmaKeyword for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #pragmaUnary for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #pragmaBinary for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #externalFunctionCallingConvention for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #module for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #blockTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #blockPatternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #instVar for: #SHTextStylerST80 to: {self dbYellow. normal }; set: #workspaceVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #undefinedIdentifier for: #SHTextStylerST80 to: {self dbInvalid}; set: #incompleteIdentifier for: #SHTextStylerST80 to: {self dbGray. underlined}; set: #tempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #patternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #poolConstant for: #SHTextStylerST80 to: {self dbConstant }; set: #classVar for: #SHTextStylerST80 to: {self dbReference}; set: #globalVar for: #SHTextStylerST80 to: {self dbClass. normal}. "And the text differ" aUserInterfaceTheme set: #insertTextAttributes for: #TextDiffBuilder to: { TextColor color: self dbRed }; set: #removeTextAttributes for: #TextDiffBuilder to: { TextEmphasis struckOut. TextColor color: self dbBlue }; set: #normalTextAttributes for: #TextDiffBuilder to: { TextEmphasis normal }.! Item was changed: ----- Method: MonokaiTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: ----- Method: SolarizedTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: ----- Method: SolarizedTheme class>>addLightFonts: (in category 'instance creation') ----- addLightFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode).! Item was changed: ----- Method: SqueakTheme class>>addFonts: (in category 'instance creation') ----- addFonts: theme theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFixedFont to: [TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]. - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFixedFont to: (TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode). ! Item was changed: ----- Method: TrimTheme class>>addFonts: (in category 'instance creation') ----- addFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: + (PackageInfo named: 'System') postscript: 'UserInterfaceTheme cleanUpAndReset.'! - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 59638 bytes Desc: pastedImage.png URL: From Das.Linux at gmx.de Fri Sep 25 15:31:47 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Fri, 25 Sep 2020 17:31:47 +0200 Subject: [squeak-dev] Trunk broken (was: The Trunk: System-topa.1174.mcz) In-Reply-To: <25cbd26a894f461fa6627a2a18a6e667@student.hpi.uni-potsdam.de> References: <25cbd26a894f461fa6627a2a18a6e667@student.hpi.uni-potsdam.de> Message-ID: <24BB4E9D-9FC1-4717-A0EF-49E1A7334F5C@gmx.de> Hi > On 25.09.2020, at 17:19, Thiede, Christoph wrote: > > Hi Tobias, hi all, > > this update just broke my image functionality sensitively! Right after loading System-topa.1174, the following happens to my fresh (!) trunk image the only adjustment to which I have applied being doing "Preferences changeFontSize: 12" once: > > - The background image disappears, I'm seeing a single dull gray tone as a background instead > - Shout in all tools is broken (no syntax highlight any longer) > - Balloons are semi-transparent > - Dialog buttons have lost their color > - Also, the fonts look a bit larger now, but I cannot say whether I like the change or not. > > I'm using a 202003021730 VM on Windows 10 64 bit. > None of these effects occurs if my font size is set to zero when loading the update. > > I do not yet completely understand the changes you made, but IMO it would be a very good idea to remove this commit again from the trunk, if possible, and re-add it with an appropriate preamble and postload. > > This screenshot shows the full extent of the crisis: > > Yea, Back it out. I didn't realize font handling got so much more complex with the userinterfacethemes. Ok, then no hidpi :) -t > Best, > Christoph > > Von: Squeak-dev im Auftrag von commits at source.squeak.org > Gesendet: Donnerstag, 24. September 2020 23:13 Uhr > An: squeak-dev at lists.squeakfoundation.org; packages at lists.squeakfoundation.org > Betreff: [squeak-dev] The Trunk: System-topa.1174.mcz > > Tobias Pape uploaded a new version of System to project The Trunk: > http://source.squeak.org/trunk/System-topa.1174.mcz > > ==================== Summary ==================== > > Name: System-topa.1174 > Author: topa > Time: 24 September 2020, 11:14:16.007835 pm > UUID: 8f692d9f-4973-4aac-b181-9a2643089f8a > Ancestors: System-eem.1173 > > Make it so that Theme fonts are looked up on usage, not on theme creation. > > =============== Diff against System-eem.1173 =============== > > Item was changed: > ----- Method: CommunityTheme class>>addDarkFonts: (in category 'instance creation') ----- > addDarkFonts: aUserInterfaceTheme > "Set-up fonts." > aUserInterfaceTheme > + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode]; > + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; > + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; > + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]! > - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode); > - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); > - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); > - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode)! > > Item was changed: > ----- Method: CommunityTheme class>>addDarkSyntaxHighlighting: (in category 'instance creation') ----- > addDarkSyntaxHighlighting: aUserInterfaceTheme > "self createDark apply." > + | normal bold italic underlined | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. > - | normal bold italic underlined darkMap | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. darkMap := StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9. > aUserInterfaceTheme > set: #color for: #TextAction to: self dbBlue; > > set: #default for: #SHTextStylerST80 to: {self dbForeground}; > set: #invalid for: #SHTextStylerST80 to: {self dbInvalid}; > set: #excessCode for: #SHTextStylerST80 to: {self dbInvalid twiceDarker}; > "Descriptive text for humans, italicized." > set: #comment for: #SHTextStylerST80 to: {self dbComment. italic}; > set: #unfinishedComment for: #SHTextStylerST80 to: {self dbComment darker. italic}; > set: #'$' for: #SHTextStylerST80 to: {self dbConstant}; > set: #character for: #SHTextStylerST80 to: {self dbConstant}; > set: #integer for: #SHTextStylerST80 to: {self dbConstant}; > set: #number for: #SHTextStylerST80 to: {self dbConstant}; > set: #- for: #SHTextStylerST80 to: {self dbForeground. bold}; > set: #= for: #SHTextStylerST80 to: {self dbForeground. bold}; > set: #symbol for: #SHTextStylerST80 to: {self dbBedrock}; > set: #stringSymbol for: #SHTextStylerST80 to: {self dbBedrock}; > set: #literalArray for: #SHTextStylerST80 to: {self dbForeground}; > set: #string for: #SHTextStylerST80 to: {self dbConstant}; > set: #unfinishedString for: #SHTextStylerST80 to: {self dbConstant darker}; > set: #assignment for: #SHTextStylerST80 to: {nil. bold}; > set: #ansiAssignment for: #SHTextStylerST80 to: {nil. bold}; > set: #literal for: #SHTextStylerST80 to: {nil. bold}; > set: #keyword for: #SHTextStylerST80 to: {self dbMessage}; > set: #binary for: #SHTextStylerST80 to: {self dbForeground. bold}; > set: #unary for: #SHTextStylerST80 to: {self dbMessage}; > set: #incompleteKeyword for: #SHTextStylerST80 to: {self dbMessage darker. {underlined. bold}}; > set: #incompleteBinary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; > set: #incompleteUnary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; > set: #undefinedKeyword for: #SHTextStylerST80 to: {self dbInvalid}; > set: #undefinedBinary for: #SHTextStylerST80 to: {self dbInvalid}; > set: #undefinedUnary for: #SHTextStylerST80 to: {self dbInvalid}; > "Delineate the selector (good for new users), and make the method look like a mini-document with a title." > set: #patternKeyword for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; > set: #patternBinary for: #SHTextStylerST80 to: {nil. bold}; > set: #patternUnary for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; > set: #self for: #SHTextStylerST80 to: {self dbBedrock. bold}; > set: #super for: #SHTextStylerST80 to: {self dbBedrock. bold}; > set: #true for: #SHTextStylerST80 to: {self dbBedrock. bold}; > set: #false for: #SHTextStylerST80 to: {self dbBedrock. bold}; > set: #nil for: #SHTextStylerST80 to: {self dbBedrock. bold}; > set: #thisContext for: #SHTextStylerST80 to: {self dbBedrock. bold}; > set: #return for: #SHTextStylerST80 to: {self dbForeground. bold}; > + set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; > + set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; > - set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; > - set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; > set: #blockPatternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; > set: #blockArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; > set: #argument for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; > set: #blockArgColon for: #SHTextStylerST80 to: {self dbBedrock}; > set: #leftParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; > set: #rightParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; > set: #leftParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; > set: #rightParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; > set: #leftParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; > set: #rightParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; > set: #leftParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #rightParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #leftParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #rightParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #leftParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #rightParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #leftParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #rightParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #leftParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; > set: #rightParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; > set: #blockStart for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; > set: #blockEnd for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; > set: #blockStart1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; > set: #blockEnd1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; > set: #blockStart2 for: #SHTextStylerST80 to: {self dbBedrock}; > set: #blockEnd2 for: #SHTextStylerST80 to: {self dbBedrock}; > set: #blockStart3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #blockEnd3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #blockStart4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #blockEnd4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; > set: #blockStart5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #blockEnd5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #blockStart6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #blockEnd6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; > set: #blockStart7 for: #SHTextStylerST80 to: {Color yellow}; > set: #blockEnd7 for: #SHTextStylerST80 to: {Color yellow}; > set: #arrayStart for: #SHTextStylerST80 to: {self dbBedrock}; > set: #arrayEnd for: #SHTextStylerST80 to: {self dbBedrock}; > set: #arrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; > set: #arrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; > set: #byteArrayStart for: #SHTextStylerST80 to: {self dbForeground}; > set: #byteArrayEnd for: #SHTextStylerST80 to: {self dbForeground}; > set: #byteArrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; > set: #byteArrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; > set: #leftBrace for: #SHTextStylerST80 to: {self dbForeground}; > set: #rightBrace for: #SHTextStylerST80 to: {self dbForeground}; > set: #cascadeSeparator for: #SHTextStylerST80 to: {self dbForeground}; > set: #statementSeparator for: #SHTextStylerST80 to: {self dbForeground}; > set: #externalCallType for: #SHTextStylerST80 to: {self dbForeground}; > set: #externalCallTypePointerIndicator for: #SHTextStylerST80 to: {self dbForeground}; > set: #primitiveOrExternalCallStart for: #SHTextStylerST80 to: {self dbForeground}; > set: #primitiveOrExternalCallEnd for: #SHTextStylerST80 to: {self dbForeground}; > set: #methodTempBar for: #SHTextStylerST80 to: {self dbBedrock}; > set: #blockTempBar for: #SHTextStylerST80 to: {self dbBedrock}; > set: #blockArgsBar for: #SHTextStylerST80 to: {self dbBedrock}; > set: #primitive for: #SHTextStylerST80 to: {self dbGreen lighter. bold}; > set: #pragmaKeyword for: #SHTextStylerST80 to: {self dbGreen. bold}; > set: #pragmaUnary for: #SHTextStylerST80 to: {self dbGreen. bold}; > set: #pragmaBinary for: #SHTextStylerST80 to: {self dbGreen. bold}; > set: #externalFunctionCallingConvention for: #SHTextStylerST80 to: {self dbGreen. bold}; > set: #module for: #SHTextStylerST80 to: {self dbGreen. bold}; > set: #blockTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; > set: #blockPatternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; > set: #instVar for: #SHTextStylerST80 to: {self dbYellow. normal }; > set: #workspaceVar for: #SHTextStylerST80 to: {self dbLabel. italic}; > set: #undefinedIdentifier for: #SHTextStylerST80 to: {self dbInvalid}; > set: #incompleteIdentifier for: #SHTextStylerST80 to: {self dbGray. underlined}; > set: #tempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; > set: #patternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; > set: #poolConstant for: #SHTextStylerST80 to: {self dbConstant }; > set: #classVar for: #SHTextStylerST80 to: {self dbReference}; > set: #globalVar for: #SHTextStylerST80 to: {self dbClass. normal}. > "And the text differ" > aUserInterfaceTheme > set: #insertTextAttributes for: #TextDiffBuilder to: { TextColor color: self dbRed }; > set: #removeTextAttributes for: #TextDiffBuilder to: { TextEmphasis struckOut. TextColor color: self dbBlue }; > set: #normalTextAttributes for: #TextDiffBuilder to: { TextEmphasis normal }.! > > Item was changed: > ----- Method: MonokaiTheme class>>addDarkFonts: (in category 'instance creation') ----- > addDarkFonts: theme > > "Set-up fonts." > theme > + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; > + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; > + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; > + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! > - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); > - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); > - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); > - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! > > Item was changed: > ----- Method: SolarizedTheme class>>addDarkFonts: (in category 'instance creation') ----- > addDarkFonts: theme > > "Set-up fonts." > theme > + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; > + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; > + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; > + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! > - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); > - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); > - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); > - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! > > Item was changed: > ----- Method: SolarizedTheme class>>addLightFonts: (in category 'instance creation') ----- > addLightFonts: theme > > "Set-up fonts." > theme > + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; > + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; > + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; > + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode].! > - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); > - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); > - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); > - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode).! > > Item was changed: > ----- Method: SqueakTheme class>>addFonts: (in category 'instance creation') ----- > addFonts: theme > > theme > + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; > + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; > + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; > + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; > + set: #standardFixedFont to: [TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0]; > + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]. > - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); > - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); > - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); > - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); > - set: #standardFixedFont to: (TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0); > - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode). > ! > > Item was changed: > ----- Method: TrimTheme class>>addFonts: (in category 'instance creation') ----- > addFonts: theme > > "Set-up fonts." > theme > + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; > + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; > + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; > + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; > + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! > - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); > - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); > - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); > - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); > - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! > > Item was changed: > + (PackageInfo named: 'System') postscript: 'UserInterfaceTheme cleanUpAndReset.'! > - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 25 15:39:40 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 25 Sep 2020 15:39:40 +0000 Subject: [squeak-dev] Squeak 25th anniversary call for participation In-Reply-To: <77b11be0-8915-3854-ce34-cd7d810dcf01@gmail.com> References: , <77b11be0-8915-3854-ce34-cd7d810dcf01@gmail.com> Message-ID: <94af9dda5a7d4677a98820760275ddd0@student.hpi.uni-potsdam.de> Hi all, very nice idea, happy birthday Squeak afterward! (I did not know that Squeak is only four years exactly to the day older than me :D) I'd like to propose the attached changeset for celebrating Squeak's anniversary inside the image, too. At the moment, it only adds some greetings to the update message dialog. For trying it out, please load the changeset into an image and hardcode "Date today" to return "Date year: 2021 month: 9 day: 24", for example: [cid:b8c2ca3a-45b9-43ae-9ecb-132daae8a92f] The CS is still a bit unpolished at the moment, but if you like it, I will refactor it of course! We could also put on a party hat onto the Squeak icon in the docking bar (similarly as I did it in XmasDecorations), but this would require some background process/recurring UI message which probably would be a too big overhead for a small gimmick. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von K K Subbu Gesendet: Freitag, 25. September 2020 15:37:37 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] Squeak 25th anniversary call for participation On 24/09/20 9:26 pm, Craig Latta wrote: > What would you like to see, hear, and do? We of your board have a > few ideas, including a new issue of Squeak News, and special interview > episodes of the Smalltalk Reflections podcast. This is a good time to > reflect, plan, and invent the future once more. Craig, Thank you for reaching out. That Squeak remains a fascinating platform for inventing the future even after 25 years is a testimony to the visions of its creators and the commitment of its community. My proposals for the home page : * a scrolling banner "25th year of Squeak" or something similar on squeak.org that links to a page on its history. * a screenshot of the email announcement that links to the actual email in https://files.squeak.org/history/ The next iteration of Squeak 5.3 needs an image (greeting card!) to highlight this milestone. https://files.squeak.org/mail/index.html has mail records only from Nov 1996. Mails from Sep to Nov can be recovered/reconstructed for a complete record. BTW, contents of mail/ folder may be merged into history for ease of access and (search) indexing. Regards .. Subbu -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 24281 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 24743 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 34619 bytes Desc: pastedImage.png URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: anniversary-version.2.cs URL: From Yoshiki.Ohshima at acm.org Fri Sep 25 15:41:31 2020 From: Yoshiki.Ohshima at acm.org (Yoshiki Ohshima) Date: Fri, 25 Sep 2020 08:41:31 -0700 Subject: [squeak-dev] Squeak 25th anniversary call for participation In-Reply-To: References: Message-ID: Nice! We have some talks by Alan Kay on the net: https://tinlizzie.org/IA/index.php/Talks_by_Alan_Kay and there are other movies of Squeak demos and stuff that are sitting disks but not uploaded. We can get some interesting segments to make a highlight reel. On Thu, Sep 24, 2020 at 8:57 AM Craig Latta wrote: > > > Hi all-- > > On 24 September 1996, Dan Ingalls and team announced the existence > of Squeak, “another run at the fence” toward the joyful use of > computation. Some would say this wonderful surprise was inevitable. > Equally inevitable is the passage of time, and there is joy in that as > well. Let’s amplify our fascination, appreciation, and ambition for > Squeak and the community, by celebrating the 25th anniversary in 2021! > > What would you like to see, hear, and do? We of your board have a > few ideas, including a new issue of Squeak News, and special interview > episodes of the Smalltalk Reflections podcast. This is a good time to > reflect, plan, and invent the future once more. > > Proceed for truth! > > > -C > > -- > Craig Latta :: research computer scientist > Black Page Digital :: Berkeley, California > 663137D7940BF5C0AFC 1349FB2ADA32C4D5314CE > > > -- -- Yoshiki From commits at source.squeak.org Fri Sep 25 16:32:48 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 25 Sep 2020 16:32:48 0000 Subject: [squeak-dev] The Inbox: Kernel-ct.1342.mcz Message-ID: A new version of Kernel was added to project The Inbox: http://source.squeak.org/inbox/Kernel-ct.1342.mcz ==================== Summary ==================== Name: Kernel-ct.1342 Author: ct Time: 25 September 2020, 6:32:44.665274 pm UUID: 037ef8ee-228f-914b-8d1e-b05cac84772b Ancestors: Kernel-eem.1341 Add ordinal suffix logic to Integer Example: (1 to: 30) collect: [:ea | ea withOrdinalSuffix] =============== Diff against Kernel-eem.1341 =============== Item was added: + ----- Method: Integer>>ordinalSuffix (in category 'printing') ----- + ordinalSuffix + "Answer a string containing the ordinal suffix of the receiver, e.g. 'th' for 4, or 'rd' for 23." + + self \\ 100 // 10 = 1 ifFalse: [ + self \\ 10 + caseOf: { + [1] -> [^ 'st']. + [2] -> [^ 'nd']. + [3] -> [^ 'rd'] } + otherwise: []]. + ^ 'th'! Item was added: + ----- Method: Integer>>printWithOrdinalSuffixOn: (in category 'printing') ----- + printWithOrdinalSuffixOn: aStream + + aStream + print: self; + nextPutAll: self ordinalSuffix.! Item was added: + ----- Method: Integer>>withOrdinalSuffix (in category 'printing') ----- + withOrdinalSuffix + + ^ String streamContents: [:stream | + self printWithOrdinalSuffixOn: stream]! From forums.jakob at resfarm.de Fri Sep 25 16:36:57 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Fri, 25 Sep 2020 18:36:57 +0200 Subject: [squeak-dev] The Inbox: Kernel-ct.1342.mcz In-Reply-To: References: Message-ID: I think that such things should not really belong in Kernel. Is there another suitable package that can provide this as an extension method? Also note that this is not internationalized yet (English only), but judging by the selectors it might well be used to produce localized output in the future. Maybe that could influence the selection of a package. Am Fr., 25. Sept. 2020 um 18:32 Uhr schrieb : > > A new version of Kernel was added to project The Inbox: > http://source.squeak.org/inbox/Kernel-ct.1342.mcz > > ==================== Summary ==================== > > Name: Kernel-ct.1342 > Author: ct > Time: 25 September 2020, 6:32:44.665274 pm > UUID: 037ef8ee-228f-914b-8d1e-b05cac84772b > Ancestors: Kernel-eem.1341 > > Add ordinal suffix logic to Integer > > Example: > (1 to: 30) collect: [:ea | ea withOrdinalSuffix] > > =============== Diff against Kernel-eem.1341 =============== > > Item was added: > + ----- Method: Integer>>ordinalSuffix (in category 'printing') ----- > + ordinalSuffix > + "Answer a string containing the ordinal suffix of the receiver, e.g. 'th' for 4, or 'rd' for 23." > + > + self \\ 100 // 10 = 1 ifFalse: [ > + self \\ 10 > + caseOf: { > + [1] -> [^ 'st']. > + [2] -> [^ 'nd']. > + [3] -> [^ 'rd'] } > + otherwise: []]. > + ^ 'th'! > > Item was added: > + ----- Method: Integer>>printWithOrdinalSuffixOn: (in category 'printing') ----- > + printWithOrdinalSuffixOn: aStream > + > + aStream > + print: self; > + nextPutAll: self ordinalSuffix.! > > Item was added: > + ----- Method: Integer>>withOrdinalSuffix (in category 'printing') ----- > + withOrdinalSuffix > + > + ^ String streamContents: [:stream | > + self printWithOrdinalSuffixOn: stream]! > > From forums.jakob at resfarm.de Fri Sep 25 16:40:44 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Fri, 25 Sep 2020 18:40:44 +0200 Subject: [squeak-dev] The Trunk: Graphics-pre.439.mcz In-Reply-To: References: Message-ID: What is an SoH character? Does not look like ASCII 001 is meant here. Am Fr., 25. Sept. 2020 um 09:48 Uhr schrieb : > > Patrick Rein uploaded a new version of Graphics to project The Trunk: > http://source.squeak.org/trunk/Graphics-pre.439.mcz > > ==================== Summary ==================== > > Name: Graphics-pre.439 > Author: pre > Time: 25 September 2020, 9:48:35.481674 am > UUID: e6d3328b-645a-e846-a3a9-c3c61b479c23 > Ancestors: Graphics-tonyg.438 > > Adds code to handle the case in which a text anchor attribute is used on more than the SoH character AND another SoH character is within the range where the text anchor was applied. Without this, the first morph would be put at the location of the second SoH character. > > =============== Diff against Graphics-tonyg.438 =============== > > Item was changed: > ----- Method: CharacterScanner>>embeddedObject (in category 'stop conditions') ----- > embeddedObject > > + | previousAttributes newAttributes | > pendingKernX := 0. > + "If a text anchor was already at the previous index, it was already dealt with" > + previousAttributes := lastIndex > 1 ifTrue: [text attributesAt: lastIndex - 1] ifFalse: [#()]. > + newAttributes := (text attributesAt: lastIndex) copyWithoutAll: previousAttributes. > + (newAttributes reject: [:each | each anchoredMorph isNil]) > - ((text attributesAt: lastIndex) reject: [:each | each anchoredMorph isNil]) > ifNotEmpty: [:attributes | (self placeEmbeddedObjectsFrom: attributes) ifTrue: [^ true]]. > self setFont. > > "Note: if ever several objects are embedded on same character, only indent lastIndex once" > lastIndex := lastIndex + 1. > + ^ false! > - ^false! > > From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 25 17:35:44 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 25 Sep 2020 17:35:44 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <20200924150359.GA88607@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <20200914154934.GA85344@shell.msen.com> , <20200924150359.GA88607@shell.msen.com> Message-ID: <6deebfc91b0d475795915733b1c0c70f@student.hpi.uni-potsdam.de> Hi Dave, sorry for that, it's another occurrence of the "display during loading changeset" issue. Could you please try to load the attached changeset first? It adds a new service provider that allows you to file in the active variables changeset without encountering any UI updates on the way. If you like the filein changeset, I would like to get this into the trunk, too. :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Donnerstag, 24. September 2020 17:03:59 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic Hi Christoph, I am using an up to date trunk image. My image locks up about half way through loading the change set. I cannot interrupt it at that point so I'm not sure what the issue may be. Is there some prerequisite that I need? BTW, Marcel explained to me during a recent board meeting that the process local variables are intended to address issues related to the debugger, so now I understand the reason for it. Thanks, I'm looking forward to trying this. Dave On Thu, Sep 24, 2020 at 12:08:54PM +0000, Thiede, Christoph wrote: > Hi all, > > > sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: > > > * Use DynamicVariables for storing the active variable states > > * Refactor and reduce the visibility of active variable accessors on Object > > * Clean up implementations of #activeHand and #primaryHand > > > @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. > > > > > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. > > I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. > A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. > > PS: Thanks for the hint to the new wiki page! > > Best, > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Montag, 14. September 2020 17:49:34 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > > Hi Dave, > > > > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > > > > What do you think? > > > > Hi Christoph, > > The thing that I like (a lot) about your changes is that they make it > much easier to see and understand the accesses to the global variables. > The references now all go through a small number of methods in Object, > so that you can see what they do now. > > Clearly we would want to leave this logic in Object, since we don't > want to add more clutter to its protocol. But you have collected the logic > in one place now, which makes it easier to think about where it ultimately > should go, and that's great. > > But where do these things actually belong? Thinking in terms of the > responsiblities of objects in the system [1], I don't think that associating > them with a Process seems to be a good fit. It's true that in Morphic there > is one distinct Process associated with the user interface for each Morphic > world. So you can make things work by associating these variables with > a Process, but it seems like an unnatural fit to me. A Morphic world has > a process, but a Process is not responsible for knowing the state of > the Morphic world. > > Dave > > [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares. > > Content-Description: Hide activeVariables.3.cs > 'From Squeak6.0alpha of 6 September 2020 [latest update: #19838] on 24 September 2020 at 1:53:22 pm'! DynamicVariable subclass: #ActiveEventVariable instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Worlds'! DynamicVariable subclass: #ActiveHandVariable instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Worlds'! DynamicVariable subclass: #ActiveWorldVariable instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Worlds'! !Object methodsFor: 'user interface' stamp: 'ct 9/12/2020 14:13'! launchPartVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins" | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph openInHand.! ! !Object methodsFor: '*Protocols' stamp: 'ct 9/12/2020 14:14'! haveFullProtocolBrowsedShowingSelector: aSelector "Open up a Lexicon on the receiver, having it open up showing aSelector, which may be nil" "(2 at 3) haveFullProtocolBrowsed" | aBrowser | aBrowser := (Smalltalk at: #InstanceBrowser ifAbsent: [^ nil]) new useVocabulary: Vocabulary fullVocabulary. aBrowser openOnObject: self inWorld: Project current world showingSelector: aSelector! ! !Object methodsFor: '*Etoys-Squeakland-user interface' stamp: 'ct 9/12/2020 14:13'! launchPartOffsetVia: aSelector label: aString "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins. This variant makes the morph offset from the hand position by an amount suitable for tile-scripting in some circumstances." | aMorph | aMorph := self perform: aSelector. aMorph setNameTo: (Project current world unusedMorphNameLike: aString). aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. aMorph setProperty: #offsetForAttachingToHand toValue: 10@ -10. aMorph fullBounds. aMorph openInHand! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/24/2020 13:46'! activeHand "Answer the active hand for the current world. Fallback implementation for non-Morphic worlds. Private!! Usually, you want to send #currentHand instead." self == self currentWorld ifTrue: [ ^ nil]. ^ self currentHand! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/16/2020 20:28'! currentEvent "Answer the current MorphicEvent. Provided that a morphic project is loaded, this method never returns nil." ^ ActiveEventVariable value! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/24/2020 13:44'! currentHand "Answer the current HandMorph. Provided that a morphic project is loaded, this method will never return nil." ^ ActiveHandVariable value ifNil: [self currentWorld activeHand]! ! !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/16/2020 20:27'! currentWorld "Answer the current world. This method will never return nil." ^ ActiveWorldVariable value! ! !ActiveEventVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:09'! default ^ self currentHand ifNotNil: [:hand | hand lastEvent]! ! !ActiveEventVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 19:42'! value: anObject during: aBlock "For backword compatibility <6.0, still maintain the ActiveEvent global." | priorEvent | priorEvent := self value. ActiveEvent := anObject. ^ [super value: anObject during: aBlock] ensure: [ ActiveEvent := priorEvent]! ! !ActiveHandVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:14'! default ^ self currentWorld activeHand! ! !ActiveHandVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 19:43'! value: anObject during: aBlock "For backword compatibility <6.0, still maintain the ActiveHand global." | priorHand | priorHand := self value. ActiveHand := anObject. ^ [super value: anObject during: aBlock] ensure: [ ActiveHand := priorHand]! ! !ActiveWorldVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 18:14'! default ^ Project current world! ! !ActiveWorldVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 18:31'! value: anObject during: aBlock "For backword compatibility <6.0, still maintain the ActiveWorld global." | priorWorld | priorWorld := self value. ActiveWorld := anObject. ^ [super value: anObject during: aBlock] ensure: [ ActiveWorld := priorWorld]! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:05'! currentBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: Browser]]) asSet! ! !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:04'! currentHierarchyBrowsers ^ (Project current world submorphsSatisfying: [:each | (each isKindOf: SystemWindow) and: [each model isKindOf: HierarchyBrowser]]) asSet! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'Your name?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "UIManager default request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint onCancelReturn: cancelResponse! ! !Flaps class methodsFor: 'construction support' stamp: 'ct 9/11/2020 20:09'! possiblyReplaceEToyFlaps "If in eToyFriendly mode, and if it's ok to reinitialize flaps, replace the existing flaps with up-too-date etoy flaps. Caution: this is destructive of existing flaps. If preserving the contents of existing flaps is important, set the preference 'okToReinitializeFlaps' to true" PartsBin thumbnailForPartsDescription: StickyPadMorph descriptionForPartsBin. "Puts StickyPadMorph's custom icon back in the cache which typically will have been called" (Preferences eToyFriendly and: [Preferences okToReinitializeFlaps]) ifTrue: [Flaps disableGlobalFlaps: false. Flaps addAndEnableEToyFlaps. Smalltalk isMorphic ifTrue: [Project current world enableGlobalFlaps]]. "PartsBin clearThumbnailCache" "Flaps possiblyReplaceEToyFlaps"! ! !Flaps class methodsFor: 'menu commands' stamp: 'ct 9/11/2020 19:49'! disableGlobalFlaps: interactive "Clobber all the shared flaps structures. First read the user her Miranda rights." interactive ifTrue: [(self confirm: 'CAUTION!! This will destroy all the shared flaps, so that they will not be present in *any* project. If, later, you want them back, you will have to reenable them, from this same menu, whereupon the standard default set of shared flaps will be created. Do you really want to go ahead and clobber all shared flaps at this time?' translated) ifFalse: [^ self]]. self globalFlapTabsIfAny do: [:aFlapTab | self removeFlapTab: aFlapTab keepInList: false. aFlapTab isInWorld ifTrue: [self error: 'Flap problem' translated]]. self clobberFlapTabList. self initializeFlapsQuads. SharedFlapsAllowed := false. Smalltalk isMorphic ifTrue: [ Project current world restoreMorphicDisplay; reformulateUpdatingMenus]. "The following reduces the risk that flaps will be created with variant IDs such as 'Stack Tools2', potentially causing some shared flap logic to fail." "Smalltalk garbageCollect." "-- see if we are OK without this"! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:08'! enableGlobalFlaps "Start using global flaps, given that they were not present." Cursor wait showWhile: [ SharedFlapsAllowed := true. self globalFlapTabs. "This will create them" Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. FlapTab allInstancesDo: [:tab | tab computeEdgeFraction]. Project current world reformulateUpdatingMenus]]! ! !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:09'! setUpSuppliesFlapOnly "Set up the Supplies flap as the only shared flap. A special version formulated for this stand-alone use is used, defined in #newLoneSuppliesFlap" | supplies | SharedFlapTabs isEmptyOrNil ifFalse: "get rid of pre-existing guys if any" [SharedFlapTabs do: [:t | t referent delete. t delete]]. SharedFlapsAllowed := true. SharedFlapTabs := OrderedCollection new. SharedFlapTabs add: (supplies := self newLoneSuppliesFlap). self enableGlobalFlapWithID: 'Supplies' translated. supplies setToPopOutOnMouseOver: false. Smalltalk isMorphic ifTrue: [ Project current world addGlobalFlaps; reformulateUpdatingMenus].! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:08'! enableClassicNavigatorChanged "The #classicNavigatorEnabled preference has changed. No senders in easily traceable in the image, but this is really sent by a Preference object!!" Preferences classicNavigatorEnabled ifTrue: [Flaps disableGlobalFlapWithID: 'Navigator' translated. Preferences enable: #showProjectNavigator. self disableGlobalFlapWithID: 'Navigator' translated.] ifFalse: [self enableGlobalFlapWithID: 'Navigator' translated. Project current world addGlobalFlaps]. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. Project current world reformulateUpdatingMenus.! ! !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:09'! makeNavigatorFlapResembleGoldenBar "At explicit request, make the flap-based navigator resemble the golden bar. No senders in the image, but sendable from a doit" "Flaps makeNavigatorFlapResembleGoldenBar" Preferences setPreference: #classicNavigatorEnabled toValue: false. Preferences setPreference: #showProjectNavigator toValue: false. (self globalFlapTabWithID: 'Navigator' translated) ifNil: [SharedFlapTabs add: self newNavigatorFlap delete]. self enableGlobalFlapWithID: 'Navigator' translated. Preferences setPreference: #navigatorOnLeftEdge toValue: true. (self globalFlapTabWithID: 'Navigator' translated) arrangeToPopOutOnMouseOver: true. Project current world addGlobalFlaps. self doAutomaticLayoutOfFlapsIfAppropriate. Project current assureNavigatorPresenceMatchesPreference. ! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:58'! addLocalFlap ^ self addLocalFlap: self currentEvent! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent "Menu command -- let the user add a new project-local flap. Once the new flap is born, the user can tell it to become a shared flap. Obtain an initial name and edge for the flap, launch the flap, and also launch a menu governing the flap, so that the user can get started right away with customizing it." | title edge | edge := self askForEdgeOfNewFlap. edge ifNil: [^ self]. title := UIManager default request: 'Wording for this flap:' translated initialAnswer: 'Flap' translated. title isEmptyOrNil ifTrue: [^ self]. ^ self addLocalFlap: anEvent titled: title onEdge: edge! ! !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! addLocalFlap: anEvent titled: title onEdge: edge | flapTab menu world | flapTab := self newFlapTitled: title onEdge: edge. (world := anEvent hand world) addMorphFront: flapTab. flapTab adaptToWorld: world. menu := flapTab buildHandleMenu: anEvent hand. flapTab addTitleForHaloMenu: menu. flapTab computeEdgeFraction. menu popUpEvent: anEvent in: world.! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! enableOnlyGlobalFlapsWithIDs: survivorList "In the current project, suppress all global flaps other than those with ids in the survivorList" self globalFlapTabsIfAny do: [:flapTab | (survivorList includes: flapTab flapID) ifTrue: [self enableGlobalFlapWithID: flapTab flapID] ifFalse: [self disableGlobalFlapWithID: flapTab flapID]]. Project current world addGlobalFlaps "Flaps enableOnlyGlobalFlapsWithIDs: #('Supplies')"! ! !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! positionVisibleFlapsRightToLeftOnEdge: edgeSymbol butPlaceAtLeftFlapsWithIDs: idList "Lay out flaps along the designated edge right-to-left, while laying left-to-right any flaps found in the exception list Flaps positionVisibleFlapsRightToLeftOnEdge: #bottom butPlaceAtLeftFlapWithIDs: {'Navigator' translated. 'Supplies' translated} Flaps sharedFlapsAlongBottom" | leftX flapList flapsOnRight flapsOnLeft | flapList := self globalFlapTabsIfAny select: [:aFlapTab | aFlapTab isInWorld and: [aFlapTab edgeToAdhereTo == edgeSymbol]]. flapsOnLeft := OrderedCollection new. flapsOnRight := OrderedCollection new. flapList do: [:fl | (idList includes: fl flapID) ifTrue: [ flapsOnLeft addLast: fl ] ifFalse: [ flapsOnRight addLast: fl ] ]. leftX := Project current world width - 15. flapsOnRight sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab right: leftX - 3. leftX := aFlapTab left]. leftX := Project current world left. flapsOnLeft sort: [:f1 :f2 | f1 left > f2 left]; do: [:aFlapTab | aFlapTab left: leftX + 3. leftX := aFlapTab right]. flapList do: [:ft | ft computeEdgeFraction. ft flapID = 'Navigator' translated ifTrue: [ft referent left: (ft center x - (ft referent width//2) max: 0)]]! ! !Flaps class methodsFor: '*Etoys-Squeakland-predefined flaps' stamp: 'ct 9/12/2020 14:29'! newSuppliesFlapFromQuads: quads positioning: positionSymbol withPreviousEntries: aCollection "Answer a fully-instantiated flap named 'Supplies' to be placed at the bottom of the screen. Use #center as the positionSymbol to have it centered at the bottom of the screen, or #right to have it placed off near the right edge." | aFlapTab aStrip aWidth sugarNavigator | sugarNavigator := SugarNavigatorBar showSugarNavigator. aStrip := PartsBin newPartsBinWithOrientation: #leftToRight andColor: Color gray muchLighter from: quads withPreviousEntries: aCollection. self twiddleSuppliesButtonsIn: aStrip. aFlapTab := (sugarNavigator ifTrue: [SolidSugarSuppliesTab] ifFalse: [FlapTab]) new referent: aStrip beSticky. aFlapTab setName: 'Supplies' translated edge: (sugarNavigator ifTrue: [#top] ifFalse: [#bottom]) color: Color red lighter. aFlapTab position: (0 @ Project current world sugarAllowance). aFlapTab setBalloonText: aFlapTab balloonTextForFlapsMenu. aFlapTab applyThickness: 20. aWidth := self currentWorld width. aStrip extent: aWidth @ (76 * (1 + (1350 // aWidth))). aStrip beFlap: true. aStrip autoLineLayout: true. aStrip vResizeToFit: true. sugarNavigator ifTrue: [ aFlapTab useSolidTab. aFlapTab height: 20; color: (Color r: 0.804 g: 0.804 b: 0.804)] ifFalse: [ aFlapTab color: Color red lighter]. ^ aFlapTab "Flaps replaceGlobalFlapwithID: 'Supplies' translated"! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleColorSees "Form exampleColorSees" "First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area - where red touches blue - superimposed on the original scene. Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" | formA formB maskA offset tally map intersection left top dCanvas sensitiveColor soughtColor index | formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. sensitiveColor := Color red. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: sensitiveColor. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map at: (index := sensitiveColor indexInMap: map) put: 1. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map at: index put: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((sensitiveColor pixelValueForDepth: formA depth) ) to: ((soughtColor pixelValueForDepth: formB depth) ) test: (Form compareMatchColor bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchTest "Form exampleTouchTest" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a non-transparent pixel of the background upon which it is displayed. First column shows a form with a red block in the midst of transparent area sneaking up on a form with a transparent LHS and blue RHS. The green frame shows the intersection area. Second column shows in grey the part of the red that is within the intersection. Third column shows in black the blue that is within the intersection. Fourth column shows just the A touching B area. Fifth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA maskB offset tally map intersection left top dCanvas| formA := formB := maskA := maskB := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := Form extent: 100 at 50 depth: 32. formB := Form extent: 100 at 50 depth: 16. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color yellow. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color red. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: Color blue. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 2. formA displayOn: maskA at: offset - intersection origin rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskB := Form extent: intersection extent depth: 2. formB displayOn: maskB at: intersection origin negated rule: Form paint. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. map := Bitmap new: 4 withAll: 1. map at: 1 put: 0. "transparent" maskA copyBits: maskA boundingBox from: maskA at: 0 at 0 colorMap: map. "maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB copyBits: maskB boundingBox from: maskB at: 0 at 0 colorMap: map. "maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150." maskB displayOn: maskA at: 0 at 0 rule: Form and. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA boundingBox area -( maskA tallyPixelValues at: 1)) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((Color transparent pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((Color transparent pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorANotColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! exampleTouchingColor "Form exampleTouchingColor" "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a particular color pixel of the background upon which it is displayed. First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this Third shows the hit area (black) superimposed on the original scene Fourth column is the tally of hits via the old algorithm Last column shows the tally of hits via the new prim" |formA formB maskA offset tally map intersection left top dCanvas ignoreColor soughtColor| formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" Project current world restoreMorphicDisplay; doOneCycle. ignoreColor := Color transparent. soughtColor := Color blue. top := 50. dCanvas := FormCanvas on: Display. -50 to: 80 by: 10 do:[:p| offset:= p at 0. "vary this to check different states" left := 10. formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". formB := Form extent: 100 at 50 depth: 32. "make a red square in the middle of the form" (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color red. (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. "formA displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." "make a blue block on the right half of the form" (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. "formB displayOn: Display at: left at top rule: Form paint. dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. left := left + 150." intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. maskA := Form extent: intersection extent depth: 1. map := Bitmap new: (1 bitShift: (formA depth min: 15)). map atAllPut: 1. map at: ( ignoreColor indexInMap: map) put: 0. maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. "intersect world pixels of the color we're looking for with sensitive pixels mask" map atAllPut: 0. "clear map and reuse it" map at: (soughtColor indexInMap: map) put: 1. maskA copyBits: intersection from: formB at: 0 at 0 clippingBox: formB boundingBox rule: Form and fillColor: nil map: map. formB displayOn: Display at: left at top rule: Form paint. formA displayOn: Display at: (left at top) + offset rule: Form paint. maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 170. (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). left := left + 70. "now try using the new primitive" tally := (BitBlt destForm: formB sourceForm: formA fillColor: nil combinationRule: 3 "really ought to work with nil but prim code checks" destOrigin: intersection origin sourceOrigin: (offset negated max: 0 at 0) extent: intersection extent clipRect: intersection) primCompareColor: ((ignoreColor pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((soughtColor pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorAMatchColorB bitOr: Form compareTallyFlag). tally asString asDisplayText displayOn: Display at: left@(top +20). top:= top + 60]! ! !HandBugs methodsFor: 'tests' stamp: 'ct 9/12/2020 14:41'! testTargetPoint "self new testTargetPoint" "self run: #testTargetPoint" "This should not throw an exception." self currentHand targetPoint ! ! !Lexicon methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:16'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'Lexicon' translated. aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var assignments (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !InstanceBrowser methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:12'! offerMenu "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: ('Messages of {1}' translated format: {objectViewed nameForViewer}). aMenu addStayUpItem. aMenu addTranslatedList: #( ('vocabulary...' chooseVocabulary) ('what to show...' offerWhatToShowMenu) - ('inst var refs (here)' setLocalInstVarRefs) ('inst var defs (here)' setLocalInstVarDefs) ('class var refs (here)' setLocalClassVarRefs) - ('navigate to a sender...' navigateToASender) ('recent...' navigateToRecentMethod) ('show methods in current change set' showMethodsInCurrentChangeSet) ('show methods with initials...' showMethodsWithInitials) - "('toggle search pane' toggleSearch)" - - ('browse full (b)' browseMethodFull) ('browse hierarchy (h)' browseClassHierarchy) ('browse protocol (p)' browseFullProtocol) - ('fileOut' fileOutMessage) ('printOut' printOutMessage) - ('senders of... (n)' browseSendersOfMessages) ('implementors of... (m)' browseMessages) ('versions (v)' browseVersions) ('inheritance (i)' methodHierarchy) - ('references... (r)' browseVariableReferences) ('assignments... (a)' browseVariableAssignments) - ('viewer on me' viewViewee) ('inspector on me' inspectViewee) - ('more...' shiftedYellowButtonActivity)). ^ aMenu popUpInWorld: self currentWorld! ! !ListChooser methodsFor: 'actions' stamp: 'ct 9/12/2020 14:42'! accept "if the user submits with no valid entry, make them start over" | choice | self canAccept ifFalse: [ self canAdd ifTrue: [^ self add]. ^ self changed: #textSelection]. choice := self selectedItem. self canAdd ifTrue: [ "Ask the user whether to add the new item or choose the list selection." (UserDialogBoxMorph confirm: 'You can either choose an existing item or add a new one.\What do you want?' translated withCRs title: 'Choose or Add' translated trueChoice: choice asString falseChoice: self searchText asString at: self currentHand position) ifNil: ["Cancelled" self result: nil. ^ self] ifNotNil: [:answer | answer ifTrue: [self result: choice] ifFalse: [self result: self searchText asString]] ] ifFalse: [self result: choice]. self changed: #close.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! setUp previousID := Locale current localeID. previousKeyboardInterpreter := self currentHand instVarNamed: 'keyboardInterpreter'. previousClipboardInterpreter := Clipboard default instVarNamed: 'interpreter'. self currentHand clearKeyboardInterpreter. Clipboard default clearInterpreter.! ! !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! tearDown self currentHand instVarNamed: 'keyboardInterpreter' put: previousKeyboardInterpreter. Clipboard default instVarNamed: 'interpreter' put: previousClipboardInterpreter. Locale switchToID: (LocaleID isoLanguage: previousID).! ! !LocaleTest methodsFor: 'tests' stamp: 'ct 9/12/2020 14:43'! testLocaleChanged "self debug: #testLocaleChanged" "LanguageEnvironment >> startUp is called from Prject >> localeChanged" "takes quite a while" Project current updateLocaleDependents. self assert: (self currentHand instVarNamed: 'keyboardInterpreter') isNil description: 'non-nil keyboardInterpreter'. self assert: (Clipboard default instVarNamed: 'interpreter') isNil description: 'non-nil interpreter'. Locale switchToID: (LocaleID isoLanguage: 'ja'). self assert: 'ja' equals: Locale current localeID isoLanguage. Locale switchToID: (LocaleID isoLanguage: 'en'). self assert: 'en' equals: Locale current localeID isoLanguage.! ! !MCCodeTool methodsFor: 'menus' stamp: 'ct 9/11/2020 20:17'! browseFullProtocol "Open up a protocol-category browser on the value of the receiver's current selection. If in mvc, an old-style protocol browser is opened instead. Someone who still uses mvc might wish to make the protocol-category-browser work there too, thanks." (Smalltalk isMorphic and: [Smalltalk hasClassNamed: #Lexicon]) ifFalse: [^ self spawnFullProtocol]. self selectedClassOrMetaClass ifNotNil: [:class | ^ (Smalltalk at: #Lexicon) new openOnClass: class inWorld: self currentWorld showingSelector: self selectedMessageName]. ^ nil! ! !Morph methodsFor: 'copying' stamp: 'ct 9/12/2020 14:20'! duplicate "Make and return a duplicate of the receiver" | newMorph aName w aPlayer topRend | ((topRend := self topRendererOrSelf) ~~ self) ifTrue: [^ topRend duplicate]. self okayToDuplicate ifFalse: [^ self]. aName := (w := self world) ifNotNil: [w nameForCopyIfAlreadyNamed: self]. newMorph := self veryDeepCopy. aName ifNotNil: [newMorph setNameTo: aName]. newMorph arrangeToStartStepping. newMorph privateOwner: nil. "no longer in world" newMorph isPartsDonor: false. "no longer parts donor" (aPlayer := newMorph player) belongsToUniClass ifTrue: [aPlayer class bringScriptsUpToDate]. aPlayer ifNotNil: [self currentWorld presenter flushPlayerListCache]. ^ newMorph! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:20'! justDroppedInto: aMorph event: anEvent "This message is sent to a dropped morph after it has been dropped on -- and been accepted by -- a drop-sensitive morph" | partsBinCase cmd | (self formerOwner notNil and: [self formerOwner ~~ aMorph]) ifTrue: [self removeHalo]. self formerOwner: nil. self formerPosition: nil. cmd := self valueOfProperty: #undoGrabCommand. cmd ifNotNil:[aMorph rememberCommand: cmd. self removeProperty: #undoGrabCommand]. (partsBinCase := aMorph isPartsBin) ifFalse: [self isPartsDonor: false]. (self isInWorld and: [partsBinCase not]) ifTrue: [self world startSteppingSubmorphsOf: self]. "Note an unhappy inefficiency here: the startStepping... call will often have already been called in the sequence leading up to entry to this method, but unfortunately the isPartsDonor: call often will not have already happened, with the result that the startStepping... call will not have resulted in the startage of the steppage." "An object launched by certain parts-launcher mechanisms should end up fully visible..." (self hasProperty: #beFullyVisibleAfterDrop) ifTrue: [aMorph == self currentWorld ifTrue: [self goHome]. self removeProperty: #beFullyVisibleAfterDrop].! ! !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:19'! slideToTrash: evt "Perhaps slide the receiver across the screen to a trash can and make it disappear into it. In any case, remove the receiver from the screen." | aForm trash startPoint endPoint morphToSlide | ((self renderedMorph == ScrapBook default scrapBook) or: [self renderedMorph isKindOf: TrashCanMorph]) ifTrue: [self dismissMorph. ^ self]. TrashCanMorph slideDismissalsToTrash ifTrue: [morphToSlide := self representativeNoTallerThan: 200 norWiderThan: 200 thumbnailHeight: 100. aForm := morphToSlide imageForm offset: (0 at 0). trash := self currentWorld findDeepSubmorphThat: [:aMorph | (aMorph isKindOf: TrashCanMorph) and: [aMorph topRendererOrSelf owner == self currentWorld]] ifAbsent: [trash := TrashCanMorph new. trash position: self currentWorld bottomLeft - (0 @ (trash extent y + 26)). trash openInWorld. trash]. endPoint := trash fullBoundsInWorld center. startPoint := self topRendererOrSelf fullBoundsInWorld center - (aForm extent // 2)]. self dismissMorph. self currentWorld displayWorld. TrashCanMorph slideDismissalsToTrash ifTrue: [aForm slideFrom: startPoint to: endPoint nSteps: 12 delay: 15]. ScrapBook default addToTrash: self! ! !Morph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:45'! yellowButtonActivity: shiftState "Find me or my outermost owner that has items to add to a yellow button menu. shiftState is true if the shift was pressed. Otherwise, build a menu that contains the contributions from myself and my interested submorphs, and present it to the user." | menu | self isWorldMorph ifFalse: [| outerOwner | outerOwner := self outermostOwnerWithYellowButtonMenu. outerOwner ifNil: [^ self]. outerOwner == self ifFalse: [^ outerOwner yellowButtonActivity: shiftState]]. menu := self buildYellowButtonMenu: self currentHand. menu addTitle: self externalName icon: (self iconOrThumbnailOfSize: (Preferences tinyDisplay ifTrue: [16] ifFalse: [28])). menu popUpInWorld: self currentWorld! ! !Morph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:00'! buildYellowButtonMenu: aHand "Build the morph menu for the yellow button." | menu | menu := MenuMorph new defaultTarget: self. self addNestedYellowButtonItemsTo: menu event: self currentEvent. MenuIcons decorateMenu: menu. ^ menu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:44'! addMiscExtrasTo: aMenu "Add a submenu of miscellaneous extra items to the menu." | realOwner realMorph subMenu | subMenu := MenuMorph new defaultTarget: self. (self isWorldMorph not and: [(self renderedMorph isSystemWindow) not]) ifTrue: [subMenu add: 'put in a window' translated action: #embedInWindow]. self isWorldMorph ifFalse: [subMenu add: 'adhere to edge...' translated action: #adhereToEdge. subMenu addLine]. realOwner := (realMorph := self topRendererOrSelf) owner. (realOwner isKindOf: TextPlusPasteUpMorph) ifTrue: [subMenu add: 'GeeMail stuff...' translated subMenu: (realOwner textPlusMenuFor: realMorph)]. subMenu add: 'add mouse up action' translated action: #addMouseUpAction; add: 'remove mouse up action' translated action: #removeMouseUpAction; add: 'hand me tiles to fire this button' translated action: #handMeTilesToFire. subMenu addLine. subMenu add: 'arrowheads on pen trails...' translated action: #setArrowheads. subMenu addLine. subMenu defaultTarget: self topRendererOrSelf. subMenu add: 'draw new path' translated action: #definePath. subMenu add: 'follow existing path' translated action: #followPath. subMenu add: 'delete existing path' translated action: #deletePath. subMenu addLine. self addGestureMenuItems: subMenu hand: self currentHand. aMenu add: 'extras...' translated subMenu: subMenu! ! !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:21'! chooseNewGraphicCoexisting: aBoolean "Allow the user to choose a different form for her form-based morph" | replacee aGraphicalMenu | self isInWorld ifFalse: "menu must have persisted for a not-in-world object." [aGraphicalMenu := Project current world submorphThat: [:m | (m isKindOf: GraphicalMenu) and: [m target == self]] ifNone: [^ self]. ^ aGraphicalMenu show; flashBounds]. aGraphicalMenu := GraphicalMenu new initializeFor: self withForms: self reasonableForms coexist: aBoolean. aBoolean ifTrue: [self primaryHand attachMorph: aGraphicalMenu] ifFalse: [replacee := self topRendererOrSelf. replacee owner replaceSubmorph: replacee by: aGraphicalMenu]! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/12/2020 14:20'! indicateAllSiblings "Indicate all the receiver and all its siblings by flashing momentarily." | aPlayer allBoxes | (aPlayer := self topRendererOrSelf player) belongsToUniClass ifFalse: [^ self "error: 'not uniclass'"]. allBoxes := aPlayer class allInstances select: [:m | m costume world == self currentWorld] thenCollect: [:m | m costume boundsInWorld]. 5 timesRepeat: [Display flashAll: allBoxes andWait: 120].! ! !Morph methodsFor: 'meta-actions' stamp: 'ct 9/11/2020 18:00'! resizeFromMenu "Commence an interaction that will resize the receiver" ^ self resizeMorph: self currentEvent! ! !Morph methodsFor: 'structure' stamp: 'ct 9/24/2020 13:37'! activeHand "Alias for #currentHand. Maybe we want to deprecate this at some point" ... ^ self currentHand! ! !Morph methodsFor: 'structure' stamp: 'ct 9/16/2020 19:06'! primaryHand ^ self activeHand! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:45'! deleteUnlessHasFocus "Runs on a step timer because we cannot be guaranteed to get focus change events." (self currentHand keyboardFocus ~= self and: [ self isInWorld ]) ifTrue: [ self stopSteppingSelector: #deleteUnlessHasFocus ; delete ]! ! !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:20'! dismissViaHalo "The user has clicked in the delete halo-handle. This provides a hook in case some concomitant action should be taken, or if the particular morph is not one which should be put in the trash can, for example." | cmd | self setProperty: #lastPosition toValue: self positionInWorld. self dismissMorph. TrashCanMorph preserveTrash ifTrue: [ TrashCanMorph slideDismissalsToTrash ifTrue:[self slideToTrash: nil] ifFalse:[TrashCanMorph moveToTrash: self]. ]. cmd := Command new cmdWording: 'dismiss ' translated, self externalName. cmd undoTarget: Project current world selector: #reintroduceIntoWorld: argument: self. cmd redoTarget: Project current world selector: #onceAgainDismiss: argument: self. Project current world rememberCommand: cmd.! ! !Morph methodsFor: 'e-toy support' stamp: 'ct 9/12/2020 14:19'! referencePlayfield "Answer the PasteUpMorph to be used for cartesian-coordinate reference" | former | owner ifNotNil: [(self topRendererOrSelf owner isHandMorph and: [(former := self formerOwner) notNil]) ifTrue: [former := former renderedMorph. ^ former isPlayfieldLike ifTrue: [former] ifFalse: [former referencePlayfield]]]. self allOwnersDo: [:o | o isPlayfieldLike ifTrue: [^ o]]. ^ Project current world! ! !Morph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:45'! handMeTilesToFire "Construct a phrase of tiles comprising a line of code that will 'fire' this object, and hand it to the user" self currentHand attachMorph: (self assuredPlayer tilesToCall: MethodInterface firingInterface)! ! !Morph methodsFor: '*Etoys-Squeakland-geometry' stamp: 'ct 9/12/2020 14:19'! stagingArea "Answer a containing Worldlet, or the World if none." ^ (self ownerThatIsA: Worldlet) ifNil: [self currentWorld]! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:22'! changeColorTarget: anObject selector: aSymbol originalColor: aColor hand: aHand showPalette: showPalette "Put up a color picker for changing some kind of color. May be modal or modeless, depending on #modalColorPickers setting" | c aRectangle | self flag: #arNote. "Simplify this due to anObject == self for almost all cases" c := ColorPickerMorph new. c choseModalityFromPreference; sourceHand: aHand; target: anObject; selector: aSymbol; originalColor: aColor. showPalette ifFalse: [c initializeForJustCursor]. aRectangle := (anObject == self currentWorld) ifTrue: [self currentHand position extent: (20 at 20)] ifFalse: [anObject isMorph ifFalse: [Rectangle center: self position extent: (20 at 20)] ifTrue: [anObject fullBoundsInWorld]]. c putUpFor: anObject near: aRectangle.! ! !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:45'! showEmbedMenu "Put up a menu offering embed targets. Emphasize the current position. Theoretically this method will only be called when there are at least two alternatives." | aMenu | aMenu := self addEmbeddingMenuItemsTo: nil hand: self currentHand. aMenu title: ('embed {1} in...' translated format: {self externalName }). aMenu popUpInWorld! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:20'! hideWillingnessToAcceptDropFeedback "Make the receiver stop looking ready to show some welcoming feedback" self currentWorld removeHighlightFeedback ! ! !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:19'! showWillingnessToAcceptDropFeedback "Make the receiver look ready to show show some welcoming feedback" | aMorph | aMorph := RectangleMorph new bounds: self bounds.. aMorph beTransparent; borderWidth: 4; borderColor: (Color green); lock. aMorph setProperty: #affilliatedPad toValue: (self ownerThatIsA: TilePadMorph). self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !Morph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 15:17'! openInWorldOrWorldlet "Open in the world-like creature affiliated with the active Hand." | aRecorder aWorldlet | (self currentHand isKindOf: HandMorphForReplay) ifTrue: [((aRecorder := self currentHand recorder) isKindOf: MentoringEventRecorder) ifTrue: [aWorldlet := aRecorder contentArea. self center: aWorldlet center. aWorldlet addMorphFront: self. ^ self]]. self openInWorld.! ! !AllPlayersTool methodsFor: 'reinvigoration' stamp: 'ct 9/12/2020 14:25'! reinvigorate "Referesh the contents of the receiver" (submorphs copyFrom: 3 to: submorphs size) do: [:m | m delete]. self currentWorld doOneCycleNow. self playSoundNamed: 'scritch'. (Delay forMilliseconds: 700) wait. self currentWorld presenter reinvigoratePlayersTool: self. self playSoundNamed: 'scratch'.! ! !AllScriptsTool methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:26'! addSecondLineOfControls "Add the second line of controls" | aRow outerButton aButton worldToUse | aRow := AlignmentMorph newRow listCentering: #center; color: Color transparent. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingOnlyActiveScripts; getSelector: #showingOnlyActiveScripts. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'tickers only' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then only scripts that are paused or ticking will be shown' translated. aRow addMorphBack: outerButton. aRow addTransparentSpacerOfSize: 20 at 0. aRow addMorphBack: self helpButton. aRow addTransparentSpacerOfSize: 20 at 0. outerButton := AlignmentMorph newRow. outerButton wrapCentering: #center; cellPositioning: #leftCenter. outerButton color: Color transparent. outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). aButton target: self; actionSelector: #toggleWhetherShowingAllInstances; getSelector: #showingAllInstances. outerButton addTransparentSpacerOfSize: (4 at 0). outerButton addMorphBack: (StringMorph contents: 'all instances' translated font: ScriptingSystem fontForEToyButtons) lock. outerButton setBalloonText: 'If checked, then entries for all instances will be shown, but if not checked, scripts for only one representative of each different kind of object will be shown. Consult the help available by clicking on the purple ? for more information.' translated. aRow addMorphBack: outerButton. self addMorphBack: aRow. worldToUse := self isInWorld ifTrue: [self world] ifFalse: [self currentWorld]. worldToUse presenter reinvigorateAllScriptsTool: self. self layoutChanged.! ! !BookMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:39'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer pageNum: pageNum "Call once to search a page of the book. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container wasIn strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [(pages at: pageNum) isInMemory ifFalse: [rawStrings] ifTrue: [(pages at: pageNum) allStringsAfter: oldContainer]]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findString: searchString startingAt: start caseSensitive: false) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" wasIn := (pages at: pageNum) isInMemory. self goToPage: pageNum. wasIn ifFalse: ["search again, on the real current text. Know page is in." ^self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) startAt: startIndex container: oldContainer pageNum: pageNum "recompute"]]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !BookMorph methodsFor: 'navigation' stamp: 'ct 9/12/2020 14:26'! goToPageMorph: newPage transitionSpec: transitionSpec "Go to a page, which is assumed to be an element of my pages array (if it is not, this method returns quickly. Apply the transitionSpec provided." | pageIndex aWorld oldPageIndex ascending tSpec readIn | pages isEmpty ifTrue: [^self]. self setProperty: #searchContainer toValue: nil. "forget previous search" self setProperty: #searchOffset toValue: nil. self setProperty: #searchKey toValue: nil. pageIndex := pages identityIndexOf: newPage ifAbsent: [^self "abort"]. readIn := newPage isInMemory not. oldPageIndex := pages identityIndexOf: currentPage ifAbsent: [nil]. ascending := (oldPageIndex isNil or: [newPage == currentPage]) ifTrue: [nil] ifFalse: [oldPageIndex < pageIndex]. tSpec := transitionSpec ifNil: ["If transition not specified by requestor..." newPage valueOfProperty: #transitionSpec ifAbsent: [" ... then consult new page" self transitionSpecFor: self " ... otherwise this is the default"]]. self flag: #arNote. "Probably unnecessary" (aWorld := self world) ifNotNil: [self primaryHand releaseKeyboardFocus]. currentPage ifNotNil: [currentPage updateCachedThumbnail]. self currentPage notNil ifTrue: [(((pages at: pageIndex) owner isKindOf: TransitionMorph) and: [(pages at: pageIndex) isInWorld]) ifTrue: [^self "In the process of a prior pageTurn"]. self currentPlayerDo: [:aPlayer | aPlayer runAllClosingScripts]. self removeViewersOnSubsIn: self currentWorld presenter. ascending ifNotNil: ["Show appropriate page transition and start new page when done" currentPage stopStepping. (pages at: pageIndex) position: currentPage position. ^(TransitionMorph effect: tSpec second direction: tSpec third inverse: (ascending or: [transitionSpec notNil]) not) showTransitionFrom: currentPage to: (pages at: pageIndex) in: self whenStart: [self playPageFlipSound: tSpec first] whenDone: [currentPage delete; fullReleaseCachedState. self insertPageMorphInCorrectSpot: (pages at: pageIndex). self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrlInBook: self url. currentPage sqkPage computeThumbnail "just store it"]]]. "No transition, but at least decommission current page" currentPage delete; fullReleaseCachedState]. self insertPageMorphInCorrectSpot: (pages at: pageIndex). "sets currentPage" self adjustCurrentPageForFullScreen. self snapToEdgeIfAppropriate. aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. (aWorld := self world) ifNotNil: ["WHY??" aWorld displayWorld]. readIn ifTrue: [currentPage updateThumbnailUrl. currentPage sqkPage computeThumbnail "just store it"]. self currentWorld presenter flushPlayerListCache.! ! !BookMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:38'! addAdvancedItemsTo: aMenu "Add advanced items to a menu which allow the user to affect all the pages of the book. NB balloon help msgs still pending." | subMenu | subMenu := MenuMorph new defaultTarget: self. subMenu addTranslatedList: #( ('make all pages the same size as this page' makeUniformPageSize 'Make all the pages of this book be the same size as the page currently showing.') ('set background color for all pages' #setPageColor 'Choose a color to assign as the background color for all of this book''s pages') - ('uncache page sorter' uncachePageSorter) ('make a thread of projects in this book' buildThreadOfProjects) - ('make this the template for new pages' setNewPagePrototype)) translatedNoop. "NB The following 2 items do not get auto-updated in a persistent menu." newPagePrototype ifNotNil: [ subMenu add: 'clear new-page template' translated action: #clearNewPagePrototype]. self isInFullScreenMode ifTrue: [ subMenu add: 'exit full screen' translated action: #exitFullScreen] ifFalse: [ subMenu add: 'show full screen' translated action: #goFullScreen]. (self currentHand pasteBuffer isKindOf: PasteUpMorph) ifTrue: [ subMenu addLine. subMenu add: 'paste book page' translated action: #pasteBookPage]. aMenu add: 'advanced...' translated subMenu: subMenu.! ! !CategoryViewer methodsFor: 'get/set slots' stamp: 'ct 9/12/2020 14:39'! makeUniversalTilesGetter: aMethodInterface event: evt from: aMorph "Button in viewer performs this to make a universal-tiles getter and attach it to hand." | newTiles | newTiles := self newGetterTilesFor: scriptedPlayer methodInterface: aMethodInterface. newTiles setProperty: #beScript toValue: true. owner ifNil: [^ newTiles]. self currentHand attachMorph: newTiles. newTiles align: newTiles topLeft with: evt hand position + (7 at 14).! ! !CategoryViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 19:42'! currentVocabulary "Answer the vocabulary currently installed in the viewer. The outer StandardViewer object holds this information." ^ self outerViewer ifNotNil: [:viewer | viewer currentVocabulary] ifNil: [(self world ifNil: [self currentWorld]) currentVocabularyFor: scriptedPlayer]! ! !CategoryViewer methodsFor: '*Etoys-Squeakland-categories' stamp: 'ct 9/11/2020 19:42'! assureCategoryFullyVisible "Keep deleting categoryviewers other than the receiver until the receiver is fully visible." | ready toDelete | ready := false. [(self bounds bottom > self world bottom) and: [ready not]] whileTrue: [ owner submorphs size > 2 ifTrue: [ toDelete := owner submorphs allButFirst reversed detect: [:cv | cv ~~ self] ifNone: [^ self]. toDelete delete. self world doOneCycleNow] ifFalse: [ ready := true]].! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: self bounds; beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock; yourself. self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! removeHighlightFeedback "Remove any existing highlight feedback" self world removeHighlightFeedback. ! ! !DialogWindow methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:44'! initialize super initialize. self changeTableLayout; listDirection: #topToBottom; hResizing: #shrinkWrap; vResizing: #shrinkWrap; rubberBandCells: true; setProperty: #indicateKeyboardFocus toValue: #never. self createTitle: 'Dialog'. self createBody. self setDefaultParameters. keyMap := Dictionary new. exclusive := true. autoCancel := false. preferredPosition := self currentWorld center.! ! !DockingBarMorph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:40'! delete self currentHand removeKeyboardListener: self. activeSubMenu ifNotNil: [ activeSubMenu delete]. ^ super delete! ! !EtoyDAVLoginMorph methodsFor: 'private' stamp: 'ct 9/11/2020 19:45'! loginAndDo: aBlock ifCanceled: cb "EtoyDAVLoginMorph loginAndDo:[:n :p | true] ifCanceled:[]" self name: '' actionBlock: aBlock cancelBlock: cb; fullBounds; position: Display extent - self extent // 2. self position: self position + (0 at 40). self currentWorld addMorphInLayer: self.! ! !EtoyDAVLoginMorph methodsFor: 'actions' stamp: 'ct 9/11/2020 19:45'! launchBrowser self currentWorld addMorph: self buildPanel centeredNear: Sensor cursorPoint. (Smalltalk classNamed: #ScratchPlugin) ifNotNil: [:sp | sp primOpenURL: self url].! ! !EventMorph methodsFor: 'drag and drop' stamp: 'ct 9/11/2020 19:45'! brownDragConcluded "After the user has manually repositioned the receiver via brown-halo-drag, this is invoked." self currentWorld abandonAllHalos. self eventRoll ifNotNil: [:evtRoll | evtRoll pushChangesBackToEventTheatre]! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! abandonReplayHandsAndHalos "Cleanup after playback." self currentWorld abandonReplayHandsAndHalosFor: eventRecorder! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! dismantlePaintBoxArtifacts "Cleanup after playback -- if a paint-box has been left up, take it down." (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/12/2020 14:28'! makeHorizontalRoll "Create a horizontal roll viewer for this recording space" state = #readyToRecord ifTrue: [ ^ self inform: 'Nothing recorded yet' translated]. "self convertToCanonicalForm." "Would prefer to do this but there are still issues." eventRoll ifNil: [ eventRoll := EventRollMorph new. eventRoll eventTheatre: self]. eventRoll formulate. eventRoll isInWorld ifFalse: [eventRoll openInWorld; setExtentFromHalo: (self currentWorld width - 10) @ eventRoll height; top: self bottom; bottom: (eventRoll bottom min: self currentWorld bottom); left: self currentWorld left + 2] "presumably zero" ifTrue: [eventRoll comeToFront].! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! pausePlayback "Pause the playback. Sender responsible for setting state to #suspendedPlayback" eventRecorder pausePlayback. (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | skEd cancelOutOfPainting. ^ self rewind]. self borderColor: Color orange. self setProperty: #suspendedContentArea toValue: contentArea veryDeepCopy. self populateControlsPanel! ! !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! record "Commence event recording..." self currentWorld abandonAllHalos. self comeToFront. initialContentArea := contentArea veryDeepCopy. self forgetPriorPaintBoxSettings. initialPicture := contentArea imageForm. self state: #recording. self borderColor: Color red. self populateControlsPanel. self currentWorld doOneCycleNow. eventRecorder record! ! !EventRecordingSpace methodsFor: 'processing' stamp: 'ct 9/11/2020 19:45'! assureContentAreaStaysAt: aPoint "selbst-verst?????ndlich" self currentWorld doOneCycleNow. self topLeft: ((self topLeft - contentArea topLeft ) + aPoint)! ! !EventRecordingSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:46'! initializeFromPlaybackButton: anEventPlaybackButton "Initialize my content area, caption, and tape from a playback button." | soundEvent | initialContentArea := anEventPlaybackButton contentArea veryDeepCopy. eventRecorder tape: anEventPlaybackButton tape veryDeepCopy. eventRecorder caption: anEventPlaybackButton caption. soundEvent := eventRecorder tape detect: [:evt | evt type = #startSound] ifNone: [nil]. soundEvent ifNotNil: "For benefit of possible re-record of voiceover" [eventRecorder startSoundEvent: soundEvent]. initialPicture := anEventPlaybackButton initialPicture veryDeepCopy ifNil: [self inform: 'caution - old playback; button lacks vital data.' translated. ^ nil]. finalPicture := anEventPlaybackButton finalPicture veryDeepCopy. eventRecorder saved: true. self rewind. self center: self currentWorld center.! ! !EventPlaybackSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:45'! launchFrom: aButton "Initialize the receiver from an invoker button, and launch it." | where | self setProperty: #originatingButton toValue: aButton. self contentArea: aButton contentArea veryDeepCopy tape: aButton tape veryDeepCopy. self captionString: aButton caption. self rewind. autoStart := aButton autoStart. autoDismiss := aButton autoDismiss. "showChrome := aButton showChrome." where := aButton whereToAppear. self openInWorld. where = #screenCenter ifTrue: [self center: self currentWorld center]. where = #buttonPosition ifTrue: [self position: aButton position]. where = #containerOrigin ifTrue: [self position: aButton owner position]. self goHome. self addStopper. autoStart ifTrue: [self play]! ! !FlapTab methodsFor: 'globalness' stamp: 'ct 9/11/2020 19:47'! toggleIsGlobalFlap "Toggle whether the receiver is currently a global flap or not" | oldWorld | self hideFlap. oldWorld := self currentWorld. self isGlobalFlap ifTrue: [Flaps removeFromGlobalFlapTabList: self. oldWorld addMorphFront: self] ifFalse: [self delete. Flaps addGlobalFlap: self. self currentWorld addGlobalFlaps]. self currentWorld reformulateUpdatingMenus.! ! !GoldBoxMenu methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:41'! initializeFor: aScriptor "Answer a graphical menu to be put up in conjunction with the Gold Box" | aButton goldBox aReceiver boxBounds example toScale | scriptor := aScriptor. lastItemMousedOver := nil. self removeAllMorphs. self setProperty: #goldBox toValue: true. self listDirection: #topToBottom; hResizing: #spaceFill; extent: 1 at 1; vResizing: #spaceFill. "standard #newColumn stuff" self setNameTo: 'Gold Box' translated. self useRoundedCorners. self color: Color white. self borderColor: (Color r: 1.0 g: 0.839 b: 0.065). self hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4. { {ScriptingSystem. #yesNoComplexOfTiles. 'test' translated. 'Test/Yes/No panes for testing a condition.' translated}. {ScriptingSystem. #timesRepeatComplexOfTiles. 'repeat' translated. 'TimesRepeat panes for running a section of code repeatedly.' translated}. { ScriptingSystem. #randomNumberTile. 'random' translated. 'A tile that will produce a random number in a given range.' translated}. { ScriptingSystem. #seminalFunctionTile. 'function' translated. 'A tile representing a function call. Click on the function name or the arrows to change functions.' translated}. {ScriptingSystem. #buttonUpTile. 'button up?' translated. 'Reports whether the mouse button is up' translated}. {ScriptingSystem. #buttonDownTile. 'button down?' translated. 'Reports whether the mouse button is down' translated}. {ScriptingSystem. #randomColorTile. 'random color' translated. 'A tile returning a random color' translated}. {scriptor playerScripted. #tileToRefer. 'tile for me' translated. 'A tile representing the object being scripted' translated}. {self. #numericConstantTile. 'number' translated. 'A tile holding a plain number' translated}. } do: [:tuple | aReceiver := tuple first. example := aReceiver perform: tuple second. aButton := IconicButton new target: aReceiver. aButton borderWidth: 0; color: Color transparent. toScale := tuple size >= 5 ifTrue: [tuple first perform: tuple fifth] "bail-out for intractable images." ifFalse: [example imageForm]. aButton labelGraphic: (toScale copy scaledToHeight: 40). aButton actionSelector: #launchPartOffsetVia:label:. aButton arguments: {tuple second. tuple third}. (tuple size > 3 and: [tuple fourth isEmptyOrNil not]) ifTrue: [aButton setBalloonText: tuple fourth]. aButton actWhen: #buttonDown. aButton on: #mouseEnter send: #mousedOverEvent:button: to: self. aButton on: #click send: #delete to: self. self addMorphBack: aButton]. goldBox := aScriptor submorphs first submorphThat: [:m | (m isKindOf: SimpleButtonMorph) and: [m actionSelector == #offerGoldBoxMenu]] ifNone: [nil]. goldBox ifNil: [self position: self currentHand position] ifNotNil: [boxBounds := goldBox boundsInWorld. self center: boxBounds center. self left: (boxBounds center x - (self width // 2)). self top: boxBounds bottom]. lastItemMousedOver := nil. self on: #mouseLeave send: #mouseLeftMenuWithEvent: to: self. self on: #mouseLeaveDragging send: #delete to: self.! ! !GrabPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:41'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair.! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! doDirection: anEvent with: directionHandle "The mouse went down on the forward-direction halo handle; respond appropriately." anEvent hand obtainHalo: self. anEvent shiftPressed ifTrue: [directionArrowAnchor := (target point: target referencePosition in: self world) rounded. self positionDirectionShaft: directionHandle. self removeAllHandlesBut: directionHandle. directionHandle setProperty: #trackDirectionArrow toValue: true] ifFalse: [self currentHand spawnBalloonFor: directionHandle]! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:10'! maybeDismiss: evt with: dismissHandle "Ask hand to dismiss my target if mouse comes up in it." evt hand obtainHalo: self. (dismissHandle containsPoint: evt cursorPoint) ifFalse: [ self delete. target addHalo: evt] ifTrue: [ target resistsRemoval ifTrue: [(UIManager default chooseFrom: { 'Yes' translated. 'Um, no, let me reconsider' translated. } title: 'Really throw this away?' translated) = 1 ifFalse: [^ self]]. evt hand removeHalo. self delete. target dismissViaHalo. self currentWorld presenter flushPlayerListCache].! ! !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! prepareToTrackCenterOfRotation: evt with: rotationHandle "The mouse went down on the center of rotation." evt hand obtainHalo: self. evt shiftPressed ifTrue: [self removeAllHandlesBut: rotationHandle. rotationHandle setProperty: #trackCenterOfRotation toValue: true. evt hand showTemporaryCursor: Cursor blank] ifFalse: [self currentHand spawnBalloonFor: rotationHandle]! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/16/2020 18:24'! cursorPoint "Implemented for allowing embedded worlds in an event cycle to query a hand's position and get it in its coordinates. The same can be achieved by #point:from: but this is simply much more convenient since it will look as if the hand is in the lower world." ^ self currentWorld point: self position from: owner! ! !HandMorph methodsFor: 'event handling' stamp: 'ct 9/16/2020 19:47'! processEvents "Process user input events from the local input devices." | evt evtBuf type hadAny | self currentEvent ~= lastMouseEvent ifTrue: [ "Meaning that we were invoked from within an event response. Make sure z-order is up to date." self mouseOverHandler processMouseOver: lastMouseEvent]. hadAny := false. [(evtBuf := Sensor nextEvent) isNil] whileFalse: [evt := nil. "for unknown event types" type := evtBuf first. type = EventTypeMouse ifTrue: [evt := self generateMouseEvent: evtBuf]. type = EventTypeMouseWheel ifTrue: [evt := self generateMouseWheelEvent: evtBuf]. type = EventTypeKeyboard ifTrue: [evt := self generateKeyboardEvent: evtBuf]. type = EventTypeDragDropFiles ifTrue: [evt := self generateDropFilesEvent: evtBuf]. type = EventTypeWindow ifTrue:[evt := self generateWindowEvent: evtBuf]. "All other events are ignored" (type ~= EventTypeDragDropFiles and: [evt isNil]) ifTrue: [^self]. evt ifNotNil: ["Finally, handle it." self handleEvent: evt. hadAny := true. "For better user feedback, return immediately after a mouse event has been processed." evt isMouse ifTrue: [^ self]]]. "note: if we come here we didn't have any mouse events" mouseClickState ifNotNil: [ "No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: [ "No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent].! ! !HandMorph methodsFor: 'initialization' stamp: 'ct 9/16/2020 19:44'! becomeActiveDuring: aBlock "Make the receiver the active hand during the evaluation of aBlock." ^ ActiveHandVariable value: self during: aBlock! ! !HandMorphForReplay methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:30'! processEvents "Play back the next event" | evt hadMouse hadAny tracker | suspended == true ifTrue: [^ self]. hadMouse := hadAny := false. tracker := recorder objectTrackingEvents. [(evt := recorder nextEventToPlay) isNil] whileFalse: [ ((evt isMemberOf: MouseMoveEvent) and: [evt trail isNil]) ifTrue: [^ self]. tracker ifNotNil: [tracker currentEventTimeStamp: evt timeStamp]. evt type == #EOF ifTrue: [recorder pauseIn: self currentWorld. ^ self]. evt type == #startSound ifTrue: [recorder perhapsPlaySound: evt argument. recorder synchronize. ^ self]. evt type == #startEventPlayback ifTrue: [evt argument launchPlayback. recorder synchronize. ^ self]. evt type == #noteTheatreBounds ifTrue: ["The argument holds the content rect --for now we don't make any use of that info in this form." ^ self]. evt isMouse ifTrue: [hadMouse := true]. (evt isMouse or: [evt isKeyboard]) ifTrue: [self handleEvent: (evt setHand: self) resetHandlerFields. hadAny := true]]. (mouseClickState notNil and: [hadMouse not]) ifTrue: ["No mouse events during this cycle. Make sure click states time out accordingly" mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. hadAny ifFalse: ["No pending events. Make sure z-order is up to date" self mouseOverHandler processMouseOver: lastMouseEvent]! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:12'! destroyThread "Manually destroy the thread" (self confirm: ('Destroy thread <{1}> ?' translated format:{threadName})) ifFalse: [^ self]. self class knownThreads removeKey: threadName ifAbsent: []. self setProperty: #moribund toValue: true. "In case pointed to in some other project" self currentWorld keyboardNavigationHandler == self ifTrue: [self stopKeyboardNavigation]. self delete.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:13'! moreCommands "Put up a menu of options" | allThreads aMenu others target | allThreads := self class knownThreads. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: 'navigation' translated. Preferences noviceMode ifFalse:[ self flag: #deferred. "Probably don't want that stay-up item, not least because the navigation-keystroke stuff is not dynamically handled" aMenu addStayUpItem ]. others := (allThreads keys reject: [ :each | each = threadName]) asArray sort. others do: [ :each | aMenu add: ('switch to <{1}>' translated format:{each}) selector: #switchToThread: argument: each ]. aMenu addList: { {'switch to recent projects' translated. #getRecentThread}. #-. {'create a new thread' translated. #threadOfNoProjects}. {'edit this thread' translated. #editThisThread}. {'create thread of all projects' translated. #threadOfAllProjects}. #-. {'First project in thread' translated. #firstPage}. {'Last project in thread' translated. #lastPage} }. (target := self currentIndex + 2) > listOfPages size ifFalse: [ aMenu add: ('skip over next project ({1})' translated format:{(listOfPages at: target - 1) first}) action: #skipOverNext ]. aMenu addList: { {'jump within this thread' translated. #jumpWithinThread}. {'insert new project' translated. #insertNewProject}. #-. {'simply close this navigator' translated. #delete}. {'destroy this thread' translated. #destroyThread}. #- }. (self currentWorld keyboardNavigationHandler == self) ifFalse:[ aMenu add: 'start keyboard navigation with this thread' translated action: #startKeyboardNavigation ] ifTrue: [ aMenu add: 'stop keyboard navigation with this thread' translated action: #stopKeyboardNavigation ]. aMenu popUpInWorld.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! positionAppropriately | others world otherRects overlaps bottomRight | (self ownerThatIsA: HandMorph) ifNotNil: [^self]. others := (world := Project currentWorld) submorphs select: [ :each | each ~~ self and: [each isKindOf: self class]]. otherRects := others collect: [ :each | each bounds]. bottomRight := (world hasProperty: #threadNavigatorPosition) ifTrue: [world valueOfProperty: #threadNavigatorPosition] ifFalse: [world bottomRight]. self align: self fullBounds bottomRight with: bottomRight. self setProperty: #previousWorldBounds toValue: self world bounds. [ overlaps := false. otherRects do: [ :r | (r intersects: bounds) ifTrue: [overlaps := true. self bottom: r top]. ]. self top < self world top ifTrue: [ self bottom: bottomRight y. self right: self left - 1. ]. overlaps ] whileTrue.! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! startKeyboardNavigation "Tell the active world to starting navigating via desktop keyboard navigation via me" self currentWorld keyboardNavigationHandler: self! ! !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:15'! stopKeyboardNavigation "Cease navigating via the receiver in response to desktop keystrokes" self currentWorld removeProperty: #keyboardNavigationHandler! ! !InternalThreadNavigationMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:13'! loadPageWithProgress "Load the desired page, showing a progress indicator as we go" | projectInfo projectName beSpaceHandler | projectInfo := listOfPages at: currentIndex. projectName := projectInfo first. loadedProject := Project named: projectName. self class know: listOfPages as: threadName. beSpaceHandler := (Project current world keyboardNavigationHandler == self). self currentWorld addDeferredUIMessage: [InternalThreadNavigationMorph openThreadNamed: threadName atIndex: currentIndex beKeyboardHandler: beSpaceHandler]. loadedProject ifNil: [ ComplexProgressIndicator new targetMorph: self; historyCategory: 'project loading' translated; withProgressDo: [ [ loadedProject := Project current fromMyServerLoad: projectName ] on: ProjectViewOpenNotification do: [ :ex | ex resume: false] "we probably don't want a project view morph in this case" ]. ]. loadedProject ifNil: [ ^self inform: 'I cannot find that project' translated ]. self delete. loadedProject enter.! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! resetBottomRightPosition self currentWorld removeProperty: #threadNavigatorPosition. ! ! !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! setBottomRightPosition self currentWorld setProperty: #threadNavigatorPosition toValue: self bottomRight. ! ! !LassoPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:42'! justTornOffFromPartsBin super justTornOffFromPartsBin. self image: (Form extent: 0 @ 0). "hide the icon" self currentHand showTemporaryCursor: Cursor crossHair! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! play "Play the movie, as it were." tape ifNil: [^ self]. tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! record "Commence recording or re-recording." tapeStream := WriteStream on: (Array new: 10000). self resumeRecordIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! resumePlayingWithoutPassingStop "Like play, but avoids the stop step that does more than we'd like." tapeStream := ReadStream on: tape. self resumePlayIn: self currentWorld. ! ! !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/12/2020 14:24'! stop "Stop recording or playing." tapeStream ifNotNil: [(#(recording recordingWithSound) includes: self state) ifTrue: [tape := tapeStream contents. saved := false]]. self terminateVoiceRecording. "In case doing" journalFile ifNotNil: [journalFile close]. self pauseIn: self currentWorld. tapeStream := nil. self state: #atEndOfPlayback. recordingSpace abandonReplayHandsAndHalos. recordMeter ifNotNil: [recordMeter width: 1].! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:43'! popUpEvent: evt in: aWorld "Present this menu in response to the given event." | aHand aPosition | aHand := evt ifNotNil: [evt hand] ifNil: [self currentHand]. aPosition := aHand position truncated. ^ self popUpAt: aPosition forHand: aHand in: aWorld! ! !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:23'! popUpNoKeyboard "Present this menu in the current World, *not* allowing keyboard input into the menu" ^ self popUpAt: self currentHand position forHand: self currentHand in: self currentWorld allowKeyboard: false! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title world | title := self allMorphs detect: [ :ea | ea hasProperty: #titleString ]. title := title submorphs first. self visible: false. world := self currentWorld. aBlock value: [:string| self visible ifFalse:[ world addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: self currentHand cursorPoint hangOut: false. self changed. world displayWorld "show myself"]. self delete. world displayWorld.! ! !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! invokeModal: allowKeyboardControl "Invoke this menu and don't return until the user has chosen a value. If the allowKeyboarControl boolean is true, permit keyboard control of the menu" ^ self invokeModalAt: self currentHand position in: self currentWorld allowKeyboard: allowKeyboardControl! ! !MenuMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:43'! positionAt: aPoint relativeTo: aMenuItem inWorld: aWorld "Note: items may not be laid out yet (I found them all to be at 0 at 0), so we have to add up heights of items above the selected item." | i yOffset sub delta | self fullBounds. "force layout" i := 0. yOffset := 0. [(sub := self submorphs at: (i := i + 1)) == aMenuItem] whileFalse: [yOffset := yOffset + sub height]. self position: aPoint - (2 @ (yOffset + 8)). "If it doesn't fit, show it to the left, not to the right of the hand." self right > aWorld worldBounds right ifTrue: [self right: aPoint x + 1]. "Make sure that the menu fits in the world." delta := self bounds amountToTranslateWithin: (aWorld worldBounds withHeight: ((aWorld worldBounds height - 18) max: (self currentHand position y) + 1)). delta isZero ifFalse: [self position: self position + delta].! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:17'! displayAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." Smalltalk isMorphic ifFalse: [^ self]. [self currentWorld addMorph: self centeredNear: aPoint. self world displayWorld. "show myself" aBlock value] ensure: [self delete]! ! !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:18'! informUserAt: aPoint during: aBlock "Add this menu to the Morphic world during the execution of the given block." | title w | Smalltalk isMorphic ifFalse: [^ self]. title := self allMorphs detect: [:ea | ea hasProperty: #titleString]. title := title submorphs first. self visible: false. w := self currentWorld. aBlock value: [:string| self visible ifFalse: [ w addMorph: self centeredNear: aPoint. self visible: true]. title contents: string. self setConstrainedPosition: Sensor cursorPoint hangOut: false. self changed. w displayWorld "show myself" ]. self delete. w displayWorld.! ! !Morph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:18'! fromFileName: fullName "Reconstitute a Morph from the file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | aFileStream morphOrList | aFileStream := (MultiByteBinaryOrTextStream with: ((FileStream readOnlyFileNamed: fullName) binary contentsOfEntireFile)) binary reset. morphOrList := aFileStream fileInObjectAndCode. (morphOrList isKindOf: SqueakPage) ifTrue: [morphOrList := morphOrList contentsMorph]. Smalltalk isMorphic ifTrue: [Project current world addMorphsAndModel: morphOrList] ifFalse: [morphOrList isMorph ifFalse: [self inform: 'Can only load a single morph into an mvc project via this mechanism.' translated]. morphOrList openInWorld]! ! !AllPlayersTool class methodsFor: '*Etoys-Squeakland-parts bin' stamp: 'ct 9/12/2020 14:26'! allPlayersToolForActiveWorld "Launch an AllPlayersTool to view the scripted objects of the active world" | aTool | aTool := self newStandAlone. aTool center: self currentWorld center. ^ aTool " AllPlayersTool allPlayersToolForActiveWorld "! ! !AllScriptsTool class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:26'! allScriptsToolForActiveWorld "Launch an AllScriptsTool to view scripts of the active world" | aTool | aTool := self newColumn. aTool initializeFor: self currentWorld presenter. ^ aTool! ! !AnonymousSoundMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:26'! fromFileName: fullName "Create an instance of the receiver from the given file path." | newPlayer aSound ext aName | newPlayer := self new initialize. ('*aif*' match: fullName) ifTrue: [aSound := SampledSound fromAIFFfileNamed: fullName]. ('*wav' match: fullName) ifTrue: [aSound := SampledSound fromWaveFileNamed: fullName]. newPlayer := self new. ext := FileDirectory extensionFor: fullName. aName := (FileDirectory on: fullName) pathParts last. ext size > 0 ifTrue: [aName := aName copyFrom: 1 to: (aName size - (ext size + 1))]. newPlayer sound: aSound interimName: aName. newPlayer openInWorld; position: self currentWorld center.! ! !BookMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:27'! openFromFile: fullName "Reconstitute a Morph from the selected file, presumed to be represent a Morph saved via the SmartRefStream mechanism, and open it in an appropriate Morphic world" | book aFileStream | Smalltalk verifyMorphicAvailability ifFalse: [^ self]. aFileStream := FileStream readOnlyFileNamed: fullName. book := BookMorph new. book setProperty: #url toValue: aFileStream url. book fromRemoteStream: aFileStream. aFileStream close. Smalltalk isMorphic ifTrue: [self currentWorld addMorphsAndModel: book] ifFalse: [book isMorph ifFalse: [^self inform: 'Can only load a single morph\into an mvc project via this mechanism.' withCRs translated]. book openInWorld]. book goToPage: 1! ! !EventRecordingSpace class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:46'! openFromPlaybackButton: aButton "Open an EventRecordingSpace derived from a playback button. The primary reason for doing this would be to re-record voiceover." | aSpace | aSpace := EventRecordingSpace new. aSpace initializeFromPlaybackButton: aButton. aSpace center: self currentWorld center. aSpace openInWorld! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?'" ^ self request: queryString initialAnswer: '' centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! request: queryString initialAnswer: defaultAnswer "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer centerAt: aPoint "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels. This variant is only for calling from within a Morphic project." "FillInTheBlankMorph request: 'Type something, then type CR.' initialAnswer: 'yo ho ho!!' centerAt: Display center" ^ self request: queryString initialAnswer: defaultAnswer centerAt: aPoint inWorld: self currentWorld! ! !FillInTheBlankMorph class methodsFor: '*Etoys-Squeakland-instance creation' stamp: 'ct 9/11/2020 19:47'! request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." "FillInTheBlankMorph request: 'What is your favorite color?' initialAnswer: 'red, no blue. Ahhh!!'" ^ self request: queryString initialAnswer: defaultAnswer centerAt: self currentHand cursorPoint inWorld: self currentWorld onCancelReturn: cancelResponse! ! !HandMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:11'! showEvents: aBool "HandMorph showEvents: true" "HandMorph showEvents: false" ShowEvents := aBool. aBool ifFalse: [ Project current world invalidRect: (0 at 0 extent: 250 at 120)].! ! !InternalThreadNavigationMorph class methodsFor: 'known threads' stamp: 'ct 9/11/2020 20:15'! openThreadNamed: nameOfThread atIndex: anInteger beKeyboardHandler: aBoolean "Activate the thread of the given name, from the given index; set it up to be navigated via desktop keys if indicated" | coll nav | coll := self knownThreads at: nameOfThread ifAbsent: [^self]. nav := Project current world submorphThat: [ :each | (each isKindOf: self) and: [each threadName = nameOfThread]] ifNone: [nav := self basicNew. nav listOfPages: coll; threadName: nameOfThread index: anInteger; initialize; openInWorld; positionAppropriately. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav]. ^ self]. nav listOfPages: coll; threadName: nameOfThread index: anInteger; removeAllMorphs; addButtons. aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav].! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! chooseFrom: aList lines: linesArray title: queryString "Choose an item from the given list. Answer the index of the selected item." | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString. 1 to: aList size do: [:i| menu add: (aList at: i) asString target: aBlock selector: #value: argument: i. (linesArray includes: i) ifTrue:[menu addLine]]. MenuIcons decorateMenu: menu. result := 0. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! confirm: queryString trueChoice: trueChoice falseChoice: falseChoice "Put up a yes/no menu with caption queryString. The actual wording for the two choices will be as provided in the trueChoice and falseChoice parameters. Answer true if the response is the true-choice, false if it's the false-choice. This is a modal question -- the user must respond one way or the other." "MenuMorph confirm: 'Are you hungry?' trueChoice: 'yes, I''m famished' falseChoice: 'no, I just ate'" | menu aBlock result | aBlock := [:v | result := v]. menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: trueChoice target: aBlock selector: #value: argument: true. menu add: falseChoice target: aBlock selector: #value: argument: false. MenuIcons decorateMenu: menu. [menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true. result == nil] whileTrue. ^ result! ! !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:22'! inform: queryString "MenuMorph inform: 'I like Squeak'" | menu | menu := self new. menu addTitle: queryString icon: MenuIcons confirmIcon. menu add: 'OK' translated target: self selector: #yourself. MenuIcons decorateMenu: menu. menu invokeAt: self currentHand position in: self currentWorld allowKeyboard: true.! ! !MorphHierarchy class methodsFor: 'opening' stamp: 'ct 9/12/2020 14:45'! openOrDelete | oldMorph | oldMorph := Project current world submorphs detect: [:each | each hasProperty: #morphHierarchy] ifNone: [| newMorph | newMorph := self new asMorph. newMorph bottomLeft: self currentHand position. newMorph openInWorld. newMorph isFullOnScreen ifFalse: [newMorph goHome]. ^ self]. "" oldMorph delete! ! !MorphWorldController methodsFor: 'basic control sequence' stamp: 'ct 9/16/2020 19:42'! controlTerminate "This window is becoming inactive; restore the normal cursor." Cursor normal show. super controlTerminate.! ! !MorphicEvent methodsFor: 'initialize' stamp: 'ct 9/16/2020 19:43'! becomeActiveDuring: aBlock "Make the receiver the active event during the evaluation of aBlock." ^ ActiveEventVariable value: self during: aBlock! ! !MultiWindowLabelButtonMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:16'! performAction "Override to interpret the actionSelector as a menu accessor and to activate that menu." actionSelector ifNil: [^ self]- (model perform: actionSelector) ifNotNil: [:menu | menu invokeModalAt: self position - (0 at 5) in: self currentWorld allowKeyboard: Preferences menuKeyboardControl].! ! !NativeImageSegment methodsFor: 'read/write segment' stamp: 'ct 9/12/2020 14:15'! smartFillRoots: dummy | refs known ours ww blockers | "Put all traced objects into my arrayOfRoots. Remove some that want to be in outPointers. Return blockers, an IdentityDictionary of objects to replace in outPointers." blockers := dummy blockers. known := (refs := dummy references) size. refs keys do: [:obj | "copy keys to be OK with removing items" (obj isSymbol) ifTrue: [refs removeKey: obj. known := known-1]. (obj class == PasteUpMorph) ifTrue: [ obj isWorldMorph & (obj owner == nil) ifTrue: [ (dummy project ~~ nil and: [obj == dummy project world]) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: 'The worldMorph of a different world')]]]. "Make a ProjectViewMorph here" "obj class == Project ifTrue: [Transcript show: obj; cr]." (blockers includesKey: obj) ifTrue: [ refs removeKey: obj ifAbsent: [known := known+1]. known := known-1]. ]. ours := (dummy project ifNil: [Project current]) world. refs keysDo: [:obj | obj isMorph ifTrue: [ ww := obj world. (ww == ours) | (ww == nil) ifFalse: [ refs removeKey: obj. known := known-1. blockers at: obj put: (StringMorph contents: obj printString, ' from another world')]]]. "keep original roots on the front of the list" dummy rootObject do: [:rr | refs removeKey: rr ifAbsent: []]. (self respondsTo: #classOrganizersBeRoots:) ifTrue: "an EToys extension" [self classOrganizersBeRoots: dummy]. ^dummy rootObject, refs keys asArray! ! !NebraskaSenderMorph methodsFor: 'parts bin' stamp: 'ct 9/12/2020 14:14'! initializeToStandAlone super initializeToStandAlone. self installModelIn: Project current world.! ! !NebraskaServerMorph class methodsFor: 'as yet unclassified' stamp: 'ct 9/12/2020 14:14'! serveWorld ^ self serveWorld: self currentWorld ! ! !NewVariableDialogMorph methodsFor: 'build' stamp: 'ct 9/12/2020 14:14'! rebuild | buttonColor itsName enableDecimalPlaces | self removeAllMorphs. self addAColumn: { self lockedString: self title. }. self addSeparator. self addARow: { self inAColumn: { (self addARow: { self lockedString: 'Name:' translated. self spacer. varNameText := self newTextMorph contentsWrapped: self varName; selectAll; crAction: (MessageSend receiver: self selector: #doAccept); yourself }) cellPositioning: #center. self inAColumn: { (self addARow: { self lockedString: 'Type:' translated. self spacer. varTypeButton := self buildVarTypeButton }) cellPositioning: #center. } named: #varType. } }. self currentHand newKeyboardFocus: varNameText. self addSeparator. self addDecimalPlaces. enableDecimalPlaces := false. (#(#Number #Point) includes: self varType) ifTrue: [ enableDecimalPlaces := true]. self allMorphsDo: [ :each | itsName := each knownName. (#(decimalPlaces) includes: itsName) ifTrue: [self enable: each when: enableDecimalPlaces]]. buttonColor := self color lighter. self addARow: { self inAColumn: { (self addARow: { self buttonNamed: 'Accept' translated action: #doAccept color: buttonColor help: 'keep changes made and close panel' translated. self buttonNamed: 'Cancel' translated action: #doCancel color: buttonColor help: 'cancel changes made and close panel' translated. }) listCentering: #center } }! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! step "Let all views know that some of my objects need to be updated." self monitorList do: [ :object | object ifNotNil: [self changed: #objectChanged with: object]]. self monitorList ifEmpty: [ self world stopStepping: self selector: #step ].! ! !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! world ^ Project current world! ! !ObjectExplorer methodsFor: 'accessing - view' stamp: 'ct 9/12/2020 14:12'! views ^ self findDeepSubmorphsIn: self world that: [:morph | morph modelOrNil = self]! ! !ObjectsTool methodsFor: 'search' stamp: 'ct 9/12/2020 14:45'! showSearchPane "Set the receiver up so that it shows the search pane" | tabsPane aPane | modeSymbol == #search ifTrue: [ ^self ]. self partsBin removeAllMorphs. tabsPane := self tabsPane. aPane := self newSearchPane. self replaceSubmorph: tabsPane by: aPane. self modeSymbol: #search. self showMorphsMatchingSearchString. self currentHand newKeyboardFocus: aPane! ! !ParagraphEditor methodsFor: 'nonediting/nontyping keys' stamp: 'ct 9/12/2020 14:12'! escapeToDesktop: characterStream "Pop up a morph to field keyboard input in the context of the desktop" Smalltalk isMorphic ifTrue: [ Project current world putUpWorldMenuFromEscapeKey]. ^ true! ! !ParagraphEditor methodsFor: '*Etoys-Squeakland-editing keys' stamp: 'ct 9/12/2020 14:07'! shiftEnclose: characterStream "Insert or remove bracket characters around the current selection. Flushes typeahead." | char left right startIndex stopIndex oldSelection which text | char := sensor keyboard. char = $9 ifTrue: [ char := $( ]. char = $, ifTrue: "[ char := $< ]" [self closeTypeIn. Project current world showSourceKeyHit. ^ true]. char = $[ ifTrue: [ char := ${ ]. char = $' ifTrue: [ char := $" ]. char asciiValue = 27 ifTrue: [ char := ${ ]. "ctrl-[" self closeTypeIn. startIndex := self startIndex. stopIndex := self stopIndex. oldSelection := self selection. which := '([<{"''' indexOf: char ifAbsent: [1]. left := '([<{"''' at: which. right := ')]>}"''' at: which. text := paragraph text. ((startIndex > 1 and: [stopIndex <= text size]) and: [(text at: startIndex-1) = left and: [(text at: stopIndex) = right]]) ifTrue: ["already enclosed; strip off brackets" self selectFrom: startIndex-1 to: stopIndex. self replaceSelectionWith: oldSelection] ifFalse: ["not enclosed; enclose by matching brackets" self replaceSelectionWith: (Text string: (String with: left), oldSelection string ,(String with: right) emphasis: emphasisHere). self selectFrom: startIndex+1 to: stopIndex]. ^true! ! !PasteUpMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:10'! flapTab "Answer the tab affilitated with the receiver. Normally every flap tab is expected to have a PasteUpMorph which serves as its 'referent.'" | ww | self isFlap ifFalse: [^ nil]. ww := self presenter associatedMorph ifNil: [self]. ^ ww flapTabs detect: [:any| any referent == self] ifNone: [nil]! ! !PasteUpMorph methodsFor: 'events-processing' stamp: 'ct 9/16/2020 18:26'! processEvent: anEvent using: defaultDispatcher "Reimplemented to install the receiver as the new active world if it is one" self isWorldMorph ifFalse: [ ^ super processEvent: anEvent using: defaultDispatcher]. ^ self becomeActiveDuring: [ super processEvent: anEvent using: defaultDispatcher]! ! !PasteUpMorph methodsFor: 'flaps' stamp: 'ct 9/12/2020 14:11'! correspondingFlapTab "If there is a flap tab whose referent is me, return it, else return nil. Will also work for flaps on the edge of embedded subareas such as within scripting-areas, but more slowly." self currentWorld flapTabs do: [:aTab | aTab referent == self ifTrue: [^ aTab]]. "Catch guys in embedded worldlets" self currentWorld allMorphs do: [:aTab | ((aTab isKindOf: FlapTab) and: [aTab referent == self]) ifTrue: [^ aTab]]. ^ nil! ! !PasteUpMorph methodsFor: 'initialization' stamp: 'ct 9/16/2020 19:44'! becomeActiveDuring: aBlock "Make the receiver the active world during the evaluation of aBlock." ^ ActiveWorldVariable value: self during: aBlock! ! !PasteUpMorph methodsFor: 'menu & halo' stamp: 'ct 9/12/2020 14:07'! putUpPenTrailsSubmenu "Put up the pen trails menu" | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'pen trails' translated. aMenu addStayUpItem. self addPenTrailsMenuItemsTo: aMenu. ^ aMenu popUpInWorld: self! ! !PasteUpMorph methodsFor: 'structure' stamp: 'ct 9/16/2020 19:39'! primaryHand self == self currentWorld ifFalse: [ ^ super primaryHand]. ^ self hands at: 1 ifAbsent: [nil]! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/12/2020 14:45'! extractScreenRegion: poly andPutSketchInHand: hand "The user has specified a polygonal area of the Display. Now capture the pixels from that region, and put in the hand as a Sketch." | screenForm outline topLeft innerForm exterior | outline := poly shadowForm. topLeft := outline offset. exterior := (outline offset: 0 at 0) anyShapeFill reverse. screenForm := Form fromDisplay: (topLeft extent: outline extent). screenForm eraseShape: exterior. innerForm := screenForm trimBordersOfColor: Color transparent. self currentHand showTemporaryCursor: nil. innerForm isAllWhite ifFalse: [hand attachMorph: (self drawingClass withForm: innerForm)]! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 20:07'! initializeDesktopCommandKeySelectors "Provide the starting settings for desktop command key selectors. Answer the dictionary." "ActiveWorld initializeDesktopCommandKeySelectors" | dict | dict := IdentityDictionary new. self defaultDesktopCommandKeyTriplets do: [:trip | | messageSend | messageSend := MessageSend receiver: trip second selector: trip third. dict at: trip first put: messageSend]. self setProperty: #commandKeySelectors toValue: dict. ^ dict! ! !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 18:00'! putUpWorldMenuFromEscapeKey Preferences noviceMode ifFalse: [self putUpWorldMenu: self currentEvent]! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/16/2020 19:45'! install owner := nil. "since we may have been inside another world previously" submorphs do: [:ss | ss owner isNil ifTrue: [ss privateOwner: self]]. "Transcript that was in outPointers and then got deleted." self viewBox: Display boundingBox. EventSensor default flushEvents. worldState handsDo: [:h | h initForEvents]. self installFlaps. self borderWidth: 0. "default" (Preferences showSecurityStatus and: [SecurityManager default isInRestrictedMode]) ifTrue: [self borderWidth: 2; borderColor: Color red]. self presenter allExtantPlayers do: [:player | player prepareToBeRunning]. SystemWindow noteTopWindowIn: self.! ! !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/12/2020 14:06'! repositionFlapsAfterScreenSizeChange "Reposition flaps after screen size change" (Flaps globalFlapTabsIfAny, self localFlapTabs) do: [:aFlapTab | aFlapTab applyEdgeFractionWithin: self bounds]. Flaps doAutomaticLayoutOfFlapsIfAppropriate! ! !PasteUpMorph methodsFor: '*Tools' stamp: 'ct 9/11/2020 20:07'! defaultDesktopCommandKeyTriplets "Answer a list of triplets of the form [+ optional fourth element, a for use in desktop-command-key-help] that will provide the default desktop command key handlers. If the selector takes an argument, that argument will be the command-key event" "World initializeDesktopCommandKeySelectors" | noviceKeys expertKeys | noviceKeys := { {$o. self. #activateObjectsTool. 'Activate the "Objects Tool"' translated}. {$r. self. #restoreMorphicDisplay. 'Redraw the screen' translated}. {$z. self. #undoOrRedoCommand. 'Undo or redo the last undoable command' translated}. {$F. Project current. #toggleFlapsSuppressed. 'Toggle the display of flaps' translated}. {$N. self. #toggleClassicNavigatorIfAppropriate. 'Show/Hide the classic Navigator, if appropriate' translated}. {$M. self. #toggleShowWorldMainDockingBar. 'Show/Hide the Main Docking Bar' translated}. {$]. Smalltalk. #saveSession. 'Save the image.' translated}. }. Preferences noviceMode ifTrue: [^ noviceKeys]. expertKeys := { {$b. SystemBrowser. #defaultOpenBrowser. 'Open a new System Browser' translated}. {$k. Workspace. #open. 'Open a new Workspace' translated}. {$m. self. #putUpNewMorphMenu. 'Put up the "New Morph" menu' translated}. {$O. self. #findAMonticelloBrowser. 'Bring a Monticello window into focus.' translated}. {$t. self. #findATranscript:. 'Make a System Transcript visible' translated}. {$w. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {Character escape. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. {$C. self. #findAChangeSorter:. 'Make a Change Sorter visible' translated}. {$L. self. #findAFileList:. 'Make a File List visible' translated}. {$P. self. #findAPreferencesPanel:. 'Activate the Preferences tool' translated}. {$R. Utilities. #browseRecentSubmissions. 'Make a Recent Submissions browser visible' translated}. {$W. self. #findAMessageNamesWindow:. 'Make a MessageNames tool visible' translated}. {$Z. ChangeList. #browseRecentLog. 'Browse recently-logged changes' translated}. {$\. SystemWindow. #sendTopWindowToBack. 'Send the top window to the back' translated}. {$_. Smalltalk. #quitPrimitive. 'Quit the image immediately.' translated}. {$-. Preferences. #decreaseFontSize. 'Decrease all font sizes' translated}. {$+. Preferences. #increaseFontSize. 'Increase all font sizes' translated}. }. ^ noviceKeys, expertKeys! ! !PasteUpMorph methodsFor: '*Etoys-playfield' stamp: 'ct 9/12/2020 14:10'! galleryOfPlayers "Put up a tool showing all the players in the project" (Project current world findA: AllPlayersTool) ifNotNil: [:aTool | ^ aTool comeToFront]. AllPlayersTool newStandAlone openInHand "ActiveWorld galleryOfPlayers"! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:36'! attemptCleanupReporting: whetherToReport "Try to fix up some bad things that are known to occur in some etoy projects we've seen. If the whetherToReport parameter is true, an informer is presented after the cleanups" | fixes faultyStatusControls | fixes := 0. self world ifNotNil: [:world | world submorphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m submorphs isEmpty]] thenDo: [:m | m delete. fixes := fixes + 1]]. TransformationMorph allSubInstancesDo: [:m | (m player notNil and: [m renderedMorph ~~ m]) ifTrue: [m renderedMorph visible ifFalse: [m renderedMorph visible: true. fixes := fixes + 1]]]. (Player class allSubInstances select: [:cl | cl isUniClass and: [cl instanceCount > 0]]) do: [:aUniclass | fixes := fixes + aUniclass cleanseScripts]. self presenter flushPlayerListCache; allExtantPlayers. faultyStatusControls := ScriptStatusControl allInstances select: [:m |m fixUpScriptInstantiation]. fixes := fixes + faultyStatusControls size. ScriptNameTile allInstancesDo: [:aTile | aTile submorphs isEmpty ifTrue: [aTile setLiteral: aTile literal. fixes := fixes + 1]]. whetherToReport ifTrue: [self inform: ('{1} [or more] repair(s) made' translated format: {fixes printString})] ifFalse: [fixes > 0 ifTrue: [Transcript cr; show: fixes printString, ' repairs made to existing content.']] " ActiveWorld attemptCleanupReporting: true. ActiveWorld attemptCleanupReporting: false. "! ! !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:09'! hideAllPlayers "Remove all Viewers belonging to scripted players associated with the receiver or any of its subjects from the screen." | a | a := OrderedCollection new. self allMorphsDo: [ :x | (self presenter currentlyViewing: x player) ifTrue: [a add: x player viewerFlapTab]]. a do: [ :each | each dismissViaHalo].! ! !PasteUpMorph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:08'! modernizeBJProject "Prepare a kids' project from the BJ fork of September 2000 -- a once-off thing for converting such projects forward to a modern 3.1a image, in July 2001. Except for the #enableOnlyGlobalFlapsWithIDs: call, this could conceivably be called upon reloading *any* project, just for safety." "ActiveWorld modernizeBJProject" self flag: #deprecate "ct: No senders". ScriptEditorMorph allInstancesDo: [:m | m userScriptObject]. Flaps enableOnlyGlobalFlapsWithIDs: {'Supplies' translated}. self abandonOldReferenceScheme. self relaunchAllViewers.! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:11'! abandonUnsituatedPlayers "If any objects in the project have references, in player-valued variables, to other objects otherwise not present in the project, abandon them and replace former references to them by references to Dot" | aList dot slotInfo varName ref allPlayers count | count := 0. allPlayers := self presenter reallyAllExtantPlayersNoSort. aList := allPlayers select: [:m | m belongsToUniClass]. dot := self presenter standardPlayer. aList do: [:p | p class slotInfo associationsDo: [:assoc | slotInfo := assoc value. varName := assoc key. (slotInfo type = #Player) ifTrue: [ref := p instVarNamed: varName. (allPlayers includes: ref) ifFalse: [p instVarNamed: varName put: dot. count := count + 1. Transcript cr; show: ('Variable named "{1}" in player named "{2}" changed to point to Dot' translated format: {varName. ref externalName})]]]]. aList := nil. "Increases chance of the next line having desired effect." self inform: ('{1} item(s) fixed up' translated format: {count}). WorldState addDeferredUIMessage: [Smalltalk garbageCollect]! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/12/2020 14:07'! putUpShowSourceMenu: evt title: aTitle "Put up a menu in response to the show-source button being hit" | menu | self bringTopmostsToFront. "put up the show-source menu" menu := (TheWorldMenu new adaptToWorld: self) buildShowSourceMenu. menu addTitle: aTitle. menu popUpEvent: evt in: self. ^ menu! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/11/2020 18:01'! showSourceKeyHit "The user hit the 'show source' key on the XO. Our current take on this is simply to put up the world menu..." ^ self putUpShowSourceMenu: self currentEvent title: 'etoys source' translated! ! !PasteUpMorph methodsFor: '*Etoys-Squeakland-menus' stamp: 'ct 9/12/2020 14:46'! presentDesktopColorMenu "Present the menu that governs the fill style of the squeak desktop." | aMenu | aMenu := MenuMorph new defaultTarget: self. aMenu title: 'desktop color' translated. self fillStyle addFillStyleMenuItems: aMenu hand: self currentHand from: self. aMenu addLine. aMenu add: 'solid fill' translated action: #useSolidFill. aMenu add: 'gradient fill' translated action: #useGradientFill. aMenu add: 'bitmap fill' translated action: #useBitmapFill. aMenu add: 'default fill' translated action: #useDefaultFill. ^ aMenu popUpInWorld! ! !PasteUpMorph methodsFor: 'as yet unclassified' stamp: 'ct 9/24/2020 13:50'! activeHand self == self currentWorld ifFalse: [ ^ super activeHand]. ^ worldState primaryHand! ! !EventTimeline methodsFor: 'dropping/grabbing' stamp: 'ct 9/11/2020 19:47'! acceptDroppingMorph: aMorph event: evt "Accept the drop of a morph." | aRect anEventRoll itsDuration itsWidthAfterDrop | self flag: #deferred. "This is a possible place for discovering whether the drop would have damaging effects on the mouse track..." (aMorph isKindOf: MouseEventSequenceMorph) ifTrue: [itsDuration := aMorph durationInMilliseconds. itsWidthAfterDrop := itsDuration // self eventRoll millisecondsPerPixel. super acceptDroppingMorph: aMorph event: evt. aMorph bounds: ((aMorph left @ 6) extent: (itsWidthAfterDrop @ aMorph height)). submorphs do: [:m | ((m ~~ aMorph) and: [m isKindOf: MouseEventSequenceMorph]) ifTrue: [(m bounds intersects: aMorph bounds) ifTrue: ["Eureka" aMorph delete. aMorph position: 100 at 100. aMorph openInWorld. aMorph flash. ^ self]]]] ifFalse: [super acceptDroppingMorph: aMorph event: evt] . aRect := (((aMorph left + 10) max: 10) @ 0) extent: 100@ 10. (anEventRoll := self eventRoll) pushChangesBackToEventTheatre. "Note that will ultimately result in replacement of the receiver by a new timeline" aMorph delete. self currentWorld abandonAllHalos. anEventRoll scrollPaneForRoll scrollHorizontallyToShow: aRect! ! !PasteUpMorph class methodsFor: '*Etoys-Squeakland-eToys-scripting' stamp: 'ct 9/12/2020 14:09'! putativeAdditionsToViewerCategoryPlayfieldOptions "Answer playfield options additions. Some of these are not yet underpinned by code in the current image; these will follow in due course." self flag: #deprecate. "ct: No senders" ^ #(#'playfield options' ( (command roundUpStrays 'Bring back all objects whose current coordinates keep them from being visible, so that at least a portion of each of my interior objects can be seen.') (command makeFitContents 'Adjust my bounds so that I fit precisely around all the objects within me') (command showAllPlayers 'Make visible the viewers for all players which have user-written scripts in this playfield.') (command hideAllPlayers 'Make invisible the viewers for all players in this playfield. This will save space before you publish this project') (command shuffleSubmorphs 'Rearranges my contents in random order') (command showAllObjectNames 'show names beneath all the objects currently in my interior, except for those for which showing such names is inappropriate.') (command hideAllObjectNames 'stop showing names beneath all the objects of my interior, If any of them is marked to "always show name", remove that designation')))! ! !PartsBin class methodsFor: '*Etoys-Squeakland-thumbnail cache' stamp: 'ct 9/12/2020 14:11'! rebuildIconsWithProgress "Put up an eye-catching progress morph while doing a complete rebuild of all the parts icons in the system." | fixBlock | fixBlock := Project current displayProgressWithJump: 'Building icons' translated. self clearThumbnailCache. self cacheAllThumbnails. fixBlock value. Project current world fullRepaintNeeded.! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: ((submorphs at: (2 max: submorphs size)) bottomRight + (2 at 1))). "inHotZone := evt ifNil: [true] ifNotNil: [rect containsPoint: evt cursorPoint]." aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/11/2020 20:47'! createMultipleTestScripts: aCount "Simulate the action of dropping a copy of the receiver to launch a new script -- for performance testing. To use: Open an Inspector on some tile command in a Viewer, e.g. on 'Car forward 5'. In the trash pane of that Inspector, then, evaluate expressions like: [self createMultipleTestScripts: 10] timeToRun. and MessageTally spyOn: [self createMultipleTestScripts: 4] " | aPosition | aPosition := 10 at 10. 1 to: aCount do: [:i | self forceScriptCreationAt: aPosition. aPosition := aPosition + (0 @ 50). "avoid dropping into existing scriptor" Project current world doOneCycle] "refresh viewer"! ! !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/12/2020 14:47'! forceScriptCreationAt: aPosition "For performance testing." | dup | dup := self duplicate. dup eventHandler: nil. "Remove viewer-related evt mouseover feedback" dup formerPosition: self currentHand position. self currentHand attachMorph: dup; simulateMorphDropAt: aPosition.! ! !PhraseTileForTest methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTest methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := CompoundTileMorph new. guyToTake setNamePropertyTo: 'TestTile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !PhraseTileForTimesRepeat methodsFor: 'hilighting' stamp: 'ct 9/11/2020 20:47'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! !PhraseTileForTimesRepeat methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! mouseDown: evt "Handle a mouse-down on the receiver" | guyToTake catViewer | guyToTake := TimesRepeatTile new. guyToTake setNamePropertyTo: 'Repeat Tile' translated. guyToTake position: evt position + (-25 at 8). guyToTake formerPosition: evt hand position. "self startSteppingSelector: #trackDropZones." (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. guyToTake setProperty: #newPermanentScript toValue: true]. guyToTake justGrabbedFromViewer: true. ^ evt hand grabMorph: guyToTake! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! adoptScriptsFrom "Let the user click on another object form which the receiver should obtain scripts and code" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ Beeper beep]. (aMorph renderedMorph isSketchMorph and: [aMorph player belongsToUniClass] and: [self belongsToUniClass not]) ifTrue: [costume acquirePlayerSimilarTo: aMorph player] ifFalse: [Beeper beep].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! beRevealedInActiveWorld "Reveal my corresponding morph in the active world" self revealPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! grabPlayerInActiveWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" ^ self grabPlayerIn: Project current world! ! !Player methodsFor: 'misc' stamp: 'ct 9/12/2020 14:47'! grabPlayerIn: aWorld "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" | aMorph newPosition | self costume == aWorld ifTrue: [^ self]. self currentHand releaseMouseFocus. (aMorph := self costume) visible: true. newPosition := self currentHand position - (aMorph extent // 2). aMorph isInWorld ifTrue: [aMorph goHome. aMorph formerPosition: aMorph positionInWorld] ifFalse: [aMorph formerPosition: aWorld center]. aMorph formerOwner: Project current world. aMorph position: newPosition. self currentHand targetOffset: aMorph position - self currentHand position; addMorphBack: aMorph.! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! impartSketchScripts "Let the user designate another object to which my scripts and code should be imparted" | aMorph | Sensor waitNoButton. aMorph := Project current world chooseClickTarget. aMorph ifNil: [^ self]. (aMorph renderedMorph isSketchMorph) ifTrue: [ aMorph acquirePlayerSimilarTo: self].! ! !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:44'! offerAlternateViewerMenuFor: aViewer event: evt "Put up an alternate Viewer menu on behalf of the receiver." | menu world | world := aViewer world. menu := MenuMorph new defaultTarget: self. (costumes notNil and: [ (costumes size > 1 or: [costumes size == 1 and: [costumes first ~~ costume renderedMorph]])]) ifTrue: [menu add: 'forget other costumes' translated target: self selector: #forgetOtherCostumes]. menu add: 'expunge empty scripts' translated target: self action: #expungeEmptyScripts. menu addLine. menu add: 'choose vocabulary...' translated target: aViewer action: #chooseVocabulary; balloonTextForLastItem: 'Choose a different vocabulary for this Viewer.' translated. menu add: 'choose limit class...' translated target: aViewer action: #chooseLimitClass; balloonTextForLastItem: 'Specify what the limitClass should be for this Viewer -- i.e., the most generic class whose methods and categories should be considered here.' translated. menu add: 'open standard lexicon' translated target: aViewer action: #openLexicon; balloonTextForLastItem: 'open a window that shows the code for this object in traditional programmer format' translated. menu add: 'open lexicon with search pane' translated target: aViewer action: #openSearchingProtocolBrowser; balloonTextForLastItem: 'open a lexicon that has a type-in pane for search (not recommended!!)' translated. menu addLine. menu add: 'inspect morph' translated target: costume selector: #inspect. menu add: 'inspect player' translated target: self selector: #inspect. self belongsToUniClass ifTrue: [ menu add: 'browse class' translated target: self action: #browsePlayerClass. menu add: 'inspect class' translated target: self class action: #inspect]. menu add: 'inspect this Viewer' translated target: aViewer selector: #inspect. menu add: 'inspect this Vocabulary' translated target: aViewer currentVocabulary selector: #inspect. menu addLine. menu add: 'relaunch this Viewer' translated target: aViewer action: #relaunchViewer. menu add: 'attempt repairs' translated target: Project current world action: #attemptCleanup. menu add: 'destroy all this object''s scripts' translated target: self action: #destroyAllScripts. menu add: 'view morph directly' translated target: aViewer action: #viewMorphDirectly. menu balloonTextForLastItem: 'opens a Viewer directly on the rendered morph.' translated. costume renderedMorph isSketchMorph ifTrue: [ menu addLine. menu add: 'impart scripts to...' translated target: self action: #impartSketchScripts]. ^ menu popUpEvent: evt in: world! ! !Player methodsFor: 'scripts-standard' stamp: 'ct 9/12/2020 14:48'! hide "Make the object be hidden, as opposed to visible" self currentHand ifNotNil: [:hand | (hand keyboardFocus == self costume renderedMorph) ifTrue: [ hand releaseKeyboardFocus]]. self costume hide.! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:45'! getLastKeystroke "Answer the last keystroke fielded" ^ Project current world lastKeystroke! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:41'! setLastKeystroke: aString "Set the last keystroke fielded" ^ self currentWorld lastKeystroke: aString! ! !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/12/2020 14:49'! setSecondColor: aColor "Setter for costume's second color, if it's using gradient fill; if not, does nothing" | morph fillStyle colorToUse | morph := costume renderedMorph. fillStyle := morph fillStyle. fillStyle isGradientFill ifFalse: [^ self]. colorToUse := (costume isWorldMorph and: [aColor isColor]) ifTrue: [aColor alpha: 1.0] "reject any translucency" ifFalse: [aColor]. fillStyle lastColor: colorToUse forMorph: morph hand: self currentHand.! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:47'! addInstanceVariable "Offer the user the opportunity to add an instance variable, and if he goes through with it, actually add it." Project current world addMorphInLayer: (NewVariableDialogMorph on: self costume) centeredNear: (self currentHand ifNil:[Sensor]) cursorPoint! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:46'! allPossibleWatchersFromWorld "Answer a list of all UpdatingStringMorphs, PlayerReferenceReadouts, ThumbnailMorphs, and UpdatingReferenceMorphs in the Active world and its hidden book pages, etc., which have me or any of my siblings as targets" | a | a := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: a. ^ a select: [:e | e isEtoyReadout and: [e target class == self class]]! ! !Player methodsFor: 'slots-user' stamp: 'ct 9/12/2020 14:48'! offerGetterTiles: slotName "For a player-type slot, offer to build convenient compound tiles that otherwise would be hard to get" | typeChoices typeChosen thePlayerThereNow slotChoices slotChosen getterTiles aCategoryViewer playerGetter | typeChoices := Vocabulary typeChoices. typeChosen := UIManager default chooseFrom: (typeChoices collect: [:t | t translated]) values: typeChoices title: ('Choose the TYPE of data to get from {1}''s {2}' translated format: {self externalName. slotName translated}). typeChosen isEmptyOrNil ifTrue: [^self]. thePlayerThereNow := self perform: slotName asGetterSelector. thePlayerThereNow ifNil: [thePlayerThereNow := self presenter standardPlayer]. slotChoices := thePlayerThereNow slotNamesOfType: typeChosen. slotChoices isEmpty ifTrue: [^self inform: 'sorry -- no slots of that type' translated]. slotChoices sort. slotChosen := UIManager default chooseFrom: (slotChoices collect: [:t | t translated]) values: slotChoices title: ('Choose the datum you want to extract from {1}''s {2}' translated format: {self externalName. slotName translated}). slotChosen isEmptyOrNil ifTrue: [^self]. "Now we want to tear off tiles of the form holder's valueAtCursor's foo" getterTiles := nil. aCategoryViewer := CategoryViewer new initializeFor: thePlayerThereNow categoryChoice: 'basic'. getterTiles := aCategoryViewer getterTilesFor: slotChosen asGetterSelector type: typeChosen. aCategoryViewer := CategoryViewer new initializeFor: self categoryChoice: 'basic'. playerGetter := aCategoryViewer getterTilesFor: slotName asGetterSelector type: #Player. getterTiles submorphs first acceptDroppingMorph: playerGetter event: nil. "the pad" "simulate a drop" getterTiles makeAllTilesGreen. getterTiles justGrabbedFromViewer: false. (getterTiles firstSubmorph) changeTableLayout; hResizing: #shrinkWrap; vResizing: #spaceFill. self currentHand attachMorph: getterTiles.! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:46'! addPatchVarNamed: nameSymbol | f | f := KedamaPatchMorph newExtent: self costume dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). self addInstanceVariable2Named: nameSymbol type: #Patch value: f player. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatch | f usedNames newName | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f kedamaWorld: self costume renderedMorph. usedNames := Project current world allKnownNames, self class instVarNames. newName := Utilities keyLike: f innocuousName satisfying: [:aName | (usedNames includes: aName) not]. f setNameTo: newName. self createSlotForPatch: f. self addToPatchDisplayList: f assuredPlayer. self costume world primaryHand attachMorph: f. ^ f! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtle | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). self costume world primaryHand attachMorph: m. ^ m! ! !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleSilently | m | m := KedamaTurtleMorph new openInWorld. self useTurtle: m player. m turtleCount: 0. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !Player methodsFor: '*Etoys-Squeakland-scripts-standard' stamp: 'ct 9/11/2020 20:41'! printInTranscript "Print a line representing the receiver in the Transcript" Project current world findATranscript: nil. Transcript cr; show: (Time now printString copyWithoutAll: '()'); space; show: self costume printString.! ! !Player methodsFor: '*Etoys-Squeakland-slots-user' stamp: 'ct 9/12/2020 14:47'! changeSlotInfo: aSymbol Project current world addMorphInLayer: (ModifyVariableDialogMorph on: self costume slot: aSymbol) centeredNear: (self currentHand ifNil: [Sensor]) cursorPoint.! ! !Player methodsFor: '*Etoys-Squeakland-slot getters/setters' stamp: 'ct 9/12/2020 14:47'! handUserPictureOfPenTrail "Called from the user-interface: hand the user a picture of the pen trail" self getHasPenTrails ifFalse: [ ^ self inform: 'no pen trails present' translated]. self currentHand attachMorph: (SketchMorph new form: self getPenTrailGraphic).! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! kedamaWorld ^ Project current world findDeeplyA: KedamaMorph ! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newPatchForSet | f | f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. f assuredPlayer assureUniClass. f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). f kedamaWorld: self costume renderedMorph. self createSlotForPatch: f. ^ f! ! !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! newTurtleForSet | m | m := KedamaTurtleMorph new openInWorld. self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. self useTurtle: m player. m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). ^ m! ! !PlayerSurrogate methodsFor: 'menu' stamp: 'ct 9/11/2020 20:37'! revealThisObject "Reveal the object I represent" playerRepresented revealPlayerIn: Project current world! ! !PlayerSurrogate methodsFor: '*Etoys-Squeakland-as yet unclassified' stamp: 'ct 9/11/2020 20:40'! forciblyRenamePlayer "Allow the receiver to seize a name already nominally in use in the project." | current reply currentlyBearingName newNameForHim binding | current := playerRepresented knownName. reply := FillInTheBlank request: 'Type the name you insist upon' translated initialAnswer: current. reply isEmptyOrNil ifTrue: [^ self]. Preferences uniquePlayerNames ifFalse: [^ self costume renameTo: reply]. reply := (reply asIdentifier: true) asSymbol. reply = current ifTrue: [^ self inform: 'no change' translated]. binding := Project current world referencePool hasBindingOf: reply. binding ifNotNil: [ currentlyBearingName := binding value. newNameForHim := Utilities keyLike: reply satisfying: [:name | (Project current world referencePool includesKey: name) not]. currentlyBearingName renameTo: newNameForHim]. playerRepresented renameTo: reply. self inform: (binding ifNil: [('There was no conflict; this object is now named {1}' translated format: {reply})] ifNotNil: ['Okay, this object is now named\{1}\and the object formerly known by this name is now called\{2}' translated format: {reply. newNameForHim}]).! ! !PlayerType methodsFor: 'tiles' stamp: 'ct 9/11/2020 20:37'! defaultArgumentTile "Answer a tile to represent the type" ^ Project current world presenter standardPlayer tileToRefer! ! !KedamaPatchType methodsFor: 'tile protocol' stamp: 'ct 9/11/2020 20:15'! defaultArgumentTile "Answer a tile to represent the type" | patch ks k p | patch := KedamaPatchTile new typeColor: self typeColor. ks := self world allMorphs select: [:e | e isKindOf: KedamaMorph]. ks isEmpty ifFalse: [ k := ks first. p := k player getPatch. ] ifTrue: [ k := KedamaPatchMorph new. k assuredPlayer. p := k player. ]. patch usePatch: p. ^ patch! ! !PluggableFileList methodsFor: 'StandardFileMenu' stamp: 'ct 9/12/2020 14:50'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PluggableListMorph methodsFor: 'model access - keystroke' stamp: 'ct 9/11/2020 18:01'! specialKeyPressed: asciiValue "A special key with the given ascii-value was pressed; dispatch it" | oldSelection nextSelection max howManyItemsShowing | (#(8 13) includes: asciiValue) ifTrue: [ "backspace key - clear the filter, restore the list with the selection" model okToChange ifFalse: [^ self]. self removeFilter. priorSelection ifNotNil: [ | prior | prior := priorSelection. priorSelection := self getCurrentSelectionIndex. asciiValue = 8 ifTrue: [ self changeModelSelection: prior ] ]. ^ self ]. asciiValue = 27 ifTrue: [" escape key" ^ self currentEvent shiftPressed ifTrue: [self currentEvent putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]]. max := self maximumSelection. max > 0 ifFalse: [^ self]. nextSelection := oldSelection := self selectionIndex. asciiValue = 31 ifTrue: [" down arrow" nextSelection := oldSelection + 1. nextSelection > max ifTrue: [nextSelection := 1]]. asciiValue = 30 ifTrue: [" up arrow" nextSelection := oldSelection - 1. nextSelection < 1 ifTrue: [nextSelection := max]]. asciiValue = 1 ifTrue: [" home" nextSelection := 1]. asciiValue = 4 ifTrue: [" end" nextSelection := max]. howManyItemsShowing := self numSelectionsInView. asciiValue = 11 ifTrue: [" page up" nextSelection := 1 max: oldSelection - howManyItemsShowing]. asciiValue = 12 ifTrue: [" page down" nextSelection := oldSelection + howManyItemsShowing min: max]. model okToChange ifFalse: [^ self]. "No change if model is locked" oldSelection = nextSelection ifTrue: [^ self flash]. ^ self changeModelSelection: (self modelIndexFor: nextSelection)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:50'! startUpCenteredWithCaption: captionOrNil "Differs from startUpWithCaption: by appearing with cursor in the menu, and thus ready to act on mouseUp, without requiring user tweak to confirm" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint - (20 @ 0)! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." self flag: #fix. "mt: Could we manage to open pop-up menus in Morphic without accessing self currentHand?" ^ self startUpWithCaption: captionOrNil at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithCaption: captionOrNil icon: aForm "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." ^ self startUpWithCaption: captionOrNil icon: aForm at: (self currentHand ifNil: [Sensor]) cursorPoint! ! !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! startUpWithoutKeyboard "Display and make a selection from the receiver as long as the button is pressed. Answer the current selection. Do not allow keyboard input into the menu" ^ self startUpWithCaption: nil at: ((self currentHand ifNil: [Sensor]) cursorPoint) allowKeyboard: false! ! !PopUpMenu methodsFor: '*Morphic-Menus' stamp: 'ct 9/11/2020 20:37'! morphicStartUpWithCaption: captionOrNil icon: aForm at: location allowKeyboard: aBoolean "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard." selection := Cursor normal showWhile: [| menuMorph | menuMorph := MVCMenuMorph from: self title: nil. (captionOrNil notNil or: [aForm notNil]) ifTrue: [menuMorph addTitle: captionOrNil icon: aForm]. MenuIcons decorateMenu: menuMorph. menuMorph invokeAt: location in: self currentWorld allowKeyboard: aBoolean]. ^ selection! ! !PopUpMenu methodsFor: '*Etoys-Squeakland-basic control sequence' stamp: 'ct 9/11/2020 20:37'! startUpWithCaption: captionOrNil at: location allowKeyboard: allowKeyboard centered: centered "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard If centered is true, the menu items are displayed centered.." | maxHeight aMenu | (ProvideAnswerNotification signal: captionOrNil) ifNotNil: [:answer | ^ selection := answer ifTrue: [1] ifFalse: [2]]. maxHeight := Display height*3//4. self frameHeight > maxHeight ifTrue: [^ self startUpSegmented: maxHeight withCaption: captionOrNil at: location allowKeyboard: allowKeyboard]. Smalltalk isMorphic ifTrue:[ selection := Cursor normal showWhile: [aMenu := MVCMenuMorph from: self title: captionOrNil. centered ifTrue: [aMenu submorphs allButFirst do: [:m | m setProperty: #centered toValue: true]]. aMenu invokeAt: location in: self currentWorld allowKeyboard: allowKeyboard]. ^ selection]. frame ifNil: [self computeForm]. Cursor normal showWhile: [self displayAt: location withCaption: captionOrNil during: [self controlActivity]]. ^ selection! ! !PopUpMenu class methodsFor: '*Etoys-Squeakland-dialogs' stamp: 'ct 9/12/2020 14:52'! informCenteredAboveCursor: aString "Put up an informer showing the given string in a box, with the OK button for dismissing the informer having the cursor at its center." "PopUpMenu informCenteredAboveCursor: 'I like Squeak\how about you?' withCRs" | lines maxWid xCoor | lines := Array streamContents: [:aStream | aString linesDo: [:l | aStream nextPut: l]]. maxWid := (lines collect: [:l | Preferences standardMenuFont widthOfString: l]) max. xCoor := self currentHand cursorPoint x - (maxWid // 2). ((xCoor + maxWid) > self currentWorld right) ifTrue: [xCoor := self currentWorld right]. "Caters to problematic PopUpMenu boundary behavior" (PopUpMenu labels: 'OK' translated) startUpWithCaption: aString at: (xCoor @ self currentHand cursorPoint y) allowKeyboard: true centered: true.! ! !PreferenceWizardMorph methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:35'! initializePreviewWorld | w1 w2 w3 | previewWorld := PasteUpMorph new hResizing: #spaceFill; vResizing: #spaceFill; viewBox: (0 at 0 corner: 500 at 500); layoutFrame: (LayoutFrame fractions: (0.3 @ 0 corner: 1.0 @ 1.0) offsets: (0@ titleMorph height corner: 0 @ buttonRowMorph height negated)); fillStyle: Project current world fillStyle; borderWidth: 2; borderColor: Color white; cornerStyle: (self hasLowPerformance ifTrue: [#square] ifFalse: [#rounded]); yourself. w1 := (ToolSet browse: Morph selector: #drawOn:) dependents detect: [:ea | ea isSystemWindow]. w2 := ToolSet browseMessageSet: (SystemNavigation default allCallsOn: #negated) name: 'Senders' translated autoSelect: 'negated'. w3 := (Workspace new contents: '3+4 "Select and hit [CMD]+[P]."') openLabel: 'Workspace'. {w1. w2. w3} do: [:ea | ea makeUnclosable. previewWorld addMorph: ea]. self updateWindowBounds.! ! !PreferenceWizardMorph methodsFor: 'support' stamp: 'ct 9/12/2020 14:36'! adjustSettingsForLowPerformance self updateLowPerformanceLabel: 'Please wait, optimizing performance...' translated. self refreshWorld. self stateGradients "flat look" ifFalse: [self toggleGradients]. self stateBlinkingCursor ifTrue: [self toggleBlinkingCursor]. self stateFastDrag ifFalse: [self toggleFastDrag]. self stateSoftShadows ifTrue: [self toggleSoftShadows]. self stateHardShadows ifTrue: [self toggleHardShadows]. self stateRoundedWindowLook ifTrue: [self toggleRoundedWindowLook]. self stateRoundedButtonLook ifTrue: [self toggleRoundedButtonLook]. self stateAttachToolsToMouse ifTrue: [self toggleAttachToolsToMouse]. self stateToolAndMenuIcons ifTrue: [self toggleToolAndMenuIcons]. self stateSmartHorizontalSplitters ifTrue: [self toggleSmartHorizontalSplitters]. self stateSmartVerticalSplitters ifTrue: [self toggleSmartVerticalSplitters]. PluggableListMorph highlightHoveredRow: false; filterableLists: false; highlightPreSelection: true; "Feedback is important!!" flashOnErrors: false. TheWorldMainDockingBar showSecondsInClock: false. Preferences disable: #balloonHelpInMessageLists. "Set simple background." Project current world setAsBackground: MorphicProject defaultFill. previewWorld fillStyle: Project current world fillStyle. "Done." self updateLowPerformanceLabel: 'Settings were adjusted for optimal performance.' translated.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! roundedWindowCornersChanged "The user changed the value of the roundedWindowCorners preference. React" Project current world fullRepaintNeeded.! ! !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! vectorVocabularySettingChanged "The current value of the useVectorVocabulary flag has changed; now react. No senders, but invoked by the Preference object associated with the #useVectorVocabulary preference." Smalltalk isMorphic ifFalse: [^ self]. Project current world makeVectorUseConformToPreference.! ! !Project methodsFor: '*Etoys-Squeakland-file in/out' stamp: 'ct 9/16/2020 19:18'! storeOnServerWithNoInteractionInnards "Save to disk as an Export Segment. Then put that file on the server I came from, as a new version. Version is literal piece of file name. Mime encoded and http encoded." | newName primaryServerDirectory serverVersionPair localDirectory localVersionPair myVersionNumber warning maxNumber myDepth | self assureIntegerVersion. "Find out what version" primaryServerDirectory := self defaultFolderForAutoSaving ifNil: [^self]. localDirectory := self squeakletDirectory. serverVersionPair := self class mostRecent: self name onServer: primaryServerDirectory. localVersionPair := self class mostRecent: self name onServer: localDirectory. maxNumber := myVersionNumber := self currentVersionNumber. ProgressNotification signal: '2:versionsDetected'. warning := ''. myVersionNumber < serverVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) on the server' translated. maxNumber := maxNumber max: serverVersionPair second. ]. myVersionNumber < localVersionPair second ifTrue: [ warning := warning,'\There are newer version(s) in the local directory' translated. maxNumber := maxNumber max: localVersionPair second. ]. version := self bumpVersion: maxNumber. "write locally - now zipped automatically" Display isVirtualScreen ifTrue: [ myDepth := displayDepth. displayDepth := OLPCVirtualScreen preferredScreenDepth.. ]. newName := self versionedFileName. lastSavedAtSeconds := Time totalSeconds. self exportSegmentFileName: newName directory: localDirectory withoutInteraction: true. (localDirectory readOnlyFileNamed: newName) setFileTypeToObject; close. Display isVirtualScreen ifTrue: [ displayDepth := myDepth. ]. ProgressNotification signal: '4:localSaveComplete'. "3 is deep in export logic" primaryServerDirectory ifNotNil: [ [ primaryServerDirectory writeProject: self inFileNamed: newName asFileName fromDirectory: localDirectory. ] on: ProjectPasswordNotification do: [ :ex | ex resume: '' ]. ]. ProgressNotification signal: '9999 save complete'.! ! !Project methodsFor: '*Etoys-Squeakland-language' stamp: 'ct 9/11/2020 20:34'! updateLocaleDependentsWithPreviousSupplies: aCollection gently: gentlyFlag "Set the project's natural language as indicated" | morphs scriptEditors | gentlyFlag ifTrue: [ LanguageEnvironment localeChangedGently. ] ifFalse: [ LanguageEnvironment localeChanged. ]. morphs := IdentitySet new: 400. Project current world allMorphsAndBookPagesInto: morphs. scriptEditors := morphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m topEditor == m]]. (morphs copyWithoutAll: scriptEditors) do: [:morph | morph localeChanged]. scriptEditors do: [:m | m localeChanged]. Flaps disableGlobalFlaps: false. SugarNavigatorBar showSugarNavigator ifTrue: [Flaps addAndEnableEToyFlapsWithPreviousEntries: aCollection. Project current world addGlobalFlaps] ifFalse: [Preferences eToyFriendly ifTrue: [Flaps addAndEnableEToyFlaps. Project current world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]]. (Project current isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ParagraphEditor initializeTextEditorMenus. MenuIcons initializeTranslations. #(PartsBin ParagraphEditor BitEditor FormEditor StandardSystemController) do: [ :key | Smalltalk at: key ifPresent: [ :class | class initialize ]]. Project current world reformulateUpdatingMenus. "self setFlaps. self setPaletteFor: aLanguageSymbol." ! ! !MorphicProject methodsFor: 'utilities' stamp: 'ct 9/12/2020 15:13'! createViewIfAppropriate "Create a project view for the receiver and place it appropriately on the screen." | aMorph requiredWidth existing proposedV proposedH despair | ProjectViewOpenNotification signal ifTrue: [Preferences projectViewsInWindows ifTrue: [(ProjectViewMorph newProjectViewInAWindowFor: self) openInWorld] ifFalse: [aMorph := ProjectViewMorph on: self. requiredWidth := aMorph width + 10. existing := self currentWorld submorphs select: [:m | m isKindOf: ProjectViewMorph] thenCollect: [:m | m fullBoundsInWorld]. proposedV := 85. proposedH := 10. despair := false. [despair not and: [((proposedH @ proposedV) extent: requiredWidth) intersectsAny: existing]] whileTrue: [proposedH := proposedH + requiredWidth. proposedH + requiredWidth > self currentWorld right ifTrue: [proposedH := 10. proposedV := proposedV + 90. proposedV > (self currentWorld bottom - 90) ifTrue: [proposedH := self currentWorld center x - 45. proposedV := self currentWorld center y - 30. despair := true]]]. aMorph position: (proposedH @ proposedV). aMorph openInWorld]]! ! !MorphicProject methodsFor: 'flaps support' stamp: 'ct 9/12/2020 14:34'! setFlaps | flapTabs flapIDs sharedFlapTabs navigationMorph | self flag: #toRemove. "check if this method still used by Etoys" flapTabs := self world flapTabs. flapIDs := flapTabs collect: [:tab | tab knownName]. flapTabs do: [:tab | (tab isMemberOf: ViewerFlapTab) ifFalse: [tab isGlobalFlap ifTrue: [Flaps removeFlapTab: tab keepInList: false. tab currentWorld reformulateUpdatingMenus] ifFalse: [| referent | referent := tab referent. referent isInWorld ifTrue: [referent delete]. tab delete]]]. sharedFlapTabs := Flaps classPool at: #SharedFlapTabs. flapIDs do: [:id | id = 'Navigator' translated ifTrue: [sharedFlapTabs add: Flaps newNavigatorFlap]. id = 'Widgets' translated ifTrue: [sharedFlapTabs add: Flaps newWidgetsFlap]. id = 'Tools' translated ifTrue: [sharedFlapTabs add: Flaps newToolsFlap]. id = 'Squeak' translated ifTrue: [sharedFlapTabs add: Flaps newSqueakFlap]. id = 'Supplies' translated ifTrue: [sharedFlapTabs add: Flaps newSuppliesFlap]. id = 'Stack Tools' translated ifTrue: [sharedFlapTabs add: Flaps newStackToolsFlap]. id = 'Painting' translated ifTrue: [sharedFlapTabs add: Flaps newPaintingFlap]. id = 'Objects' translated ifTrue: [sharedFlapTabs add: Flaps newObjectsFlap ]]. 2 timesRepeat: [flapIDs do: [:id | Flaps enableDisableGlobalFlapWithID: id]]. self world flapTabs do: [:flapTab | flapTab isCurrentlyTextual ifTrue: [flapTab changeTabText: flapTab knownName]]. Flaps positionNavigatorAndOtherFlapsAccordingToPreference. navigationMorph := self currentWorld findDeeplyA: ProjectNavigationMorph preferredNavigator. navigationMorph isNil ifTrue: [^ self]. navigationMorph allMorphs do: [:morph | morph class == SimpleButtonDelayedMenuMorph ifTrue: [(morph findA: ImageMorph) isNil ifTrue: [| label | label := morph label. label isNil ifFalse: [| name | name := morph knownName. name isNil ifTrue: [morph name: label. name := label]. morph label: name translated]]]]! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/16/2020 19:45'! clearGlobalState "Clean up global state. This method may be removed if the use of global state variables is eliminated." "If global World is defined, clear it now. The value is expected to be set again as a new project is entered." Smalltalk globals at: #World ifPresent: [:w | Smalltalk globals at: #World put: nil].! ! !MorphicProject methodsFor: 'enter' stamp: 'ct 9/12/2020 14:45'! wakeUpTopWindow "Image has been restarted, and the startUp list has been processed. Perform any additional actions needed to restart the user interface." SystemWindow wakeUpTopWindowUponStartup. Preferences mouseOverForKeyboardFocus ifTrue: [ "Allow global command keys to work upon re-entry without having to cause a focus change first." self currentHand releaseKeyboardFocus ]! ! !MorphicProject methodsFor: 'language' stamp: 'ct 9/12/2020 14:17'! updateLocaleDependents "Set the project's natural language as indicated" (self world respondsTo: #isTileScriptingElement) ifTrue: "Etoys present" [ self world allTileScriptingElements do: [:viewerOrScriptor | viewerOrScriptor localeChanged]]. Flaps disableGlobalFlaps: false. (Preferences eToyFriendly or: [ (Smalltalk classNamed: #SugarNavigatorBar) ifNotNil: [:c | c showSugarNavigator] ifNil: [false]]) ifTrue: [ Flaps addAndEnableEToyFlaps. self world addGlobalFlaps] ifFalse: [Flaps enableGlobalFlaps]. (self isFlapIDEnabled: 'Navigator' translated) ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. ScrapBook default emptyScrapBook. MenuIcons initializeTranslations. super updateLocaleDependents. "self setFlaps. self setPaletteFor: aLanguageSymbol."! ! !MorphicProject methodsFor: 'protocols' stamp: 'ct 9/12/2020 14:18'! currentVocabulary ^ self world currentVocabulary! ! !MorphicProject methodsFor: 'scheduling & debugging' stamp: 'ct 9/16/2020 19:45'! interruptCleanUpFor: interruptedProcess "Clean up things in case of a process interrupt." super interruptCleanUpFor: interruptedProcess. self uiProcess == interruptedProcess ifTrue: [ self currentHand ifNotNil: [:hand | hand interrupted]. Preferences eToyFriendly ifTrue: [ Project current world stopRunningAll]].! ! !MorphicProject class methodsFor: 'shrinking' stamp: 'ct 9/12/2020 15:45'! unloadMorphic "MorphicProject unloadMorphic" Project current isMorphic ifTrue: [ ^ Error signal: 'You can only unload Morphic from within another kind of project.' translated]. MorphicProject removeProjectsFromSystem. #(ActiveEvent ActiveHand ActiveWorld World) do: [:ea | Smalltalk globals removeKey: ea]. Processor allInstancesDo: [:process | #(ActiveHand ActiveEvent ActiveWorld) do: [:ea | process environmentRemoveKey: ea ifAbsent: []]]. { 'ToolBuilder-Morphic' . 'MorphicTests' . 'MorphicExtras' . 'Morphic' } do: [ :package | (MCPackage named: package) unload ].! ! !ProjectNavigationMorph methodsFor: 'stepping and presenter' stamp: 'ct 9/11/2020 20:34'! undoButtonWording "Answer the wording for the Undo button." | wdng | wdng := Project current world commandHistory undoOrRedoMenuWording. (wdng endsWith: ' (z)') ifTrue: [ wdng := wdng copyFrom: 1to: wdng size - 4]. ^ wdng! ! !ProjectNavigationMorph methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:34'! undoOrRedoLastCommand "Undo or redo the last command, as approrpiate." ^ Project current world commandHistory undoOrRedoCommand! ! !EventRecordingSpaceNavigator methodsFor: 'the actions' stamp: 'ct 9/11/2020 19:47'! doNewPainting "Make a new painting" | worldlet | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. worldlet closeNavigatorFlap. worldlet makeNewDrawing: (self currentEvent copy setPosition: worldlet center).! ! !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/11/2020 20:34'! maximumUsableArea ^ self maximumUsableAreaInWorld: Project current world! ! !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:41'! maximumUsableAreaInWorld: aWorldOrNil | allowedArea | allowedArea := Display usableArea. aWorldOrNil ifNotNil: [ Smalltalk isMorphic ifTrue: [ allowedArea := allowedArea intersect: aWorldOrNil visibleClearArea. (((Smalltalk classNamed: 'Flaps') ifNil: [false] ifNotNil: [:cls | cls anyFlapsVisibleIn: aWorldOrNil]) and: [self respondsTo: #reduceByFlaps:]) ifTrue: [allowedArea := self reduceByFlaps: allowedArea]]]. ^allowedArea! ! !RecordingControls methodsFor: 'private' stamp: 'ct 9/12/2020 14:52'! makeSoundMorph "Hand the user an anonymous-sound object representing the receiver's sound." | m aName | recorder verifyExistenceOfRecordedSound ifFalse: [^ self]. recorder pause. recordingSaved := true. m := AnonymousSoundMorph new. m sound: recorder recordedSound interimName: (aName := 'Unnamed Sound'). m setNameTo: aName. self currentHand attachMorph: m.! ! !ReleaseBuilder class methodsFor: 'scripts - support' stamp: 'ct 9/11/2020 20:33'! setProjectBackground: aFormOrColorOrFillStyle | world | world := Project current world. world fillStyle: aFormOrColorOrFillStyle. MorphicProject defaultFill: world fillStyle. world removeProperty: #hasCustomBackground.! ! !SARInstaller methodsFor: 'client services' stamp: 'ct 9/11/2020 20:33'! fileInMorphsNamed: memberName addToWorld: aBoolean "This will load the Morph (or Morphs) from the given member. Answers a Morph, or a list of Morphs, or nil if no such member or error. If aBoolean is true, also adds them and their models to the World." | member morphOrList | member := self memberNamed: memberName. member ifNil: [^ self errorNoSuchMember: memberName]. self installed: member. morphOrList := member contentStream fileInObjectAndCode. morphOrList ifNil: [^ nil]. aBoolean ifTrue: [Project current world addMorphsAndModel: morphOrList]. ^ morphOrList! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/12/2020 14:52'! addYesNoToHand "Place a test/yes/no complex in the hand of the beloved user" | ms messageNodeMorph aMorph | Preferences universalTiles ifTrue: [ms := MessageSend receiver: true selector: #ifTrue:ifFalse: arguments: {['do nothing']. ['do nothing']}. messageNodeMorph := ms asTilesIn: playerScripted class globalNames: true. self primaryHand attachMorph: messageNodeMorph] ifFalse: [aMorph := CompoundTileMorph new. self currentHand attachMorph: aMorph. aMorph setNamePropertyTo: 'TestTile' translated. aMorph position: self currentHand position. aMorph formerPosition: self currentHand position. self startSteppingSelector: #trackDropZones].! ! !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:32'! dismiss "Dismiss the scriptor, usually nondestructively. Possibly animate the dismissal." | endPoint aForm startPoint topRend | owner ifNil: [^ self]. scriptName ifNil: [^ self delete]. "ad hoc fixup for bkwrd compat" endPoint := self viewerTile ifNotNilDo: [:tile | tile topLeft] ifNil: [owner topRight]. aForm := (topRend := self topRendererOrSelf) imageForm offset: (0 at 0). handWithTile := nil. startPoint := topRend topLeft. topRend topRendererOrSelf delete. (playerScripted isExpendableScript: scriptName) ifTrue: [ ^ playerScripted removeScript: scriptName fromWorld: Project current world]. Project current world displayWorld. aForm slideFrom: startPoint to: endPoint nSteps: 4 delay: 30. "The OLPC Virtual Screen wouldn't notice the last update here." Display forceToScreen: (endPoint extent: aForm extent).! ! !ScriptEditorMorph methodsFor: 'other' stamp: 'ct 9/12/2020 14:52'! offerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer" | aMenu count | self modernize. self currentHand showTemporaryCursor: nil. Preferences eToyFriendly ifTrue: [^ self offerSimplerScriptorMenu]. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addStayUpItem. "NB: the kids version in #offerSimplerScriptorMenu does not deploy the stay-up item" aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: { {'button to fire this script' translatedNoop. #tearOfButtonToFireScript}. {'fires per tick...' translatedNoop. #chooseFrequency}. #- }]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. "aMenu addLine. self addGoldBoxItemsTo: aMenu." aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addTranslatedList: { #-. {'open viewer' translatedNoop. #openObjectsViewer. 'open the viewer of the object to which this script belongs' translatedNoop}. {'detached method pane' translatedNoop. #makeIsolatedCodePane. 'open a little window that shows the Smalltalk code underlying this script.' translatedNoop}. #-. {'destroy this script' translatedNoop. #destroyScript} }. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-menu commands' stamp: 'ct 9/11/2020 20:32'! findObject "Reveal the object bearing the code " playerScripted revealPlayerIn: Project current world.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:52'! handUserTimesRepeatTile "Hand the user a times-repeat tile, presumably to drop in the script" | aMorph | aMorph := TimesRepeatTile new. self currentHand attachMorph: aMorph. aMorph position: self currentHand position.! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:53'! offerSimplerScriptorMenu "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer. This variant is used when eToyFriendly preference is true." | aMenu count | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu addTitle: scriptName asString. aMenu addList: (self hasParameter ifTrue: [{ {'remove parameter' translated. #ceaseHavingAParameter}}] ifFalse: [{ {'add parameter' translated. #addParameter}}]). self hasParameter ifFalse: [aMenu addTranslatedList: #( ('button to fire this script' tearOfButtonToFireScript) -) translatedNoop]. aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. aMenu addLine. aMenu addList: { {'edit balloon help for this script' translated. #editMethodDescription}. {'explain status alternatives' translated. #explainStatusAlternatives}. {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. #- }. Preferences universalTiles ifFalse: [count := self savedTileVersionsCount. self showingMethodPane ifFalse: "currently showing tiles" [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. count > 0 ifTrue: [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. aMenu add: 'save this version' translated action: #saveScriptVersion] ifTrue: "current showing textual source" [count >= 1 ifTrue: [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. aMenu addLine. aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu addTranslatedList: #( - ('open viewer' openObjectsViewer 'open the viewer of the object to which this script belongs') - ('destroy this script' destroyScript)) translatedNoop. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:32'! goldBoxMenu "Answer a graphical menu to be put up in conjunction with the Gold Box" | aBox | aBox := Project current world findA: GoldBoxMenu. aBox ifNil: [aBox := GoldBoxMenu new]. aBox initializeFor: self. ^ aBox! ! !ScriptEncoder methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:31'! init: class notifying: parser super init: class notifying: parser. self referenceObject: Project current world referenceWorld.! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/12/2020 14:53'! offerMenuIn: aStatusViewer "Put up a menu." | aMenu | self currentHand showTemporaryCursor: nil. aMenu := MenuMorph new defaultTarget: self. aMenu title: player knownName, ' ', selector. aMenu addStayUpItem. (player class instanceCount > 1) ifTrue: [aMenu add: 'propagate status to siblings' translated selector: #assignStatusToAllSiblingsIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'Make the status of this script in all of my sibling instances be the same as the status you see here' translated]. aMenu addLine. aMenu add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. aMenu add: 'reveal this object' translated target: player selector: #revealPlayerIn: argument: self currentWorld. aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. aMenu add: 'tile representing this object' translated target: player selector: #tearOffTileForSelf. aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. aMenu addLine. aMenu add: 'open this script''s Scriptor' translated target: player selector: #grabScriptorForSelector:in: argumentList: {selector. aStatusViewer world}. aMenu balloonTextForLastItem: 'Open up the Scriptor for this script' translated. aMenu add: 'open this object''s Viewer' translated target: player selector: #beViewed. aMenu balloonTextForLastItem: 'Open up a Viewer for this object' translated. aMenu addLine. aMenu add: 'more...' translated target: self selector: #offerShiftedMenuIn: argument: aStatusViewer. aMenu balloonTextForLastItem: 'The "more..." branch offers you menu items that are less frequently used.' translated. ^ aMenu popUpInWorld: self currentWorld! ! !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/11/2020 20:30'! offerShiftedMenuIn: aStatusViewer "Put up the shifted menu" ^ (MenuMorph new defaultTarget: self) title: player knownName, ' ', selector; add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld; balloonTextForLastItem: 'Wherever this object currently is, the "grab" command will rip it out, and place it in your "hand". This is a very drastic step, that can disassemble things that may be very hard to put back together!!' translated; add: 'destroy this script' translated target: player selector: #removeScriptWithSelector: argument: selector; balloonTextForLastItem: 'Caution!! This is irreversibly destructive -- it removes the script from the system.' translated; addLine; add: 'inspect morph' translated target: player costume selector: #inspect; add: 'inspect player' translated target: player selector: #inspect; popUpInWorld: self currentWorld! ! !ScriptNameType methodsFor: 'queries' stamp: 'ct 9/11/2020 20:29'! choices "Answer an alphabetized list of known script selectors in the current project" ^ Project current world presenter allKnownUnaryScriptSelectors ! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:29'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock "Answer a MethodNode for the argument, sourceStream, that is the root of a parse tree. Parsing is done with respect to the argument, class, to find instance, class, and pool variables; and with respect to the argument, ctxt, to find temporary variables. Errors in parsing are reported to the argument, req, if not nil; otherwise aBlock is evaluated. The argument noPattern is a Boolean that is true if the the sourceStream does not contain a method header (i.e., for DoIts)." "Copied from superclass, use ScriptEncoder and give it a referenceWorld. This assumes worldLoading has been set to the right world this player belongs to. --bf 5/4/2010" | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: Project current world referenceWorld )] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:28'! parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock for: anInstance | methNode repeatNeeded myStream parser s p | (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) ifTrue: [parser := self as: DialectParser] ifFalse: [parser := self]. myStream := sourceStream. [repeatNeeded := false. p := myStream position. s := myStream upToEnd. myStream position: p. parser init: myStream notifying: req failBlock: [^ aBlock value]. doitFlag := noPattern. failBlock := aBlock. [methNode := parser method: noPattern context: ctxt encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: (anInstance costume ifNotNil: [anInstance costume referenceWorld] ifNil: [Project current world]))] on: ParserRemovedUnusedTemps do: [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. myStream := ReadStream on: requestor text string. ex resume]. repeatNeeded] whileTrue. encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" methNode sourceText: s. ^ methNode! ! !SearchTopic methodsFor: 'private' stamp: 'ct 9/11/2020 20:28'! triggerUpdateContents self mutex critical: [ updatePending == true ifFalse: [ updatePending := true. Project current addDeferredUIMessage: [Project current world addAlarm: #updateContents withArguments: #() for: self at: Time millisecondClockValue + 250]]].! ! !SelectionMorph methodsFor: 'halo commands' stamp: 'ct 9/11/2020 20:28'! duplicate "Make a duplicate of the receiver and havbe the hand grab it" selectedItems := self duplicateMorphCollection: selectedItems. selectedItems reverseDo: [:m | (owner ifNil: [self currentWorld]) addMorph: m]. dupLoc := self position. self currentHand grabMorph: self. self currentWorld presenter flushPlayerListCache.! ! !SimpleHierarchicalListMorph methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:28'! specialKeyPressed: asciiValue (self arrowKey: asciiValue) ifTrue: [^ true]. asciiValue = 27 "escape" ifTrue: [ self currentEvent shiftPressed ifTrue: [self currentWorld putUpWorldMenuFromEscapeKey] ifFalse: [self yellowButtonActivity: false]. ^ true]. ^ false! ! !SimpleSelectionMorph methodsFor: 'extending' stamp: 'ct 9/11/2020 20:27'! extendByHand: aHand "Assumes selection has just been created and added to some pasteUp or world" | startPoint handle m inner | startPoint := Sensor cursorPoint. handle := NewHandleMorph new followHand: aHand forEachPointDo: [:newPoint | | localPt | Cursor crossHair show. localPt := (self transformFrom: self world) globalPointToLocal: newPoint. self bounds: (startPoint rect: localPt)] lastPointDo: [:newPoint | inner := self bounds insetBy: 2 at 2. inner area >= 16 ifTrue: [m := SketchMorph new form: (Form fromDisplay: inner). aHand attachMorph: m. self currentWorld fullRepaintNeeded] "selection tracking can leave unwanted artifacts" ifFalse: [Beeper beep]. "throw minnows back" self delete]. handle visible: false. aHand attachMorph: handle. handle startStepping! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! cancelOutOfPainting "The user requested to back out of a painting session without saving" self deleteSelfAndSubordinates. emptyPicBlock ifNotNil: [emptyPicBlock value]. "note no args to block!!" hostView ifNotNil: [hostView changed]. Project current world resumeScriptsPausedByPainting. ^ nil! ! !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! deliverPainting: result evt: evt "Done painting. May come from resume, or from original call. Execute user's post painting instructions in the block. Always use this standard one. 4/21/97 tk" | newBox newForm ans | palette ifNotNil: "nil happens" [palette setAction: #paint: evt: evt]. "Get out of odd modes" "rot := palette getRotations." "rotate with heading, or turn to and fro" "palette setRotation: #normal." result == #cancel ifTrue: [ ans := UIManager default chooseFrom: { 'throw it away' translated. 'keep painting it' translated. } title: 'Do you really want to throw away what you just painted?' translated. ^ ans = 1 ifTrue: [self cancelOutOfPainting] ifFalse: [nil]]. "cancelled out of cancelling." "hostView rotationStyle: rot." "rotate with heading, or turn to and fro" newBox := paintingForm rectangleEnclosingPixelsNotOfColor: Color transparent. registrationPoint ifNotNil: [registrationPoint := registrationPoint - newBox origin]. "relative to newForm origin" newForm := Form extent: newBox extent depth: paintingForm depth. newForm copyBits: newBox from: paintingForm at: 0 at 0 clippingBox: newForm boundingBox rule: Form over fillColor: nil. newForm isAllWhite ifTrue: [ (self valueOfProperty: #background) == true ifFalse: [^ self cancelOutOfPainting]]. newForm fixAlpha. "so alpha channel stays intact for 32bpp" self delete. "so won't find me again" dimForm ifNotNil: [dimForm delete]. newPicBlock value: newForm value: (newBox copy translateBy: bounds origin). Project current world resumeScriptsPausedByPainting.! ! !SketchMorph methodsFor: 'menus' stamp: 'ct 9/11/2020 20:27'! collapse "Replace the receiver with a collapsed rendition of itself." | w collapsedVersion a ht | (w := self world) ifNil: [^ self]. collapsedVersion := (self imageForm scaledToSize: 50 at 50) asMorph. collapsedVersion setProperty: #uncollapsedMorph toValue: self. collapsedVersion on: #mouseUp send: #uncollapseSketch to: collapsedVersion. collapsedVersion setBalloonText: ('A collapsed version of {1}. Click to open it back up.' translated format: {self externalName}). self delete. w addMorphFront: ( a := AlignmentMorph newRow hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4; borderColor: Color white; addMorph: collapsedVersion; yourself). a setNameTo: self externalName. ht := (Smalltalk at: #SugarNavTab ifPresent: [:c | Project current world findA: c]) ifNotNil: [:tab | tab height] ifNil: [80]. a position: 0 at ht. collapsedVersion setProperty: #collapsedMorphCarrier toValue: a. (self valueOfProperty: #collapsedPosition) ifNotNil: [:priorPosition | a position: priorPosition].! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-event handling' stamp: 'ct 9/12/2020 14:39'! deleteBoxHit "The delete box was hit..." self currentHand showTemporaryCursor: nil. self delete.! ! !ColorPickerMorph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:39'! openPropertySheet "Delete the receiver and open a property sheet on my target instead." self currentHand showTemporaryCursor: nil. target openAppropriatePropertySheet. self delete.! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer cardNum: cardNum "Call once to search a card of the stack. Return true if found and highlight the text. oldContainer should be NIL. (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" | container strings old good insideOf place start | good := true. start := startIndex. strings := oldContainer ifNil: ["normal case" rawStrings] ifNotNil: [self currentPage allStringsAfter: oldContainer text]. keys do: [:searchString | | thisWord | "each key" good ifTrue: [thisWord := false. strings do: [:longString | | index | (index := longString findWordStart: searchString startingAt: start) > 0 ifTrue: [thisWord not & (searchString == keys first) ifTrue: [insideOf := longString. place := index]. thisWord := true]. start := 1]. "only first key on first container" good := thisWord]]. good ifTrue: ["all are on this page" "wasIn := (pages at: pageNum) isInMemory." self goToCardNumber: cardNum "wasIn ifFalse: ['search again, on the real current text. Know page is in.'. ^ self findText: keys inStrings: ((pages at: pageNum) allStringsAfter: nil) recompute it startAt: startIndex container: oldContainer pageNum: pageNum]"]. (old := self valueOfProperty: #searchContainer) ifNotNil: [(old respondsTo: #editor) ifTrue: [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" old changed]]. good ifTrue: ["have the exact string object" (container := oldContainer) ifNil: [container := self highlightText: keys first at: place in: insideOf] ifNotNil: [container userString == insideOf ifFalse: [container := self highlightText: keys first at: place in: insideOf] ifTrue: [(container isTextMorph) ifTrue: [container editor selectFrom: place to: keys first size - 1 + place. container changed]]]. self setProperty: #searchContainer toValue: container. self setProperty: #searchOffset toValue: place. self setProperty: #searchKey toValue: keys. "override later" self currentHand newKeyboardFocus: container. ^true]. ^false! ! !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! findViaTemplate | list pl cardInst | "Current card is the template. Only search cards in this background. Look at cards directly (not allText). Key must be found in the same field as in the template. HyperCard style (multiple starts of words). Put results in a list, outside the stack." list := self templateMatches. list isEmpty ifTrue: [^ self inform: 'No matches were found. Be sure the current card is mostly blank and only has text you want to match.' translated]. "put up a PluggableListMorph" cardInst := self currentCard. cardInst matchIndex: 0. "establish entries" cardInst results at: 1 put: list. self currentPage setProperty: #myStack toValue: self. "way to get back" pl := PluggableListMorph new on: cardInst list: #matchNames selected: #matchIndex changeSelected: #matchIndex: menu: nil "#matchMenu:shifted:" keystroke: nil. self currentHand attachMorph: (self formatList: pl). ! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! assureFlapOfLabel: aTitle withContents: aString "Answer an info flap with the given title and contents. If one exists in the project, use that, else create one & insert it in the world. Answer the flap tab." | allFlapTabs aTab | allFlapTabs := Project current world localFlapTabs, Project current world extantGlobalFlapTabs. aTab := allFlapTabs detect: [:ft | ft flapID = aTitle] ifNone: [nil]. aTab ifNotNil: [^ aTab]. "already present" aTab := self openInfoFlapWithLabel: aTitle helpContents: aString edge: #left. aTab bottom: Project current world bottom. self cleanUpFlapTabsOnLeft. aTab hideFlap. aTab referent show. aTab show. ^ aTab " ScriptingSystem assureFlapOfLabel: 'Egg Sample' withContents: EventRollMorph basicNew helpString "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! cleanUpFlapTabsOnLeft "Make sure the flap tabs on the left of the screen line up nicely, making best use of realestate." | tabsOnLeft current | tabsOnLeft := ((Project current world localFlapTabs, Project current world extantGlobalFlapTabs) select: [:f | f edgeToAdhereTo = #left]) sort: [:a :b | a top <= b top]. current := SugarNavigatorBar showSugarNavigator ifTrue: [75] ifFalse: [0]. tabsOnLeft do: [:aTab | aTab top: (current min: Project current world height - aTab height). current := aTab bottom + 2]. " ScriptingSystem cleanUpFlapTabsOnLeft "! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:25'! openInfoFlapWithLabel: aTitle helpContents: aString edge: anEdge "Open an info flap with the given label, contents, and edge" | aPlug outer leftStrip rightStrip titleRow aDismissButton aFlapTab | Preferences enable: #scrollBarsOnRight. Preferences enable: #inboardScrollbars. aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab referentMargin: 0 @ Project current world sugarAllowance. outer := HelpFlap newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #morphicLayerNumber toValue: 26. leftStrip := Morph new beTransparent. leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip beTransparent. rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. outer addMorphBack: rightStrip. outer clipSubmorphs: true. titleRow := AlignmentMorph newRow. titleRow borderColor: Color veryVeryLightGray; borderWidth: 1. titleRow hResizing: #spaceFill; vResizing: #shrinkWrap. titleRow beTransparent. aDismissButton := aFlapTab tanOButton. aDismissButton actionSelector: #dismissViaHalo. titleRow addMorphFront: aDismissButton. titleRow addTransparentSpacerOfSize: 8 @ 0. titleRow addMorphBack: (StringMorph contents: aTitle font: Preferences standardEToysTitleFont). rightStrip addMorph: titleRow. aPlug := PluggableTextMorph new. aPlug width: 540. aPlug setText: aString. aPlug textMorph beAllFont: Preferences standardEToysFont. aPlug retractable: false; scrollBarOnLeft: false. aPlug hScrollBarPolicy: #never. aPlug borderColor: ScriptingSystem borderColor. aPlug setNameTo: aTitle. aPlug hResizing: #spaceFill. aPlug vResizing: #spaceFill. rightStrip addMorphBack: aPlug. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: aTitle edge: anEdge color: (Color r: 0.677 g: 0.935 b: 0.484). aFlapTab submorphs first beAllFont: Preferences standardEToysFont. Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. anEdge == #left ifTrue: [aFlapTab position: (outer left @ outer top). outer extent: (540 @ Project current world height)]. anEdge == #right ifTrue: [aFlapTab position: ((Project current world right - aFlapTab width) @ Project current world top). outer extent: (540 @ Project current world height)]. outer beFlap: true. outer color: Color green veryMuchLighter. aPlug textMorph lock. aFlapTab referent hide. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. Project current world doOneCycle. aPlug width: 540. aPlug setText: aString. "hmm, again" aPlug color: outer color. aPlug borderWidth: 0. aPlug textMorph contents: aString wrappedTo: 520. aFlapTab applyThickness: 560. aFlapTab fitOnScreen. aFlapTab referent show. ^ aFlapTab! ! !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:24'! systemQueryPhraseWithActionString: aString labelled: anotherString "Answer a system-query-phrase with the give action-string and label." ^ Project current world presenter systemQueryPhraseWithActionString: aString labelled: anotherString! ! !StandardViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 20:24'! currentVocabulary "Answer the vocabulary currently associated with the receiver" | aSym aVocab | aSym := self valueOfProperty: #currentVocabularySymbol ifAbsent: [nil]. aSym ifNil: [aVocab := self valueOfProperty: #currentVocabulary ifAbsent: [nil]. aVocab ifNotNil: [aSym := aVocab vocabularyName. self removeProperty: #currentVocabulary. self setProperty: #currentVocabularySymbol toValue: aSym]]. ^ aSym ifNotNil: [Vocabulary vocabularyNamed: aSym] ifNil: [(self world ifNil: [Project current world]) currentVocabularyFor: scriptedPlayer]! ! !SugarLauncher methodsFor: 'commands' stamp: 'ct 9/12/2020 14:07'! viewSource Project current world addDeferredUIMessage: [ Project current world showSourceKeyHit].! ! !SugarNavTab methodsFor: 'positioning' stamp: 'ct 9/11/2020 20:24'! occupyTopRightCorner "Make the receiver be the correct size, and occupy the top-right corner of the screen." | worldBounds toUse | worldBounds := Project current world bounds. " toUse := Preferences useArtificialSweetenerBar ifFalse: [75] ifTrue: [(ActiveWorld extent >= (1200 @ 900)) ifTrue: [75] ifFalse: [40]]." toUse := 40. "Trying for the moment to use the smaller icon always when in this mode." referent height: toUse; resizeButtonsAndTabTo: toUse. self extent: toUse @ toUse. self topRight: worldBounds topRight! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelp " SugarNavigatorBar putUpInitialBalloonHelp " | suppliesButton b1 b2 p b | suppliesButton := paintButton owner submorphs detect: [:e | e isButton and: [e actionSelector = #toggleSupplies]]. b1 := BalloonMorph string: self paintButtonInitialExplanation for: paintButton corner: #topRight force: false. b2 := BalloonMorph string: self suppliesButtonInitialExplanation for: suppliesButton corner: #topLeft force: true. p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. p addMorph: b1. p addMorph: b2. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! putUpInitialBalloonHelpFor: quads "Given a list of quads of the form (see senders for examples), put up initial balloon help for them." " SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((doNewPainting 'make a new painting' topRight false) (toggleSupplies 'open the supplies bin' topLeft true)) SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((showNavBar 'show the tool bar' bottomLeft false) (hideNavBar 'hide the tool bar' bottomLeft false)) " | b1 p b | p := PasteUpMorph new. p clipSubmorphs: false. p color: Color transparent. p borderWidth: 0. quads do: [:aQuad | (submorphs first submorphs detect: [:e | e isButton and: [e actionSelector = aQuad first]] ifNone: [nil]) ifNotNil: [:aButton | b1 := BalloonMorph string: aQuad second for: aButton corner: aQuad third force: aQuad fourth. p addMorph: b1]]. b := BalloonMorph string: p for: self world corner: #bottomLeft. b color: Color transparent. b borderWidth: 0. [(Delay forSeconds: 1) wait. b popUp] fork.! ! !SugarNavigatorBar methodsFor: 'help flap' stamp: 'ct 9/12/2020 14:37'! buildAndOpenHelpFlap "Called only when flaps are being created afresh." | aFlapTab outer leftStrip rightStrip aGuide | aFlapTab := FlapTab new. aFlapTab assureExtension visible: false. aFlapTab setProperty: #rigidThickness toValue: true. outer := AlignmentMorph newRow. outer assureExtension visible: false. outer clipSubmorphs: true. outer beTransparent. outer vResizing: #spaceFill; hResizing: #spaceFill. outer layoutInset: 0; cellInset: 0; borderWidth: 0. outer setProperty: #wantsHaloFromClick toValue: false. leftStrip := Morph new beTransparent. "This provides space for tabs to be seen." leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. leftStrip width: 20. leftStrip hResizing: #rigid; vResizing: #spaceFill. outer addMorphBack: leftStrip. rightStrip := AlignmentMorph newColumn. rightStrip color: (Color green veryMuchLighter alpha: 0.2). rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. rightStrip setProperty: #wantsHaloFromClick toValue: false. outer addMorphBack: rightStrip. outer clipSubmorphs: true. aGuide := QuickGuideMorph new. aGuide initializeIndexPage. " aGuide order: QuickGuideMorph defaultOrder. " QuickGuideMorph loadIndexAndPeekOnDisk. aGuide loadPages. rightStrip addMorphBack: aGuide. aGuide beSticky. aFlapTab referent ifNotNil: [aFlapTab referent delete]. aFlapTab referent: outer. aFlapTab setName: 'Help' translated edge: #left color: (Color r: 0.677 g: 0.935 b: 0.484). Project current world addMorphFront: aFlapTab. aFlapTab adaptToWorld: Project current world. aFlapTab computeEdgeFraction. aFlapTab position: outer left @ outer top. outer extent: 462 @ Project current world height. outer beFlap: true. outer beTransparent. aFlapTab referent hide. aFlapTab referentMargin: 0 at self height. aFlapTab openFully. outer beSticky. leftStrip beSticky. rightStrip beSticky. aFlapTab applyThickness: 462. aFlapTab fitOnScreen. aFlapTab referent show. aFlapTab show. aFlapTab makeFlapCompact: true. aFlapTab setToPopOutOnDragOver: false. Flaps addGlobalFlap: aFlapTab. Project current world addGlobalFlaps. ScriptingSystem cleanUpFlapTabsOnLeft! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/12/2020 14:53'! gotoAnother EToyProjectHistoryMorph new position: self currentHand position; openInWorld ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! makeProjectNameLabel | t | projectNameField := SugarRoundedField new. t := UpdatingStringMorph new. t setProperty: #okToTextEdit toValue: true. t putSelector: #projectNameChanged:. t getSelector: #projectName. projectNameField backgroundColor: self color. t target: self. t useStringFormat. t beSticky. t label: Project current name font: (StrikeFont familyName: 'BitstreamVeraSans' size: 24). t color: Color black. t width: projectNameField width - 10. projectNameField label: t. projectNameField setBalloonText: self projectNameFieldBalloonHelp. projectNameField on: #mouseDown send: #mouseDown: to: t. projectNameField on: #mouseUp send: #mouseUp: to: t. self resizeProjectNameField. ^projectNameField.! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectName ^ Project current name ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! projectNameChanged: aString Project current renameTo: aString. ! ! !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! shareMenu | menu item ext | menu := MenuMorph new. ext := 200 at 50. #((stopSharing makePrivateLabelIn:) (startSharing makeMyNeighborhoodLabelIn:) "(shareThisWorld makeBadgeLabelIn:)") do: [:pair | item := MenuItemMorph new contents: ''; target: self; selector: pair first; arguments: #(). item color: Color black. item addMorph: (self perform: pair second with: ext). item setProperty: #minHeight toValue: ext y. item fitContents. item extent: ext. item setProperty: #selectionFillStyle toValue: (Color gray alpha: 0.5). menu addMorphBack: item. ]. menu color: Color black. menu borderColor: Color white. ^ menu invokeModalAt: shareButton position + (10 at 20) in: Project current world allowKeyboard: false.! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/12/2020 14:37'! startNebraska | nebraska | Project current world remoteServer: nil. Project current world submorphs do: [:e | (e isMemberOf: NebraskaServerMorph) ifTrue: [e delete]]. nebraska := NebraskaServerMorph serveWorld. SugarLauncher current offerStreamTube: 'sqk-nebraska' inBackgroundOnPort: [nebraska listeningPort]. ! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! startP2P listener ifNotNil: [listener stopListening]. listener ifNil: [listener := SugarListenerMorph new]. listener position: -200@ -200. Project current world addMorphBack: listener. listener startListening. SugarLauncher current offerStreamTube: 'sqk-etoy-p2p' inBackgroundOnPort: [listener listeningPort].! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! stopSharing SugarLauncher current leaveSharedActivity. listener ifNotNil: [listener stopListening. listener := nil]. Project current world remoteServer: nil. Project current world submorphs do: [:ea | (ea isMemberOf: NebraskaServerMorph) ifTrue: [ea delete]]. self sharingChanged.! ! !SugarNavigatorBar methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:22'! undoButtonAppearance | wording | undoButton ifNotNil: [ Project current world commandHistory undoEnabled ifTrue: [undoButton enabled] ifFalse: [undoButton disabled]. wording := self undoButtonWording. undoButton setBalloonText: wording. ]. ! ! !InteriorSugarNavBar methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:12'! doNewPainting "Make a new painting" | worldlet aRect | self currentWorld assureNotPaintingElse: [^ self]. worldlet := self ownerThatIsA: Worldlet. aRect := (worldlet topLeft + (0 @ self height)) corner: worldlet bottomRight. worldlet makeNewDrawing: (self currentEvent copy setPosition: aRect center).! ! !SugarNavigatorBar class methodsFor: 'utilitity' stamp: 'ct 9/11/2020 20:22'! findAnythingMorph ^ FileList2 morphicViewProjectLoader2InWorld: Project current world title: 'Find...' translated reallyLoad: true dirFilterType: #initialDirectoryList isGeneral: true.! ! !SugarRoundedField methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:22'! resizeLabel | small | (label notNil and: [label hasFocus not]) ifTrue: [ label width: self width - 10. small :=self height < 45. label label: Project current world project name font: (StrikeFont familyName: 'BitstreamVeraSans' size: (small ifTrue: [15] ifFalse: [24])). label center: self center. label left: self left + 10. self addMorph: label. ]. ! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerTilesMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'me, by name' target: self selector: #attachTileForCode:nodeType: argumentList: {''. aReceiver}. menu add: 'self' target: self selector: #attachTileForCode:nodeType: argumentList: {'self'. VariableNode}. menu add: '_ (assignment)' target: self selector: #attachTileForCode:nodeType: argumentList: {''. nil}. menu add: '"a Comment"' target: self selector: #attachTileForCode:nodeType: argumentList: {'"a comment"\' withCRs. CommentNode}. menu submorphs last color: Color blue. menu add: 'a Number' target: self selector: #attachTileForCode:nodeType: argumentList: {'5'. LiteralNode}. menu add: 'a Character' target: self selector: #attachTileForCode:nodeType: argumentList: {'$z'. LiteralNode}. menu add: '''abc''' target: self selector: #attachTileForCode:nodeType: argumentList: {'''abc'''. LiteralNode}. menu add: 'a Symbol constant' target: self selector: #attachTileForCode:nodeType: argumentList: {'#next'. LiteralNode}. menu add: 'true' target: self selector: #attachTileForCode:nodeType: argumentList: {'true'. VariableNode}. menu add: 'a Test' target: self selector: #attachTileForCode:nodeType: argumentList: {'true ifTrue: [self] ifFalse: [self]'. MessageNode}. menu add: 'a Loop' target: self selector: #attachTileForCode:nodeType: argumentList: {'1 to: 10 do: [:index | self]'. MessageNode}. menu add: 'a Block' target: self selector: #attachTileForCode:nodeType: argumentList: {'[self]'. BlockNode}. menu add: 'a Class or Global' target: self selector: #attachTileForCode:nodeType: argumentList: {'Character'. LiteralVariableNode}. menu add: 'a Reply' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. ReturnNode}. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! offerVarsMenuFor: aReceiver in: aLexiconModel "Offer a menu of tiles for assignment and constants" | menu instVarList cls | menu := MenuMorph new addTitle: 'Hand me a tile for...'. menu addLine. menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. menu submorphs last color: Color red darker. menu addLine. menu add: 'new temp variable' target: self selector: #attachTileForCode:nodeType: argumentList: {'| temp | temp'. TempVariableNode}. instVarList := OrderedCollection new. cls := aReceiver class. [instVarList addAllFirst: cls instVarNames. cls == aLexiconModel limitClass] whileFalse: [cls := cls superclass]. instVarList do: [:nn | menu add: nn target: self selector: #instVarTile: argument: nn]. menu popUpInWorld: self world.! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! attachToHand "Adjust my look and attach me to the hand" self roundedCorners. self currentHand attachMorph: self. Preferences tileTranslucentDrag ifTrue: [self lookTranslucent. self align: self center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ self align: self topLeft with: self currentHand position + self cursorBaseOffset].! ! !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! instVarTile: aName "Make and put into hand a tile for an instance variable" | sm | sm := ((VariableNode new name: aName index: 1 type: 1 "LdInstType") asMorphicSyntaxIn: SyntaxMorph new). sm roundedCorners. self currentHand attachMorph: sm. Preferences tileTranslucentDrag ifTrue: [sm lookTranslucent. sm align: sm center with: self currentHand position "+ self cursorBaseOffset"] ifFalse: [ sm align: sm topLeft with: self currentHand position + self cursorBaseOffset]! ! !SyntaxMorph methodsFor: 'scripting' stamp: 'ct 9/12/2020 14:55'! tearOffTile "For a SyntaxMorph, this means give a copy of me" | dup | dup := self duplicate. self currentHand attachMorph: dup. ^ Preferences tileTranslucentDrag ifTrue: [dup lookTranslucent] ifFalse: [dup align: dup topLeft with: self currentHand position + self cursorBaseOffset]! ! !SystemWindow methodsFor: 'events' stamp: 'ct 9/11/2020 20:22'! doFastFrameDrag: grabPoint "Do fast frame dragging from the given point" | offset newBounds outerWorldBounds clearArea | outerWorldBounds := self boundsIn: nil. offset := outerWorldBounds origin - grabPoint. clearArea := Project current world clearArea. newBounds := outerWorldBounds newRectFrom: [:f | | p selector | p := Sensor cursorPoint. (self class dragToEdges and: [(selector := self dragToEdgesSelectorFor: p in: clearArea) notNil]) ifTrue: [clearArea perform: selector] ifFalse: [p + offset extent: outerWorldBounds extent]]. self bounds: newBounds; comeToFront! ! !CollapsedMorph methodsFor: 'collapse/expand' stamp: 'ct 9/12/2020 14:39'! uncollapseToHand "Hand the uncollapsedMorph to the user, placing it in her hand, after remembering appropriate state for possible future use" | nakedMorph | nakedMorph := uncollapsedMorph. uncollapsedMorph := nil. nakedMorph setProperty: #collapsedPosition toValue: self position. mustNotClose := false. "so the delete will succeed" self delete. self currentHand attachMorph: nakedMorph.! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 18:01'! rotateWindows "Rotate the z-ordering of the windows." self currentEvent shiftPressed ifTrue: [self sendTopWindowBackOne] ifFalse: [self sendTopWindowToBack].! ! !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 20:21'! sendTopWindowBackOne "Rotate the window-list one downward, i.e., make the bottommost one be the active one, pushing the receiver to next-to-topmost." | dows | dows := Project current world submorphs select: [:m | m isSystemWindow]. dows ifNotEmpty: [dows last expand; comeToFront]! ! !TextEditor methodsFor: 'menu commands' stamp: 'ct 9/11/2020 18:02'! offerMenuFromEsc: aKeyboardEvent "The escape key was hit while the receiver has the keyboard focus; take action." self currentEvent shiftPressed ifFalse: [ self raiseContextMenu: aKeyboardEvent]. ^ true! ! !ThreePhaseButtonMorph methodsFor: 'button' stamp: 'ct 9/11/2020 18:02'! doButtonAction "Perform the action of this button. Subclasses may override this method. The default behavior is to send the button's actionSelector to its target object with its arguments." | args | (target notNil and: [actionSelector notNil]) ifTrue: [ args := actionSelector numArgs > arguments size ifTrue: [arguments copyWith: self currentEvent] ifFalse: [arguments]. Cursor normal showWhile: [ target perform: actionSelector withArguments: args]. target isMorph ifTrue: [target changed]].! ! !TileMorph methodsFor: 'arrows' stamp: 'ct 9/11/2020 18:02'! showSuffixChoices "The suffix arrow has been hit, so respond appropriately" | plusPhrase phrase pad outer num | self currentEvent shiftPressed ifTrue: [^ self wrapPhraseInFunction]. (phrase := self ownerThatIsA: PhraseTileMorph orA: FunctionTile) ifNil: [nil]. (type == #literal) & (literal isNumber) ifTrue: ["Tile is a constant number" (phrase isNil or: [phrase finalTilePadSubmorph == owner]) "pad" ifTrue: ["we are adding the first time (at end of our phrase)" plusPhrase := self phraseForOp: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). owner acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. num := plusPhrase firstSubmorph firstSubmorph. num deleteSuffixArrow]]. (#(function expression parameter) includes: type) ifTrue: [pad := self ownerThatIsA: TilePadMorph. plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: self. pad topEditor scriptEdited "recompile"]. type = #operator ifTrue: ["Tile is accessor of an expression" phrase resultType == #Number ifTrue: [outer := phrase ownerThatIsA: PhraseTileMorph orA: TimesRepeatTile. pad := self ownerThatIsA: TilePadMorph. outer ifNotNil: [(outer lastSubmorph == pad or: [true]) ifTrue: [ "first time" plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. plusPhrase firstSubmorph removeAllMorphs; addMorph: phrase. "car's heading" pad topEditor scriptEdited "recompile & deal with carets"]]]]. (self topEditor ifNil: [phrase ifNil: [^ self]]) enforceTileColorPolicy! ! !TileMorph methodsFor: 'code generation' stamp: 'ct 9/11/2020 20:21'! acceptNewLiteral "Tell the scriptEditor who I belong to that I have a new literal value." | topScript | topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript installWithNewLiteral]. (self ownerThatIsA: ViewerLine) ifNotNil: [:aLine | (self ownerThatIsA: PhraseTileMorph) ifNotNil: [aLine removeHighlightFeedback. self layoutChanged. Project current world doOneSubCycle. aLine addCommandFeedback: nil]]! ! !TileMorph methodsFor: 'misc' stamp: 'ct 9/12/2020 14:56'! handReferentMorph "Hand the user the actual morph referred to" | aMorph surrogate | ((aMorph := actualObject costume) isMorph and: [aMorph isWorldMorph not]) ifTrue: [ surrogate := CollapsedMorph collapsedMorphOrNilFor: aMorph. surrogate ifNotNil: [surrogate uncollapseToHand] ifNil: [self currentHand attachMorph: aMorph]].! ! !SymbolListTile methodsFor: 'user interface' stamp: 'ct 9/11/2020 20:22'! choices "Answer the list of current choices for the receiver's symbol" dataType == #ScriptName ifTrue: "Backward compatibility with old tiles" [^ Project current world presenter allKnownUnaryScriptSelectors]. ^ choices! ! !ScriptNameTile methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:29'! choices "Answer the current list of choices" ^ Project current world presenter allKnownUnaryScriptSelectors! ! !TileMorph class methodsFor: '*Etoys-Squeakland-utilities' stamp: 'ct 9/11/2020 20:21'! implicitSelfInTilesChanged "The implicitSelfInTiles preference changed. Caution: although this may appear to have no senders in the image, it is in fact invoked when the implicitSelfInTiles preference is toggled... so please do not delete it." Smalltalk isMorphic ifFalse: [^ self]. Project current world allScriptEditorsInProject do: [:scriptEditor | scriptEditor install]. Project current world allViewersInProject do: [:viewer | viewer enforceImplicitSelf]. " (Preferences buttonForPreference: #implicitSelfInTiles) openInHand. "! ! !TileMorphTest methodsFor: 'testing' stamp: 'ct 9/12/2020 14:56'! testAssignmentTile "self debug: #testAssignmentTile" | player viewer tile phrase | player := Morph new assuredPlayer. viewer := CategoryViewer new invisiblySetPlayer: player. viewer makeSetter: #(#getX #Number) event: nil from: player costume. phrase := self currentHand firstSubmorph. self currentHand removeAllMorphs. tile := phrase submorphs second. self assert: tile codeString = 'setX: '. tile arrowAction: 1. self assert: tile codeString = 'setX: self getX + '.! ! !TypeListTile methodsFor: 'mouse handling' stamp: 'ct 9/12/2020 14:56'! showOptions | topScript | suffixArrow ifNotNil: [(suffixArrow bounds containsPoint: self currentHand cursorPoint) ifTrue: [^ super showOptions]]. topScript := self outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. topScript ifNotNil: [topScript handUserParameterTile]! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice at: aPointOrNil "UserDialogBoxMorph confirm: 'Make your choice carefully' withCRs title: 'Do you like chocolate?' trueChoice: 'Oh yessir!!' falseChoice: 'Not so much...'" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: 1; registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponse! ! !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice default: default triggerAfter: seconds at: aPointOrNil "UserDialogBoxMorph confirm: 'I like hot java' title: 'What do you say?' trueChoice: 'You bet!!' falseChoice: 'Nope' default: false triggerAfter: 12 at: 121 at 212" ^self new title: titleString; message: aString; createButton: trueChoice translated value: true; createButton: falseChoice translated value: false; createCancelButton: 'Cancel' translated translated value: nil; selectedButtonIndex: (default ifTrue: [1] ifFalse: [2]); registerKeyboardShortcuts; preferredPosition: (aPointOrNil ifNil: [Project current world center]); getUserResponseAfter: seconds! ! !UserText methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:56'! keyStroke: evt "Handle a keystroke event." | newSel | super keyStroke: evt. evt hand keyboardFocus == self ifFalse: [self releaseEditor. ^ self]. newSel := self editor selectionInterval. "restore editor state" self refreshParagraph. self editor selectFrom: newSel first to: newSel last. wrapFlag ifFalse: [self fullBounds right > owner right ifTrue: [self wrapFlag: true. self right: owner right. self refreshParagraph. self editor selectFrom: text string size + 1 to: text string size]].! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs fourth topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 0)). aMorph useRoundedCorners; beTransparent; borderWidth: 2; borderColor: (Color r: 1.0 g: 0.548 b: 0.452); lock. aMorph setProperty: #highlight toValue: true. ^ Project current world addMorphFront: aMorph! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addGetterFeedback "Add feedback during mouseover of a getter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: (self firstAlignmentMorph ifNil: [self submorphs last bottomRight] ifNotNil: [:m | m bottomLeft])). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem getterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil. " Color fromUser (Color r: 1.0 g: 0.355 b: 0.839) "! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! addSetterFeedback "Add screen feedback showing what would be torn off to make a setter" | aMorph | aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: self bounds bottomRight). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem setterFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! removeHighlightFeedback "Remove any existing highlight feedback" ^ Project current world removeHighlightFeedback ! ! !ViewerLine methodsFor: '*Etoys-Squeakland-slot' stamp: 'ct 9/11/2020 20:20'! addCommandFeedback: evt "Add screen feedback showing what would be torn off in a drag" | aMorph | aMorph := RectangleMorph new bounds: ((submorphs third topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 1)). aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. ^ Project current world addHighlightMorph: aMorph for: nil! ! !Vocabulary class methodsFor: '*Etoys-Squeakland-type vocabularies' stamp: 'ct 9/11/2020 20:19'! typeChoicesForUserVariables "Answer a list of all user-choosable value types for variables." | aList | aList := #(Boolean Color CustomEvents Graphic Number Patch Player Point ScriptName Sound String) copy. self currentWorld isKedamaPresent ifFalse: [ ^ aList copyWithout: #Patch]. ^ aList " Vocabulary typeChoicesForUserVariables "! ! !WorldState methodsFor: 'hands' stamp: 'ct 9/24/2020 13:50'! primaryHand ^ ActiveHand value ifNil: [self hands at: 1 ifAbsent: [nil]]! ! !WorldState methodsFor: 'hands' stamp: 'ct 9/24/2020 13:50'! removeHand: aHandMorph "Remove the given hand from the list of hands for this world." (hands includes: aHandMorph) ifFalse: [^ self]. self currentHand == aHandMorph ifTrue: [self halt: 'ct: Cannot remove the active hand because it is a dynamic variable. What should we do now? Panic!!!!1']. hands := hands copyWithout: aHandMorph.! ! !WorldState methodsFor: 'stepping' stamp: 'ct 9/16/2020 18:27'! runLocalStepMethodsIn: aWorld "Run morph 'step' methods (LOCAL TO THIS WORLD) whose time has come. Purge any morphs that are no longer in this world. ar 3/13/1999: Remove buggy morphs from the step list so that they don't raise repeated errors." | now morphToStep stepTime | now := Time millisecondClockValue. aWorld becomeActiveDuring: [ self triggerAlarmsBefore: now. stepList ifEmpty: [^ self]. (now < lastStepTime or: [now - lastStepTime > 5000]) ifTrue: [ self adjustWakeupTimes: now]. "clock slipped" [stepList notEmpty and: [stepList first scheduledTime < now]] whileTrue: [ lastStepMessage := stepList removeFirst. morphToStep := lastStepMessage receiver. (morphToStep shouldGetStepsFrom: aWorld) ifTrue: [ lastStepMessage value: now. lastStepMessage ifNotNil: [ stepTime := lastStepMessage stepTime ifNil: [morphToStep stepTime]. lastStepMessage scheduledTime: now + (stepTime max: 1). stepList add: lastStepMessage]]. lastStepMessage := nil]. lastStepTime := now].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/16/2020 18:44'! doOneCycleNowFor: aWorld "Immediately do one cycle of the interaction loop. This should not be called directly, but only via doOneCycleFor:" | capturingGesture | DisplayScreen checkForNewScreenSize. capturingGesture := false. "self flag: #bob. " "need to consider remote hands in lower worlds" "process user input events" LastCycleTime := Time millisecondClockValue. self handsDo: [:hand | hand becomeActiveDuring: [ hand processEvents. capturingGesture := capturingGesture or: [hand isCapturingGesturePoints]]]. "The gesture recognizer needs enough points to be accurate. Therefore morph stepping is disabled while capturing points for the recognizer" capturingGesture ifFalse: [ aWorld runStepMethods. "there are currently some variations here" self displayWorldSafely: aWorld].! ! !WorldState methodsFor: 'update cycle' stamp: 'ct 9/16/2020 18:47'! doOneSubCycleFor: aWorld self flag: #deprecate. "ct: Historically, global state was preserved here. Since the introduction of ActiveHandVariable, this is no longer necessary, so this is equivalent to #doOneCycleFor:. However, let's keep this possibly valuable hook for now." ^ self doOneCycleFor: aWorld! ! !WorldState methodsFor: '*MorphicExtras-update cycle' stamp: 'ct 9/12/2020 15:18'! doOneCycleInBackground "Do one cycle of the interactive loop. This method is called repeatedly when this world is not the active window but is running in the background." self halt. "not ready for prime time" "process user input events, but only for remote hands" self handsDo: [:hand | (hand isKindOf: RemoteHandMorph) ifTrue: [ hand becomeActiveDuring: [ hand processEvents]]]. self runStepMethods. self displayWorldSafely.! ! !ZASMCameraMarkMorph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:02'! setTransition "Set the transition" ^ self setTransition: self currentEvent! ! WorldState removeSelector: #activeHand! WorldState removeSelector: #activeHand:! !MorphicProject reorganize! ('display' invalidate noDisplayDuring: previewImageForm restore viewLocFor:) ('initialize' initMorphic initialize installPasteUpAsWorld: openProject: setWorldBackground:) ('testing' isMorphic) ('menu messages' assureNavigatorPresenceMatchesPreference makeThumbnail) ('utilities' addItem:toMenu:selection:color:thumbnail: composeDisplayTextIntoForm: createViewIfAppropriate do:withProgressInfoOn:label: findAFolderForProject:label: findProjectView: jumpToProject offerMenu:from:shifted: pointerMoved setAsBackground: showImage:named: textWindows) ('flaps support' assureFlapIntegrity cleanseDisabledGlobalFlapIDsList enableDisableGlobalFlap: flapsSuppressed: globalFlapEnabledString: globalFlapWithIDEnabledString: isFlapEnabled: isFlapIDEnabled: setFlaps suppressFlapsString toggleFlapsSuppressed) ('docking bars support' assureMainDockingBarPresenceMatchesPreference createOrUpdateMainDockingBar dockingBar dockingBar: removeMainDockingBar showWorldMainDockingBar showWorldMainDockingBar: showWorldMainDockingBarString toggleShowWorldMainDockingBar) ('file in/out' acceptProjectDetails: armsLengthCommand:withDescription: compressFilesIn:to:in: compressFilesIn:to:in:resources: exportSegmentInSexpWithChangeSet:fileName:directory:withoutInteraction: exportSegmentWithChangeSet:fileName:directory: exportSegmentWithChangeSet:fileName:directory:withoutInteraction: loadFromServer: noteManifestDetailsIn: storeSegment storeSegmentNoFile) ('enter' clearGlobalState enterAsActiveSubprojectWithin: finalEnterActions: finalExitActions: initializeMenus isIncompletelyLoaded resumeEventRecorder: scheduleProcessForEnter startUpActions suspendProcessForDebug terminateProcessForLeave wakeUpTopWindow) ('squeaklet on server' enterIfThereOrFind: openBlankProjectNamed:) ('release' deletingProject: myPlayerClasses prepareForDelete) ('language' chooseNaturalLanguage updateLocaleDependents) ('*Etoys-stack' currentStack currentStack:) ('editors' bitEdit: bitEdit:at:scale: editCharacter:ofFont: formEdit: formViewClass openImage:name:saveResource:) ('futures' future:do:at:args: future:send:at:args:) ('protocols' currentVocabulary) ('active process' spawnNewProcess spawnNewProcessAndTerminateOld: spawnNewProcessIfThisIsUI: uiProcess uiProcess:) ('transcripter' displayTranscripter: initializeParagraphForTranscripter:) ('*46Deprecated') ('*51Deprecated') ('subprojects' addProject: subProjects) ('accessing' color) ('updating' applyUserInterfaceTheme canApplyUserInterfaceTheme) ('*Etoys-Worlds' setWorld:) ('scheduling & debugging' addDeferredUIMessage: debuggerClass fatalDrawingError: interruptCleanUpFor: recursiveError: restoreDisplay syntaxError:) ! !Project reorganize! ('accessing' changeSet color displayDepth displayDepth: findProjectView: forgetExistingURL labelString lastDirectory: lastSavedAtSeconds name nameAdjustedForDepth nextProject previousProject projectChangeSet renameTo: storeNewPrimaryURL: thumbnail thumbnail: transcript uiManager urlList viewSize viewSize: world) ('active process' depth spawnNewProcessIfThisIsUI: uiProcess) ('displaying' displayDepthChanged displaySizeChanged displayZoom: imageForm imageFormOfSize:depth: invalidate noDisplayDuring: previewImageForm restore restoreDisplay showZoom shrinkDisplay viewLocFor:) ('file in/out' armsLengthCommand:withDescription: assureIntegerVersion bumpVersion: couldBeSwappedOut currentVersionNumber decideAboutCreatingBlank: doArmsLengthCommand: downloadUrl ensureChangeSetNameUnique exportSegmentFileName:directory: exportSegmentFileName:directory:withoutInteraction: exportSegmentInSexpWithChangeSet:fileName:directory:withoutInteraction: exportSegmentWithCatagories:classes:fileName:directory: exportSegmentWithChangeSet:fileName:directory: findAFolderToLoadProjectFrom findAFolderToStoreProjectIn fromMyServerLoad: hasBadNameForStoring htmlPagePrototype loadFromServer loadFromServer: objectForDataStream: primaryServer primaryServerIfNil: projectExtension restoreReferences revert saveAs saveForRevert serverList squeakletDirectory storeAttributeKey:value:on: storeAttributesOn: storeDataOn: storeHtmlPageIn: storeManifestFileIn: storeOnServer storeOnServerAssumingNameValid storeOnServerInnards storeOnServerShowProgressOn:forgetURL: storeOnServerWithProgressInfo storeOnServerWithProgressInfoOn: storeSegment storeSegmentNoFile storeSomeSegment storeToMakeRoom tryToFindAServerWithMe url urlForLoading versionForFileName versionFrom: versionedFileName writeFileNamed:fromDirectory:toServer: writeStackText:in:registerIn:) ('flaps support' flapsSuppressed flapsSuppressed: showSharedFlaps) ('initialization' initialExtent initialProject initialize installNewDisplay:depth: openProject: setChangeSet: setServer windowActiveOnFirstClick windowReqNewLabel:) ('language' chooseNaturalLanguage localeChanged localeID naturalLanguage updateLocaleDependents) ('menu messages' assureNavigatorPresenceMatchesPreference doWeWantToRename exit fileOut installProjectPreferences makeThumbnail saveProjectPreferences validateProjectNameIfOK:) ('printing' addSubProjectNamesTo:indentation: printOn:) ('project parameters' initializeProjectParameters initializeProjectPreferences noteThatParameter:justChangedTo: parameterAt: parameterAt:ifAbsent: projectParameterAt: projectParameterAt:ifAbsent: projectParameterAt:ifAbsentPut: projectParameterAt:put: projectParameters projectPreferenceAt: projectPreferenceAt:ifAbsent: projectPreferenceFlagDictionary rawParameters removeParameter:) ('release' addDependent: canDiscardEdits close delete deletingProject: forget okToChange prepareForDelete release removeChangeSetIfPossible windowIsClosing) ('resources' abortResourceLoading resourceDirectoryName resourceManager resourceManager: resourceUrl startResourceLoading storeResourceList:in:) ('SuperSwiki' tellAFriend tellAFriend:) ('*sound' beep) ('enter' enter enter: enter:revert:saveForRevert: finalEnterActions: finalExitActions: loadState saveState scheduleProcessForEnter startUpActions terminateProcessForLeave wakeUpTopWindow) ('utilities' addItem:toMenu:selection:color:thumbnail: buildJumpToMenu: composeDisplayTextIntoForm: do:withProgressInfoOn:label: findAFolderForProject:label: jumpToProject jumpToSelection: offerMenu:from:shifted: pointerMoved setAsBackground: showImage:named: textWindows) ('squeaklet on server' enterIfThereOrFind: openBlankProjectNamed:) ('futures' future:do:at:args: future:send:at:args:) ('editors' bitEdit: bitEdit:at:scale: editCharacter:ofFont: formEdit: formViewClass openImage:name:saveResource:) ('protocols' currentVocabulary) ('transcripter' displayTranscripter: initializeParagraphForTranscripter:) ('*51Deprecated') ('sub-projects & hierarchy' addProject: beTopProject children isTopProject liftSubProjects parent setParent: subProjects withChildrenDo:) ('enter - recovery' enterForEmergencyRecovery suspendProcessForDebug) ('testing' isCurrentProject isIncompletelyLoaded isMorphic) ('shrinking' removeAllOtherProjects) ('*ST80-Testing' isMVC) ('*Etoys-Squeakland-file in/out' defaultFolderForAutoSaving latestProjectVersionsFromFileEntries: storeOnServerWithNoInteraction storeOnServerWithNoInteractionInnards storeOnServerWithNoInteractionThenQuit storePngThumbnailIn: version: writeForExportInSexp:withSources:inDirectory:changeSet:) ('*Etoys-Squeakland-displaying' displayExtent) ('*Etoys-Squeakland-menu messages' displayProgressWithJump: displaySavingProgress) ('*Etoys-Squeakland-flaps support' helpGuideIfOpen) ('*Etoys-Squeakland-initialization' installVirtualDisplayIfNeededFor:) ('*Etoys-Squeakland-sugar' keepSugarProperties:monitor: sugarObjectId sugarObjectId: sugarProperties sugarProperties:) ('*Etoys-Squeakland-language' locales localesString toggleUseLocale updateLocaleDependentsGently updateLocaleDependentsWithPreviousSupplies:gently: useLocaleString) ('*Etoys-Squeakland-accessing' name:) ('*Etoys-Squeakland-release' okToChangeSilently) ('*60Deprecated-accessing' setThumbnail: setViewSize:) ('scheduling & debugging' addDeferredUIMessage: debuggerClass interruptCleanUpFor: interruptName: interruptName:message: interruptName:message:preemptedProcess: interruptName:preemptedProcess: primitiveError: recursiveError: syntaxError:) ('*Morphic-Kernel-accessing') ! PasteUpMorph removeSelector: #activeHand:! !Browser reorganize! ('*46Deprecated') ('*60Deprecated-multi-window support' classHierarchy) ('*Etoys-Squeakland-class functions' buildClassBrowser) ('*Etoys-Squeakland-drag and drop' overwriteDialogHierarchyChange:higher:sourceClassName:destinationClassName:methodSelector:) ('*Etoys-Squeakland-initialize-release' browserWindowActivated) ('*Etoys-Squeakland-message functions' buildMessageBrowser) ('*SUnitTools-class list functions' testRunTests) ('*SUnitTools-menus' testsClassListMenu: testsSystemCategoryMenu:) ('*SUnitTools-system category functions' hasSystemCategoryWithTestsSelected testRunTestsCategory) ('*services-base' browseReference: classCategoryMenuServices: classListMenuServices: messageCategoryMenuServices: methodReference optionalButtonRow selectReference:) ('accessing' contents contents:notifying: contentsSelection couldBrowseAnyClass doItReceiver editSelection editSelection: environment newClassContents noteSelectionIndex:for: request:initialAnswer: selectEnvironment: spawn: suggestCategoryToSpawnedBrowser:) ('annotation' annotation annotation:) ('class comment pane' annotationForClassCommentFor: annotationForClassDefinitionFor: noCommentNagString stripNaggingAttributeFromComment:) ('class functions' addAllMethodsToCurrentChangeSet classCommentText classDefinitionText classListMenu: classListMenu:shifted: classListMenuMore: copyClass createInstVarAccessors defineClass:notifying: editClass editComment explainSpecial: fileOutClass findMethod hierarchy makeNewSubclass plusButtonHit printOutClass removeClass renameClass shiftedClassListMenu: shiftedClassListMenuMore:) ('class list' classIconAt: classList classListIndex classListIndex: classListIndexOf: classListSingleton createHierarchyTreeOf: defaultClassList flattenHierarchyTree:on:indent: flattenHierarchyTree:on:indent:by: flattenHierarchyTree:on:indent:by:format: hasClassSelected hierarchicalClassList recent selectClass: selectClassNamed: selectedClass selectedClassName) ('code pane' aboutToStyle: compileMessage:notifying: showBytecodes) ('controls' decorateButtons) ('copying' veryDeepInner:) ('drag and drop' dragFromClassList: dragFromMessageList: dropOnMessageCategories:at: dropOnSystemCategories:at: wantsMessageCategoriesDrop: wantsSystemCategoriesDrop:) ('initialize-release' classListFrame: classListFrame:fromLeft:width: classListFrame:fromTop:fromLeft:width: defaultBrowserTitle frameOffsetFromTop:fromLeft:width:bottomFraction: labelString methodCategoryChanged setClass: setClass:selector: setSelector: switchesFrame: switchesFrame:fromLeft:width: systemCatSingletonKey:from: systemOrganizer: topConstantHeightFrame:fromLeft:width:) ('message category functions' addCategory alphabetizeMessageCategories buildMessageCategoryBrowser buildMessageCategoryBrowserEditString: canShowMultipleMessageCategories categoryOfCurrentMethod changeMessageCategories: editMessageCategories fileOutMessageCategories highlightMessageList:with: mainMessageCategoryMenu: messageCategoryMenu: printOutMessageCategories removeEmptyCategories removeMessageCategory renameCategory showHomeCategory) ('message category list' categorizeAllUncategorizedMethods hasMessageCategorySelected messageCatListSingleton messageCategoryList messageCategoryListIndex messageCategoryListIndex: messageCategoryListKey:from: messageCategoryListSelection rawMessageCategoryList recategorizeMethodSelector: selectMessageCategoryNamed: selectedMessageCategoryName setOriginalCategoryIndexForCurrentMethod toggleCategorySelectionForCurrentMethod) ('message functions' browseAllCommentsForClass defineMessageFrom:notifying: inspectInstances inspectSubInstances mainMessageListMenu: removeMessage removeMessageFromBrowser) ('message list' addExtraShiftedItemsTo: hasMessageSelected lastMessageName messageHelpAt: messageIconAt: messageIconFor: messageIconHelpFor: messageList messageListIndex messageListIndex: messageListIndexOf: messageListMenu:shifted: reformulateList selectMessageNamed: selectedMessage selectedMessageName selectedMessageName: shiftedMessageListMenu:) ('metaclass' classCommentIndicated classDefinitionIndicated classMessagesIndicated classOrMetaClassOrganizer indicateClassMessages indicateInstanceMessages instanceMessagesIndicated metaClassIndicated metaClassIndicated: selectedClassOrMetaClass selectedClassOrMetaClassName setClassDefinition setClassOrganizer) ('multi-window support' arrowKey:from: browseClassHierarchy isHierarchy isPackage multiWindowName multiWindowNameForState: okToClose restoreMultiWindowState: restoreToCategory:className:protocol:selector:mode:meta: saveMultiWindowState) ('pluggable menus - hooks' classListMenuHook:shifted: messageCategoryMenuHook:shifted: messageListMenuHook:shifted: systemCategoryMenuHook:shifted:) ('self-updating' didCodeChangeElsewhere) ('system category functions' addSystemCategory alphabetizeSystemCategories browseAllClasses buildSystemCategoryBrowser buildSystemCategoryBrowserEditString: changeSystemCategories: classNotFound editSystemCategories fileOutSystemCategory findClass mainSystemCategoryMenu: printOutSystemCategory removeSystemCategory renameSystemCategory systemCatSingletonMenu: systemCategoryMenu: updateSystemCategories) ('system category list' hasSystemCategorySelected indexIsOne indexIsOne: selectCategoryForClass: selectSystemCategory: selectedEnvironment selectedSystemCategory selectedSystemCategoryName systemCatListKey:from: systemCategoryList systemCategoryListIndex systemCategoryListIndex: systemCategorySingleton) ('toolbuilder' buildAndOpenCategoryBrowser buildAndOpenCategoryBrowserLabel: buildAndOpenClassBrowserLabel: buildAndOpenFullBrowser buildAndOpenMessageCategoryBrowserLabel: buildCategoryBrowserWith: buildClassListSingletonWith: buildClassListWith: buildDefaultBrowserWith: buildMessageCategoryListWith: buildMessageListCatSingletonWith: buildMessageListWith: buildSwitchesWith: buildSystemCatListSingletonWith: buildSystemCategoryListWith: buildWith: setMultiWindowFor:) ('traits' addSpecialMenu: addTrait defineTrait:notifying: newClass newTrait) ('user interface' addModelItemsToWindowMenu: defaultWindowColor) ('private' spawnOrNavigateTo:) ! !ActiveWorldVariable class reorganize! ('accessing' default value:during:) ! !ActiveHandVariable class reorganize! ('accessing' default value:during:) ! !ActiveEventVariable class reorganize! ('accessing' default value:during:) ! !Object reorganize! ('accessing' at: at:modify: at:put: basicAt: basicAt:put: basicSize bindWithTemp: enclosedSetElement ifNil:ifNotNilDo: ifNotNilDo: ifNotNilDo:ifNil: in: presenter readFromString: size yourself) ('associating' ->) ('binding' bindingOf:) ('casing' caseOf: caseOf:otherwise:) ('class membership' class inheritsFromAnyIn: isKindOf: isKindOf:orOf: isMemberOf: respondsTo: xxxClass) ('comparing' closeTo: hash identityHashPrintString literalEqual: = ~=) ('converting' adaptToFloat:andCompare: adaptToFloat:andSend: adaptToFraction:andCompare: adaptToFraction:andSend: adaptToInteger:andCompare: adaptToInteger:andSend: adaptToScaledDecimal:andCompare: asOrderedCollection asSetElement asString asStringOrText as: changeClassTo: complexContents mustBeBoolean mustBeBooleanIn: printDirectlyToDisplay withoutListWrapper) ('copying' copy copyAddedStateFrom: copyFrom: copySameFrom: copyTwoLevel deepCopy initialDeepCopierSize postCopy shallowCopy veryDeepCopy veryDeepCopySibling veryDeepCopyUsing: veryDeepCopyWith: veryDeepFixupWith: veryDeepInner:) ('debugging' haltIf: needsWork) ('debugging-haltOnce' checkHaltCountExpired clearHaltOnce decrementAndCheckHaltCount decrementHaltCount doExpiredHaltCount doExpiredHaltCount: doExpiredInspectCount haltOnCount: haltOnce haltOnceEnabled haltOnce: halt:onCount: hasHaltCount inspectOnCount: inspectOnce inspectUntilCount: removeHaltCount setHaltCountTo: setHaltOnce toggleHaltOnce) ('dependents access' addDependent: breakDependents canDiscardEdits dependents evaluate:wheneverChangeIn: hasUnacceptedEdits myDependents myDependents: release removeDependent:) ('drag and drop' acceptDroppingMorph:event:inMorph: dragPassengerFor:inMorph: dragStartedFor:transferMorph: dragTransferTypeForMorph: wantsDroppedMorph:event:inMorph:) ('error handling' assert: assert:descriptionBlock: assert:description: backwardCompatibilityOnly: caseError deprecated deprecated: deprecated:block: doesNotUnderstand: dpsTrace: dpsTrace:levels: dpsTrace:levels:withContext: error error: halt halt: handles: notify: notify:at: primitiveFailed primitiveFailed: shouldBeImplemented shouldNotImplement subclassResponsibility traitConflict) ('evaluating' value valueWithArguments:) ('filter streaming' byteEncode: drawOnCanvas: elementSeparator encodePostscriptOn: flattenOnStream: fullDrawPostscriptOn: putOn: writeOnFilterStream:) ('flagging' isThisEverCalled isThisEverCalled: logEntry logExecution logExit) ('graph model' addModelYellowButtonMenuItemsTo:forMorph:hand: hasModelYellowButtonMenuItems) ('macpal' codeStrippedOut: contentsChanged flash instanceVariableValues isUniversalTiles objectRepresented refusesToAcceptCode scriptPerformer slotInfo) ('message handling' executeMethod: perform: perform:orSendTo: perform:with:with:with:with: perform:with:with:with:with:with: perform:withArguments: perform:withArguments:inSuperclass: perform:withEnoughArguments: perform:with: perform:with:with: perform:with:with:with: withArgs:executeMethod: with:executeMethod: with:with:executeMethod: with:with:with:executeMethod: with:with:with:with:executeMethod:) ('objects from disk' comeFullyUpOnReload: convertToCurrentVersion:refStream: fixUponLoad:seg: objectForDataStream: readDataFrom:size: saveOnFile saveOnFileNamed: storeDataOn:) ('printing' fullPrintString isLiteral longPrintOn: longPrintOn:limitedTo:indent: longPrintString longPrintStringLimitedTo: nominallyUnsent: printOn: printString printStringLimitedTo: printWithClosureAnalysisOn: reportableSize storeOn: storeString stringForReadout stringRepresentation) ('scripting' adaptedToWorld: defaultFloatPrecisionFor: evaluateUnloggedForSelf: methodInterfacesForCategory:inVocabulary:limitClass: methodInterfacesForInstanceVariablesCategoryIn: methodInterfacesForScriptsCategoryIn: selfWrittenAsIll selfWrittenAsIm selfWrittenAsMe selfWrittenAsMy selfWrittenAsThis) ('system primitives' asOop become: becomeForward: becomeForward:copyHash: className creationStamp instVarAt: instVarAt:put: instVarNamed: instVarNamed:put: oopString primitiveChangeClassTo: rootStubInImageSegment: someObject) ('testing' beViewed belongsToUniClass costumes haltIfNil isArray isBehavior isBlock isBoolean isCharacter isClassReference isClosure isCodeReference isCollection isColor isColorForm isCompiledCode isCompiledMethod isComplex isContext isDictionary isFloat isForm isFraction isHeap isInteger isInterval isMessageSend isMethodContext isMethodProperties isMethodReference isMorph isMorphicEvent isMorphicModel isNumber isPlayer isPoint isPrimitiveCostume isPromise isRectangle isScriptEditorMorph isSketchMorph isStream isString isSymbol isSystemWindow isText isTextView isTrait isTransparent isVariableBinding isWebBrowser isWindowForModel: knownName name nameForViewer renameInternal: renameTo: shouldBePrintedAsLiteral shouldBePrintedAsLiteralVisiting: showDiffs stepAt:in: stepIn: stepTime stepTimeIn: vocabularyDemanded wantsDiffFeedback wantsSteps wantsStepsIn:) ('updating' changed changed: changed:with: handledListVerification noteSelectionIndex:for: okToChange okToClose updateListsAndCodeIn: update: update:with: windowIsClosing) ('user interface' addModelItemsToWindowMenu: addModelMenuItemsTo:forMorph:hand: asExplorerString defaultLabelForInspector launchPartVia: launchPartVia:label: launchTileToRefer modelSleep modelWakeUp modelWakeUpIn: mouseUpBalk: notYetImplemented windowActiveOnFirstClick windowReqNewLabel:) ('*monticello' isConflict) ('*services-base' requestor) ('*system-support' isPrimitiveError systemNavigation) ('*Tools-Explorer' explore exploreWithLabel:) ('*tools-browser' browseHierarchy) ('private' errorImproperStore errorNonIntegerIndex errorNotIndexable errorSubscriptBounds: setPinned: species storeAt:inTempFrame:) ('thumbnail' iconOrThumbnailOfSize:) ('*Morphic-Explorer' explorerContents hasContentsInExplorer) ('futures' future future: futureDo:at:args: futureSend:at:args:) ('*Etoys-viewer' assureUniClass browseOwnClassSubProtocol categoriesForViewer: categoriesForVocabulary:limitClass: chooseNewNameForReference defaultLimitClassForVocabulary: defaultNameStemForInstances elementTypeFor:vocabulary: externalName graphicForViewerTab hasUserDefinedSlots infoFor:inViewer: initialTypeForSlotNamed: isPlayerLike methodInterfacesInPresentationOrderFrom:forCategory: newScriptorAround: newTileMorphRepresentative offerViewerMenuFor:event: offerViewerMenuForEvt:morph: renameScript: tilePhrasesForCategory:inViewer: tilePhrasesForMethodInterfaces:inViewer: tilePhrasesForSelectorList:inViewer: tileToRefer uniqueInstanceVariableNameLike:excluding: uniqueNameForReference uniqueNameForReferenceFrom: uniqueNameForReferenceOrNil updateThresholdForGraphicInViewerTab usableMethodInterfacesIn:) ('*Protocols' currentVocabulary haveFullProtocolBrowsed haveFullProtocolBrowsedShowingSelector:) ('*Etoys-tiles' basicType tearOffTile) ('*Tools-MessageSets' browseAllCallsOn: browseAllImplementorsOf:) ('*Tools-multi-window support' canHaveUnacceptedEdits) ('*morphic' asDraggableMorph asMorph asStringMorph asTextMorph isPluggableListMorph openAsMorph) ('*MorphicExtras-Undo' capturedState commandHistory purgeAllCommands redoFromCapturedState: refineRedoTarget:selector:arguments:in: refineUndoTarget:selector:arguments:in: rememberCommand: rememberUndoableAction:named: undoFromCapturedState:) ('*MorphicExtras-PartsBin' descriptionForPartsBin) ('tracing' inboundPointers inboundPointersExcluding: outboundPointers outboundPointersDo:) ('*Tools-Debugger' canonicalArgumentName chasePointers explorePointers shouldFollowOutboundPointers) ('*System-Object Events-accessing' actionForEvent: actionForEvent:ifAbsent: actionMap actionSequenceForEvent: actionsDo: createActionMap hasActionForEvent: setActionSequence:forEvent: updateableActionMap) ('*System-Change Notification-events' actionsWithReceiver:forEvent: renameActionsWithReceiver:forEvent:toEvent:) ('*System-Change Notification-converting' asActionSequence asActionSequenceTrappingErrors) ('*System-Tools-breakpoint' break) ('*ToolBuilder-Kernel-error handling' confirm: confirm:orCancel:) ('*EToys-user interface' eToyStreamedRepresentationNotifying:) ('*ToolBuilder-Kernel-user interface' inform:) ('*System-Support-user interface' initialExtent) ('*System-Localization-locales' localeChanged localeChangedGently translatedNoop) ('*System-Object Events-accessing-removing' releaseActionMap removeAction:forEvent: removeActionsForEvent: removeActionsSatisfying: removeActionsSatisfying:forEvent: removeActionsWithReceiver: removeActionsWithReceiver:forEvent:) ('*System-Object Events-accessing-triggering' triggerEvent: triggerEvent:ifNotHandled: triggerEvent:with: triggerEvent:with:ifNotHandled: triggerEvent:withArguments: triggerEvent:withArguments:ifNotHandled:) ('*System-Object Events-accessing-registering' when:evaluate: when:send:to: when:send:to:with: when:send:to:withArguments:) ('*Tools-inspecting') ('*Traits' explicitRequirement requirement) ('*Graphics-KernelExtensions' fullScreenSize) ('*System-Finalization' actAsExecutor executor finalizationRegistry finalize hasMultipleExecutors retryWithGC:until: toFinalizeSend:to:with:) ('*collections' asLink compareSafely:) ('*Tools-Browsing' browse) ('*System-Recovery-error handling' primitiveError:) ('*SUnit-testing' isTestClass) ('*51Deprecated') ('*Morphic-Events-Filtering' filterEvent:for:) ('*System-Preferences' applyUserInterfaceTheme canApplyUserInterfaceTheme userInterfaceTheme) ('*Etoys-Squeakland-accessing' customizeExplorerContents) ('*Etoys-Squeakland-comparing' eToysEQ: eToysGE: eToysGT: eToysLE: eToysLT: eToysNE:) ('*Etoys-Squeakland-error handling' eToysError:) ('*Etoys-Squeakland-Etoys-viewer' hasUserDefinedScripts) ('*Etoys-Squeakland-translation support' inline:) ('*Etoys-Squeakland-testing' isReallyString) ('*Etoys-Squeakland-user interface' launchPartOffsetVia:label:) ('*Etoys-Squeakland-translating' literalStringsDo:) ('*Etoys-Squeakland-Tweak-Kedama' test:ifTrue:ifFalse: times:repeat:) ('*Tools' environment) ('*60Deprecated-copying' clone) ('pinning' isPinned pin unpin) ('*EToys-Scripting-accessing' addInstanceVarNamed:withValue: basicAddInstanceVarNamed:withValue:) ('*60Deprecated-Tools' exploreAndYourself notifyWithLabel:) ('literals' allLiteralsDo: hasLiteral: hasLiteralSuchThat:) ('write barrier' attemptToAssign:withIndex: beReadOnlyObject beWritableObject isReadOnlyObject setIsReadOnlyObject:) ('*Tools-Inspector' basicInspect inspect inspectWithLabel: inspectorClass) ('*Morphic-Kernel-accessing' activeHand currentEvent currentHand currentWorld) ! > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: filein-updateui.1.cs URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 25 17:40:30 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 25 Sep 2020 17:40:30 +0000 Subject: [squeak-dev] The Inbox: Kernel-ct.1342.mcz In-Reply-To: References: , Message-ID: <3bddcbdb247c44e6a16657593a4c6b86@student.hpi.uni-potsdam.de> Hi Jakob, there are also #asWords, #threeDigitName, or #asPluralBasedOn: which seem to operate on a similar level. Maybe we should move them all into an extension category of another package (System? Multilingual-Languages?)? I also agree it's not internationalized yet, but it does not feel the right time for me to design an international solution for this at the moment (I once tried out Squeak 5.3 with German language once, and it was horrible). Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Jakob Reschke Gesendet: Freitag, 25. September 2020 18:36:57 An: squeak-dev at lists.squeakfoundation.org Betreff: Re: [squeak-dev] The Inbox: Kernel-ct.1342.mcz I think that such things should not really belong in Kernel. Is there another suitable package that can provide this as an extension method? Also note that this is not internationalized yet (English only), but judging by the selectors it might well be used to produce localized output in the future. Maybe that could influence the selection of a package. Am Fr., 25. Sept. 2020 um 18:32 Uhr schrieb : > > A new version of Kernel was added to project The Inbox: > http://source.squeak.org/inbox/Kernel-ct.1342.mcz > > ==================== Summary ==================== > > Name: Kernel-ct.1342 > Author: ct > Time: 25 September 2020, 6:32:44.665274 pm > UUID: 037ef8ee-228f-914b-8d1e-b05cac84772b > Ancestors: Kernel-eem.1341 > > Add ordinal suffix logic to Integer > > Example: > (1 to: 30) collect: [:ea | ea withOrdinalSuffix] > > =============== Diff against Kernel-eem.1341 =============== > > Item was added: > + ----- Method: Integer>>ordinalSuffix (in category 'printing') ----- > + ordinalSuffix > + "Answer a string containing the ordinal suffix of the receiver, e.g. 'th' for 4, or 'rd' for 23." > + > + self \\ 100 // 10 = 1 ifFalse: [ > + self \\ 10 > + caseOf: { > + [1] -> [^ 'st']. > + [2] -> [^ 'nd']. > + [3] -> [^ 'rd'] } > + otherwise: []]. > + ^ 'th'! > > Item was added: > + ----- Method: Integer>>printWithOrdinalSuffixOn: (in category 'printing') ----- > + printWithOrdinalSuffixOn: aStream > + > + aStream > + print: self; > + nextPutAll: self ordinalSuffix.! > > Item was added: > + ----- Method: Integer>>withOrdinalSuffix (in category 'printing') ----- > + withOrdinalSuffix > + > + ^ String streamContents: [:stream | > + self printWithOrdinalSuffixOn: stream]! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 25 19:43:52 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 25 Sep 2020 19:43:52 +0000 Subject: [squeak-dev] highdpi testing In-Reply-To: <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de>, <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> Message-ID: <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> Hi Tobias, sorry I did not answer earlier! This approach looks better for me, however, the wheel is still not absolutely round: [cid:3212099a-026e-460d-924c-75a9564be237] (The shadows have a wrong position.) For the first step, we could start by asking the user whether they would like to change the font size (#changeFontSize:) when a new scale factor is detected, couldn't we? PS: @Tom: > we try drawing things while we load things :) For the case you did not already read it, I created a changeset that could solve this problem: http://forum.world.st/Changeset-Eliminating-global-state-from-Morphic-tp5121690p5122464.html It also allow me to file in the original DpiAware changeset. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Montag, 21. September 2020 15:50:30 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] highdpi testing Hi > On 19.09.2020, at 19:26, Thiede, Christoph wrote: > > Hi, > > ah, I had assumed something like this, thanks for fixing, Tom! :-) > > #actualScreenScaleFactor is a very helpful tool, but I do not yet see the whole idea of the changeset. > In what sense is this meant to be a complement of RealEstateAgent >> #scaleFactor or rather an orthogonal concept? > After loading the changeset and resetting my UI theme, the background image of the world disappeared and all new windows are huge, but they still have a tiny font - am I supposed to set the #scaleFactor manually (to 12, in my example)? > Where exactly can I see any components in the image that are upscaled by the new changeset? > These do something. Load in order: DpiAware-Kernel DpiAware-Morpic DpiAware-Display First two are harmless, third will do something. If nothing changes, do UserInterfaceTheme cleanUpAndReset. Issues ensue. - Layouting borks. The assert after the potential relayout self assert: (self hasProperty: #doLayoutAgain) not fails already in the docking bar… - Fonts with UI-Themes do not work. These are "Deep-referenced" to a pixel size (for Strike fonts) on creation, (so also during UserInterfaceTheme cleanUpAndReset). However, when the scale factor changes, the pixel-sized fonts don't match the point sizes anymore… This used to work pre-UI-Themes, as everyone requesting a Font did so by directly asking StrikeFont or TextStyle at the time the Font was used. Hummm.. Hope you enjoy. -t -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 149697 bytes Desc: pastedImage.png URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Fri Sep 25 19:46:00 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Fri, 25 Sep 2020 19:46:00 +0000 Subject: [squeak-dev] Multiple processes and morphic state (was Re: Changeset: Eliminating global state from Morphic) In-Reply-To: <495a43a9-69fa-1758-7f03-9e712067d98b@leastfixedpoint.com> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <20200914154934.GA85344@shell.msen.com> , <495a43a9-69fa-1758-7f03-9e712067d98b@leastfixedpoint.com> Message-ID: Yes, Tony, usually, I like to use deferred UI messages, too. :-) But when designing such a low-level construct such as ActiveEvent, I think it's better to expect every eventuality, isn't it? Best, Christoph ________________________________ Von: Tony Garnock-Jones Gesendet: Freitag, 25. September 2020 16:55:23 An: The general-purpose Squeak developers list; Thiede, Christoph Betreff: Multiple processes and morphic state (was Re: Changeset: Eliminating global state from Morphic) Hi Christoph, On 9/24/20 2:08 PM, Thiede, Christoph wrote: >> It's true that in Morphic there is one distinct Process associated > with the user interface for each Morphic world. > > I'm not so sure about this. You can always do forks within UI code, so > sometimes there are also multiple Processes within one Morphic world. > Just think about Shout's background process or debugging processes, and > please keep in mind that Jakob justifiably proposed to renew the process > handling for non-modal dialogs, which maybe could also result in > multiple processes being active at the same time. > A world can have multiple hands, for example, RemoteHandMorphs > (Nebraska), event-simulating hands (EventRecorder), or just > multiple plain HandMorphs as demonstrated by Tony recently in > his PostmarketOS implementation. There is no reason for these hands to > run on the same process. Analogously, I don't see why we should restrict > a hand not to be thread-safe, so the hand-event relation is 1:n again. I've found that while, yes, there are lots of opportunities for concurrency in Morphic, actually *taking* those opportunities results in all sorts of problems. Morphic state is I think not thread-safe enough to allow multiple processes "inside" of it at once. So I've ended up being *very* careful to serialize all Morphic access within the one, main UI process. Where other processes (Actors) exist, I send messages from the UI process to the others, and when Morphic state change code has to run, I package it up into a block and enqueue it for later execution by the UI process rather than doing it in each Actor. (It's painful, actually, having to be so careful about it, in such a C-style "you do all the work yourself and the system won't help you" manner. Maybe there's something we can improve here?) So I think it's *morally* true that "there is one distinct Process associated with the user interface for each Morphic world," even if as a rule it can be bent a little :-) Cheers, Tony -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Fri Sep 25 20:16:45 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 25 Sep 2020 20:16:45 0000 Subject: [squeak-dev] The Trunk: Network-eem.242.mcz Message-ID: Eliot Miranda uploaded a new version of Network to project The Trunk: http://source.squeak.org/trunk/Network-eem.242.mcz ==================== Summary ==================== Name: Network-eem.242 Author: eem Time: 25 September 2020, 1:16:43.056168 pm UUID: 555fce79-4be7-49f4-a0d6-b976a406942e Ancestors: Network-eem.241 Sockets: Add an inst var to Socket to remember the address family (INET4 vs INET6) for a new-style socket. Add family-specific lookup facilities to NetNameResolver. =============== Diff against Network-eem.241 =============== Item was added: + ----- Method: NetNameResolver class>>addressForName:family: (in category 'lookups') ----- + addressForName: hostName family: addressFamily + "NetNameResolver addressForName: 'squeak.org' family: SocketAddressInformation addressFamilyINET4" + "NetNameResolver addressForName: 'localhost' family: SocketAddressInformation addressFamilyINET6" + "NetNameResolver addressForName: '127.0.0.1' family: SocketAddressInformation addressFamilyINET6" + | addresses | + self useOldNetwork ifTrue: + [^self oldAddressForName: hostName]. + addresses := self addressesForName: hostName family: addressFamily. + ^addresses + ifEmpty: [nil] + ifNotEmpty: [addresses first socketAddress]! Item was changed: ----- Method: NetNameResolver class>>addressesForName: (in category 'lookup') ----- addressesForName: hostName "NetNameResolver addressesForName: 'impara.de' " + ^SocketAddressInformation - | addresses | - addresses := SocketAddressInformation forHost: hostName service: '' flags: 0 addressFamily: 0 socketType: SocketAddressInformation socketTypeStream + protocol: SocketAddressInformation protocolTCP! - protocol: SocketAddressInformation protocolTCP. - ^addresses! Item was added: + ----- Method: NetNameResolver class>>addressesForName:family: (in category 'lookup') ----- + addressesForName: hostName family: addressFamily + "NetNameResolver + addressesForName: 'squeak.org' + family: SocketAddressInformation addressFamilyINET4" + "NetNameResolver + addressesForName: 'impara.de' + family: SocketAddressInformation addressFamilyINET6" + + ^SocketAddressInformation + forHost: hostName + service: '' + flags: 0 + addressFamily: addressFamily + socketType: SocketAddressInformation socketTypeStream + protocol: SocketAddressInformation protocolTCP! Item was changed: Object subclass: #Socket + instanceVariableNames: 'semaphore socketHandle readSemaphore writeSemaphore family' - instanceVariableNames: 'semaphore socketHandle readSemaphore writeSemaphore' classVariableNames: 'Connected DeadServer DefaultReceiveBufferSize DefaultSendBufferSize InvalidSocket MaximumReadSemaphoreWaitTimeout OtherEndClosed Registry TCPSocketType ThisEndClosed UDPSocketType Unconnected WaitingForConnection' poolDictionaries: '' category: 'Network-Kernel'! !Socket commentStamp: 'gk 12/13/2005 00:43' prior: 0! A Socket represents a network connection point. Current sockets are designed to support the TCP/IP and UDP protocols. Sockets are the lowest level of networking object in Squeak and are not normally used directly. SocketStream is a higher level object wrapping a Socket in a stream like protocol. ProtocolClient and subclasses are in turn wrappers around a SocketStream to provide support for specific network protocols such as POP, NNTP, HTTP, and FTP.! Item was added: + ----- Method: Socket>>addressFamily (in category 'accessing') ----- + addressFamily + ^family! Item was changed: ----- Method: Socket>>initialize:family: (in category 'initialize-destroy') ----- + initialize: socketType family: addressFamily - initialize: socketType family: family "Initialize a new socket handle. If socket creation fails, socketHandle will be set to nil." + family := addressFamily. NetNameResolver useOldNetwork ifTrue: [ ^self initialize: socketType ]. self initializeSocketHandleUsing: [ :semaIndex :readSemaIndex :writeSemaIndex | self primSocketCreateNetwork: family type: socketType receiveBufferSize: DefaultReceiveBufferSize sendBufSize: DefaultSendBufferSize semaIndex: semaIndex readSemaIndex: readSemaIndex writeSemaIndex: writeSemaIndex ]! Item was changed: ----- Method: Socket>>waitForDataIfClosed: (in category 'waiting') ----- waitForDataIfClosed: closedBlock "Wait indefinitely for data to arrive. This method will block until data is available or the socket is closed." + [socketHandle ifNil: [^closedBlock value ]. + (self primSocketReceiveDataAvailable: socketHandle) ifTrue: [ ^self ]. + self isConnected ifFalse: [ ^closedBlock value ]. + "ul 8/13/2014 21:16 + Providing a maximum for the time for waiting is a workaround for a VM bug which + causes sockets waiting for data forever in some rare cases, because the semaphore + doesn't get signaled. Replace the ""waitTimeoutMSecs: self class maximumReadSemaphoreWaitTimeout"" + part with ""wait"" when the bug is fixed." + readSemaphore waitTimeoutMSecs: self class maximumReadSemaphoreWaitTimeout ] repeat! - socketHandle ifNil: [ ^closedBlock value ]. - [ - (self primSocketReceiveDataAvailable: socketHandle) ifTrue: [ ^self ]. - self isConnected ifFalse: [ ^closedBlock value ]. - "ul 8/13/2014 21:16 - Providing a maximum for the time for waiting is a workaround for a VM bug which - causes sockets waiting for data forever in some rare cases, because the semaphore - doesn't get signaled. Replace the ""waitTimeoutMSecs: self class maximumReadSemaphoreWaitTimeout"" - part with ""wait"" when the bug is fixed." - readSemaphore waitTimeoutMSecs: self class maximumReadSemaphoreWaitTimeout ] repeat! From commits at source.squeak.org Fri Sep 25 20:20:03 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Fri, 25 Sep 2020 20:20:03 0000 Subject: [squeak-dev] The Trunk: NetworkTests-eem.59.mcz Message-ID: Eliot Miranda uploaded a new version of NetworkTests to project The Trunk: http://source.squeak.org/trunk/NetworkTests-eem.59.mcz ==================== Summary ==================== Name: NetworkTests-eem.59 Author: eem Time: 25 September 2020, 1:20:02.115435 pm UUID: 6c005829-7edf-4318-bbb9-bf452903461a Ancestors: NetworkTests-pre.58 SocketTest: use teh facilities in Network-eem.242 to use the crrect address family when looking up a name for a socket to connect/accept to. Hence get rid of the sqConnectToAddressSize: Address family not supported by protocol family error message from the SocketPlugin when running the SocketTest tests. =============== Diff against NetworkTests-pre.58 =============== Item was added: + ----- Method: SocketTest>>listenerAddressForFamily: (in category 'fixtures') ----- + listenerAddressForFamily: addressFamily + ^NetNameResolver addressForName: 'localhost' family: addressFamily! Item was changed: ----- Method: SocketTest>>testClientConnect (in category 'tests') ----- testClientConnect "Tests a client socket connection" clientSocket := Socket newTCP. + clientSocket connectTo: (self listenerAddressForFamily: clientSocket addressFamily) port: self listenerPort. - clientSocket connectTo: self listenerAddress port: self listenerPort. clientSocket waitForConnectionFor: 2. + self assert: clientSocket isConnected! - self assert: clientSocket isConnected. - ! Item was changed: ----- Method: SocketTest>>testLocalAddress (in category 'tests') ----- testLocalAddress "Tests the various localAddress values for sockets" self testServerAccept. + self assert: listenerSocket localAddress equals: (self listenerAddressForFamily: listenerSocket addressFamily). + self assert: clientSocket localAddress equals: (self listenerAddressForFamily: clientSocket addressFamily). + self assert: serverSocket localAddress equals: (self listenerAddressForFamily: serverSocket addressFamily)! - self assert: listenerSocket localAddress = self listenerAddress. - self assert: clientSocket localAddress = self listenerAddress. - self assert: serverSocket localAddress = self listenerAddress. - ! From forums.jakob at resfarm.de Fri Sep 25 21:03:36 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Fri, 25 Sep 2020 23:03:36 +0200 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: Hello, Here is what I had to do to have the Pharo launcher launch a Squeak image: - Download and extract a Squeak VM and a supported image with .changes. - Download and install the latest Pharo Launcher release. - Start the launcher. - Open Settings > Pharo Launcher > edit the folder paths to my likings. - Click Store Settings at the top right. - Move the Squeak VM directory below the VM directory entered in the settings. Do not move the image yet. - Press shift+return, type vmExecutableName and open the method WindowsResolver>>#vmExecutableName. - Change WindowsResolver>>#vmExecutableName to return 'Squeak.exe'. - Close the system browser window. - Press the VMs button on the top right. It should now list the Squeak VM, for example 'squeak.cog.spur_win64x64_202003021730'. Close the VMs window. - Press Import at the top, choose the extracted image. It will be moved to the images directory from the Settings! - The image should now appear in the main list of the launcher window. - In the new directory of the image, put a file named "pharo.version" with the contents "0" next to the image. This will prevent the launcher from trying to find out the Pharo version of the image by running a pharo-version.st script with --headless on the image. Squeak does not support this. - Back in the launcher, in the combo box next to the Launch button at the top, drop down the list and select Edit Configurations. - For the Default configuration, select your Squeak VM instead of "0-x64" on the right. The latter would try and fail to download a fitting Pharo VM again. - Close the window with "Save & Select". - Now I am able to launch the Squeak image. To preserve the changes to the launcher image: - Press shift+return, enter Playground and press return to open a Workspace. - Evaluate the following: Smalltalk snapshot: true andQuit: true. Kind regards, Jakob Am Mi., 2. Sept. 2020 um 18:20 Uhr schrieb Sean DeNigris : > > I've been pontificating about unity between dialects, convinced more than ever that we need each other and have more of a simple (not easy) communication problem than anything else, and pestering the principals of various communities, so I decided to put some concrete action behind it - some "skin in the game" as we say in the U.S. > > After reading Trygve's frustration with Squeak image/VM management, and Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage and run Squeak (and GToolkit) VMs/images. > > The current limitation vs. launching standard Pharo images is that you have to download the VMs and images and manually install them due to differences in URL and other conventions. However once the files are in place, you can create templates allowing images to be created at will from different Squeak versions and automatically run with the correct VM. Auto-installation could probably be done if someone cares enough but it scratched my itch for the moment to manage GT images. I’m sure Cuis support could be added, but the hooks are now available and someone more familiar with its VM/image installation might have an easier time. > > Until my latest PR [1] is merged, brave souls can play with it and give feedback by loading my issue branch [2] into the latest Launcher release [3]. NB only tested on Mac. > > It seemed Trygve was at his wits' end, but maybe this and other small gestures will let him know how important he is to our community if not motivate him to resume his important work. > > I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what small action can you take today that unites us rather than divides us? We can be each others' supporters even if technical, vision and other differences keep our codebases somewhat distinct. I believe the seeds of wars are planted when I lack the grace to give those around me the benefit of the doubt that they mean well and are trying their best. There is more than enough of that already in the world. Let’s invent a *better* future… > > Your brother, > Sean > > 1. https://github.com/pharo-project/pharo-launcher/pull/503 > 2. https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks > 3. https://pharo.org/download > From lewis at mail.msen.com Fri Sep 25 22:02:32 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Fri, 25 Sep 2020 18:02:32 -0400 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <6deebfc91b0d475795915733b1c0c70f@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <20200914154934.GA85344@shell.msen.com> <20200924150359.GA88607@shell.msen.com> <6deebfc91b0d475795915733b1c0c70f@student.hpi.uni-potsdam.de> Message-ID: <20200925220232.GA79476@shell.msen.com> Hi Christoph, I tried loading filein-updateui.1.cs followed by activeVariables.3.cs in a trunk image, and I still get a hung image half way through the activeVariables.3.cs change set. I cannot debug this, because the hung image cannot be interrupted. Can you please double-check in a clean image and see if you get the same? Thanks, Dave On Fri, Sep 25, 2020 at 05:35:44PM +0000, Thiede, Christoph wrote: > Hi Dave, > > > sorry for that, it's another occurrence of the "display during loading changeset" issue. Could you please try to load the attached changeset first? It adds a new service provider that allows you to file in the active variables changeset without encountering any UI updates on the way. If you like the filein changeset, I would like to get this into the trunk, too. :-) > > > Best, > > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Donnerstag, 24. September 2020 17:03:59 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > Hi Christoph, > > I am using an up to date trunk image. My image locks up about > half way through loading the change set. I cannot interrupt it > at that point so I'm not sure what the issue may be. > > Is there some prerequisite that I need? > > BTW, Marcel explained to me during a recent board meeting that the > process local variables are intended to address issues related to > the debugger, so now I understand the reason for it. > > Thanks, I'm looking forward to trying this. > > Dave > > > On Thu, Sep 24, 2020 at 12:08:54PM +0000, Thiede, Christoph wrote: > > Hi all, > > > > > > sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: > > > > > > * Use DynamicVariables for storing the active variable states > > > > * Refactor and reduce the visibility of active variable accessors on Object > > > > * Clean up implementations of #activeHand and #primaryHand > > > > > > @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. > > > > > > > > > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. > > > > I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. > > A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. > > > > PS: Thanks for the hint to the new wiki page! > > > > Best, > > Christoph > > > > ________________________________ > > Von: Squeak-dev im Auftrag von David T. Lewis > > Gesendet: Montag, 14. September 2020 17:49:34 > > An: The general-purpose Squeak developers list > > Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic > > > > On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > > > Hi Dave, > > > > > > > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > > > > > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > > > > > > > What do you think? > > > > > > > Hi Christoph, > > > > The thing that I like (a lot) about your changes is that they make it > > much easier to see and understand the accesses to the global variables. > > The references now all go through a small number of methods in Object, > > so that you can see what they do now. > > > > Clearly we would want to leave this logic in Object, since we don't > > want to add more clutter to its protocol. But you have collected the logic > > in one place now, which makes it easier to think about where it ultimately > > should go, and that's great. > > > > But where do these things actually belong? Thinking in terms of the > > responsiblities of objects in the system [1], I don't think that associating > > them with a Process seems to be a good fit. It's true that in Morphic there > > is one distinct Process associated with the user interface for each Morphic > > world. So you can make things work by associating these variables with > > a Process, but it seems like an unnatural fit to me. A Morphic world has > > a process, but a Process is not responsible for knowing the state of > > the Morphic world. > > > > Dave > > > > [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares. > > > > > > Content-Description: Hide activeVariables.3.cs > > 'From Squeak6.0alpha of 6 September 2020 [latest update: #19838] on 24 September 2020 at 1:53:22 pm'! > DynamicVariable subclass: #ActiveEventVariable > instanceVariableNames: '' > classVariableNames: '' > poolDictionaries: '' > category: 'Morphic-Worlds'! > DynamicVariable subclass: #ActiveHandVariable > instanceVariableNames: '' > classVariableNames: '' > poolDictionaries: '' > category: 'Morphic-Worlds'! > DynamicVariable subclass: #ActiveWorldVariable > instanceVariableNames: '' > classVariableNames: '' > poolDictionaries: '' > category: 'Morphic-Worlds'! > > !Object methodsFor: 'user interface' stamp: 'ct 9/12/2020 14:13'! > launchPartVia: aSelector label: aString > "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins" > > | aMorph | > aMorph := self perform: aSelector. > aMorph setNameTo: (Project current world unusedMorphNameLike: aString). > aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. > aMorph openInHand.! ! > > !Object methodsFor: '*Protocols' stamp: 'ct 9/12/2020 14:14'! > haveFullProtocolBrowsedShowingSelector: aSelector > "Open up a Lexicon on the receiver, having it open up showing aSelector, which may be nil" > "(2 at 3) haveFullProtocolBrowsed" > > | aBrowser | > aBrowser := (Smalltalk at: #InstanceBrowser ifAbsent: [^ nil]) new > useVocabulary: Vocabulary fullVocabulary. > aBrowser > openOnObject: self > inWorld: Project current world > showingSelector: aSelector! ! > > !Object methodsFor: '*Etoys-Squeakland-user interface' stamp: 'ct 9/12/2020 14:13'! > launchPartOffsetVia: aSelector label: aString > "Obtain a morph by sending aSelector to self, and attach it to the morphic hand. This provides a general protocol for parts bins. This variant makes the morph offset from the hand position by an amount suitable for tile-scripting in some circumstances." > > | aMorph | > aMorph := self perform: aSelector. > aMorph setNameTo: (Project current world unusedMorphNameLike: aString). > aMorph setProperty: #beFullyVisibleAfterDrop toValue: true. > aMorph setProperty: #offsetForAttachingToHand toValue: 10@ -10. > aMorph fullBounds. > aMorph openInHand! ! > > !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/24/2020 13:46'! > activeHand > "Answer the active hand for the current world. Fallback implementation for non-Morphic worlds. Private!! Usually, you want to send #currentHand instead." > > self == self currentWorld ifTrue: [ > ^ nil]. > > ^ self currentHand! ! > > !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/16/2020 20:28'! > currentEvent > "Answer the current MorphicEvent. Provided that a morphic project is loaded, this method never returns nil." > > ^ ActiveEventVariable value! ! > > !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/24/2020 13:44'! > currentHand > "Answer the current HandMorph. Provided that a morphic project is loaded, this method will never return nil." > > ^ ActiveHandVariable value ifNil: [self currentWorld activeHand]! ! > > !Object methodsFor: '*Morphic-Kernel-accessing' stamp: 'ct 9/16/2020 20:27'! > currentWorld > "Answer the current world. This method will never return nil." > > ^ ActiveWorldVariable value! ! > > > !ActiveEventVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:09'! > default > > ^ self currentHand ifNotNil: [:hand | hand lastEvent]! ! > > !ActiveEventVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 19:42'! > value: anObject during: aBlock > > "For backword compatibility <6.0, still maintain the ActiveEvent global." > | priorEvent | > priorEvent := self value. > ActiveEvent := anObject. > ^ [super value: anObject during: aBlock] ensure: [ > ActiveEvent := priorEvent]! ! > > > !ActiveHandVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:14'! > default > > ^ self currentWorld activeHand! ! > > !ActiveHandVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 19:43'! > value: anObject during: aBlock > > "For backword compatibility <6.0, still maintain the ActiveHand global." > | priorHand | > priorHand := self value. > ActiveHand := anObject. > ^ [super value: anObject during: aBlock] ensure: [ > ActiveHand := priorHand]! ! > > > !ActiveWorldVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 18:14'! > default > > ^ Project current world! ! > > !ActiveWorldVariable class methodsFor: 'accessing' stamp: 'ct 9/16/2020 18:31'! > value: anObject during: aBlock > > "For backword compatibility <6.0, still maintain the ActiveWorld global." > | priorWorld | > priorWorld := self value. > ActiveWorld := anObject. > ^ [super value: anObject during: aBlock] ensure: [ > ActiveWorld := priorWorld]! ! > > > !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:05'! > currentBrowsers > > ^ (Project current world submorphsSatisfying: [:each | > (each isKindOf: SystemWindow) > and: [each model isKindOf: Browser]]) asSet! ! > > !BrowseTest methodsFor: 'private' stamp: 'ct 9/11/2020 18:04'! > currentHierarchyBrowsers > > ^ (Project current world submorphsSatisfying: [:each | > (each isKindOf: SystemWindow) > and: [each model isKindOf: HierarchyBrowser]]) asSet! ! > > > !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! > request: queryString > "Create an instance of me whose question is queryString. Invoke it > centered at the cursor, and answer the string the user accepts. Answer > the empty string if the user cancels." > > "UIManager default request: 'Your name?'" > > ^ self > request: queryString > initialAnswer: '' > centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! > > !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:40'! > request: queryString initialAnswer: defaultAnswer > "Create an instance of me whose question is queryString with the given > initial answer. Invoke it centered at the given point, and answer the > string the user accepts. Answer the empty string if the user cancels." > > "UIManager default > request: 'What is your favorite color?' > initialAnswer: 'red, no blue. Ahhh!!'" > > ^ self > request: queryString > initialAnswer: defaultAnswer > centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! > > !FillInTheBlank class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! > request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse > > ^ self > request: queryString > initialAnswer: defaultAnswer > centerAt: (self currentHand ifNil: [Sensor]) cursorPoint > onCancelReturn: cancelResponse! ! > > > !Flaps class methodsFor: 'construction support' stamp: 'ct 9/11/2020 20:09'! > possiblyReplaceEToyFlaps > "If in eToyFriendly mode, and if it's ok to reinitialize flaps, replace the existing flaps with up-too-date etoy flaps. Caution: this is destructive of existing flaps. If preserving the contents of existing flaps is important, set the preference 'okToReinitializeFlaps' to true" > > PartsBin thumbnailForPartsDescription: StickyPadMorph descriptionForPartsBin. "Puts StickyPadMorph's custom icon back in the cache which typically will have been called" > (Preferences eToyFriendly and: [Preferences okToReinitializeFlaps]) ifTrue: > [Flaps disableGlobalFlaps: false. > Flaps addAndEnableEToyFlaps. > Smalltalk isMorphic ifTrue: [Project current world enableGlobalFlaps]]. > "PartsBin clearThumbnailCache" > > "Flaps possiblyReplaceEToyFlaps"! ! > > !Flaps class methodsFor: 'menu commands' stamp: 'ct 9/11/2020 19:49'! > disableGlobalFlaps: interactive > "Clobber all the shared flaps structures. First read the user her Miranda rights." > > interactive > ifTrue: [(self confirm: > 'CAUTION!! This will destroy all the shared > flaps, so that they will not be present in > *any* project. If, later, you want them > back, you will have to reenable them, from > this same menu, whereupon the standard > default set of shared flaps will be created. > Do you really want to go ahead and clobber > all shared flaps at this time?' translated) ifFalse: [^ self]]. > > self globalFlapTabsIfAny do: > [:aFlapTab | self removeFlapTab: aFlapTab keepInList: false. > aFlapTab isInWorld ifTrue: [self error: 'Flap problem' translated]]. > self clobberFlapTabList. > self initializeFlapsQuads. > SharedFlapsAllowed := false. > Smalltalk isMorphic ifTrue: [ > Project current world > restoreMorphicDisplay; > reformulateUpdatingMenus]. > > "The following reduces the risk that flaps will be created with variant IDs > such as 'Stack Tools2', potentially causing some shared flap logic to fail." > "Smalltalk garbageCollect." "-- see if we are OK without this"! ! > > !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:08'! > enableGlobalFlaps > "Start using global flaps, given that they were not present." > > Cursor wait showWhile: [ > SharedFlapsAllowed := true. > self globalFlapTabs. "This will create them" > Smalltalk isMorphic ifTrue: [ > Project current world addGlobalFlaps. > self doAutomaticLayoutOfFlapsIfAppropriate. > FlapTab allInstancesDo: [:tab | tab computeEdgeFraction]. > Project current world reformulateUpdatingMenus]]! ! > > !Flaps class methodsFor: 'menu support' stamp: 'ct 9/11/2020 20:09'! > setUpSuppliesFlapOnly > "Set up the Supplies flap as the only shared flap. A special version formulated for this stand-alone use is used, defined in #newLoneSuppliesFlap" > > | supplies | > SharedFlapTabs isEmptyOrNil ifFalse: "get rid of pre-existing guys if any" > [SharedFlapTabs do: > [:t | t referent delete. t delete]]. > > SharedFlapsAllowed := true. > SharedFlapTabs := OrderedCollection new. > SharedFlapTabs add: (supplies := self newLoneSuppliesFlap). > self enableGlobalFlapWithID: 'Supplies' translated. > supplies setToPopOutOnMouseOver: false. > > Smalltalk isMorphic ifTrue: [ > Project current world > addGlobalFlaps; > reformulateUpdatingMenus].! ! > > !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:08'! > enableClassicNavigatorChanged > "The #classicNavigatorEnabled preference has changed. No senders in easily traceable in the image, but this is really sent by a Preference object!!" > > Preferences classicNavigatorEnabled > ifTrue: > [Flaps disableGlobalFlapWithID: 'Navigator' translated. > Preferences enable: #showProjectNavigator. > self disableGlobalFlapWithID: 'Navigator' translated.] > ifFalse: > [self enableGlobalFlapWithID: 'Navigator' translated. > Project current world addGlobalFlaps]. > > self doAutomaticLayoutOfFlapsIfAppropriate. > Project current assureNavigatorPresenceMatchesPreference. > Project current world reformulateUpdatingMenus.! ! > > !Flaps class methodsFor: 'miscellaneous' stamp: 'ct 9/11/2020 20:09'! > makeNavigatorFlapResembleGoldenBar > "At explicit request, make the flap-based navigator resemble the golden bar. No senders in the image, but sendable from a doit" > > "Flaps makeNavigatorFlapResembleGoldenBar" > > Preferences setPreference: #classicNavigatorEnabled toValue: false. > Preferences setPreference: #showProjectNavigator toValue: false. > (self globalFlapTabWithID: 'Navigator' translated) ifNil: > [SharedFlapTabs add: self newNavigatorFlap delete]. > self enableGlobalFlapWithID: 'Navigator' translated. > Preferences setPreference: #navigatorOnLeftEdge toValue: true. > (self globalFlapTabWithID: 'Navigator' translated) arrangeToPopOutOnMouseOver: true. > Project current world addGlobalFlaps. > self doAutomaticLayoutOfFlapsIfAppropriate. > Project current assureNavigatorPresenceMatchesPreference. ! ! > > !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:58'! > addLocalFlap > > ^ self addLocalFlap: self currentEvent! ! > > !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! > addLocalFlap: anEvent > "Menu command -- let the user add a new project-local flap. Once the new flap is born, the user can tell it to become a shared flap. Obtain an initial name and edge for the flap, launch the flap, and also launch a menu governing the flap, so that the user can get started right away with customizing it." > > | title edge | > edge := self askForEdgeOfNewFlap. > edge ifNil: [^ self]. > > title := UIManager default request: 'Wording for this flap:' translated initialAnswer: 'Flap' translated. > title isEmptyOrNil ifTrue: [^ self]. > > ^ self addLocalFlap: anEvent titled: title onEdge: edge! ! > > !Flaps class methodsFor: 'new flap' stamp: 'ct 9/11/2020 17:59'! > addLocalFlap: anEvent titled: title onEdge: edge > > | flapTab menu world | > flapTab := self newFlapTitled: title onEdge: edge. > (world := anEvent hand world) addMorphFront: flapTab. > flapTab adaptToWorld: world. > menu := flapTab buildHandleMenu: anEvent hand. > flapTab addTitleForHaloMenu: menu. > flapTab computeEdgeFraction. > menu popUpEvent: anEvent in: world.! ! > > !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! > enableOnlyGlobalFlapsWithIDs: survivorList > "In the current project, suppress all global flaps other than those with ids in the survivorList" > > self globalFlapTabsIfAny do: [:flapTab | > (survivorList includes: flapTab flapID) > ifTrue: [self enableGlobalFlapWithID: flapTab flapID] > ifFalse: [self disableGlobalFlapWithID: flapTab flapID]]. > Project current world addGlobalFlaps > > "Flaps enableOnlyGlobalFlapsWithIDs: #('Supplies')"! ! > > !Flaps class methodsFor: 'shared flaps' stamp: 'ct 9/11/2020 20:09'! > positionVisibleFlapsRightToLeftOnEdge: edgeSymbol butPlaceAtLeftFlapsWithIDs: idList > "Lay out flaps along the designated edge right-to-left, while laying left-to-right any flaps found in the exception list > > Flaps positionVisibleFlapsRightToLeftOnEdge: #bottom butPlaceAtLeftFlapWithIDs: {'Navigator' translated. 'Supplies' translated} > Flaps sharedFlapsAlongBottom" > > | leftX flapList flapsOnRight flapsOnLeft | > flapList := self globalFlapTabsIfAny select: > [:aFlapTab | aFlapTab isInWorld and: [aFlapTab edgeToAdhereTo == edgeSymbol]]. > flapsOnLeft := OrderedCollection new. > flapsOnRight := OrderedCollection new. > > flapList do: [:fl | > (idList includes: fl flapID) > ifTrue: [ flapsOnLeft addLast: fl ] > ifFalse: [ flapsOnRight addLast: fl ] ]. > > leftX := Project current world width - 15. > > flapsOnRight > sort: [:f1 :f2 | f1 left > f2 left]; > do: [:aFlapTab | > aFlapTab right: leftX - 3. > leftX := aFlapTab left]. > > leftX := Project current world left. > > flapsOnLeft > sort: [:f1 :f2 | f1 left > f2 left]; > do: [:aFlapTab | > aFlapTab left: leftX + 3. > leftX := aFlapTab right]. > > flapList do: > [:ft | ft computeEdgeFraction. > ft flapID = 'Navigator' translated ifTrue: > [ft referent left: (ft center x - (ft referent width//2) max: 0)]]! ! > > !Flaps class methodsFor: '*Etoys-Squeakland-predefined flaps' stamp: 'ct 9/12/2020 14:29'! > newSuppliesFlapFromQuads: quads positioning: positionSymbol withPreviousEntries: aCollection > "Answer a fully-instantiated flap named 'Supplies' to be placed at the bottom of the screen. Use #center as the positionSymbol to have it centered at the bottom of the screen, or #right to have it placed off near the right edge." > > | aFlapTab aStrip aWidth sugarNavigator | > sugarNavigator := SugarNavigatorBar showSugarNavigator. > aStrip := PartsBin newPartsBinWithOrientation: #leftToRight andColor: Color gray muchLighter from: quads withPreviousEntries: aCollection. > self twiddleSuppliesButtonsIn: aStrip. > aFlapTab := (sugarNavigator ifTrue: [SolidSugarSuppliesTab] ifFalse: [FlapTab]) new referent: aStrip beSticky. > aFlapTab setName: 'Supplies' translated edge: (sugarNavigator ifTrue: [#top] ifFalse: [#bottom]) color: Color red lighter. > aFlapTab position: (0 @ Project current world sugarAllowance). > aFlapTab setBalloonText: aFlapTab balloonTextForFlapsMenu. > aFlapTab applyThickness: 20. > > aWidth := self currentWorld width. > aStrip extent: aWidth @ (76 * (1 + (1350 // aWidth))). > aStrip beFlap: true. > aStrip autoLineLayout: true. > aStrip vResizeToFit: true. > sugarNavigator > ifTrue: [ > aFlapTab useSolidTab. > aFlapTab height: 20; color: (Color r: 0.804 g: 0.804 b: 0.804)] > ifFalse: [ > aFlapTab color: Color red lighter]. > > ^ aFlapTab > > "Flaps replaceGlobalFlapwithID: 'Supplies' translated"! ! > > > !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! > exampleColorSees > "Form exampleColorSees" > "First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. > Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this > Third shows the hit area - where red touches blue - superimposed on the original scene. > Fourth column is the tally of hits via the old algorithm > Last column shows the tally of hits via the new prim" > > | formA formB maskA offset tally map intersection left top dCanvas sensitiveColor soughtColor index | > formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" > Project current world restoreMorphicDisplay; doOneCycle. > > sensitiveColor := Color red. > soughtColor := Color blue. > > top := 50. > dCanvas := FormCanvas on: Display. > -50 to: 80 by: 10 do:[:p| > offset:= p at 0. "vary this to check different states" > left := 10. > > formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". > formB := Form extent: 100 at 50 depth: 32. > > "make a red square in the middle of the form" > (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: sensitiveColor. > (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. > (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. > "formA displayOn: Display at: left at top rule: Form paint. > dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. > left := left + 150." > > "make a blue block on the right half of the form" > (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. > (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. > "formB displayOn: Display at: left at top rule: Form paint. > dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. > left := left + 150." > > intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). > > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 150. > > maskA := Form extent: intersection extent depth: 1. > > map := Bitmap new: (1 bitShift: (formA depth min: 15)). > map at: (index := sensitiveColor indexInMap: map) put: 1. > > maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. > > "intersect world pixels of the color we're looking for with sensitive pixels mask" > map at: index put: 0. "clear map and reuse it" > map at: (soughtColor indexInMap: map) put: 1. > > maskA > copyBits: intersection > from: formB at: 0 at 0 clippingBox: formB boundingBox > rule: Form and > fillColor: nil > map: map. > > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 170. > > (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). > left := left + 70. > > "now try using the new primitive" > tally := (BitBlt > destForm: formB > sourceForm: formA > fillColor: nil > combinationRule: 3 "really ought to work with nil but prim code checks" > destOrigin: intersection origin > sourceOrigin: (offset negated max: 0 at 0) > extent: intersection extent > clipRect: intersection) > primCompareColor: ((sensitiveColor pixelValueForDepth: formA depth) ) to: ((soughtColor pixelValueForDepth: formB depth) ) test: (Form compareMatchColor bitOr: Form compareTallyFlag). > tally asString asDisplayText displayOn: Display at: left@(top +20). > top:= top + 60]! ! > > !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! > exampleTouchTest > "Form exampleTouchTest" > "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a > non-transparent pixel of the background upon which it is displayed. > First column shows a form with a red block in the midst of transparent area sneaking up on a form with a transparent LHS and blue RHS. The green frame shows the intersection area. > Second column shows in grey the part of the red that is within the intersection. > Third column shows in black the blue that is within the intersection. > Fourth column shows just the A touching B area. > Fifth column is the tally of hits via the old algorithm > Last column shows the tally of hits via the new prim" > |formA formB maskA maskB offset tally map intersection left top dCanvas| > formA := formB := maskA := maskB := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" > > Project current world restoreMorphicDisplay; doOneCycle. > > top := 50. > dCanvas := FormCanvas on: Display. > -50 to: 80 by: 10 do:[:p| > offset:= p at 0. "vary this to check different states" > left := 10. > > formA := Form extent: 100 at 50 depth: 32. > formB := Form extent: 100 at 50 depth: 16. > > "make a red square in the middle of the form" > (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color yellow. > (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. > (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color red. > "formA displayOn: Display at: left at top rule: Form paint. > dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. > left := left + 150." > > "make a blue block on the right half of the form" > (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: Color blue. > (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. > "formB displayOn: Display at: left at top rule: Form paint. > dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. > left := left + 150." > > intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). > > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 150. > > maskA := Form extent: intersection extent depth: 2. > formA displayOn: maskA at: offset - intersection origin rule: Form paint. > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 150. > > maskB := Form extent: intersection extent depth: 2. > formB displayOn: maskB at: intersection origin negated rule: Form paint. > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 150. > > map := Bitmap new: 4 withAll: 1. > map at: 1 put: 0. "transparent" > > maskA copyBits: maskA boundingBox from: maskA at: 0 at 0 colorMap: map. > "maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 150." > > maskB copyBits: maskB boundingBox from: maskB at: 0 at 0 colorMap: map. > "maskB displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 150." > > maskB displayOn: maskA at: 0 at 0 rule: Form and. > maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 170. > > (maskA boundingBox area -( maskA tallyPixelValues at: 1)) asString asDisplayText displayOn: Display at: left@(top +20). > left := left + 70. > > "now try using the new primitive" > tally := (BitBlt > destForm: formB > sourceForm: formA > fillColor: nil > combinationRule: 3 "really ought to work with nil but prim code checks" > destOrigin: intersection origin > sourceOrigin: (offset negated max: 0 at 0) > extent: intersection extent > clipRect: intersection) > primCompareColor: ((Color transparent pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((Color transparent pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorANotColorB bitOr: Form compareTallyFlag). > tally asString asDisplayText displayOn: Display at: left@(top +20). > top:= top + 60]! ! > > !Form class methodsFor: '*Morphic-examples' stamp: 'ct 9/11/2020 19:49'! > exampleTouchingColor > "Form exampleTouchingColor" > "Demonstrate the algorithm used in Scratch code to determine if a sprite's non-transparent pixels touch a > particular color pixel of the background upon which it is displayed. > First column as above shows the sneaky red/yellow pirate sneaking up on the blue/peach galleon. > Second column shows the 1bpp made from the red/yellow/transparent - white -> ignore this, black -> test this > Third shows the hit area (black) superimposed on the original scene > Fourth column is the tally of hits via the old algorithm > Last column shows the tally of hits via the new prim" > |formA formB maskA offset tally map intersection left top dCanvas ignoreColor soughtColor| > formA := formB := maskA := offset := tally := map := intersection := nil. "just to shut up the compiler when testing" > Project current world restoreMorphicDisplay; doOneCycle. > > ignoreColor := Color transparent. > soughtColor := Color blue. > > top := 50. > dCanvas := FormCanvas on: Display. > -50 to: 80 by: 10 do:[:p| > offset:= p at 0. "vary this to check different states" > left := 10. > > formA := (Form extent: 100 at 50 depth: 32) asFormOfDepth: 16 "so we can try original forms of other depths". > formB := Form extent: 100 at 50 depth: 32. > > "make a red square in the middle of the form" > (FormCanvas on: formA) fillRectangle: (25 at 25 extent: 50 at 5) fillStyle: Color red. > (FormCanvas on: formA) fillRectangle: (25 at 30 extent: 50 at 5) fillStyle: Color transparent. > (FormCanvas on: formA) fillRectangle: (25 at 35 extent: 50 at 50) fillStyle: Color yellow. > "formA displayOn: Display at: left at top rule: Form paint. > dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. > left := left + 150." > > "make a blue block on the right half of the form" > (FormCanvas on: formB) fillRectangle: (50 at 0 extent: 50 at 100) fillStyle: soughtColor. > (FormCanvas on: formB) fillRectangle: (60 at 0 extent: 10 at 100) fillStyle: Color palePeach. > "formB displayOn: Display at: left at top rule: Form paint. > dCanvas frameRectangle: (left at top extent: formA extent) width:2 color: Color green. > left := left + 150." > > intersection := (formA boundingBox translateBy: offset) intersect: (formB boundingBox). > > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 150. > > maskA := Form extent: intersection extent depth: 1. > > map := Bitmap new: (1 bitShift: (formA depth min: 15)). > map atAllPut: 1. > map at: ( ignoreColor indexInMap: map) put: 0. > > maskA copyBits: (intersection translateBy: offset negated) from: formA at: 0 at 0 colorMap: map. > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. left := left + 150. > > "intersect world pixels of the color we're looking for with sensitive pixels mask" > map atAllPut: 0. "clear map and reuse it" > map at: (soughtColor indexInMap: map) put: 1. > > maskA > copyBits: intersection > from: formB at: 0 at 0 clippingBox: formB boundingBox > rule: Form and > fillColor: nil > map: map. > > formB displayOn: Display at: left at top rule: Form paint. > formA displayOn: Display at: (left at top) + offset rule: Form paint. > maskA displayOn: Display at: (left at top) + intersection origin rule: Form paint. > dCanvas frameRectangle: (intersection translateBy: left at top) width:2 color: Color green. > left := left + 170. > > (maskA tallyPixelValues at: 2) asString asDisplayText displayOn: Display at: left@(top +20). > left := left + 70. > > "now try using the new primitive" > tally := (BitBlt > destForm: formB > sourceForm: formA > fillColor: nil > combinationRule: 3 "really ought to work with nil but prim code checks" > destOrigin: intersection origin > sourceOrigin: (offset negated max: 0 at 0) > extent: intersection extent > clipRect: intersection) > primCompareColor: ((ignoreColor pixelValueForDepth: formA depth) bitAnd: 16rFFFFFF) to: ((soughtColor pixelValueForDepth: formB depth) bitAnd: 16rFFFFFF) test: (Form compareNotColorAMatchColorB bitOr: Form compareTallyFlag). > tally asString asDisplayText displayOn: Display at: left@(top +20). > top:= top + 60]! ! > > > !HandBugs methodsFor: 'tests' stamp: 'ct 9/12/2020 14:41'! > testTargetPoint > "self new testTargetPoint" > "self run: #testTargetPoint" > > "This should not throw an exception." > self currentHand targetPoint > > ! ! > > > !Lexicon methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:16'! > offerMenu > "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" > > | aMenu | > aMenu := MenuMorph new defaultTarget: self. > aMenu addTitle: 'Lexicon' translated. > aMenu addStayUpItem. > aMenu addTranslatedList: #( > ('vocabulary...' chooseVocabulary) > ('what to show...' offerWhatToShowMenu) > - > ('inst var refs (here)' setLocalInstVarRefs) > ('inst var assignments (here)' setLocalInstVarDefs) > ('class var refs (here)' setLocalClassVarRefs) > - > ('navigate to a sender...' navigateToASender) > ('recent...' navigateToRecentMethod) > ('show methods in current change set' showMethodsInCurrentChangeSet) > ('show methods with initials...' showMethodsWithInitials) > - > "('toggle search pane' toggleSearch)" > - > ('browse full (b)' browseMethodFull) > ('browse hierarchy (h)' browseClassHierarchy) > ('browse protocol (p)' browseFullProtocol) > - > ('fileOut' fileOutMessage) > ('printOut' printOutMessage) > - > ('senders of... (n)' browseSendersOfMessages) > ('implementors of... (m)' browseMessages) > ('versions (v)' browseVersions) > ('inheritance (i)' methodHierarchy) > - > ('references... (r)' browseVariableReferences) > ('assignments... (a)' browseVariableAssignments) > - > ('more...' shiftedYellowButtonActivity)). > > ^ aMenu popUpInWorld: self currentWorld! ! > > > !InstanceBrowser methodsFor: 'menu commands' stamp: 'ct 9/11/2020 20:12'! > offerMenu > "Offer a menu to the user, in response to the hitting of the menu button on the tool pane" > > | aMenu | > aMenu := MenuMorph new defaultTarget: self. > aMenu title: ('Messages of {1}' translated format: {objectViewed nameForViewer}). > aMenu addStayUpItem. > aMenu addTranslatedList: #( > ('vocabulary...' chooseVocabulary) > ('what to show...' offerWhatToShowMenu) > - > ('inst var refs (here)' setLocalInstVarRefs) > ('inst var defs (here)' setLocalInstVarDefs) > ('class var refs (here)' setLocalClassVarRefs) > - > > ('navigate to a sender...' navigateToASender) > ('recent...' navigateToRecentMethod) > ('show methods in current change set' > showMethodsInCurrentChangeSet) > ('show methods with initials...' > showMethodsWithInitials) > - > "('toggle search pane' toggleSearch)" > > - > - > ('browse full (b)' browseMethodFull) > ('browse hierarchy (h)' browseClassHierarchy) > ('browse protocol (p)' browseFullProtocol) > - > ('fileOut' fileOutMessage) > ('printOut' printOutMessage) > - > ('senders of... (n)' browseSendersOfMessages) > ('implementors of... (m)' browseMessages) > ('versions (v)' browseVersions) > ('inheritance (i)' methodHierarchy) > - > ('references... (r)' browseVariableReferences) > ('assignments... (a)' browseVariableAssignments) > - > ('viewer on me' viewViewee) > ('inspector on me' inspectViewee) > - > ('more...' shiftedYellowButtonActivity)). > > ^ aMenu popUpInWorld: self currentWorld! ! > > > !ListChooser methodsFor: 'actions' stamp: 'ct 9/12/2020 14:42'! > accept > "if the user submits with no valid entry, make them start over" > > | choice | > self canAccept ifFalse: [ > self canAdd ifTrue: [^ self add]. > ^ self changed: #textSelection]. > > choice := self selectedItem. > > self canAdd ifTrue: [ > "Ask the user whether to add the new item or choose the list selection." > (UserDialogBoxMorph > confirm: 'You can either choose an existing item or add a new one.\What do you want?' translated withCRs > title: 'Choose or Add' translated > trueChoice: choice asString > falseChoice: self searchText asString at: self currentHand position) > ifNil: ["Cancelled" self result: nil. ^ self] > ifNotNil: [:answer | > answer ifTrue: [self result: choice] ifFalse: [self result: self searchText asString]] > ] ifFalse: [self result: choice]. > > self changed: #close.! ! > > > !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! > setUp > > previousID := Locale current localeID. > previousKeyboardInterpreter := self currentHand instVarNamed: 'keyboardInterpreter'. > previousClipboardInterpreter := Clipboard default instVarNamed: 'interpreter'. > self currentHand clearKeyboardInterpreter. > Clipboard default clearInterpreter.! ! > > !LocaleTest methodsFor: 'running' stamp: 'ct 9/12/2020 14:42'! > tearDown > > self currentHand instVarNamed: 'keyboardInterpreter' put: previousKeyboardInterpreter. > Clipboard default instVarNamed: 'interpreter' put: previousClipboardInterpreter. > Locale switchToID: (LocaleID isoLanguage: previousID).! ! > > !LocaleTest methodsFor: 'tests' stamp: 'ct 9/12/2020 14:43'! > testLocaleChanged > "self debug: #testLocaleChanged" > "LanguageEnvironment >> startUp is called from Prject >> localeChanged" > "takes quite a while" > Project current updateLocaleDependents. > self assert: (self currentHand instVarNamed: 'keyboardInterpreter') isNil description: 'non-nil keyboardInterpreter'. > self assert: (Clipboard default instVarNamed: 'interpreter') isNil description: 'non-nil interpreter'. > Locale switchToID: (LocaleID isoLanguage: 'ja'). > self assert: 'ja' equals: Locale current localeID isoLanguage. > Locale switchToID: (LocaleID isoLanguage: 'en'). > self assert: 'en' equals: Locale current localeID isoLanguage.! ! > > > !MCCodeTool methodsFor: 'menus' stamp: 'ct 9/11/2020 20:17'! > browseFullProtocol > "Open up a protocol-category browser on the value of the receiver's current selection. If in mvc, an old-style protocol browser is opened instead. Someone who still uses mvc might wish to make the protocol-category-browser work there too, thanks." > > (Smalltalk isMorphic and: [Smalltalk hasClassNamed: #Lexicon]) ifFalse: [^ self spawnFullProtocol]. > self selectedClassOrMetaClass ifNotNil: [:class | > ^ (Smalltalk at: #Lexicon) new > openOnClass: class > inWorld: self currentWorld > showingSelector: self selectedMessageName]. > ^ nil! ! > > > !Morph methodsFor: 'copying' stamp: 'ct 9/12/2020 14:20'! > duplicate > "Make and return a duplicate of the receiver" > > | newMorph aName w aPlayer topRend | > ((topRend := self topRendererOrSelf) ~~ self) ifTrue: [^ topRend duplicate]. > > self okayToDuplicate ifFalse: [^ self]. > aName := (w := self world) ifNotNil: > [w nameForCopyIfAlreadyNamed: self]. > newMorph := self veryDeepCopy. > aName ifNotNil: [newMorph setNameTo: aName]. > > newMorph arrangeToStartStepping. > newMorph privateOwner: nil. "no longer in world" > newMorph isPartsDonor: false. "no longer parts donor" > (aPlayer := newMorph player) belongsToUniClass ifTrue: > [aPlayer class bringScriptsUpToDate]. > aPlayer ifNotNil: [self currentWorld presenter flushPlayerListCache]. > ^ newMorph! ! > > !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:20'! > justDroppedInto: aMorph event: anEvent > "This message is sent to a dropped morph after it has been dropped on -- and been accepted by -- a drop-sensitive morph" > > | partsBinCase cmd | > (self formerOwner notNil and: [self formerOwner ~~ aMorph]) > ifTrue: [self removeHalo]. > self formerOwner: nil. > self formerPosition: nil. > cmd := self valueOfProperty: #undoGrabCommand. > cmd ifNotNil:[aMorph rememberCommand: cmd. > self removeProperty: #undoGrabCommand]. > (partsBinCase := aMorph isPartsBin) ifFalse: > [self isPartsDonor: false]. > (self isInWorld and: [partsBinCase not]) ifTrue: > [self world startSteppingSubmorphsOf: self]. > "Note an unhappy inefficiency here: the startStepping... call will often have already been called in the sequence leading up to entry to this method, but unfortunately the isPartsDonor: call often will not have already happened, with the result that the startStepping... call will not have resulted in the startage of the steppage." > > "An object launched by certain parts-launcher mechanisms should end up fully visible..." > (self hasProperty: #beFullyVisibleAfterDrop) ifTrue: > [aMorph == self currentWorld ifTrue: > [self goHome]. > self removeProperty: #beFullyVisibleAfterDrop].! ! > > !Morph methodsFor: 'dropping/grabbing' stamp: 'ct 9/12/2020 14:19'! > slideToTrash: evt > "Perhaps slide the receiver across the screen to a trash can and make it disappear into it. In any case, remove the receiver from the screen." > > | aForm trash startPoint endPoint morphToSlide | > ((self renderedMorph == ScrapBook default scrapBook) or: [self renderedMorph isKindOf: TrashCanMorph]) ifTrue: > [self dismissMorph. ^ self]. > TrashCanMorph slideDismissalsToTrash ifTrue: > [morphToSlide := self representativeNoTallerThan: 200 norWiderThan: 200 thumbnailHeight: 100. > aForm := morphToSlide imageForm offset: (0 at 0). > trash := self currentWorld > findDeepSubmorphThat: > [:aMorph | (aMorph isKindOf: TrashCanMorph) and: > [aMorph topRendererOrSelf owner == self currentWorld]] > ifAbsent: > [trash := TrashCanMorph new. > trash position: self currentWorld bottomLeft - (0 @ (trash extent y + 26)). > trash openInWorld. > trash]. > endPoint := trash fullBoundsInWorld center. > startPoint := self topRendererOrSelf fullBoundsInWorld center - (aForm extent // 2)]. > self dismissMorph. > self currentWorld displayWorld. > TrashCanMorph slideDismissalsToTrash ifTrue: > [aForm slideFrom: startPoint to: endPoint nSteps: 12 delay: 15]. > ScrapBook default addToTrash: self! ! > > !Morph methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:45'! > yellowButtonActivity: shiftState > "Find me or my outermost owner that has items to add to a > yellow button menu. > shiftState is true if the shift was pressed. > Otherwise, build a menu that contains the contributions from > myself and my interested submorphs, > and present it to the user." > | menu | > self isWorldMorph > ifFalse: [| outerOwner | > outerOwner := self outermostOwnerWithYellowButtonMenu. > outerOwner > ifNil: [^ self]. > outerOwner == self > ifFalse: [^ outerOwner yellowButtonActivity: shiftState]]. > menu := self buildYellowButtonMenu: self currentHand. > menu > addTitle: self externalName > icon: (self iconOrThumbnailOfSize: (Preferences tinyDisplay ifTrue: [16] ifFalse: [28])). > menu popUpInWorld: self currentWorld! ! > > !Morph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:00'! > buildYellowButtonMenu: aHand > "Build the morph menu for the yellow button." > > | menu | > menu := MenuMorph new defaultTarget: self. > self addNestedYellowButtonItemsTo: menu event: self currentEvent. > MenuIcons decorateMenu: menu. > ^ menu! ! > > !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:44'! > addMiscExtrasTo: aMenu > "Add a submenu of miscellaneous extra items to the menu." > > | realOwner realMorph subMenu | > subMenu := MenuMorph new defaultTarget: self. > (self isWorldMorph not and: [(self renderedMorph isSystemWindow) not]) > ifTrue: [subMenu add: 'put in a window' translated action: #embedInWindow]. > > self isWorldMorph ifFalse: > [subMenu add: 'adhere to edge...' translated action: #adhereToEdge. > subMenu addLine]. > > realOwner := (realMorph := self topRendererOrSelf) owner. > (realOwner isKindOf: TextPlusPasteUpMorph) ifTrue: > [subMenu add: 'GeeMail stuff...' translated subMenu: (realOwner textPlusMenuFor: realMorph)]. > > subMenu > add: 'add mouse up action' translated action: #addMouseUpAction; > add: 'remove mouse up action' translated action: #removeMouseUpAction; > add: 'hand me tiles to fire this button' translated action: #handMeTilesToFire. > subMenu addLine. > subMenu add: 'arrowheads on pen trails...' translated action: #setArrowheads. > subMenu addLine. > > subMenu defaultTarget: self topRendererOrSelf. > subMenu add: 'draw new path' translated action: #definePath. > subMenu add: 'follow existing path' translated action: #followPath. > subMenu add: 'delete existing path' translated action: #deletePath. > subMenu addLine. > > self addGestureMenuItems: subMenu hand: self currentHand. > > aMenu add: 'extras...' translated subMenu: subMenu! ! > > !Morph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:21'! > chooseNewGraphicCoexisting: aBoolean > "Allow the user to choose a different form for her form-based morph" > > | replacee aGraphicalMenu | > self isInWorld ifFalse: "menu must have persisted for a not-in-world object." > [aGraphicalMenu := Project current world submorphThat: > [:m | (m isKindOf: GraphicalMenu) and: [m target == self]] > ifNone: > [^ self]. > ^ aGraphicalMenu show; flashBounds]. > aGraphicalMenu := GraphicalMenu new > initializeFor: self > withForms: self reasonableForms > coexist: aBoolean. > aBoolean > ifTrue: [self primaryHand attachMorph: aGraphicalMenu] > ifFalse: [replacee := self topRendererOrSelf. > replacee owner replaceSubmorph: replacee by: aGraphicalMenu]! ! > > !Morph methodsFor: 'meta-actions' stamp: 'ct 9/12/2020 14:20'! > indicateAllSiblings > "Indicate all the receiver and all its siblings by flashing momentarily." > > | aPlayer allBoxes | > (aPlayer := self topRendererOrSelf player) belongsToUniClass ifFalse: [^ self "error: 'not uniclass'"]. > allBoxes := aPlayer class allInstances > select: [:m | m costume world == self currentWorld] > thenCollect: [:m | m costume boundsInWorld]. > > 5 timesRepeat: > [Display flashAll: allBoxes andWait: 120].! ! > > !Morph methodsFor: 'meta-actions' stamp: 'ct 9/11/2020 18:00'! > resizeFromMenu > "Commence an interaction that will resize the receiver" > > ^ self resizeMorph: self currentEvent! ! > > !Morph methodsFor: 'structure' stamp: 'ct 9/24/2020 13:37'! > activeHand > "Alias for #currentHand. Maybe we want to deprecate this at some point" ... > > ^ self currentHand! ! > > !Morph methodsFor: 'structure' stamp: 'ct 9/16/2020 19:06'! > primaryHand > > ^ self activeHand! ! > > !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:45'! > deleteUnlessHasFocus > "Runs on a step timer because we cannot be guaranteed to get focus change events." > (self currentHand keyboardFocus ~= self and: [ self isInWorld ]) ifTrue: > [ self > stopSteppingSelector: #deleteUnlessHasFocus ; > delete ]! ! > > !Morph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:20'! > dismissViaHalo > "The user has clicked in the delete halo-handle. This provides a hook in case some concomitant action should be taken, or if the particular morph is not one which should be put in the trash can, for example." > > | cmd | > self setProperty: #lastPosition toValue: self positionInWorld. > self dismissMorph. > TrashCanMorph preserveTrash ifTrue: [ > TrashCanMorph slideDismissalsToTrash > ifTrue:[self slideToTrash: nil] > ifFalse:[TrashCanMorph moveToTrash: self]. > ]. > > cmd := Command new cmdWording: 'dismiss ' translated, self externalName. > cmd undoTarget: Project current world selector: #reintroduceIntoWorld: argument: self. > cmd redoTarget: Project current world selector: #onceAgainDismiss: argument: self. > Project current world rememberCommand: cmd.! ! > > !Morph methodsFor: 'e-toy support' stamp: 'ct 9/12/2020 14:19'! > referencePlayfield > "Answer the PasteUpMorph to be used for cartesian-coordinate reference" > > | former | > owner ifNotNil: > [(self topRendererOrSelf owner isHandMorph and: [(former := self formerOwner) notNil]) > ifTrue: > [former := former renderedMorph. > ^ former isPlayfieldLike > ifTrue: [former] > ifFalse: [former referencePlayfield]]]. > > self allOwnersDo: [:o | o isPlayfieldLike ifTrue: [^ o]]. > ^ Project current world! ! > > !Morph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:45'! > handMeTilesToFire > "Construct a phrase of tiles comprising a line of code that will 'fire' this object, and hand it to the user" > > self currentHand attachMorph: (self assuredPlayer tilesToCall: MethodInterface firingInterface)! ! > > !Morph methodsFor: '*Etoys-Squeakland-geometry' stamp: 'ct 9/12/2020 14:19'! > stagingArea > "Answer a containing Worldlet, or the World if none." > > ^ (self ownerThatIsA: Worldlet) ifNil: [self currentWorld]! ! > > !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:22'! > changeColorTarget: anObject selector: aSymbol originalColor: aColor hand: aHand showPalette: showPalette > "Put up a color picker for changing some kind of color. May be modal or modeless, depending on #modalColorPickers setting" > | c aRectangle | > self flag: #arNote. "Simplify this due to anObject == self for almost all cases" > c := ColorPickerMorph new. > c > choseModalityFromPreference; > sourceHand: aHand; > target: anObject; > selector: aSymbol; > originalColor: aColor. > showPalette ifFalse: [c initializeForJustCursor]. > aRectangle := (anObject == self currentWorld) > ifTrue: [self currentHand position extent: (20 at 20)] > ifFalse: [anObject isMorph > ifFalse: [Rectangle center: self position extent: (20 at 20)] > ifTrue: [anObject fullBoundsInWorld]]. > > c putUpFor: anObject near: aRectangle.! ! > > !Morph methodsFor: '*Etoys-Squeakland-meta-actions' stamp: 'ct 9/12/2020 14:45'! > showEmbedMenu > "Put up a menu offering embed targets. Emphasize the current position. Theoretically this method will only be called when there are at least two alternatives." > > | aMenu | > aMenu := self addEmbeddingMenuItemsTo: nil hand: self currentHand. > aMenu title: ('embed {1} in...' translated format: {self externalName }). > aMenu popUpInWorld! ! > > !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:20'! > hideWillingnessToAcceptDropFeedback > "Make the receiver stop looking ready to show some welcoming feedback" > > self currentWorld removeHighlightFeedback > > ! ! > > !Morph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:19'! > showWillingnessToAcceptDropFeedback > "Make the receiver look ready to show show some welcoming feedback" > > | aMorph | > aMorph := RectangleMorph new bounds: self bounds.. > aMorph beTransparent; borderWidth: 4; borderColor: (Color green); lock. > aMorph setProperty: #affilliatedPad toValue: (self ownerThatIsA: TilePadMorph). > self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! > > !Morph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 15:17'! > openInWorldOrWorldlet > "Open in the world-like creature affiliated with the active Hand." > > | aRecorder aWorldlet | > (self currentHand isKindOf: HandMorphForReplay) ifTrue: > [((aRecorder := self currentHand recorder) isKindOf: MentoringEventRecorder) > ifTrue: > [aWorldlet := aRecorder contentArea. > self center: aWorldlet center. > aWorldlet addMorphFront: self. > ^ self]]. > > self openInWorld.! ! > > > !AllPlayersTool methodsFor: 'reinvigoration' stamp: 'ct 9/12/2020 14:25'! > reinvigorate > "Referesh the contents of the receiver" > > (submorphs copyFrom: 3 to: submorphs size) do: [:m | m delete]. > self currentWorld doOneCycleNow. > self playSoundNamed: 'scritch'. > (Delay forMilliseconds: 700) wait. > self currentWorld presenter reinvigoratePlayersTool: self. > self playSoundNamed: 'scratch'.! ! > > > !AllScriptsTool methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:26'! > addSecondLineOfControls > "Add the second line of controls" > > | aRow outerButton aButton worldToUse | > aRow := AlignmentMorph newRow listCentering: #center; color: Color transparent. > outerButton := AlignmentMorph newRow. > outerButton wrapCentering: #center; cellPositioning: #leftCenter. > outerButton color: Color transparent. > outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. > outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). > aButton > target: self; > actionSelector: #toggleWhetherShowingOnlyActiveScripts; > getSelector: #showingOnlyActiveScripts. > outerButton addTransparentSpacerOfSize: (4 at 0). > outerButton addMorphBack: (StringMorph contents: 'tickers only' translated font: ScriptingSystem fontForEToyButtons) lock. > outerButton setBalloonText: 'If checked, then only scripts that are paused or ticking will be shown' translated. > aRow addMorphBack: outerButton. > > aRow addTransparentSpacerOfSize: 20 at 0. > aRow addMorphBack: self helpButton. > > aRow addTransparentSpacerOfSize: 20 at 0. > > outerButton := AlignmentMorph newRow. > outerButton wrapCentering: #center; cellPositioning: #leftCenter. > outerButton color: Color transparent. > outerButton hResizing: #shrinkWrap; vResizing: #shrinkWrap. > outerButton addMorph: (aButton := UpdatingThreePhaseButtonMorph checkBox). > aButton > target: self; > actionSelector: #toggleWhetherShowingAllInstances; > getSelector: #showingAllInstances. > outerButton addTransparentSpacerOfSize: (4 at 0). > outerButton addMorphBack: (StringMorph contents: 'all instances' translated font: ScriptingSystem fontForEToyButtons) lock. > outerButton setBalloonText: 'If checked, then entries for all instances will be shown, but if not checked, scripts for only one representative of each different kind of object will be shown. Consult the help available by clicking on the purple ? for more information.' translated. > aRow addMorphBack: outerButton. > > self addMorphBack: aRow. > worldToUse := self isInWorld ifTrue: [self world] ifFalse: [self currentWorld]. > worldToUse presenter reinvigorateAllScriptsTool: self. > self layoutChanged.! ! > > > !BookMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:39'! > findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer pageNum: pageNum > "Call once to search a page of the book. Return true if found and highlight the text. oldContainer should be NIL. > (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" > > | container wasIn strings old good insideOf place start | > good := true. > start := startIndex. > strings := oldContainer ifNil: > ["normal case" > > rawStrings] > ifNotNil: > [(pages at: pageNum) isInMemory > ifFalse: [rawStrings] > ifTrue: [(pages at: pageNum) allStringsAfter: oldContainer]]. > keys do: > [:searchString | | thisWord | > "each key" > > good > ifTrue: > [thisWord := false. > strings do: > [:longString | | index | > (index := longString > findString: searchString > startingAt: start > caseSensitive: false) > 0 > ifTrue: > [thisWord not & (searchString == keys first) > ifTrue: > [insideOf := longString. > place := index]. > thisWord := true]. > start := 1]. "only first key on first container" > good := thisWord]]. > good > ifTrue: > ["all are on this page" > > wasIn := (pages at: pageNum) isInMemory. > self goToPage: pageNum. > wasIn > ifFalse: > ["search again, on the real current text. Know page is in." > > ^self > findText: keys > inStrings: ((pages at: pageNum) allStringsAfter: nil) > startAt: startIndex > container: oldContainer > pageNum: pageNum "recompute"]]. > (old := self valueOfProperty: #searchContainer) ifNotNil: > [(old respondsTo: #editor) > ifTrue: > [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" > old changed]]. > good > ifTrue: > ["have the exact string object" > > (container := oldContainer) ifNil: > [container := self > highlightText: keys first > at: place > in: insideOf] > ifNotNil: > [container userString == insideOf > ifFalse: > [container := self > highlightText: keys first > at: place > in: insideOf] > ifTrue: > [(container isTextMorph) > ifTrue: > [container editor selectFrom: place to: keys first size - 1 + place. > container changed]]]. > self setProperty: #searchContainer toValue: container. > self setProperty: #searchOffset toValue: place. > self setProperty: #searchKey toValue: keys. "override later" > self currentHand newKeyboardFocus: container. > ^true]. > ^false! ! > > !BookMorph methodsFor: 'navigation' stamp: 'ct 9/12/2020 14:26'! > goToPageMorph: newPage transitionSpec: transitionSpec > "Go to a page, which is assumed to be an element of my pages array (if it is not, this method returns quickly. Apply the transitionSpec provided." > > | pageIndex aWorld oldPageIndex ascending tSpec readIn | > pages isEmpty ifTrue: [^self]. > self setProperty: #searchContainer toValue: nil. "forget previous search" > self setProperty: #searchOffset toValue: nil. > self setProperty: #searchKey toValue: nil. > pageIndex := pages identityIndexOf: newPage ifAbsent: [^self "abort"]. > readIn := newPage isInMemory not. > oldPageIndex := pages identityIndexOf: currentPage ifAbsent: [nil]. > ascending := (oldPageIndex isNil or: [newPage == currentPage]) > ifTrue: [nil] > ifFalse: [oldPageIndex < pageIndex]. > tSpec := transitionSpec ifNil: > ["If transition not specified by requestor..." > > newPage valueOfProperty: #transitionSpec > ifAbsent: > [" ... then consult new page" > > self transitionSpecFor: self " ... otherwise this is the default"]]. > self flag: #arNote. "Probably unnecessary" > (aWorld := self world) ifNotNil: [self primaryHand releaseKeyboardFocus]. > currentPage ifNotNil: [currentPage updateCachedThumbnail]. > self currentPage notNil > ifTrue: > [(((pages at: pageIndex) owner isKindOf: TransitionMorph) > and: [(pages at: pageIndex) isInWorld]) > ifTrue: [^self "In the process of a prior pageTurn"]. > self currentPlayerDo: [:aPlayer | aPlayer runAllClosingScripts]. > self removeViewersOnSubsIn: self currentWorld presenter. > ascending ifNotNil: > ["Show appropriate page transition and start new page when done" > > currentPage stopStepping. > (pages at: pageIndex) position: currentPage position. > ^(TransitionMorph > effect: tSpec second > direction: tSpec third > inverse: (ascending or: [transitionSpec notNil]) not) > showTransitionFrom: currentPage > to: (pages at: pageIndex) > in: self > whenStart: [self playPageFlipSound: tSpec first] > whenDone: > [currentPage > delete; > fullReleaseCachedState. > self insertPageMorphInCorrectSpot: (pages at: pageIndex). > self adjustCurrentPageForFullScreen. > self snapToEdgeIfAppropriate. > aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. > self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. > (aWorld := self world) ifNotNil: > ["WHY??" > > aWorld displayWorld]. > readIn > ifTrue: > [currentPage updateThumbnailUrlInBook: self url. > currentPage sqkPage computeThumbnail "just store it"]]]. > > "No transition, but at least decommission current page" > currentPage > delete; > fullReleaseCachedState]. > self insertPageMorphInCorrectSpot: (pages at: pageIndex). "sets currentPage" > self adjustCurrentPageForFullScreen. > self snapToEdgeIfAppropriate. > aWorld ifNotNil: [self world startSteppingSubmorphsOf: currentPage]. > self currentPlayerDo: [:aPlayer | aPlayer runAllOpeningScripts]. > (aWorld := self world) ifNotNil: > ["WHY??" > aWorld displayWorld]. > readIn > ifTrue: > [currentPage updateThumbnailUrl. > currentPage sqkPage computeThumbnail "just store it"]. > self currentWorld presenter flushPlayerListCache.! ! > > !BookMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:38'! > addAdvancedItemsTo: aMenu > "Add advanced items to a menu which allow the user to affect all the pages of the book. NB balloon help msgs still pending." > > | subMenu | > subMenu := MenuMorph new defaultTarget: self. > subMenu addTranslatedList: #( > ('make all pages the same size as this page' makeUniformPageSize 'Make all the pages of this book be the same size as the page currently showing.') > ('set background color for all pages' #setPageColor 'Choose a color to assign as the background color for all of this book''s pages') > - > ('uncache page sorter' uncachePageSorter) > ('make a thread of projects in this book' buildThreadOfProjects) > - > ('make this the template for new pages' setNewPagePrototype)) translatedNoop. > > "NB The following 2 items do not get auto-updated in a persistent menu." > newPagePrototype ifNotNil: [ > subMenu add: 'clear new-page template' translated action: #clearNewPagePrototype]. > self isInFullScreenMode > ifTrue: [ > subMenu add: 'exit full screen' translated action: #exitFullScreen] > ifFalse: [ > subMenu add: 'show full screen' translated action: #goFullScreen]. > > (self currentHand pasteBuffer isKindOf: PasteUpMorph) ifTrue: [ > subMenu addLine. > subMenu add: 'paste book page' translated action: #pasteBookPage]. > > aMenu add: 'advanced...' translated subMenu: subMenu.! ! > > > !CategoryViewer methodsFor: 'get/set slots' stamp: 'ct 9/12/2020 14:39'! > makeUniversalTilesGetter: aMethodInterface event: evt from: aMorph > "Button in viewer performs this to make a universal-tiles getter and attach it to hand." > > | newTiles | > newTiles := self newGetterTilesFor: scriptedPlayer methodInterface: aMethodInterface. > newTiles setProperty: #beScript toValue: true. > owner ifNil: [^ newTiles]. > self currentHand attachMorph: newTiles. > newTiles align: newTiles topLeft with: evt hand position + (7 at 14).! ! > > !CategoryViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 19:42'! > currentVocabulary > "Answer the vocabulary currently installed in the viewer. The outer StandardViewer object holds this information." > > ^ self outerViewer > ifNotNil: [:viewer | viewer currentVocabulary] > ifNil: [(self world ifNil: [self currentWorld]) currentVocabularyFor: scriptedPlayer]! ! > > !CategoryViewer methodsFor: '*Etoys-Squeakland-categories' stamp: 'ct 9/11/2020 19:42'! > assureCategoryFullyVisible > "Keep deleting categoryviewers other than the receiver until the receiver is fully visible." > > | ready toDelete | > ready := false. > [(self bounds bottom > self world bottom) and: [ready not]] whileTrue: [ > owner submorphs size > 2 > ifTrue: [ > toDelete := owner submorphs allButFirst reversed > detect: [:cv | cv ~~ self] > ifNone: [^ self]. > toDelete delete. > self world doOneCycleNow] > ifFalse: [ > ready := true]].! ! > > > !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! > addCommandFeedback: evt > "Add screen feedback showing what would be torn off in a drag" > > | aMorph | > aMorph := RectangleMorph new > bounds: self bounds; > beTransparent; > borderWidth: 2; > borderColor: ScriptingSystem commandFeedback; > lock; > yourself. > self currentWorld addHighlightMorph: aMorph for: self outmostScriptEditor.! ! > > !CompoundTileMorph methodsFor: '*Etoys-Squeakland-miscellaneous' stamp: 'ct 9/11/2020 19:44'! > removeHighlightFeedback > "Remove any existing highlight feedback" > > self world removeHighlightFeedback. > ! ! > > > !DialogWindow methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:44'! > initialize > > super initialize. > > self > changeTableLayout; > listDirection: #topToBottom; > hResizing: #shrinkWrap; > vResizing: #shrinkWrap; > rubberBandCells: true; > setProperty: #indicateKeyboardFocus toValue: #never. > > self createTitle: 'Dialog'. > self createBody. > > self setDefaultParameters. > > keyMap := Dictionary new. > exclusive := true. > autoCancel := false. > preferredPosition := self currentWorld center.! ! > > > !DockingBarMorph methodsFor: 'submorphs-add/remove' stamp: 'ct 9/12/2020 14:40'! > delete > > self currentHand removeKeyboardListener: self. > activeSubMenu ifNotNil: [ > activeSubMenu delete]. > ^ super delete! ! > > > !EtoyDAVLoginMorph methodsFor: 'private' stamp: 'ct 9/11/2020 19:45'! > loginAndDo: aBlock ifCanceled: cb > "EtoyDAVLoginMorph loginAndDo:[:n :p | true] ifCanceled:[]" > self name: '' actionBlock: aBlock cancelBlock: cb; > fullBounds; > position: Display extent - self extent // 2. > self position: self position + (0 at 40). > self currentWorld addMorphInLayer: self.! ! > > !EtoyDAVLoginMorph methodsFor: 'actions' stamp: 'ct 9/11/2020 19:45'! > launchBrowser > > self currentWorld addMorph: self buildPanel centeredNear: Sensor cursorPoint. > (Smalltalk classNamed: #ScratchPlugin) ifNotNil: [:sp | sp primOpenURL: self url].! ! > > > !EventMorph methodsFor: 'drag and drop' stamp: 'ct 9/11/2020 19:45'! > brownDragConcluded > "After the user has manually repositioned the receiver via brown-halo-drag, this is invoked." > > self currentWorld abandonAllHalos. > self eventRoll ifNotNil: > [:evtRoll | evtRoll pushChangesBackToEventTheatre]! ! > > > !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! > abandonReplayHandsAndHalos > "Cleanup after playback." > > self currentWorld abandonReplayHandsAndHalosFor: eventRecorder! ! > > !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:45'! > dismantlePaintBoxArtifacts > "Cleanup after playback -- if a paint-box has been left up, take it down." > > (self currentWorld findA: SketchEditorMorph) ifNotNil: [:skEd | > skEd cancelOutOfPainting].! ! > > !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/12/2020 14:28'! > makeHorizontalRoll > "Create a horizontal roll viewer for this recording space" > > state = #readyToRecord ifTrue: [ > ^ self inform: 'Nothing recorded yet' translated]. > > "self convertToCanonicalForm." "Would prefer to do this but there are still issues." > > eventRoll ifNil: [ > eventRoll := EventRollMorph new. > eventRoll eventTheatre: self]. > > eventRoll formulate. > > eventRoll isInWorld > ifFalse: [eventRoll > openInWorld; > setExtentFromHalo: (self currentWorld width - 10) @ eventRoll height; > top: self bottom; > bottom: (eventRoll bottom min: self currentWorld bottom); > left: self currentWorld left + 2] "presumably zero" > ifTrue: [eventRoll comeToFront].! ! > > !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! > pausePlayback > "Pause the playback. Sender responsible for setting state to #suspendedPlayback" > > eventRecorder pausePlayback. > (self currentWorld findA: SketchEditorMorph) ifNotNil: > [:skEd | skEd cancelOutOfPainting. > ^ self rewind]. > self borderColor: Color orange. > self setProperty: #suspendedContentArea toValue: contentArea veryDeepCopy. > self populateControlsPanel! ! > > !EventRecordingSpace methodsFor: 'commands' stamp: 'ct 9/11/2020 19:46'! > record > "Commence event recording..." > > self currentWorld abandonAllHalos. > self comeToFront. > > initialContentArea := contentArea veryDeepCopy. > self forgetPriorPaintBoxSettings. > initialPicture := contentArea imageForm. > self state: #recording. > self borderColor: Color red. > self populateControlsPanel. > self currentWorld doOneCycleNow. > > eventRecorder record! ! > > !EventRecordingSpace methodsFor: 'processing' stamp: 'ct 9/11/2020 19:45'! > assureContentAreaStaysAt: aPoint > "selbst-verst?????ndlich" > > self currentWorld doOneCycleNow. > self topLeft: ((self topLeft - contentArea topLeft ) + aPoint)! ! > > !EventRecordingSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:46'! > initializeFromPlaybackButton: anEventPlaybackButton > "Initialize my content area, caption, and tape from a playback button." > > | soundEvent | > initialContentArea := anEventPlaybackButton contentArea veryDeepCopy. > eventRecorder tape: anEventPlaybackButton tape veryDeepCopy. > eventRecorder caption: anEventPlaybackButton caption. > soundEvent := eventRecorder tape detect: [:evt | evt > type = #startSound] ifNone: [nil]. > soundEvent ifNotNil: "For benefit of possible re-record of voiceover" > [eventRecorder startSoundEvent: soundEvent]. > initialPicture := anEventPlaybackButton initialPicture veryDeepCopy ifNil: > [self inform: 'caution - old playback; button lacks vital data.' translated. > ^ nil]. > finalPicture := anEventPlaybackButton finalPicture veryDeepCopy. > eventRecorder saved: true. > > self rewind. > self center: self currentWorld center.! ! > > > !EventPlaybackSpace methodsFor: 'initialization' stamp: 'ct 9/11/2020 19:45'! > launchFrom: aButton > "Initialize the receiver from an invoker button, and launch it." > > | where | > self setProperty: #originatingButton toValue: aButton. > self contentArea: aButton contentArea veryDeepCopy tape: aButton tape veryDeepCopy. > self captionString: aButton caption. > self rewind. > autoStart := aButton autoStart. > autoDismiss := aButton autoDismiss. > > "showChrome := aButton showChrome." > where := aButton whereToAppear. > > self openInWorld. > where = #screenCenter ifTrue: [self center: self currentWorld center]. > where = #buttonPosition ifTrue: [self position: aButton position]. > where = #containerOrigin ifTrue: [self position: aButton owner position]. > self goHome. > self addStopper. > > autoStart ifTrue: [self play]! ! > > > !FlapTab methodsFor: 'globalness' stamp: 'ct 9/11/2020 19:47'! > toggleIsGlobalFlap > "Toggle whether the receiver is currently a global flap or not" > > | oldWorld | > self hideFlap. > oldWorld := self currentWorld. > self isGlobalFlap > ifTrue: > [Flaps removeFromGlobalFlapTabList: self. > oldWorld addMorphFront: self] > ifFalse: > [self delete. > Flaps addGlobalFlap: self. > self currentWorld addGlobalFlaps]. > self currentWorld reformulateUpdatingMenus.! ! > > > !GoldBoxMenu methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:41'! > initializeFor: aScriptor > "Answer a graphical menu to be put up in conjunction with the Gold Box" > > | aButton goldBox aReceiver boxBounds example toScale | > scriptor := aScriptor. > lastItemMousedOver := nil. > self removeAllMorphs. > self setProperty: #goldBox toValue: true. > self listDirection: #topToBottom; > hResizing: #spaceFill; extent: 1 at 1; vResizing: #spaceFill. "standard #newColumn stuff" > > self setNameTo: 'Gold Box' translated. > self useRoundedCorners. > self color: Color white. > self borderColor: (Color r: 1.0 g: 0.839 b: 0.065). > self hResizing: #shrinkWrap; vResizing: #shrinkWrap; borderWidth: 4. > { > {ScriptingSystem. #yesNoComplexOfTiles. 'test' translated. 'Test/Yes/No panes for testing a condition.' translated}. > {ScriptingSystem. #timesRepeatComplexOfTiles. 'repeat' translated. 'TimesRepeat panes for running a section of code repeatedly.' translated}. > { ScriptingSystem. #randomNumberTile. 'random' translated. 'A tile that will produce a random number in a given range.' translated}. > { ScriptingSystem. #seminalFunctionTile. 'function' translated. 'A tile representing a function call. Click on the function name or the arrows to change functions.' translated}. > {ScriptingSystem. #buttonUpTile. 'button up?' translated. 'Reports whether the mouse button is up' translated}. > {ScriptingSystem. #buttonDownTile. 'button down?' translated. 'Reports whether the mouse button is down' translated}. > {ScriptingSystem. #randomColorTile. 'random color' translated. 'A tile returning a random color' translated}. > {scriptor playerScripted. #tileToRefer. 'tile for me' translated. 'A tile representing the object being scripted' translated}. > {self. #numericConstantTile. 'number' translated. 'A tile holding a plain number' translated}. > } do: > [:tuple | > aReceiver := tuple first. > example := aReceiver perform: tuple second. > > aButton := IconicButton new target: aReceiver. > aButton borderWidth: 0; > color: Color transparent. > toScale := tuple size >= 5 > ifTrue: > [tuple first perform: tuple fifth] "bail-out for intractable images." > ifFalse: > [example imageForm]. > aButton labelGraphic: (toScale copy scaledToHeight: 40). > > aButton actionSelector: #launchPartOffsetVia:label:. > aButton arguments: {tuple second. tuple third}. > (tuple size > 3 and: [tuple fourth isEmptyOrNil not]) ifTrue: > [aButton setBalloonText: tuple fourth]. > aButton actWhen: #buttonDown. > aButton on: #mouseEnter send: #mousedOverEvent:button: to: self. > aButton on: #click send: #delete to: self. > self addMorphBack: aButton]. > goldBox := aScriptor submorphs first submorphThat: [:m | (m isKindOf: SimpleButtonMorph) and: [m actionSelector == #offerGoldBoxMenu]] ifNone: [nil]. > goldBox > ifNil: > [self position: self currentHand position] > ifNotNil: > [boxBounds := goldBox boundsInWorld. > self center: boxBounds center. > self left: (boxBounds center x - (self width // 2)). > self top: boxBounds bottom]. > lastItemMousedOver := nil. > self on: #mouseLeave send: #mouseLeftMenuWithEvent: to: self. > self on: #mouseLeaveDragging send: #delete to: self.! ! > > > !GrabPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:41'! > justTornOffFromPartsBin > > super justTornOffFromPartsBin. > self image: (Form extent: 0 @ 0). "hide the icon" > self currentHand showTemporaryCursor: Cursor crossHair.! ! > > > !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! > doDirection: anEvent with: directionHandle > "The mouse went down on the forward-direction halo handle; respond appropriately." > > anEvent hand obtainHalo: self. > anEvent shiftPressed > ifTrue: > [directionArrowAnchor := (target point: target referencePosition in: self world) rounded. > self positionDirectionShaft: directionHandle. > self removeAllHandlesBut: directionHandle. > directionHandle setProperty: #trackDirectionArrow toValue: true] > ifFalse: > [self currentHand spawnBalloonFor: directionHandle]! ! > > !HaloMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:10'! > maybeDismiss: evt with: dismissHandle > "Ask hand to dismiss my target if mouse comes up in it." > > evt hand obtainHalo: self. > (dismissHandle containsPoint: evt cursorPoint) > ifFalse: [ > self delete. > target addHalo: evt] > ifTrue: [ > target resistsRemoval ifTrue: > [(UIManager default chooseFrom: { > 'Yes' translated. > 'Um, no, let me reconsider' translated. > } title: 'Really throw this away?' translated) = 1 ifFalse: [^ self]]. > evt hand removeHalo. > self delete. > target dismissViaHalo. > self currentWorld presenter flushPlayerListCache].! ! > > !HaloMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:41'! > prepareToTrackCenterOfRotation: evt with: rotationHandle > "The mouse went down on the center of rotation." > > evt hand obtainHalo: self. > evt shiftPressed > ifTrue: > [self removeAllHandlesBut: rotationHandle. > rotationHandle setProperty: #trackCenterOfRotation toValue: true. > evt hand showTemporaryCursor: Cursor blank] > ifFalse: > [self currentHand spawnBalloonFor: rotationHandle]! ! > > > !HandMorph methodsFor: 'event handling' stamp: 'ct 9/16/2020 18:24'! > cursorPoint > "Implemented for allowing embedded worlds in an event cycle to query a hand's position and get it in its coordinates. The same can be achieved by #point:from: but this is simply much more convenient since it will look as if the hand is in the lower world." > > ^ self currentWorld point: self position from: owner! ! > > !HandMorph methodsFor: 'event handling' stamp: 'ct 9/16/2020 19:47'! > processEvents > "Process user input events from the local input devices." > > | evt evtBuf type hadAny | > self currentEvent ~= lastMouseEvent ifTrue: [ > "Meaning that we were invoked from within an event response. > Make sure z-order is up to date." > self mouseOverHandler processMouseOver: lastMouseEvent]. > > hadAny := false. > [(evtBuf := Sensor nextEvent) isNil] whileFalse: > [evt := nil. "for unknown event types" > type := evtBuf first. > type = EventTypeMouse > ifTrue: [evt := self generateMouseEvent: evtBuf]. > type = EventTypeMouseWheel > ifTrue: [evt := self generateMouseWheelEvent: evtBuf]. > type = EventTypeKeyboard > ifTrue: [evt := self generateKeyboardEvent: evtBuf]. > type = EventTypeDragDropFiles > ifTrue: [evt := self generateDropFilesEvent: evtBuf]. > type = EventTypeWindow > ifTrue:[evt := self generateWindowEvent: evtBuf]. > "All other events are ignored" > (type ~= EventTypeDragDropFiles and: [evt isNil]) ifTrue: [^self]. > evt ifNotNil: ["Finally, handle it." > self handleEvent: evt. > hadAny := true. > > "For better user feedback, return immediately after a mouse event has been processed." > evt isMouse ifTrue: [^ self]]]. > > "note: if we come here we didn't have any mouse events" > mouseClickState ifNotNil: [ > "No mouse events during this cycle. Make sure click states time out accordingly" > mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. > hadAny ifFalse: [ > "No pending events. Make sure z-order is up to date" > self mouseOverHandler processMouseOver: lastMouseEvent].! ! > > !HandMorph methodsFor: 'initialization' stamp: 'ct 9/16/2020 19:44'! > becomeActiveDuring: aBlock > "Make the receiver the active hand during the evaluation of aBlock." > > ^ ActiveHandVariable value: self during: aBlock! ! > > > !HandMorphForReplay methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:30'! > processEvents > "Play back the next event" > > | evt hadMouse hadAny tracker | > suspended == true ifTrue: [^ self]. > hadMouse := hadAny := false. > tracker := recorder objectTrackingEvents. > [(evt := recorder nextEventToPlay) isNil] whileFalse: > [ > ((evt isMemberOf: MouseMoveEvent) and: [evt trail isNil]) ifTrue: [^ self]. > tracker ifNotNil: [tracker currentEventTimeStamp: evt timeStamp]. > evt type == #EOF > ifTrue: > [recorder pauseIn: self currentWorld. > ^ self]. > evt type == #startSound > ifTrue: > [recorder perhapsPlaySound: evt argument. > recorder synchronize. > ^ self]. > evt type == #startEventPlayback > ifTrue: > [evt argument launchPlayback. > recorder synchronize. > ^ self]. > > evt type == #noteTheatreBounds > ifTrue: > ["The argument holds the content rect --for now we don't make any use of that info in this form." > ^ self]. > > evt isMouse ifTrue: [hadMouse := true]. > (evt isMouse or: [evt isKeyboard]) > ifTrue: > [self handleEvent: (evt setHand: self) resetHandlerFields. > hadAny := true]]. > (mouseClickState notNil and: [hadMouse not]) > ifTrue: > ["No mouse events during this cycle. Make sure click states time out accordingly" > > mouseClickState handleEvent: lastMouseEvent asMouseMove from: self]. > hadAny > ifFalse: > ["No pending events. Make sure z-order is up to date" > > self mouseOverHandler processMouseOver: lastMouseEvent]! ! > > > !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:12'! > destroyThread > "Manually destroy the thread" > > (self confirm: ('Destroy thread <{1}> ?' translated format:{threadName})) ifFalse: [^ self]. > self class knownThreads removeKey: threadName ifAbsent: []. > self setProperty: #moribund toValue: true. "In case pointed to in some other project" > self currentWorld keyboardNavigationHandler == self ifTrue: > [self stopKeyboardNavigation]. > self delete.! ! > > !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:13'! > moreCommands > "Put up a menu of options" > > | allThreads aMenu others target | > allThreads := self class knownThreads. > aMenu := MenuMorph new defaultTarget: self. > aMenu addTitle: 'navigation' translated. > > Preferences noviceMode ifFalse:[ > self flag: #deferred. "Probably don't want that stay-up item, not least because the navigation-keystroke stuff is not dynamically handled" > aMenu addStayUpItem > ]. > > others := (allThreads keys reject: [ :each | each = threadName]) asArray sort. > others do: [ :each | > aMenu add: ('switch to <{1}>' translated format:{each}) selector: #switchToThread: argument: each > ]. > > aMenu addList: { > {'switch to recent projects' translated. #getRecentThread}. > #-. > {'create a new thread' translated. #threadOfNoProjects}. > {'edit this thread' translated. #editThisThread}. > {'create thread of all projects' translated. #threadOfAllProjects}. > #-. > {'First project in thread' translated. #firstPage}. > {'Last project in thread' translated. #lastPage} > }. > > (target := self currentIndex + 2) > listOfPages size ifFalse: [ > aMenu > add: ('skip over next project ({1})' translated format:{(listOfPages at: target - 1) first}) > action: #skipOverNext > ]. > > aMenu addList: { > {'jump within this thread' translated. #jumpWithinThread}. > {'insert new project' translated. #insertNewProject}. > #-. > {'simply close this navigator' translated. #delete}. > {'destroy this thread' translated. #destroyThread}. > #- > }. > > (self currentWorld keyboardNavigationHandler == self) ifFalse:[ > aMenu add: 'start keyboard navigation with this thread' translated action: #startKeyboardNavigation > ] > ifTrue: [ > aMenu add: 'stop keyboard navigation with this thread' translated action: #stopKeyboardNavigation > ]. > > aMenu popUpInWorld.! ! > > !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! > positionAppropriately > > | others world otherRects overlaps bottomRight | > (self ownerThatIsA: HandMorph) ifNotNil: [^self]. > others := (world := Project currentWorld) submorphs select: [ :each | each ~~ self and: [each isKindOf: self class]]. > otherRects := others collect: [ :each | each bounds]. > bottomRight := (world hasProperty: #threadNavigatorPosition) > ifTrue: [world valueOfProperty: #threadNavigatorPosition] > ifFalse: [world bottomRight]. > self align: self fullBounds bottomRight with: bottomRight. > self setProperty: #previousWorldBounds toValue: self world bounds. > > [ > overlaps := false. > otherRects do: [ :r | > (r intersects: bounds) ifTrue: [overlaps := true. self bottom: r top]. > ]. > self top < self world top ifTrue: [ > self bottom: bottomRight y. > self right: self left - 1. > ]. > overlaps > ] whileTrue.! ! > > !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:14'! > startKeyboardNavigation > "Tell the active world to starting navigating via desktop keyboard navigation via me" > > self currentWorld keyboardNavigationHandler: self! ! > > !InternalThreadNavigationMorph methodsFor: 'navigation' stamp: 'ct 9/11/2020 20:15'! > stopKeyboardNavigation > "Cease navigating via the receiver in response to desktop keystrokes" > > self currentWorld removeProperty: #keyboardNavigationHandler! ! > > !InternalThreadNavigationMorph methodsFor: 'private' stamp: 'ct 9/11/2020 20:13'! > loadPageWithProgress > "Load the desired page, showing a progress indicator as we go" > > | projectInfo projectName beSpaceHandler | > projectInfo := listOfPages at: currentIndex. > projectName := projectInfo first. > loadedProject := Project named: projectName. > self class know: listOfPages as: threadName. > beSpaceHandler := (Project current world keyboardNavigationHandler == self). > self currentWorld addDeferredUIMessage: > [InternalThreadNavigationMorph openThreadNamed: threadName atIndex: currentIndex beKeyboardHandler: beSpaceHandler]. > > loadedProject ifNil: [ > ComplexProgressIndicator new > targetMorph: self; > historyCategory: 'project loading' translated; > withProgressDo: [ > [ > loadedProject := Project current > fromMyServerLoad: projectName > ] > on: ProjectViewOpenNotification > do: [ :ex | ex resume: false] > "we probably don't want a project view morph in this case" > ]. > ]. > loadedProject ifNil: [ > ^self inform: 'I cannot find that project' translated > ]. > self delete. > > loadedProject enter.! ! > > !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! > resetBottomRightPosition > > self currentWorld removeProperty: #threadNavigatorPosition. > ! ! > > !InternalThreadNavigationMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/11/2020 20:14'! > setBottomRightPosition > > self currentWorld setProperty: #threadNavigatorPosition toValue: self bottomRight. > ! ! > > > !LassoPatchMorph methodsFor: '*Etoys-Squeakland-initialization' stamp: 'ct 9/12/2020 14:42'! > justTornOffFromPartsBin > > super justTornOffFromPartsBin. > self image: (Form extent: 0 @ 0). "hide the icon" > self currentHand showTemporaryCursor: Cursor crossHair! ! > > > !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! > play > "Play the movie, as it were." > > tape ifNil: [^ self]. > tapeStream := ReadStream on: tape. > self resumePlayIn: self currentWorld. > ! ! > > !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! > record > "Commence recording or re-recording." > > tapeStream := WriteStream on: (Array new: 10000). > self resumeRecordIn: self currentWorld. > ! ! > > !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/11/2020 20:18'! > resumePlayingWithoutPassingStop > "Like play, but avoids the stop step that does more than we'd like." > > tapeStream := ReadStream on: tape. > self resumePlayIn: self currentWorld. > ! ! > > !MentoringEventRecorder methodsFor: 'commands' stamp: 'ct 9/12/2020 14:24'! > stop > "Stop recording or playing." > > tapeStream ifNotNil: > [(#(recording recordingWithSound) includes: self state) ifTrue: > [tape := tapeStream contents. > saved := false]]. > self terminateVoiceRecording. "In case doing" > journalFile ifNotNil: > [journalFile close]. > self pauseIn: self currentWorld. > tapeStream := nil. > self state: #atEndOfPlayback. > recordingSpace abandonReplayHandsAndHalos. > recordMeter ifNotNil: [recordMeter width: 1].! ! > > > !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:43'! > popUpEvent: evt in: aWorld > "Present this menu in response to the given event." > > | aHand aPosition | > aHand := evt ifNotNil: [evt hand] ifNil: [self currentHand]. > aPosition := aHand position truncated. > ^ self popUpAt: aPosition forHand: aHand in: aWorld! ! > > !MenuMorph methodsFor: 'control' stamp: 'ct 9/12/2020 14:23'! > popUpNoKeyboard > "Present this menu in the current World, *not* allowing keyboard input into the menu" > > ^ self > popUpAt: self currentHand position > forHand: self currentHand > in: self currentWorld > allowKeyboard: false! ! > > !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! > informUserAt: aPoint during: aBlock > "Add this menu to the Morphic world during the execution of the given block." > > | title world | > title := self allMorphs detect: [ :ea | ea hasProperty: #titleString ]. > title := title submorphs first. > self visible: false. > world := self currentWorld. > aBlock value: [:string| > self visible ifFalse:[ > world addMorph: self centeredNear: aPoint. > self visible: true]. > title contents: string. > self setConstrainedPosition: self currentHand cursorPoint hangOut: false. > self changed. > world displayWorld "show myself"]. > self delete. > world displayWorld.! ! > > !MenuMorph methodsFor: 'modal control' stamp: 'ct 9/12/2020 14:24'! > invokeModal: allowKeyboardControl > "Invoke this menu and don't return until the user has chosen a value. If the allowKeyboarControl boolean is true, permit keyboard control of the menu" > > ^ self > invokeModalAt: self currentHand position > in: self currentWorld > allowKeyboard: allowKeyboardControl! ! > > !MenuMorph methodsFor: 'private' stamp: 'ct 9/12/2020 14:43'! > positionAt: aPoint relativeTo: aMenuItem inWorld: aWorld > "Note: items may not be laid out yet (I found them all to be at 0 at 0), > so we have to add up heights of items above the selected item." > > | i yOffset sub delta | > self fullBounds. "force layout" > i := 0. > yOffset := 0. > [(sub := self submorphs at: (i := i + 1)) == aMenuItem] > whileFalse: [yOffset := yOffset + sub height]. > > self position: aPoint - (2 @ (yOffset + 8)). > > "If it doesn't fit, show it to the left, not to the right of the hand." > self right > aWorld worldBounds right > ifTrue: > [self right: aPoint x + 1]. > > "Make sure that the menu fits in the world." > delta := self bounds amountToTranslateWithin: > (aWorld worldBounds withHeight: ((aWorld worldBounds height - 18) max: (self currentHand position y) + 1)). > delta isZero ifFalse: [self position: self position + delta].! ! > > > !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:17'! > displayAt: aPoint during: aBlock > "Add this menu to the Morphic world during the execution of the given block." > > Smalltalk isMorphic ifFalse: [^ self]. > > [self currentWorld addMorph: self centeredNear: aPoint. > self world displayWorld. "show myself" > aBlock value] > ensure: [self delete]! ! > > !MVCMenuMorph methodsFor: 'invoking' stamp: 'ct 9/11/2020 20:18'! > informUserAt: aPoint during: aBlock > "Add this menu to the Morphic world during the execution of the given block." > > | title w | > Smalltalk isMorphic ifFalse: [^ self]. > > title := self allMorphs detect: [:ea | ea hasProperty: #titleString]. > title := title submorphs first. > self visible: false. > w := self currentWorld. > aBlock value: [:string| > self visible ifFalse: [ > w addMorph: self centeredNear: aPoint. > self visible: true]. > title contents: string. > self setConstrainedPosition: Sensor cursorPoint hangOut: false. > self changed. > w displayWorld "show myself" > ]. > self delete. > w displayWorld.! ! > > > !Morph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:18'! > fromFileName: fullName > "Reconstitute a Morph from the file, presumed to be represent a Morph saved > via the SmartRefStream mechanism, and open it in an appropriate Morphic world" > > | aFileStream morphOrList | > aFileStream := (MultiByteBinaryOrTextStream with: ((FileStream readOnlyFileNamed: fullName) binary contentsOfEntireFile)) binary reset. > morphOrList := aFileStream fileInObjectAndCode. > (morphOrList isKindOf: SqueakPage) ifTrue: [morphOrList := morphOrList contentsMorph]. > Smalltalk isMorphic > ifTrue: [Project current world addMorphsAndModel: morphOrList] > ifFalse: > [morphOrList isMorph ifFalse: [self inform: 'Can only load a single morph > into an mvc project via this mechanism.' translated]. > morphOrList openInWorld]! ! > > > !AllPlayersTool class methodsFor: '*Etoys-Squeakland-parts bin' stamp: 'ct 9/12/2020 14:26'! > allPlayersToolForActiveWorld > "Launch an AllPlayersTool to view the scripted objects of the active world" > > | aTool | > aTool := self newStandAlone. > aTool center: self currentWorld center. > ^ aTool > > " > AllPlayersTool allPlayersToolForActiveWorld > "! ! > > > !AllScriptsTool class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:26'! > allScriptsToolForActiveWorld > "Launch an AllScriptsTool to view scripts of the active world" > > | aTool | > aTool := self newColumn. > aTool initializeFor: self currentWorld presenter. > ^ aTool! ! > > > !AnonymousSoundMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:26'! > fromFileName: fullName > "Create an instance of the receiver from the given file path." > > | newPlayer aSound ext aName | > newPlayer := self new initialize. > ('*aif*' match: fullName) > ifTrue: [aSound := SampledSound fromAIFFfileNamed: fullName]. > ('*wav' match: fullName) > ifTrue: [aSound := SampledSound fromWaveFileNamed: fullName]. > newPlayer := self new. > > ext := FileDirectory extensionFor: fullName. > aName := (FileDirectory on: fullName) pathParts last. > ext size > 0 ifTrue: > [aName := aName copyFrom: 1 to: (aName size - (ext size + 1))]. > > newPlayer sound: aSound interimName: aName. > > newPlayer openInWorld; position: self currentWorld center.! ! > > > !BookMorph class methodsFor: 'fileIn/Out' stamp: 'ct 9/12/2020 14:27'! > openFromFile: fullName > "Reconstitute a Morph from the selected file, presumed to be represent > a Morph saved via the SmartRefStream mechanism, and open it in an > appropriate Morphic world" > > | book aFileStream | > Smalltalk verifyMorphicAvailability ifFalse: [^ self]. > > aFileStream := FileStream readOnlyFileNamed: fullName. > book := BookMorph new. > book setProperty: #url toValue: aFileStream url. > book fromRemoteStream: aFileStream. > aFileStream close. > > Smalltalk isMorphic > ifTrue: [self currentWorld addMorphsAndModel: book] > ifFalse: [book isMorph ifFalse: [^self inform: 'Can only load a single morph\into an mvc project via this mechanism.' withCRs translated]. > book openInWorld]. > book goToPage: 1! ! > > > !EventRecordingSpace class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:46'! > openFromPlaybackButton: aButton > "Open an EventRecordingSpace derived from a playback button. The primary reason for doing this would be to re-record voiceover." > > | aSpace | > aSpace := EventRecordingSpace new. > aSpace initializeFromPlaybackButton: aButton. > aSpace center: self currentWorld center. > aSpace openInWorld! ! > > > !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! > request: queryString > "Create an instance of me whose question is queryString. Invoke it centered at the cursor, and answer the string the user accepts. Answer the empty string if the user cancels." > "FillInTheBlankMorph request: 'What is your favorite color?'" > > ^ self > request: queryString > initialAnswer: '' > centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! > > !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/12/2020 14:41'! > request: queryString initialAnswer: defaultAnswer > "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." > "FillInTheBlankMorph > request: 'What is your favorite color?' > initialAnswer: 'red, no blue. Ahhh!!'" > > ^ self > request: queryString > initialAnswer: defaultAnswer > centerAt: (self currentHand ifNil: [Sensor]) cursorPoint! ! > > !FillInTheBlankMorph class methodsFor: 'instance creation' stamp: 'ct 9/11/2020 19:47'! > request: queryString initialAnswer: defaultAnswer centerAt: aPoint > "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels. > This variant is only for calling from within a Morphic project." > "FillInTheBlankMorph > request: 'Type something, then type CR.' > initialAnswer: 'yo ho ho!!' > centerAt: Display center" > > ^ self > request: queryString > initialAnswer: defaultAnswer > centerAt: aPoint > inWorld: self currentWorld! ! > > !FillInTheBlankMorph class methodsFor: '*Etoys-Squeakland-instance creation' stamp: 'ct 9/11/2020 19:47'! > request: queryString initialAnswer: defaultAnswer onCancelReturn: cancelResponse > "Create an instance of me whose question is queryString with the given initial answer. Invoke it centered at the given point, and answer the string the user accepts. Answer the empty string if the user cancels." > "FillInTheBlankMorph > request: 'What is your favorite color?' > initialAnswer: 'red, no blue. Ahhh!!'" > > ^ self > request: queryString > initialAnswer: defaultAnswer > centerAt: self currentHand cursorPoint > inWorld: self currentWorld > onCancelReturn: cancelResponse! ! > > > !HandMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:11'! > showEvents: aBool > "HandMorph showEvents: true" > "HandMorph showEvents: false" > > ShowEvents := aBool. > aBool ifFalse: [ > Project current world invalidRect: (0 at 0 extent: 250 at 120)].! ! > > > !InternalThreadNavigationMorph class methodsFor: 'known threads' stamp: 'ct 9/11/2020 20:15'! > openThreadNamed: nameOfThread atIndex: anInteger beKeyboardHandler: aBoolean > "Activate the thread of the given name, from the given index; set it up to be navigated via desktop keys if indicated" > > | coll nav | > > coll := self knownThreads at: nameOfThread ifAbsent: [^self]. > nav := Project current world > submorphThat: [ :each | (each isKindOf: self) and: [each threadName = nameOfThread]] > ifNone: > [nav := self basicNew. > nav > listOfPages: coll; > threadName: nameOfThread index: anInteger; > initialize; > openInWorld; > positionAppropriately. > aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav]. > ^ self]. > nav > listOfPages: coll; > threadName: nameOfThread index: anInteger; > removeAllMorphs; > addButtons. > aBoolean ifTrue: [Project current world keyboardNavigationHandler: nav].! ! > > > !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! > chooseFrom: aList lines: linesArray title: queryString > "Choose an item from the given list. Answer the index of the selected item." > > | menu aBlock result | > aBlock := [:v | result := v]. > menu := self new. > menu addTitle: queryString. > 1 to: aList size do: [:i| > menu add: (aList at: i) asString target: aBlock selector: #value: argument: i. > (linesArray includes: i) ifTrue:[menu addLine]]. > MenuIcons decorateMenu: menu. > result := 0. > menu > invokeAt: self currentHand position > in: self currentWorld > allowKeyboard: true. > ^ result! ! > > !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:23'! > confirm: queryString trueChoice: trueChoice falseChoice: falseChoice > "Put up a yes/no menu with caption queryString. The actual wording for the two choices will be as provided in the trueChoice and falseChoice parameters. Answer true if the response is the true-choice, false if it's the false-choice. This is a modal question -- the user must respond one way or the other." > "MenuMorph > confirm: 'Are you hungry?' > trueChoice: 'yes, I''m famished' > falseChoice: 'no, I just ate'" > > | menu aBlock result | > aBlock := [:v | result := v]. > menu := self new. > menu addTitle: queryString icon: MenuIcons confirmIcon. > menu add: trueChoice target: aBlock selector: #value: argument: true. > menu add: falseChoice target: aBlock selector: #value: argument: false. > MenuIcons decorateMenu: menu. > [menu > invokeAt: self currentHand position > in: self currentWorld > allowKeyboard: true. > result == nil] whileTrue. > ^ result! ! > > !MenuMorph class methodsFor: 'utilities' stamp: 'ct 9/12/2020 14:22'! > inform: queryString > "MenuMorph inform: 'I like Squeak'" > > | menu | > menu := self new. > menu addTitle: queryString icon: MenuIcons confirmIcon. > menu add: 'OK' translated target: self selector: #yourself. > MenuIcons decorateMenu: menu. > menu > invokeAt: self currentHand position > in: self currentWorld > allowKeyboard: true.! ! > > > !MorphHierarchy class methodsFor: 'opening' stamp: 'ct 9/12/2020 14:45'! > openOrDelete > | oldMorph | > oldMorph := Project current world submorphs > detect: [:each | each hasProperty: #morphHierarchy] > ifNone: [| newMorph | > newMorph := self new asMorph. > newMorph bottomLeft: self currentHand position. > newMorph openInWorld. > newMorph isFullOnScreen > ifFalse: [newMorph goHome]. > ^ self]. > "" > oldMorph delete! ! > > > !MorphWorldController methodsFor: 'basic control sequence' stamp: 'ct 9/16/2020 19:42'! > controlTerminate > "This window is becoming inactive; restore the normal cursor." > > Cursor normal show. > super controlTerminate.! ! > > > !MorphicEvent methodsFor: 'initialize' stamp: 'ct 9/16/2020 19:43'! > becomeActiveDuring: aBlock > "Make the receiver the active event during the evaluation of aBlock." > > ^ ActiveEventVariable value: self during: aBlock! ! > > > !MultiWindowLabelButtonMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:16'! > performAction > "Override to interpret the actionSelector as a menu accessor and to activate that menu." > > actionSelector ifNil: [^ self]- > (model perform: actionSelector) ifNotNil: [:menu | > menu > invokeModalAt: self position - (0 at 5) > in: self currentWorld > allowKeyboard: Preferences menuKeyboardControl].! ! > > > !NativeImageSegment methodsFor: 'read/write segment' stamp: 'ct 9/12/2020 14:15'! > smartFillRoots: dummy > | refs known ours ww blockers | > "Put all traced objects into my arrayOfRoots. Remove some > that want to be in outPointers. Return blockers, an > IdentityDictionary of objects to replace in outPointers." > > blockers := dummy blockers. > known := (refs := dummy references) size. > refs keys do: [:obj | "copy keys to be OK with removing items" > (obj isSymbol) ifTrue: [refs removeKey: obj. known := known-1]. > (obj class == PasteUpMorph) ifTrue: [ > obj isWorldMorph & (obj owner == nil) ifTrue: [ > (dummy project ~~ nil and: [obj == dummy project world]) ifFalse: [ > refs removeKey: obj. known := known-1. > blockers at: obj put: > (StringMorph contents: 'The worldMorph of a different world')]]]. > "Make a ProjectViewMorph here" > "obj class == Project ifTrue: [Transcript show: obj; cr]." > (blockers includesKey: obj) ifTrue: [ > refs removeKey: obj ifAbsent: [known := known+1]. known := known-1]. > ]. > ours := (dummy project ifNil: [Project current]) world. > refs keysDo: [:obj | > obj isMorph ifTrue: [ > ww := obj world. > (ww == ours) | (ww == nil) ifFalse: [ > refs removeKey: obj. known := known-1. > blockers at: obj put: (StringMorph contents: > obj printString, ' from another world')]]]. > "keep original roots on the front of the list" > dummy rootObject do: [:rr | refs removeKey: rr ifAbsent: []]. > (self respondsTo: #classOrganizersBeRoots:) ifTrue: "an EToys extension" > [self classOrganizersBeRoots: dummy]. > ^dummy rootObject, refs keys asArray! ! > > > !NebraskaSenderMorph methodsFor: 'parts bin' stamp: 'ct 9/12/2020 14:14'! > initializeToStandAlone > > super initializeToStandAlone. > self installModelIn: Project current world.! ! > > > !NebraskaServerMorph class methodsFor: 'as yet unclassified' stamp: 'ct 9/12/2020 14:14'! > serveWorld > > ^ self serveWorld: self currentWorld > ! ! > > > !NewVariableDialogMorph methodsFor: 'build' stamp: 'ct 9/12/2020 14:14'! > rebuild > | buttonColor itsName enableDecimalPlaces | > self removeAllMorphs. > self addAColumn: { > self lockedString: self title. > }. > self addSeparator. > > self addARow: { > self inAColumn: { > (self addARow: { > self lockedString: 'Name:' translated. > self spacer. > varNameText := self newTextMorph > contentsWrapped: self varName; > selectAll; > crAction: (MessageSend > receiver: self > selector: #doAccept); > yourself > }) cellPositioning: #center. > self inAColumn: { > (self addARow: { > self lockedString: 'Type:' translated. > self spacer. > varTypeButton := self buildVarTypeButton > }) cellPositioning: #center. > } named: #varType. > } > }. > self currentHand newKeyboardFocus: varNameText. > self addSeparator. > self addDecimalPlaces. > enableDecimalPlaces := false. > (#(#Number #Point) includes: self varType) > ifTrue: [ enableDecimalPlaces := true]. > self allMorphsDo: [ :each | > itsName := each knownName. > (#(decimalPlaces) includes: itsName) ifTrue: > [self enable: each when: enableDecimalPlaces]]. > > > > > buttonColor := self color lighter. > self addARow: { > self inAColumn: { > (self addARow: { > self > buttonNamed: 'Accept' translated action: #doAccept color: buttonColor > help: 'keep changes made and close panel' translated. > self > buttonNamed: 'Cancel' translated action: #doCancel color: buttonColor > help: 'cancel changes made and close panel' translated. > }) listCentering: #center > } > }! ! > > > !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! > step > "Let all views know that some of my objects need to be updated." > > self monitorList do: [ :object | > object ifNotNil: [self changed: #objectChanged with: object]]. > self monitorList ifEmpty: [ > self world stopStepping: self selector: #step ].! ! > > !ObjectExplorer methodsFor: 'monitoring' stamp: 'ct 9/12/2020 14:12'! > world > > ^ Project current world! ! > > !ObjectExplorer methodsFor: 'accessing - view' stamp: 'ct 9/12/2020 14:12'! > views > > ^ self findDeepSubmorphsIn: self world that: [:morph | > morph modelOrNil = self]! ! > > > !ObjectsTool methodsFor: 'search' stamp: 'ct 9/12/2020 14:45'! > showSearchPane > "Set the receiver up so that it shows the search pane" > > | tabsPane aPane | > modeSymbol == #search ifTrue: [ ^self ]. > > self partsBin removeAllMorphs. > > tabsPane := self tabsPane. > aPane := self newSearchPane. > self replaceSubmorph: tabsPane by: aPane. > > self modeSymbol: #search. > self showMorphsMatchingSearchString. > self currentHand newKeyboardFocus: aPane! ! > > > !ParagraphEditor methodsFor: 'nonediting/nontyping keys' stamp: 'ct 9/12/2020 14:12'! > escapeToDesktop: characterStream > "Pop up a morph to field keyboard input in the context of the desktop" > > Smalltalk isMorphic ifTrue: [ > Project current world putUpWorldMenuFromEscapeKey]. > ^ true! ! > > !ParagraphEditor methodsFor: '*Etoys-Squeakland-editing keys' stamp: 'ct 9/12/2020 14:07'! > shiftEnclose: characterStream > "Insert or remove bracket characters around the current selection. > Flushes typeahead." > > | char left right startIndex stopIndex oldSelection which text | > char := sensor keyboard. > char = $9 ifTrue: [ char := $( ]. > char = $, ifTrue: "[ char := $< ]" > [self closeTypeIn. > Project current world showSourceKeyHit. > ^ true]. > char = $[ ifTrue: [ char := ${ ]. > char = $' ifTrue: [ char := $" ]. > char asciiValue = 27 ifTrue: [ char := ${ ]. "ctrl-[" > > self closeTypeIn. > startIndex := self startIndex. > stopIndex := self stopIndex. > oldSelection := self selection. > which := '([<{"''' indexOf: char ifAbsent: [1]. > left := '([<{"''' at: which. > right := ')]>}"''' at: which. > text := paragraph text. > ((startIndex > 1 and: [stopIndex <= text size]) > and: > [(text at: startIndex-1) = left and: [(text at: stopIndex) = right]]) > ifTrue: > ["already enclosed; strip off brackets" > self selectFrom: startIndex-1 to: stopIndex. > self replaceSelectionWith: oldSelection] > ifFalse: > ["not enclosed; enclose by matching brackets" > self replaceSelectionWith: > (Text string: (String with: left), oldSelection string ,(String with: right) > emphasis: emphasisHere). > self selectFrom: startIndex+1 to: stopIndex]. > ^true! ! > > > !PasteUpMorph methodsFor: 'accessing' stamp: 'ct 9/12/2020 14:10'! > flapTab > "Answer the tab affilitated with the receiver. Normally every flap tab is expected to have a PasteUpMorph which serves as its 'referent.'" > > | ww | > self isFlap ifFalse: [^ nil]. > ww := self presenter associatedMorph ifNil: [self]. > ^ ww flapTabs > detect: [:any| any referent == self] > ifNone: [nil]! ! > > !PasteUpMorph methodsFor: 'events-processing' stamp: 'ct 9/16/2020 18:26'! > processEvent: anEvent using: defaultDispatcher > "Reimplemented to install the receiver as the new active world if it is one" > > self isWorldMorph ifFalse: [ > ^ super processEvent: anEvent using: defaultDispatcher]. > > ^ self becomeActiveDuring: [ > super processEvent: anEvent using: defaultDispatcher]! ! > > !PasteUpMorph methodsFor: 'flaps' stamp: 'ct 9/12/2020 14:11'! > correspondingFlapTab > "If there is a flap tab whose referent is me, return it, else return nil. Will also work for flaps on the edge of embedded subareas such as within scripting-areas, but more slowly." > > self currentWorld flapTabs do: > [:aTab | aTab referent == self ifTrue: [^ aTab]]. > > "Catch guys in embedded worldlets" > self currentWorld allMorphs do: > [:aTab | ((aTab isKindOf: FlapTab) and: [aTab referent == self]) ifTrue: [^ aTab]]. > > ^ nil! ! > > !PasteUpMorph methodsFor: 'initialization' stamp: 'ct 9/16/2020 19:44'! > becomeActiveDuring: aBlock > "Make the receiver the active world during the evaluation of aBlock." > > ^ ActiveWorldVariable value: self during: aBlock! ! > > !PasteUpMorph methodsFor: 'menu & halo' stamp: 'ct 9/12/2020 14:07'! > putUpPenTrailsSubmenu > "Put up the pen trails menu" > > | aMenu | > aMenu := MenuMorph new defaultTarget: self. > aMenu title: 'pen trails' translated. > aMenu addStayUpItem. > self addPenTrailsMenuItemsTo: aMenu. > ^ aMenu popUpInWorld: self! ! > > !PasteUpMorph methodsFor: 'structure' stamp: 'ct 9/16/2020 19:39'! > primaryHand > > self == self currentWorld ifFalse: [ > ^ super primaryHand]. > > ^ self hands at: 1 ifAbsent: [nil]! ! > > !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/12/2020 14:45'! > extractScreenRegion: poly andPutSketchInHand: hand > "The user has specified a polygonal area of the Display. > Now capture the pixels from that region, and put in the hand as a Sketch." > | screenForm outline topLeft innerForm exterior | > outline := poly shadowForm. > topLeft := outline offset. > exterior := (outline offset: 0 at 0) anyShapeFill reverse. > screenForm := Form fromDisplay: (topLeft extent: outline extent). > screenForm eraseShape: exterior. > innerForm := screenForm trimBordersOfColor: Color transparent. > self currentHand showTemporaryCursor: nil. > innerForm isAllWhite ifFalse: > [hand attachMorph: (self drawingClass withForm: innerForm)]! ! > > !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 20:07'! > initializeDesktopCommandKeySelectors > "Provide the starting settings for desktop command key selectors. Answer the dictionary." > > "ActiveWorld initializeDesktopCommandKeySelectors" > | dict | > dict := IdentityDictionary new. > self defaultDesktopCommandKeyTriplets do: [:trip | > | messageSend | > messageSend := MessageSend receiver: trip second selector: trip third. > dict at: trip first put: messageSend]. > self setProperty: #commandKeySelectors toValue: dict. > ^ dict! ! > > !PasteUpMorph methodsFor: 'world menu' stamp: 'ct 9/11/2020 18:00'! > putUpWorldMenuFromEscapeKey > Preferences noviceMode > ifFalse: [self putUpWorldMenu: self currentEvent]! ! > > !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/16/2020 19:45'! > install > > owner := nil. "since we may have been inside another world previously" > > submorphs do: [:ss | ss owner isNil ifTrue: [ss privateOwner: self]]. > "Transcript that was in outPointers and then got deleted." > self viewBox: Display boundingBox. > EventSensor default flushEvents. > worldState handsDo: [:h | h initForEvents]. > self installFlaps. > self borderWidth: 0. "default" > (Preferences showSecurityStatus > and: [SecurityManager default isInRestrictedMode]) > ifTrue: > [self > borderWidth: 2; > borderColor: Color red]. > self presenter allExtantPlayers do: [:player | player prepareToBeRunning]. > SystemWindow noteTopWindowIn: self.! ! > > !PasteUpMorph methodsFor: 'world state' stamp: 'ct 9/12/2020 14:06'! > repositionFlapsAfterScreenSizeChange > "Reposition flaps after screen size change" > > (Flaps globalFlapTabsIfAny, self localFlapTabs) do: > [:aFlapTab | > aFlapTab applyEdgeFractionWithin: self bounds]. > Flaps doAutomaticLayoutOfFlapsIfAppropriate! ! > > !PasteUpMorph methodsFor: '*Tools' stamp: 'ct 9/11/2020 20:07'! > defaultDesktopCommandKeyTriplets > "Answer a list of triplets of the form > [+ optional fourth element, a for use in desktop-command-key-help] > that will provide the default desktop command key handlers. If the selector takes an argument, that argument will be the command-key event" > "World initializeDesktopCommandKeySelectors" > > | noviceKeys expertKeys | > > noviceKeys := { > {$o. self. #activateObjectsTool. 'Activate the "Objects Tool"' translated}. > {$r. self. #restoreMorphicDisplay. 'Redraw the screen' translated}. > {$z. self. #undoOrRedoCommand. 'Undo or redo the last undoable command' translated}. > {$F. Project current. #toggleFlapsSuppressed. 'Toggle the display of flaps' translated}. > {$N. self. #toggleClassicNavigatorIfAppropriate. 'Show/Hide the classic Navigator, if appropriate' translated}. > {$M. self. #toggleShowWorldMainDockingBar. 'Show/Hide the Main Docking Bar' translated}. > {$]. Smalltalk. #saveSession. 'Save the image.' translated}. > }. > > Preferences noviceMode ifTrue: [^ noviceKeys]. > > expertKeys := { > {$b. SystemBrowser. #defaultOpenBrowser. 'Open a new System Browser' translated}. > {$k. Workspace. #open. 'Open a new Workspace' translated}. > {$m. self. #putUpNewMorphMenu. 'Put up the "New Morph" menu' translated}. > {$O. self. #findAMonticelloBrowser. 'Bring a Monticello window into focus.' translated}. > {$t. self. #findATranscript:. 'Make a System Transcript visible' translated}. > {$w. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. > {Character escape. SystemWindow. #closeTopWindow. 'Close the topmost window' translated}. > > {$C. self. #findAChangeSorter:. 'Make a Change Sorter visible' translated}. > > {$L. self. #findAFileList:. 'Make a File List visible' translated}. > {$P. self. #findAPreferencesPanel:. 'Activate the Preferences tool' translated}. > {$R. Utilities. #browseRecentSubmissions. 'Make a Recent Submissions browser visible' translated}. > > {$W. self. #findAMessageNamesWindow:. 'Make a MessageNames tool visible' translated}. > {$Z. ChangeList. #browseRecentLog. 'Browse recently-logged changes' translated}. > > {$\. SystemWindow. #sendTopWindowToBack. 'Send the top window to the back' translated}. > {$_. Smalltalk. #quitPrimitive. 'Quit the image immediately.' translated}. > > {$-. Preferences. #decreaseFontSize. 'Decrease all font sizes' translated}. > {$+. Preferences. #increaseFontSize. 'Increase all font sizes' translated}. > }. > > ^ noviceKeys, expertKeys! ! > > !PasteUpMorph methodsFor: '*Etoys-playfield' stamp: 'ct 9/12/2020 14:10'! > galleryOfPlayers > "Put up a tool showing all the players in the project" > > (Project current world findA: AllPlayersTool) ifNotNil: [:aTool | ^ aTool comeToFront]. > AllPlayersTool newStandAlone openInHand > > "ActiveWorld galleryOfPlayers"! ! > > !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:36'! > attemptCleanupReporting: whetherToReport > "Try to fix up some bad things that are known to occur in some etoy projects we've seen. If the whetherToReport parameter is true, an informer is presented after the cleanups" > > | fixes faultyStatusControls | > fixes := 0. > self world ifNotNil: [:world | world submorphs > select: [:m | (m isKindOf: ScriptEditorMorph) and: [m submorphs isEmpty]] > thenDo: [:m | m delete. fixes := fixes + 1]]. > > TransformationMorph allSubInstancesDo: > [:m | (m player notNil and: [m renderedMorph ~~ m]) > ifTrue: > [m renderedMorph visible ifFalse: > [m renderedMorph visible: true. fixes := fixes + 1]]]. > > (Player class allSubInstances select: [:cl | cl isUniClass and: [cl instanceCount > 0]]) do: > [:aUniclass | > fixes := fixes + aUniclass cleanseScripts]. > > self presenter flushPlayerListCache; allExtantPlayers. > > faultyStatusControls := ScriptStatusControl allInstances select: [:m |m fixUpScriptInstantiation]. > fixes := fixes + faultyStatusControls size. > > ScriptNameTile allInstancesDo: > [:aTile | aTile submorphs isEmpty ifTrue: > [aTile setLiteral: aTile literal. > fixes := fixes + 1]]. > > whetherToReport > ifTrue: > [self inform: ('{1} [or more] repair(s) made' translated format: {fixes printString})] > ifFalse: > [fixes > 0 ifTrue: [Transcript cr; show: fixes printString, ' repairs made to existing content.']] > > " > ActiveWorld attemptCleanupReporting: true. > ActiveWorld attemptCleanupReporting: false. > "! ! > > !PasteUpMorph methodsFor: '*Etoys-world menu' stamp: 'ct 9/12/2020 14:09'! > hideAllPlayers > "Remove all Viewers belonging to scripted players associated with the receiver or any of its subjects from the screen." > > | a | > a := OrderedCollection new. > self allMorphsDo: [ :x | > (self presenter currentlyViewing: x player) ifTrue: > [a add: x player viewerFlapTab]]. > > a do: [ :each | each dismissViaHalo].! ! > > !PasteUpMorph methodsFor: '*Etoys-support' stamp: 'ct 9/12/2020 14:08'! > modernizeBJProject > "Prepare a kids' project from the BJ fork of September 2000 -- a once-off thing for converting such projects forward to a modern 3.1a image, in July 2001. Except for the #enableOnlyGlobalFlapsWithIDs: call, this could conceivably be called upon reloading *any* project, just for safety." > > "ActiveWorld modernizeBJProject" > > self flag: #deprecate "ct: No senders". > > ScriptEditorMorph allInstancesDo: > [:m | m userScriptObject]. > Flaps enableOnlyGlobalFlapsWithIDs: {'Supplies' translated}. > self abandonOldReferenceScheme. > self relaunchAllViewers.! ! > > !PasteUpMorph methodsFor: '*Etoys-Squeakland-menu' stamp: 'ct 9/12/2020 14:11'! > abandonUnsituatedPlayers > "If any objects in the project have references, in player-valued variables, to other objects otherwise not present in the project, abandon them and replace former references to them by references to Dot" > > | aList dot slotInfo varName ref allPlayers count | > count := 0. > allPlayers := self presenter reallyAllExtantPlayersNoSort. > aList := allPlayers select: [:m | m belongsToUniClass]. > dot := self presenter standardPlayer. > aList do: > [:p | > p class slotInfo associationsDo: > [:assoc | > slotInfo := assoc value. > varName := assoc key. > (slotInfo type = #Player) ifTrue: > [ref := p instVarNamed: varName. > (allPlayers includes: ref) ifFalse: > [p instVarNamed: varName put: dot. > count := count + 1. > Transcript cr; show: ('Variable named "{1}" in player named "{2}" changed to point to Dot' translated format: {varName. ref externalName})]]]]. > aList := nil. "Increases chance of the next line having desired effect." > self inform: ('{1} item(s) fixed up' translated format: {count}). > > WorldState addDeferredUIMessage: [Smalltalk garbageCollect]! ! > > !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/12/2020 14:07'! > putUpShowSourceMenu: evt title: aTitle > "Put up a menu in response to the show-source button being hit" > > | menu | > self bringTopmostsToFront. > "put up the show-source menu" > menu := (TheWorldMenu new adaptToWorld: self) buildShowSourceMenu. > menu addTitle: aTitle. > menu popUpEvent: evt in: self. > ^ menu! ! > > !PasteUpMorph methodsFor: '*Etoys-Squeakland-world menu' stamp: 'ct 9/11/2020 18:01'! > showSourceKeyHit > "The user hit the 'show source' key on the XO. Our current take on this is simply to put up the world menu..." > > ^ self putUpShowSourceMenu: self currentEvent title: 'etoys source' translated! ! > > !PasteUpMorph methodsFor: '*Etoys-Squeakland-menus' stamp: 'ct 9/12/2020 14:46'! > presentDesktopColorMenu > "Present the menu that governs the fill style of the squeak desktop." > > | aMenu | > aMenu := MenuMorph new defaultTarget: self. > aMenu title: 'desktop color' translated. > self fillStyle addFillStyleMenuItems: aMenu hand: self currentHand from: self. > aMenu addLine. > aMenu add: 'solid fill' translated action: #useSolidFill. > aMenu add: 'gradient fill' translated action: #useGradientFill. > aMenu add: 'bitmap fill' translated action: #useBitmapFill. > aMenu add: 'default fill' translated action: #useDefaultFill. > ^ aMenu popUpInWorld! ! > > !PasteUpMorph methodsFor: 'as yet unclassified' stamp: 'ct 9/24/2020 13:50'! > activeHand > > self == self currentWorld ifFalse: [ > ^ super activeHand]. > > ^ worldState primaryHand! ! > > > !EventTimeline methodsFor: 'dropping/grabbing' stamp: 'ct 9/11/2020 19:47'! > acceptDroppingMorph: aMorph event: evt > "Accept the drop of a morph." > > | aRect anEventRoll itsDuration itsWidthAfterDrop | > self flag: #deferred. "This is a possible place for discovering whether the drop would have damaging effects on the mouse track..." > > (aMorph isKindOf: MouseEventSequenceMorph) > ifTrue: > [itsDuration := aMorph durationInMilliseconds. > itsWidthAfterDrop := itsDuration // self eventRoll millisecondsPerPixel. > super acceptDroppingMorph: aMorph event: evt. > aMorph bounds: ((aMorph left @ 6) extent: (itsWidthAfterDrop @ aMorph height)). > submorphs do: > [:m | > ((m ~~ aMorph) and: [m isKindOf: MouseEventSequenceMorph]) > ifTrue: > [(m bounds intersects: aMorph bounds) > ifTrue: > ["Eureka" > aMorph delete. > aMorph position: 100 at 100. > aMorph openInWorld. > aMorph flash. > ^ self]]]] > ifFalse: > [super acceptDroppingMorph: aMorph event: evt] > . > aRect := (((aMorph left + 10) max: 10) @ 0) extent: 100@ 10. > > (anEventRoll := self eventRoll) pushChangesBackToEventTheatre. "Note that will ultimately result in replacement of the receiver by a new timeline" > aMorph delete. > self currentWorld abandonAllHalos. > anEventRoll scrollPaneForRoll scrollHorizontallyToShow: aRect! ! > > > !PasteUpMorph class methodsFor: '*Etoys-Squeakland-eToys-scripting' stamp: 'ct 9/12/2020 14:09'! > putativeAdditionsToViewerCategoryPlayfieldOptions > "Answer playfield options additions. Some of these are not yet underpinned by code in the current image; these will follow in due course." > > self flag: #deprecate. "ct: No senders" > > ^ #(#'playfield options' ( > (command roundUpStrays 'Bring back all objects whose current coordinates keep them from being visible, so that at least a portion of each of my interior objects can be seen.') > (command makeFitContents 'Adjust my bounds so that I fit precisely around all the objects within me') > (command showAllPlayers 'Make visible the viewers for all players which have user-written scripts in this playfield.') > (command hideAllPlayers 'Make invisible the viewers for all players in this playfield. This will save space before you publish this project') > (command shuffleSubmorphs 'Rearranges my contents in random order') > (command showAllObjectNames 'show names beneath all the objects currently in my interior, except for those for which showing such names is inappropriate.') > (command hideAllObjectNames 'stop showing names beneath all the objects of my interior, If any of them is marked to "always show name", remove that designation')))! ! > > > !PartsBin class methodsFor: '*Etoys-Squeakland-thumbnail cache' stamp: 'ct 9/12/2020 14:11'! > rebuildIconsWithProgress > "Put up an eye-catching progress morph while doing a complete rebuild of all the parts icons in the system." > > | fixBlock | > fixBlock := Project current displayProgressWithJump: 'Building icons' translated. > self clearThumbnailCache. > self cacheAllThumbnails. > fixBlock value. > Project current world fullRepaintNeeded.! ! > > > !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! > addCommandFeedback: evt > "Add screen feedback showing what would be torn off in a drag" > > | aMorph | > (self owner owner isMemberOf: PhraseTileMorph) > ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. > aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: ((submorphs at: (2 max: submorphs size)) bottomRight + (2 at 1))). > "inHotZone := evt ifNil: [true] ifNotNil: [rect containsPoint: evt cursorPoint]." > aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. > Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! > > !PhraseTileMorph methodsFor: '*Etoys-Squeakland-hilighting' stamp: 'ct 9/11/2020 20:47'! > removeHighlightFeedback > "Remove any existing highlight feedback" > > ^ Project current world removeHighlightFeedback > ! ! > > !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/11/2020 20:47'! > createMultipleTestScripts: aCount > "Simulate the action of dropping a copy of the receiver to launch a new script -- for performance testing. To use: Open an Inspector on some tile command in a Viewer, e.g. on 'Car forward 5'. In the trash pane of that Inspector, then, evaluate expressions like: > [self createMultipleTestScripts: 10] timeToRun. > and > MessageTally spyOn: [self createMultipleTestScripts: 4] > " > > | aPosition | > aPosition := 10 at 10. > 1 to: aCount do: > [:i | self forceScriptCreationAt: aPosition. > aPosition := aPosition + (0 @ 50). "avoid dropping into existing scriptor" > Project current world doOneCycle] "refresh viewer"! ! > > !PhraseTileMorph methodsFor: '*Etoys-Squeakland-mouse' stamp: 'ct 9/12/2020 14:47'! > forceScriptCreationAt: aPosition > "For performance testing." > > | dup | > dup := self duplicate. > dup eventHandler: nil. "Remove viewer-related evt mouseover feedback" > dup formerPosition: self currentHand position. > self currentHand > attachMorph: dup; > simulateMorphDropAt: aPosition.! ! > > > !PhraseTileForTest methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:47'! > addCommandFeedback: evt > "Add screen feedback showing what would be torn off in a drag" > > | aMorph | > (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. > aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). > aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. > Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! > > !PhraseTileForTest methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! > mouseDown: evt > "Handle a mouse-down on the receiver" > > | guyToTake catViewer | > guyToTake := CompoundTileMorph new. > guyToTake setNamePropertyTo: 'TestTile' translated. > guyToTake position: evt position + (-25 at 8). > > guyToTake formerPosition: evt hand position. > "self startSteppingSelector: #trackDropZones." > (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: > [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. > guyToTake setProperty: #newPermanentScript toValue: true]. > guyToTake justGrabbedFromViewer: true. > > ^ evt hand grabMorph: guyToTake! ! > > > !PhraseTileForTimesRepeat methodsFor: 'hilighting' stamp: 'ct 9/11/2020 20:47'! > addCommandFeedback: evt > "Add screen feedback showing what would be torn off in a drag" > > | aMorph | > > (self owner owner isMemberOf: PhraseTileMorph) ifTrue: [self owner owner addCommandFeedback: evt. ^ self]. > aMorph := RectangleMorph new bounds: ((self topLeft - (2 at 1)) corner: (self bottomRight) + (2 at 1)). > aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. > Project current world addHighlightMorph: aMorph for: self outmostScriptEditor! ! > > !PhraseTileForTimesRepeat methodsFor: 'mouse' stamp: 'ct 9/12/2020 14:46'! > mouseDown: evt > "Handle a mouse-down on the receiver" > > | guyToTake catViewer | > guyToTake := TimesRepeatTile new. > guyToTake setNamePropertyTo: 'Repeat Tile' translated. > guyToTake position: evt position + (-25 at 8). > > guyToTake formerPosition: evt hand position. > "self startSteppingSelector: #trackDropZones." > (catViewer := self ownerThatIsA: CategoryViewer) ifNotNil: > [guyToTake setProperty: #newPermanentPlayer toValue: catViewer scriptedPlayer. > guyToTake setProperty: #newPermanentScript toValue: true]. > guyToTake justGrabbedFromViewer: true. > > ^ evt hand grabMorph: guyToTake! ! > > > !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! > adoptScriptsFrom > "Let the user click on another object form which the receiver should obtain scripts and code" > > | aMorph | > Sensor waitNoButton. > aMorph := Project current world chooseClickTarget. > aMorph ifNil: [^ Beeper beep]. > > (aMorph renderedMorph isSketchMorph > and: [aMorph player belongsToUniClass] > and: [self belongsToUniClass not]) > ifTrue: [costume acquirePlayerSimilarTo: aMorph player] > ifFalse: [Beeper beep].! ! > > !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:46'! > beRevealedInActiveWorld > "Reveal my corresponding morph in the active world" > > self revealPlayerIn: Project current world! ! > > !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! > grabPlayerInActiveWorld > "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" > > ^ self grabPlayerIn: Project current world! ! > > !Player methodsFor: 'misc' stamp: 'ct 9/12/2020 14:47'! > grabPlayerIn: aWorld > "Invoked from a Viewer: rip my morph out of its container, wherever that may be, and place it in the hand, being careful to set things up so that if the subsequent drop is rejected, the morph will end up in a visible location on the screen" > > | aMorph newPosition | > self costume == aWorld ifTrue: [^ self]. > self currentHand releaseMouseFocus. > (aMorph := self costume) visible: true. > newPosition := self currentHand position - (aMorph extent // 2). > aMorph isInWorld > ifTrue: > [aMorph goHome. > aMorph formerPosition: aMorph positionInWorld] > ifFalse: > [aMorph formerPosition: aWorld center]. > aMorph formerOwner: Project current world. > aMorph position: newPosition. > > self currentHand > targetOffset: aMorph position - self currentHand position; > addMorphBack: aMorph.! ! > > !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:45'! > impartSketchScripts > "Let the user designate another object to which my scripts and code should be imparted" > > | aMorph | > Sensor waitNoButton. > aMorph := Project current world chooseClickTarget. > aMorph ifNil: [^ self]. > (aMorph renderedMorph isSketchMorph) ifTrue: [ > aMorph acquirePlayerSimilarTo: self].! ! > > !Player methodsFor: 'misc' stamp: 'ct 9/11/2020 20:44'! > offerAlternateViewerMenuFor: aViewer event: evt > "Put up an alternate Viewer menu on behalf of the receiver." > > | menu world | > world := aViewer world. > menu := MenuMorph new defaultTarget: self. > (costumes notNil and: [ > (costumes size > 1 or: [costumes size == 1 and: [costumes first ~~ costume renderedMorph]])]) ifTrue: > [menu add: 'forget other costumes' translated target: self selector: #forgetOtherCostumes]. > > menu add: 'expunge empty scripts' translated target: self action: #expungeEmptyScripts. > menu addLine. > menu > add: 'choose vocabulary...' translated target: aViewer action: #chooseVocabulary; > balloonTextForLastItem: 'Choose a different vocabulary for this Viewer.' translated. > menu > add: 'choose limit class...' translated target: aViewer action: #chooseLimitClass; > balloonTextForLastItem: 'Specify what the limitClass should be for this Viewer -- i.e., the most generic class whose methods and categories should be considered here.' translated. > > menu > add: 'open standard lexicon' translated target: aViewer action: #openLexicon; > balloonTextForLastItem: 'open a window that shows the code for this object in traditional programmer format' translated. > > menu > add: 'open lexicon with search pane' translated target: aViewer action: #openSearchingProtocolBrowser; > balloonTextForLastItem: 'open a lexicon that has a type-in pane for search (not recommended!!)' translated. > > > menu addLine. > menu add: 'inspect morph' translated target: costume selector: #inspect. > menu add: 'inspect player' translated target: self selector: #inspect. > self belongsToUniClass ifTrue: [ > menu add: 'browse class' translated target: self action: #browsePlayerClass. > menu add: 'inspect class' translated target: self class action: #inspect]. > menu add: 'inspect this Viewer' translated target: aViewer selector: #inspect. > menu add: 'inspect this Vocabulary' translated target: aViewer currentVocabulary selector: #inspect. > > menu addLine. > menu add: 'relaunch this Viewer' translated target: aViewer action: #relaunchViewer. > menu add: 'attempt repairs' translated target: Project current world action: #attemptCleanup. > menu add: 'destroy all this object''s scripts' translated target: self action: #destroyAllScripts. > menu add: 'view morph directly' translated target: aViewer action: #viewMorphDirectly. > menu balloonTextForLastItem: 'opens a Viewer directly on the rendered morph.' translated. > costume renderedMorph isSketchMorph ifTrue: [ > menu addLine. > menu add: 'impart scripts to...' translated target: self action: #impartSketchScripts]. > > ^ menu popUpEvent: evt in: world! ! > > !Player methodsFor: 'scripts-standard' stamp: 'ct 9/12/2020 14:48'! > hide > "Make the object be hidden, as opposed to visible" > > self currentHand ifNotNil: [:hand | > (hand keyboardFocus == self costume renderedMorph) ifTrue: [ > hand releaseKeyboardFocus]]. > self costume hide.! ! > > !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:45'! > getLastKeystroke > "Answer the last keystroke fielded" > > ^ Project current world lastKeystroke! ! > > !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/11/2020 20:41'! > setLastKeystroke: aString > "Set the last keystroke fielded" > > ^ self currentWorld lastKeystroke: aString! ! > > !Player methodsFor: 'slot getters/setters' stamp: 'ct 9/12/2020 14:49'! > setSecondColor: aColor > "Setter for costume's second color, if it's using gradient fill; if not, does nothing" > > | morph fillStyle colorToUse | > morph := costume renderedMorph. > fillStyle := morph fillStyle. > fillStyle isGradientFill ifFalse: [^ self]. > > colorToUse := (costume isWorldMorph and: [aColor isColor]) > ifTrue: [aColor alpha: 1.0] "reject any translucency" > ifFalse: [aColor]. > fillStyle lastColor: colorToUse forMorph: morph hand: self currentHand.! ! > > !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:47'! > addInstanceVariable > "Offer the user the opportunity to add an instance variable, and if he goes through with it, actually add it." > > Project current world > addMorphInLayer: (NewVariableDialogMorph on: self costume) > centeredNear: (self currentHand ifNil:[Sensor]) cursorPoint! ! > > !Player methodsFor: 'slots-user' stamp: 'ct 9/11/2020 20:46'! > allPossibleWatchersFromWorld > "Answer a list of all UpdatingStringMorphs, PlayerReferenceReadouts, ThumbnailMorphs, and UpdatingReferenceMorphs in the Active world and its hidden book pages, etc., which have me or any of my siblings as targets" > > | a | > a := IdentitySet new: 400. > Project current world allMorphsAndBookPagesInto: a. > ^ a select: [:e | e isEtoyReadout and: [e target class == self class]]! ! > > !Player methodsFor: 'slots-user' stamp: 'ct 9/12/2020 14:48'! > offerGetterTiles: slotName > "For a player-type slot, offer to build convenient compound tiles that otherwise would be hard to get" > > | typeChoices typeChosen thePlayerThereNow slotChoices slotChosen getterTiles aCategoryViewer playerGetter | > typeChoices := Vocabulary typeChoices. > typeChosen := UIManager default > chooseFrom: (typeChoices collect: [:t | t translated]) > values: typeChoices > title: ('Choose the TYPE > of data to get from > {1}''s {2}' translated format: {self externalName. slotName translated}). > typeChosen isEmptyOrNil ifTrue: [^self]. > thePlayerThereNow := self perform: slotName asGetterSelector. > thePlayerThereNow > ifNil: [thePlayerThereNow := self presenter standardPlayer]. > slotChoices := thePlayerThereNow slotNamesOfType: typeChosen. > slotChoices isEmpty > ifTrue: [^self inform: 'sorry -- no slots of that type' translated]. > slotChoices sort. > slotChosen := UIManager default > chooseFrom: (slotChoices collect: [:t | t translated]) > values: slotChoices > title: ('Choose the datum > you want to extract from {1}''s {2}' translated format: {self externalName. slotName translated}). > slotChosen isEmptyOrNil ifTrue: [^self]. > "Now we want to tear off tiles of the form > holder's valueAtCursor's foo" > getterTiles := nil. > aCategoryViewer := CategoryViewer new initializeFor: thePlayerThereNow > categoryChoice: 'basic'. > getterTiles := aCategoryViewer > getterTilesFor: slotChosen asGetterSelector > type: typeChosen. > aCategoryViewer := CategoryViewer new initializeFor: self > categoryChoice: 'basic'. > playerGetter := aCategoryViewer > getterTilesFor: slotName asGetterSelector > type: #Player. > getterTiles submorphs first acceptDroppingMorph: playerGetter event: nil. "the pad" "simulate a drop" > getterTiles makeAllTilesGreen. > getterTiles justGrabbedFromViewer: false. > (getterTiles firstSubmorph) > changeTableLayout; > hResizing: #shrinkWrap; > vResizing: #spaceFill. > self currentHand attachMorph: getterTiles.! ! > > !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:46'! > addPatchVarNamed: nameSymbol > > | f | > f := KedamaPatchMorph newExtent: self costume dimensions. > f assuredPlayer assureUniClass. > f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). > self addInstanceVariable2Named: nameSymbol type: #Patch value: f player. > ^ f! ! > > !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! > newPatch > > | f usedNames newName | > f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. > f assuredPlayer assureUniClass. > f kedamaWorld: self costume renderedMorph. > usedNames := Project current world allKnownNames, self class instVarNames. > newName := Utilities keyLike: f innocuousName satisfying: > [:aName | (usedNames includes: aName) not]. > f setNameTo: newName. > self createSlotForPatch: f. > self addToPatchDisplayList: f assuredPlayer. > self costume world primaryHand attachMorph: f. > ^ f! ! > > !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! > newTurtle > > | m | > m := KedamaTurtleMorph new openInWorld. > self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. > self useTurtle: m player. > m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). > self costume world primaryHand attachMorph: m. > ^ m! ! > > !Player methodsFor: 'slot-kedama' stamp: 'ct 9/11/2020 20:44'! > newTurtleSilently > > | m | > m := KedamaTurtleMorph new openInWorld. > self useTurtle: m player. > m turtleCount: 0. > m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). > ^ m! ! > > !Player methodsFor: '*Etoys-Squeakland-scripts-standard' stamp: 'ct 9/11/2020 20:41'! > printInTranscript > "Print a line representing the receiver in the Transcript" > > Project current world findATranscript: nil. > Transcript cr; > show: (Time now printString copyWithoutAll: '()'); > space; > show: self costume printString.! ! > > !Player methodsFor: '*Etoys-Squeakland-slots-user' stamp: 'ct 9/12/2020 14:47'! > changeSlotInfo: aSymbol > > Project current world > addMorphInLayer: (ModifyVariableDialogMorph on: self costume slot: aSymbol) > centeredNear: (self currentHand ifNil: [Sensor]) cursorPoint.! ! > > !Player methodsFor: '*Etoys-Squeakland-slot getters/setters' stamp: 'ct 9/12/2020 14:47'! > handUserPictureOfPenTrail > "Called from the user-interface: hand the user a picture of the pen trail" > > self getHasPenTrails ifFalse: [ > ^ self inform: 'no pen trails present' translated]. > > self currentHand attachMorph: (SketchMorph new form: self getPenTrailGraphic).! ! > > !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! > kedamaWorld > > ^ Project current world findDeeplyA: KedamaMorph > ! ! > > !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! > newPatchForSet > > | f | > f := KedamaPatchMorph newExtent: self costume renderedMorph dimensions. > f assuredPlayer assureUniClass. > f setNameTo: (Project current world unusedMorphNameLike: f innocuousName). > f kedamaWorld: self costume renderedMorph. > self createSlotForPatch: f. > ^ f! ! > > !Player methodsFor: '*Etoys-Squeakland-slot-kedama' stamp: 'ct 9/11/2020 20:44'! > newTurtleForSet > > | m | > m := KedamaTurtleMorph new openInWorld. > self costume renderedMorph hasNoTurtleBreed ifTrue: [m color: Color red]. > self useTurtle: m player. > m setNameTo: (Project current world unusedMorphNameLike: m innocuousName). > ^ m! ! > > > !PlayerSurrogate methodsFor: 'menu' stamp: 'ct 9/11/2020 20:37'! > revealThisObject > "Reveal the object I represent" > > playerRepresented revealPlayerIn: Project current world! ! > > !PlayerSurrogate methodsFor: '*Etoys-Squeakland-as yet unclassified' stamp: 'ct 9/11/2020 20:40'! > forciblyRenamePlayer > "Allow the receiver to seize a name already nominally in use in the project." > > | current reply currentlyBearingName newNameForHim binding | > current := playerRepresented knownName. > reply := FillInTheBlank request: 'Type the name you insist upon' translated initialAnswer: current. > reply isEmptyOrNil ifTrue: [^ self]. > Preferences uniquePlayerNames ifFalse: [^ self costume renameTo: reply]. > reply := (reply asIdentifier: true) asSymbol. > reply = current ifTrue: [^ self inform: 'no change' translated]. > > binding := Project current world referencePool hasBindingOf: reply. > binding ifNotNil: [ > currentlyBearingName := binding value. > newNameForHim := Utilities keyLike: reply satisfying: [:name | > (Project current world referencePool includesKey: name) not]. > currentlyBearingName renameTo: newNameForHim]. > playerRepresented renameTo: reply. > self inform: (binding > ifNil: [('There was no conflict; this object is now named {1}' translated format: {reply})] > ifNotNil: ['Okay, this object is now named\{1}\and the object formerly known by this name is now called\{2}' translated format: {reply. newNameForHim}]).! ! > > > !PlayerType methodsFor: 'tiles' stamp: 'ct 9/11/2020 20:37'! > defaultArgumentTile > "Answer a tile to represent the type" > > ^ Project current world presenter standardPlayer tileToRefer! ! > > > !KedamaPatchType methodsFor: 'tile protocol' stamp: 'ct 9/11/2020 20:15'! > defaultArgumentTile > "Answer a tile to represent the type" > | patch ks k p | > patch := KedamaPatchTile new typeColor: self typeColor. > ks := self world allMorphs select: [:e | e isKindOf: KedamaMorph]. > ks isEmpty ifFalse: [ > k := ks first. > p := k player getPatch. > ] ifTrue: [ > k := KedamaPatchMorph new. > k assuredPlayer. > p := k player. > ]. > patch usePatch: p. > ^ patch! ! > > > !PluggableFileList methodsFor: 'StandardFileMenu' stamp: 'ct 9/12/2020 14:50'! > startUpWithCaption: captionOrNil > "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." > > ^ self > startUpWithCaption: captionOrNil > at: (self currentHand ifNil: [Sensor]) cursorPoint! ! > > > !PluggableListMorph methodsFor: 'model access - keystroke' stamp: 'ct 9/11/2020 18:01'! > specialKeyPressed: asciiValue > "A special key with the given ascii-value was pressed; dispatch it" > | oldSelection nextSelection max howManyItemsShowing | > (#(8 13) includes: asciiValue) ifTrue: > [ "backspace key - clear the filter, restore the list with the selection" > model okToChange ifFalse: [^ self]. > self removeFilter. > priorSelection ifNotNil: > [ | prior | > prior := priorSelection. > priorSelection := self getCurrentSelectionIndex. > asciiValue = 8 ifTrue: [ self changeModelSelection: prior ] ]. > ^ self ]. > asciiValue = 27 ifTrue: > [" escape key" > ^ self currentEvent shiftPressed > ifTrue: > [self currentEvent putUpWorldMenuFromEscapeKey] > ifFalse: > [self yellowButtonActivity: false]]. > > max := self maximumSelection. > max > 0 ifFalse: [^ self]. > nextSelection := oldSelection := self selectionIndex. > asciiValue = 31 ifTrue: > [" down arrow" > nextSelection := oldSelection + 1. > nextSelection > max ifTrue: [nextSelection := 1]]. > asciiValue = 30 ifTrue: > [" up arrow" > nextSelection := oldSelection - 1. > nextSelection < 1 ifTrue: [nextSelection := max]]. > asciiValue = 1 ifTrue: > [" home" > nextSelection := 1]. > asciiValue = 4 ifTrue: > [" end" > nextSelection := max]. > howManyItemsShowing := self numSelectionsInView. > asciiValue = 11 ifTrue: > [" page up" > nextSelection := 1 max: oldSelection - howManyItemsShowing]. > asciiValue = 12 ifTrue: > [" page down" > nextSelection := oldSelection + howManyItemsShowing min: max]. > model okToChange ifFalse: [^ self]. > "No change if model is locked" > oldSelection = nextSelection ifTrue: [^ self flash]. > ^ self changeModelSelection: (self modelIndexFor: nextSelection)! ! > > > !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:50'! > startUpCenteredWithCaption: captionOrNil > "Differs from startUpWithCaption: by appearing with cursor in the menu, and thus ready to act on mouseUp, without requiring user tweak to confirm" > > ^ self > startUpWithCaption: captionOrNil > at: (self currentHand ifNil: [Sensor]) cursorPoint - (20 @ 0)! ! > > !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! > startUpWithCaption: captionOrNil > "Display the menu, slightly offset from the cursor, > so that a slight tweak is required to confirm any action." > self flag: #fix. "mt: Could we manage to open pop-up menus in Morphic without accessing self currentHand?" > > ^ self > startUpWithCaption: captionOrNil > at: (self currentHand ifNil: [Sensor]) cursorPoint! ! > > !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! > startUpWithCaption: captionOrNil icon: aForm > "Display the menu, slightly offset from the cursor, so that a slight tweak is required to confirm any action." > > ^ self > startUpWithCaption: captionOrNil > icon: aForm > at: (self currentHand ifNil: [Sensor]) cursorPoint! ! > > !PopUpMenu methodsFor: 'basic control sequence' stamp: 'ct 9/12/2020 14:51'! > startUpWithoutKeyboard > "Display and make a selection from the receiver as long as the button is pressed. Answer the current selection. Do not allow keyboard input into the menu" > > ^ self > startUpWithCaption: nil > at: ((self currentHand ifNil: [Sensor]) cursorPoint) > allowKeyboard: false! ! > > !PopUpMenu methodsFor: '*Morphic-Menus' stamp: 'ct 9/11/2020 20:37'! > morphicStartUpWithCaption: captionOrNil icon: aForm at: location allowKeyboard: aBoolean > "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, > Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard." > > selection := Cursor normal > showWhile: [| menuMorph | > menuMorph := MVCMenuMorph from: self title: nil. > (captionOrNil notNil > or: [aForm notNil]) > ifTrue: [menuMorph addTitle: captionOrNil icon: aForm]. > MenuIcons decorateMenu: menuMorph. > menuMorph > invokeAt: location > in: self currentWorld > allowKeyboard: aBoolean]. > ^ selection! ! > > !PopUpMenu methodsFor: '*Etoys-Squeakland-basic control sequence' stamp: 'ct 9/11/2020 20:37'! > startUpWithCaption: captionOrNil at: location allowKeyboard: allowKeyboard centered: centered > "Display the menu, with caption if supplied. Wait for the mouse button to go down, then track the selection as long as the button is pressed. When the button is released, > Answer the index of the current selection, or zero if the mouse is not released over any menu item. Location specifies the desired topLeft of the menu body rectangle. The final argument indicates whether the menu should seize the keyboard focus in order to allow the user to navigate it via the keyboard > If centered is true, the menu items are displayed centered.." > > | maxHeight aMenu | > (ProvideAnswerNotification signal: captionOrNil) ifNotNil: > [:answer | ^ selection := answer ifTrue: [1] ifFalse: [2]]. > > maxHeight := Display height*3//4. > self frameHeight > maxHeight ifTrue: > [^ self > startUpSegmented: maxHeight > withCaption: captionOrNil > at: location > allowKeyboard: allowKeyboard]. > > Smalltalk isMorphic > ifTrue:[ > selection := Cursor normal showWhile: > [aMenu := MVCMenuMorph from: self title: captionOrNil. > centered ifTrue: > [aMenu submorphs allButFirst do: > [:m | m setProperty: #centered toValue: true]]. > aMenu > invokeAt: location > in: self currentWorld > allowKeyboard: allowKeyboard]. > ^ selection]. > > frame ifNil: [self computeForm]. > Cursor normal showWhile: > [self > displayAt: location > withCaption: captionOrNil > during: [self controlActivity]]. > ^ selection! ! > > > !PopUpMenu class methodsFor: '*Etoys-Squeakland-dialogs' stamp: 'ct 9/12/2020 14:52'! > informCenteredAboveCursor: aString > "Put up an informer showing the given string in a box, with the OK button for dismissing the informer having the cursor at its center." > > "PopUpMenu informCenteredAboveCursor: 'I like Squeak\how about you?' withCRs" > > | lines maxWid xCoor | > lines := Array streamContents: [:aStream | > aString linesDo: [:l | aStream nextPut: l]]. > maxWid := (lines collect: [:l | Preferences standardMenuFont widthOfString: l]) max. > xCoor := self currentHand cursorPoint x - (maxWid // 2). > ((xCoor + maxWid) > self currentWorld right) ifTrue: > [xCoor := self currentWorld right]. "Caters to problematic PopUpMenu boundary behavior" > > (PopUpMenu labels: 'OK' translated) startUpWithCaption: aString > at: (xCoor @ self currentHand cursorPoint y) > allowKeyboard: true centered: true.! ! > > > !PreferenceWizardMorph methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:35'! > initializePreviewWorld > > | w1 w2 w3 | > > previewWorld := PasteUpMorph new > hResizing: #spaceFill; > vResizing: #spaceFill; > viewBox: (0 at 0 corner: 500 at 500); > layoutFrame: (LayoutFrame fractions: (0.3 @ 0 corner: 1.0 @ 1.0) offsets: (0@ titleMorph height corner: 0 @ buttonRowMorph height negated)); > fillStyle: Project current world fillStyle; > borderWidth: 2; > borderColor: Color white; > cornerStyle: (self hasLowPerformance ifTrue: [#square] ifFalse: [#rounded]); > yourself. > > w1 := (ToolSet browse: Morph selector: #drawOn:) dependents detect: [:ea | ea isSystemWindow]. > w2 := ToolSet browseMessageSet: (SystemNavigation default allCallsOn: #negated) name: 'Senders' translated autoSelect: 'negated'. > w3 := (Workspace new contents: '3+4 "Select and hit [CMD]+[P]."') openLabel: 'Workspace'. > > {w1. w2. w3} do: [:ea | > ea makeUnclosable. > previewWorld addMorph: ea]. > > self updateWindowBounds.! ! > > !PreferenceWizardMorph methodsFor: 'support' stamp: 'ct 9/12/2020 14:36'! > adjustSettingsForLowPerformance > > self updateLowPerformanceLabel: 'Please wait, optimizing performance...' translated. > self refreshWorld. > > self stateGradients "flat look" ifFalse: [self toggleGradients]. > self stateBlinkingCursor ifTrue: [self toggleBlinkingCursor]. > self stateFastDrag ifFalse: [self toggleFastDrag]. > > self stateSoftShadows ifTrue: [self toggleSoftShadows]. > self stateHardShadows ifTrue: [self toggleHardShadows]. > > self stateRoundedWindowLook ifTrue: [self toggleRoundedWindowLook]. > self stateRoundedButtonLook ifTrue: [self toggleRoundedButtonLook]. > > self stateAttachToolsToMouse ifTrue: [self toggleAttachToolsToMouse]. > self stateToolAndMenuIcons ifTrue: [self toggleToolAndMenuIcons]. > > self stateSmartHorizontalSplitters ifTrue: [self toggleSmartHorizontalSplitters]. > self stateSmartVerticalSplitters ifTrue: [self toggleSmartVerticalSplitters]. > > PluggableListMorph > highlightHoveredRow: false; > filterableLists: false; > highlightPreSelection: true; "Feedback is important!!" > flashOnErrors: false. > TheWorldMainDockingBar showSecondsInClock: false. > Preferences disable: #balloonHelpInMessageLists. > > > "Set simple background." > Project current world setAsBackground: MorphicProject defaultFill. > previewWorld fillStyle: Project current world fillStyle. > > "Done." > self updateLowPerformanceLabel: 'Settings were adjusted for optimal performance.' translated.! ! > > > !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! > roundedWindowCornersChanged > "The user changed the value of the roundedWindowCorners preference. React" > > Project current world fullRepaintNeeded.! ! > > !Preferences class methodsFor: 'updating - system' stamp: 'ct 9/11/2020 20:35'! > vectorVocabularySettingChanged > "The current value of the useVectorVocabulary flag has changed; now react. No senders, but invoked by the Preference object associated with the #useVectorVocabulary preference." > > Smalltalk isMorphic ifFalse: [^ self]. > Project current world makeVectorUseConformToPreference.! ! > > > !Project methodsFor: '*Etoys-Squeakland-file in/out' stamp: 'ct 9/16/2020 19:18'! > storeOnServerWithNoInteractionInnards > "Save to disk as an Export Segment. Then put that file on the server I came from, as a new version. Version is literal piece of file name. Mime encoded and http encoded." > > | newName primaryServerDirectory serverVersionPair localDirectory localVersionPair myVersionNumber warning maxNumber myDepth | > self assureIntegerVersion. > > "Find out what version" > primaryServerDirectory := self defaultFolderForAutoSaving ifNil: [^self]. > > localDirectory := self squeakletDirectory. > serverVersionPair := self class mostRecent: self name onServer: primaryServerDirectory. > localVersionPair := self class mostRecent: self name onServer: localDirectory. > maxNumber := myVersionNumber := self currentVersionNumber. > > ProgressNotification signal: '2:versionsDetected'. > > warning := ''. > myVersionNumber < serverVersionPair second ifTrue: [ > warning := warning,'\There are newer version(s) on the server' translated. > maxNumber := maxNumber max: serverVersionPair second. > ]. > myVersionNumber < localVersionPair second ifTrue: [ > warning := warning,'\There are newer version(s) in the local directory' translated. > maxNumber := maxNumber max: localVersionPair second. > ]. > version := self bumpVersion: maxNumber. > > "write locally - now zipped automatically" > Display isVirtualScreen ifTrue: [ > myDepth := displayDepth. > displayDepth := OLPCVirtualScreen preferredScreenDepth.. > ]. > newName := self versionedFileName. > lastSavedAtSeconds := Time totalSeconds. > self exportSegmentFileName: newName directory: localDirectory withoutInteraction: true. > (localDirectory readOnlyFileNamed: newName) setFileTypeToObject; close. > Display isVirtualScreen ifTrue: [ > displayDepth := myDepth. > ]. > > ProgressNotification signal: '4:localSaveComplete'. "3 is deep in export logic" > > primaryServerDirectory ifNotNil: [ > [ > primaryServerDirectory > writeProject: self > inFileNamed: newName asFileName > fromDirectory: localDirectory. > ] on: ProjectPasswordNotification do: [ :ex | > ex resume: '' > ]. > ]. > ProgressNotification signal: '9999 save complete'.! ! > > !Project methodsFor: '*Etoys-Squeakland-language' stamp: 'ct 9/11/2020 20:34'! > updateLocaleDependentsWithPreviousSupplies: aCollection gently: gentlyFlag > "Set the project's natural language as indicated" > > | morphs scriptEditors | > gentlyFlag ifTrue: [ > LanguageEnvironment localeChangedGently. > ] ifFalse: [ > LanguageEnvironment localeChanged. > ]. > > morphs := IdentitySet new: 400. > Project current world allMorphsAndBookPagesInto: morphs. > scriptEditors := morphs select: [:m | (m isKindOf: ScriptEditorMorph) and: [m topEditor == m]]. > (morphs copyWithoutAll: scriptEditors) do: [:morph | morph localeChanged]. > scriptEditors do: [:m | m localeChanged]. > > Flaps disableGlobalFlaps: false. > SugarNavigatorBar showSugarNavigator > ifTrue: > [Flaps addAndEnableEToyFlapsWithPreviousEntries: aCollection. > Project current world addGlobalFlaps] > ifFalse: > [Preferences eToyFriendly > ifTrue: > [Flaps addAndEnableEToyFlaps. > Project current world addGlobalFlaps] > ifFalse: > [Flaps enableGlobalFlaps]]. > > (Project current isFlapIDEnabled: 'Navigator' translated) > ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. > > ParagraphEditor initializeTextEditorMenus. > MenuIcons initializeTranslations. > > #(PartsBin ParagraphEditor BitEditor FormEditor StandardSystemController) > do: [ :key | Smalltalk at: key ifPresent: [ :class | class initialize ]]. > > Project current world reformulateUpdatingMenus. > "self setFlaps. > self setPaletteFor: aLanguageSymbol." > ! ! > > > !MorphicProject methodsFor: 'utilities' stamp: 'ct 9/12/2020 15:13'! > createViewIfAppropriate > "Create a project view for the receiver and place it appropriately on the screen." > > | aMorph requiredWidth existing proposedV proposedH despair | > ProjectViewOpenNotification signal ifTrue: > [Preferences projectViewsInWindows > ifTrue: > [(ProjectViewMorph newProjectViewInAWindowFor: self) openInWorld] > ifFalse: > [aMorph := ProjectViewMorph on: self. > requiredWidth := aMorph width + 10. > existing := self currentWorld submorphs > select: [:m | m isKindOf: ProjectViewMorph] > thenCollect: [:m | m fullBoundsInWorld]. > proposedV := 85. > proposedH := 10. > despair := false. > [despair not and: [((proposedH @ proposedV) extent: requiredWidth) intersectsAny: existing]] whileTrue: > [proposedH := proposedH + requiredWidth. > proposedH + requiredWidth > self currentWorld right ifTrue: > [proposedH := 10. > proposedV := proposedV + 90. > proposedV > (self currentWorld bottom - 90) > ifTrue: > [proposedH := self currentWorld center x - 45. > proposedV := self currentWorld center y - 30. > despair := true]]]. > aMorph position: (proposedH @ proposedV). > aMorph openInWorld]]! ! > > !MorphicProject methodsFor: 'flaps support' stamp: 'ct 9/12/2020 14:34'! > setFlaps > > | flapTabs flapIDs sharedFlapTabs navigationMorph | > self flag: #toRemove. "check if this method still used by Etoys" > > flapTabs := self world flapTabs. > flapIDs := flapTabs collect: [:tab | tab knownName]. > flapTabs > do: [:tab | (tab isMemberOf: ViewerFlapTab) > ifFalse: [tab isGlobalFlap > ifTrue: [Flaps removeFlapTab: tab keepInList: false. > tab currentWorld reformulateUpdatingMenus] > ifFalse: [| referent | > referent := tab referent. > referent isInWorld > ifTrue: [referent delete]. > tab delete]]]. > sharedFlapTabs := Flaps classPool at: #SharedFlapTabs. > flapIDs > do: [:id | > id = 'Navigator' translated > ifTrue: [sharedFlapTabs add: Flaps newNavigatorFlap]. > id = 'Widgets' translated > ifTrue: [sharedFlapTabs add: Flaps newWidgetsFlap]. > id = 'Tools' translated > ifTrue: [sharedFlapTabs add: Flaps newToolsFlap]. > id = 'Squeak' translated > ifTrue: [sharedFlapTabs add: Flaps newSqueakFlap]. > id = 'Supplies' translated > ifTrue: [sharedFlapTabs add: Flaps newSuppliesFlap]. > id = 'Stack Tools' translated > ifTrue: [sharedFlapTabs add: Flaps newStackToolsFlap]. > id = 'Painting' translated > ifTrue: [sharedFlapTabs add: Flaps newPaintingFlap]. > id = 'Objects' translated > ifTrue: [sharedFlapTabs add: Flaps newObjectsFlap ]]. > 2 timesRepeat: [flapIDs do: [:id | Flaps enableDisableGlobalFlapWithID: id]]. > self world flapTabs > do: [:flapTab | flapTab isCurrentlyTextual > ifTrue: [flapTab changeTabText: flapTab knownName]]. > Flaps positionNavigatorAndOtherFlapsAccordingToPreference. > navigationMorph := self currentWorld findDeeplyA: ProjectNavigationMorph preferredNavigator. > navigationMorph isNil > ifTrue: [^ self]. > navigationMorph allMorphs > do: [:morph | morph class == SimpleButtonDelayedMenuMorph > ifTrue: [(morph findA: ImageMorph) isNil > ifTrue: [| label | > label := morph label. > label isNil > ifFalse: [| name | > name := morph knownName. > name isNil > ifTrue: [morph name: label. > name := label]. > morph label: name translated]]]]! ! > > !MorphicProject methodsFor: 'enter' stamp: 'ct 9/16/2020 19:45'! > clearGlobalState > "Clean up global state. This method may be removed if the use of global state variables is eliminated." > > "If global World is defined, clear it now. The value is expected to be set again as a new project is entered." > Smalltalk globals at: #World ifPresent: [:w | > Smalltalk globals at: #World put: nil].! ! > > !MorphicProject methodsFor: 'enter' stamp: 'ct 9/12/2020 14:45'! > wakeUpTopWindow > "Image has been restarted, and the startUp list has been processed. Perform > any additional actions needed to restart the user interface." > > SystemWindow wakeUpTopWindowUponStartup. > Preferences mouseOverForKeyboardFocus ifTrue: > [ "Allow global command keys to work upon re-entry without having to cause a focus change first." > self currentHand releaseKeyboardFocus ]! ! > > !MorphicProject methodsFor: 'language' stamp: 'ct 9/12/2020 14:17'! > updateLocaleDependents > "Set the project's natural language as indicated" > > (self world respondsTo: #isTileScriptingElement) ifTrue: "Etoys present" [ > self world allTileScriptingElements do: [:viewerOrScriptor | > viewerOrScriptor localeChanged]]. > > Flaps disableGlobalFlaps: false. > (Preferences eToyFriendly or: [ > (Smalltalk classNamed: #SugarNavigatorBar) ifNotNil: [:c | c showSugarNavigator] ifNil: [false]]) > ifTrue: [ > Flaps addAndEnableEToyFlaps. > self world addGlobalFlaps] > ifFalse: [Flaps enableGlobalFlaps]. > > (self isFlapIDEnabled: 'Navigator' translated) > ifFalse: [Flaps enableDisableGlobalFlapWithID: 'Navigator' translated]. > > ScrapBook default emptyScrapBook. > MenuIcons initializeTranslations. > > super updateLocaleDependents. > > "self setFlaps. > self setPaletteFor: aLanguageSymbol."! ! > > !MorphicProject methodsFor: 'protocols' stamp: 'ct 9/12/2020 14:18'! > currentVocabulary > > ^ self world currentVocabulary! ! > > !MorphicProject methodsFor: 'scheduling & debugging' stamp: 'ct 9/16/2020 19:45'! > interruptCleanUpFor: interruptedProcess > "Clean up things in case of a process interrupt." > > super interruptCleanUpFor: interruptedProcess. > > self uiProcess == interruptedProcess ifTrue: [ > self currentHand ifNotNil: [:hand | hand interrupted]. > > Preferences eToyFriendly ifTrue: [ > Project current world stopRunningAll]].! ! > > > !MorphicProject class methodsFor: 'shrinking' stamp: 'ct 9/12/2020 15:45'! > unloadMorphic > "MorphicProject unloadMorphic" > > Project current isMorphic ifTrue: [ > ^ Error signal: 'You can only unload Morphic from within another kind of project.' translated]. > > MorphicProject removeProjectsFromSystem. > > #(ActiveEvent ActiveHand ActiveWorld World) do: [:ea | > Smalltalk globals removeKey: ea]. > Processor allInstancesDo: [:process | > #(ActiveHand ActiveEvent ActiveWorld) do: [:ea | > process environmentRemoveKey: ea ifAbsent: []]]. > > { 'ToolBuilder-Morphic' . 'MorphicTests' . 'MorphicExtras' . 'Morphic' } > do: [ :package | (MCPackage named: package) unload ].! ! > > > !ProjectNavigationMorph methodsFor: 'stepping and presenter' stamp: 'ct 9/11/2020 20:34'! > undoButtonWording > "Answer the wording for the Undo button." > > | wdng | > wdng := Project current world commandHistory undoOrRedoMenuWording. > (wdng endsWith: ' (z)') ifTrue: [ > wdng := wdng copyFrom: 1to: wdng size - 4]. > ^ wdng! ! > > !ProjectNavigationMorph methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:34'! > undoOrRedoLastCommand > "Undo or redo the last command, as approrpiate." > > ^ Project current world commandHistory undoOrRedoCommand! ! > > > !EventRecordingSpaceNavigator methodsFor: 'the actions' stamp: 'ct 9/11/2020 19:47'! > doNewPainting > "Make a new painting" > > | worldlet | > self currentWorld assureNotPaintingElse: [^ self]. > worldlet := self ownerThatIsA: Worldlet. > worldlet closeNavigatorFlap. > worldlet makeNewDrawing: (self currentEvent copy setPosition: worldlet center).! ! > > > !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/11/2020 20:34'! > maximumUsableArea > > ^ self maximumUsableAreaInWorld: Project current world! ! > > !RealEstateAgent class methodsFor: 'accessing' stamp: 'ct 9/16/2020 20:41'! > maximumUsableAreaInWorld: aWorldOrNil > > | allowedArea | > allowedArea := Display usableArea. > aWorldOrNil ifNotNil: [ > Smalltalk isMorphic ifTrue: [ > allowedArea := allowedArea intersect: aWorldOrNil visibleClearArea. > (((Smalltalk classNamed: 'Flaps') ifNil: [false] ifNotNil: [:cls | cls anyFlapsVisibleIn: aWorldOrNil]) > and: [self respondsTo: #reduceByFlaps:]) > ifTrue: [allowedArea := self reduceByFlaps: allowedArea]]]. > ^allowedArea! ! > > > !RecordingControls methodsFor: 'private' stamp: 'ct 9/12/2020 14:52'! > makeSoundMorph > "Hand the user an anonymous-sound object representing the receiver's sound." > > | m aName | > recorder verifyExistenceOfRecordedSound ifFalse: [^ self]. > recorder pause. > recordingSaved := true. > m := AnonymousSoundMorph new. > > m sound: recorder recordedSound interimName: (aName := 'Unnamed Sound'). > > m setNameTo: aName. > self currentHand attachMorph: m.! ! > > > !ReleaseBuilder class methodsFor: 'scripts - support' stamp: 'ct 9/11/2020 20:33'! > setProjectBackground: aFormOrColorOrFillStyle > > | world | > world := Project current world. > world fillStyle: aFormOrColorOrFillStyle. > MorphicProject defaultFill: world fillStyle. > world removeProperty: #hasCustomBackground.! ! > > > !SARInstaller methodsFor: 'client services' stamp: 'ct 9/11/2020 20:33'! > fileInMorphsNamed: memberName addToWorld: aBoolean > "This will load the Morph (or Morphs) from the given member. > Answers a Morph, or a list of Morphs, or nil if no such member or error. > If aBoolean is true, also adds them and their models to the World." > > | member morphOrList | > member := self memberNamed: memberName. > member ifNil: [^ self errorNoSuchMember: memberName]. > self installed: member. > > morphOrList := member contentStream fileInObjectAndCode. > morphOrList ifNil: [^ nil]. > aBoolean ifTrue: [Project current world addMorphsAndModel: morphOrList]. > > ^ morphOrList! ! > > > !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/12/2020 14:52'! > addYesNoToHand > "Place a test/yes/no complex in the hand of the beloved user" > > | ms messageNodeMorph aMorph | > Preferences universalTiles > ifTrue: > [ms := MessageSend receiver: true selector: #ifTrue:ifFalse: > arguments: {['do nothing']. ['do nothing']}. > messageNodeMorph := ms asTilesIn: playerScripted class globalNames: true. > self primaryHand attachMorph: messageNodeMorph] > ifFalse: > [aMorph := CompoundTileMorph new. > self currentHand attachMorph: aMorph. > aMorph setNamePropertyTo: 'TestTile' translated. > aMorph position: self currentHand position. > aMorph formerPosition: self currentHand position. > self startSteppingSelector: #trackDropZones].! ! > > !ScriptEditorMorph methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:32'! > dismiss > "Dismiss the scriptor, usually nondestructively. Possibly animate the dismissal." > > | endPoint aForm startPoint topRend | > owner ifNil: [^ self]. > scriptName ifNil: [^ self delete]. "ad hoc fixup for bkwrd compat" > > endPoint := self viewerTile ifNotNilDo: [:tile | tile topLeft] ifNil: [owner topRight]. > aForm := (topRend := self topRendererOrSelf) imageForm offset: (0 at 0). > handWithTile := nil. > startPoint := topRend topLeft. > topRend topRendererOrSelf delete. > (playerScripted isExpendableScript: scriptName) ifTrue: [ > ^ playerScripted removeScript: scriptName fromWorld: Project current world]. > > Project current world displayWorld. > aForm slideFrom: startPoint to: endPoint nSteps: 4 delay: 30. > "The OLPC Virtual Screen wouldn't notice the last update here." > Display forceToScreen: (endPoint extent: aForm extent).! ! > > !ScriptEditorMorph methodsFor: 'other' stamp: 'ct 9/12/2020 14:52'! > offerScriptorMenu > "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer" > > | aMenu count | > > self modernize. > self currentHand showTemporaryCursor: nil. > > Preferences eToyFriendly ifTrue: [^ self offerSimplerScriptorMenu]. > > aMenu := MenuMorph new defaultTarget: self. > aMenu addTitle: scriptName asString. > aMenu addStayUpItem. "NB: the kids version in #offerSimplerScriptorMenu does not deploy the stay-up item" > > aMenu addList: (self hasParameter > ifTrue: [{ > {'remove parameter' translated. #ceaseHavingAParameter}}] > ifFalse: [{ > {'add parameter' translated. #addParameter}}]). > > self hasParameter ifFalse: > [aMenu addTranslatedList: { > {'button to fire this script' translatedNoop. #tearOfButtonToFireScript}. > {'fires per tick...' translatedNoop. #chooseFrequency}. > #- > }]. > > aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. > aMenu addLine. > aMenu addList: { > {'edit balloon help for this script' translated. #editMethodDescription}. > {'explain status alternatives' translated. #explainStatusAlternatives}. > {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. > #- > }. > > > Preferences universalTiles ifFalse: > [count := self savedTileVersionsCount. > self showingMethodPane > ifFalse: "currently showing tiles" > [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. > count > 0 ifTrue: > [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. > aMenu add: 'save this version' translated action: #saveScriptVersion] > > ifTrue: "current showing textual source" > [count >= 1 ifTrue: > [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. > > "aMenu addLine. > self addGoldBoxItemsTo: aMenu." > > aMenu addLine. > > aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. > aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. > > aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. > aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. > > aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. > aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. > > aMenu addTranslatedList: { > #-. > {'open viewer' translatedNoop. #openObjectsViewer. 'open the viewer of the object to which this script belongs' translatedNoop}. > {'detached method pane' translatedNoop. #makeIsolatedCodePane. 'open a little window that shows the Smalltalk code underlying this script.' translatedNoop}. > #-. > {'destroy this script' translatedNoop. #destroyScript} > }. > > > ^ aMenu popUpInWorld: self currentWorld! ! > > !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-menu commands' stamp: 'ct 9/11/2020 20:32'! > findObject > "Reveal the object bearing the code " > > playerScripted revealPlayerIn: Project current world.! ! > > !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:52'! > handUserTimesRepeatTile > "Hand the user a times-repeat tile, presumably to drop in the script" > > | aMorph | > aMorph := TimesRepeatTile new. > self currentHand attachMorph: aMorph. > aMorph position: self currentHand position.! ! > > !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-other' stamp: 'ct 9/12/2020 14:53'! > offerSimplerScriptorMenu > "Put up a menu in response to the user's clicking in the menu-request area of the scriptor's heaer. This variant is used when eToyFriendly preference is true." > > | aMenu count | > self currentHand showTemporaryCursor: nil. > > aMenu := MenuMorph new defaultTarget: self. > aMenu addTitle: scriptName asString. > > aMenu addList: (self hasParameter > ifTrue: [{ > {'remove parameter' translated. #ceaseHavingAParameter}}] > ifFalse: [{ > {'add parameter' translated. #addParameter}}]). > > self hasParameter ifFalse: > [aMenu addTranslatedList: #( > ('button to fire this script' tearOfButtonToFireScript) > -) translatedNoop]. > > aMenu addUpdating: #showingCaretsString target: self action: #toggleShowingCarets. > aMenu addLine. > aMenu addList: { > {'edit balloon help for this script' translated. #editMethodDescription}. > {'explain status alternatives' translated. #explainStatusAlternatives}. > {'button to show/hide this script' translated. #buttonToOpenOrCloseThisScript}. > #- > }. > > > Preferences universalTiles ifFalse: > [count := self savedTileVersionsCount. > self showingMethodPane > ifFalse: "currently showing tiles" > [aMenu add: 'show code textually' translated action: #toggleWhetherShowingTiles. > count > 0 ifTrue: > [aMenu add: 'revert to tile version...' translated action: #revertScriptVersion]. > aMenu add: 'save this version' translated action: #saveScriptVersion] > > ifTrue: "current showing textual source" > [count >= 1 ifTrue: > [aMenu add: 'revert to tile version' translated action: #toggleWhetherShowingTiles]]]. > > aMenu addLine. > > aMenu add: 'grab this object' translated target: playerScripted selector: #grabPlayerIn: argument: self currentWorld. > aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. > > aMenu add: 'reveal this object' translated target: playerScripted selector: #revealPlayerIn: argument: self currentWorld. > aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. > > aMenu add: 'tile representing this object' translated target: playerScripted action: #tearOffTileForSelf. > aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. > > aMenu addLine. > > aMenu addTranslatedList: #( > - > ('open viewer' openObjectsViewer 'open the viewer of the object to which this script belongs') > - > ('destroy this script' destroyScript)) translatedNoop. > > > ^ aMenu popUpInWorld: self currentWorld! ! > > !ScriptEditorMorph methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:32'! > goldBoxMenu > "Answer a graphical menu to be put up in conjunction with the Gold Box" > > | aBox | > aBox := Project current world findA: GoldBoxMenu. > aBox ifNil: [aBox := GoldBoxMenu new]. > aBox initializeFor: self. > ^ aBox! ! > > > !ScriptEncoder methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:31'! > init: class notifying: parser > > super init: class notifying: parser. > self referenceObject: Project current world referenceWorld.! ! > > > !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/12/2020 14:53'! > offerMenuIn: aStatusViewer > "Put up a menu." > > | aMenu | > self currentHand showTemporaryCursor: nil. > aMenu := MenuMorph new defaultTarget: self. > aMenu title: player knownName, ' ', selector. > aMenu addStayUpItem. > (player class instanceCount > 1) ifTrue: > [aMenu add: 'propagate status to siblings' translated selector: #assignStatusToAllSiblingsIn: argument: aStatusViewer. > aMenu balloonTextForLastItem: 'Make the status of this script in all of my sibling instances be the same as the status you see here' translated]. > aMenu addLine. > > aMenu add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld. > aMenu balloonTextForLastItem: 'This will actually pick up the object bearing this script and hand it to you. Click the (left) button to drop it' translated. > > aMenu add: 'reveal this object' translated target: player selector: #revealPlayerIn: argument: self currentWorld. > aMenu balloonTextForLastItem: 'If you have misplaced the object bearing this script, use this item to (try to) make it visible' translated. > > aMenu add: 'tile representing this object' translated target: player selector: #tearOffTileForSelf. > aMenu balloonTextForLastItem: 'choose this to obtain a tile which represents the object associated with this script' translated. > > aMenu addLine. > > aMenu add: 'open this script''s Scriptor' translated target: player selector: #grabScriptorForSelector:in: argumentList: {selector. aStatusViewer world}. > aMenu balloonTextForLastItem: 'Open up the Scriptor for this script' translated. > aMenu add: 'open this object''s Viewer' translated target: player selector: #beViewed. > aMenu balloonTextForLastItem: 'Open up a Viewer for this object' translated. > aMenu addLine. > aMenu add: 'more...' translated target: self selector: #offerShiftedMenuIn: argument: aStatusViewer. > aMenu balloonTextForLastItem: 'The "more..." branch offers you menu items that are less frequently used.' translated. > ^ aMenu popUpInWorld: self currentWorld! ! > > !ScriptInstantiation methodsFor: 'misc' stamp: 'ct 9/11/2020 20:30'! > offerShiftedMenuIn: aStatusViewer > "Put up the shifted menu" > > ^ (MenuMorph new defaultTarget: self) > title: player knownName, ' ', selector; > add: 'grab this object' translated target: player selector: #grabPlayerIn: argument: self currentWorld; > balloonTextForLastItem: 'Wherever this object currently is, the "grab" command will rip it out, and place it in your "hand". This is a very drastic step, that can disassemble things that may be very hard to put back together!!' translated; > add: 'destroy this script' translated target: player selector: #removeScriptWithSelector: argument: selector; > balloonTextForLastItem: 'Caution!! This is irreversibly destructive -- it removes the script from the system.' translated; > > addLine; > > add: 'inspect morph' translated target: player costume selector: #inspect; > add: 'inspect player' translated target: player selector: #inspect; > > popUpInWorld: self currentWorld! ! > > > !ScriptNameType methodsFor: 'queries' stamp: 'ct 9/11/2020 20:29'! > choices > "Answer an alphabetized list of known script selectors in the current project" > > ^ Project current world presenter allKnownUnaryScriptSelectors > ! ! > > > !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:29'! > parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock > "Answer a MethodNode for the argument, sourceStream, that is the root of > a parse tree. Parsing is done with respect to the argument, class, to find > instance, class, and pool variables; and with respect to the argument, > ctxt, to find temporary variables. Errors in parsing are reported to the > argument, req, if not nil; otherwise aBlock is evaluated. The argument > noPattern is a Boolean that is true if the the sourceStream does not > contain a method header (i.e., for DoIts)." > > "Copied from superclass, use ScriptEncoder and give it a referenceWorld. This assumes worldLoading has been set to the right world this player belongs to. --bf 5/4/2010" > > | methNode repeatNeeded myStream parser s p | > (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) > ifTrue: [parser := self as: DialectParser] > ifFalse: [parser := self]. > myStream := sourceStream. > [repeatNeeded := false. > p := myStream position. > s := myStream upToEnd. > myStream position: p. > parser init: myStream notifying: req failBlock: [^ aBlock value]. > doitFlag := noPattern. > failBlock := aBlock. > [methNode := parser method: noPattern context: ctxt > encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; > referenceObject: Project current world referenceWorld )] > on: ParserRemovedUnusedTemps > do: > [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. > myStream := ReadStream on: requestor text string. > ex resume]. > repeatNeeded] whileTrue. > encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" > methNode sourceText: s. > ^ methNode! ! > > !ScriptParser methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:28'! > parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock for: anInstance > > | methNode repeatNeeded myStream parser s p | > (req notNil and: [RequestAlternateSyntaxSetting signal and: [(sourceStream isKindOf: FileStream) not]]) > ifTrue: [parser := self as: DialectParser] > ifFalse: [parser := self]. > myStream := sourceStream. > [repeatNeeded := false. > p := myStream position. > s := myStream upToEnd. > myStream position: p. > parser init: myStream notifying: req failBlock: [^ aBlock value]. > doitFlag := noPattern. > failBlock := aBlock. > [methNode := parser method: noPattern context: ctxt > encoder: (ScriptEncoder new init: class context: ctxt notifying: parser; referenceObject: (anInstance costume ifNotNil: [anInstance costume referenceWorld] ifNil: [Project current world]))] > on: ParserRemovedUnusedTemps > do: > [ :ex | repeatNeeded := (requestor isKindOf: TextEditor) not. > myStream := ReadStream on: requestor text string. > ex resume]. > repeatNeeded] whileTrue. > encoder := failBlock := requestor := parseNode := nil. "break cycles & mitigate refct overflow" > methNode sourceText: s. > ^ methNode! ! > > > !SearchTopic methodsFor: 'private' stamp: 'ct 9/11/2020 20:28'! > triggerUpdateContents > > self mutex critical: [ > updatePending == true ifFalse: [ > updatePending := true. > Project current addDeferredUIMessage: [Project current world > addAlarm: #updateContents withArguments: #() > for: self > at: Time millisecondClockValue + 250]]].! ! > > > !SelectionMorph methodsFor: 'halo commands' stamp: 'ct 9/11/2020 20:28'! > duplicate > "Make a duplicate of the receiver and havbe the hand grab it" > > selectedItems := self duplicateMorphCollection: selectedItems. > selectedItems reverseDo: [:m | (owner ifNil: [self currentWorld]) addMorph: m]. > dupLoc := self position. > self currentHand grabMorph: self. > self currentWorld presenter flushPlayerListCache.! ! > > > !SimpleHierarchicalListMorph methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:28'! > specialKeyPressed: asciiValue > > (self arrowKey: asciiValue) > ifTrue: [^ true]. > > asciiValue = 27 "escape" > ifTrue: [ > self currentEvent shiftPressed > ifTrue: [self currentWorld putUpWorldMenuFromEscapeKey] > ifFalse: [self yellowButtonActivity: false]. > ^ true]. > > ^ false! ! > > > !SimpleSelectionMorph methodsFor: 'extending' stamp: 'ct 9/11/2020 20:27'! > extendByHand: aHand > "Assumes selection has just been created and added to some pasteUp or world" > > | startPoint handle m inner | > startPoint := Sensor cursorPoint. > > handle := NewHandleMorph new followHand: aHand > forEachPointDo: [:newPoint | > | localPt | > Cursor crossHair show. > localPt := (self transformFrom: self world) globalPointToLocal: newPoint. > self bounds: (startPoint rect: localPt)] > lastPointDo: > [:newPoint | > inner := self bounds insetBy: 2 at 2. > inner area >= 16 > ifTrue: > [m := SketchMorph new form: (Form fromDisplay: inner). > aHand attachMorph: m. > self currentWorld fullRepaintNeeded] "selection tracking can leave unwanted artifacts" > ifFalse: > [Beeper beep]. "throw minnows back" > self delete]. > > handle visible: false. > aHand attachMorph: handle. > handle startStepping! ! > > > !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! > cancelOutOfPainting > "The user requested to back out of a painting session without saving" > > self deleteSelfAndSubordinates. > emptyPicBlock ifNotNil: [emptyPicBlock value]. "note no args to block!!" > hostView ifNotNil: [hostView changed]. > Project current world resumeScriptsPausedByPainting. > ^ nil! ! > > !SketchEditorMorph methodsFor: 'start & finish' stamp: 'ct 9/11/2020 20:27'! > deliverPainting: result evt: evt > "Done painting. May come from resume, or from original call. Execute user's post painting instructions in the block. Always use this standard one. 4/21/97 tk" > > | newBox newForm ans | > palette ifNotNil: "nil happens" [palette setAction: #paint: evt: evt]. "Get out of odd modes" > "rot := palette getRotations." "rotate with heading, or turn to and fro" > "palette setRotation: #normal." > result == #cancel ifTrue: [ > ans := UIManager default chooseFrom: { > 'throw it away' translated. > 'keep painting it' translated. > } title: 'Do you really want to throw away > what you just painted?' translated. > ^ ans = 1 ifTrue: [self cancelOutOfPainting] > ifFalse: [nil]]. "cancelled out of cancelling." > > "hostView rotationStyle: rot." "rotate with heading, or turn to and fro" > newBox := paintingForm rectangleEnclosingPixelsNotOfColor: Color transparent. > registrationPoint ifNotNil: > [registrationPoint := registrationPoint - newBox origin]. "relative to newForm origin" > newForm := Form extent: newBox extent depth: paintingForm depth. > newForm copyBits: newBox from: paintingForm at: 0 at 0 > clippingBox: newForm boundingBox rule: Form over fillColor: nil. > newForm isAllWhite ifTrue: [ > (self valueOfProperty: #background) == true > ifFalse: [^ self cancelOutOfPainting]]. > > newForm fixAlpha. "so alpha channel stays intact for 32bpp" > > self delete. "so won't find me again" > dimForm ifNotNil: [dimForm delete]. > newPicBlock value: newForm value: (newBox copy translateBy: bounds origin). > Project current world resumeScriptsPausedByPainting.! ! > > > !SketchMorph methodsFor: 'menus' stamp: 'ct 9/11/2020 20:27'! > collapse > "Replace the receiver with a collapsed rendition of itself." > > | w collapsedVersion a ht | > > (w := self world) ifNil: [^ self]. > collapsedVersion := (self imageForm scaledToSize: 50 at 50) asMorph. > collapsedVersion setProperty: #uncollapsedMorph toValue: self. > collapsedVersion on: #mouseUp send: #uncollapseSketch to: collapsedVersion. > > collapsedVersion setBalloonText: ('A collapsed version of {1}. Click to open it back up.' translated format: {self externalName}). > > self delete. > w addMorphFront: ( > a := AlignmentMorph newRow > hResizing: #shrinkWrap; > vResizing: #shrinkWrap; > borderWidth: 4; > borderColor: Color white; > addMorph: collapsedVersion; > yourself). > a setNameTo: self externalName. > ht := (Smalltalk at: #SugarNavTab ifPresent: [:c | Project current world findA: c]) > ifNotNil: [:tab | tab height] > ifNil: [80]. > a position: 0 at ht. > > collapsedVersion setProperty: #collapsedMorphCarrier toValue: a. > > (self valueOfProperty: #collapsedPosition) ifNotNil: [:priorPosition | > a position: priorPosition].! ! > > > !ColorPickerMorph methodsFor: '*Etoys-Squeakland-event handling' stamp: 'ct 9/12/2020 14:39'! > deleteBoxHit > "The delete box was hit..." > > self currentHand showTemporaryCursor: nil. > self delete.! ! > > !ColorPickerMorph methodsFor: '*Etoys-Squeakland-e-toy support' stamp: 'ct 9/12/2020 14:39'! > openPropertySheet > "Delete the receiver and open a property sheet on my target instead." > > self currentHand showTemporaryCursor: nil. > target openAppropriatePropertySheet. > self delete.! ! > > > !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! > findText: keys inStrings: rawStrings startAt: startIndex container: oldContainer cardNum: cardNum > "Call once to search a card of the stack. Return true if found and highlight the text. oldContainer should be NIL. > (oldContainer is only non-nil when (1) doing a 'search again' and (2) the page is in memory and (3) keys has just one element. oldContainer is a TextMorph.)" > > | container strings old good insideOf place start | > good := true. > start := startIndex. > strings := oldContainer ifNil: > ["normal case" > > rawStrings] > ifNotNil: [self currentPage allStringsAfter: oldContainer text]. > keys do: > [:searchString | | thisWord | > "each key" > > good > ifTrue: > [thisWord := false. > strings do: > [:longString | | index | > (index := longString findWordStart: searchString startingAt: start) > 0 > ifTrue: > [thisWord not & (searchString == keys first) > ifTrue: > [insideOf := longString. > place := index]. > thisWord := true]. > start := 1]. "only first key on first container" > good := thisWord]]. > good > ifTrue: > ["all are on this page" > > "wasIn := (pages at: pageNum) isInMemory." > > self goToCardNumber: cardNum > "wasIn ifFalse: ['search again, on the real current text. Know page is in.'. > ^ self findText: keys > inStrings: ((pages at: pageNum) allStringsAfter: nil) recompute it > startAt: startIndex container: oldContainer > pageNum: pageNum]"]. > (old := self valueOfProperty: #searchContainer) ifNotNil: > [(old respondsTo: #editor) > ifTrue: > [old editor selectFrom: 1 to: 0. "trying to remove the previous selection!!" > old changed]]. > good > ifTrue: > ["have the exact string object" > > (container := oldContainer) ifNil: > [container := self > highlightText: keys first > at: place > in: insideOf] > ifNotNil: > [container userString == insideOf > ifFalse: > [container := self > highlightText: keys first > at: place > in: insideOf] > ifTrue: > [(container isTextMorph) > ifTrue: > [container editor selectFrom: place to: keys first size - 1 + place. > container changed]]]. > self setProperty: #searchContainer toValue: container. > self setProperty: #searchOffset toValue: place. > self setProperty: #searchKey toValue: keys. "override later" > self currentHand newKeyboardFocus: container. > ^true]. > ^false! ! > > !StackMorph methodsFor: 'menu' stamp: 'ct 9/12/2020 14:53'! > findViaTemplate > | list pl cardInst | > "Current card is the template. Only search cards in this background. Look at cards directly (not allText). Key must be found in the same field as in the template. HyperCard style (multiple starts of words). > Put results in a list, outside the stack." > > list := self templateMatches. > list isEmpty ifTrue: [^ self inform: 'No matches were found. > Be sure the current card is mostly blank > and only has text you want to match.' translated]. > "put up a PluggableListMorph" > cardInst := self currentCard. > cardInst matchIndex: 0. "establish entries" > cardInst results at: 1 put: list. > self currentPage setProperty: #myStack toValue: self. "way to get back" > > pl := PluggableListMorph new > on: cardInst list: #matchNames > selected: #matchIndex changeSelected: #matchIndex: > menu: nil "#matchMenu:shifted:" keystroke: nil. > self currentHand attachMorph: (self formatList: pl). > ! ! > > > !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! > assureFlapOfLabel: aTitle withContents: aString > "Answer an info flap with the given title and contents. If one exists in the project, use that, else create one & insert it in the world. Answer the flap tab." > > | allFlapTabs aTab | > allFlapTabs := Project current world localFlapTabs, Project current world extantGlobalFlapTabs. > aTab := allFlapTabs detect: > [:ft | ft flapID = aTitle] ifNone: [nil]. > aTab ifNotNil: [^ aTab]. "already present" > > aTab := self openInfoFlapWithLabel: aTitle helpContents: aString edge: #left. > aTab bottom: Project current world bottom. > self cleanUpFlapTabsOnLeft. > aTab hideFlap. > aTab referent show. > aTab show. > ^ aTab > > " > ScriptingSystem assureFlapOfLabel: 'Egg Sample' withContents: EventRollMorph basicNew helpString > "! ! > > !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:26'! > cleanUpFlapTabsOnLeft > "Make sure the flap tabs on the left of the screen line up nicely, making best use of realestate." > > | tabsOnLeft current | > tabsOnLeft := ((Project current world localFlapTabs, Project current world extantGlobalFlapTabs) select: [:f | f edgeToAdhereTo = #left]) > sort: [:a :b | a top <= b top]. > current := SugarNavigatorBar showSugarNavigator > ifTrue: [75] > ifFalse: [0]. > tabsOnLeft do: > [:aTab | > aTab top: (current min: Project current world height - aTab height). > current := aTab bottom + 2]. > " > ScriptingSystem cleanUpFlapTabsOnLeft > "! ! > > !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-help in a flap' stamp: 'ct 9/11/2020 20:25'! > openInfoFlapWithLabel: aTitle helpContents: aString edge: anEdge > "Open an info flap with the given label, contents, and edge" > > | aPlug outer leftStrip rightStrip titleRow aDismissButton aFlapTab | > Preferences enable: #scrollBarsOnRight. > Preferences enable: #inboardScrollbars. > > aFlapTab := FlapTab new. > aFlapTab assureExtension visible: false. > aFlapTab referentMargin: 0 @ Project current world sugarAllowance. > > outer := HelpFlap newRow. > outer assureExtension visible: false. > outer clipSubmorphs: true. > outer beTransparent. > outer vResizing: #spaceFill; hResizing: #spaceFill. > outer layoutInset: 0; cellInset: 0; borderWidth: 0. > outer setProperty: #morphicLayerNumber toValue: 26. > > leftStrip := Morph new beTransparent. > leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. > leftStrip width: 20. > leftStrip hResizing: #rigid; vResizing: #spaceFill. > outer addMorphBack: leftStrip. > > rightStrip := AlignmentMorph newColumn. > rightStrip beTransparent. > rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. > outer addMorphBack: rightStrip. > outer clipSubmorphs: true. > > titleRow := AlignmentMorph newRow. > titleRow borderColor: Color veryVeryLightGray; borderWidth: 1. > titleRow hResizing: #spaceFill; vResizing: #shrinkWrap. > titleRow beTransparent. > aDismissButton := aFlapTab tanOButton. > aDismissButton actionSelector: #dismissViaHalo. > titleRow addMorphFront: aDismissButton. > titleRow addTransparentSpacerOfSize: 8 @ 0. > titleRow > addMorphBack: (StringMorph contents: aTitle font: Preferences standardEToysTitleFont). > rightStrip addMorph: titleRow. > > aPlug := PluggableTextMorph new. > aPlug width: 540. > aPlug setText: aString. > aPlug textMorph beAllFont: Preferences standardEToysFont. > aPlug retractable: false; scrollBarOnLeft: false. > aPlug hScrollBarPolicy: #never. > aPlug borderColor: ScriptingSystem borderColor. > aPlug setNameTo: aTitle. > aPlug hResizing: #spaceFill. > aPlug vResizing: #spaceFill. > rightStrip addMorphBack: aPlug. > aFlapTab referent ifNotNil: [aFlapTab referent delete]. > aFlapTab referent: outer. > aFlapTab setName: aTitle edge: anEdge color: (Color r: 0.677 g: 0.935 b: 0.484). > aFlapTab submorphs first beAllFont: Preferences standardEToysFont. > Project current world addMorphFront: aFlapTab. > aFlapTab adaptToWorld: Project current world. > aFlapTab computeEdgeFraction. > > anEdge == #left ifTrue: > [aFlapTab position: (outer left @ outer top). > outer extent: (540 @ Project current world height)]. > anEdge == #right ifTrue: > [aFlapTab position: ((Project current world right - aFlapTab width) @ Project current world top). > outer extent: (540 @ Project current world height)]. > > outer beFlap: true. > outer color: Color green veryMuchLighter. > > aPlug textMorph lock. > aFlapTab referent hide. > aFlapTab openFully. > > outer beSticky. > leftStrip beSticky. > rightStrip beSticky. > > Project current world doOneCycle. > aPlug width: 540. > aPlug setText: aString. "hmm, again" > > aPlug color: outer color. > > aPlug borderWidth: 0. > > aPlug textMorph contents: aString wrappedTo: 520. > aFlapTab applyThickness: 560. > aFlapTab fitOnScreen. > aFlapTab referent show. > ^ aFlapTab! ! > > !StandardScriptingSystem methodsFor: '*Etoys-Squeakland-gold box' stamp: 'ct 9/11/2020 20:24'! > systemQueryPhraseWithActionString: aString labelled: anotherString > "Answer a system-query-phrase with the give action-string and label." > > ^ Project current world presenter > systemQueryPhraseWithActionString: aString labelled: anotherString! ! > > > !StandardViewer methodsFor: 'macpal' stamp: 'ct 9/11/2020 20:24'! > currentVocabulary > "Answer the vocabulary currently associated with the receiver" > > | aSym aVocab | > aSym := self valueOfProperty: #currentVocabularySymbol ifAbsent: [nil]. > aSym ifNil: > [aVocab := self valueOfProperty: #currentVocabulary ifAbsent: [nil]. > aVocab ifNotNil: > [aSym := aVocab vocabularyName. > self removeProperty: #currentVocabulary. > self setProperty: #currentVocabularySymbol toValue: aSym]]. > ^ aSym > ifNotNil: > [Vocabulary vocabularyNamed: aSym] > ifNil: > [(self world ifNil: [Project current world]) currentVocabularyFor: scriptedPlayer]! ! > > > !SugarLauncher methodsFor: 'commands' stamp: 'ct 9/12/2020 14:07'! > viewSource > > Project current world addDeferredUIMessage: [ > Project current world showSourceKeyHit].! ! > > > !SugarNavTab methodsFor: 'positioning' stamp: 'ct 9/11/2020 20:24'! > occupyTopRightCorner > "Make the receiver be the correct size, and occupy the top-right corner of the screen." > > | worldBounds toUse | > worldBounds := Project current world bounds. > " toUse := Preferences useArtificialSweetenerBar > ifFalse: > [75] > ifTrue: > [(ActiveWorld extent >= (1200 @ 900)) > ifTrue: > [75] > ifFalse: > [40]]." > toUse := 40. "Trying for the moment to use the smaller icon always when in this mode." > > referent height: toUse; resizeButtonsAndTabTo: toUse. > self extent: toUse @ toUse. > self topRight: worldBounds topRight! ! > > > !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! > putUpInitialBalloonHelp > " > SugarNavigatorBar putUpInitialBalloonHelp > " > > | suppliesButton b1 b2 p b | > suppliesButton := paintButton owner submorphs detect: [:e | e isButton and: [e actionSelector = #toggleSupplies]]. > > b1 := BalloonMorph string: self paintButtonInitialExplanation for: paintButton corner: #topRight force: false. > b2 := BalloonMorph string: self suppliesButtonInitialExplanation for: suppliesButton corner: #topLeft force: true. > > p := PasteUpMorph new. > p clipSubmorphs: false. > p color: Color transparent. > p borderWidth: 0. > p addMorph: b1. > p addMorph: b2. > b := BalloonMorph string: p for: self world corner: #bottomLeft. > b color: Color transparent. > b borderWidth: 0. > [(Delay forSeconds: 1) wait. b popUp] fork.! ! > > !SugarNavigatorBar methodsFor: 'initialization' stamp: 'ct 9/12/2020 14:53'! > putUpInitialBalloonHelpFor: quads > "Given a list of quads of the form (see senders for examples), put up initial balloon help for them." > " > SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((doNewPainting 'make a new painting' topRight false) (toggleSupplies 'open the supplies bin' topLeft true)) > SugarNavigatorBar someInstance putUpInitialBalloonHelpFor: #((showNavBar 'show the tool bar' bottomLeft false) (hideNavBar 'hide the tool bar' bottomLeft false)) > > " > | b1 p b | > > p := PasteUpMorph new. > p clipSubmorphs: false. > p color: Color transparent. > p borderWidth: 0. > > quads do: [:aQuad | > (submorphs first submorphs detect: [:e | e isButton and: [e actionSelector = aQuad first]] ifNone: [nil]) ifNotNil: > [:aButton | > b1 := BalloonMorph string: aQuad second for: aButton corner: aQuad third force: aQuad fourth. > p addMorph: b1]]. > > b := BalloonMorph string: p for: self world corner: #bottomLeft. > b color: Color transparent. > b borderWidth: 0. > [(Delay forSeconds: 1) wait. b popUp] fork.! ! > > !SugarNavigatorBar methodsFor: 'help flap' stamp: 'ct 9/12/2020 14:37'! > buildAndOpenHelpFlap > "Called only when flaps are being created afresh." > > | aFlapTab outer leftStrip rightStrip aGuide | > aFlapTab := FlapTab new. > aFlapTab assureExtension visible: false. > aFlapTab setProperty: #rigidThickness toValue: true. > > outer := AlignmentMorph newRow. > outer assureExtension visible: false. > outer clipSubmorphs: true. > outer beTransparent. > outer vResizing: #spaceFill; hResizing: #spaceFill. > outer layoutInset: 0; cellInset: 0; borderWidth: 0. > outer setProperty: #wantsHaloFromClick toValue: false. > > leftStrip := Morph new beTransparent. "This provides space for tabs to be seen." > leftStrip layoutInset: 0; cellInset: 0; borderWidth: 0. > leftStrip width: 20. > leftStrip hResizing: #rigid; vResizing: #spaceFill. > outer addMorphBack: leftStrip. > > rightStrip := AlignmentMorph newColumn. > rightStrip color: (Color green veryMuchLighter alpha: 0.2). > rightStrip layoutInset: 0; cellInset: 0; borderWidth: 0. > rightStrip setProperty: #wantsHaloFromClick toValue: false. > outer addMorphBack: rightStrip. > outer clipSubmorphs: true. > > aGuide := QuickGuideMorph new. > aGuide initializeIndexPage. > " aGuide order: QuickGuideMorph defaultOrder. " > QuickGuideMorph loadIndexAndPeekOnDisk. > aGuide loadPages. > rightStrip addMorphBack: aGuide. > aGuide beSticky. > > aFlapTab referent ifNotNil: [aFlapTab referent delete]. > aFlapTab referent: outer. > aFlapTab setName: 'Help' translated edge: #left color: (Color r: 0.677 g: 0.935 b: 0.484). > Project current world addMorphFront: aFlapTab. > aFlapTab adaptToWorld: Project current world. > aFlapTab computeEdgeFraction. > > aFlapTab position: outer left @ outer top. > outer extent: 462 @ Project current world height. > > outer beFlap: true. > outer beTransparent. > > aFlapTab referent hide. > aFlapTab referentMargin: 0 at self height. > aFlapTab openFully. > > outer beSticky. > leftStrip beSticky. > rightStrip beSticky. > > aFlapTab applyThickness: 462. > aFlapTab fitOnScreen. > aFlapTab referent show. > aFlapTab show. > aFlapTab makeFlapCompact: true. > aFlapTab setToPopOutOnDragOver: false. > Flaps addGlobalFlap: aFlapTab. > Project current world addGlobalFlaps. > ScriptingSystem cleanUpFlapTabsOnLeft! ! > > !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/12/2020 14:53'! > gotoAnother > > EToyProjectHistoryMorph new > position: self currentHand position; > openInWorld > ! ! > > !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! > makeProjectNameLabel > > | t | > projectNameField := SugarRoundedField new. > t := UpdatingStringMorph new. > t setProperty: #okToTextEdit toValue: true. > t putSelector: #projectNameChanged:. > t getSelector: #projectName. > projectNameField backgroundColor: self color. > t target: self. > t useStringFormat. > t beSticky. > t label: Project current name font: (StrikeFont familyName: 'BitstreamVeraSans' size: 24). > t color: Color black. > t width: projectNameField width - 10. > projectNameField label: t. > projectNameField setBalloonText: self projectNameFieldBalloonHelp. > projectNameField on: #mouseDown send: #mouseDown: to: t. > projectNameField on: #mouseUp send: #mouseUp: to: t. > self resizeProjectNameField. > ^projectNameField.! ! > > !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! > projectName > > ^ Project current name > ! ! > > !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! > projectNameChanged: aString > > Project current renameTo: aString. > ! ! > > !SugarNavigatorBar methodsFor: 'the actions' stamp: 'ct 9/11/2020 20:23'! > shareMenu > > | menu item ext | > menu := MenuMorph new. > ext := 200 at 50. > #((stopSharing makePrivateLabelIn:) (startSharing makeMyNeighborhoodLabelIn:) "(shareThisWorld makeBadgeLabelIn:)") do: [:pair | > > item := MenuItemMorph new > contents: ''; > target: self; > selector: pair first; > arguments: #(). > item color: Color black. > item addMorph: (self perform: pair second with: ext). > item setProperty: #minHeight toValue: ext y. > item fitContents. > item extent: ext. > item setProperty: #selectionFillStyle toValue: (Color gray alpha: 0.5). > menu addMorphBack: item. > ]. > menu color: Color black. > menu borderColor: Color white. > ^ menu invokeModalAt: shareButton position + (10 at 20) in: Project current world allowKeyboard: false.! ! > > !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/12/2020 14:37'! > startNebraska > > | nebraska | > Project current world remoteServer: nil. > Project current world submorphs do: [:e | (e isMemberOf: NebraskaServerMorph) ifTrue: [e delete]]. > nebraska := NebraskaServerMorph serveWorld. > SugarLauncher current > offerStreamTube: 'sqk-nebraska' > inBackgroundOnPort: [nebraska listeningPort]. > ! ! > > !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! > startP2P > listener ifNotNil: [listener stopListening]. > listener ifNil: [listener := SugarListenerMorph new]. > listener position: -200@ -200. > Project current world addMorphBack: listener. > listener startListening. > SugarLauncher current > offerStreamTube: 'sqk-etoy-p2p' > inBackgroundOnPort: [listener listeningPort].! ! > > !SugarNavigatorBar methodsFor: 'sharing' stamp: 'ct 9/11/2020 20:22'! > stopSharing > SugarLauncher current leaveSharedActivity. > listener ifNotNil: [listener stopListening. listener := nil]. > Project current world remoteServer: nil. > Project current world submorphs do: [:ea | > (ea isMemberOf: NebraskaServerMorph) ifTrue: [ea delete]]. > self sharingChanged.! ! > > !SugarNavigatorBar methodsFor: 'event handling' stamp: 'ct 9/11/2020 20:22'! > undoButtonAppearance > > | wording | > undoButton ifNotNil: [ > Project current world commandHistory undoEnabled > ifTrue: [undoButton enabled] > ifFalse: [undoButton disabled]. > wording := self undoButtonWording. > undoButton setBalloonText: wording. > ]. > > ! ! > > > !InteriorSugarNavBar methodsFor: 'buttons' stamp: 'ct 9/11/2020 20:12'! > doNewPainting > "Make a new painting" > > | worldlet aRect | > self currentWorld assureNotPaintingElse: [^ self]. > worldlet := self ownerThatIsA: Worldlet. > aRect := (worldlet topLeft + (0 @ self height)) corner: worldlet bottomRight. > worldlet makeNewDrawing: (self currentEvent copy setPosition: aRect center).! ! > > > !SugarNavigatorBar class methodsFor: 'utilitity' stamp: 'ct 9/11/2020 20:22'! > findAnythingMorph > > ^ FileList2 morphicViewProjectLoader2InWorld: Project current world > title: 'Find...' translated > reallyLoad: true > dirFilterType: #initialDirectoryList > isGeneral: true.! ! > > > !SugarRoundedField methodsFor: 'as yet unclassified' stamp: 'ct 9/11/2020 20:22'! > resizeLabel > > | small | > (label notNil and: [label hasFocus not]) ifTrue: [ > label width: self width - 10. > small :=self height < 45. > label label: Project current world project name font: (StrikeFont familyName: 'BitstreamVeraSans' size: (small ifTrue: [15] ifFalse: [24])). > label center: self center. > label left: self left + 10. > self addMorph: label. > ]. > ! ! > > > !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! > offerTilesMenuFor: aReceiver in: aLexiconModel > "Offer a menu of tiles for assignment and constants" > > | menu | > menu := MenuMorph new addTitle: 'Hand me a tile for...'. > menu addLine. > menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. > menu submorphs last color: Color red darker. > menu addLine. > > menu add: 'me, by name' target: self selector: #attachTileForCode:nodeType: > argumentList: {''. aReceiver}. > menu add: 'self' target: self selector: #attachTileForCode:nodeType: > argumentList: {'self'. VariableNode}. > menu add: '_ (assignment)' target: self selector: #attachTileForCode:nodeType: > argumentList: {''. nil}. > menu add: '"a Comment"' target: self selector: #attachTileForCode:nodeType: > argumentList: {'"a comment"\' withCRs. CommentNode}. > menu submorphs last color: Color blue. > menu add: 'a Number' target: self selector: #attachTileForCode:nodeType: > argumentList: {'5'. LiteralNode}. > menu add: 'a Character' target: self selector: #attachTileForCode:nodeType: > argumentList: {'$z'. LiteralNode}. > menu add: '''abc''' target: self selector: #attachTileForCode:nodeType: > argumentList: {'''abc'''. LiteralNode}. > menu add: 'a Symbol constant' target: self selector: #attachTileForCode:nodeType: > argumentList: {'#next'. LiteralNode}. > menu add: 'true' target: self selector: #attachTileForCode:nodeType: > argumentList: {'true'. VariableNode}. > menu add: 'a Test' target: self selector: #attachTileForCode:nodeType: > argumentList: {'true ifTrue: [self] ifFalse: [self]'. MessageNode}. > menu add: 'a Loop' target: self selector: #attachTileForCode:nodeType: > argumentList: {'1 to: 10 do: [:index | self]'. MessageNode}. > menu add: 'a Block' target: self selector: #attachTileForCode:nodeType: > argumentList: {'[self]'. BlockNode}. > menu add: 'a Class or Global' target: self selector: #attachTileForCode:nodeType: > argumentList: {'Character'. LiteralVariableNode}. > menu add: 'a Reply' target: self selector: #attachTileForCode:nodeType: > argumentList: {'| temp | temp'. ReturnNode}. > menu popUpInWorld: self world.! ! > > !SyntaxMorph methodsFor: 'menus' stamp: 'ct 9/12/2020 14:55'! > offerVarsMenuFor: aReceiver in: aLexiconModel > "Offer a menu of tiles for assignment and constants" > > | menu instVarList cls | > menu := MenuMorph new addTitle: 'Hand me a tile for...'. > menu addLine. > menu add: '(accept method now)' target: aLexiconModel selector: #acceptTiles. > menu submorphs last color: Color red darker. > menu addLine. > menu add: 'new temp variable' target: self selector: #attachTileForCode:nodeType: > argumentList: {'| temp | temp'. TempVariableNode}. > > instVarList := OrderedCollection new. > cls := aReceiver class. > [instVarList addAllFirst: cls instVarNames. > cls == aLexiconModel limitClass] whileFalse: [cls := cls superclass]. > instVarList do: [:nn | > menu add: nn target: self selector: #instVarTile: argument: nn]. > menu popUpInWorld: self world.! ! > > !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! > attachToHand > "Adjust my look and attach me to the hand" > > self roundedCorners. > self currentHand attachMorph: self. > Preferences tileTranslucentDrag > ifTrue: [self lookTranslucent. > self align: self center with: self currentHand position "+ self cursorBaseOffset"] > ifFalse: [ > self align: self topLeft with: self currentHand position + self cursorBaseOffset].! ! > > !SyntaxMorph methodsFor: 'new tiles' stamp: 'ct 9/12/2020 14:54'! > instVarTile: aName > "Make and put into hand a tile for an instance variable" > > | sm | > sm := ((VariableNode new > name: aName > index: 1 > type: 1 "LdInstType") asMorphicSyntaxIn: SyntaxMorph new). > sm roundedCorners. > self currentHand attachMorph: sm. > Preferences tileTranslucentDrag > ifTrue: [sm lookTranslucent. > sm align: sm center with: self currentHand position "+ self cursorBaseOffset"] > ifFalse: [ > sm align: sm topLeft with: self currentHand position + self cursorBaseOffset]! ! > > !SyntaxMorph methodsFor: 'scripting' stamp: 'ct 9/12/2020 14:55'! > tearOffTile > "For a SyntaxMorph, this means give a copy of me" > > | dup | > dup := self duplicate. > self currentHand attachMorph: dup. > ^ Preferences tileTranslucentDrag > ifTrue: [dup lookTranslucent] > ifFalse: [dup align: dup topLeft with: self currentHand position + self cursorBaseOffset]! ! > > > !SystemWindow methodsFor: 'events' stamp: 'ct 9/11/2020 20:22'! > doFastFrameDrag: grabPoint > "Do fast frame dragging from the given point" > > | offset newBounds outerWorldBounds clearArea | > outerWorldBounds := self boundsIn: nil. > offset := outerWorldBounds origin - grabPoint. > clearArea := Project current world clearArea. > newBounds := outerWorldBounds newRectFrom: [:f | > | p selector | > p := Sensor cursorPoint. > (self class dragToEdges and: [(selector := self dragToEdgesSelectorFor: p in: clearArea) notNil]) > ifTrue: [clearArea perform: selector] > ifFalse: [p + offset extent: outerWorldBounds extent]]. > self bounds: newBounds; comeToFront! ! > > > !CollapsedMorph methodsFor: 'collapse/expand' stamp: 'ct 9/12/2020 14:39'! > uncollapseToHand > "Hand the uncollapsedMorph to the user, placing it in her hand, after remembering appropriate state for possible future use" > > | nakedMorph | > nakedMorph := uncollapsedMorph. > uncollapsedMorph := nil. > nakedMorph setProperty: #collapsedPosition toValue: self position. > mustNotClose := false. "so the delete will succeed" > self delete. > self currentHand attachMorph: nakedMorph.! ! > > > !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 18:01'! > rotateWindows > "Rotate the z-ordering of the windows." > > self currentEvent shiftPressed > ifTrue: [self sendTopWindowBackOne] > ifFalse: [self sendTopWindowToBack].! ! > > !SystemWindow class methodsFor: '*Etoys-Squeakland-top window' stamp: 'ct 9/11/2020 20:21'! > sendTopWindowBackOne > "Rotate the window-list one downward, i.e., make the bottommost one be the active one, pushing the receiver to next-to-topmost." > > | dows | > dows := Project current world submorphs select: [:m | m isSystemWindow]. > dows ifNotEmpty: [dows last expand; comeToFront]! ! > > > !TextEditor methodsFor: 'menu commands' stamp: 'ct 9/11/2020 18:02'! > offerMenuFromEsc: aKeyboardEvent > "The escape key was hit while the receiver has the keyboard focus; take action." > > self currentEvent shiftPressed ifFalse: [ > self raiseContextMenu: aKeyboardEvent]. > ^ true! ! > > > !ThreePhaseButtonMorph methodsFor: 'button' stamp: 'ct 9/11/2020 18:02'! > doButtonAction > "Perform the action of this button. Subclasses may override this method. The default behavior is to send the button's actionSelector to its target object with its arguments." > > | args | > (target notNil and: [actionSelector notNil]) ifTrue: [ > args := actionSelector numArgs > arguments size > ifTrue: [arguments copyWith: self currentEvent] > ifFalse: [arguments]. > Cursor normal showWhile: [ > target perform: actionSelector withArguments: args]. > target isMorph ifTrue: [target changed]].! ! > > > !TileMorph methodsFor: 'arrows' stamp: 'ct 9/11/2020 18:02'! > showSuffixChoices > "The suffix arrow has been hit, so respond appropriately" > > | plusPhrase phrase pad outer num | > self currentEvent shiftPressed ifTrue: [^ self wrapPhraseInFunction]. > > (phrase := self ownerThatIsA: PhraseTileMorph orA: FunctionTile) ifNil: [nil]. > > (type == #literal) & (literal isNumber) ifTrue: ["Tile is a constant number" > (phrase isNil or: [phrase finalTilePadSubmorph == owner]) "pad" > ifTrue: ["we are adding the first time (at end of our phrase)" > plusPhrase := self phraseForOp: #+ arg: 1 resultType: #Number. > plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). > owner acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. > num := plusPhrase firstSubmorph firstSubmorph. > num deleteSuffixArrow]]. > > (#(function expression parameter) includes: type) ifTrue: > [pad := self ownerThatIsA: TilePadMorph. > plusPhrase := self presenter phraseForReceiver: 1 op: #+ arg: 1 resultType: #Number. > plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). > pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. > plusPhrase firstSubmorph removeAllMorphs; addMorph: self. > pad topEditor scriptEdited "recompile"]. > > type = #operator ifTrue: ["Tile is accessor of an expression" > phrase resultType == #Number ifTrue: > [outer := phrase ownerThatIsA: PhraseTileMorph orA: TimesRepeatTile. > pad := self ownerThatIsA: TilePadMorph. > outer ifNotNil: > [(outer lastSubmorph == pad or: [true]) ifTrue: [ "first time" > plusPhrase := self presenter phraseForReceiver: 1 > op: #+ arg: 1 resultType: #Number. > plusPhrase submorphs second submorphs last setBalloonText: (ScriptingSystem helpStringForOperator: #+). > pad acceptDroppingMorph: plusPhrase event: self primaryHand lastEvent. > plusPhrase firstSubmorph removeAllMorphs; addMorph: phrase. "car's heading" > pad topEditor scriptEdited "recompile & deal with carets"]]]]. > > (self topEditor ifNil: [phrase ifNil: [^ self]]) enforceTileColorPolicy! ! > > !TileMorph methodsFor: 'code generation' stamp: 'ct 9/11/2020 20:21'! > acceptNewLiteral > "Tell the scriptEditor who I belong to that I have a new literal value." > > | topScript | > topScript := self outermostMorphThat: > [:m | m isKindOf: ScriptEditorMorph]. > topScript ifNotNil: [topScript installWithNewLiteral]. > (self ownerThatIsA: ViewerLine) ifNotNil: > [:aLine | > (self ownerThatIsA: PhraseTileMorph) ifNotNil: > [aLine removeHighlightFeedback. > self layoutChanged. > Project current world doOneSubCycle. > aLine addCommandFeedback: nil]]! ! > > !TileMorph methodsFor: 'misc' stamp: 'ct 9/12/2020 14:56'! > handReferentMorph > "Hand the user the actual morph referred to" > > | aMorph surrogate | > ((aMorph := actualObject costume) isMorph > and: [aMorph isWorldMorph not]) > ifTrue: [ > surrogate := CollapsedMorph collapsedMorphOrNilFor: aMorph. > surrogate > ifNotNil: [surrogate uncollapseToHand] > ifNil: [self currentHand attachMorph: aMorph]].! ! > > > !SymbolListTile methodsFor: 'user interface' stamp: 'ct 9/11/2020 20:22'! > choices > "Answer the list of current choices for the receiver's symbol" > > dataType == #ScriptName ifTrue: "Backward compatibility with old tiles" > [^ Project current world presenter allKnownUnaryScriptSelectors]. > ^ choices! ! > > > !ScriptNameTile methodsFor: 'initialization' stamp: 'ct 9/11/2020 20:29'! > choices > "Answer the current list of choices" > > ^ Project current world presenter allKnownUnaryScriptSelectors! ! > > > !TileMorph class methodsFor: '*Etoys-Squeakland-utilities' stamp: 'ct 9/11/2020 20:21'! > implicitSelfInTilesChanged > "The implicitSelfInTiles preference changed. Caution: although this may appear to have no senders in the image, it is in fact invoked when the implicitSelfInTiles preference is toggled... so please do not delete it." > > Smalltalk isMorphic ifFalse: [^ self]. > Project current world allScriptEditorsInProject do: [:scriptEditor | scriptEditor install]. > Project current world allViewersInProject do: [:viewer | viewer enforceImplicitSelf]. > > " > (Preferences buttonForPreference: #implicitSelfInTiles) openInHand. > "! ! > > > !TileMorphTest methodsFor: 'testing' stamp: 'ct 9/12/2020 14:56'! > testAssignmentTile > "self debug: #testAssignmentTile" > > | player viewer tile phrase | > player := Morph new assuredPlayer. > viewer := CategoryViewer new invisiblySetPlayer: player. > viewer makeSetter: #(#getX #Number) event: nil from: player costume. > phrase := self currentHand firstSubmorph. > self currentHand removeAllMorphs. > tile := phrase submorphs second. > > self assert: tile codeString = 'setX: '. > tile arrowAction: 1. > self assert: tile codeString = 'setX: self getX + '.! ! > > > !TypeListTile methodsFor: 'mouse handling' stamp: 'ct 9/12/2020 14:56'! > showOptions > | topScript | > suffixArrow > ifNotNil: [(suffixArrow bounds containsPoint: self currentHand cursorPoint) > ifTrue: [^ super showOptions]]. > topScript := self > outermostMorphThat: [:m | m isKindOf: ScriptEditorMorph]. > topScript > ifNotNil: [topScript handUserParameterTile]! ! > > > !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! > confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice at: aPointOrNil > "UserDialogBoxMorph confirm: 'Make your choice carefully' withCRs title: 'Do you like chocolate?' trueChoice: 'Oh yessir!!' falseChoice: 'Not so much...'" > ^self new > title: titleString; > message: aString; > createButton: trueChoice translated value: true; > createButton: falseChoice translated value: false; > createCancelButton: 'Cancel' translated translated value: nil; > selectedButtonIndex: 1; > registerKeyboardShortcuts; > preferredPosition: (aPointOrNil ifNil: [Project current world center]); > getUserResponse! ! > > !UserDialogBoxMorph class methodsFor: 'utilities' stamp: 'ct 9/11/2020 20:20'! > confirm: aString title: titleString trueChoice: trueChoice falseChoice: falseChoice default: default triggerAfter: seconds at: aPointOrNil > "UserDialogBoxMorph confirm: 'I like hot java' title: 'What do you say?' trueChoice: 'You bet!!' falseChoice: 'Nope' default: false triggerAfter: 12 at: 121 at 212" > ^self new > title: titleString; > message: aString; > createButton: trueChoice translated value: true; > createButton: falseChoice translated value: false; > createCancelButton: 'Cancel' translated translated value: nil; > selectedButtonIndex: (default ifTrue: [1] ifFalse: [2]); > registerKeyboardShortcuts; > preferredPosition: (aPointOrNil ifNil: [Project current world center]); > getUserResponseAfter: seconds! ! > > > !UserText methodsFor: 'event handling' stamp: 'ct 9/12/2020 14:56'! > keyStroke: evt > "Handle a keystroke event." > > | newSel | > super keyStroke: evt. > evt hand keyboardFocus == self ifFalse: [self releaseEditor. ^ self]. > newSel := self editor selectionInterval. "restore editor state" > self refreshParagraph. > self editor selectFrom: newSel first to: newSel last. > > wrapFlag ifFalse: > [self fullBounds right > owner right ifTrue: > [self wrapFlag: true. > self right: owner right. > self refreshParagraph. > self editor selectFrom: text string size + 1 to: text string size]].! ! > > > !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! > addCommandFeedback > "Add screen feedback showing what would be torn off in a drag" > > | aMorph | > aMorph := RectangleMorph new bounds: ((submorphs fourth topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 0)). > aMorph useRoundedCorners; beTransparent; borderWidth: 2; borderColor: (Color r: 1.0 g: 0.548 b: 0.452); lock. > aMorph setProperty: #highlight toValue: true. > ^ Project current world addMorphFront: aMorph! ! > > !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! > addGetterFeedback > "Add feedback during mouseover of a getter" > > | aMorph | > aMorph := RectangleMorph new > bounds: (self firstTileMorph topLeft corner: > (self firstAlignmentMorph ifNil: [self submorphs last bottomRight] ifNotNil: [:m | m bottomLeft])). > aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem getterFeedback; lock. > ^ Project current world addHighlightMorph: aMorph for: nil. > > " > Color fromUser (Color r: 1.0 g: 0.355 b: 0.839) > "! ! > > !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! > addSetterFeedback > "Add screen feedback showing what would be torn off to make a setter" > > | aMorph | > aMorph := RectangleMorph new bounds: (self firstTileMorph topLeft corner: self bounds bottomRight). > aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem setterFeedback; lock. > ^ Project current world addHighlightMorph: aMorph for: nil! ! > > !ViewerLine methodsFor: 'slot' stamp: 'ct 9/11/2020 20:20'! > removeHighlightFeedback > "Remove any existing highlight feedback" > > ^ Project current world removeHighlightFeedback > ! ! > > !ViewerLine methodsFor: '*Etoys-Squeakland-slot' stamp: 'ct 9/11/2020 20:20'! > addCommandFeedback: evt > "Add screen feedback showing what would be torn off in a drag" > > | aMorph | > aMorph := RectangleMorph new bounds: ((submorphs third topLeft - (2 at 1)) corner: (submorphs last bottomRight) + (2 at 1)). > aMorph beTransparent; borderWidth: 2; borderColor: ScriptingSystem commandFeedback; lock. > ^ Project current world addHighlightMorph: aMorph for: nil! ! > > > !Vocabulary class methodsFor: '*Etoys-Squeakland-type vocabularies' stamp: 'ct 9/11/2020 20:19'! > typeChoicesForUserVariables > "Answer a list of all user-choosable value types for variables." > > | aList | > aList := #(Boolean Color CustomEvents Graphic Number Patch Player Point ScriptName Sound String) copy. > self currentWorld isKedamaPresent ifFalse: [ > ^ aList copyWithout: #Patch]. > ^ aList > > " > Vocabulary typeChoicesForUserVariables > "! ! > > > !WorldState methodsFor: 'hands' stamp: 'ct 9/24/2020 13:50'! > primaryHand > > ^ ActiveHand value ifNil: [self hands at: 1 ifAbsent: [nil]]! ! > > !WorldState methodsFor: 'hands' stamp: 'ct 9/24/2020 13:50'! > removeHand: aHandMorph > "Remove the given hand from the list of hands for this world." > > (hands includes: aHandMorph) ifFalse: [^ self]. > self currentHand == aHandMorph ifTrue: [self halt: 'ct: Cannot remove the active hand because it is a dynamic variable. What should we do now? Panic!!!!1']. > hands := hands copyWithout: aHandMorph.! ! > > !WorldState methodsFor: 'stepping' stamp: 'ct 9/16/2020 18:27'! > runLocalStepMethodsIn: aWorld > "Run morph 'step' methods (LOCAL TO THIS WORLD) whose time has come. Purge any morphs that are no longer in this world. > ar 3/13/1999: Remove buggy morphs from the step list so that they don't raise repeated errors." > > | now morphToStep stepTime | > now := Time millisecondClockValue. > > aWorld becomeActiveDuring: [ > self triggerAlarmsBefore: now. > > stepList ifEmpty: [^ self]. > > (now < lastStepTime or: [now - lastStepTime > 5000]) ifTrue: [ > self adjustWakeupTimes: now]. "clock slipped" > > [stepList notEmpty and: [stepList first scheduledTime < now]] whileTrue: [ > lastStepMessage := stepList removeFirst. > morphToStep := lastStepMessage receiver. > (morphToStep shouldGetStepsFrom: aWorld) ifTrue: [ > lastStepMessage value: now. > lastStepMessage ifNotNil: [ > stepTime := lastStepMessage stepTime ifNil: [morphToStep stepTime]. > lastStepMessage scheduledTime: now + (stepTime max: 1). > stepList add: lastStepMessage]]. > lastStepMessage := nil]. > > lastStepTime := now].! ! > > !WorldState methodsFor: 'update cycle' stamp: 'ct 9/16/2020 18:44'! > doOneCycleNowFor: aWorld > "Immediately do one cycle of the interaction loop. > This should not be called directly, but only via doOneCycleFor:" > > | capturingGesture | > DisplayScreen checkForNewScreenSize. > capturingGesture := false. > "self flag: #bob. " "need to consider remote hands in lower worlds" > > "process user input events" > LastCycleTime := Time millisecondClockValue. > self handsDo: [:hand | > hand becomeActiveDuring: [ > hand processEvents. > capturingGesture := capturingGesture or: [hand isCapturingGesturePoints]]]. > > "The gesture recognizer needs enough points to be accurate. > Therefore morph stepping is disabled while capturing points for the recognizer" > capturingGesture ifFalse: [ > aWorld runStepMethods. "there are currently some variations here" > self displayWorldSafely: aWorld].! ! > > !WorldState methodsFor: 'update cycle' stamp: 'ct 9/16/2020 18:47'! > doOneSubCycleFor: aWorld > > self flag: #deprecate. "ct: Historically, global state was preserved here. Since the introduction of ActiveHandVariable, this is no longer necessary, so this is equivalent to #doOneCycleFor:. However, let's keep this possibly valuable hook for now." > > ^ self doOneCycleFor: aWorld! ! > > !WorldState methodsFor: '*MorphicExtras-update cycle' stamp: 'ct 9/12/2020 15:18'! > doOneCycleInBackground > "Do one cycle of the interactive loop. This method is called repeatedly when this world is not the active window but is running in the background." > > self halt. "not ready for prime time" > > "process user input events, but only for remote hands" > self handsDo: [:hand | > (hand isKindOf: RemoteHandMorph) ifTrue: [ > hand becomeActiveDuring: [ > hand processEvents]]]. > > self runStepMethods. > self displayWorldSafely.! ! > > > !ZASMCameraMarkMorph methodsFor: 'menu' stamp: 'ct 9/11/2020 18:02'! > setTransition > "Set the transition" > > ^ self setTransition: self currentEvent! ! > > WorldState removeSelector: #activeHand! > WorldState removeSelector: #activeHand:! > > !MorphicProject reorganize! > ('display' invalidate noDisplayDuring: previewImageForm restore viewLocFor:) > ('initialize' initMorphic initialize installPasteUpAsWorld: openProject: setWorldBackground:) > ('testing' isMorphic) > ('menu messages' assureNavigatorPresenceMatchesPreference makeThumbnail) > ('utilities' addItem:toMenu:selection:color:thumbnail: composeDisplayTextIntoForm: createViewIfAppropriate do:withProgressInfoOn:label: findAFolderForProject:label: findProjectView: jumpToProject offerMenu:from:shifted: pointerMoved setAsBackground: showImage:named: textWindows) > ('flaps support' assureFlapIntegrity cleanseDisabledGlobalFlapIDsList enableDisableGlobalFlap: flapsSuppressed: globalFlapEnabledString: globalFlapWithIDEnabledString: isFlapEnabled: isFlapIDEnabled: setFlaps suppressFlapsString toggleFlapsSuppressed) > ('docking bars support' assureMainDockingBarPresenceMatchesPreference createOrUpdateMainDockingBar dockingBar dockingBar: removeMainDockingBar showWorldMainDockingBar showWorldMainDockingBar: showWorldMainDockingBarString toggleShowWorldMainDockingBar) > ('file in/out' acceptProjectDetails: armsLengthCommand:withDescription: compressFilesIn:to:in: compressFilesIn:to:in:resources: exportSegmentInSexpWithChangeSet:fileName:directory:withoutInteraction: exportSegmentWithChangeSet:fileName:directory: exportSegmentWithChangeSet:fileName:directory:withoutInteraction: loadFromServer: noteManifestDetailsIn: storeSegment storeSegmentNoFile) > ('enter' clearGlobalState enterAsActiveSubprojectWithin: finalEnterActions: finalExitActions: initializeMenus isIncompletelyLoaded resumeEventRecorder: scheduleProcessForEnter startUpActions suspendProcessForDebug terminateProcessForLeave wakeUpTopWindow) > ('squeaklet on server' enterIfThereOrFind: openBlankProjectNamed:) > ('release' deletingProject: myPlayerClasses prepareForDelete) > ('language' chooseNaturalLanguage updateLocaleDependents) > ('*Etoys-stack' currentStack currentStack:) > ('editors' bitEdit: bitEdit:at:scale: editCharacter:ofFont: formEdit: formViewClass openImage:name:saveResource:) > ('futures' future:do:at:args: future:send:at:args:) > ('protocols' currentVocabulary) > ('active process' spawnNewProcess spawnNewProcessAndTerminateOld: spawnNewProcessIfThisIsUI: uiProcess uiProcess:) > ('transcripter' displayTranscripter: initializeParagraphForTranscripter:) > ('*46Deprecated') > ('*51Deprecated') > ('subprojects' addProject: subProjects) > ('accessing' color) > ('updating' applyUserInterfaceTheme canApplyUserInterfaceTheme) > ('*Etoys-Worlds' setWorld:) > ('scheduling & debugging' addDeferredUIMessage: debuggerClass fatalDrawingError: interruptCleanUpFor: recursiveError: restoreDisplay syntaxError:) > ! > > > !Project reorganize! > ('accessing' changeSet color displayDepth displayDepth: findProjectView: forgetExistingURL labelString lastDirectory: lastSavedAtSeconds name nameAdjustedForDepth nextProject previousProject projectChangeSet renameTo: storeNewPrimaryURL: thumbnail thumbnail: transcript uiManager urlList viewSize viewSize: world) > ('active process' depth spawnNewProcessIfThisIsUI: uiProcess) > ('displaying' displayDepthChanged displaySizeChanged displayZoom: imageForm imageFormOfSize:depth: invalidate noDisplayDuring: previewImageForm restore restoreDisplay showZoom shrinkDisplay viewLocFor:) > ('file in/out' armsLengthCommand:withDescription: assureIntegerVersion bumpVersion: couldBeSwappedOut currentVersionNumber decideAboutCreatingBlank: doArmsLengthCommand: downloadUrl ensureChangeSetNameUnique exportSegmentFileName:directory: exportSegmentFileName:directory:withoutInteraction: exportSegmentInSexpWithChangeSet:fileName:directory:withoutInteraction: exportSegmentWithCatagories:classes:fileName:directory: exportSegmentWithChangeSet:fileName:directory: findAFolderToLoadProjectFrom findAFolderToStoreProjectIn fromMyServerLoad: hasBadNameForStoring htmlPagePrototype loadFromServer loadFromServer: objectForDataStream: primaryServer primaryServerIfNil: projectExtension restoreReferences revert saveAs saveForRevert serverList squeakletDirectory storeAttributeKey:value:on: storeAttributesOn: storeDataOn: storeHtmlPageIn: storeManifestFileIn: storeOnServer storeOnServerAssumingNameValid storeOnServerInnards storeOnServerShowProgressOn:forgetURL: storeOnServerWithProgressInfo storeOnServerWithProgressInfoOn: storeSegment storeSegmentNoFile storeSomeSegment storeToMakeRoom tryToFindAServerWithMe url urlForLoading versionForFileName versionFrom: versionedFileName writeFileNamed:fromDirectory:toServer: writeStackText:in:registerIn:) > ('flaps support' flapsSuppressed flapsSuppressed: showSharedFlaps) > ('initialization' initialExtent initialProject initialize installNewDisplay:depth: openProject: setChangeSet: setServer windowActiveOnFirstClick windowReqNewLabel:) > ('language' chooseNaturalLanguage localeChanged localeID naturalLanguage updateLocaleDependents) > ('menu messages' assureNavigatorPresenceMatchesPreference doWeWantToRename exit fileOut installProjectPreferences makeThumbnail saveProjectPreferences validateProjectNameIfOK:) > ('printing' addSubProjectNamesTo:indentation: printOn:) > ('project parameters' initializeProjectParameters initializeProjectPreferences noteThatParameter:justChangedTo: parameterAt: parameterAt:ifAbsent: projectParameterAt: projectParameterAt:ifAbsent: projectParameterAt:ifAbsentPut: projectParameterAt:put: projectParameters projectPreferenceAt: projectPreferenceAt:ifAbsent: projectPreferenceFlagDictionary rawParameters removeParameter:) > ('release' addDependent: canDiscardEdits close delete deletingProject: forget okToChange prepareForDelete release removeChangeSetIfPossible windowIsClosing) > ('resources' abortResourceLoading resourceDirectoryName resourceManager resourceManager: resourceUrl startResourceLoading storeResourceList:in:) > ('SuperSwiki' tellAFriend tellAFriend:) > ('*sound' beep) > ('enter' enter enter: enter:revert:saveForRevert: finalEnterActions: finalExitActions: loadState saveState scheduleProcessForEnter startUpActions terminateProcessForLeave wakeUpTopWindow) > ('utilities' addItem:toMenu:selection:color:thumbnail: buildJumpToMenu: composeDisplayTextIntoForm: do:withProgressInfoOn:label: findAFolderForProject:label: jumpToProject jumpToSelection: offerMenu:from:shifted: pointerMoved setAsBackground: showImage:named: textWindows) > ('squeaklet on server' enterIfThereOrFind: openBlankProjectNamed:) > ('futures' future:do:at:args: future:send:at:args:) > ('editors' bitEdit: bitEdit:at:scale: editCharacter:ofFont: formEdit: formViewClass openImage:name:saveResource:) > ('protocols' currentVocabulary) > ('transcripter' displayTranscripter: initializeParagraphForTranscripter:) > ('*51Deprecated') > ('sub-projects & hierarchy' addProject: beTopProject children isTopProject liftSubProjects parent setParent: subProjects withChildrenDo:) > ('enter - recovery' enterForEmergencyRecovery suspendProcessForDebug) > ('testing' isCurrentProject isIncompletelyLoaded isMorphic) > ('shrinking' removeAllOtherProjects) > ('*ST80-Testing' isMVC) > ('*Etoys-Squeakland-file in/out' defaultFolderForAutoSaving latestProjectVersionsFromFileEntries: storeOnServerWithNoInteraction storeOnServerWithNoInteractionInnards storeOnServerWithNoInteractionThenQuit storePngThumbnailIn: version: writeForExportInSexp:withSources:inDirectory:changeSet:) > ('*Etoys-Squeakland-displaying' displayExtent) > ('*Etoys-Squeakland-menu messages' displayProgressWithJump: displaySavingProgress) > ('*Etoys-Squeakland-flaps support' helpGuideIfOpen) > ('*Etoys-Squeakland-initialization' installVirtualDisplayIfNeededFor:) > ('*Etoys-Squeakland-sugar' keepSugarProperties:monitor: sugarObjectId sugarObjectId: sugarProperties sugarProperties:) > ('*Etoys-Squeakland-language' locales localesString toggleUseLocale updateLocaleDependentsGently updateLocaleDependentsWithPreviousSupplies:gently: useLocaleString) > ('*Etoys-Squeakland-accessing' name:) > ('*Etoys-Squeakland-release' okToChangeSilently) > ('*60Deprecated-accessing' setThumbnail: setViewSize:) > ('scheduling & debugging' addDeferredUIMessage: debuggerClass interruptCleanUpFor: interruptName: interruptName:message: interruptName:message:preemptedProcess: interruptName:preemptedProcess: primitiveError: recursiveError: syntaxError:) > ('*Morphic-Kernel-accessing') > ! > > PasteUpMorph removeSelector: #activeHand:! > > !Browser reorganize! > ('*46Deprecated') > ('*60Deprecated-multi-window support' classHierarchy) > ('*Etoys-Squeakland-class functions' buildClassBrowser) > ('*Etoys-Squeakland-drag and drop' overwriteDialogHierarchyChange:higher:sourceClassName:destinationClassName:methodSelector:) > ('*Etoys-Squeakland-initialize-release' browserWindowActivated) > ('*Etoys-Squeakland-message functions' buildMessageBrowser) > ('*SUnitTools-class list functions' testRunTests) > ('*SUnitTools-menus' testsClassListMenu: testsSystemCategoryMenu:) > ('*SUnitTools-system category functions' hasSystemCategoryWithTestsSelected testRunTestsCategory) > ('*services-base' browseReference: classCategoryMenuServices: classListMenuServices: messageCategoryMenuServices: methodReference optionalButtonRow selectReference:) > ('accessing' contents contents:notifying: contentsSelection couldBrowseAnyClass doItReceiver editSelection editSelection: environment newClassContents noteSelectionIndex:for: request:initialAnswer: selectEnvironment: spawn: suggestCategoryToSpawnedBrowser:) > ('annotation' annotation annotation:) > ('class comment pane' annotationForClassCommentFor: annotationForClassDefinitionFor: noCommentNagString stripNaggingAttributeFromComment:) > ('class functions' addAllMethodsToCurrentChangeSet classCommentText classDefinitionText classListMenu: classListMenu:shifted: classListMenuMore: copyClass createInstVarAccessors defineClass:notifying: editClass editComment explainSpecial: fileOutClass findMethod hierarchy makeNewSubclass plusButtonHit printOutClass removeClass renameClass shiftedClassListMenu: shiftedClassListMenuMore:) > ('class list' classIconAt: classList classListIndex classListIndex: classListIndexOf: classListSingleton createHierarchyTreeOf: defaultClassList flattenHierarchyTree:on:indent: flattenHierarchyTree:on:indent:by: flattenHierarchyTree:on:indent:by:format: hasClassSelected hierarchicalClassList recent selectClass: selectClassNamed: selectedClass selectedClassName) > ('code pane' aboutToStyle: compileMessage:notifying: showBytecodes) > ('controls' decorateButtons) > ('copying' veryDeepInner:) > ('drag and drop' dragFromClassList: dragFromMessageList: dropOnMessageCategories:at: dropOnSystemCategories:at: wantsMessageCategoriesDrop: wantsSystemCategoriesDrop:) > ('initialize-release' classListFrame: classListFrame:fromLeft:width: classListFrame:fromTop:fromLeft:width: defaultBrowserTitle frameOffsetFromTop:fromLeft:width:bottomFraction: labelString methodCategoryChanged setClass: setClass:selector: setSelector: switchesFrame: switchesFrame:fromLeft:width: systemCatSingletonKey:from: systemOrganizer: topConstantHeightFrame:fromLeft:width:) > ('message category functions' addCategory alphabetizeMessageCategories buildMessageCategoryBrowser buildMessageCategoryBrowserEditString: canShowMultipleMessageCategories categoryOfCurrentMethod changeMessageCategories: editMessageCategories fileOutMessageCategories highlightMessageList:with: mainMessageCategoryMenu: messageCategoryMenu: printOutMessageCategories removeEmptyCategories removeMessageCategory renameCategory showHomeCategory) > ('message category list' categorizeAllUncategorizedMethods hasMessageCategorySelected messageCatListSingleton messageCategoryList messageCategoryListIndex messageCategoryListIndex: messageCategoryListKey:from: messageCategoryListSelection rawMessageCategoryList recategorizeMethodSelector: selectMessageCategoryNamed: selectedMessageCategoryName setOriginalCategoryIndexForCurrentMethod toggleCategorySelectionForCurrentMethod) > ('message functions' browseAllCommentsForClass defineMessageFrom:notifying: inspectInstances inspectSubInstances mainMessageListMenu: removeMessage removeMessageFromBrowser) > ('message list' addExtraShiftedItemsTo: hasMessageSelected lastMessageName messageHelpAt: messageIconAt: messageIconFor: messageIconHelpFor: messageList messageListIndex messageListIndex: messageListIndexOf: messageListMenu:shifted: reformulateList selectMessageNamed: selectedMessage selectedMessageName selectedMessageName: shiftedMessageListMenu:) > ('metaclass' classCommentIndicated classDefinitionIndicated classMessagesIndicated classOrMetaClassOrganizer indicateClassMessages indicateInstanceMessages instanceMessagesIndicated metaClassIndicated metaClassIndicated: selectedClassOrMetaClass selectedClassOrMetaClassName setClassDefinition setClassOrganizer) > ('multi-window support' arrowKey:from: browseClassHierarchy isHierarchy isPackage multiWindowName multiWindowNameForState: okToClose restoreMultiWindowState: restoreToCategory:className:protocol:selector:mode:meta: saveMultiWindowState) > ('pluggable menus - hooks' classListMenuHook:shifted: messageCategoryMenuHook:shifted: messageListMenuHook:shifted: systemCategoryMenuHook:shifted:) > ('self-updating' didCodeChangeElsewhere) > ('system category functions' addSystemCategory alphabetizeSystemCategories browseAllClasses buildSystemCategoryBrowser buildSystemCategoryBrowserEditString: changeSystemCategories: classNotFound editSystemCategories fileOutSystemCategory findClass mainSystemCategoryMenu: printOutSystemCategory removeSystemCategory renameSystemCategory systemCatSingletonMenu: systemCategoryMenu: updateSystemCategories) > ('system category list' hasSystemCategorySelected indexIsOne indexIsOne: selectCategoryForClass: selectSystemCategory: selectedEnvironment selectedSystemCategory selectedSystemCategoryName systemCatListKey:from: systemCategoryList systemCategoryListIndex systemCategoryListIndex: systemCategorySingleton) > ('toolbuilder' buildAndOpenCategoryBrowser buildAndOpenCategoryBrowserLabel: buildAndOpenClassBrowserLabel: buildAndOpenFullBrowser buildAndOpenMessageCategoryBrowserLabel: buildCategoryBrowserWith: buildClassListSingletonWith: buildClassListWith: buildDefaultBrowserWith: buildMessageCategoryListWith: buildMessageListCatSingletonWith: buildMessageListWith: buildSwitchesWith: buildSystemCatListSingletonWith: buildSystemCategoryListWith: buildWith: setMultiWindowFor:) > ('traits' addSpecialMenu: addTrait defineTrait:notifying: newClass newTrait) > ('user interface' addModelItemsToWindowMenu: defaultWindowColor) > ('private' spawnOrNavigateTo:) > ! > > > !ActiveWorldVariable class reorganize! > ('accessing' default value:during:) > ! > > > !ActiveHandVariable class reorganize! > ('accessing' default value:during:) > ! > > > !ActiveEventVariable class reorganize! > ('accessing' default value:during:) > ! > > > !Object reorganize! > ('accessing' at: at:modify: at:put: basicAt: basicAt:put: basicSize bindWithTemp: enclosedSetElement ifNil:ifNotNilDo: ifNotNilDo: ifNotNilDo:ifNil: in: presenter readFromString: size yourself) > ('associating' ->) > ('binding' bindingOf:) > ('casing' caseOf: caseOf:otherwise:) > ('class membership' class inheritsFromAnyIn: isKindOf: isKindOf:orOf: isMemberOf: respondsTo: xxxClass) > ('comparing' closeTo: hash identityHashPrintString literalEqual: = ~=) > ('converting' adaptToFloat:andCompare: adaptToFloat:andSend: adaptToFraction:andCompare: adaptToFraction:andSend: adaptToInteger:andCompare: adaptToInteger:andSend: adaptToScaledDecimal:andCompare: asOrderedCollection asSetElement asString asStringOrText as: changeClassTo: complexContents mustBeBoolean mustBeBooleanIn: printDirectlyToDisplay withoutListWrapper) > ('copying' copy copyAddedStateFrom: copyFrom: copySameFrom: copyTwoLevel deepCopy initialDeepCopierSize postCopy shallowCopy veryDeepCopy veryDeepCopySibling veryDeepCopyUsing: veryDeepCopyWith: veryDeepFixupWith: veryDeepInner:) > ('debugging' haltIf: needsWork) > ('debugging-haltOnce' checkHaltCountExpired clearHaltOnce decrementAndCheckHaltCount decrementHaltCount doExpiredHaltCount doExpiredHaltCount: doExpiredInspectCount haltOnCount: haltOnce haltOnceEnabled haltOnce: halt:onCount: hasHaltCount inspectOnCount: inspectOnce inspectUntilCount: removeHaltCount setHaltCountTo: setHaltOnce toggleHaltOnce) > ('dependents access' addDependent: breakDependents canDiscardEdits dependents evaluate:wheneverChangeIn: hasUnacceptedEdits myDependents myDependents: release removeDependent:) > ('drag and drop' acceptDroppingMorph:event:inMorph: dragPassengerFor:inMorph: dragStartedFor:transferMorph: dragTransferTypeForMorph: wantsDroppedMorph:event:inMorph:) > ('error handling' assert: assert:descriptionBlock: assert:description: backwardCompatibilityOnly: caseError deprecated deprecated: deprecated:block: doesNotUnderstand: dpsTrace: dpsTrace:levels: dpsTrace:levels:withContext: error error: halt halt: handles: notify: notify:at: primitiveFailed primitiveFailed: shouldBeImplemented shouldNotImplement subclassResponsibility traitConflict) > ('evaluating' value valueWithArguments:) > ('filter streaming' byteEncode: drawOnCanvas: elementSeparator encodePostscriptOn: flattenOnStream: fullDrawPostscriptOn: putOn: writeOnFilterStream:) > ('flagging' isThisEverCalled isThisEverCalled: logEntry logExecution logExit) > ('graph model' addModelYellowButtonMenuItemsTo:forMorph:hand: hasModelYellowButtonMenuItems) > ('macpal' codeStrippedOut: contentsChanged flash instanceVariableValues isUniversalTiles objectRepresented refusesToAcceptCode scriptPerformer slotInfo) > ('message handling' executeMethod: perform: perform:orSendTo: perform:with:with:with:with: perform:with:with:with:with:with: perform:withArguments: perform:withArguments:inSuperclass: perform:withEnoughArguments: perform:with: perform:with:with: perform:with:with:with: withArgs:executeMethod: with:executeMethod: with:with:executeMethod: with:with:with:executeMethod: with:with:with:with:executeMethod:) > ('objects from disk' comeFullyUpOnReload: convertToCurrentVersion:refStream: fixUponLoad:seg: objectForDataStream: readDataFrom:size: saveOnFile saveOnFileNamed: storeDataOn:) > ('printing' fullPrintString isLiteral longPrintOn: longPrintOn:limitedTo:indent: longPrintString longPrintStringLimitedTo: nominallyUnsent: printOn: printString printStringLimitedTo: printWithClosureAnalysisOn: reportableSize storeOn: storeString stringForReadout stringRepresentation) > ('scripting' adaptedToWorld: defaultFloatPrecisionFor: evaluateUnloggedForSelf: methodInterfacesForCategory:inVocabulary:limitClass: methodInterfacesForInstanceVariablesCategoryIn: methodInterfacesForScriptsCategoryIn: selfWrittenAsIll selfWrittenAsIm selfWrittenAsMe selfWrittenAsMy selfWrittenAsThis) > ('system primitives' asOop become: becomeForward: becomeForward:copyHash: className creationStamp instVarAt: instVarAt:put: instVarNamed: instVarNamed:put: oopString primitiveChangeClassTo: rootStubInImageSegment: someObject) > ('testing' beViewed belongsToUniClass costumes haltIfNil isArray isBehavior isBlock isBoolean isCharacter isClassReference isClosure isCodeReference isCollection isColor isColorForm isCompiledCode isCompiledMethod isComplex isContext isDictionary isFloat isForm isFraction isHeap isInteger isInterval isMessageSend isMethodContext isMethodProperties isMethodReference isMorph isMorphicEvent isMorphicModel isNumber isPlayer isPoint isPrimitiveCostume isPromise isRectangle isScriptEditorMorph isSketchMorph isStream isString isSymbol isSystemWindow isText isTextView isTrait isTransparent isVariableBinding isWebBrowser isWindowForModel: knownName name nameForViewer renameInternal: renameTo: shouldBePrintedAsLiteral shouldBePrintedAsLiteralVisiting: showDiffs stepAt:in: stepIn: stepTime stepTimeIn: vocabularyDemanded wantsDiffFeedback wantsSteps wantsStepsIn:) > ('updating' changed changed: changed:with: handledListVerification noteSelectionIndex:for: okToChange okToClose updateListsAndCodeIn: update: update:with: windowIsClosing) > ('user interface' addModelItemsToWindowMenu: addModelMenuItemsTo:forMorph:hand: asExplorerString defaultLabelForInspector launchPartVia: launchPartVia:label: launchTileToRefer modelSleep modelWakeUp modelWakeUpIn: mouseUpBalk: notYetImplemented windowActiveOnFirstClick windowReqNewLabel:) > ('*monticello' isConflict) > ('*services-base' requestor) > ('*system-support' isPrimitiveError systemNavigation) > ('*Tools-Explorer' explore exploreWithLabel:) > ('*tools-browser' browseHierarchy) > ('private' errorImproperStore errorNonIntegerIndex errorNotIndexable errorSubscriptBounds: setPinned: species storeAt:inTempFrame:) > ('thumbnail' iconOrThumbnailOfSize:) > ('*Morphic-Explorer' explorerContents hasContentsInExplorer) > ('futures' future future: futureDo:at:args: futureSend:at:args:) > ('*Etoys-viewer' assureUniClass browseOwnClassSubProtocol categoriesForViewer: categoriesForVocabulary:limitClass: chooseNewNameForReference defaultLimitClassForVocabulary: defaultNameStemForInstances elementTypeFor:vocabulary: externalName graphicForViewerTab hasUserDefinedSlots infoFor:inViewer: initialTypeForSlotNamed: isPlayerLike methodInterfacesInPresentationOrderFrom:forCategory: newScriptorAround: newTileMorphRepresentative offerViewerMenuFor:event: offerViewerMenuForEvt:morph: renameScript: tilePhrasesForCategory:inViewer: tilePhrasesForMethodInterfaces:inViewer: tilePhrasesForSelectorList:inViewer: tileToRefer uniqueInstanceVariableNameLike:excluding: uniqueNameForReference uniqueNameForReferenceFrom: uniqueNameForReferenceOrNil updateThresholdForGraphicInViewerTab usableMethodInterfacesIn:) > ('*Protocols' currentVocabulary haveFullProtocolBrowsed haveFullProtocolBrowsedShowingSelector:) > ('*Etoys-tiles' basicType tearOffTile) > ('*Tools-MessageSets' browseAllCallsOn: browseAllImplementorsOf:) > ('*Tools-multi-window support' canHaveUnacceptedEdits) > ('*morphic' asDraggableMorph asMorph asStringMorph asTextMorph isPluggableListMorph openAsMorph) > ('*MorphicExtras-Undo' capturedState commandHistory purgeAllCommands redoFromCapturedState: refineRedoTarget:selector:arguments:in: refineUndoTarget:selector:arguments:in: rememberCommand: rememberUndoableAction:named: undoFromCapturedState:) > ('*MorphicExtras-PartsBin' descriptionForPartsBin) > ('tracing' inboundPointers inboundPointersExcluding: outboundPointers outboundPointersDo:) > ('*Tools-Debugger' canonicalArgumentName chasePointers explorePointers shouldFollowOutboundPointers) > ('*System-Object Events-accessing' actionForEvent: actionForEvent:ifAbsent: actionMap actionSequenceForEvent: actionsDo: createActionMap hasActionForEvent: setActionSequence:forEvent: updateableActionMap) > ('*System-Change Notification-events' actionsWithReceiver:forEvent: renameActionsWithReceiver:forEvent:toEvent:) > ('*System-Change Notification-converting' asActionSequence asActionSequenceTrappingErrors) > ('*System-Tools-breakpoint' break) > ('*ToolBuilder-Kernel-error handling' confirm: confirm:orCancel:) > ('*EToys-user interface' eToyStreamedRepresentationNotifying:) > ('*ToolBuilder-Kernel-user interface' inform:) > ('*System-Support-user interface' initialExtent) > ('*System-Localization-locales' localeChanged localeChangedGently translatedNoop) > ('*System-Object Events-accessing-removing' releaseActionMap removeAction:forEvent: removeActionsForEvent: removeActionsSatisfying: removeActionsSatisfying:forEvent: removeActionsWithReceiver: removeActionsWithReceiver:forEvent:) > ('*System-Object Events-accessing-triggering' triggerEvent: triggerEvent:ifNotHandled: triggerEvent:with: triggerEvent:with:ifNotHandled: triggerEvent:withArguments: triggerEvent:withArguments:ifNotHandled:) > ('*System-Object Events-accessing-registering' when:evaluate: when:send:to: when:send:to:with: when:send:to:withArguments:) > ('*Tools-inspecting') > ('*Traits' explicitRequirement requirement) > ('*Graphics-KernelExtensions' fullScreenSize) > ('*System-Finalization' actAsExecutor executor finalizationRegistry finalize hasMultipleExecutors retryWithGC:until: toFinalizeSend:to:with:) > ('*collections' asLink compareSafely:) > ('*Tools-Browsing' browse) > ('*System-Recovery-error handling' primitiveError:) > ('*SUnit-testing' isTestClass) > ('*51Deprecated') > ('*Morphic-Events-Filtering' filterEvent:for:) > ('*System-Preferences' applyUserInterfaceTheme canApplyUserInterfaceTheme userInterfaceTheme) > ('*Etoys-Squeakland-accessing' customizeExplorerContents) > ('*Etoys-Squeakland-comparing' eToysEQ: eToysGE: eToysGT: eToysLE: eToysLT: eToysNE:) > ('*Etoys-Squeakland-error handling' eToysError:) > ('*Etoys-Squeakland-Etoys-viewer' hasUserDefinedScripts) > ('*Etoys-Squeakland-translation support' inline:) > ('*Etoys-Squeakland-testing' isReallyString) > ('*Etoys-Squeakland-user interface' launchPartOffsetVia:label:) > ('*Etoys-Squeakland-translating' literalStringsDo:) > ('*Etoys-Squeakland-Tweak-Kedama' test:ifTrue:ifFalse: times:repeat:) > ('*Tools' environment) > ('*60Deprecated-copying' clone) > ('pinning' isPinned pin unpin) > ('*EToys-Scripting-accessing' addInstanceVarNamed:withValue: basicAddInstanceVarNamed:withValue:) > ('*60Deprecated-Tools' exploreAndYourself notifyWithLabel:) > ('literals' allLiteralsDo: hasLiteral: hasLiteralSuchThat:) > ('write barrier' attemptToAssign:withIndex: beReadOnlyObject beWritableObject isReadOnlyObject setIsReadOnlyObject:) > ('*Tools-Inspector' basicInspect inspect inspectWithLabel: inspectorClass) > ('*Morphic-Kernel-accessing' activeHand currentEvent currentHand currentWorld) > ! > > > > > Content-Description: filein-updateui.1.cs > 'From Squeak6.0alpha of 15 September 2020 [latest update: #19851] on 25 September 2020 at 7:32 pm'! !ChangeSet class methodsFor: 'file list services' stamp: 'ct 9/25/2020 19:24'! serviceFileIntoNewChangeSet "Answer a service for installing a file into a new change set" ^ SimpleServiceEntry provider: self label: 'install into new change set' translated selector: #fileIntoNewChangeSet: description: 'install the file as a body of code in the image: create a new change set and file-in the selected file into it' translated buttonLabel: 'install'! ! !ChangeSet class methodsFor: 'file list services' stamp: 'ct 9/25/2020 19:26'! serviceFileIntoNewChangeSetWithoutUpdatingUI "Answer a service for installing a file into a new change set without updating the UI." ^ SimpleServiceEntry provider: self label: 'install into new change set - without updating UI' translated selector: #fileIntoNewChangeSetWithoutUpdatingUI: description: 'Install the file as a body of code in the image: create a new change set and file-in the selected file into it. Don''t update the UI in the meantime. Recommended when loading change sets that touch critical UI services.' translated buttonLabel: 'install without updating UI' translated! ! !ChangeSet class methodsFor: 'file list services' stamp: 'ct 9/25/2020 19:25'! services ^ { self serviceFileIntoNewChangeSet. self serviceFileIntoNewChangeSetWithoutUpdatingUI }! ! !ChangeSet class methodsFor: 'services' stamp: 'ct 9/25/2020 19:27'! fileIntoNewChangeSet: fullName "File in all of the contents of the currently selected file, if any, into a new change set." ^ self fileIntoNewChangeSet: fullName updateUI: true! ! !ChangeSet class methodsFor: 'services' stamp: 'ct 9/25/2020 19:27'! fileIntoNewChangeSet: fullName updateUI: updateUI "File in all of the contents of the currently selected file, if any, into a new change set." | fn ff | fullName ifNil: [^ Beeper beep]. ff := FileStream readOnlyFileNamed: (fn := GZipReadStream uncompressedFileName: fullName). ^ ChangeSet newChangesFromStream: ff named: (FileDirectory localNameFor: fn) updateUI: updateUI! ! !ChangeSet class methodsFor: 'services' stamp: 'ct 9/25/2020 19:28'! fileIntoNewChangeSetWithoutUpdatingUI: fullName "File in all of the contents of the currently selected file, if any, into a new change set. Don't update the UI during filein. Useful when loading change sets that touch critical UI services." ^ self fileIntoNewChangeSet: fullName updateUI: false! ! !ChangeSet class methodsFor: 'services' stamp: 'ct 9/25/2020 19:21'! newChangesFromStream: aStream named: aName ^ self newChangesFromStream: aStream named: aName updateUI: true! ! !ChangeSet class methodsFor: 'services' stamp: 'ct 9/25/2020 19:16'! newChangesFromStream: aStream named: aName updateUI: updateUI "File in the code from the stream into a new change set whose name is derived from aName. Leave the current change set unchanged. Return the new change set or nil on failure." | oldChanges newName newSet | oldChanges := ChangeSet current. PreviousSet := oldChanges name. "so a Bumper update can find it" newName := aName sansPeriodSuffix. newSet := self basicNewChangeSet: newName. [ | newStream | newSet ifNotNil: [ newStream := (aStream respondsTo: #converter:) ifTrue: [aStream] ifFalse: [(MultiByteBinaryOrTextStream with: (aStream contentsOfEntireFile)) reset]. self newChanges: newSet. newStream setConverterForCode; fileInAnnouncing: ('Loading {1}...' translated format: {newName}) silently: false updateUI: updateUI. RemarkNotification signal: ('File {1} successfully filed in to change set {2}' translated format: {aName. newName})]. aStream close ] ensure: [ self newChanges: oldChanges ]. PreviousSet := nil. ^ newSet! ! !PositionableStream methodsFor: '*System-Changes-fileIn/Out' stamp: 'ct 9/25/2020 19:17'! fileInAnnouncing: announcement ^ self fileInAnnouncing: announcement silently: false updateUI: true! ! !PositionableStream methodsFor: '*System-Changes-fileIn/Out' stamp: 'ct 9/25/2020 19:19'! fileInAnnouncing: announcement silently: silently updateUI: updateUI ^ self fileInFor: nil announcing: announcement silently: silently updateUI: updateUI! ! !PositionableStream methodsFor: '*System-Changes-fileIn/Out' stamp: 'ct 9/25/2020 19:19'! fileInFor: client announcing: announcement ^ self fileInFor: client announcing: announcement silently: true updateUI: true! ! !PositionableStream methodsFor: '*System-Changes-fileIn/Out' stamp: 'ct 9/25/2020 19:18'! fileInFor: client announcing: announcement silently: silently updateUI: updateUI "This is special for reading expressions from text that has been formatted with exclamation delimitors. The expressions are read and passed to the Compiler. Answer the result of compilation. Put up a progress report with the given announcement as the title. If not silently, every change record will be logged to the changes file. If updateUI is true, the progress report will be updated after each processed change record." | fileInBlock val chunk | fileInBlock := [ self skipSeparators. [val := (self peekFor: $!!) ifTrue: [(Compiler evaluate: self nextChunk for: client logged: false) scanFrom: self] ifFalse: [ chunk := self nextChunk. self checkForPreamble: chunk. Compiler evaluate: chunk for: client logged: silently not] ] on: InMidstOfFileinNotification do: [:ex | ex resume: true]. self skipStyleChunk]. [updateUI ifTrue: [ announcement displayProgressFrom: 0 to: self size during: [:bar | [self atEnd] whileFalse: [ bar value: self position. fileInBlock value]] ] ifFalse: [ Project uiManager informUser: announcement during: [ [self atEnd] whileFalse: fileInBlock] ]. ] ensure: [self close]. "Note: The main purpose of this banner is to flush the changes file." Smalltalk logChange: '----End fileIn of ' , self name , '----'. ^ val! ! !PositionableStream methodsFor: '*System-Changes-fileIn/Out' stamp: 'ct 9/25/2020 19:20'! fileInSilentlyAnnouncing: announcement ^ self fileInAnnouncing: announcement silently: false updateUI: false! ! PositionableStream removeSelector: #fileInAnnouncing:silently:! ChangeSet class removeSelector: #fileIntoNewChangeSet:silently:! > From squeaklist at gmail.com Fri Sep 25 23:55:12 2020 From: squeaklist at gmail.com (Kjell Godo) Date: Fri, 25 Sep 2020 16:55:12 -0700 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: could this text be included in this Squeak PharoLauncher somehow as a help text On Fri, Sep 25, 2020 at 14:03 Jakob Reschke wrote: > Hello, > > Here is what I had to do to have the Pharo launcher launch a Squeak image: > > - Download and extract a Squeak VM and a supported image with .changes. > - Download and install the latest Pharo Launcher release. > - Start the launcher. > - Open Settings > Pharo Launcher > edit the folder paths to my likings. > - Click Store Settings at the top right. > - Move the Squeak VM directory below the VM directory entered in the > settings. Do not move the image yet. > - Press shift+return, type vmExecutableName and open the method > WindowsResolver>>#vmExecutableName. > - Change WindowsResolver>>#vmExecutableName to return 'Squeak.exe'. > - Close the system browser window. > - Press the VMs button on the top right. It should now list the Squeak > VM, for example 'squeak.cog.spur_win64x64_202003021730'. Close the VMs > window. > - Press Import at the top, choose the extracted image. It will be > moved to the images directory from the Settings! > - The image should now appear in the main list of the launcher window. > - In the new directory of the image, put a file named "pharo.version" > with the contents "0" next to the image. This will prevent the > launcher from trying to find out the Pharo version of the image by > running a pharo-version.st script with --headless on the image. Squeak > does not support this. > - Back in the launcher, in the combo box next to the Launch button at > the top, drop down the list and select Edit Configurations. > - For the Default configuration, select your Squeak VM instead of > "0-x64" on the right. The latter would try and fail to download a > fitting Pharo VM again. > - Close the window with "Save & Select". > - Now I am able to launch the Squeak image. > > To preserve the changes to the launcher image: > - Press shift+return, enter Playground and press return to open a > Workspace. > - Evaluate the following: Smalltalk snapshot: true andQuit: true. > > Kind regards, > Jakob > > > Am Mi., 2. Sept. 2020 um 18:20 Uhr schrieb Sean DeNigris > : > > > > I've been pontificating about unity between dialects, convinced more > than ever that we need each other and have more of a simple (not easy) > communication problem than anything else, and pestering the principals of > various communities, so I decided to put some concrete action behind it - > some "skin in the game" as we say in the U.S. > > > > After reading Trygve's frustration with Squeak image/VM management, and > Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage > and run Squeak (and GToolkit) VMs/images. > > > > The current limitation vs. launching standard Pharo images is that you > have to download the VMs and images and manually install them due to > differences in URL and other conventions. However once the files are in > place, you can create templates allowing images to be created at will from > different Squeak versions and automatically run with the correct VM. > Auto-installation could probably be done if someone cares enough but it > scratched my itch for the moment to manage GT images. I’m sure Cuis support > could be added, but the hooks are now available and someone more familiar > with its VM/image installation might have an easier time. > > > > Until my latest PR [1] is merged, brave souls can play with it and give > feedback by loading my issue branch [2] into the latest Launcher release > [3]. NB only tested on Mac. > > > > It seemed Trygve was at his wits' end, but maybe this and other small > gestures will let him know how important he is to our community if not > motivate him to resume his important work. > > > > I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what > small action can you take today that unites us rather than divides us? We > can be each others' supporters even if technical, vision and other > differences keep our codebases somewhat distinct. I believe the seeds of > wars are planted when I lack the grace to give those around me the benefit > of the doubt that they mean well and are trying their best. There is more > than enough of that already in the world. Let’s invent a *better* future… > > > > Your brother, > > Sean > > > > 1. https://github.com/pharo-project/pharo-launcher/pull/503 > > 2. > https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks > > 3. https://pharo.org/download > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Sat Sep 26 01:29:41 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Fri, 25 Sep 2020 18:29:41 -0700 Subject: [squeak-dev] We need an Applications tab on squeak.org Message-ID: Hi All, I propose that we add an Applications tab on squek.org and in that page we have a brief description of, the logo/iconography of, and URL of any and all commercial projects currently built with Squeak. Tim's company Sage Tea is perhaps transitioning a part of its solution to Squeak from VisualWorks. 3D ICC's Terf is all built on Squeak. Who else is building commercial applications on Squeak? I bet there are a few. We should not keep the commercial success of Squeak in stealth mode, We should broadcast it far and wide. If you are, then please, if you're part of the Squeak.org design team, start building this tab, if you're an application developer and/or owner, please mention your project here, and/or add it to the Applications tab as soon as it goes live. Fabio Neiphaus and others have built a very nice process for updating the website. One clones https://github.com/squeak-smalltalk/squeak.org.git, edits the checkout of the clone, commits to one's clone, and submits a pull request. Who's with me? _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From robert.withers at pm.me Sat Sep 26 01:42:14 2020 From: robert.withers at pm.me (Robert Withers) Date: Sat, 26 Sep 2020 01:42:14 +0000 Subject: [squeak-dev] Multiple processes and morphic state (was Re: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <20200914154934.GA85344@shell.msen.com> <495a43a9-69fa-1758-7f03-9e712067d98b@leastfixedpoint.com> Message-ID: <1013fa15-717b-71bb-e6b3-d9ecae8a08c0@pm.me> Hi Tony, I am excited to conversate with you, regarding Promises. Vollständig geil! Von: Tony Garnock-Jones [](mailto:tonyg at leastfixedpoint.com) > Gesendet: Freitag, 25. September 2020 16:55:23 > An: The general-purpose Squeak developers list; Thiede, Christoph > Betreff: Multiple processes and morphic state (was Re: Changeset: Eliminating global state from Morphic) > Hi Christoph, > > On 9/24/20 2:08 PM, Thiede, Christoph wrote: >>> It's true that in Morphic there is one distinct Process associated >> with the user interface for each Morphic world. >> >> I'm not so sure about this. You can always do forks within UI code, so >> sometimes there are also multiple Processes within one Morphic world. >> Just think about Shout's background process or debugging processes, and >> please keep in mind that Jakob justifiably proposed to renew the process >> handling for non-modal dialogs, which maybe could also result in >> multiple processes being active at the same time. >> A world can have multiple hands, for example, RemoteHandMorphs >> (Nebraska), event-simulating hands (EventRecorder), or just >> multiple plain HandMorphs as demonstrated by Tony recently in >> his PostmarketOS implementation. There is no reason for these hands to >> run on the same process. Analogously, I don't see why we should restrict >> a hand not to be thread-safe, so the hand-event relation is 1:n again. > > I've found that while, yes, there are lots of opportunities for > concurrency in Morphic, actually *taking* those opportunities results in > all sorts of problems. > > Morphic state is I think not thread-safe enough to allow multiple > processes "inside" of it at once. > > So I've ended up being *very* careful to serialize all Morphic access > within the one, main UI process. Where other processes (Actors) exist, I > send messages from the UI process to the others, and when Morphic state > change code has to run, I package it up into a block and enqueue it for > later execution by the UI process rather than doing it in each Actor. > > (It's painful, actually, having to be so careful about it, in such a > C-style "you do all the work yourself and the system won't help you" > manner. Maybe there's something we can improve here?) > > So I think it's *morally* true that "there is one distinct Process > associated with the user interface for each Morphic world," even if as a > rule it can be bent a little :-) This would be sa-super spot to put a Vat, running an event loop, with a proscribed way to submit a new context through its queue. I have gotten super-lazy about it, am watching Jen Aniston's show The Morning Show and other avoidance behaviors (Totally Awsome!). I am halfway through getting tests to work. I maybe 65% there in porting ELibs PromisesLocal as the implementation behind your Promise/A* protocol interface. The main issue is your Promise can immediately resolve to a value, whereas ELibs event loop has to process the resolution async, so many tests need a small delay to allow the Promise to resolve and process its traffic (#whenResolved:). I am having some trouble with figuring out my own process architecture and services. I believe I have the event loop restarting if it blows up, but with a loop guard requiring a #running state (#stopping isFalse). I believe I am passing Halts up the stack. I am trying to reschedule the context copy through the vat, when a VatSemaphore is signaled. Trying to create a pool of event pending contexts to get rescheduled so, my efforts are not quite right and it is Process/Exception handling. Extremely complicated for Noobs. I am unsure! My plan was to port PromisesLocal over and onto and get all the PromiseTests tests running. As I was needing to modify them for the async resolution, I was going to get them all passing, then unload the PromisesLocal and verify the Kernel-Tests work in the updated trunk. Then publish the Tests to the Inbox, would be my first time! Make sure all is coebatany, then publish PromisesLocal for considerations of bringing that ELib model into trunk Squeak, with all of its corresponding PromisesLocal Tests #Green. I am proposing a deep change, in an area you developed and extended into your Actors model. It underlies a greeat work orf yours, so geil to always see happening in Squeak!!! Again, vollständig geil! My PromisesRemote brings a remote messaging solution. I am unsure whether your Promises or Actors are remote aware. PromisesRemote are #green (aside from 3-way and the Galaxy Object Scenario [1] being broken). Its core are the proxy references that mutate through their state, forwarding all traffic, at the right time. Speaking of only PromisesLocal, as to her ability to reimplement your Promise; having a Resolver object is powerful and a good place to control the manner in which a value manifests. All activity outgoing or incoming is interleaved through the Vat's priority queues. It is a strong guarantee of thread-safe activity. The #becoming of Promises into References or Broken, is an interesting feature that really helps one chain pipelined promises, is PPPS [2]. It's core is the EventualProcess running contexts, as the Vat pumps them in priority [#1-#4] order. [1] Galaxy Object Scenario - https://www.dropbox.com/s/5rxwno7heimimx2/The%20GalaxyObject%20scenario%20repaired.pdf?dl=0 [2] PPS - PromisePipeliningProgrammingStyle Kindly, Robert > Cheers, > Tony -- K, r -------------- next part -------------- An HTML attachment was scrubbed... URL: From robert.withers at pm.me Sat Sep 26 02:08:28 2020 From: robert.withers at pm.me (Robert Withers) Date: Sat, 26 Sep 2020 02:08:28 +0000 Subject: [squeak-dev] Multiple processes and morphic state (was Re: Changeset: Eliminating global state from Morphic) In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <20200914154934.GA85344@shell.msen.com> <495a43a9-69fa-1758-7f03-9e712067d98b@leastfixedpoint.com> Message-ID: <5803901a-7981-5533-1bda-5182bd7b7732@pm.me> My current #RED code in progress to these ends are: http://www.squeaksource.com/Oceanside/PromisesLocal-rww.12.mcz http://www.squeaksource.com/Oceanside/PromisesLocal-KernelTests-rww.383.mcz K, r On 9/25/20 9:42 PM, Robert Withers wrote: > Hi Tony, I am excited to conversate with you, regarding Promises. Vollständig geil! > > Von: Tony Garnock-Jones [](mailto:tonyg at leastfixedpoint.com) > >> Gesendet: Freitag, 25. September 2020 16:55:23 >> An: The general-purpose Squeak developers list; Thiede, Christoph >> Betreff: Multiple processes and morphic state (was Re: Changeset: Eliminating global state from Morphic) >> Hi Christoph, >> >> On 9/24/20 2:08 PM, Thiede, Christoph wrote: >>>> It's true that in Morphic there is one distinct Process associated >>> with the user interface for each Morphic world. >>> >>> I'm not so sure about this. You can always do forks within UI code, so >>> sometimes there are also multiple Processes within one Morphic world. >>> Just think about Shout's background process or debugging processes, and >>> please keep in mind that Jakob justifiably proposed to renew the process >>> handling for non-modal dialogs, which maybe could also result in >>> multiple processes being active at the same time. >>> A world can have multiple hands, for example, RemoteHandMorphs >>> (Nebraska), event-simulating hands (EventRecorder), or just >>> multiple plain HandMorphs as demonstrated by Tony recently in >>> his PostmarketOS implementation. There is no reason for these hands to >>> run on the same process. Analogously, I don't see why we should restrict >>> a hand not to be thread-safe, so the hand-event relation is 1:n again. >> >> I've found that while, yes, there are lots of opportunities for >> concurrency in Morphic, actually *taking* those opportunities results in >> all sorts of problems. >> >> Morphic state is I think not thread-safe enough to allow multiple >> processes "inside" of it at once. >> >> So I've ended up being *very* careful to serialize all Morphic access >> within the one, main UI process. Where other processes (Actors) exist, I >> send messages from the UI process to the others, and when Morphic state >> change code has to run, I package it up into a block and enqueue it for >> later execution by the UI process rather than doing it in each Actor. >> >> (It's painful, actually, having to be so careful about it, in such a >> C-style "you do all the work yourself and the system won't help you" >> manner. Maybe there's something we can improve here?) >> >> So I think it's *morally* true that "there is one distinct Process >> associated with the user interface for each Morphic world," even if as a >> rule it can be bent a little :-) > > This would be sa-super spot to put a Vat, running an event loop, with a proscribed way to submit a new context through its queue. > > I have gotten super-lazy about it, am watching Jen Aniston's show The Morning Show and other avoidance behaviors (Totally Awsome!). I am halfway through getting tests to work. > > I maybe 65% there in porting ELibs PromisesLocal as the implementation behind your Promise/A* protocol interface. The main issue is your Promise can immediately resolve to a value, whereas ELibs event loop has to process the resolution async, so many tests need a small delay to allow the Promise to resolve and process its traffic (#whenResolved:). > > I am having some trouble with figuring out my own process architecture and services. I believe I have the event loop restarting if it blows up, but with a loop guard requiring a #running state (#stopping isFalse). I believe I am passing Halts up the stack. I am trying to reschedule the context copy through the vat, when a VatSemaphore is signaled. Trying to create a pool of event pending contexts to get rescheduled so, my efforts are not quite right and it is Process/Exception handling. Extremely complicated for Noobs. I am unsure! > > My plan was to port PromisesLocal over and onto and get all the PromiseTests tests running. As I was needing to modify them for the async resolution, I was going to get them all passing, then unload the PromisesLocal and verify the Kernel-Tests work in the updated trunk. Then publish the Tests to the Inbox, would be my first time! Make sure all is coebatany, then publish PromisesLocal for considerations of bringing that ELib model into trunk Squeak, with all of its corresponding PromisesLocal Tests #Green. > > I am proposing a deep change, in an area you developed and extended into your Actors model. It underlies a greeat work orf yours, so geil to always see happening in Squeak!!! Again, vollständig geil! > > My PromisesRemote brings a remote messaging solution. I am unsure whether your Promises or Actors are remote aware. PromisesRemote are #green (aside from 3-way and the Galaxy Object Scenario [1] being broken). Its core are the proxy references that mutate through their state, forwarding all traffic, at the right time. > > Speaking of only PromisesLocal, as to her ability to reimplement your Promise; having a Resolver object is powerful and a good place to control the manner in which a value manifests. All activity outgoing or incoming is interleaved through the Vat's priority queues. It is a strong guarantee of thread-safe activity. The #becoming of Promises into References or Broken, is an interesting feature that really helps one chain pipelined promises, is PPPS [2]. It's core is the EventualProcess running contexts, as the Vat pumps them in priority [#1-#4] order. > > [1] Galaxy Object Scenario - https://www.dropbox.com/s/5rxwno7heimimx2/The%20GalaxyObject%20scenario%20repaired.pdf?dl=0 > [2] PPS - PromisePipeliningProgrammingStyle > > Kindly, > Robert > >> Cheers, >> Tony > > -- > K, r -- K, r -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Sat Sep 26 02:14:12 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sat, 26 Sep 2020 02:14:12 0000 Subject: [squeak-dev] The Trunk: Morphic-eem.1686.mcz Message-ID: Eliot Miranda uploaded a new version of Morphic to project The Trunk: http://source.squeak.org/trunk/Morphic-eem.1686.mcz ==================== Summary ==================== Name: Morphic-eem.1686 Author: eem Time: 25 September 2020, 7:14:07.295188 pm UUID: 203a1355-2c2d-4600-a378-868b5c5fff82 Ancestors: Morphic-eem.1685 Make TextEditor>>starteArray[Put:] robust when used on uninitialized or stale instances. =============== Diff against Morphic-eem.1685 =============== Item was changed: ----- Method: TextEditor>>stateArray (in category 'initialize-release') ----- stateArray + ^[ {ChangeText. - ^ {ChangeText. FindText. history ifNil: [TextEditorCommandHistory new]. "Convert old instances" self markIndex to: self pointIndex - 1. self startOfTyping. emphasisHere. + lastParenLocation}] + on: MessageNotUnderstood + do: [:ex| ex resume: nil]! - lastParenLocation}! Item was changed: ----- Method: TextEditor>>stateArrayPut: (in category 'initialize-release') ----- stateArrayPut: stateArray - | sel | ChangeText := stateArray at: 1. FindText := stateArray at: 2. history := stateArray at: 3. + (stateArray at: 4) ifNotNil: [:sel| self selectFrom: sel first to: sel last]. - sel := stateArray at: 4. - self selectFrom: sel first to: sel last. beginTypeInIndex := stateArray at: 5. emphasisHere := stateArray at: 6. lastParenLocation := stateArray at: 7! From tim at rowledge.org Sat Sep 26 04:08:43 2020 From: tim at rowledge.org (tim Rowledge) Date: Fri, 25 Sep 2020 21:08:43 -0700 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: Message-ID: > On 2020-09-25, at 6:29 PM, Eliot Miranda wrote: > > Tim's company Sage Tea is perhaps transitioning a part of its solution to Squeak from VisualWorks. No perhaps at all; definitely. All of it. > > Who's with me? I am Farticus! tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: LOW: Launch on Warning From karlramberg at gmail.com Sat Sep 26 08:41:43 2020 From: karlramberg at gmail.com (karl ramberg) Date: Sat, 26 Sep 2020 10:41:43 +0200 Subject: [squeak-dev] Font Handles (was: Inspector Custom Value Panes & Related) In-Reply-To: References: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de> Message-ID: Hi, I tested using PluggableCheckBoxMorph but that widget is unwieldy. I'm not sure how to add a label to it. The widget seem to expect the model eg. FontChooserTool to perform the label ? PluggableCheckBoxMorph>>on: anObject getState: getStateSel action: actionSel label: labelSel menu: menuSel ... self label: (self model perform: labelSel). I'm not sure how that is supposed to work... There is no other use of the widget in the image I can look at either. Best, Karl On Tue, Sep 8, 2020 at 9:57 PM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Great ideas! I never get the right handle on the first try, merging them > sounds very reasonable. :-) > > Hm ... doesn't the existence of such an "Apply" button impede Morphic's > liveness? Why not just apply all changes as soon as a font/style has been > selected? You could replace the buttons by a checkbox "selection only". > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von karl ramberg > *Gesendet:* Dienstag, 8. September 2020 21:49 Uhr > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] Inspector Custom Value Panes & Related > > > > On Tue, Sep 8, 2020 at 8:16 AM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Hi Karl, >> >> >> I'm sorry your proposal is lost a bit - I think it's a nice addition! It >> does not look exactly like the TextMorph dialog, but I actually prefer "one >> dialog that fits it all" over the three separate handles for a TextMorph >> (that do not even all appear to work properly at the moment.) I think we >> should get this merged! :-) >> >> >> My change just adds handle support to the already existing > StringMorph>>changeFont > > The three TextMorph handles are not optimal and could be refactored into > one handle. > > I made another change to FontChooserTool where I added a button to apply > text changes to the partition of text selected or a bulk change to all text > in the pane. With this change I also enabled the emphasis pane to > FontChooserTool. > > [image: bild.png] > > Best, > Karl > > > Best, >> >> Christoph >> >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von karl ramberg >> *Gesendet:* Donnerstag, 3. September 2020 13:28:41 >> *An:* The general-purpose Squeak developers list >> *Betreff:* Re: [squeak-dev] Inspector Custom Value Panes & Related >> >> Here is a change set that adds HaloHandle for font change to StringMorph >> >> Best, >> Karl >> >> On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel >> wrote: >> >>> Hi Eric. >>> >>> > What is the proper way to make a "custom value pane" for my objects >>> whenever they appear inside an Inspector? >>> >>> Unfortunately, the current inspector framework has no such extension >>> point. You can, however, prepare a custom inspector for your domain >>> objects. See MorphInspector as an example. >>> >>> Best, >>> Marcel >>> >>> Am 02.09.2020 00:10:56 schrieb Eric Gade : >>> Hi all, >>> >>> What is the proper way to make a "custom value pane" for my objects >>> whenever they appear inside an Inspector? For my RISCV work, I'm looking to >>> show a morphic representation of the bitfields when a given instruction's >>> `self` property is highlighted in the Inspector window. >>> >>> As a related question, what's the best way to make a composed Morph that >>> has StringMorphs of different fonts, sizes, and alignments? It seems like >>> not all Fonts are available to StringMorph (TrueType ones, for example) -- >>> is that correct? >>> >>> Thanks! >>> >>> -- >>> Eric >>> >>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 21823 bytes Desc: not available URL: From lecteur at zogotounga.net Sat Sep 26 09:01:49 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 26 Sep 2020 11:01:49 +0200 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: Message-ID: Would muO count as an application? It is not commercial but it has proven successful at the task it was designed for, namely composing music. http://www.zogotounga.net/comp/squeak/sqgeo.htm https://stephanerollandin.bandcamp.com/ Stef From lists at fniephaus.com Sat Sep 26 11:22:17 2020 From: lists at fniephaus.com (Fabio Niephaus) Date: Sat, 26 Sep 2020 13:22:17 +0200 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: Message-ID: On Sat, Sep 26, 2020 at 11:01 AM Stéphane Rollandin wrote: > Would muO count as an application? > muO's already on the projects page: https://squeak.org/projects/ There's not much space for another item in the navigation, could we maybe add a "Commercial Applications" section on the top of the projects page? Fabio > > It is not commercial but it has proven successful at the task it was > designed for, namely composing music. > > http://www.zogotounga.net/comp/squeak/sqgeo.htm > https://stephanerollandin.bandcamp.com/ > > Stef > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lecteur at zogotounga.net Sat Sep 26 12:50:22 2020 From: lecteur at zogotounga.net (=?UTF-8?Q?St=c3=a9phane_Rollandin?=) Date: Sat, 26 Sep 2020 14:50:22 +0200 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: Message-ID: > muO's already on the projects page: > https://squeak.org/projects/ Yes, I know. It's all a question of vocabulary I guess. Eliot proposed an "application" tab for commercial "projects". We currently have a "project" tab. I'm not clear on what a project actually is (it is not a word I use), and to me an application (not a word I use either) does not have to be commercial. I call what I do "programs" of "software". I made two games, which I call "games", not projects (in that instance the word seems very wrong to me). I am working on a third one, of a different kind and scope, that is indeed currently maybe a project but when somewhat finished will be a software or a framework because it is actually a open-ended game engine. There will still be a game there, though. In the current project tab at squeak.org, very different types of software are listed together. If a different tab for applications is going to be added, then it is maybe the opportunity to sort things out. At the moment is feels like "oh look at all the things people made with Squeak" but since there are not too many of them and they are very different in nature it may give the impression that we just gathered what we could, and that in fact Squeak is not much used at all. Stef From karlramberg at gmail.com Sat Sep 26 17:19:45 2020 From: karlramberg at gmail.com (karl ramberg) Date: Sat, 26 Sep 2020 19:19:45 +0200 Subject: [squeak-dev] Font Handles (was: Inspector Custom Value Panes & Related) In-Reply-To: References: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de> Message-ID: Figured this out... Best, Karl On Sat, Sep 26, 2020 at 10:41 AM karl ramberg wrote: > Hi, > I tested using PluggableCheckBoxMorph but that widget is unwieldy. > I'm not sure how to add a label to it. > The widget seem to expect the model eg. FontChooserTool to perform the > label ? > > PluggableCheckBoxMorph>>on: anObject getState: getStateSel action: > actionSel label: labelSel menu: menuSel > ... > self label: (self model perform: labelSel). > > I'm not sure how that is supposed to work... > > There is no other use of the widget in the image I can look at either. > > Best, > Karl > > On Tue, Sep 8, 2020 at 9:57 PM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Great ideas! I never get the right handle on the first try, merging them >> sounds very reasonable. :-) >> >> Hm ... doesn't the existence of such an "Apply" button impede Morphic's >> liveness? Why not just apply all changes as soon as a font/style has been >> selected? You could replace the buttons by a checkbox "selection only". >> >> >> Best, >> >> Christoph >> >> ------------------------------ >> *Von:* Squeak-dev im >> Auftrag von karl ramberg >> *Gesendet:* Dienstag, 8. September 2020 21:49 Uhr >> *An:* The general-purpose Squeak developers list >> *Betreff:* Re: [squeak-dev] Inspector Custom Value Panes & Related >> >> >> >> On Tue, Sep 8, 2020 at 8:16 AM Thiede, Christoph < >> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >> >>> Hi Karl, >>> >>> >>> I'm sorry your proposal is lost a bit - I think it's a nice addition! It >>> does not look exactly like the TextMorph dialog, but I actually prefer "one >>> dialog that fits it all" over the three separate handles for a TextMorph >>> (that do not even all appear to work properly at the moment.) I think we >>> should get this merged! :-) >>> >>> >>> My change just adds handle support to the already existing >> StringMorph>>changeFont >> >> The three TextMorph handles are not optimal and could be refactored into >> one handle. >> >> I made another change to FontChooserTool where I added a button to apply >> text changes to the partition of text selected or a bulk change to all text >> in the pane. With this change I also enabled the emphasis pane to >> FontChooserTool. >> >> [image: bild.png] >> >> Best, >> Karl >> >> >> Best, >>> >>> Christoph >>> >>> ------------------------------ >>> *Von:* Squeak-dev im >>> Auftrag von karl ramberg >>> *Gesendet:* Donnerstag, 3. September 2020 13:28:41 >>> *An:* The general-purpose Squeak developers list >>> *Betreff:* Re: [squeak-dev] Inspector Custom Value Panes & Related >>> >>> Here is a change set that adds HaloHandle for font change to StringMorph >>> >>> Best, >>> Karl >>> >>> On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel >>> wrote: >>> >>>> Hi Eric. >>>> >>>> > What is the proper way to make a "custom value pane" for my objects >>>> whenever they appear inside an Inspector? >>>> >>>> Unfortunately, the current inspector framework has no such extension >>>> point. You can, however, prepare a custom inspector for your domain >>>> objects. See MorphInspector as an example. >>>> >>>> Best, >>>> Marcel >>>> >>>> Am 02.09.2020 00:10:56 schrieb Eric Gade : >>>> Hi all, >>>> >>>> What is the proper way to make a "custom value pane" for my objects >>>> whenever they appear inside an Inspector? For my RISCV work, I'm looking to >>>> show a morphic representation of the bitfields when a given instruction's >>>> `self` property is highlighted in the Inspector window. >>>> >>>> As a related question, what's the best way to make a composed Morph >>>> that has StringMorphs of different fonts, sizes, and alignments? It seems >>>> like not all Fonts are available to StringMorph (TrueType ones, for >>>> example) -- is that correct? >>>> >>>> Thanks! >>>> >>>> -- >>>> Eric >>>> >>>> >>>> >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 21823 bytes Desc: not available URL: From gettimothy at zoho.com Sat Sep 26 17:45:37 2020 From: gettimothy at zoho.com (gettimothy) Date: Sat, 26 Sep 2020 13:45:37 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: <174cb853027.e9e779b216826.6710387107182355048@zoho.com> Jakob Thank you. I have it launching squeak6.0 alpha. very promising! ---- On Fri, 25 Sep 2020 17:03:36 -0400 Jakob Reschke wrote ---- Hello, Here is what I had to do to have the Pharo launcher launch a Squeak image: - Download and extract a Squeak VM and a supported image with .changes. - Download and install the latest Pharo Launcher release. - Start the launcher. - Open Settings > Pharo Launcher > edit the folder paths to my likings. - Click Store Settings at the top right. - Move the Squeak VM directory below the VM directory entered in the settings. Do not move the image yet. - Press shift+return, type vmExecutableName and open the method WindowsResolver>>#vmExecutableName. - Change WindowsResolver>>#vmExecutableName to return 'Squeak.exe'. - Close the system browser window. - Press the VMs button on the top right. It should now list the Squeak VM, for example 'squeak.cog.spur_win64x64_202003021730'. Close the VMs window. - Press Import at the top, choose the extracted image. It will be moved to the images directory from the Settings! - The image should now appear in the main list of the launcher window. - In the new directory of the image, put a file named "pharo.version" with the contents "0" next to the image. This will prevent the launcher from trying to find out the Pharo version of the image by running a pharo-version.st script with --headless on the image. Squeak does not support this. - Back in the launcher, in the combo box next to the Launch button at the top, drop down the list and select Edit Configurations. - For the Default configuration, select your Squeak VM instead of "0-x64" on the right. The latter would try and fail to download a fitting Pharo VM again. - Close the window with "Save & Select". - Now I am able to launch the Squeak image. To preserve the changes to the launcher image: - Press shift+return, enter Playground and press return to open a Workspace. - Evaluate the following: Smalltalk snapshot: true andQuit: true. Kind regards, Jakob Am Mi., 2. Sept. 2020 um 18:20 Uhr schrieb Sean DeNigris : > > I've been pontificating about unity between dialects, convinced more than ever that we need each other and have more of a simple (not easy) communication problem than anything else, and pestering the principals of various communities, so I decided to put some concrete action behind it - some "skin in the game" as we say in the U.S. > > After reading Trygve's frustration with Squeak image/VM management, and Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage and run Squeak (and GToolkit) VMs/images. > > The current limitation vs. launching standard Pharo images is that you have to download the VMs and images and manually install them due to differences in URL and other conventions. However once the files are in place, you can create templates allowing images to be created at will from different Squeak versions and automatically run with the correct VM. Auto-installation could probably be done if someone cares enough but it scratched my itch for the moment to manage GT images. I’m sure Cuis support could be added, but the hooks are now available and someone more familiar with its VM/image installation might have an easier time. > > Until my latest PR [1] is merged, brave souls can play with it and give feedback by loading my issue branch [2] into the latest Launcher release [3]. NB only tested on Mac. > > It seemed Trygve was at his wits' end, but maybe this and other small gestures will let him know how important he is to our community if not motivate him to resume his important work. > > I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what small action can you take today that unites us rather than divides us? We can be each others' supporters even if technical, vision and other differences keep our codebases somewhat distinct. I believe the seeds of wars are planted when I lack the grace to give those around me the benefit of the doubt that they mean well and are trying their best. There is more than enough of that already in the world. Let’s invent a *better* future… > > Your brother, > Sean > > 1. https://github.com/pharo-project/pharo-launcher/pull/503 > 2. https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks > 3. https://pharo.org/download > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sat Sep 26 17:57:14 2020 From: gettimothy at zoho.com (gettimothy) Date: Sat, 26 Sep 2020 13:57:14 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs        https://www.w3schools.com/html/html_elements.asp In-Reply-To: <17DE76DF-54DD-4826-BF26-65CA143F7404@gmail.com> References: <17DE76DF-54DD-4826-BF26-65CA143F7404@gmail.com> Message-ID: <174cb8fd18e.103a0b4d916904.3943481991164041112@zoho.com> Thanks all for the replies. Just to be clear here as I want to do the right thing. We should keep the existing Squeak XML Framework If we need improvements , modify that XML framework to include them. Live with the per-application (Terf, XTreams) overrides of that default Squeak XML Framework. "Squeak" does NOT want to replace the existing Squeak XML Framework with a more feature complete framework such as those in Terf or XTreams. However, let me know if you are interested in upgrading.... 1. let me know which is the best XML Framework out there. topa, etc.... 2. I will get it working on my 6.0 alpha pristine images. 3. bring in other Squeak "stuff" that uses XML and adapt it to that. cordially, tty -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Sat Sep 26 18:21:15 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sat, 26 Sep 2020 20:21:15 +0200 Subject: [squeak-dev] XML-Parser and
vs
vs        https://www.w3schools.com/html/html_elements.asp In-Reply-To: <174cb8fd18e.103a0b4d916904.3943481991164041112@zoho.com> References: <17DE76DF-54DD-4826-BF26-65CA143F7404@gmail.com> <174cb8fd18e.103a0b4d916904.3943481991164041112@zoho.com> Message-ID: <4C52E15A-703B-4E8B-809E-805557758A60@gmx.de> > On 26.09.2020, at 19:57, gettimothy via Squeak-dev wrote: > > Thanks all for the replies. > > Just to be clear here as I want to do the right thing. > > > We should keep the existing Squeak XML Framework > If we need improvements , modify that XML framework to include them. > Live with the per-application (Terf, XTreams) overrides of that default Squeak XML Framework. > > > > "Squeak" does NOT want to replace the existing Squeak XML Framework with a more feature complete framework such as those in Terf or XTreams. > I do... -t > > However, let me know if you are interested in upgrading.... > > 1. let me know which is the best XML Framework out there. topa, etc.... > 2. I will get it working on my 6.0 alpha pristine images. > 3. bring in other Squeak "stuff" that uses XML and adapt it to that. > > > cordially, > > tty > > > > > > From gettimothy at zoho.com Sat Sep 26 19:12:07 2020 From: gettimothy at zoho.com (gettimothy) Date: Sat, 26 Sep 2020 15:12:07 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> Message-ID: <174cbd461d1.bc4b381e17330.4131801858905654763@zoho.com> ok,  If I am reading this correctly.... Then http://www.smalltalkhub.com/mc/PharoExtras/XMLParser/main is the most complete XML implementation out there and importing this into Squeak6.0 alpha would be useful. amirite? Also, importing that would break existing Squeak stuff that depends on the  XML-Parser (tpr.45) and XML-Explorer (topa.1)  currently in the latest squeak6.0 alpha image.. So.... for a hard-replace.... remove XML-Parser (tpr.45) and XML-Explorer (topa.1) from a pristine 6.0 alpha image... then run tests.. find all the stuff that depends on those packages and adapt them to the latest-greatest. Is that about right? thx in advance. ---- On Mon, 21 Sep 2020 13:03:43 -0400 Tobias Pape wrote ---- > On 21.09.2020, at 17:56, Levente Uzonyi wrote: > > Hi Ron, > > On Mon, 21 Sep 2020, Ron Teitelbaum wrote: > >> Hi All, >> Terf uses a modified version of XML-Parser based on trunk XML-Parser-ul.44 and XML-Explorer-topa.1. >> >> Levente Uzonyi >> >> Monty's XML implementation is way more complete than the one in the Trunk. >> How so? > > AFAIK, it supports namespaces, dtd and schema validation, and sax2. There may be other things too. I never really checked. > XLink support and the like. It's really impressive and quite useful. Had I had time, I'd incorporated it in 2017 or so. I hadn't had time and I forgot :/ 6.0 is a good Idea tho. -t > > Levente > >> Ron Tetielbaum >> On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda wrote: >> >> > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi wrote: >> > >> > H Eliot, >> > >> >> On Sun, 20 Sep 2020, Eliot Miranda wrote: >> >> >> >> Hi Levente, Hi Tty, >> >> >> >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: >> >>> Hi Tim, >> >>> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. >> >>> That Xtreams dependency on that library is pretty much pointless: >> >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package >> >>> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: >> >>> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. >> >>> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) >> >>> - the example does not use any features to justify using the not-so-Squeak-compatible XML package >> >>> - that example grammar only uses it for the sake of making it Pharo-compatible >> >>> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. >> >>> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. >> >>> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo >> version of the example, making the example use Squeak's XML package in Squeak. >> >>> The latter also involves more work, and a more compilcated Metacello configuration. >> >> >> >> I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework. >> Cc’ing Ron as he knows the reality far better than I (hi Ron). >> > >> > Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. >> >> Terf is built in Squeak 5.3. We want to move forward to trunk soon (we want to stay on trunk). I have zero desire to develop on Pharo. >> >> > >> > Levente >> > >> >> >> >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: >> >>>> Hi Folks, >> >>>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 >> >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. >> >>>> >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> vs >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. >> >>>> Checking the Monticello, the 6.0 >> >>>> XML-Parser (monty.428) >> >>>> while the 5.3 is >> >>>> >> >>>> XML-Parser (monty.428 , ul.44) >> >>> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. >> >>>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. >> >>>> Being new to this, who knows how I managed that. >> >>> No. The two packages are not compatible with each other and you have "both" in your image at the same time. >> >>>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. >> >>>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. >> >>>> Which should I use? >> >>> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. >> >>>> Should I bring in the ul.44? and how do I do that? >> >>>> Should I revise my tests (there are not too many) to insert that extra space. >> >>> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). >> >>> Levente >> >>>> thank you for your time. >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Sat Sep 26 20:24:23 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sat, 26 Sep 2020 22:24:23 +0200 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <174cbd461d1.bc4b381e17330.4131801858905654763@zoho.com> References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> <174cbd461d1.bc4b381e17330.4131801858905654763@zoho.com> Message-ID: <00E42A1E-F7CC-43E7-81DF-B2D76E17B16E@gmx.de> > On 26.09.2020, at 21:12, gettimothy via Squeak-dev wrote: > > > ok, > > If I am reading this correctly.... > > Then http://www.smalltalkhub.com/mc/PharoExtras/XMLParser/main Plus http://www.smalltalkhub.com/mc/PharoExtras/XMLWriter/main and http://www.smalltalkhub.com/mc/PharoExtras/XPath/main > > is the most complete XML implementation out there and importing this into Squeak6.0 alpha would be useful. > > amirite? > > Also, importing that would break existing Squeak stuff that depends on the > XML-Parser (tpr.45) and XML-Explorer (topa.1) currently in the latest squeak6.0 alpha image.. > > > So.... > > for a hard-replace.... > > remove XML-Parser (tpr.45) and XML-Explorer (topa.1) from a pristine 6.0 alpha image... > > then run tests.. > > find all the stuff that depends on those packages and adapt them to the latest-greatest. > > Is that about right? more or less. I think I have a version of XML-Explorer that work's with monty's XML variant (see attached image) -t > > thx in advance. > > > > ---- On Mon, 21 Sep 2020 13:03:43 -0400 Tobias Pape wrote ---- > > > > On 21.09.2020, at 17:56, Levente Uzonyi wrote: > > > > Hi Ron, > > > > On Mon, 21 Sep 2020, Ron Teitelbaum wrote: > > > >> Hi All, > >> Terf uses a modified version of XML-Parser based on trunk XML-Parser-ul.44 and XML-Explorer-topa.1. > >> > >> Levente Uzonyi > >> >> Monty's XML implementation is way more complete than the one in the Trunk. > >> How so? > > > > AFAIK, it supports namespaces, dtd and schema validation, and sax2. There may be other things too. I never really checked. > > > XLink support and the like. > It's really impressive and quite useful. > Had I had time, I'd incorporated it in 2017 or so. > I hadn't had time and I forgot :/ > > 6.0 is a good Idea tho. > > -t > > > > > Levente > > > >> Ron Tetielbaum > >> On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda wrote: > >> > >> > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi wrote: > >> > > >> > H Eliot, > >> > > >> >> On Sun, 20 Sep 2020, Eliot Miranda wrote: > >> >> > >> >> Hi Levente, Hi Tty, > >> >> > >> >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: > >> >>> Hi Tim, > >> >>> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. > >> >>> That Xtreams dependency on that library is pretty much pointless: > >> >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package > >> >>> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: > >> >>> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. > >> >>> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) > >> >>> - the example does not use any features to justify using the not-so-Squeak-compatible XML package > >> >>> - that example grammar only uses it for the sake of making it Pharo-compatible > >> >>> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. > >> >>> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. > >> >>> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo > >> version of the example, making the example use Squeak's XML package in Squeak. > >> >>> The latter also involves more work, and a more compilcated Metacello configuration. > >> >> > >> >> I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework. > >> Cc’ing Ron as he knows the reality far better than I (hi Ron). > >> > > >> > Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. > >> > >> Terf is built in Squeak 5.3. We want to move forward to trunk soon (we want to stay on trunk). I have zero desire to develop on Pharo. > >> > >> > > >> > Levente > >> > > >> >> > >> >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: > >> >>>> Hi Folks, > >> >>>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 > >> >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. > >> >>>> > >> >>>> (XMLElement name:'br' ) printString '
' > >> >>>> vs > >> >>>> (XMLElement name:'br' ) printString '
' > >> >>>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. > >> >>>> Checking the Monticello, the 6.0 > >> >>>> XML-Parser (monty.428) > >> >>>> while the 5.3 is > >> >>>> > >> >>>> XML-Parser (monty.428 , ul.44) > >> >>> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. > >> >>>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. > >> >>>> Being new to this, who knows how I managed that. > >> >>> No. The two packages are not compatible with each other and you have "both" in your image at the same time. > >> >>>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. > >> >>>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. > >> >>>> Which should I use? > >> >>> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. > >> >>>> Should I bring in the ul.44? and how do I do that? > >> >>>> Should I revise my tests (there are not too many) to insert that extra space. > >> >>> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). > >> >>> Levente > >> >>>> thank you for your time. > >> > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: Bildschirmfoto 2020-09-26 um 22.19.40.PNG Type: image/png Size: 66012 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: Bildschirmfoto 2020-09-26 um 22.22.12.PNG Type: image/png Size: 7261 bytes Desc: not available URL: From gettimothy at zoho.com Sun Sep 27 06:21:55 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 02:21:55 -0400 Subject: [squeak-dev] XML-Parser and
vs
vs https://www.w3schools.com/html/html_elements.asp In-Reply-To: <00E42A1E-F7CC-43E7-81DF-B2D76E17B16E@gmx.de> References: <8F090CE5-B6BC-4154-9638-C651CC40B6C1@gmail.com> <1C80EC2A-7039-4754-9C4B-F8C1DE0E55F9@gmx.de> <174cbd461d1.bc4b381e17330.4131801858905654763@zoho.com> <00E42A1E-F7CC-43E7-81DF-B2D76E17B16E@gmx.de> Message-ID: <174ce399822.dabd18cd19482.836685021295282701@zoho.com> thank you! ---- On Sat, 26 Sep 2020 16:23:49 -0400 Tobias Pape wrote ---- On 26.09.2020, at 21:12, gettimothy via Squeak-dev wrote: ok,  If I am reading this correctly.... Then http://www.smalltalkhub.com/mc/PharoExtras/XMLParser/main Plus http://www.smalltalkhub.com/mc/PharoExtras/XMLWriter/main and http://www.smalltalkhub.com/mc/PharoExtras/XPath/main is the most complete XML implementation out there and importing this into Squeak6.0 alpha would be useful. amirite? Also, importing that would break existing Squeak stuff that depends on the  XML-Parser (tpr.45) and XML-Explorer (topa.1)  currently in the latest squeak6.0 alpha image.. So.... for a hard-replace.... remove XML-Parser (tpr.45) and XML-Explorer (topa.1) from a pristine 6.0 alpha image... then run tests.. find all the stuff that depends on those packages and adapt them to the latest-greatest. Is that about right? more or less. I think I have a version of XML-Explorer that work's with monty's XML variant (see attached image) -t thx in advance. ---- On Mon, 21 Sep 2020 13:03:43 -0400 Tobias Pape  wrote ---- > On 21.09.2020, at 17:56, Levente Uzonyi wrote: >  > Hi Ron, >  > On Mon, 21 Sep 2020, Ron Teitelbaum wrote: >  >> Hi All, >> Terf uses a modified version of XML-Parser based on trunk XML-Parser-ul.44 and XML-Explorer-topa.1. >>  >> Levente Uzonyi >> >> Monty's XML implementation is way more complete than the one in the Trunk. >> How so?  >  > AFAIK, it supports namespaces, dtd and schema validation, and sax2. There may be other things too. I never really checked. >  XLink support and the like. It's really impressive and quite useful. Had I had time, I'd incorporated it in 2017 or so. I hadn't had time and I forgot :/ 6.0 is a good Idea tho. -t >  > Levente >  >> Ron Tetielbaum >> On Sun, Sep 20, 2020 at 6:30 PM Eliot Miranda wrote: >>  >> > On Sep 20, 2020, at 3:12 PM, Levente Uzonyi wrote: >> > >> > H Eliot, >> > >> >> On Sun, 20 Sep 2020, Eliot Miranda wrote: >> >> >> >> Hi Levente, Hi Tty, >> >> >> >>>> On Sep 20, 2020, at 8:10 AM, Levente Uzonyi wrote: >> >>> Hi Tim, >> >>> Be very careful here. The Xtreams Metacello configuration pulls in Monty's XML implementation along with the whole kitchen sink it depends on, and leaves your image with a bunch of undeclared variables. >> >>> That Xtreams dependency on that library is pretty much pointless: >> >>> - only one example grammar (PEGWikiGenerator) uses Monty's XML package >> >>> - the example uses it to produce web content as XHTML which is rather unusual for two reasons: >> >>> - the majority of web content is HTML5 nowadays. Some stats on the internet say XHTML is still at 10% but without proof. >> >>> - it uses an XML library to produce XHTML, but the two are surprisingly not as compatible as you would think (see below) >> >>> - the example does not use any features to justify using the not-so-Squeak-compatible XML package >> >>> - that example grammar only uses it for the sake of making it Pharo-compatible >> >>> Monty's XML implementation is way more complete than the one in the Trunk, but it's not compatible with the one in the Trunk. >> >>> So, anything that depends on Squeak's XML implementation will be broken if you load Xtreams. >> >>> IMO, the right solution is to either drop Pharo support for Xtreams, as Pharo does not use squeaksource (Metacello configuration assumes Pharo 4 the latest) or split the package, and have a Squeak and a Pharo >> version of the example, making the example use Squeak's XML package in Squeak. >> >>> The latter also involves more work, and a more compilcated Metacello configuration. >> >> >> >> I’m pretty sure Terf uses the Squeak XML framework and I know that we’re heavily dependent on XML. So let me make a plea for the latter. I fear we would be heavily impacted by a change of XML framework.  >> Cc’ing Ron as he knows the reality far better than I (hi Ron). >> > >> > Does Terf use Pharo (version 4 or earlier)? If not, this change would make no difference. >>  >> Terf is built in Squeak 5.3. We want to move forward to trunk soon (we want to stay on trunk). I have zero desire to develop on Pharo. >>  >> > >> > Levente >> > >> >> >> >>>> On Sat, 19 Sep 2020, gettimothy via Squeak-dev wrote: >> >>>> Hi Folks, >> >>>> I have pretty much succesfully imported XTreams and the XML-Parser stuff to Squeak 6alpha. from Squeak5.3 >> >>>> Some SUnit tests from my project on 5.3 , now ported to 6.0alpha are failing for the following reason. >> >>>> >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> vs >> >>>> (XMLElement name:'br' ) printString '
' >> >>>> Notice the latter one inserts an extra space after the 'br'...that is the behavior on my 6.0. >> >>>> Checking the Monticello, the 6.0 >> >>>> XML-Parser (monty.428) >> >>>> while the 5.3 is >> >>>> >> >>>> XML-Parser (monty.428 , ul.44) >> >>> You have probably updated that image after loading Xtreams, and the update process merged in the XML-Parsing package from the Trunk. >> >>>> So, presumably having the extra ul.44 removes the space, but I have no idea what the ul.44 is, why it is there, how it got there....yada,yada,yada. >> >>>> Being new to this, who knows how I managed that. >> >>> No. The two packages are not compatible with each other and you have "both" in your image at the same time. >> >>>> before I mailed this, I checked at the w3 schools on the
tag and it asserts it should not have the slash in it. w3 schools is not the xHTML standard, so this may or may not be true. >> >>>> FWIW, the
stuff works fine for me as does the
, but I do not know if it will cause problems when I move on to XPath stuff later on the resulting xHTML docs. >> >>>> Which should I use? >> >>> The space is required for XHTML. It is not required for XML. You're using an XML library to generate XHTML, which, as you see, may or may not work. >> >>>> Should I bring in the ul.44? and how do I do that? >> >>>> Should I revise my tests (there are not too many) to insert that extra space. >> >>> If you want to produce specifically XHTML, then yes. Else you're better off generating HTML and not bother with how an XML library produces XHTML (which is not the job of an XML library IMO). >> >>> Levente >> >>>> thank you for your time. >> > >  -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 1.png Type: image/png Size: 66012 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 2.png Type: image/png Size: 7261 bytes Desc: not available URL: From gettimothy at zoho.com Sun Sep 27 07:08:15 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 03:08:15 -0400 Subject: [squeak-dev] XML upgrade to 6.0 alpha experiment Message-ID: <174ce64035a.f640086819616.1772319578056170750@zoho.com> Hi folks. In a prisitine 6.0 alpha 19881 64 bit, I ran all tests. out of the box 7120 run in 0:00:05:20.485, 6893 passes, 57 expected failures, 43 failures, 127 errors, 0 unexpected passes took screenshots of the test runner results (Thank you to whoever wrote GIFReadWriter grabScreenAndSaveOnDisk and put it in the Do menu, very handy) Then I removed XML-Explorer (topa.1) and XML-Parser (tpr.45) rerun tests: 7111 run in 0:00:05:20.006, 6887 passes, 57 expected failures, 40 failures, 127 errors, 0 unexpected passes XML-Parser (tpr.45) contains 9 tests and the difference between 7120 and 7111 is 9. So, I am assuming no side effects from removing the existing XML stuff in the pristine 6.0 image. I will work on installing the  http://www.smalltalkhub.com/mc/PharoExtras/XMLParser/main http://www.smalltalkhub.com/mc/PharoExtras/XMLWriter/main http://www.smalltalkhub.com/mc/PharoExtras/XPath/main stuff later this week. thx for the pointers. tty -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 27 09:25:42 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 05:25:42 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: <174cee1dc91.ad1a1dc420082.3392381296394294726@zoho.com> Hi Jakob An update for your instructions for non-windows users. - Press shift+return, type vmExecutableName and open the method WindowsResolver>>#vmExecutableName. - Change WindowsResolver>>#vmExecutableName to return 'Squeak.exe'. Is  - Press shift+return, type vmExecutableName and open the method UnixResolver>>#vmExecutableName. - Change UnixResolver>>#vmExecutableName to return 'squeak'. I found an "interesting" side effect. I had hoped to have a squeaklauncher and a pharolauncher coexist as separate apps. However, after I got squeakluancher running, my pharolauncher was launching the squeaklauncher. The two executables where in completely separate directories /home/wm/usr/src/smalltalk/squeaklauncher  and /home/wm/usr/src/smalltalk/pharolauncher re-installs of PharoLauncher only confused matters. It appears that the Settings are shared "globally" somehow. I could not find a file that stored them. Meager attempts to get it to launch both kinds of images failed. anyhoo, thanks for your work. -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 27 10:08:49 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 06:08:49 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) Message-ID: <174cf095424.1070a5cad20325.7089446597164069542@zoho.com> Hi Jakob The pharo-launcher and pharo store their settings (on linux) in ~/.config/pharo and ~/.config/pharo-launcher  respectivelly. this explains the shared state between squeaklauncher and pharolauncher. cordially, t -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 27 11:44:54 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 07:44:54 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) Message-ID: <174cf614dd8.cd035eeb20789.2138876911533251701@zoho.com> Hi folks... I seem to have succesfully disentantangled squeaklauncher and pharolauncher. First off, regarding To preserve the changes to the launcher image: - Press shift+return, enter Playground and press return to open a Workspace. - Evaluate the following: Smalltalk snapshot: true andQuit: true. There appears to be an Easter egg in the launcher image. Click at the bottom right corner and the pharo menu for the image running the Launcher app will show up and you can open finders, browsers, etc. The process is generally... create duplicate of .config/pharo* folders to contain the settings create a ~/Squeak directory to mirror the ~/Pharo directory. Create a /home/xyz/usr/src/smalltalk/squeaklauncher and /home/xyz/usr/src/smalltalk/pharolauncher  to contain the separate executables. launch the squeaklauncher. then... Here is a copy-n-paste of my running notes (it duplicates some of the steps above). unzip Pharo-Launcher.zip mv pharo-launcher to squeak-launcher. In ~/.config/... cp -Rv pharo  squeak cp -Rv pharo-launcher squeak-launcher In ~/ directory, cp -Rv Pharo Squeak launch squeak-launcher open the settings browser. change paths to reflect the separate squeak-browser stuff. click in bottom right of the World to open the Pharo tools->Browser (or whatever it is called) Find PhLSettingsBrowser Class side, change folder to squeak-launcher. modify  UnixResolver and PlatformResolver launcherUserFilesLocation "Documents folder is not a standard on Unix. Put files in the $HOME directory" ^ self home / 'Squeak' I can now run the two applications independently without clobbering each other. hth. t -------------- next part -------------- An HTML attachment was scrubbed... URL: From robert.withers at pm.me Sun Sep 27 13:38:30 2020 From: robert.withers at pm.me (Robert Withers) Date: Sun, 27 Sep 2020 13:38:30 +0000 Subject: [squeak-dev] Looking to build a WeatherStation for my sailboat Message-ID: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> I finally got my sailboat! A long-term dream of mine, a bucket list item! I bought her last week and I leave Thursday with my mate Speedy, to sail her down from CT to NC. I will rename her SV Slosh outta Oriental, NC. WOOT! WOOT! WOOT! Here are the rest of the pics: https://www.dropbox.com/sh/3p9xc4q4ltydhjg/AABes9TP5uIXgs0Frto9d67Oa?dl=0. In thinking about her capabilities, on the water, I will always be curious about the weather. Tim, your WeatherStation sprung immediately to mind. I found several websites, I am perusing, but I am a little lost on which hardware to get and bringing her up. [1][2][3]. Am I supposed to buy the OurWeather device? The plot and rotary dial displays are AWESOME! I'd love to have a rotary dial for both the barometer, as you have shown, as well as the anemometer. Perhaps on a waterproof display at the helm...that would be good. This would be perfect for my Yawl, especially the anemometer and barometer measurements. I have 12v DC power. TIm, would you guide me to getting it setup? I would greatly appreciate your mentoring me a little. For this trip I have until Thursday, to make it happen, so it is a bit of a rush to accomplish this. [1] WeatherStation - https://wiki.squeak.org/squeak/6573 [2] Weather Station at SqueakSource - http://www.squeaksource.com/WeatherStation.html [3] OurWeather - https://shop.switchdoc.com/products/ourweather-complete-weather-kit -- K, r -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 7420963_20200328125419106_1_LARGE.jpg Type: image/jpeg Size: 39165 bytes Desc: not available URL: From robert.withers at pm.me Sun Sep 27 13:39:22 2020 From: robert.withers at pm.me (Robert Withers) Date: Sun, 27 Sep 2020 13:39:22 +0000 Subject: [squeak-dev] Looking to build a WeatherStation for my sailboat Message-ID: I finally got my sailboat! A long-term dream of mine, a bucket list item! I bought her last week and I leave Thursday with my mate Speedy, to sail her down from CT to NC. I will rename her SV Slosh outta Oriental, NC. WOOT! WOOT! WOOT! Here are the rest of the pics: https://www.dropbox.com/sh/3p9xc4q4ltydhjg/AABes9TP5uIXgs0Frto9d67Oa?dl=0. In thinking about her capabilities, on the water, I will always be curious about the weather. Tim, your WeatherStation sprung immediately to mind. I found several websites, I am perusing, but I am a little lost on which hardware to get and bringing her up. [1][2][3]. Am I supposed to buy the OurWeather device? The plot and rotary dial displays are AWESOME! I'd love to have a rotary dial for both the barometer, as you have shown, as well as the anemometer. Perhaps on a waterproof display at the helm...that would be good. This would be perfect for my Yawl, especially the anemometer and barometer measurements. I have 12v DC power. TIm, would you guide me to getting it setup? I would greatly appreciate your mentoring me a little. For this trip I have until Thursday, to make it happen, so it is a bit of a rush to accomplish this. [1] WeatherStation - https://wiki.squeak.org/squeak/6573 [2] Weather Station at SqueakSource - http://www.squeaksource.com/WeatherStation.html [3] OurWeather - https://shop.switchdoc.com/products/ourweather-complete-weather-kit -- K, r -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 7420963_20200328125419106_1_LARGE.jpg Type: image/jpeg Size: 39165 bytes Desc: not available URL: From tim at rowledge.org Sun Sep 27 17:36:12 2020 From: tim at rowledge.org (tim Rowledge) Date: Sun, 27 Sep 2020 10:36:12 -0700 Subject: [squeak-dev] XML upgrade to 6.0 alpha experiment In-Reply-To: <174ce64035a.f640086819616.1772319578056170750@zoho.com> References: <174ce64035a.f640086819616.1772319578056170750@zoho.com> Message-ID: <3C3C6261-C01A-46A5-A33E-EE87A6A02228@rowledge.org> > On 2020-09-27, at 12:08 AM, gettimothy via Squeak-dev wrote: > > XML-Parser (tpr.45) For a moment there I thought "wait, *I* messed with XML parsing? Surely not!" but thankfully it was just some clean up to replace FileList with FileRegistry. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Useful Latin Phrases:- Ne auderis delere orbem rigidum meum! = Don't you dare erase my hard disk! From Das.Linux at gmx.de Sun Sep 27 17:51:04 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sun, 27 Sep 2020 19:51:04 +0200 Subject: [squeak-dev] XML upgrade to 6.0 alpha experiment In-Reply-To: <174ce64035a.f640086819616.1772319578056170750@zoho.com> References: <174ce64035a.f640086819616.1772319578056170750@zoho.com> Message-ID: <1C0C315E-FEAC-4487-93E5-E31CA080ADBB@gmx.de> This might help... > On 27.09.2020, at 09:08, gettimothy via Squeak-dev wrote: > > Hi folks. > > > In a prisitine 6.0 alpha 19881 64 bit, I ran all tests. > > out of the box > 7120 run in 0:00:05:20.485, 6893 passes, 57 expected failures, 43 failures, 127 errors, 0 unexpected passes > > took screenshots of the test runner results (Thank you to whoever wrote GIFReadWriter grabScreenAndSaveOnDisk and put it in the Do menu, very handy) > > Then I removed XML-Explorer (topa.1) and XML-Parser (tpr.45) > > rerun tests: > 7111 run in 0:00:05:20.006, 6887 passes, 57 expected failures, 40 failures, 127 errors, 0 unexpected passes > > XML-Parser (tpr.45) contains 9 tests and the difference between 7120 and 7111 is 9. So, I am assuming no side effects from removing the existing XML stuff in the pristine 6.0 image. > > I will work on installing the > > http://www.smalltalkhub.com/mc/PharoExtras/XMLParser/main > http://www.smalltalkhub.com/mc/PharoExtras/XMLWriter/main > http://www.smalltalkhub.com/mc/PharoExtras/XPath/main > > stuff later this week. > > thx for the pointers. > > tty > > > > -------------- next part -------------- A non-text attachment was scrubbed... Name: TheJournalist-XMLCompat.st Type: application/octet-stream Size: 5790 bytes Desc: not available URL: From tim at rowledge.org Sun Sep 27 18:07:15 2020 From: tim at rowledge.org (tim Rowledge) Date: Sun, 27 Sep 2020 11:07:15 -0700 Subject: [squeak-dev] *****SPAM***** Looking to build a WeatherStation for my sailboat In-Reply-To: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> References: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> Message-ID: <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> > On 2020-09-27, at 6:38 AM, Robert Withers wrote: > > I finally got my sailboat! A long-term dream of mine, a bucket list item! I bought her last week and I leave Thursday with my mate Speedy, to sail her down from CT to NC. I will rename her SV Slosh outta Oriental, NC. WOOT! WOOT! WOOT! Congratulations on getting your very own money slurping hole in the water :-) > In thinking about her capabilities, on the water, I will always be curious about the weather. I understand knowing about the weather is quite important on a boat. Something about 100ft waves etc? > Tim, your WeatherStation sprung immediately to mind. I found several websites, I am perusing, but I am a little lost on which hardware to get and bringing her up. [1][2][3]. Am I supposed to buy the OurWeather device? I'd be very surprised if the very basic stuff I bought would survive more than a few weeks at sea, it simply isn't that sort of quality. I can't imagine the lightweight plastic cup anemometer surviving the first heavy gust of wind! So, that definitely means some research into what gear you can find that is suitably tough. I imagine it will not be cheap because nothing about a boat is cheap. > > The plot and rotary dial displays are AWESOME! I'd love to have a rotary dial for both the barometer, as you have shown, as well as the anemometer. Perhaps on a waterproof display at the helm...that would be good. > If you want to use any of the stuff I wrote you'll need to find some sensor hardware that has some variety of output that could be handled by a Pi or similar SBC - maybe even something like an ESP-32 board would be smart here. The cheap stuff simply uses a magnetic reed switch on the anemometer (grief, that's hard to type) and so you get to count pulses and have all the fun of sorting out de-bouncing etc. The wind direction vane does some sort of cockamamie resistor network and magentic reed switches that require an ADC and some rather hope-and-guess maths to work out where it is pointing - and only 8 options, so not very useful on a boat. I'm told that all the smart people are using ultrasonic based sensors that use differential signal propagation to work out both the wind speed and direction with no moving parts. And apparently, can work out the rain density from the impacts of raindrops on the flat plate sensor. Cool stuff. The nearest I've seen to the mil-spec system a friend is working on is https://www.kickstarter.com/projects/weatherflow/tempest-a-revolutionary-personal-weather-system?ref=discovery_category_newest&term=weather This is all going to take some research with your boating contacts to find a good system. The downside is that any commercial system will almost certainly have its own display suff. You could do much worse than looking at https://microship.com and contacting Steve; he has done this sort of thing for many years and may have parts to point to or even sell. Once you've got sensors that can talk to an ESP or a Pi (so various digital connections or i2c or spi or even bluetooth?) you can consider getting the data to some other machine to display. It may be that your sensors can connect directly to that display machine, saving one step. For a home built display you might go with a Pi attached to their 7" multi-touch display inside a very well sealed box. I'm afraid this may take longer than 'by Thursday' :-) tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Useful Latin Phrases:- Tam exanimis quam tunica nehru fio. = I am as dead as the nehru jacket. From gettimothy at zoho.com Sun Sep 27 18:44:19 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 14:44:19 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: <174d0e14b1a.cd91833723440.826434339730047099@zoho.com> Update.... The launcher is able to differentiate between vm versions. I just downloaded squeak.cog.spur_linux64x64_202009271755.tar.gz   and placed it in the ~/Squeak/vms/ directory via the import process. Then in squeaklauncher, the vm's button shows the new vm and its date. So now, we can run different vms for the images we import by changing the configuration on the fly if desired. During the 'vm's' button view, I got that vmExecuatable error...I don't know if that is because I forgot to save the pharo image or if there is a subtle bug somewhere. anyhoo, kinda handy, nifty tool to muck about with. cheers. -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 27 19:47:35 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 15:47:35 -0400 Subject: [squeak-dev] XML upgrade to 6.0 alpha experiment In-Reply-To: <1C0C315E-FEAC-4487-93E5-E31CA080ADBB@gmx.de> References: <174ce64035a.f640086819616.1772319578056170750@zoho.com> <1C0C315E-FEAC-4487-93E5-E31CA080ADBB@gmx.de> Message-ID: <174d11b3536.f9004bc023871.4724541188855333019@zoho.com> Thank you, it did. (Installer ss project: 'XMLSupport') install: 'ConfigurationOfXMLSupport'. (Smalltalk at: #ConfigurationOfXMLSupport)   load did not throw any warnings on the install. Monticello Browser now looks something like this:*XML() XML-Explorer (topa.1) XML-Parser (monty....) XML-Tests-Parser (monty....) XML-Writer-Core (monty....) XML-Writer-Tests (monty....) the XML only tests all pass: 5213 run in 0:00:00:02.064, 5213 passes, 0 expected failures, 0 failures, 0 errors, 0 unexpected passes There is no effect on the rest of the tests. 12682 run in 0:00:07:19.355, 12453 passes, 56 expected failures, 46 failures, 127 errors, 0 unexpected passes Next up is to load XTreams. I tried it earlier, and got errors in the tests probably related to the new Array immutability (?)  . I will get some better precision on that statement later this week. thx for your help. ---- On Sun, 27 Sep 2020 13:50:29 -0400Tobias Pape wrote ---- This might help... > On 27.09.2020, at 09:08, gettimothy via Squeak-dev wrote: > > Hi folks. > > > In a prisitine 6.0 alpha 19881 64 bit, I ran all tests. > > out of the box > 7120 run in 0:00:05:20.485, 6893 passes, 57 expected failures, 43 failures, 127 errors, 0 unexpected passes > > took screenshots of the test runner results (Thank you to whoever wrote GIFReadWriter grabScreenAndSaveOnDisk and put it in the Do menu, very handy) > > Then I removed XML-Explorer (topa.1) and XML-Parser (tpr.45) > > rerun tests: > 7111 run in 0:00:05:20.006, 6887 passes, 57 expected failures, 40 failures, 127 errors, 0 unexpected passes > > XML-Parser (tpr.45) contains 9 tests and the difference between 7120 and 7111 is 9. So, I am assuming no side effects from removing the existing XML stuff in the pristine 6.0 image. > > I will work on installing the > > http://www.smalltalkhub.com/mc/PharoExtras/XMLParser/main > http://www.smalltalkhub.com/mc/PharoExtras/XMLWriter/main > http://www.smalltalkhub.com/mc/PharoExtras/XPath/main > > stuff later this week. > > thx for the pointers. > > tty > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 27 20:01:05 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 16:01:05 -0400 Subject: [squeak-dev] bug in Debugger hacked during install of XTreams on Squeak6.0 alpha Message-ID: <174d127925d.dd5dbe0723989.6483616299426859564@zoho.com> Hi folks. Doing the XML-thing on a pristine 6.0alpha image Image ----- /home/wm/Squeak/images/XMLXtreamsSqueak6.0alpha-19881-64bit/XMLXtreamsSqueak6.0alpha-19881-64bit.image Squeak6.0alpha latest update: #19881 Current Change Set: HomeProject Image format 68021 (64 bit) Virtual Machine --------------- /home/wm/Squeak/vms/sqcogspur64linuxht202009271755/lib/squeak/5.0-202009271755/squeak During the  Installer ss        project: 'MetacelloRepository';        install: 'ConfigurationOfXtreams'. (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load I get the expected error in XTIOHandle class >> intialize initialize self makeNonviewableInstances. Smalltalk addToShutDownList: self after: Project where I have to change DisplayScreen to Project . Doing this in the Debugger (as I have previously done) threw a MorphicDebugger(Object) MessageNotUnderstood: MorphicDebugger >> stepToStatement I cancelled the install and modified MorphicDebugger(Debugger) >> contents: notifying: comment out "self stepToStatement" down in the guts of the method. I rerun the  Installer ss        project: 'MetacelloRepository';        install: 'ConfigurationOfXtreams'. (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load and am now able to modify the XTIOHandle class >> initialize method, on the fly in the debugger without throwing that error. cheers. -------------- next part -------------- An HTML attachment was scrubbed... URL: From gettimothy at zoho.com Sun Sep 27 20:09:50 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 16:09:50 -0400 Subject: [squeak-dev] Loading XTreams into Squeak6.0 alpha with the monty XML stuff. Message-ID: <174d12f9518.ca5063b624089.2291076570429066501@zoho.com> Hi folks. Just and FYI as I port "stuff I like" to 6.0 alpha... home/wm/Squeak/images/XMLXtreamsSqueak6.0alpha-19881-64bit/XMLXtreamsSqueak6.0alpha-19881-64bit.image Squeak6.0alpha latest update: #19881 Current Change Set: HomeProject Image format 68021 (64 bit) Virtual Machine --------------- /home/wm/Squeak/vms/sqcogspur64linuxht202009271755/lib/squeak/5.0-202009271755/squeak Open Smalltalk Cog[Spur] VM [CoInterpreterPrimitives VMMaker.oscog-eem.2824] Pristine image and load the following. (Installer ss project: 'XMLSupport') install: 'ConfigurationOfXMLSupport'. (Smalltalk at: #ConfigurationOfXMLSupport)   load Installer ss        project: 'MetacelloRepository';        install: 'ConfigurationOfXtreams'. (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load This is probably not related at all to XML, The install went fine, minus Xtreams-TerminalsFileSystem Warning: This package depends on the following classes:   MemoryHandle   FileSystemHandle   FileHandle   FileReference   FileLocator You must resolve these dependencies before you will be able to load these definitions:   FileHandle>>closed   FileHandle>>commit   FileHandle>>fileSize   FileHandle>>readInto:   FileHandle>>readInto:startingAt:count:   FileHandle>>reading   FileHandle>>seekTo:   FileHandle>>writeFrom:startingAt:count:   FileHandle>>writing   FileLocator>>appending   FileLocator>>reading   FileLocator>>writing   FileReference>>appending   FileReference>>reading   FileReference>>writing   FileSystemHandle>>streamingReadClose   FileSystemHandle>>streamingWriteClose   MemoryHandle>>closed   MemoryHandle>>commit   MemoryHandle>>fileSize   MemoryHandle>>reading   MemoryHandle>>seekTo:   MemoryHandle>>writing I ignore that and running the XTreams-Core, Xtreams-TerminalsTests, Xtreams-TransformsTests, XTreamsSubstreamsTests Xtreams-ParsingTests Xtreams-XtrasTests 794 run in 0:00:00:00.150, 335 passes, 0 expected failures, 1 failures, 458 errors, 0 unexpected passes On a first pass perusal,   XTRecyclingCenter class >> new: class: is the culprit. I will poke around more deeply next week, but I figured to post here in case anybody sees anything obvious. cordially, t -------------- next part -------------- An HTML attachment was scrubbed... URL: From robert.withers at pm.me Sun Sep 27 20:22:11 2020 From: robert.withers at pm.me (Robert Withers) Date: Sun, 27 Sep 2020 20:22:11 +0000 Subject: [squeak-dev] *****SPAM***** Looking to build a WeatherStation for my sailboat In-Reply-To: <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> References: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> Message-ID: <9c36fbb3-57d3-9fe5-bf33-8dae45999c10@pm.me> Hi tim, On 9/27/20 2:07 PM, tim Rowledge wrote: > On 2020-09-27, at 6:38 AM, Robert Withers [](mailto:robert.withers at pm.me) wrote: > >> I finally got my sailboat! A long-term dream of mine, a bucket list item! I bought her last week and I leave Thursday with my mate Speedy, to sail her down from CT to NC. I will rename her SV Slosh outta Oriental, NC. WOOT! WOOT! WOOT! > > Congratulations on getting your very own money slurping hole in the water :-) Yes, I totally underestimated the upfront costs. So I will distribute my fiscal load, month over month. Yes, I am retired! Me and my dog are heading to open water! She is going to love live-aboard, I think! --- I will approach my 1972 boat in the same fashion I approached my truck. I bought a 1997 Pathfinder, the key feature being a manual transmission. This turns out to be fairly rare in trucks, most are sold with only an automatic transmission, these days. What a horrible outcome! I like changing my own gears, thank you very much. The truck being a '97, there were certain maintenance issues. As well I had some upgrades I wanted (tow hitch!). My approach was to generate 3 lists: maintenance list, upgrade list, exploratory list. Doing the same with my boat, I will add weather instrumentation to my upgrade list and to my exploratory list. My upgrade list already has such items as gimbaled 3-burner/oven boat stove, air conditioner, 15 W A/C generator, 12v refrigerator, battery recharger and now includes a distributed Weather Station. It will take time, but I will get there. I am just watching my money to ensure my emergency funds and maintenance are well covered. My monthly costs for the boat are $100/mo boat insurance and $265/mo live-aboard slip fees. Yearly costs are haul, bottom paint and zincs (~$2,000/year). My upgrade list is pricey! >> In thinking about her capabilities, on the water, I will always be curious about the weather. > > I understand knowing about the weather is quite important on a boat. Something about 100ft waves etc? Yes, especially if I follow through on my plan to sail to Morocco in 2022. Big Atlantic swell! >> Tim, your WeatherStation sprung immediately to mind. I found several websites, I am perusing, but I am a little lost on which hardware to get and bringing her up. [1][2][3]. Am I supposed to buy the OurWeather device? > > I'd be very surprised if the very basic stuff I bought would survive more than a few weeks at sea, it simply isn't that sort of quality. I can't imagine the lightweight plastic cup anemometer surviving the first heavy gust of wind! > > So, that definitely means some research into what gear you can find that is suitably tough. I imagine it will not be cheap because nothing about a boat is cheap. Yes and so the weather sensing lands on my exploratory list! Not too much is cheap. NC slips are cheaper than so and so... >> The plot and rotary dial displays are AWESOME! I'd love to have a rotary dial for both the barometer, as you have shown, as well as the anemometer. Perhaps on a waterproof display at the helm...that would be good. > > If you want to use any of the stuff I wrote you'll need to find some sensor hardware that has some variety of output that could be handled by a Pi or similar SBC - maybe even something like an ESP-32 board would be smart here. The cheap stuff simply uses a magnetic reed switch on the anemometer (grief, that's hard to type) and so you get to count pulses and have all the fun of sorting out de-bouncing etc. The wind direction vane does some sort of cockamamie resistor network and magentic reed switches that require an ADC and some rather hope-and-guess maths to work out where it is pointing - and only 8 options, so not very useful on a boat. My boat may already have various meteorological sensors, that I would just need to figure out how to interface with. I will fidn out more this Thursday. From reading your WeatherStation page and other sources, it is sounding like the data collection issue is two-fold: A) communications interface and B) data format. > I'm told that all the smart people are using ultrasonic based sensors that use differential signal propagation to work out both the wind speed and direction with no moving parts. And apparently, can work out the rain density from the impacts of raindrops on the flat plate sensor. Cool stuff. The nearest I've seen to the mil-spec system a friend is working on is > https://www.kickstarter.com/projects/weatherflow/tempest-a-revolutionary-personal-weather-system?ref=discovery_category_newest&term=weather Alright, this is quite cool. I dug a bit deeper and found a site to buy the Tempest device: https://shop.weatherflow.com/collections/frontpage/products/tempest. This only sells the Tempest sensor device and a WiFi hub, no battery backup. From the page you linked it describes an option: Tempest - Storm & Fire Ready Kit This includes: "with Pro Hub and battery backup. Includes mobile / cellular communication fail-over during WiFi outages. Battery backup provides power to the Hub for up to seven days. Includes a prepaid data plan for at least 750 hours of continuous data streaming during WiFi outages." Perhaps there is some way to buy the Kickstarter package. I also researched, briefly a weatherproof option for the RPi. I found this: https://www.crowdsupply.com/openh/rubicon. Stackable to include the hub and battery backup with the stackable option? Perhaps. If the sensor is Wifi/Bluetooth, then this Pi may be stored in the cabin. But the display,a at the helm, would need weatherproofing. My curiosity is a weatherproof touch display at the helm. On the exploratory list! > This is all going to take some research with your boating contacts to find a good system. The downside is that any commercial system will almost certainly have its own display suff. You could do much worse than looking at > https://microship.com > and contacting Steve; he has done this sort of thing for many years and may have parts to point to or even sell. Alright, lots to explore! I will save his contact! > Once you've got sensors that can talk to an ESP or a Pi (so various digital connections or i2c or spi or even bluetooth?) you can consider getting the data to some other machine to display. Yes, this sounds the right approach! I read, I think in the Tempest stuff, about a EU weather data standard. > It may be that your sensors can connect directly to that display machine, saving one step. For a home built display you might go with a Pi attached to their 7" multi-touch display inside a very well sealed box. Yes, though it would be fantastic to have the touch screen (12"?) available/accessible in the weather. > I'm afraid this may take longer than 'by Thursday' :-) That is for sure! I am excited to dig into it, over time! Thank you for your guidance, tim! I look forwards to learning more. Kindly, Robert > tim > -- > tim Rowledge; > tim at rowledge.org > ; > http://www.rowledge.org/tim > Useful Latin Phrases:- Tam exanimis quam tunica nehru fio. = I am as dead as the nehru jacket. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: IMG_0547.JPG Type: image/jpeg Size: 992108 bytes Desc: not available URL: From Das.Linux at gmx.de Sun Sep 27 20:48:30 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Sun, 27 Sep 2020 22:48:30 +0200 Subject: [squeak-dev] bug in Debugger hacked during install of XTreams on Squeak6.0 alpha In-Reply-To: <174d127925d.dd5dbe0723989.6483616299426859564@zoho.com> References: <174d127925d.dd5dbe0723989.6483616299426859564@zoho.com> Message-ID: > On 27.09.2020, at 22:01, gettimothy via Squeak-dev wrote: > > Hi folks. > > Doing the XML-thing on a pristine 6.0alpha image > > Image > ----- > /home/wm/Squeak/images/XMLXtreamsSqueak6.0alpha-19881-64bit/XMLXtreamsSqueak6.0alpha-19881-64bit.image > Squeak6.0alpha > latest update: #19881 > Current Change Set: HomeProject > Image format 68021 (64 bit) > > Virtual Machine > --------------- > /home/wm/Squeak/vms/sqcogspur64linuxht202009271755/lib/squeak/5.0-202009271755/squeak > > > During the > Installer ss > project: 'MetacelloRepository'; > install: 'ConfigurationOfXtreams'. > (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load > > I get the expected error in XTIOHandle class >> intialize > initialize > self makeNonviewableInstances. > Smalltalk addToShutDownList: self after: Project > where I have to change DisplayScreen to Project . > > > Doing this in the Debugger (as I have previously done) threw a > > > MorphicDebugger(Object) MessageNotUnderstood: MorphicDebugger >> stepToStatement > > > I cancelled the install and modified > > MorphicDebugger(Debugger) >> contents: notifying: > comment out "self stepToStatement" down in the guts of the method. > > I rerun the > > Installer ss > project: 'MetacelloRepository'; > install: 'ConfigurationOfXtreams'. > (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load > > and am now able to modify the XTIOHandle class >> initialize method, on the fly in the debugger without throwing that error. > If possible, please replace The `installer`-dance with the configurations wiht: Metacello new configuration: 'Xtreams'; version: #bleedingEdge; load. It does things a tad different… -t From gettimothy at zoho.com Sun Sep 27 21:23:10 2020 From: gettimothy at zoho.com (gettimothy) Date: Sun, 27 Sep 2020 17:23:10 -0400 Subject: [squeak-dev] bug in Debugger hacked during install of XTreams on Squeak6.0 alpha In-Reply-To: References: <174d127925d.dd5dbe0723989.6483616299426859564@zoho.com> Message-ID: <174d172b684.d5072f59127382.7596247264350322984@zoho.com> Heh! Thank you, I will restart the process next week and see what happens. Cheers. ---- On Sun, 27 Sep 2020 16:47:53 -0400 Das.Linux at gmx.de wrote ---- > On 27.09.2020, at 22:01, gettimothy via Squeak-dev wrote: > > Hi folks. > > Doing the XML-thing on a pristine 6.0alpha image > > Image > ----- > /home/wm/Squeak/images/XMLXtreamsSqueak6.0alpha-19881-64bit/XMLXtreamsSqueak6.0alpha-19881-64bit.image > Squeak6.0alpha > latest update: #19881 > Current Change Set: HomeProject > Image format 68021 (64 bit) > > Virtual Machine > --------------- > /home/wm/Squeak/vms/sqcogspur64linuxht202009271755/lib/squeak/5.0-202009271755/squeak > > > During the > Installer ss > project: 'MetacelloRepository'; > install: 'ConfigurationOfXtreams'. > (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load > > I get the expected error in XTIOHandle class >> intialize > initialize > self makeNonviewableInstances. > Smalltalk addToShutDownList: self after: Project > where I have to change DisplayScreen to Project . > > > Doing this in the Debugger (as I have previously done) threw a > > > MorphicDebugger(Object) MessageNotUnderstood: MorphicDebugger >> stepToStatement > > > I cancelled the install and modified > > MorphicDebugger(Debugger) >> contents: notifying: > comment out "self stepToStatement" down in the guts of the method. > > I rerun the > > Installer ss > project: 'MetacelloRepository'; > install: 'ConfigurationOfXtreams'. > (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load > > and am now able to modify the XTIOHandle class >> initialize method, on the fly in the debugger without throwing that error. > If possible, please replace The `installer`-dance with the configurations wiht: Metacello new configuration: 'Xtreams'; version: #bleedingEdge; load. It does things a tad different… -t -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Sun Sep 27 22:12:40 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Mon, 28 Sep 2020 00:12:40 +0200 (CEST) Subject: [squeak-dev] The Trunk: CollectionsTests-eem.342.mcz In-Reply-To: References: Message-ID: Hi Eliot, The new test has revealed a difference in #nextOrNilSuchThat: between SharedQueue and SharedQueue2. In SharedQueue, if there's an element matching the argument block, all preceding elements are removed from the queue. That is not what I would expect from such method, and that behavior contradicts the method's comment. Fortunately, #nextOrNilSuchThat: has no real users in the Trunk. Etoys has EventSensor >> #hasDandDEvents which uses #nextOrNilSuchThat:, but that method won't remove any elements, just checks if there's a matching event. IMO, we should replace all uses of SharedQueue with SharedQueue2 for the 6.0 release. A bit of surgery will be required to safely migrate all alive instances... Levente On Sat, 19 Sep 2020, commits at source.squeak.org wrote: > Eliot Miranda uploaded a new version of CollectionsTests to project The Trunk: > http://source.squeak.org/trunk/CollectionsTests-eem.342.mcz > > ==================== Summary ==================== > > Name: CollectionsTests-eem.342 > Author: eem > Time: 19 September 2020, 1:11:15.902536 pm > UUID: ba823c3b-42f7-4276-8a25-8c59d26a3346 > Ancestors: CollectionsTests-ul.341 > > Refator SHaredQueue2Test so that now we also test SharedQueue. Add a test for peek and peekLast > > =============== Diff against CollectionsTests-ul.341 =============== > > Item was added: > + TestCase subclass: #AbstractSharedQueueTest > + instanceVariableNames: '' > + classVariableNames: '' > + poolDictionaries: '' > + category: 'CollectionsTests-Sequenceable'! > > Item was added: > + ----- Method: AbstractSharedQueueTest class>>isAbstract (in category 'testing') ----- > + isAbstract > + ^self class == thisContext methodClass! > > Item was added: > + ----- Method: AbstractSharedQueueTest>>queueClass (in category 'private') ----- > + queueClass > + ^self subclassResponsibility! > > Item was added: > + ----- Method: AbstractSharedQueueTest>>testBasics (in category 'tests') ----- > + testBasics > + | q | > + q := self queueClass new. > + > + self assert: nil equals: q nextOrNil. > + > + q nextPut: 5. > + self assert: 5 equals: q nextOrNil. > + self assert: nil equals: q nextOrNil! > > Item was added: > + ----- Method: AbstractSharedQueueTest>>testContention1 (in category 'tests') ----- > + testContention1 > + "here is a test case that breaks the standard SharedQueue from Squeak 3.8" > + > + | q r1 r2 | > + q := self queueClass new. > + q nextPut: 5. > + q nextPut: 10. > + > + self assert: 5 equals: q nextOrNil. > + > + [ r1 := q next ] fork. > + [ r2 := q next ] fork. > + Processor yield. "let the above two threads block" > + > + q nextPut: 10. > + Processor yield. > + > + self assert: 10 equals: r1. > + self assert: 10 equals: r2. > + self assert: nil equals: q nextOrNil! > > Item was added: > + ----- Method: AbstractSharedQueueTest>>testNextOrNilSuchThat (in category 'tests') ----- > + testNextOrNilSuchThat > + | q item | > + q := self queueClass new. > + q nextPut: 5. > + q nextPut: 6. > + > + item := q nextOrNilSuchThat: [ :x | x even ]. > + self assert: 6 equals: item. > + > + self assert: 5 equals: q nextOrNil. > + self assert: nil equals: q nextOrNil! > > Item was added: > + ----- Method: AbstractSharedQueueTest>>testPeeks (in category 'tests') ----- > + testPeeks > + | q | > + q := self queueClass new. > + > + self assert: nil equals: q peek. > + self assert: nil equals: q peekLast. > + > + q nextPut: #first; nextPut: #last. > + > + self assert: #first equals: q peek. > + self assert: #last equals: q peekLast. > + > + self assert: #first equals: q next. > + > + self assert: #last equals: q peek. > + self assert: #last equals: q peekLast! > > Item was changed: > + AbstractSharedQueueTest subclass: #SharedQueue2Test > - TestCase subclass: #SharedQueue2Test > instanceVariableNames: '' > classVariableNames: '' > poolDictionaries: '' > category: 'CollectionsTests-Sequenceable'! > > Item was added: > + ----- Method: SharedQueue2Test>>queueClass (in category 'private') ----- > + queueClass > + ^SharedQueue2! > > Item was removed: > - ----- Method: SharedQueue2Test>>testBasics (in category 'tests') ----- > - testBasics > - | q | > - q := SharedQueue2 new. > - > - self should: [ q nextOrNil = nil ]. > - > - q nextPut: 5. > - self should: [ q nextOrNil = 5 ]. > - self should: [ q nextOrNil = nil ]. > - > - ! > > Item was removed: > - ----- Method: SharedQueue2Test>>testContention1 (in category 'tests') ----- > - testContention1 > - "here is a test case that breaks the standard SharedQueue from Squeak 3.8" > - > - | q r1 r2 | > - q := SharedQueue2 new. > - q nextPut: 5. > - q nextPut: 10. > - > - self should: [ q nextOrNil = 5 ]. > - > - [ r1 := q next ] fork. > - [ r2 := q next ] fork. > - Processor yield. "let the above two threads block" > - > - q nextPut: 10. > - Processor yield. > - > - self should: [ r1 = 10 ]. > - self should: [ r2 = 10 ]. > - self should: [ q nextOrNil = nil ]. > - ! > > Item was removed: > - ----- Method: SharedQueue2Test>>testNextOrNilSuchThat (in category 'tests') ----- > - testNextOrNilSuchThat > - | q item | > - q := SharedQueue2 new. > - q nextPut: 5. > - q nextPut: 6. > - > - item := q nextOrNilSuchThat: [ :x | x even ]. > - self should: [ item = 6 ]. > - > - self should: [ q nextOrNil = 5 ]. > - self should: [ q nextOrNil = nil ]. > - ! > > Item was added: > + AbstractSharedQueueTest subclass: #SharedQueueTest > + instanceVariableNames: '' > + classVariableNames: '' > + poolDictionaries: '' > + category: 'CollectionsTests-Sequenceable'! > > Item was added: > + ----- Method: SharedQueueTest>>queueClass (in category 'private') ----- > + queueClass > + ^SharedQueue! From commits at source.squeak.org Sun Sep 27 23:22:30 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 27 Sep 2020 23:22:30 0000 Subject: [squeak-dev] The Inbox: Collections-ul.913.mcz Message-ID: Levente Uzonyi uploaded a new version of Collections to project The Inbox: http://source.squeak.org/inbox/Collections-ul.913.mcz ==================== Summary ==================== Name: Collections-ul.913 Author: ul Time: 28 September 2020, 1:19:09.686632 am UUID: 03eb89d7-ea1f-4116-99eb-0181542610f7 Ancestors: Collections-eem.912 HashedCollection changes: - make #capacity return the actual capacity of the collection instead of the size of the internal array. This change is obviously not backwards compatible. - introduce #arraySize to return the size of the internal array - improve the performance of #isEmpty when tally is 0 OrderedDictionary changes; - make it a subclass of PluggableDictionary. This lets one create e.g. an ordered identity dictionary without creating a subclass with duplicated behavior - simplify #initialize and #growTo: now that #capacity is accurate =============== Diff against Collections-eem.912 =============== Item was added: + ----- Method: HashedCollection>>arraySize (in category 'accessing') ----- + arraySize + "Answer the size of the internal array of the receiver." + + ^array size! Item was changed: ----- Method: HashedCollection>>capacity (in category 'accessing') ----- capacity + "Answer the current capacity of the receiver - aka the number of elements the receiver can hold without growing." - "Answer the current capacity of the receiver." + ^ array size * 3 // 4! - ^ array size! Item was changed: ----- Method: HashedCollection>>isEmpty (in category 'testing') ----- isEmpty "For non-weak collections, we can use the tally to speed up the empty check. For weak collections, we must use the traditional way because the tally is unreliable. Also see #size vs. #slowSize." + tally = 0 ifTrue: [ ^true ]. + ^array class isWeak and: [ super isEmpty ]! - ^ array class isWeak - ifFalse: [ tally = 0 ] - ifTrue: [ super isEmpty ]! Item was changed: ----- Method: HashedCollection>>removeAll (in category 'removing') ----- removeAll "remove all elements from this collection. Preserve the capacity" + self initialize: self arraySize! - self initialize: self capacity! Item was changed: + PluggableDictionary subclass: #OrderedDictionary - Dictionary subclass: #OrderedDictionary instanceVariableNames: 'order' classVariableNames: '' poolDictionaries: '' category: 'Collections-Sequenceable'! !OrderedDictionary commentStamp: 'mt 1/16/2015 10:42' prior: 0! I am an ordered dictionary. I have an additional index (called 'order') to keep track of the insertion order of my associations. The read access is not affected by the additional index. The index is updated in O(1) [time] when inserting new keys. For present keys, that insertion involves actions in O(n) to move the respective element to the end of the order. The growth operation compacts the index and takes O(n) additional time. NOTE: This is still no instance of SequenceableCollection. Having this, some protocols are missing and may require working on #associations, which is an Array and thus sequenceable.! Item was changed: ----- Method: OrderedDictionary>>growTo: (in category 'private') ----- growTo: anInteger - | oldOrder | super growTo: anInteger. + order := order grownBy: self capacity - order size! - oldOrder := order. - "Grow only to 75%. See #atNewIndex:put: in HashedCollection." - order := self class arrayType new: anInteger + 1 * 3 // 4. - order - replaceFrom: 1 - to: tally - with: oldOrder - startingAt: 1! Item was changed: ----- Method: OrderedDictionary>>initialize: (in category 'private') ----- initialize: n super initialize: n. + order := self class arrayType new: self capacity! - order := self class arrayType new: n + 1 * 3 // 4! From commits at source.squeak.org Sun Sep 27 23:22:41 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Sun, 27 Sep 2020 23:22:41 0000 Subject: [squeak-dev] The Inbox: CollectionsTests-ul.343.mcz Message-ID: Levente Uzonyi uploaded a new version of CollectionsTests to project The Inbox: http://source.squeak.org/inbox/CollectionsTests-ul.343.mcz ==================== Summary ==================== Name: CollectionsTests-ul.343 Author: ul Time: 28 September 2020, 1:21:54.222475 am UUID: 4971789d-4093-4368-a7b1-6f0d47c3bb55 Ancestors: CollectionsTests-eem.342 Hashed collections: - updated tests to use #arraySize instead of #capacity - added a few tests for #capacity =============== Diff against CollectionsTests-eem.342 =============== Item was added: + ----- Method: DictionaryTest>>testArraySizeOfNew (in category 'tests - basic') ----- + testArraySizeOfNew + "Test the special cases implemented in HashedCollection class >> #new and #new: using Dictionary as an example because HashedCollection is abstract." + + | goodPrimes | + goodPrimes := HashedCollection goodPrimes. + self assert: (goodPrimes includes: Dictionary new arraySize). + 0 to: 100 do: [ :size | + | dictionary | + dictionary := Dictionary new: size. + self assert: (goodPrimes includes: dictionary arraySize). + self assert: dictionary capacity >= size ]! Item was removed: - ----- Method: DictionaryTest>>testCapcityOfNew (in category 'tests - basic') ----- - testCapcityOfNew - "Test the special cases implemented in HashedCollection class >> #new and #new: using Dictionary as an example because HashedCollection is abstract." - - | goodPrimes | - goodPrimes := HashedCollection goodPrimes. - self assert: (goodPrimes includes: Dictionary new capacity). - 0 to: 100 do: [ :size | - | dictionary | - dictionary := Dictionary new: size. - self assert: (goodPrimes includes: dictionary capacity) ]! Item was added: + ----- Method: HashedCollectionTest>>testArraySize (in category 'tests - integrity') ----- + testArraySize + + | inconsistentCollections | + inconsistentCollections := HashedCollection allSubInstances reject: [ :each | + each class == MethodDictionary "MethodDictionary is the only HashedCollection which doesn't have prime array size" + ifTrue: [ each arraySize isPowerOfTwo ] + ifFalse: [ each arraySize isPrime ] ]. + self assert: inconsistentCollections isEmpty! Item was changed: ----- Method: HashedCollectionTest>>testCapacity (in category 'tests - integrity') ----- testCapacity + self assert: (HashedCollection allSubInstances allSatisfy: [ :each | + each arraySize * 3 // 4 = each capacity ])! - | inconsistentCollections | - inconsistentCollections := HashedCollection allSubInstances reject: [ :each | - each class == MethodDictionary "MethodDictionary is the only HashedCollection which doesn't have prime array size" - ifTrue: [ each capacity isPowerOfTwo ] - ifFalse: [ each capacity isPrime ] ]. - self assert: inconsistentCollections isEmpty! Item was changed: ----- Method: OrderedDictionaryTest>>testGrow (in category 'tests') ----- testGrow self + assert: 11 equals: sut arraySize; "next prime number to 7; see #setUp" + assert: sut capacity >= (sut instVarNamed: #order) size; "save memory" + assert: sut arraySize >(sut instVarNamed: #order) size. - assert: 11 equals: sut capacity; "next prime number to 7; see #setUp" - assert: sut capacity > (sut instVarNamed: #order) size. "save memory" + 1 to: sut arraySize do: [:ea | - 1 to: sut capacity do: [:ea | sut at: ea put: nil]. self + assert: sut arraySize > 11; + assert: sut arraySize > (sut instVarNamed: #order) size. "save memory"! - assert: sut capacity > 11; - assert: sut capacity > (sut instVarNamed: #order) size. "save memory"! Item was added: + ----- Method: SetTest>>testCapacity (in category 'tests') ----- + testCapacity + + | set capacity | + set := Set new. + self assert: set size = 0. + 10 timesRepeat: [ + capacity := set capacity. + self assert: set size < capacity. + set size + 1 to: capacity do: [ :i | + set add: i. + self assert: set capacity = capacity ]. + self assert: set size equals: capacity. + set add: set capacity + 1. + self assert: set capacity > capacity ]! Item was changed: ----- Method: WeakIdentityKeyDictionaryTest>>testFinalizeValuesWhenLastChainContinuesAtFront (in category 'tests') ----- testFinalizeValuesWhenLastChainContinuesAtFront + | objectWithHashModulo dictionary arraySize a b c | - | objectWithHashModulo dictionary capacity a b c | objectWithHashModulo := [ :requestedHash :modulo | | object | [ object := Object new. object hash \\ modulo = requestedHash ] whileFalse. object ]. dictionary := self classToBeTested new. + arraySize := dictionary arraySize. + a := objectWithHashModulo value: arraySize - 2 value: arraySize. - capacity := dictionary capacity. - a := objectWithHashModulo value: capacity - 2 value: capacity. dictionary at: a put: 1. + b := objectWithHashModulo value: arraySize - 1 value: arraySize. - b := objectWithHashModulo value: capacity - 1 value: capacity. dictionary at: b put: 2. + c := objectWithHashModulo value: arraySize - 2 value: arraySize. - c := objectWithHashModulo value: capacity - 2 value: capacity. dictionary at: c put: 3. + self assert: dictionary arraySize = arraySize. + self assert: (dictionary array at: arraySize - 1) key == a. + self assert: (dictionary array at: arraySize) key == b. - self assert: dictionary capacity = capacity. - self assert: (dictionary array at: capacity - 1) key == a. - self assert: (dictionary array at: capacity) key == b. self assert: (dictionary array at: 1) key == c. a := nil. Smalltalk garbageCollect. dictionary finalizeValues. self assert: (dictionary includesKey: b). self assert: (dictionary includesKey: c). + self assert: dictionary slowSize = 2! - self assert: dictionary slowSize = 2. - ! Item was changed: ----- Method: WeakSetTest>>testIncludes (in category 'tests') ----- testIncludes | weakSet transientFakeNilObject | weakSet := WeakSet new. #(true nil 1) do: [ :each | self deny: (weakSet includes: each) ]. weakSet add: true. self assert: (weakSet includes: true). weakSet remove: true. self deny: (weakSet includes: true). + transientFakeNilObject := ((1 to: 1000) detect: [ :each | each asString hash - nil hash \\ weakSet arraySize = 0 ]) asString. "this string will occupy the same slot as nil would" - transientFakeNilObject := ((1 to: 1000) detect: [ :each | each asString hash - nil hash \\ weakSet capacity = 0 ]) asString. "this string will occupy the same slot as nil would" weakSet add: transientFakeNilObject. transientFakeNilObject := transientFakeNilObject copy. Smalltalk garbageCollect. "get rid of transientFakeNilObject" self deny: (weakSet includes: transientFakeNilObject). self deny: (weakSet includes: nil) ! From commits at source.squeak.org Mon Sep 28 00:43:10 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 28 Sep 2020 00:43:10 0000 Subject: [squeak-dev] The Trunk: EToys-eem.402.mcz Message-ID: Eliot Miranda uploaded a new version of EToys to project The Trunk: http://source.squeak.org/trunk/EToys-eem.402.mcz ==================== Summary ==================== Name: EToys-eem.402 Author: eem Time: 27 September 2020, 5:43:02.068038 pm UUID: bbda2475-d7bb-4381-b306-0eab22b9ddad Ancestors: EToys-eem.401 CameraInterface: document camera:getParam:. Provide a utility for evaluating a block with grabbed frames. =============== Diff against EToys-eem.401 =============== Item was added: + ----- Method: CameraInterface class>>camera:framesDo:while: (in category 'utilities') ----- + camera: cameraNum framesDo: aBlock while: whileBlock + "Evaluate aBlock every time a frame becomes available. Answer a tuple of frames per second and number of 16ms delays per second. + Be destructive; use only one bitmap, overwriting its contents with each successive frame. + It is the sender's responsibility to open and close the camera." + | form bitmap delay start duration frameCount delayCount | + form := Form + extent: (self frameExtent: cameraNum) + depth: 32. + bitmap := form bits. + delay := Delay forMilliseconds: (1000 / 60) asInteger. "60 fps is fast" + start := Time utcMicrosecondClock. + frameCount := delayCount := 0. + [[(self camera: cameraNum getParam: 1) <= 0] whileTrue: + [delay wait. delayCount := delayCount + 1]. + self getFrameForCamera: cameraNum into: bitmap. + frameCount := frameCount + 1. + aBlock value: form. + whileBlock value] whileTrue. + ^{ frameCount * 1.0e6 / (duration := Time utcMicrosecondClock - start). + delayCount * 1.0e6 / duration } + + "| cameraNum | + self openCamera: (cameraNum := 1) width: 640 height: 480. + self waitForCameraStart: cameraNum. + [self camera: cameraNum framesDo: [:bitmap| bitmap display] while: [Sensor noButtonPressed]] ensure: + [self closeCamera: cameraNum]"! Item was changed: ----- Method: CameraInterface class>>camera:getParam: (in category 'camera ops') ----- camera: cameraNum getParam: paramNum + "Answer the given parameter for the given camera. + param 1 is the frame count, the number of frames grabbed since the last send of getFrameForCamera:into: + param 2 is the size of the bitmap in bytes required for an image" - "Answer the given parameter for the given camera." + + ^nil - - ^ nil ! From marcel.taeumel at hpi.de Mon Sep 28 06:50:36 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 08:50:36 +0200 Subject: [squeak-dev] The Inbox: Collections-ul.913.mcz In-Reply-To: References: Message-ID: Hi Levente. It looks like #arraySize is "private" or at least "tests only", right? I understand that you need a better accessor in tests regarding that growth behavior. Well, you could just add #arraySize via meta-programming to the test case where you need it. Hmm...  So, +1 for fixing #capacity. Not sure about #arraySize though ... :-) Best, Marcel Am 28.09.2020 01:22:39 schrieb commits at source.squeak.org : Levente Uzonyi uploaded a new version of Collections to project The Inbox: http://source.squeak.org/inbox/Collections-ul.913.mcz ==================== Summary ==================== Name: Collections-ul.913 Author: ul Time: 28 September 2020, 1:19:09.686632 am UUID: 03eb89d7-ea1f-4116-99eb-0181542610f7 Ancestors: Collections-eem.912 HashedCollection changes: - make #capacity return the actual capacity of the collection instead of the size of the internal array. This change is obviously not backwards compatible. - introduce #arraySize to return the size of the internal array - improve the performance of #isEmpty when tally is 0 OrderedDictionary changes; - make it a subclass of PluggableDictionary. This lets one create e.g. an ordered identity dictionary without creating a subclass with duplicated behavior - simplify #initialize and #growTo: now that #capacity is accurate =============== Diff against Collections-eem.912 =============== Item was added: + ----- Method: HashedCollection>>arraySize (in category 'accessing') ----- + arraySize + "Answer the size of the internal array of the receiver." + + ^array size! Item was changed: ----- Method: HashedCollection>>capacity (in category 'accessing') ----- capacity + "Answer the current capacity of the receiver - aka the number of elements the receiver can hold without growing." - "Answer the current capacity of the receiver." + ^ array size * 3 // 4! - ^ array size! Item was changed: ----- Method: HashedCollection>>isEmpty (in category 'testing') ----- isEmpty "For non-weak collections, we can use the tally to speed up the empty check. For weak collections, we must use the traditional way because the tally is unreliable. Also see #size vs. #slowSize." + tally = 0 ifTrue: [ ^true ]. + ^array class isWeak and: [ super isEmpty ]! - ^ array class isWeak - ifFalse: [ tally = 0 ] - ifTrue: [ super isEmpty ]! Item was changed: ----- Method: HashedCollection>>removeAll (in category 'removing') ----- removeAll "remove all elements from this collection. Preserve the capacity" + self initialize: self arraySize! - self initialize: self capacity! Item was changed: + PluggableDictionary subclass: #OrderedDictionary - Dictionary subclass: #OrderedDictionary instanceVariableNames: 'order' classVariableNames: '' poolDictionaries: '' category: 'Collections-Sequenceable'! !OrderedDictionary commentStamp: 'mt 1/16/2015 10:42' prior: 0! I am an ordered dictionary. I have an additional index (called 'order') to keep track of the insertion order of my associations. The read access is not affected by the additional index. The index is updated in O(1) [time] when inserting new keys. For present keys, that insertion involves actions in O(n) to move the respective element to the end of the order. The growth operation compacts the index and takes O(n) additional time. NOTE: This is still no instance of SequenceableCollection. Having this, some protocols are missing and may require working on #associations, which is an Array and thus sequenceable.! Item was changed: ----- Method: OrderedDictionary>>growTo: (in category 'private') ----- growTo: anInteger - | oldOrder | super growTo: anInteger. + order := order grownBy: self capacity - order size! - oldOrder := order. - "Grow only to 75%. See #atNewIndex:put: in HashedCollection." - order := self class arrayType new: anInteger + 1 * 3 // 4. - order - replaceFrom: 1 - to: tally - with: oldOrder - startingAt: 1! Item was changed: ----- Method: OrderedDictionary>>initialize: (in category 'private') ----- initialize: n super initialize: n. + order := self class arrayType new: self capacity! - order := self class arrayType new: n + 1 * 3 // 4! -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Mon Sep 28 06:58:52 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 28 Sep 2020 06:58:52 0000 Subject: [squeak-dev] The Trunk: Tools-ct.988.mcz Message-ID: Marcel Taeumel uploaded a new version of Tools to project The Trunk: http://source.squeak.org/trunk/Tools-ct.988.mcz ==================== Summary ==================== Name: Tools-ct.988 Author: ct Time: 25 September 2020, 4:56:31.616748 pm UUID: 31bde789-95f1-2745-90b1-4b8ff66adcbb Ancestors: Tools-ct.987 Hotfix for regression introduced in Tools-ct.987 (merge error). Please apologize any inconveniences! =============== Diff against Tools-ct.987 =============== Item was changed: ----- Method: Debugger>>contents:notifying: (in category 'accessing') ----- contents: aText notifying: aController "Accept new method source of the selected context." | selector classOfMethod category ctxt newMethod | contextStackIndex = 0 ifTrue: [^ false]. "First, handle some edge cases" selector := self selectedClass newParser parseSelector: aText. "selector isDoIt ifTrue: [ currentCompiledMethod := self compileDoIt: aText]." self flag: #todo. "ct: Recompile doIt method *without* creating method litters!! See Compiler>>#evaluateCue:ifFail:." selector = self selectedMessageName ifFalse: [ "Different message compiled, delegating to super" ^ super contents: aText notifying: aController]. self selectedContext isExecutingBlock ifTrue: [ "If we are in a block context, we need to rewind the stack before ." | home | home := self selectedContext activeHome. home ifNil: [ self inform: 'Method for block not found on stack, can''t edit and continue' translated. ^ false]. (self confirm: 'I will have to revert to the method from\which this block originated. Is that OK?' withCRs translated) ifFalse: [ ^ false]. self resetContext: home changeContents: false. "N.B. Only reset the contents if the compilation succeeds. If contents would be reset when compilation fails, both compiler error message and modifications were lost." ^ (self contents: aText notifying: aController) ifTrue: [self contentsChanged]; yourself]. classOfMethod := self selectedClass. category := self selectedMessageCategoryName. "Do the actual compilation" selector := classOfMethod compile: aText classified: category notifying: aController. selector ifNil: [^ false]. "compilation cancelled" "Update views" contents := aText. newMethod := classOfMethod compiledMethodAt: selector. newMethod isQuick ifTrue: [ self cutBackExecutionToSenderContext]. ctxt := interruptedProcess popTo: self selectedContext. ctxt == self selectedContext ifFalse: [self inform: 'Method saved, but current context unchanged\because of unwind error. Click OK to see error' withCRs translated] ifTrue: [ newMethod isQuick ifFalse: [ interruptedProcess restartTopWith: newMethod. + interruptedProcess stepToSendOrReturn]. - self stepToStatement]. contextVariablesInspector object: nil]. self resetContext: ctxt. Project current addDeferredUIMessage: [ self changed: #contentsSelection]. ^ true! From marcel.taeumel at hpi.de Mon Sep 28 06:59:36 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 08:59:36 +0200 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> <,> Message-ID: Hi all. Merged and fixed. Best, Marcel Am 25.09.2020 16:58:40 schrieb Thiede, Christoph : Hi Eliot, I'm very sorry for this bug, which was so unnecessary because my image has still a gigantic working copy ...! Tools-ct.988 should fix the issue, I tested it in a fresh trunk image. But it would be probably better if you could test it yourself, too. ;-) Best, Christoph PS: #stepToStatement gets relevant with the BytecodeDebugger changeset I proposed somewhen earlier in this year. I still would appreciate any review of it! :-) [http://www.hpi.de/] Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Donnerstag, 24. September 2020 21:42:37 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] tedious programming-in-the-debugger error needs fixing   Hi Christoph,     I didn't do my due dilligence in reviewing the changes.  I've just hit a problem.  When I accept as method in the debugger I get an MNU of stepToStatement.  MorphicDebugger needs to implement stepToStatement, or contents:notifying: needs not to send it.  Can you please fix ASAP?  Apologies!! On Thu, Sep 3, 2020 at 5:50 AM Christoph Thiede wrote: Hi Eliot, when you reported this issue, I was not able to reproduce it. About two months later, your new Sista bytecode set has arrived in the Trunk. Today, I encountered a very similar issue, could reproduce it, and trace it back to a comparison issue of Context >> #method which was affected by the introduction of CompiledBlock instances. Is it possible that you already used Sista in your image when reporting this bug? In case you were able to reproduce the bug: Can you confirm whether Tools-ct.987 (inbox) fixes the issue for you? :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html [http://forum.world.st/Squeak-Dev-f45488.html] -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Mon Sep 28 07:00:21 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 09:00:21 +0200 Subject: [squeak-dev] bug in Debugger hacked during install of XTreams on Squeak6.0 alpha In-Reply-To: <174d172b684.d5072f59127382.7596247264350322984@zoho.com> References: <174d127925d.dd5dbe0723989.6483616299426859564@zoho.com> <174d172b684.d5072f59127382.7596247264350322984@zoho.com> Message-ID: Hi Timothy, merged and fixed via Tools-ct.988. Best, Marcel Am 27.09.2020 23:23:23 schrieb gettimothy via Squeak-dev : Heh! Thank you, I will  restart the process next week and see what happens. Cheers. ---- On Sun, 27 Sep 2020 16:47:53 -0400 Das.Linux at gmx.de wrote ---- > On 27.09.2020, at 22:01, gettimothy via Squeak-dev wrote: > > Hi folks. > > Doing the XML-thing on a pristine 6.0alpha image > > Image > ----- > /home/wm/Squeak/images/XMLXtreamsSqueak6.0alpha-19881-64bit/XMLXtreamsSqueak6.0alpha-19881-64bit.image > Squeak6.0alpha > latest update: #19881 > Current Change Set: HomeProject > Image format 68021 (64 bit) > > Virtual Machine > --------------- > /home/wm/Squeak/vms/sqcogspur64linuxht202009271755/lib/squeak/5.0-202009271755/squeak > > > During the > Installer ss > project: 'MetacelloRepository'; > install: 'ConfigurationOfXtreams'. > (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load > > I get the expected error in XTIOHandle class >> intialize > initialize > self makeNonviewableInstances. > Smalltalk addToShutDownList: self after: Project > where I have to change DisplayScreen to Project . > > > Doing this in the Debugger (as I have previously done) threw a > > > MorphicDebugger(Object) MessageNotUnderstood: MorphicDebugger >> stepToStatement > > > I cancelled the install and modified > > MorphicDebugger(Debugger) >> contents: notifying: > comment out "self stepToStatement" down in the guts of the method. > > I rerun the > > Installer ss > project: 'MetacelloRepository'; > install: 'ConfigurationOfXtreams'. > (Smalltalk at: #ConfigurationOfXtreams) project bleedingEdge load > > and am now able to modify the XTIOHandle class >> initialize method, on the fly in the debugger without throwing that error. > If possible, please replace The `installer`-dance with the configurations wiht: Metacello new configuration: 'Xtreams'; version: #bleedingEdge; load. It does things a tad different… -t -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Mon Sep 28 07:22:16 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 09:22:16 +0200 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: Hi all. Something like an "OpenSmalltalk Launcher" would be nice. :-) Integrating, Squeak, Cuis, Pharo, ... Best, Marcel Am 25.09.2020 23:04:00 schrieb Jakob Reschke : Hello, Here is what I had to do to have the Pharo launcher launch a Squeak image: - Download and extract a Squeak VM and a supported image with .changes. - Download and install the latest Pharo Launcher release. - Start the launcher. - Open Settings > Pharo Launcher > edit the folder paths to my likings. - Click Store Settings at the top right. - Move the Squeak VM directory below the VM directory entered in the settings. Do not move the image yet. - Press shift+return, type vmExecutableName and open the method WindowsResolver>>#vmExecutableName. - Change WindowsResolver>>#vmExecutableName to return 'Squeak.exe'. - Close the system browser window. - Press the VMs button on the top right. It should now list the Squeak VM, for example 'squeak.cog.spur_win64x64_202003021730'. Close the VMs window. - Press Import at the top, choose the extracted image. It will be moved to the images directory from the Settings! - The image should now appear in the main list of the launcher window. - In the new directory of the image, put a file named "pharo.version" with the contents "0" next to the image. This will prevent the launcher from trying to find out the Pharo version of the image by running a pharo-version.st script with --headless on the image. Squeak does not support this. - Back in the launcher, in the combo box next to the Launch button at the top, drop down the list and select Edit Configurations. - For the Default configuration, select your Squeak VM instead of "0-x64" on the right. The latter would try and fail to download a fitting Pharo VM again. - Close the window with "Save & Select". - Now I am able to launch the Squeak image. To preserve the changes to the launcher image: - Press shift+return, enter Playground and press return to open a Workspace. - Evaluate the following: Smalltalk snapshot: true andQuit: true. Kind regards, Jakob Am Mi., 2. Sept. 2020 um 18:20 Uhr schrieb Sean DeNigris : > > I've been pontificating about unity between dialects, convinced more than ever that we need each other and have more of a simple (not easy) communication problem than anything else, and pestering the principals of various communities, so I decided to put some concrete action behind it - some "skin in the game" as we say in the U.S. > > After reading Trygve's frustration with Squeak image/VM management, and Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage and run Squeak (and GToolkit) VMs/images. > > The current limitation vs. launching standard Pharo images is that you have to download the VMs and images and manually install them due to differences in URL and other conventions. However once the files are in place, you can create templates allowing images to be created at will from different Squeak versions and automatically run with the correct VM. Auto-installation could probably be done if someone cares enough but it scratched my itch for the moment to manage GT images. I’m sure Cuis support could be added, but the hooks are now available and someone more familiar with its VM/image installation might have an easier time. > > Until my latest PR [1] is merged, brave souls can play with it and give feedback by loading my issue branch [2] into the latest Launcher release [3]. NB only tested on Mac. > > It seemed Trygve was at his wits' end, but maybe this and other small gestures will let him know how important he is to our community if not motivate him to resume his important work. > > I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what small action can you take today that unites us rather than divides us? We can be each others' supporters even if technical, vision and other differences keep our codebases somewhat distinct. I believe the seeds of wars are planted when I lack the grace to give those around me the benefit of the doubt that they mean well and are trying their best. There is more than enough of that already in the world. Let’s invent a *better* future… > > Your brother, > Sean > > 1. https://github.com/pharo-project/pharo-launcher/pull/503 > 2. https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks > 3. https://pharo.org/download > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Mon Sep 28 07:27:53 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 09:27:53 +0200 Subject: [squeak-dev] Font Handles (was: Inspector Custom Value Panes & Related) In-Reply-To: References: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de> Message-ID: Hi Karl. We might want to merge the behavior of those: PluggableCheckBoxMorph ThreePhaseButtonMorph class >> #checkBox Best, Marcel Am 26.09.2020 19:20:10 schrieb karl ramberg : Figured this out... Best, Karl On Sat, Sep 26, 2020 at 10:41 AM karl ramberg wrote: Hi, I tested using PluggableCheckBoxMorph but that widget is unwieldy. I'm not sure how to add a label to it. The widget seem to expect the model eg. FontChooserTool to perform the label ? PluggableCheckBoxMorph>>on: anObject getState: getStateSel action: actionSel label: labelSel menu: menuSel ... self label: (self model perform: labelSel). I'm not sure how that is supposed to work... There is no other use of the widget in the image I can look at either. Best, Karl On Tue, Sep 8, 2020 at 9:57 PM Thiede, Christoph wrote: Great ideas! I never get the right handle on the first try, merging them sounds very reasonable. :-) Hm ... doesn't the existence of such an "Apply" button impede Morphic's liveness? Why not just apply all changes as soon as a font/style has been selected? You could replace the buttons by a checkbox "selection only". Best, Christoph Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Dienstag, 8. September 2020 21:49 Uhr An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related   On Tue, Sep 8, 2020 at 8:16 AM Thiede, Christoph wrote: Hi Karl, I'm sorry your proposal is lost a bit - I think it's a nice addition! It does not look exactly like the TextMorph dialog, but I actually prefer "one dialog that fits it all" over the three separate handles for a TextMorph (that do not even all appear to work properly at the moment.) I think we should get this merged! :-) My change just adds handle support to the already existing  StringMorph>>changeFont The three TextMorph handles are not optimal and could be refactored into one handle. I made another change  to FontChooserTool where I added a button to apply text changes to the partition of text selected or a bulk change to all text in the pane. With this change I also enabled the emphasis pane to FontChooserTool.   [bild.png] Best, Karl Best, Christoph [http://www.hpi.de/] Von: Squeak-dev im Auftrag von karl ramberg Gesendet: Donnerstag, 3. September 2020 13:28:41 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Inspector Custom Value Panes & Related   Here is a change set that adds HaloHandle for font change to StringMorph Best, Karl On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel wrote: Hi Eric. > What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? Unfortunately, the current inspector framework has no such extension point. You can, however, prepare a custom inspector for your domain objects. See MorphInspector as an example.  Best, Marcel Am 02.09.2020 00:10:56 schrieb Eric Gade : Hi all, What is the proper way to make a "custom value pane" for my objects whenever they appear inside an Inspector? For my RISCV work, I'm looking to show a morphic representation of the bitfields when a given instruction's `self` property is highlighted in the Inspector window. As a related question, what's the best way to make a composed Morph that has StringMorphs of different fonts, sizes, and alignments? It seems like not all Fonts are available to StringMorph (TrueType ones, for example) -- is that correct? Thanks! -- Eric -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 21823 bytes Desc: not available URL: From lists at fniephaus.com Mon Sep 28 07:57:56 2020 From: lists at fniephaus.com (Fabio Niephaus) Date: Mon, 28 Sep 2020 09:57:56 +0200 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: Message-ID: On Sat, Sep 26, 2020 at 2:50 PM Stéphane Rollandin wrote: > > > muO's already on the projects page: > > https://squeak.org/projects/ > > Yes, I know. It's all a question of vocabulary I guess. > > Eliot proposed an "application" tab for commercial "projects". We > currently have a "project" tab. I'm not clear on what a project actually > is (it is not a word I use), and to me an application (not a word I use > either) does not have to be commercial. > > I call what I do "programs" of "software". I made two games, which I > call "games", not projects (in that instance the word seems very wrong > to me). I am working on a third one, of a different kind and scope, that > is indeed currently maybe a project but when somewhat finished will be a > software or a framework because it is actually a open-ended game engine. > There will still be a game there, though. > > In the current project tab at squeak.org, very different types of > software are listed together. If a different tab for applications is > going to be added, then it is maybe the opportunity to sort things out. > At the moment is feels like "oh look at all the things people made with > Squeak" but since there are not too many of them and they are very > different in nature it may give the impression that we just gathered > what we could, and that in fact Squeak is not much used at all. What you see under "Projects" is the first or maybe second iteration of what we came up with when we built the new website. All sources are open source, so anyone can contribute or make suggestions on how we could improve. Having different sections ("Commercial Applications", "Games", "Tools", ...) on the projects page sounds like a reasonable thing to have. Fabio > > > > Stef > From gettimothy at zoho.com Mon Sep 28 08:27:24 2020 From: gettimothy at zoho.com (gettimothy) Date: Mon, 28 Sep 2020 04:27:24 -0400 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: References: Message-ID: <174d3d2d75e.d00fd80e141993.6559233476249756539@zoho.com> Excellent idea and doable!Modify existing pharo app? Seems simplest route. Pharo team ok with that? ---- On Mon, 28 Sep 2020 03:22:16 -0400 marcel.taeumel at hpi.de wrote ---- Hi all.Something like an "OpenSmalltalk Launcher" would be nice. :-) Integrating, Squeak, Cuis, Pharo, ...Best,Marcel Am 25.09.2020 23:04:00 schrieb Jakob Reschke :Hello,Here is what I had to do to have the Pharo launcher launch a Squeak image:- Download and extract a Squeak VM and a supported image with .changes.- Download and install the latest Pharo Launcher release.- Start the launcher.- Open Settings > Pharo Launcher > edit the folder paths to my likings.- Click Store Settings at the top right.- Move the Squeak VM directory below the VM directory entered in thesettings. Do not move the image yet.- Press shift+return, type vmExecutableName and open the methodWindowsResolver>>#vmExecutableName.- Change WindowsResolver>>#vmExecutableName to return 'Squeak.exe'.- Close the system browser window.- Press the VMs button on the top right. It should now list the SqueakVM, for example 'squeak.cog.spur_win64x64_202003021730'. Close the VMswindow.- Press Import at the top, choose the extracted image. It will bemoved to the images directory from the Settings!- The image should now appear in the main list of the launcher window.- In the new directory of the image, put a file named "pharo.version"with the contents "0" next to the image. This will prevent thelauncher from trying to find out the Pharo version of the image byrunning a pharo-version.st script with --headless on the image. Squeakdoes not support this.- Back in the launcher, in the combo box next to the Launch button atthe top, drop down the list and select Edit Configurations.- For the Default configuration, select your Squeak VM instead of"0-x64" on the right. The latter would try and fail to download afitting Pharo VM again.- Close the window with "Save & Select".- Now I am able to launch the Squeak image.To preserve the changes to the launcher image:- Press shift+return, enter Playground and press return to open a Workspace.- Evaluate the following: Smalltalk snapshot: true andQuit: true.Kind regards,JakobAm Mi., 2. Sept. 2020 um 18:20 Uhr schrieb Sean DeNigris:>> I've been pontificating about unity between dialects, convinced more than ever that we need each other and have more of a simple (not easy) communication problem than anything else, and pestering the principals of various communities, so I decided to put some concrete action behind it - some "skin in the game" as we say in the U.S.>> After reading Trygve's frustration with Squeak image/VM management, and Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage and run Squeak (and GToolkit) VMs/images.>> The current limitation vs. launching standard Pharo images is that you have to download the VMs and images and manually install them due to differences in URL and other conventions. However once the files are in place, you can create templates allowing images to be created at will from different Squeak versions and automatically run with the correct VM. Auto-installation could probably be done if someone cares enough but it scratched my itch for the moment to manage GT images. I’m sure Cuis support could be added, but the hooks are now available and someone more familiar with its VM/image installation might have an easier time.>> Until my latest PR [1] is merged, brave souls can play with it and give feedback by loading my issue branch [2] into the latest Launcher release [3]. NB only tested on Mac.>> It seemed Trygve was at his wits' end, but maybe this and other small gestures will let him know how important he is to our community if not motivate him to resume his important work.>> I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what small action can you take today that unites us rather than divides us? We can be each others' supporters even if technical, vision and other differences keep our codebases somewhat distinct. I believe the seeds of wars are planted when I lack the grace to give those around me the benefit of the doubt that they mean well and are trying their best. There is more than enough of that already in the world. Let’s invent a *better* future…>> Your brother,> Sean>> 1. https://github.com/pharo-project/pharo-launcher/pull/503> 2. https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks> 3. https://pharo.org/download> -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Mon Sep 28 08:28:54 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 10:28:54 +0200 Subject: [squeak-dev] Unity Happens One Step at a Time (was Re: Porting my programs from Squeak 3.10.2 to 3.5) In-Reply-To: <174d3d2d75e.d00fd80e141993.6559233476249756539@zoho.com> References: <174d3d2d75e.d00fd80e141993.6559233476249756539@zoho.com> Message-ID: This repository could be easily created: https://github.com/OpenSmalltalk/opensmalltalk-launcher [https://github.com/OpenSmalltalk/opensmalltalk-launcher] Best, Marcel Am 28.09.2020 10:27:37 schrieb gettimothy via Squeak-dev : Excellent idea and doable! Modify existing pharo app?  Seems simplest route. Pharo team ok with that? ---- On Mon, 28 Sep 2020 03:22:16 -0400 marcel.taeumel at hpi.de wrote ---- Hi all. Something like an "OpenSmalltalk Launcher" would be nice. :-) Integrating, Squeak, Cuis, Pharo, ... Best, Marcel Am 25.09.2020 23:04:00 schrieb Jakob Reschke : Hello, Here is what I had to do to have the Pharo launcher launch a Squeak image: - Download and extract a Squeak VM and a supported image with .changes. - Download and install the latest Pharo Launcher release. - Start the launcher. - Open Settings > Pharo Launcher > edit the folder paths to my likings. - Click Store Settings at the top right. - Move the Squeak VM directory below the VM directory entered in the settings. Do not move the image yet. - Press shift+return, type vmExecutableName and open the method WindowsResolver>>#vmExecutableName. - Change WindowsResolver>>#vmExecutableName to return 'Squeak.exe'. - Close the system browser window. - Press the VMs button on the top right. It should now list the Squeak VM, for example 'squeak.cog.spur_win64x64_202003021730'. Close the VMs window. - Press Import at the top, choose the extracted image. It will be moved to the images directory from the Settings! - The image should now appear in the main list of the launcher window. - In the new directory of the image, put a file named "pharo.version" with the contents "0" next to the image. This will prevent the launcher from trying to find out the Pharo version of the image by running a pharo-version.st script with --headless on the image. Squeak does not support this. - Back in the launcher, in the combo box next to the Launch button at the top, drop down the list and select Edit Configurations. - For the Default configuration, select your Squeak VM instead of "0-x64" on the right. The latter would try and fail to download a fitting Pharo VM again. - Close the window with "Save & Select". - Now I am able to launch the Squeak image. To preserve the changes to the launcher image: - Press shift+return, enter Playground and press return to open a Workspace. - Evaluate the following: Smalltalk snapshot: true andQuit: true. Kind regards, Jakob Am Mi., 2. Sept. 2020 um 18:20 Uhr schrieb Sean DeNigris : > > I've been pontificating about unity between dialects, convinced more than ever that we need each other and have more of a simple (not easy) communication problem than anything else, and pestering the principals of various communities, so I decided to put some concrete action behind it - some "skin in the game" as we say in the U.S. > > After reading Trygve's frustration with Squeak image/VM management, and Eliot's mention of Pharo's solution, I've extended PharoLauncher to manage and run Squeak (and GToolkit) VMs/images. > > The current limitation vs. launching standard Pharo images is that you have to download the VMs and images and manually install them due to differences in URL and other conventions. However once the files are in place, you can create templates allowing images to be created at will from different Squeak versions and automatically run with the correct VM. Auto-installation could probably be done if someone cares enough but it scratched my itch for the moment to manage GT images. I’m sure Cuis support could be added, but the hooks are now available and someone more familiar with its VM/image installation might have an easier time. > > Until my latest PR [1] is merged, brave souls can play with it and give feedback by loading my issue branch [2] into the latest Launcher release [3]. NB only tested on Mac. > > It seemed Trygve was at his wits' end, but maybe this and other small gestures will let him know how important he is to our community if not motivate him to resume his important work. > > I challenge you, as a member of our wider Squeak/Pharo/Cuis family: what small action can you take today that unites us rather than divides us? We can be each others' supporters even if technical, vision and other differences keep our codebases somewhat distinct. I believe the seeds of wars are planted when I lack the grace to give those around me the benefit of the doubt that they mean well and are trying their best. There is more than enough of that already in the world. Let’s invent a *better* future… > > Your brother, > Sean > > 1. https://github.com/pharo-project/pharo-launcher/pull/503 [https://github.com/pharo-project/pharo-launcher/pull/503] > 2. https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks [https://github.com/seandenigris/pharo-launcher/tree/enh_vm-image-custom-hooks] > 3. https://pharo.org/download [https://pharo.org/download] > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Mon Sep 28 08:34:14 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 10:34:14 +0200 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: Message-ID: Hi all! Our current "Projects" tab should be able to also cover "commercial applications". We could add tags to those projects or split them up into two lists, the "commercial projects/applications" list being the first one to appear. I agree, we should highlight some projects. Our current wording is "This is an (almost) random selection.", which might be improved. No need for an extra tab, though. Best, Marcel Am 28.09.2020 09:58:23 schrieb Fabio Niephaus : On Sat, Sep 26, 2020 at 2:50 PM Stéphane Rollandin wrote: > > > muO's already on the projects page: > > https://squeak.org/projects/ > > Yes, I know. It's all a question of vocabulary I guess. > > Eliot proposed an "application" tab for commercial "projects". We > currently have a "project" tab. I'm not clear on what a project actually > is (it is not a word I use), and to me an application (not a word I use > either) does not have to be commercial. > > I call what I do "programs" of "software". I made two games, which I > call "games", not projects (in that instance the word seems very wrong > to me). I am working on a third one, of a different kind and scope, that > is indeed currently maybe a project but when somewhat finished will be a > software or a framework because it is actually a open-ended game engine. > There will still be a game there, though. > > In the current project tab at squeak.org, very different types of > software are listed together. If a different tab for applications is > going to be added, then it is maybe the opportunity to sort things out. > At the moment is feels like "oh look at all the things people made with > Squeak" but since there are not too many of them and they are very > different in nature it may give the impression that we just gathered > what we could, and that in fact Squeak is not much used at all. What you see under "Projects" is the first or maybe second iteration of what we came up with when we built the new website. All sources are open source, so anyone can contribute or make suggestions on how we could improve. Having different sections ("Commercial Applications", "Games", "Tools", ...) on the projects page sounds like a reasonable thing to have. Fabio > > > > Stef > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Mon Sep 28 08:38:22 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 10:38:22 +0200 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: Message-ID: > Having different sections ("Commercial Applications", "Games", "Tools", ...) on the projects page sounds like a reasonable thing to have. Maybe one could work on some integration with SqueakMap. Such project indexes (or databases) need to be (manually) updated. We are having a hard time doing that with SqueakMap already. Adding a new index through squeak.org does not seem to be like a sustainable idea. The current "projects" list on squeak.org is almost maintenance-free. :-D Let's consider that when we move forward. Best, Marcel Am 28.09.2020 09:58:23 schrieb Fabio Niephaus : On Sat, Sep 26, 2020 at 2:50 PM Stéphane Rollandin wrote: > > > muO's already on the projects page: > > https://squeak.org/projects/ > > Yes, I know. It's all a question of vocabulary I guess. > > Eliot proposed an "application" tab for commercial "projects". We > currently have a "project" tab. I'm not clear on what a project actually > is (it is not a word I use), and to me an application (not a word I use > either) does not have to be commercial. > > I call what I do "programs" of "software". I made two games, which I > call "games", not projects (in that instance the word seems very wrong > to me). I am working on a third one, of a different kind and scope, that > is indeed currently maybe a project but when somewhat finished will be a > software or a framework because it is actually a open-ended game engine. > There will still be a game there, though. > > In the current project tab at squeak.org, very different types of > software are listed together. If a different tab for applications is > going to be added, then it is maybe the opportunity to sort things out. > At the moment is feels like "oh look at all the things people made with > Squeak" but since there are not too many of them and they are very > different in nature it may give the impression that we just gathered > what we could, and that in fact Squeak is not much used at all. What you see under "Projects" is the first or maybe second iteration of what we came up with when we built the new website. All sources are open source, so anyone can contribute or make suggestions on how we could improve. Having different sections ("Commercial Applications", "Games", "Tools", ...) on the projects page sounds like a reasonable thing to have. Fabio > > > > Stef > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Mon Sep 28 09:01:05 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 11:01:05 +0200 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <,20200914154934.GA85344@shell.msen.com> Message-ID: Hi Christoph. >> It's true that in Morphic there is one distinct Process associated >> with the user interface for each Morphic world. > I'm not so sure about this. You can always do forks within UI code,  > so sometimes there are also multiple Processes within one Morphic  > world. Just think about Shout's background process or debugging > processes, and please keep in mind that Jakob justifiably proposed to > renew the process handling for non-modal dialogs, which maybe could > also result in multiple processes being active at the same time. There is (and should) only be a single UI process running in Morphic at any time. All other processes that can be scheduled must synchronize with that single UI process when doing UI stuff. Shout, for example, does that via #addDeferredUIMessage:, which is effectively a SharedQueue (using a Mutex or similar). Having two UI processes running (or be scheduled) at the same time will produce strange side effects or even lock up the image. I don't know. At least, things will slow down. You could change that by re-desiging the ownership partitioning of UI objects. Even then, there will be some process responsible for a set of UI objects. All other processes must synchronize into that one when talking to those (foreign) UI objects. I forgot. When does process switching happen through higher-priority processes having a semaphore ready (i.e. Delay >> #wait)? At byte-code level? Oder message-send level? Well, you might not see the effects of omitting such process synchronization immediately. However, occasionally, debuggers with "nil does not understand" may pop up. Causing you headaches. :-) Best, Marcel Am 24.09.2020 14:08:56 schrieb Thiede, Christoph : Hi all, sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: * Use DynamicVariables for storing the active variable states * Refactor and reduce the visibility of active variable accessors on Object * Clean up implementations of #activeHand and #primaryHand @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. [http://www.hpi.de/] > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. PS: Thanks for the hint to the new wiki page! Best, Christoph Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Montag, 14. September 2020 17:49:34 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic   On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong?  Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares [https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares]. -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Mon Sep 28 09:15:04 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 28 Sep 2020 02:15:04 -0700 Subject: [squeak-dev] The Trunk: CollectionsTests-eem.342.mcz In-Reply-To: References: Message-ID: <6E0B225C-2DF6-4DC1-9B8B-AF4CC27DA129@gmail.com> Hi Levente, > On Sep 27, 2020, at 3:13 PM, Levente Uzonyi wrote: > > Hi Eliot, > > The new test has revealed a difference in #nextOrNilSuchThat: between SharedQueue and SharedQueue2. > In SharedQueue, if there's an element matching the argument block, all preceding elements are removed from the queue. That is not what I would expect from such method, and that behavior contradicts the method's comment. > > Fortunately, #nextOrNilSuchThat: has no real users in the Trunk. > Etoys has EventSensor >> #hasDandDEvents which uses #nextOrNilSuchThat:, but that method won't remove any elements, just checks if there's a matching event. > > IMO, we should replace all uses of SharedQueue with SharedQueue2 for the 6.0 release. I don’t disagree :-) > A bit of surgery will be required to safely migrate all alive instances... We have two choices it seems to me 1. Keep the names the same 2. change the names so that SharedQueue becomes OldSharedQueue and SharedQueue2 becomes SharedQueue If we’re keeping the names then it’s likely that all instances will migrate simply through initialization of new event queues when new bout processes etc are defined at startup. If that’s the case we can do 2. later on when there are no instances of OldSharedQueue left. > > > Levente > >> On Sat, 19 Sep 2020, commits at source.squeak.org wrote: >> >> Eliot Miranda uploaded a new version of CollectionsTests to project The Trunk: >> http://source.squeak.org/trunk/CollectionsTests-eem.342.mcz >> >> ==================== Summary ==================== >> >> Name: CollectionsTests-eem.342 >> Author: eem >> Time: 19 September 2020, 1:11:15.902536 pm >> UUID: ba823c3b-42f7-4276-8a25-8c59d26a3346 >> Ancestors: CollectionsTests-ul.341 >> >> Refator SHaredQueue2Test so that now we also test SharedQueue. Add a test for peek and peekLast >> >> =============== Diff against CollectionsTests-ul.341 =============== >> >> Item was added: >> + TestCase subclass: #AbstractSharedQueueTest >> + instanceVariableNames: '' >> + classVariableNames: '' >> + poolDictionaries: '' >> + category: 'CollectionsTests-Sequenceable'! >> >> Item was added: >> + ----- Method: AbstractSharedQueueTest class>>isAbstract (in category 'testing') ----- >> + isAbstract >> + ^self class == thisContext methodClass! >> >> Item was added: >> + ----- Method: AbstractSharedQueueTest>>queueClass (in category 'private') ----- >> + queueClass >> + ^self subclassResponsibility! >> >> Item was added: >> + ----- Method: AbstractSharedQueueTest>>testBasics (in category 'tests') ----- >> + testBasics >> + | q | >> + q := self queueClass new. >> + + self assert: nil equals: q nextOrNil. >> + + q nextPut: 5. >> + self assert: 5 equals: q nextOrNil. >> + self assert: nil equals: q nextOrNil! >> >> Item was added: >> + ----- Method: AbstractSharedQueueTest>>testContention1 (in category 'tests') ----- >> + testContention1 >> + "here is a test case that breaks the standard SharedQueue from Squeak 3.8" >> + + | q r1 r2 | >> + q := self queueClass new. >> + q nextPut: 5. >> + q nextPut: 10. >> + + self assert: 5 equals: q nextOrNil. >> + + [ r1 := q next ] fork. >> + [ r2 := q next ] fork. >> + Processor yield. "let the above two threads block" >> + + q nextPut: 10. >> + Processor yield. >> + + self assert: 10 equals: r1. >> + self assert: 10 equals: r2. >> + self assert: nil equals: q nextOrNil! >> >> Item was added: >> + ----- Method: AbstractSharedQueueTest>>testNextOrNilSuchThat (in category 'tests') ----- >> + testNextOrNilSuchThat >> + | q item | >> + q := self queueClass new. >> + q nextPut: 5. >> + q nextPut: 6. >> + + item := q nextOrNilSuchThat: [ :x | x even ]. >> + self assert: 6 equals: item. >> + + self assert: 5 equals: q nextOrNil. >> + self assert: nil equals: q nextOrNil! >> >> Item was added: >> + ----- Method: AbstractSharedQueueTest>>testPeeks (in category 'tests') ----- >> + testPeeks >> + | q | >> + q := self queueClass new. >> + + self assert: nil equals: q peek. >> + self assert: nil equals: q peekLast. >> + + q nextPut: #first; nextPut: #last. >> + + self assert: #first equals: q peek. >> + self assert: #last equals: q peekLast. >> + + self assert: #first equals: q next. >> + + self assert: #last equals: q peek. >> + self assert: #last equals: q peekLast! >> >> Item was changed: >> + AbstractSharedQueueTest subclass: #SharedQueue2Test >> - TestCase subclass: #SharedQueue2Test >> instanceVariableNames: '' >> classVariableNames: '' >> poolDictionaries: '' >> category: 'CollectionsTests-Sequenceable'! >> >> Item was added: >> + ----- Method: SharedQueue2Test>>queueClass (in category 'private') ----- >> + queueClass >> + ^SharedQueue2! >> >> Item was removed: >> - ----- Method: SharedQueue2Test>>testBasics (in category 'tests') ----- >> - testBasics >> - | q | >> - q := SharedQueue2 new. >> - - self should: [ q nextOrNil = nil ]. >> - - q nextPut: 5. >> - self should: [ q nextOrNil = 5 ]. >> - self should: [ q nextOrNil = nil ]. >> - - ! >> >> Item was removed: >> - ----- Method: SharedQueue2Test>>testContention1 (in category 'tests') ----- >> - testContention1 >> - "here is a test case that breaks the standard SharedQueue from Squeak 3.8" >> - - | q r1 r2 | >> - q := SharedQueue2 new. >> - q nextPut: 5. >> - q nextPut: 10. >> - - self should: [ q nextOrNil = 5 ]. >> - - [ r1 := q next ] fork. >> - [ r2 := q next ] fork. >> - Processor yield. "let the above two threads block" >> - - q nextPut: 10. >> - Processor yield. >> - - self should: [ r1 = 10 ]. >> - self should: [ r2 = 10 ]. >> - self should: [ q nextOrNil = nil ]. >> - ! >> >> Item was removed: >> - ----- Method: SharedQueue2Test>>testNextOrNilSuchThat (in category 'tests') ----- >> - testNextOrNilSuchThat >> - | q item | >> - q := SharedQueue2 new. >> - q nextPut: 5. >> - q nextPut: 6. >> - - item := q nextOrNilSuchThat: [ :x | x even ]. >> - self should: [ item = 6 ]. >> - - self should: [ q nextOrNil = 5 ]. >> - self should: [ q nextOrNil = nil ]. >> - ! >> >> Item was added: >> + AbstractSharedQueueTest subclass: #SharedQueueTest >> + instanceVariableNames: '' >> + classVariableNames: '' >> + poolDictionaries: '' >> + category: 'CollectionsTests-Sequenceable'! >> >> Item was added: >> + ----- Method: SharedQueueTest>>queueClass (in category 'private') ----- >> + queueClass >> + ^SharedQueue! > From karlramberg at gmail.com Mon Sep 28 09:39:12 2020 From: karlramberg at gmail.com (karl ramberg) Date: Mon, 28 Sep 2020 11:39:12 +0200 Subject: [squeak-dev] Font Handles (was: Inspector Custom Value Panes & Related) In-Reply-To: References: <25c9f5cb1809467092521d5c925a15bc@student.hpi.uni-potsdam.de> Message-ID: PluggableCheckBoxMorph is just as wrapper for UpdatingThreePhaseButtonMorph afaics. My issue was ToolBuilders use of symbols instead of strings for the label. I just made a method in FontChooserTool that returned the label as a string. Best, Karl On Mon, Sep 28, 2020 at 9:28 AM Marcel Taeumel wrote: > Hi Karl. > > We might want to merge the behavior of those: > > PluggableCheckBoxMorph > ThreePhaseButtonMorph class >> #checkBox > > Best, > Marcel > > Am 26.09.2020 19:20:10 schrieb karl ramberg : > Figured this out... > Best, > Karl > > On Sat, Sep 26, 2020 at 10:41 AM karl ramberg > wrote: > >> Hi, >> I tested using PluggableCheckBoxMorph but that widget is unwieldy. >> I'm not sure how to add a label to it. >> The widget seem to expect the model eg. FontChooserTool to perform the >> label ? >> >> PluggableCheckBoxMorph>>on: anObject getState: getStateSel action: >> actionSel label: labelSel menu: menuSel >> ... >> self label: (self model perform: labelSel). >> >> I'm not sure how that is supposed to work... >> >> There is no other use of the widget in the image I can look at either. >> >> Best, >> Karl >> >> On Tue, Sep 8, 2020 at 9:57 PM Thiede, Christoph < >> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >> >>> Great ideas! I never get the right handle on the first try, merging them >>> sounds very reasonable. :-) >>> >>> Hm ... doesn't the existence of such an "Apply" button impede Morphic's >>> liveness? Why not just apply all changes as soon as a font/style has been >>> selected? You could replace the buttons by a checkbox "selection only". >>> >>> >>> Best, >>> >>> Christoph >>> >>> ------------------------------ >>> *Von:* Squeak-dev im >>> Auftrag von karl ramberg >>> *Gesendet:* Dienstag, 8. September 2020 21:49 Uhr >>> *An:* The general-purpose Squeak developers list >>> *Betreff:* Re: [squeak-dev] Inspector Custom Value Panes & Related >>> >>> >>> >>> On Tue, Sep 8, 2020 at 8:16 AM Thiede, Christoph < >>> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >>> >>>> Hi Karl, >>>> >>>> >>>> I'm sorry your proposal is lost a bit - I think it's a nice addition! >>>> It does not look exactly like the TextMorph dialog, but I actually prefer >>>> "one dialog that fits it all" over the three separate handles for a >>>> TextMorph (that do not even all appear to work properly at the moment.) I >>>> think we should get this merged! :-) >>>> >>>> >>>> My change just adds handle support to the already existing >>> StringMorph>>changeFont >>> >>> The three TextMorph handles are not optimal and could be refactored into >>> one handle. >>> >>> I made another change to FontChooserTool where I added a button to >>> apply text changes to the partition of text selected or a bulk change to >>> all text in the pane. With this change I also enabled the emphasis pane to >>> FontChooserTool. >>> >>> [image: bild.png] >>> >>> Best, >>> Karl >>> >>> >>> Best, >>>> >>>> Christoph >>>> >>>> ------------------------------ >>>> *Von:* Squeak-dev im >>>> Auftrag von karl ramberg >>>> *Gesendet:* Donnerstag, 3. September 2020 13:28:41 >>>> *An:* The general-purpose Squeak developers list >>>> *Betreff:* Re: [squeak-dev] Inspector Custom Value Panes & Related >>>> >>>> Here is a change set that adds HaloHandle for font change to StringMorph >>>> >>>> Best, >>>> Karl >>>> >>>> On Thu, Sep 3, 2020 at 9:56 AM Marcel Taeumel >>>> wrote: >>>> >>>>> Hi Eric. >>>>> >>>>> > What is the proper way to make a "custom value pane" for my objects >>>>> whenever they appear inside an Inspector? >>>>> >>>>> Unfortunately, the current inspector framework has no such extension >>>>> point. You can, however, prepare a custom inspector for your domain >>>>> objects. See MorphInspector as an example. >>>>> >>>>> Best, >>>>> Marcel >>>>> >>>>> Am 02.09.2020 00:10:56 schrieb Eric Gade : >>>>> Hi all, >>>>> >>>>> What is the proper way to make a "custom value pane" for my objects >>>>> whenever they appear inside an Inspector? For my RISCV work, I'm looking to >>>>> show a morphic representation of the bitfields when a given instruction's >>>>> `self` property is highlighted in the Inspector window. >>>>> >>>>> As a related question, what's the best way to make a composed Morph >>>>> that has StringMorphs of different fonts, sizes, and alignments? It seems >>>>> like not all Fonts are available to StringMorph (TrueType ones, for >>>>> example) -- is that correct? >>>>> >>>>> Thanks! >>>>> >>>>> -- >>>>> Eric >>>>> >>>>> >>>>> >>>> >>> > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: bild.png Type: image/png Size: 21823 bytes Desc: not available URL: From marcel.taeumel at hpi.de Mon Sep 28 10:29:04 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 12:29:04 +0200 Subject: [squeak-dev] The Trunk: Graphics-pre.439.mcz In-Reply-To: References: Message-ID: Hi Jakob, it is start-of-heading (SOH, 001). See https://en.wikipedia.org/wiki/Control_character#Transmission_control [https://en.wikipedia.org/wiki/Control_character#Transmission_control] It is a compromise for "non-data section of a data stream" :-) That is, the morph or form. Best, Marcel Am 25.09.2020 18:41:04 schrieb Jakob Reschke : What is an SoH character? Does not look like ASCII 001 is meant here. Am Fr., 25. Sept. 2020 um 09:48 Uhr schrieb : > > Patrick Rein uploaded a new version of Graphics to project The Trunk: > http://source.squeak.org/trunk/Graphics-pre.439.mcz > > ==================== Summary ==================== > > Name: Graphics-pre.439 > Author: pre > Time: 25 September 2020, 9:48:35.481674 am > UUID: e6d3328b-645a-e846-a3a9-c3c61b479c23 > Ancestors: Graphics-tonyg.438 > > Adds code to handle the case in which a text anchor attribute is used on more than the SoH character AND another SoH character is within the range where the text anchor was applied. Without this, the first morph would be put at the location of the second SoH character. > > =============== Diff against Graphics-tonyg.438 =============== > > Item was changed: > ----- Method: CharacterScanner>>embeddedObject (in category 'stop conditions') ----- > embeddedObject > > + | previousAttributes newAttributes | > pendingKernX := 0. > + "If a text anchor was already at the previous index, it was already dealt with" > + previousAttributes := lastIndex > 1 ifTrue: [text attributesAt: lastIndex - 1] ifFalse: [#()]. > + newAttributes := (text attributesAt: lastIndex) copyWithoutAll: previousAttributes. > + (newAttributes reject: [:each | each anchoredMorph isNil]) > - ((text attributesAt: lastIndex) reject: [:each | each anchoredMorph isNil]) > ifNotEmpty: [:attributes | (self placeEmbeddedObjectsFrom: attributes) ifTrue: [^ true]]. > self setFont. > > "Note: if ever several objects are embedded on same character, only indent lastIndex once" > lastIndex := lastIndex + 1. > + ^ false! > - ^false! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 10:42:00 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 10:42:00 +0000 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: , Message-ID: <922b664c26bc4c57b4a9eae7b1b3d78b@student.hpi.uni-potsdam.de> There are so many small and big projects on GitHub that are not yet listed on squeak.org: https://github.com/search?l=Smalltalk&p=1&q=squeak&type=Repositories Just for example Squot, smalltalkCI, Autocompletion, ... And also I would like to list several smaller tools and frameworks somewhere that, despite their small size, might be very interesting for your personal Squeak workflow: Autocompletion, MessageSendRecorder, WindowAcrobatics, and so many others (just telling some names from my individual filter bubble). SqueakMap integration sounds reasonable, even if I personally do not really use SqueakMap; but I think it would be good to keep an up-to-date project list on squeak.org, and even it is only good for the public visibility of Squeak. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 28. September 2020 10:38:22 An: squeak-dev Betreff: Re: [squeak-dev] We need an Applications tab on squeak.org > Having different sections ("Commercial Applications", "Games", "Tools", ...) on the projects page sounds like a reasonable thing to have. Maybe one could work on some integration with SqueakMap. Such project indexes (or databases) need to be (manually) updated. We are having a hard time doing that with SqueakMap already. Adding a new index through squeak.org does not seem to be like a sustainable idea. The current "projects" list on squeak.org is almost maintenance-free. :-D Let's consider that when we move forward. Best, Marcel Am 28.09.2020 09:58:23 schrieb Fabio Niephaus : On Sat, Sep 26, 2020 at 2:50 PM Stéphane Rollandin wrote: > > > muO's already on the projects page: > > https://squeak.org/projects/ > > Yes, I know. It's all a question of vocabulary I guess. > > Eliot proposed an "application" tab for commercial "projects". We > currently have a "project" tab. I'm not clear on what a project actually > is (it is not a word I use), and to me an application (not a word I use > either) does not have to be commercial. > > I call what I do "programs" of "software". I made two games, which I > call "games", not projects (in that instance the word seems very wrong > to me). I am working on a third one, of a different kind and scope, that > is indeed currently maybe a project but when somewhat finished will be a > software or a framework because it is actually a open-ended game engine. > There will still be a game there, though. > > In the current project tab at squeak.org, very different types of > software are listed together. If a different tab for applications is > going to be added, then it is maybe the opportunity to sort things out. > At the moment is feels like "oh look at all the things people made with > Squeak" but since there are not too many of them and they are very > different in nature it may give the impression that we just gathered > what we could, and that in fact Squeak is not much used at all. What you see under "Projects" is the first or maybe second iteration of what we came up with when we built the new website. All sources are open source, so anyone can contribute or make suggestions on how we could improve. Having different sections ("Commercial Applications", "Games", "Tools", ...) on the projects page sounds like a reasonable thing to have. Fabio > > > > Stef > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 10:55:45 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 10:55:45 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <,20200914154934.GA85344@shell.msen.com> , Message-ID: <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> Hi all, Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? [cid:3848a1c6-d67f-4999-a714-ffafff2b4a22] Marcel, I absolutely see that you should not run two UI processes side by side in the sense that both of them handle events at the same time. (As a consequence of some attempts in my image, I accidentally ran into this situation a few times (2 worlds active at the same time), and in practice, about every second event is just ignored.) I was only talking about deferred event-handling actions, such as: mouseDown: anEvent [|result| result := self doLongComputation. self currentHand attachMorph: result asMorph] fork. In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice, but in other examples, this is not so easy, for example when some application logic raises a FileDoesNotExistException which opens some UI stuff again ... We could, of course, try to use more deferred UI messages in such cases and maybe use Promises or some other mechanism to resume the background process after that, but I am not sure we can completely get rid of any sends to activeHand, activeWorld, and activeEvent in every non-UI process. What do you think? :-) Best, Christoph ________________________________ Von: Taeumel, Marcel Gesendet: Montag, 28. September 2020 11:01:05 An: Thiede, Christoph; squeak-dev Betreff: Re: AW: [squeak-dev] Changeset: Eliminating global state from Morphic Hi Christoph. >> It's true that in Morphic there is one distinct Process associated >> with the user interface for each Morphic world. > I'm not so sure about this. You can always do forks within UI code, > so sometimes there are also multiple Processes within one Morphic > world. Just think about Shout's background process or debugging > processes, and please keep in mind that Jakob justifiably proposed to > renew the process handling for non-modal dialogs, which maybe could > also result in multiple processes being active at the same time. There is (and should) only be a single UI process running in Morphic at any time. All other processes that can be scheduled must synchronize with that single UI process when doing UI stuff. Shout, for example, does that via #addDeferredUIMessage:, which is effectively a SharedQueue (using a Mutex or similar). Having two UI processes running (or be scheduled) at the same time will produce strange side effects or even lock up the image. I don't know. At least, things will slow down. You could change that by re-desiging the ownership partitioning of UI objects. Even then, there will be some process responsible for a set of UI objects. All other processes must synchronize into that one when talking to those (foreign) UI objects. I forgot. When does process switching happen through higher-priority processes having a semaphore ready (i.e. Delay >> #wait)? At byte-code level? Oder message-send level? Well, you might not see the effects of omitting such process synchronization immediately. However, occasionally, debuggers with "nil does not understand" may pop up. Causing you headaches. :-) Best, Marcel Am 24.09.2020 14:08:56 schrieb Thiede, Christoph : Hi all, sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: * Use DynamicVariables for storing the active variable states * Refactor and reduce the visibility of active variable accessors on Object * Clean up implementations of #activeHand and #primaryHand @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. PS: Thanks for the hint to the new wiki page! Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Montag, 14. September 2020 17:49:34 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong? Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 28985 bytes Desc: pastedImage.png URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 32042 bytes Desc: pastedImage.png URL: From marcel.taeumel at hpi.de Mon Sep 28 10:56:24 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 12:56:24 +0200 Subject: [squeak-dev] Trunk broken (was: The Trunk: System-topa.1174.mcz) In-Reply-To: <25cbd26a894f461fa6627a2a18a6e667@student.hpi.uni-potsdam.de> References: <25cbd26a894f461fa6627a2a18a6e667@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, in my image, too, the "Demo +"-Theme was not updated. Going back to "Squeak" and then hit "Extras > Themes & Colors > Set High-DPI Mode" fixed those issues in my image. The bug is most likely in the full-copy of the current theme for the Demo/High-DPI mode. And UserInterfaceTheme class >> #cleanUpAndReset not considering that copy. Best, Marcel Am 25.09.2020 17:19:22 schrieb Thiede, Christoph : Hi Tobias, hi all, this update just broke my image functionality sensitively! Right after loading System-topa.1174, the following happens to my fresh (!) trunk image the only adjustment to which I have applied being doing "Preferences changeFontSize: 12" once: - The background image disappears, I'm seeing a single dull gray tone as a background instead - Shout in all tools is broken (no syntax highlight any longer) - Balloons are semi-transparent - Dialog buttons have lost their color - Also, the fonts look a bit larger now, but I cannot say whether I like the change or not. I'm using a 202003021730 VM on Windows 10 64 bit. None of these effects occurs if my font size is set to zero when loading the update. I do not yet completely understand the changes you made, but IMO it would be a very good idea to remove this commit again from the trunk, if possible, and re-add it with an appropriate preamble and postload. This screenshot shows the full extent of the crisis: Best, Christoph Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Donnerstag, 24. September 2020 23:13 Uhr An: squeak-dev at lists.squeakfoundation.org; packages at lists.squeakfoundation.org Betreff: [squeak-dev] The Trunk: System-topa.1174.mcz   Tobias Pape uploaded a new version of System to project The Trunk: http://source.squeak.org/trunk/System-topa.1174.mcz [http://source.squeak.org/trunk/System-topa.1174.mcz] ==================== Summary ==================== Name: System-topa.1174 Author: topa Time: 24 September 2020, 11:14:16.007835 pm UUID: 8f692d9f-4973-4aac-b181-9a2643089f8a Ancestors: System-eem.1173 Make it so that Theme fonts are looked up on usage, not on theme creation. =============== Diff against System-eem.1173 =============== Item was changed:   ----- Method: CommunityTheme class>>addDarkFonts: (in category 'instance creation') -----   addDarkFonts: aUserInterfaceTheme          "Set-up fonts."          aUserInterfaceTheme +                set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode]; +                set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; +                set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; +                set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]! -                set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode); -                set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); -                set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); -                set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode)! Item was changed:   ----- Method: CommunityTheme class>>addDarkSyntaxHighlighting: (in category 'instance creation') -----   addDarkSyntaxHighlighting: aUserInterfaceTheme          "self createDark apply." +        | normal bold italic underlined | normal  := TextEmphasis normal.  bold:=TextEmphasis bold.  italic:=TextEmphasis italic.  underlined := TextEmphasis underlined. -        | normal bold italic underlined darkMap | normal  := TextEmphasis normal.  bold:=TextEmphasis bold.  italic:=TextEmphasis italic.  underlined := TextEmphasis underlined.  darkMap := StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9.          aUserInterfaceTheme                  set: #color for: #TextAction to: self dbBlue;                           set: #default for: #SHTextStylerST80 to: {self dbForeground};                  set: #invalid for: #SHTextStylerST80 to: {self dbInvalid};                  set: #excessCode for: #SHTextStylerST80 to: {self dbInvalid twiceDarker};                  "Descriptive text for humans, italicized."                  set: #comment for: #SHTextStylerST80 to: {self dbComment. italic};                  set: #unfinishedComment for: #SHTextStylerST80 to: {self dbComment darker. italic};                  set: #'$' for: #SHTextStylerST80 to: {self dbConstant};                  set: #character for: #SHTextStylerST80 to: {self dbConstant};                  set: #integer for: #SHTextStylerST80 to: {self dbConstant};                  set: #number for: #SHTextStylerST80 to: {self dbConstant};                  set: #- for: #SHTextStylerST80 to: {self dbForeground. bold};                  set: #= for: #SHTextStylerST80 to: {self dbForeground. bold};                  set: #symbol for: #SHTextStylerST80 to: {self dbBedrock};                  set: #stringSymbol for: #SHTextStylerST80 to: {self dbBedrock};                  set: #literalArray for: #SHTextStylerST80 to: {self dbForeground};                  set: #string for: #SHTextStylerST80 to: {self dbConstant};                  set: #unfinishedString for: #SHTextStylerST80 to: {self dbConstant darker};                  set: #assignment for: #SHTextStylerST80 to: {nil. bold};                  set: #ansiAssignment for: #SHTextStylerST80 to: {nil. bold};                  set: #literal for: #SHTextStylerST80 to: {nil. bold};                  set: #keyword for: #SHTextStylerST80 to: {self dbMessage};                  set: #binary for: #SHTextStylerST80 to: {self dbForeground. bold};                  set: #unary for: #SHTextStylerST80 to: {self dbMessage};                  set: #incompleteKeyword for: #SHTextStylerST80 to: {self dbMessage darker. {underlined. bold}};                  set: #incompleteBinary for: #SHTextStylerST80 to: {self dbMessage darker. underlined};                  set: #incompleteUnary for: #SHTextStylerST80 to: {self dbMessage darker. underlined};                  set: #undefinedKeyword for: #SHTextStylerST80 to: {self dbInvalid};                  set: #undefinedBinary for: #SHTextStylerST80 to: {self dbInvalid};                  set: #undefinedUnary for: #SHTextStylerST80 to: {self dbInvalid};                  "Delineate the selector (good for new users), and make the method look like a mini-document with a title."                  set: #patternKeyword for: #SHTextStylerST80 to: {self dbMessage lighter.  {bold. underlined}};                  set: #patternBinary for: #SHTextStylerST80 to: {nil. bold};                  set: #patternUnary for: #SHTextStylerST80 to: {self dbMessage lighter.  {bold. underlined}};                     set: #self for: #SHTextStylerST80 to: {self dbBedrock. bold};                  set: #super for: #SHTextStylerST80 to: {self dbBedrock. bold};                  set: #true for: #SHTextStylerST80 to: {self dbBedrock. bold};                  set: #false for: #SHTextStylerST80 to: {self dbBedrock. bold};                  set: #nil for: #SHTextStylerST80 to: {self dbBedrock. bold};                  set: #thisContext for: #SHTextStylerST80 to: {self dbBedrock. bold};                  set: #return for: #SHTextStylerST80 to: {self dbForeground. bold}; +                set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""};   +                set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; -                set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"};    -                set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"};                  set: #blockPatternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter};                  set: #blockArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter};                  set: #argument for: #SHTextStylerST80 to: {self dbSelection twiceLighter};                  set: #blockArgColon for: #SHTextStylerST80 to: {self dbBedrock};                  set: #leftParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter};                  set: #rightParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter};                  set: #leftParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter};                  set: #rightParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter};                  set: #leftParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock};                  set: #rightParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock};                  set: #leftParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #rightParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #leftParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #rightParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #leftParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #rightParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #leftParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #rightParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #leftParenthesis7 for: #SHTextStylerST80 to: {Color yellow};                  set: #rightParenthesis7 for: #SHTextStylerST80 to: {Color yellow};                  set: #blockStart for: #SHTextStylerST80 to: {self dbBedrock muchLighter};                  set: #blockEnd for: #SHTextStylerST80 to: {self dbBedrock muchLighter};                  set: #blockStart1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter};                  set: #blockEnd1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter};                  set: #blockStart2 for: #SHTextStylerST80 to: {self dbBedrock};                  set: #blockEnd2 for: #SHTextStylerST80 to: {self dbBedrock};                  set: #blockStart3 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #blockEnd3 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #blockStart4 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #blockEnd4 for: #SHTextStylerST80 to: {self dbPurple muchLighter};                  set: #blockStart5 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #blockEnd5 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #blockStart6 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #blockEnd6 for: #SHTextStylerST80 to: {self dbOrange muchLighter};                  set: #blockStart7 for: #SHTextStylerST80 to: {Color yellow};                  set: #blockEnd7 for: #SHTextStylerST80 to: {Color yellow};                                                                                                                                                                                                                                                                                                                        set: #arrayStart for: #SHTextStylerST80 to: {self dbBedrock};                  set: #arrayEnd for: #SHTextStylerST80 to: {self dbBedrock};                  set: #arrayStart1 for: #SHTextStylerST80 to: {self dbForeground};                  set: #arrayEnd1 for: #SHTextStylerST80 to: {self dbForeground};                  set: #byteArrayStart for: #SHTextStylerST80 to: {self dbForeground};                  set: #byteArrayEnd for: #SHTextStylerST80 to: {self dbForeground};                  set: #byteArrayStart1 for: #SHTextStylerST80 to: {self dbForeground};                  set: #byteArrayEnd1 for: #SHTextStylerST80 to: {self dbForeground};                  set: #leftBrace for: #SHTextStylerST80 to: {self dbForeground};                  set: #rightBrace for: #SHTextStylerST80 to: {self dbForeground};                  set: #cascadeSeparator for: #SHTextStylerST80 to: {self dbForeground};                  set: #statementSeparator for: #SHTextStylerST80 to: {self dbForeground};                  set: #externalCallType for: #SHTextStylerST80 to: {self dbForeground};                  set: #externalCallTypePointerIndicator for: #SHTextStylerST80 to: {self dbForeground};                  set: #primitiveOrExternalCallStart for: #SHTextStylerST80 to: {self dbForeground};                  set: #primitiveOrExternalCallEnd for: #SHTextStylerST80 to: {self dbForeground};                  set: #methodTempBar for: #SHTextStylerST80 to: {self dbBedrock};                  set: #blockTempBar for: #SHTextStylerST80 to: {self dbBedrock};                  set: #blockArgsBar for: #SHTextStylerST80 to: {self dbBedrock};                  set: #primitive for: #SHTextStylerST80 to: {self dbGreen lighter. bold};                  set: #pragmaKeyword for: #SHTextStylerST80 to: {self dbGreen. bold};                  set: #pragmaUnary for: #SHTextStylerST80 to: {self dbGreen. bold};                  set: #pragmaBinary for: #SHTextStylerST80 to: {self dbGreen. bold};                                                                                      set: #externalFunctionCallingConvention for: #SHTextStylerST80 to: {self dbGreen. bold};                  set: #module for: #SHTextStylerST80 to: {self dbGreen. bold};                  set: #blockTempVar for: #SHTextStylerST80 to: {self dbLabel. italic};                  set: #blockPatternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic};                  set: #instVar for: #SHTextStylerST80 to: {self dbYellow. normal };                  set: #workspaceVar for: #SHTextStylerST80 to: {self dbLabel. italic};                  set: #undefinedIdentifier for: #SHTextStylerST80 to: {self dbInvalid};                  set: #incompleteIdentifier for: #SHTextStylerST80 to: {self dbGray. underlined};                  set: #tempVar for: #SHTextStylerST80 to: {self dbLabel. italic};                  set: #patternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic};                  set: #poolConstant for: #SHTextStylerST80 to: {self dbConstant };                  set: #classVar for: #SHTextStylerST80 to: {self dbReference};                  set: #globalVar for: #SHTextStylerST80 to: {self dbClass. normal}.          "And the text differ"          aUserInterfaceTheme                      set: #insertTextAttributes for: #TextDiffBuilder to: { TextColor color: self dbRed };                  set: #removeTextAttributes for: #TextDiffBuilder to: { TextEmphasis struckOut. TextColor color: self dbBlue };                  set: #normalTextAttributes for: #TextDiffBuilder to: { TextEmphasis normal }.! Item was changed:   ----- Method: MonokaiTheme class>>addDarkFonts: (in category 'instance creation') -----   addDarkFonts: theme            "Set-up fonts."          theme +                set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; +                set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; +                set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; +                set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! -                set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); -                set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); -                set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); -                set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed:   ----- Method: SolarizedTheme class>>addDarkFonts: (in category 'instance creation') -----   addDarkFonts: theme            "Set-up fonts."          theme +                set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; +                set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; +                set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; +                set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! -                set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); -                set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); -                set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); -                set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed:   ----- Method: SolarizedTheme class>>addLightFonts: (in category 'instance creation') -----   addLightFonts: theme            "Set-up fonts."          theme +                set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; +                set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; +                set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; +                set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode].! -                set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); -                set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); -                set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); -                set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode).! Item was changed:   ----- Method: SqueakTheme class>>addFonts: (in category 'instance creation') -----   addFonts: theme            theme +                set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; +                set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; +                set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; +                set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; +                set: #standardFixedFont to: [TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0]; +                set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]. -                set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); -                set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); -                set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); -                set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); -                set: #standardFixedFont to: (TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0); -                set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode).   ! Item was changed:   ----- Method: TrimTheme class>>addFonts: (in category 'instance creation') -----   addFonts: theme            "Set-up fonts."          theme +                set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; +                set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; +                set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; +                set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; +                set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! -                set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); -                set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); -                set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); -                set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); -                set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: + (PackageInfo named: 'System') postscript: 'UserInterfaceTheme cleanUpAndReset.'! - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 59638 bytes Desc: not available URL: From marcel.taeumel at hpi.de Mon Sep 28 11:00:06 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 13:00:06 +0200 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <,20200914154934.GA85344@shell.msen.com> <,Mailbird-a28e0e53-b99d-4c98-90b9-af56f774f4c8@hpi.de> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph. > In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice Not only better but the only correct way without risking debuggers. ;-)  You should not just #fork UI code or any code for that matter that makes shared use of an object. In your example, the problems come through the use of "self" as well as the access to hand morph. Don't do it. Never. Please. Avoid #fork for such things. Use #deferredUIMessages. Best, Marcel Am 28.09.2020 12:55:46 schrieb Thiede, Christoph : Hi all, Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? Marcel, I absolutely see that you should not run two UI processes side by side in the sense that both of them handle events at the same time. (As a consequence of some attempts in my image, I accidentally ran into this situation a few times (2 worlds active at the same time), and in practice, about every second event is just ignored.) I was only talking about deferred event-handling actions, such as: mouseDown: anEvent     [|result|     result := self doLongComputation.     self currentHand attachMorph: result asMorph] fork. In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice, but in other examples, this is not so easy, for example when some application logic raises a FileDoesNotExistException which opens some UI stuff again ... We could, of course, try to use more deferred UI messages in such cases and maybe use Promises or some other mechanism to resume the background process after that, but I am not sure we can completely get rid of any sends to activeHand, activeWorld, and activeEvent in every non-UI process. What do you think? :-) Best, Christoph Von: Taeumel, Marcel Gesendet: Montag, 28. September 2020 11:01:05 An: Thiede, Christoph; squeak-dev Betreff: Re: AW: [squeak-dev] Changeset: Eliminating global state from Morphic   Hi Christoph. >> It's true that in Morphic there is one distinct Process associated >> with the user interface for each Morphic world. > I'm not so sure about this. You can always do forks within UI code,  > so sometimes there are also multiple Processes within one Morphic  > world. Just think about Shout's background process or debugging > processes, and please keep in mind that Jakob justifiably proposed to > renew the process handling for non-modal dialogs, which maybe could > also result in multiple processes being active at the same time. There is (and should) only be a single UI process running in Morphic at any time. All other processes that can be scheduled must synchronize with that single UI process when doing UI stuff. Shout, for example, does that via #addDeferredUIMessage:, which is effectively a SharedQueue (using a Mutex or similar). Having two UI processes running (or be scheduled) at the same time will produce strange side effects or even lock up the image. I don't know. At least, things will slow down. You could change that by re-desiging the ownership partitioning of UI objects. Even then, there will be some process responsible for a set of UI objects. All other processes must synchronize into that one when talking to those (foreign) UI objects. I forgot. When does process switching happen through higher-priority processes having a semaphore ready (i.e. Delay >> #wait)? At byte-code level? Oder message-send level? Well, you might not see the effects of omitting such process synchronization immediately. However, occasionally, debuggers with "nil does not understand" may pop up. Causing you headaches. :-) Best, Marcel Am 24.09.2020 14:08:56 schrieb Thiede, Christoph : Hi all, sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: * Use DynamicVariables for storing the active variable states * Refactor and reduce the visibility of active variable accessors on Object * Clean up implementations of #activeHand and #primaryHand @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. [http://www.hpi.de/] > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. PS: Thanks for the hint to the new wiki page! Best, Christoph Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Montag, 14. September 2020 17:49:34 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic   On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong?  Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares [https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares]. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 32042 bytes Desc: not available URL: From marcel.taeumel at hpi.de Mon Sep 28 11:14:26 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 13:14:26 +0200 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <,20200914154934.GA85344@shell.msen.com> <,Mailbird-a28e0e53-b99d-4c98-90b9-af56f774f4c8@hpi.de> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> Message-ID: Hi, all! > Don't do it. Never. Please. Avoid #fork for such things. Use #deferredUIMessages. Let me clarify this a little bit more. :-) Given that the OpenSmalltalk VM offers cooperative process scheduling with priority-based interrupting and round-robin for each priority level, a simple #fork from the UI process will do no harm. That forked process will get the same priority as the UI process and thus safely run (and likely finish) the next time the UI process yields after each cycle. However, Squeak is in the lucky position to enable experimentation with in-image process scheduling algorithms ... if you fancy. :-) Writing code that relys on the current scheduling behavior may break for those experiments. Given that we already have those deferred-ui-messages, there is no reason not to use them. Best, Marcel Am 28.09.2020 13:00:06 schrieb Marcel Taeumel : Hi Christoph. > In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice Not only better but the only correct way without risking debuggers. ;-)  You should not just #fork UI code or any code for that matter that makes shared use of an object. In your example, the problems come through the use of "self" as well as the access to hand morph. Don't do it. Never. Please. Avoid #fork for such things. Use #deferredUIMessages. Best, Marcel Am 28.09.2020 12:55:46 schrieb Thiede, Christoph : Hi all, Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? Marcel, I absolutely see that you should not run two UI processes side by side in the sense that both of them handle events at the same time. (As a consequence of some attempts in my image, I accidentally ran into this situation a few times (2 worlds active at the same time), and in practice, about every second event is just ignored.) I was only talking about deferred event-handling actions, such as: mouseDown: anEvent     [|result|     result := self doLongComputation.     self currentHand attachMorph: result asMorph] fork. In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice, but in other examples, this is not so easy, for example when some application logic raises a FileDoesNotExistException which opens some UI stuff again ... We could, of course, try to use more deferred UI messages in such cases and maybe use Promises or some other mechanism to resume the background process after that, but I am not sure we can completely get rid of any sends to activeHand, activeWorld, and activeEvent in every non-UI process. What do you think? :-) Best, Christoph Von: Taeumel, Marcel Gesendet: Montag, 28. September 2020 11:01:05 An: Thiede, Christoph; squeak-dev Betreff: Re: AW: [squeak-dev] Changeset: Eliminating global state from Morphic   Hi Christoph. >> It's true that in Morphic there is one distinct Process associated >> with the user interface for each Morphic world. > I'm not so sure about this. You can always do forks within UI code,  > so sometimes there are also multiple Processes within one Morphic  > world. Just think about Shout's background process or debugging > processes, and please keep in mind that Jakob justifiably proposed to > renew the process handling for non-modal dialogs, which maybe could > also result in multiple processes being active at the same time. There is (and should) only be a single UI process running in Morphic at any time. All other processes that can be scheduled must synchronize with that single UI process when doing UI stuff. Shout, for example, does that via #addDeferredUIMessage:, which is effectively a SharedQueue (using a Mutex or similar). Having two UI processes running (or be scheduled) at the same time will produce strange side effects or even lock up the image. I don't know. At least, things will slow down. You could change that by re-desiging the ownership partitioning of UI objects. Even then, there will be some process responsible for a set of UI objects. All other processes must synchronize into that one when talking to those (foreign) UI objects. I forgot. When does process switching happen through higher-priority processes having a semaphore ready (i.e. Delay >> #wait)? At byte-code level? Oder message-send level? Well, you might not see the effects of omitting such process synchronization immediately. However, occasionally, debuggers with "nil does not understand" may pop up. Causing you headaches. :-) Best, Marcel Am 24.09.2020 14:08:56 schrieb Thiede, Christoph : Hi all, sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: * Use DynamicVariables for storing the active variable states * Refactor and reduce the visibility of active variable accessors on Object * Clean up implementations of #activeHand and #primaryHand @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. [http://www.hpi.de/] > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. PS: Thanks for the hint to the new wiki page! Best, Christoph Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Montag, 14. September 2020 17:49:34 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic   On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong?  Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares [https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares]. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 32042 bytes Desc: not available URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 11:40:53 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 11:40:53 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <,20200914154934.GA85344@shell.msen.com> <,Mailbird-a28e0e53-b99d-4c98-90b9-af56f774f4c8@hpi.de> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> , Message-ID: <00a890cd67354800933b8d786ae549c9@student.hpi.uni-potsdam.de> Hi Marcel, probably the example I have given was oversimplified. Please give me another chance to give a better example. :-) Imagine a CalculatorMorph, which has the purpose to run an expensive computation (#doExpensiveComputation). When the morph is clicked, it should start the computation, and when it is clicked again, it should abort the computation. During the computation, unfortunately, an error could be raised which's handling logic might communicate to UI elements (such as a FileDoesNotExistException (#inform:) or a SyntaxError (#openAsTool)). So I would implement #mouseDown: in the following way: mouseDown: anEvent status caseOf: { [#ready] -> [ computation := [ result := self doExpensiveComputation. self world addDeferredUIMessage: [ self showResult: result]] forkAt: Processor userBackgroundPriority. self status: #computing]. [#computing] -> [ computation terminate. self status: #ready] } Isn't this a justified reason to use forks in UI code? :-) Best, Christoph ________________________________ Von: Taeumel, Marcel Gesendet: Montag, 28. September 2020 13:14:26 An: Thiede, Christoph; squeak-dev Betreff: Re: AW: AW: [squeak-dev] Changeset: Eliminating global state from Morphic Hi, all! > Don't do it. Never. Please. Avoid #fork for such things. Use #deferredUIMessages. Let me clarify this a little bit more. :-) Given that the OpenSmalltalk VM offers cooperative process scheduling with priority-based interrupting and round-robin for each priority level, a simple #fork from the UI process will do no harm. That forked process will get the same priority as the UI process and thus safely run (and likely finish) the next time the UI process yields after each cycle. However, Squeak is in the lucky position to enable experimentation with in-image process scheduling algorithms ... if you fancy. :-) Writing code that relys on the current scheduling behavior may break for those experiments. Given that we already have those deferred-ui-messages, there is no reason not to use them. Best, Marcel Am 28.09.2020 13:00:06 schrieb Marcel Taeumel : Hi Christoph. > In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice Not only better but the only correct way without risking debuggers. ;-) You should not just #fork UI code or any code for that matter that makes shared use of an object. In your example, the problems come through the use of "self" as well as the access to hand morph. Don't do it. Never. Please. Avoid #fork for such things. Use #deferredUIMessages. Best, Marcel Am 28.09.2020 12:55:46 schrieb Thiede, Christoph : Hi all, Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? [cid:3848a1c6-d67f-4999-a714-ffafff2b4a22] Marcel, I absolutely see that you should not run two UI processes side by side in the sense that both of them handle events at the same time. (As a consequence of some attempts in my image, I accidentally ran into this situation a few times (2 worlds active at the same time), and in practice, about every second event is just ignored.) I was only talking about deferred event-handling actions, such as: mouseDown: anEvent [|result| result := self doLongComputation. self currentHand attachMorph: result asMorph] fork. In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice, but in other examples, this is not so easy, for example when some application logic raises a FileDoesNotExistException which opens some UI stuff again ... We could, of course, try to use more deferred UI messages in such cases and maybe use Promises or some other mechanism to resume the background process after that, but I am not sure we can completely get rid of any sends to activeHand, activeWorld, and activeEvent in every non-UI process. What do you think? :-) Best, Christoph ________________________________ Von: Taeumel, Marcel Gesendet: Montag, 28. September 2020 11:01:05 An: Thiede, Christoph; squeak-dev Betreff: Re: AW: [squeak-dev] Changeset: Eliminating global state from Morphic Hi Christoph. >> It's true that in Morphic there is one distinct Process associated >> with the user interface for each Morphic world. > I'm not so sure about this. You can always do forks within UI code, > so sometimes there are also multiple Processes within one Morphic > world. Just think about Shout's background process or debugging > processes, and please keep in mind that Jakob justifiably proposed to > renew the process handling for non-modal dialogs, which maybe could > also result in multiple processes being active at the same time. There is (and should) only be a single UI process running in Morphic at any time. All other processes that can be scheduled must synchronize with that single UI process when doing UI stuff. Shout, for example, does that via #addDeferredUIMessage:, which is effectively a SharedQueue (using a Mutex or similar). Having two UI processes running (or be scheduled) at the same time will produce strange side effects or even lock up the image. I don't know. At least, things will slow down. You could change that by re-desiging the ownership partitioning of UI objects. Even then, there will be some process responsible for a set of UI objects. All other processes must synchronize into that one when talking to those (foreign) UI objects. I forgot. When does process switching happen through higher-priority processes having a semaphore ready (i.e. Delay >> #wait)? At byte-code level? Oder message-send level? Well, you might not see the effects of omitting such process synchronization immediately. However, occasionally, debuggers with "nil does not understand" may pop up. Causing you headaches. :-) Best, Marcel Am 24.09.2020 14:08:56 schrieb Thiede, Christoph : Hi all, sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: * Use DynamicVariables for storing the active variable states * Refactor and reduce the visibility of active variable accessors on Object * Clean up implementations of #activeHand and #primaryHand @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. PS: Thanks for the hint to the new wiki page! Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Montag, 14. September 2020 17:49:34 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong? Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 32042 bytes Desc: pastedImage.png URL: From commits at source.squeak.org Mon Sep 28 11:44:16 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 28 Sep 2020 11:44:16 0000 Subject: [squeak-dev] The Inbox: System-ct.1175.mcz Message-ID: A new version of System was added to project The Inbox: http://source.squeak.org/inbox/System-ct.1175.mcz ==================== Summary ==================== Name: System-ct.1175 Author: ct Time: 28 September 2020, 1:44:09.091219 pm UUID: 9b5c7784-f99d-6a4e-8980-42b8046fa7b9 Ancestors: System-topa.1174 Fixes hi-dpi images after resetting UI theme in System-topa.1174. See http://forum.world.st/The-Trunk-System-topa-1174-mcz-tp5122421p5122455.html. Thanks to Marcel for the hint! =============== Diff against System-topa.1174 =============== Item was changed: + (PackageInfo named: 'System') postscript: '| delta | + "Reset hi-dpi mode after resetting UI them in System-topa.1174. See http://forum.world.st/The-Trunk-System-topa-1174-mcz-tp5122421p5122455.html." + delta := Preferences standardSystemFont pointSize - 9. + delta isZero ifFalse: [ + [Preferences restoreDefaultFonts] valueSuppressingMessages: #(''Do you want to apply*''). + Preferences changeFontSize: delta].'! - (PackageInfo named: 'System') postscript: 'UserInterfaceTheme cleanUpAndReset.'! From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 11:45:57 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 11:45:57 +0000 Subject: [squeak-dev] Trunk broken (was: The Trunk: System-topa.1174.mcz) In-Reply-To: References: <25cbd26a894f461fa6627a2a18a6e667@student.hpi.uni-potsdam.de>, Message-ID: Hi Marcel, alright, that fixed it. Please see System-ct.1175, which should fix all affected images. Independently of that hotfix, we need to fix the copy issue. At the moment, I'm not yet familiar with this stuff ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Taeumel, Marcel Gesendet: Montag, 28. September 2020 12:56:24 An: squeak-dev; packages at lists.squeakfoundation.org Betreff: Re: [squeak-dev] Trunk broken (was: The Trunk: System-topa.1174.mcz) Hi Christoph, in my image, too, the "Demo +"-Theme was not updated. Going back to "Squeak" and then hit "Extras > Themes & Colors > Set High-DPI Mode" fixed those issues in my image. The bug is most likely in the full-copy of the current theme for the Demo/High-DPI mode. And UserInterfaceTheme class >> #cleanUpAndReset not considering that copy. Best, Marcel Am 25.09.2020 17:19:22 schrieb Thiede, Christoph : Hi Tobias, hi all, this update just broke my image functionality sensitively! Right after loading System-topa.1174, the following happens to my fresh (!) trunk image the only adjustment to which I have applied being doing "Preferences changeFontSize: 12" once: - The background image disappears, I'm seeing a single dull gray tone as a background instead - Shout in all tools is broken (no syntax highlight any longer) - Balloons are semi-transparent - Dialog buttons have lost their color - Also, the fonts look a bit larger now, but I cannot say whether I like the change or not. I'm using a 202003021730 VM on Windows 10 64 bit. None of these effects occurs if my font size is set to zero when loading the update. I do not yet completely understand the changes you made, but IMO it would be a very good idea to remove this commit again from the trunk, if possible, and re-add it with an appropriate preamble and postload. This screenshot shows the full extent of the crisis: [cid:1ff543dd-1790-488a-b262-d7a614543dc5] Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von commits at source.squeak.org Gesendet: Donnerstag, 24. September 2020 23:13 Uhr An: squeak-dev at lists.squeakfoundation.org; packages at lists.squeakfoundation.org Betreff: [squeak-dev] The Trunk: System-topa.1174.mcz Tobias Pape uploaded a new version of System to project The Trunk: http://source.squeak.org/trunk/System-topa.1174.mcz ==================== Summary ==================== Name: System-topa.1174 Author: topa Time: 24 September 2020, 11:14:16.007835 pm UUID: 8f692d9f-4973-4aac-b181-9a2643089f8a Ancestors: System-eem.1173 Make it so that Theme fonts are looked up on usage, not on theme creation. =============== Diff against System-eem.1173 =============== Item was changed: ----- Method: CommunityTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: aUserInterfaceTheme "Set-up fonts." aUserInterfaceTheme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis italic emphasisCode); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode)! Item was changed: ----- Method: CommunityTheme class>>addDarkSyntaxHighlighting: (in category 'instance creation') ----- addDarkSyntaxHighlighting: aUserInterfaceTheme "self createDark apply." + | normal bold italic underlined | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. - | normal bold italic underlined darkMap | normal := TextEmphasis normal. bold:=TextEmphasis bold. italic:=TextEmphasis italic. underlined := TextEmphasis underlined. darkMap := StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9. aUserInterfaceTheme set: #color for: #TextAction to: self dbBlue; set: #default for: #SHTextStylerST80 to: {self dbForeground}; set: #invalid for: #SHTextStylerST80 to: {self dbInvalid}; set: #excessCode for: #SHTextStylerST80 to: {self dbInvalid twiceDarker}; "Descriptive text for humans, italicized." set: #comment for: #SHTextStylerST80 to: {self dbComment. italic}; set: #unfinishedComment for: #SHTextStylerST80 to: {self dbComment darker. italic}; set: #'$' for: #SHTextStylerST80 to: {self dbConstant}; set: #character for: #SHTextStylerST80 to: {self dbConstant}; set: #integer for: #SHTextStylerST80 to: {self dbConstant}; set: #number for: #SHTextStylerST80 to: {self dbConstant}; set: #- for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #= for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #symbol for: #SHTextStylerST80 to: {self dbBedrock}; set: #stringSymbol for: #SHTextStylerST80 to: {self dbBedrock}; set: #literalArray for: #SHTextStylerST80 to: {self dbForeground}; set: #string for: #SHTextStylerST80 to: {self dbConstant}; set: #unfinishedString for: #SHTextStylerST80 to: {self dbConstant darker}; set: #assignment for: #SHTextStylerST80 to: {nil. bold}; set: #ansiAssignment for: #SHTextStylerST80 to: {nil. bold}; set: #literal for: #SHTextStylerST80 to: {nil. bold}; set: #keyword for: #SHTextStylerST80 to: {self dbMessage}; set: #binary for: #SHTextStylerST80 to: {self dbForeground. bold}; set: #unary for: #SHTextStylerST80 to: {self dbMessage}; set: #incompleteKeyword for: #SHTextStylerST80 to: {self dbMessage darker. {underlined. bold}}; set: #incompleteBinary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; set: #incompleteUnary for: #SHTextStylerST80 to: {self dbMessage darker. underlined}; set: #undefinedKeyword for: #SHTextStylerST80 to: {self dbInvalid}; set: #undefinedBinary for: #SHTextStylerST80 to: {self dbInvalid}; set: #undefinedUnary for: #SHTextStylerST80 to: {self dbInvalid}; "Delineate the selector (good for new users), and make the method look like a mini-document with a title." set: #patternKeyword for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; set: #patternBinary for: #SHTextStylerST80 to: {nil. bold}; set: #patternUnary for: #SHTextStylerST80 to: {self dbMessage lighter. {bold. underlined}}; set: #self for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #super for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #true for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #false for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #nil for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #thisContext for: #SHTextStylerST80 to: {self dbBedrock. bold}; set: #return for: #SHTextStylerST80 to: {self dbForeground. bold}; + set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; + set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. ""}; - set: #patternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; - set: #methodArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter. TextEmphasis normal. "darkMap"}; set: #blockPatternArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #blockArg for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #argument for: #SHTextStylerST80 to: {self dbSelection twiceLighter}; set: #blockArgColon for: #SHTextStylerST80 to: {self dbBedrock}; set: #leftParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #rightParenthesis for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #leftParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #rightParenthesis1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #leftParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #rightParenthesis2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #leftParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #rightParenthesis3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #leftParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #rightParenthesis4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #leftParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #rightParenthesis5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #leftParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #rightParenthesis6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #leftParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; set: #rightParenthesis7 for: #SHTextStylerST80 to: {Color yellow}; set: #blockStart for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #blockEnd for: #SHTextStylerST80 to: {self dbBedrock muchLighter}; set: #blockStart1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #blockEnd1 for: #SHTextStylerST80 to: {self dbBedrock twiceLighter}; set: #blockStart2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockEnd2 for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockStart3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockEnd3 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockStart4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockEnd4 for: #SHTextStylerST80 to: {self dbPurple muchLighter}; set: #blockStart5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockEnd5 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockStart6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockEnd6 for: #SHTextStylerST80 to: {self dbOrange muchLighter}; set: #blockStart7 for: #SHTextStylerST80 to: {Color yellow}; set: #blockEnd7 for: #SHTextStylerST80 to: {Color yellow}; set: #arrayStart for: #SHTextStylerST80 to: {self dbBedrock}; set: #arrayEnd for: #SHTextStylerST80 to: {self dbBedrock}; set: #arrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; set: #arrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayStart for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayEnd for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayStart1 for: #SHTextStylerST80 to: {self dbForeground}; set: #byteArrayEnd1 for: #SHTextStylerST80 to: {self dbForeground}; set: #leftBrace for: #SHTextStylerST80 to: {self dbForeground}; set: #rightBrace for: #SHTextStylerST80 to: {self dbForeground}; set: #cascadeSeparator for: #SHTextStylerST80 to: {self dbForeground}; set: #statementSeparator for: #SHTextStylerST80 to: {self dbForeground}; set: #externalCallType for: #SHTextStylerST80 to: {self dbForeground}; set: #externalCallTypePointerIndicator for: #SHTextStylerST80 to: {self dbForeground}; set: #primitiveOrExternalCallStart for: #SHTextStylerST80 to: {self dbForeground}; set: #primitiveOrExternalCallEnd for: #SHTextStylerST80 to: {self dbForeground}; set: #methodTempBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockTempBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #blockArgsBar for: #SHTextStylerST80 to: {self dbBedrock}; set: #primitive for: #SHTextStylerST80 to: {self dbGreen lighter. bold}; set: #pragmaKeyword for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #pragmaUnary for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #pragmaBinary for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #externalFunctionCallingConvention for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #module for: #SHTextStylerST80 to: {self dbGreen. bold}; set: #blockTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #blockPatternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #instVar for: #SHTextStylerST80 to: {self dbYellow. normal }; set: #workspaceVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #undefinedIdentifier for: #SHTextStylerST80 to: {self dbInvalid}; set: #incompleteIdentifier for: #SHTextStylerST80 to: {self dbGray. underlined}; set: #tempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #patternTempVar for: #SHTextStylerST80 to: {self dbLabel. italic}; set: #poolConstant for: #SHTextStylerST80 to: {self dbConstant }; set: #classVar for: #SHTextStylerST80 to: {self dbReference}; set: #globalVar for: #SHTextStylerST80 to: {self dbClass. normal}. "And the text differ" aUserInterfaceTheme set: #insertTextAttributes for: #TextDiffBuilder to: { TextColor color: self dbRed }; set: #removeTextAttributes for: #TextDiffBuilder to: { TextEmphasis struckOut. TextColor color: self dbBlue }; set: #normalTextAttributes for: #TextDiffBuilder to: { TextEmphasis normal }.! Item was changed: ----- Method: MonokaiTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: ----- Method: SolarizedTheme class>>addDarkFonts: (in category 'instance creation') ----- addDarkFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: ----- Method: SolarizedTheme class>>addLightFonts: (in category 'instance creation') ----- addLightFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode).! Item was changed: ----- Method: SqueakTheme class>>addFonts: (in category 'instance creation') ----- addFonts: theme theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9]; + set: #standardFixedFont to: [TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0]; + set: #windowTitleFont to: [StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode]. - set: #balloonHelpFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9); - set: #standardFixedFont to: (TTCFont familyName: 'BitstreamVeraSansMono' pointSize: 12 emphasis: 0); - set: #windowTitleFont to: (StrikeFont familyName: 'Bitmap DejaVu Sans' pointSize: 9 emphasized: TextEmphasis bold emphasisCode). ! Item was changed: ----- Method: TrimTheme class>>addFonts: (in category 'instance creation') ----- addFonts: theme "Set-up fonts." theme + set: #balloonHelpFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardButtonFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7]; + set: #standardCodeFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardFlapFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode]; + set: #haloLabelFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardListFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardMenuFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #standardSystemFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9]; + set: #windowTitleFont to: [StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9].! - set: #balloonHelpFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardButtonFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7); - set: #standardCodeFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardFlapFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 7 emphasized: TextEmphasis bold emphasisCode); - set: #haloLabelFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardListFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardMenuFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #standardSystemFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9); - set: #windowTitleFont to: (StrikeFont familyName: 'Darkmap DejaVu Sans' pointSize: 9).! Item was changed: + (PackageInfo named: 'System') postscript: 'UserInterfaceTheme cleanUpAndReset.'! - (PackageInfo named: 'System') postscript: 'SystemNavigation initializeAuthors. "new: Tim Johnson (tcj)"'! -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 59638 bytes Desc: pastedImage.png URL: From marcel.taeumel at hpi.de Mon Sep 28 11:45:49 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Mon, 28 Sep 2020 13:45:49 +0200 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <00a890cd67354800933b8d786ae549c9@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <,20200914154934.GA85344@shell.msen.com> <,Mailbird-a28e0e53-b99d-4c98-90b9-af56f774f4c8@hpi.de> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> <,Mailbird-be74a9b6-698a-4115-9558-8f210320f909@hpi.de> <00a890cd67354800933b8d786ae549c9@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph. Of course. That's what Shout does. Yet, you have to ensure that you only work on your own objects in #doExpensiveComputation. You can make a #copy like Shout does. Best, Marcel Am 28.09.2020 13:40:54 schrieb Thiede, Christoph : Hi Marcel, probably the example I have given was oversimplified. Please give me another chance to give a better example. :-) Imagine a CalculatorMorph, which has the purpose to run an expensive computation (#doExpensiveComputation). When the morph is clicked, it should start the computation, and when it is clicked again, it should abort the computation. During the computation, unfortunately, an error could be raised which's handling logic might communicate to UI elements (such as a FileDoesNotExistException (#inform:) or a SyntaxError (#openAsTool)). So I would implement #mouseDown: in the following way: mouseDown: anEvent     status caseOf: {         [#ready] -> [             computation := [                 result := self doExpensiveComputation.                 self world addDeferredUIMessage: [                     self showResult: result]] forkAt: Processor userBackgroundPriority.             self status: #computing].         [#computing] -> [             computation terminate.             self status: #ready] } Isn't this a justified reason to use forks in UI code? :-) Best, Christoph Von: Taeumel, Marcel Gesendet: Montag, 28. September 2020 13:14:26 An: Thiede, Christoph; squeak-dev Betreff: Re: AW: AW: [squeak-dev] Changeset: Eliminating global state from Morphic   Hi, all! > Don't do it. Never. Please. Avoid #fork for such things. Use #deferredUIMessages. Let me clarify this a little bit more. :-) Given that the OpenSmalltalk VM offers cooperative process scheduling with priority-based interrupting and round-robin for each priority level, a simple #fork from the UI process will do no harm. That forked process will get the same priority as the UI process and thus safely run (and likely finish) the next time the UI process yields after each cycle. However, Squeak is in the lucky position to enable experimentation with in-image process scheduling algorithms ... if you fancy. :-) Writing code that relys on the current scheduling behavior may break for those experiments. Given that we already have those deferred-ui-messages, there is no reason not to use them. Best, Marcel Am 28.09.2020 13:00:06 schrieb Marcel Taeumel : Hi Christoph. > In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice Not only better but the only correct way without risking debuggers. ;-)  You should not just #fork UI code or any code for that matter that makes shared use of an object. In your example, the problems come through the use of "self" as well as the access to hand morph. Don't do it. Never. Please. Avoid #fork for such things. Use #deferredUIMessages. Best, Marcel Am 28.09.2020 12:55:46 schrieb Thiede, Christoph : Hi all, Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? Marcel, I absolutely see that you should not run two UI processes side by side in the sense that both of them handle events at the same time. (As a consequence of some attempts in my image, I accidentally ran into this situation a few times (2 worlds active at the same time), and in practice, about every second event is just ignored.) I was only talking about deferred event-handling actions, such as: mouseDown: anEvent     [|result|     result := self doLongComputation.     self currentHand attachMorph: result asMorph] fork. In this trivial example, a deferred UI message for attaching the morph would obviously be the better choice, but in other examples, this is not so easy, for example when some application logic raises a FileDoesNotExistException which opens some UI stuff again ... We could, of course, try to use more deferred UI messages in such cases and maybe use Promises or some other mechanism to resume the background process after that, but I am not sure we can completely get rid of any sends to activeHand, activeWorld, and activeEvent in every non-UI process. What do you think? :-) Best, Christoph Von: Taeumel, Marcel Gesendet: Montag, 28. September 2020 11:01:05 An: Thiede, Christoph; squeak-dev Betreff: Re: AW: [squeak-dev] Changeset: Eliminating global state from Morphic   Hi Christoph. >> It's true that in Morphic there is one distinct Process associated >> with the user interface for each Morphic world. > I'm not so sure about this. You can always do forks within UI code,  > so sometimes there are also multiple Processes within one Morphic  > world. Just think about Shout's background process or debugging > processes, and please keep in mind that Jakob justifiably proposed to > renew the process handling for non-modal dialogs, which maybe could > also result in multiple processes being active at the same time. There is (and should) only be a single UI process running in Morphic at any time. All other processes that can be scheduled must synchronize with that single UI process when doing UI stuff. Shout, for example, does that via #addDeferredUIMessage:, which is effectively a SharedQueue (using a Mutex or similar). Having two UI processes running (or be scheduled) at the same time will produce strange side effects or even lock up the image. I don't know. At least, things will slow down. You could change that by re-desiging the ownership partitioning of UI objects. Even then, there will be some process responsible for a set of UI objects. All other processes must synchronize into that one when talking to those (foreign) UI objects. I forgot. When does process switching happen through higher-priority processes having a semaphore ready (i.e. Delay >> #wait)? At byte-code level? Oder message-send level? Well, you might not see the effects of omitting such process synchronization immediately. However, occasionally, debuggers with "nil does not understand" may pop up. Causing you headaches. :-) Best, Marcel Am 24.09.2020 14:08:56 schrieb Thiede, Christoph : Hi all, sorry for the long delay on this issue. I'm attaching a new version of the changeset including the following changes: * Use DynamicVariables for storing the active variable states * Refactor and reduce the visibility of active variable accessors on Object * Clean up implementations of #activeHand and #primaryHand @Dave I think associating these objects to Process is an implementation detail of DynamicVariables which I would not like to denigrate in general. [http://www.hpi.de/] > It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. I'm not so sure about this. You can always do forks within UI code, so sometimes there are also multiple Processes within one Morphic world. Just think about Shout's background process or debugging processes, and please keep in mind that Jakob justifiably proposed to renew the process handling for non-modal dialogs, which maybe could also result in multiple processes being active at the same time. A world can have multiple hands, for example, RemoteHandMorphs (Nebraska), event-simulating hands (EventRecorder), or just multiple plain HandMorphs as demonstrated by Tony recently in his PostmarketOS implementation. There is no reason for these hands to run on the same process. Analogously, I don't see why we should restrict a hand not to be thread-safe, so the hand-event relation is 1:n again. PS: Thanks for the hint to the new wiki page! Best, Christoph Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Montag, 14. September 2020 17:49:34 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic   On Mon, Sep 14, 2020 at 11:17:53AM +0000, Thiede, Christoph wrote: > Hi Dave, > > > I agree that could try to place the active variables at better places than the most generic Object class. Still, I am not sure whether you are arguing against the thread-local storage of their values - which I do find pretty important for enabling concurrency. > > > Of course, we could move my proposed implementation #activeWorld down to Project, and on Object, forward the request to "Project current activeWorld". Still, I do not think that an instance variable would be the right way to store the world, because it is not thread-local. If you would like to do this, we should implement some kind of PluggableThreadLocalVariable as proposed below, and store an instance of this class in Project. > > > What do you think? > Hi Christoph, The thing that I like (a lot) about your changes is that they make it much easier to see and understand the accesses to the global variables. The references now all go through a small number of methods in Object, so that you can see what they do now. Clearly we would want to leave this logic in Object, since we don't want to add more clutter to its protocol. But you have collected the logic in one place now, which makes it easier to think about where it ultimately should go, and that's great. But where do these things actually belong?  Thinking in terms of the responsiblities of objects in the system [1], I don't think that associating them with a Process seems to be a good fit. It's true that in Morphic there is one distinct Process associated with the user interface for each Morphic world. So you can make things work by associating these variables with a Process, but it seems like an unnatural fit to me. A Morphic world has a process, but a Process is not responsible for knowing the state of the Morphic world. Dave [1] https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares [https://en.wikipedia.org/wiki/Responsibility-driven_design#:~:text=Responsibility%2Ddriven%20design%20is%20a,information%20that%20the%20object%20shares]. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: pastedImage.png Type: image/png Size: 32042 bytes Desc: not available URL: From eliot.miranda at gmail.com Mon Sep 28 15:07:00 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 28 Sep 2020 08:07:00 -0700 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> Message-ID: Hi Christoph, On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > Hi Eliot, > > > I'm very sorry for this bug, which was so unnecessary because my image has > still a gigantic working copy ...! Tools-ct.988 should fix the issue, I > tested it in a fresh trunk image. But it would be probably better if you > could test it yourself, too. ;-) > No need to apologise. It's an easy mistake, and you fixed it. As long as we're all patient with each other and take responsibility (Andreas said "if you break it, you fix it") we're going to get along fine and be collectively productive. Cheers!! > Best, > > Christoph > > > PS: #stepToStatement gets relevant with the BytecodeDebugger changeset I > proposed somewhen earlier in this year. I still would appreciate any review > of it! :-) > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Eliot Miranda > *Gesendet:* Donnerstag, 24. September 2020 21:42:37 > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] tedious programming-in-the-debugger error > needs fixing > > Hi Christoph, > > I didn't do my due dilligence in reviewing the changes. I've just hit > a problem. When I accept as method in the debugger I get an MNU > of stepToStatement. MorphicDebugger needs to implement stepToStatement, or > contents:notifying: needs not to send it. Can you please fix ASAP? > Apologies!! > > On Thu, Sep 3, 2020 at 5:50 AM Christoph Thiede < > christoph.thiede at student.hpi.uni-potsdam.de> wrote: > >> Hi Eliot, >> >> when you reported this issue, I was not able to reproduce it. About two >> months later, your new Sista bytecode set has arrived in the Trunk. >> Today, I >> encountered a very similar issue, could reproduce it, and trace it back >> to a >> comparison issue of Context >> #method which was affected by the >> introduction of CompiledBlock instances. Is it possible that you already >> used Sista in your image when reporting this bug? >> >> In case you were able to reproduce the bug: Can you confirm whether >> Tools-ct.987 (inbox) fixes the issue for you? :-) >> >> Best, >> Christoph >> >> >> >> -- >> Sent from: http://forum.world.st/Squeak-Dev-f45488.html >> >> > > -- > _,,,^..^,,,_ > best, Eliot > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Mon Sep 28 15:44:57 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Mon, 28 Sep 2020 17:44:57 +0200 (CEST) Subject: [squeak-dev] The Inbox: Collections-ul.913.mcz In-Reply-To: References: Message-ID: Hi Marcel, On Mon, 28 Sep 2020, Marcel Taeumel wrote: > Hi Levente. > It looks like #arraySize is "private" or at least "tests only", right? I understand that you need a better accessor in tests regarding that growth behavior. Well, you could just add #arraySize via meta-programming to the test > case where you need it. Hmm...  Not really. It's used by HashedCollection >> #removeAll, MethodDictionary >> #compact, and MethodDictionary class >> #compactAllInstances. The latter two are in Kernel, which I forgot to push to the Inbox. But we can entirely avoid introducing the new selector by using the existing private/tests-only #array selector, and send #size to the object it returns. Levente > > So, +1 for fixing #capacity. Not sure about #arraySize though ... :-) > > Best, > Marcel > > Am 28.09.2020 01:22:39 schrieb commits at source.squeak.org : > > Levente Uzonyi uploaded a new version of Collections to project The Inbox: > http://source.squeak.org/inbox/Collections-ul.913.mcz > > ==================== Summary ==================== > > Name: Collections-ul.913 > Author: ul > Time: 28 September 2020, 1:19:09.686632 am > UUID: 03eb89d7-ea1f-4116-99eb-0181542610f7 > Ancestors: Collections-eem.912 > > HashedCollection changes: > - make #capacity return the actual capacity of the collection instead of the size of the internal array. This change is obviously not backwards compatible. > - introduce #arraySize to return the size of the internal array > - improve the performance of #isEmpty when tally is 0 > > OrderedDictionary changes; > - make it a subclass of PluggableDictionary. This lets one create e.g. an ordered identity dictionary without creating a subclass with duplicated behavior > - simplify #initialize and #growTo: now that #capacity is accurate > > =============== Diff against Collections-eem.912 =============== > > Item was added: > + ----- Method: HashedCollection>>arraySize (in category 'accessing') ----- > + arraySize > + "Answer the size of the internal array of the receiver." > + > + ^array size! > > Item was changed: > ----- Method: HashedCollection>>capacity (in category 'accessing') ----- > capacity > + "Answer the current capacity of the receiver - aka the number of elements the receiver can hold without growing." > - "Answer the current capacity of the receiver." > > + ^ array size * 3 // 4! > - ^ array size! > > Item was changed: > ----- Method: HashedCollection>>isEmpty (in category 'testing') ----- > isEmpty > "For non-weak collections, we can use the tally to speed up the empty check. For weak collections, we must use the traditional way because the tally is unreliable. Also see #size vs. #slowSize." > > + tally = 0 ifTrue: [ ^true ]. > + ^array class isWeak and: [ super isEmpty ]! > - ^ array class isWeak > - ifFalse: [ tally = 0 ] > - ifTrue: [ super isEmpty ]! > > Item was changed: > ----- Method: HashedCollection>>removeAll (in category 'removing') ----- > removeAll > "remove all elements from this collection. > Preserve the capacity" > > + self initialize: self arraySize! > - self initialize: self capacity! > > Item was changed: > + PluggableDictionary subclass: #OrderedDictionary > - Dictionary subclass: #OrderedDictionary > instanceVariableNames: 'order' > classVariableNames: '' > poolDictionaries: '' > category: 'Collections-Sequenceable'! > > !OrderedDictionary commentStamp: 'mt 1/16/2015 10:42' prior: 0! > I am an ordered dictionary. I have an additional index (called 'order') to keep track of the insertion order of my associations. > > The read access is not affected by the additional index. > > The index is updated in O(1) [time] when inserting new keys. For present keys, that insertion involves actions in O(n) to move the respective element to the end of the order. > > The growth operation compacts the index and takes O(n) additional time. > > NOTE: This is still no instance of SequenceableCollection. Having this, some protocols are missing and may require working on #associations, which is an Array and thus sequenceable.! > > Item was changed: > ----- Method: OrderedDictionary>>growTo: (in category 'private') ----- > growTo: anInteger > > - | oldOrder | > super growTo: anInteger. > + order := order grownBy: self capacity - order size! > - oldOrder := order. > - "Grow only to 75%. See #atNewIndex:put: in HashedCollection." > - order := self class arrayType new: anInteger + 1 * 3 // 4. > - order > - replaceFrom: 1 > - to: tally > - with: oldOrder > - startingAt: 1! > > Item was changed: > ----- Method: OrderedDictionary>>initialize: (in category 'private') ----- > initialize: n > > super initialize: n. > + order := self class arrayType new: self capacity! > - order := self class arrayType new: n + 1 * 3 // 4! > > > > From asqueaker at gmail.com Mon Sep 28 21:23:23 2020 From: asqueaker at gmail.com (Chris Muller) Date: Mon, 28 Sep 2020 16:23:23 -0500 Subject: [squeak-dev] Question about RemoteTask In-Reply-To: <20200927000604.GA27742@shell.msen.com> References: <90a453f5ab86ec88d0a1e4a63182b0a7@whidbey.com> <20200927000604.GA27742@shell.msen.com> Message-ID: Hi Dave, I saw your RemoteTask the other day and had a quick question about it. Do you see any pitfalls doing operations that require or provide sensitive security key information as input, or output. As an analogy, I've been using my own convenience method on OSProcess to give me a way get Linux command output into the image, but it does so by simply redirecting the command's output to a file, reading that file into an in-image String, then deleting the file. But that's not something I would want to use if it involved passing a password, for example. My hope is RemoteTask operates all in memory and does not utilize the hard drive to accomplish that. Thanks, Chris On Sat, Sep 26, 2020 at 7:06 PM David T. Lewis wrote: > > On Sat, Sep 26, 2020 at 03:20:10PM -0700, tim Rowledge wrote: > > > > And multi-core? Well yes I suppose. Except that one can very easily > spawn multiple running images using Dave Lewis' OSProcess package, > including in ways that do some work and return the results.The spawning > takes very little time; for example on said Pi4 it takes 39mS to do > > UnixProcess forkHeadlessSqueakAndDoThenQuit: [UnixProcess helloWorld] > > > Or the somewhat more interesting example: > > RemoteTask do: [3 + 4] ==> 7 > > which completes in on the order of 10ms on my PC, and hopefully not too > much worse on Pi4. The [3 + 4] block is evaluated in a spawned image with > results returned to the parent image. > > > Dave > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Mon Sep 28 21:51:42 2020 From: asqueaker at gmail.com (Chris Muller) Date: Mon, 28 Sep 2020 16:51:42 -0500 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: <922b664c26bc4c57b4a9eae7b1b3d78b@student.hpi.uni-potsdam.de> References: <922b664c26bc4c57b4a9eae7b1b3d78b@student.hpi.uni-potsdam.de> Message-ID: On Mon, Sep 28, 2020 at 5:42 AM Thiede, Christoph < Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > There are so many small and big projects on GitHub that are not yet listed > on squeak.org: > > https://github.com/search?l=Smalltalk&p=1&q=squeak&type=Repositories > > Just for example Squot, smalltalkCI, Autocompletion, ... > The projects to "showcase" on the Projects page should be ones that are meaningful _beyond_ Squeak. Things people outside of Squeak might care about, and not IDE tools. > And also I would like to list several smaller tools and frameworks > somewhere that, despite their small size, might be very interesting for > your personal Squeak workflow: Autocompletion, > MessageSendRecorder, WindowAcrobatics, and so many others (just telling > some names from my individual filter bubble). > -1. The Projects page is for outside readers' interest -- e.g. "reasons to get into Squeak". Small tools like these would bog it down, IMO. Regards, Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Mon Sep 28 21:53:05 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 28 Sep 2020 21:53:05 0000 Subject: [squeak-dev] The Inbox: Collections-ul.914.mcz Message-ID: Levente Uzonyi uploaded a new version of Collections to project The Inbox: http://source.squeak.org/inbox/Collections-ul.914.mcz ==================== Summary ==================== Name: Collections-ul.914 Author: ul Time: 28 September 2020, 11:50:07.373304 pm UUID: eb80ed0e-d5ed-4ff5-9206-18cc75e54990 Ancestors: Collections-eem.912 HashedCollection changes: - make #capacity return the actual capacity of the collection instead of the size of the internal array. This change is obviously not backwards compatible. - improve the performance of #isEmpty when tally is 0 OrderedDictionary changes: - make it a subclass of PluggableDictionary. This lets one create e.g. an ordered identity dictionary without creating a subclass with duplicated behavior - simplify #initialize and #growTo: now that #capacity is accurate =============== Diff against Collections-eem.912 =============== Item was changed: ----- Method: HashedCollection>>capacity (in category 'accessing') ----- capacity + "Answer the current capacity of the receiver - aka the number of elements the receiver can hold without growing." - "Answer the current capacity of the receiver." + ^ array size * 3 // 4! - ^ array size! Item was changed: ----- Method: HashedCollection>>isEmpty (in category 'testing') ----- isEmpty "For non-weak collections, we can use the tally to speed up the empty check. For weak collections, we must use the traditional way because the tally is unreliable. Also see #size vs. #slowSize." + tally = 0 ifTrue: [ ^true ]. + ^array class isWeak and: [ super isEmpty ]! - ^ array class isWeak - ifFalse: [ tally = 0 ] - ifTrue: [ super isEmpty ]! Item was changed: ----- Method: HashedCollection>>removeAll (in category 'removing') ----- removeAll "remove all elements from this collection. Preserve the capacity" + self initialize: array size! - self initialize: self capacity! Item was changed: + PluggableDictionary subclass: #OrderedDictionary - Dictionary subclass: #OrderedDictionary instanceVariableNames: 'order' classVariableNames: '' poolDictionaries: '' category: 'Collections-Sequenceable'! !OrderedDictionary commentStamp: 'mt 1/16/2015 10:42' prior: 0! I am an ordered dictionary. I have an additional index (called 'order') to keep track of the insertion order of my associations. The read access is not affected by the additional index. The index is updated in O(1) [time] when inserting new keys. For present keys, that insertion involves actions in O(n) to move the respective element to the end of the order. The growth operation compacts the index and takes O(n) additional time. NOTE: This is still no instance of SequenceableCollection. Having this, some protocols are missing and may require working on #associations, which is an Array and thus sequenceable.! Item was changed: ----- Method: OrderedDictionary>>growTo: (in category 'private') ----- growTo: anInteger - | oldOrder | super growTo: anInteger. + order := order grownBy: self capacity - order size! - oldOrder := order. - "Grow only to 75%. See #atNewIndex:put: in HashedCollection." - order := self class arrayType new: anInteger + 1 * 3 // 4. - order - replaceFrom: 1 - to: tally - with: oldOrder - startingAt: 1! Item was changed: ----- Method: OrderedDictionary>>initialize: (in category 'private') ----- initialize: n super initialize: n. + order := self class arrayType new: self capacity! - order := self class arrayType new: n + 1 * 3 // 4! From commits at source.squeak.org Mon Sep 28 21:53:17 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Mon, 28 Sep 2020 21:53:17 0000 Subject: [squeak-dev] The Inbox: CollectionsTests-ul.344.mcz Message-ID: Levente Uzonyi uploaded a new version of CollectionsTests to project The Inbox: http://source.squeak.org/inbox/CollectionsTests-ul.344.mcz ==================== Summary ==================== Name: CollectionsTests-ul.344 Author: ul Time: 28 September 2020, 11:52:49.129468 pm UUID: 82e7d021-2a4e-4e58-8758-c4e8838805cf Ancestors: CollectionsTests-eem.342 Hashed collections: - updated tests to send #array and #size instead of #capacity - added a few tests for #capacity =============== Diff against CollectionsTests-eem.342 =============== Item was added: + ----- Method: DictionaryTest>>testArraySizeOfNew (in category 'tests - basic') ----- + testArraySizeOfNew + "Test the special cases implemented in HashedCollection class >> #new and #new: using Dictionary as an example because HashedCollection is abstract." + + | goodPrimes | + goodPrimes := HashedCollection goodPrimes. + self assert: (goodPrimes includes: Dictionary new array size). + 0 to: 100 do: [ :size | + | dictionary | + dictionary := Dictionary new: size. + self assert: (goodPrimes includes: dictionary array size). + self assert: dictionary capacity >= size ]! Item was removed: - ----- Method: DictionaryTest>>testCapcityOfNew (in category 'tests - basic') ----- - testCapcityOfNew - "Test the special cases implemented in HashedCollection class >> #new and #new: using Dictionary as an example because HashedCollection is abstract." - - | goodPrimes | - goodPrimes := HashedCollection goodPrimes. - self assert: (goodPrimes includes: Dictionary new capacity). - 0 to: 100 do: [ :size | - | dictionary | - dictionary := Dictionary new: size. - self assert: (goodPrimes includes: dictionary capacity) ]! Item was added: + ----- Method: HashedCollectionTest>>testArraySize (in category 'tests - integrity') ----- + testArraySize + + | inconsistentCollections | + inconsistentCollections := HashedCollection allSubInstances reject: [ :each | + each class == MethodDictionary "MethodDictionary is the only HashedCollection which doesn't have prime array size" + ifTrue: [ each array size isPowerOfTwo ] + ifFalse: [ each array size isPrime ] ]. + self assert: inconsistentCollections isEmpty! Item was changed: ----- Method: HashedCollectionTest>>testCapacity (in category 'tests - integrity') ----- testCapacity + self assert: (HashedCollection allSubInstances allSatisfy: [ :each | + each array size * 3 // 4 = each capacity ])! - | inconsistentCollections | - inconsistentCollections := HashedCollection allSubInstances reject: [ :each | - each class == MethodDictionary "MethodDictionary is the only HashedCollection which doesn't have prime array size" - ifTrue: [ each capacity isPowerOfTwo ] - ifFalse: [ each capacity isPrime ] ]. - self assert: inconsistentCollections isEmpty! Item was changed: ----- Method: OrderedDictionaryTest>>testGrow (in category 'tests') ----- testGrow self + assert: 11 equals: sut array size; "next prime number to 7; see #setUp" + assert: sut capacity = (sut instVarNamed: #order) size; + assert: sut array size >(sut instVarNamed: #order) size. "save memory" - assert: 11 equals: sut capacity; "next prime number to 7; see #setUp" - assert: sut capacity > (sut instVarNamed: #order) size. "save memory" + 1 to: sut array size do: [:ea | - 1 to: sut capacity do: [:ea | sut at: ea put: nil]. self + assert: sut array size > 11; + assert: sut capacity = (sut instVarNamed: #order) size; + assert: sut array size > (sut instVarNamed: #order) size. "save memory"! - assert: sut capacity > 11; - assert: sut capacity > (sut instVarNamed: #order) size. "save memory"! Item was added: + ----- Method: SetTest>>testCapacity (in category 'tests') ----- + testCapacity + + | set capacity | + set := Set new. + self assert: set size = 0. + 10 timesRepeat: [ + capacity := set capacity. + self assert: set size < capacity. + set size + 1 to: capacity do: [ :i | + set add: i. + self assert: set capacity = capacity ]. + self assert: set size equals: capacity. + set add: set capacity + 1. + self assert: set capacity > capacity ]! Item was changed: ----- Method: WeakIdentityKeyDictionaryTest>>testFinalizeValuesWhenLastChainContinuesAtFront (in category 'tests') ----- testFinalizeValuesWhenLastChainContinuesAtFront + | objectWithHashModulo dictionary arraySize a b c | - | objectWithHashModulo dictionary capacity a b c | objectWithHashModulo := [ :requestedHash :modulo | | object | [ object := Object new. object hash \\ modulo = requestedHash ] whileFalse. object ]. dictionary := self classToBeTested new. + arraySize := dictionary array size. + a := objectWithHashModulo value: arraySize - 2 value: arraySize. - capacity := dictionary capacity. - a := objectWithHashModulo value: capacity - 2 value: capacity. dictionary at: a put: 1. + b := objectWithHashModulo value: arraySize - 1 value: arraySize. - b := objectWithHashModulo value: capacity - 1 value: capacity. dictionary at: b put: 2. + c := objectWithHashModulo value: arraySize - 2 value: arraySize. - c := objectWithHashModulo value: capacity - 2 value: capacity. dictionary at: c put: 3. + self assert: dictionary array size = arraySize. + self assert: (dictionary array at: arraySize - 1) key == a. + self assert: (dictionary array at: arraySize) key == b. - self assert: dictionary capacity = capacity. - self assert: (dictionary array at: capacity - 1) key == a. - self assert: (dictionary array at: capacity) key == b. self assert: (dictionary array at: 1) key == c. a := nil. Smalltalk garbageCollect. dictionary finalizeValues. self assert: (dictionary includesKey: b). self assert: (dictionary includesKey: c). + self assert: dictionary slowSize = 2! - self assert: dictionary slowSize = 2. - ! Item was changed: ----- Method: WeakSetTest>>testIncludes (in category 'tests') ----- testIncludes | weakSet transientFakeNilObject | weakSet := WeakSet new. #(true nil 1) do: [ :each | self deny: (weakSet includes: each) ]. weakSet add: true. self assert: (weakSet includes: true). weakSet remove: true. self deny: (weakSet includes: true). + transientFakeNilObject := ((1 to: 1000) detect: [ :each | each asString hash - nil hash \\ weakSet array size = 0 ]) asString. "this string will occupy the same slot as nil would" - transientFakeNilObject := ((1 to: 1000) detect: [ :each | each asString hash - nil hash \\ weakSet capacity = 0 ]) asString. "this string will occupy the same slot as nil would" weakSet add: transientFakeNilObject. transientFakeNilObject := transientFakeNilObject copy. Smalltalk garbageCollect. "get rid of transientFakeNilObject" self deny: (weakSet includes: transientFakeNilObject). self deny: (weakSet includes: nil) ! From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 22:24:45 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 22:24:45 +0000 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: <922b664c26bc4c57b4a9eae7b1b3d78b@student.hpi.uni-potsdam.de>, Message-ID: <88ea19668f3a4a3593f0eb26cf55477f@student.hpi.uni-potsdam.de> Alright, I see that, too. Hm ... can we maybe add another tab that is reserved for tools? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Chris Muller Gesendet: Montag, 28. September 2020 23:51:42 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] We need an Applications tab on squeak.org On Mon, Sep 28, 2020 at 5:42 AM Thiede, Christoph > wrote: There are so many small and big projects on GitHub that are not yet listed on squeak.org: https://github.com/search?l=Smalltalk&p=1&q=squeak&type=Repositories Just for example Squot, smalltalkCI, Autocompletion, ... The projects to "showcase" on the Projects page should be ones that are meaningful _beyond_ Squeak. Things people outside of Squeak might care about, and not IDE tools. And also I would like to list several smaller tools and frameworks somewhere that, despite their small size, might be very interesting for your personal Squeak workflow: Autocompletion, MessageSendRecorder, WindowAcrobatics, and so many others (just telling some names from my individual filter bubble). -1. The Projects page is for outside readers' interest -- e.g. "reasons to get into Squeak". Small tools like these would bog it down, IMO. Regards, Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From tim at rowledge.org Mon Sep 28 22:49:15 2020 From: tim at rowledge.org (tim Rowledge) Date: Mon, 28 Sep 2020 15:49:15 -0700 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: <88ea19668f3a4a3593f0eb26cf55477f@student.hpi.uni-potsdam.de> References: <922b664c26bc4c57b4a9eae7b1b3d78b@student.hpi.uni-potsdam.de> <88ea19668f3a4a3593f0eb26cf55477f@student.hpi.uni-potsdam.de> Message-ID: > On 2020-09-28, at 3:24 PM, Thiede, Christoph wrote: > > Alright, I see that, too. Hm ... can we maybe add another tab that is reserved for tools? :-) > Von: Squeak-dev im Auftrag von Chris Muller > > -1. The Projects page is for outside readers' interest -- e.g. "reasons to get into Squeak". Small tools like these would bog it down, IMO. Perhaps a split along the lines of "this is cool stuff people are doing with Squeak, take a look" and "here are cool tools and packages to peek at". For example, NuScratch is the former. My weather station project might be as well - but the mqtt & dials and & plotmorph extensions are definitely the latter. tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Strange OpCodes: MTP: MounT Programmer From asqueaker at gmail.com Mon Sep 28 23:00:27 2020 From: asqueaker at gmail.com (Chris Muller) Date: Mon, 28 Sep 2020 18:00:27 -0500 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> Message-ID: On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda wrote: > Hi Christoph, > > On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> Hi Eliot, >> >> >> I'm very sorry for this bug, which was so unnecessary because my image >> has still a gigantic working copy ...! Tools-ct.988 should fix the >> issue, I tested it in a fresh trunk image. But it would be probably better >> if you could test it yourself, too. ;-) >> > > No need to apologise. It's an easy mistake, and you fixed it. As long as > we're all patient with each other and take responsibility (Andreas said "if > you break it, you fix it") we're going to get along fine and be > collectively productive. > The following is not addressed to Christoph or his commit, but to Eliots comment, above: Patience should begin within our development methodology. The words above are correct and sweet, and I agree with them, but I feel the need to caution against the implication that "everything's great as long as you fix it *afterward*." Maybe for git-based projects, a *commit-first, fix-later* strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk. I believe Andreas understood this, and he indicated that "breaking the trunk is generally frowned upon." When it comes to code less than 24 hours old, no matter how simple it seems, chances are about 80% that a subsequent "oops, sorry" commit will need to follow. With "older," (e.g., even only just 48 hours!) *tested* code, that chance drops significantly. Patience. Restraint. Please. :) Let our methodology put time to work *for* us, by living with our changes for a bit (as, it sounds like, Christoph did!) and witness them work in context. Maybe not this time, but *generally, *you'll have a gist enough to know whether it should be loaded and tested separately in a clean trunk first. Cheers, Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 23:18:07 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 23:18:07 +0000 Subject: [squeak-dev] ResumableTestFailure Message-ID: Hi all! :-) I was just working a bit with Squeak by Example (the book) and found out that resumable assertions as described in the book (see chapter SUnit, Advanced, Continuing after a failure) do not any longer work in a trunk image. If you write a test case with some resumable assertions, turn on logging, and run the test, only the first failure will be logged, not the rest, too. (Methods to reproduce: isLogging ^ true "You will need to open a Transcript" testResumable #(1 2 3 4 5) do: [:i | self assert: i even description: ['{1} is not even' format: {i}] resumable: true] ) A second issue I experienced frequently when I felt the desire to work with resumable assertions was that they are basically only available for two selectors: #assert:description:resumable: and #deny:description:identical:. I often would like to insert a resumable into any test without being that verbose, and nearly redoubling the number of assertion selectors by adding a resumable argument to each of them did not feel right, either. Please find the attached changeset, which fixes both issues. * In TestResult >> #runCase:, resumable assertions are identified and - finally - resumed, after marking the test as failed. Unfortunately, other test runners (yes, I'm thinking of smalltalkCI) will need to reimplement this feature unless they reuse the TestResult implementation. * In TestCase, I refactored some methods that raise assertion failures and added the most basic #signalFailure:resumable: as the single source of signalization. Instead of setting the resumable bit manually for each assertion, you now can also make the whole test execution resumable by: * overriding #isResumable, * setting a resumable flag using #resumable:, * setting a resumable flag temporarily using #beResumable:during:, or * adding a or pragma to the test method. * In addition, I tested the added and fixed behavior in ResumableTestFailureTestCase. Looking forward to your review! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: resumable.1.cs URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 23:38:13 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 23:38:13 +0000 Subject: [squeak-dev] Development methodology (was: tedious programming-in-the-debugger error needs fixing) In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> , Message-ID: <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> > Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk. And that's why - sorry for rolling out my old chestnut again - I still do believe that a git-based workflow could make us as a community more efficient. I don't question that manual testing is great, and I don't question that quality can increase if you give your patches time for maturing, but when I compare the resulting workflow to the "mainstream" workflow that I can use anywhere on GitHub, I repeatedly have the dissatisfying feeling that the inbox/trunk workflow is so slow that it ruins all the efficiency from the Smalltalkish development workflow (which, however, unquestionably outperforms the "mainstream" workflow in a dead, non-live UI without first-class objects for code and tools!). This might apply most significantly to small changes that would form a PR of two or three commits in a git project because our inbox workflow does not scale so well for changes of such extent. I do not know how many hours I already have spent on fixing the ancestry of my versions, comparing them to their ancestors, or re-uploading them, but it has definitively been too many hours ... Did someone ever investigate this question seriously by doing a study or so? I would really find the results interesting. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Chris Muller Gesendet: Dienstag, 29. September 2020 01:00 Uhr An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] tedious programming-in-the-debugger error needs fixing On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda > wrote: Hi Christoph, On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph > wrote: Hi Eliot, I'm very sorry for this bug, which was so unnecessary because my image has still a gigantic working copy ...! Tools-ct.988 should fix the issue, I tested it in a fresh trunk image. But it would be probably better if you could test it yourself, too. ;-) No need to apologise. It's an easy mistake, and you fixed it. As long as we're all patient with each other and take responsibility (Andreas said "if you break it, you fix it") we're going to get along fine and be collectively productive. The following is not addressed to Christoph or his commit, but to Eliots comment, above: Patience should begin within our development methodology. The words above are correct and sweet, and I agree with them, but I feel the need to caution against the implication that "everything's great as long as you fix it afterward." Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk. I believe Andreas understood this, and he indicated that "breaking the trunk is generally frowned upon." When it comes to code less than 24 hours old, no matter how simple it seems, chances are about 80% that a subsequent "oops, sorry" commit will need to follow. With "older," (e.g., even only just 48 hours!) tested code, that chance drops significantly. Patience. Restraint. Please. :) Let our methodology put time to work for us, by living with our changes for a bit (as, it sounds like, Christoph did!) and witness them work in context. Maybe not this time, but generally, you'll have a gist enough to know whether it should be loaded and tested separately in a clean trunk first. Cheers, Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Mon Sep 28 23:39:21 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Mon, 28 Sep 2020 23:39:21 +0000 Subject: [squeak-dev] ResumableTestFailure In-Reply-To: References: Message-ID: <8163d812a7bc4e808bc80e7fc19394af@student.hpi.uni-potsdam.de> PS: I ran all tests in a fresh trunk image before and after loading the changeset, there were no regressions. ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Dienstag, 29. September 2020 01:18:07 An: Squeak Dev Betreff: [squeak-dev] ResumableTestFailure Hi all! :-) I was just working a bit with Squeak by Example (the book) and found out that resumable assertions as described in the book (see chapter SUnit, Advanced, Continuing after a failure) do not any longer work in a trunk image. If you write a test case with some resumable assertions, turn on logging, and run the test, only the first failure will be logged, not the rest, too. (Methods to reproduce: isLogging ^ true "You will need to open a Transcript" testResumable #(1 2 3 4 5) do: [:i | self assert: i even description: ['{1} is not even' format: {i}] resumable: true] ) A second issue I experienced frequently when I felt the desire to work with resumable assertions was that they are basically only available for two selectors: #assert:description:resumable: and #deny:description:identical:. I often would like to insert a resumable into any test without being that verbose, and nearly redoubling the number of assertion selectors by adding a resumable argument to each of them did not feel right, either. Please find the attached changeset, which fixes both issues. * In TestResult >> #runCase:, resumable assertions are identified and - finally - resumed, after marking the test as failed. Unfortunately, other test runners (yes, I'm thinking of smalltalkCI) will need to reimplement this feature unless they reuse the TestResult implementation. * In TestCase, I refactored some methods that raise assertion failures and added the most basic #signalFailure:resumable: as the single source of signalization. Instead of setting the resumable bit manually for each assertion, you now can also make the whole test execution resumable by: * overriding #isResumable, * setting a resumable flag using #resumable:, * setting a resumable flag temporarily using #beResumable:during:, or * adding a or pragma to the test method. * In addition, I tested the added and fixed behavior in ResumableTestFailureTestCase. Looking forward to your review! :-) Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Tue Sep 29 00:33:06 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Mon, 28 Sep 2020 20:33:06 -0400 Subject: [squeak-dev] Question about RemoteTask In-Reply-To: References: <90a453f5ab86ec88d0a1e4a63182b0a7@whidbey.com> <20200927000604.GA27742@shell.msen.com> Message-ID: <20200929003306.GB7470@shell.msen.com> Hi Chris, RemoteTask is entirely in memory, no disk access, it forks an exact copy of the image and VM. The block is evaluated in the child image, and the result is serialized back to your parent image. The parent and child image are identical at the point of the fork, so neither is any more or less secure than the other. Regarding your convenience method, please try using OSProcess class>>outputOf: instead. This will probably do a much better job for you because it handles all of the stdio piping without using files, and it also will present any external command errors in the form of an exception in Squeak. Dave On Mon, Sep 28, 2020 at 04:23:23PM -0500, Chris Muller wrote: > Hi Dave, > > I saw your RemoteTask the other day and had a quick question about it. > > Do you see any pitfalls doing operations that require or provide sensitive > security key information as input, or output. > > As an analogy, I've been using my own convenience method on OSProcess to > give me a way get Linux command output into the image, but it does so by > simply redirecting the command's output to a file, reading that file into > an in-image String, then deleting the file. But that's not something I > would want to use if it involved passing a password, for example. > > My hope is RemoteTask operates all in memory and does not utilize the hard > drive to accomplish that. > > Thanks, > Chris > > On Sat, Sep 26, 2020 at 7:06 PM David T. Lewis wrote: > > > > > On Sat, Sep 26, 2020 at 03:20:10PM -0700, tim Rowledge wrote: > > > > > > And multi-core? Well yes I suppose. Except that one can very easily > > spawn multiple running images using Dave Lewis' OSProcess package, > > including in ways that do some work and return the results.The spawning > > takes very little time; for example on said Pi4 it takes 39mS to do > > > UnixProcess forkHeadlessSqueakAndDoThenQuit: [UnixProcess helloWorld] > > > > > > Or the somewhat more interesting example: > > > > RemoteTask do: [3 + 4] ==> 7 > > > > which completes in on the order of 10ms on my PC, and hopefully not too > > much worse on Pi4. The [3 + 4] block is evaluated in a spawned image with > > results returned to the parent image. > > > > > > Dave > > > > > > > From Christoph.Thiede at student.hpi.uni-potsdam.de Tue Sep 29 00:37:31 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Tue, 29 Sep 2020 00:37:31 +0000 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de>, <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de>, Message-ID: <6dce22431e5d4e78a36f8f42d65554f7@student.hpi.uni-potsdam.de> Hi all, hi Levente, I just wanted to send you a fix for parsing "\u2139\ufffd", but then I realized that you already had fixed this in the repository. So what, at least I understood my own error! However, is there an option to subscribe to arbitrary packages (such as the JSON package) in order to get notified via email about new changes to it? :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Levente Uzonyi Gesendet: Dienstag, 8. September 2020 22:22:10 An: The general-purpose Squeak developers list Cc: Niephaus, Fabio Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project Hi Christoph, On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > Hi Levente, > > > is there no active development happening on http://www.squeaksource.com/JSON any longer? Last commit is from 2016. I wanted to have our changes added to the main repository[1]. > Is your fork specialized for optimized code? It has got performance improvements and new features as well. There are quite a few changes, so I suggest you go through the MC history if you want to know what has been changed. >I was rather trying to reuse existing functionality such as #nextHexDigit. Just for > interest, how much faster did you get the code by optimizing my changes? ;-) I haven't measured it. The parser has been rewritten, so the code is quite different. But I expect my version to be at least a magnitude faster. Levente [1] http://forum.world.st/Another-version-of-JSON-in-the-Inbox-td5066614.html > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Levente Uzonyi > Gesendet: Sonntag, 6. September 2020 02:13:56 > An: The general-purpose Squeak developers list > Cc: Niephaus, Fabio > Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project > Hi Christoph, > > Your code looks correct to me, though it's a bit too slow for my taste. > I've fixed it in my fork of JSON[1] as well. > > Levente > [1] http://squeaksource.com/PostgresV3/JSON-ul.55.mcz > > On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > > > > Hi all, > > > > > > I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into > the > > repository? :-) > > > > > > Best, > > > > Christoph > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 29 03:55:36 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Mon, 28 Sep 2020 20:55:36 -0700 Subject: [squeak-dev] We need an Applications tab on squeak.org In-Reply-To: References: <922b664c26bc4c57b4a9eae7b1b3d78b@student.hpi.uni-potsdam.de> Message-ID: On Mon, Sep 28, 2020 at 2:52 PM Chris Muller wrote: > On Mon, Sep 28, 2020 at 5:42 AM Thiede, Christoph < > Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: > >> There are so many small and big projects on GitHub that are not yet >> listed on squeak.org: >> >> https://github.com/search?l=Smalltalk&p=1&q=squeak&type=Repositories >> >> Just for example Squot, smalltalkCI, Autocompletion, ... >> > The projects to "showcase" on the Projects page should be ones that are > meaningful _beyond_ Squeak. Things people outside of Squeak might care > about, and not IDE tools. > Amen. +1,000,000. This is the point. I had a long conversation with a good friend and SMalltalker the other day who made the point that the success of Python, et al, is crucially because they can get things done, not because they are an end in themselves. We as a community know the beauty of Smalltalk and can be happy and fulfilled in working on the system itself. But we will never grow the community until we can solve real-world problems, until people in the outside world come knocking with problems they need solving. It is the ability to solve real-world problems that has relevance to someone visiting from outside of the community. We have to stop being so hermetic if we want to grow and prosper. Articulating that we *can* fix real world problems (as well as all the coll things we can do inside) is the point of the Applications tab. And there's nothing threatening about this. Smalltalk is one of the *most powerful* solvers of real-world problems, as evinced by the application of VisualWorks in areas such as all mobile phone chip construction, scheduling 60% of world container traffic, etfc, etc, etc. So can we please emphasise our problem-solving abilities to those outside the community with an Applications tab? And also I would like to list several smaller tools and frameworks >> somewhere that, despite their small size, might be very interesting for >> your personal Squeak workflow: Autocompletion, >> MessageSendRecorder, WindowAcrobatics, and so many others (just telling >> some names from my individual filter bubble). >> > -1. The Projects page is for outside readers' interest -- e.g. "reasons > to get into Squeak". Small tools like these would bog it down, IMO. > > Regards, > Chris > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 29 06:38:01 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 29 Sep 2020 08:38:01 +0200 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> Message-ID: Hi all! +1 to Eliot +1 to Chris +1000 to our community! :-) Happy programming. Happy squeaking. Best, Marcel Am 29.09.2020 01:01:14 schrieb Chris Muller : On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda wrote: Hi Christoph, On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph wrote: Hi Eliot, I'm very sorry for this bug, which was so unnecessary because my image has still a gigantic working copy ...! Tools-ct.988 should fix the issue, I tested it in a fresh trunk image. But it would be probably better if you could test it yourself, too. ;-) No need to apologise.  It's an easy mistake, and you fixed it.  As long as we're all patient with each other and take responsibility (Andreas said "if you break it, you fix it") we're going to get along fine and be collectively productive. The following is not addressed to Christoph or his commit, but to Eliots comment, above:  Patience should begin within our development methodology.  The words above are correct and sweet, and I agree with them, but I feel the need to caution against the implication that "everything's great as long as you fix it afterward."  Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk.  I believe Andreas understood this, and he indicated that "breaking the trunk is generally frowned upon." When it comes to code less than 24 hours old, no matter how simple it seems, chances are about 80% that a subsequent "oops, sorry" commit will need to follow.  With "older," (e.g., even only just 48 hours!) tested code, that chance drops significantly.  Patience.  Restraint.  Please.  :)  Let our methodology put time to work for us, by living with our changes for a bit (as, it sounds like, Christoph did!) and witness them work in context.  Maybe not this time, but generally, you'll have a gist enough to know whether it should be loaded and tested separately in a clean trunk first. Cheers,   Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 29 08:06:09 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 29 Sep 2020 08:06:09 0000 Subject: [squeak-dev] The Trunk: Tools-mt.989.mcz Message-ID: Marcel Taeumel uploaded a new version of Tools to project The Trunk: http://source.squeak.org/trunk/Tools-mt.989.mcz ==================== Summary ==================== Name: Tools-mt.989 Author: mt Time: 29 September 2020, 10:06:06.429444 am UUID: 5730f8ac-96c7-6c41-be31-f2c51310ad53 Ancestors: Tools-ct.988 In debuggers, unify run-until/to logic, which also skips unnecessary UI updates to speed up "run to here" again. =============== Diff against Tools-ct.988 =============== Item was added: + ----- Method: Debugger>>doStepUntil: (in category 'context stack menu') ----- + doStepUntil: condition + "Step until the given condition evaluates to other than false, reporting an error it if does not evaluate to true. + + If shift is pressed when the expression is supplied, don't update the UI. If shift is pressed while stepping, stop stepping. Using a user interrupt to break out would be more natural but Squeak currently doesn't provide a UserInterrupt exception. It should do." + + | currentContext newContext value lastUpdate updateUI breakOnShift | + self okToChange ifFalse: [^ self]. + self checkContextSelection. + currentContext := newContext := self selectedContext. + lastUpdate := Time millisecondClockValue. + updateUI := breakOnShift := Sensor shiftPressed not. + + Cursor execute showWhile: [[ + newContext == currentContext + and: [currentContext willReturn not + and: [(value := condition value) == false]] ] whileTrue: [ + + self + handleLabelUpdatesIn: [newContext := interruptedProcess completeStep: currentContext] + whenExecuting: currentContext. + newContext == currentContext ifTrue: [ + newContext := interruptedProcess stepToSendOrReturn. + self resetContext: newContext changeContents: false]. + + Time millisecondClockValue - lastUpdate > 250 "ms" ifTrue: [ + updateUI ifTrue: [ + self changed: #contentsSelection. + Project current world displayWorldSafely]. + breakOnShift + ifTrue: [Sensor shiftPressed ifTrue: [ + self changed: #contentsSelection. + self updateInspectors. + ^self]] + ifFalse: [Sensor shiftPressed ifFalse: [breakOnShift := true]]. + lastUpdate := Time millisecondClockValue] ]]. + + self contextStackIndex > 1 + ifTrue: [self resetContext: newContext] + ifFalse: + [newContext == currentContext + ifTrue: [self changed: #contentsSelection; updateInspectors] + ifFalse: [self resetContext: newContext]]. + + ^ value + ! Item was changed: ----- Method: Debugger>>runToSelection: (in category 'code pane menu') ----- runToSelection: selectionInterval + - | currentContext | self pc first >= selectionInterval first ifTrue: [ ^self ]. + self doStepUntil: [ self pc first >= selectionInterval first ].! - currentContext := self selectedContext. - [ currentContext == self selectedContext and: [ self pc first < selectionInterval first ] ] whileTrue: [ self doStep ].! Item was changed: ----- Method: Debugger>>runUntil (in category 'code pane menu') ----- runUntil + "Step until an expression evaluates to other than false, reporting an error if it doesn't evaluate to true. Remember the expression in an inst var." + + | expression receiver context method value | - "Step until an expression evaluates to other than false, reporting an error if it doesn't evaluate to true. - Remember the expression in an inst var. If shift is pressed when the expression is supplied, don't update the UI. - If shift is pressed while stepping, stop stepping. Using a user interrupt to break out would be more natural - but Squeak currently doesn't provide a UserInterrupt expection. It should do." - | expression receiver context method value lastUpdate updateUI breakOnShift | expression := UIManager default request: 'run until expression is true (shift to disable ui update; shift to break).' initialAnswer: (untilExpression ifNil: 'boolean expression'). (expression isNil or: [expression isEmpty]) ifTrue: [^self]. - updateUI := breakOnShift := Sensor shiftPressed not. untilExpression := expression. context := self selectedContext. receiver := context receiver. method := receiver class evaluatorClass new compiledMethodFor: untilExpression in: context to: receiver notifying: nil ifFail: [^ #failedDoit]. + value := self doStepUntil: [method valueWithReceiver: receiver arguments: {context}]. + - lastUpdate := Time millisecondClockValue. - Cursor execute showWhile: - [[self selectedContext == context - and: [context willReturn not - and: [(value := method valueWithReceiver: receiver arguments: {context}) == false]]] whileTrue: - [interruptedProcess completeStep: self selectedContext. - self selectedContext == context ifTrue: - [self resetContext: interruptedProcess stepToSendOrReturn changeContents: false]. - Time millisecondClockValue - lastUpdate > 50 ifTrue: - [updateUI ifTrue: - [self changed: #contentsSelection. - Project current world displayWorldSafely]. - breakOnShift - ifTrue: [Sensor shiftPressed ifTrue: - [self changed: #contentsSelection. - self updateInspectors. - ^self]] - ifFalse: [Sensor shiftPressed ifFalse: [breakOnShift := true]]. - lastUpdate := Time millisecondClockValue]]]. - self changed: #contentsSelection. - self updateInspectors. (value ~~ false and: [value ~~ true]) ifTrue: [UIManager default inform: 'expression ', (untilExpression contractTo: 40), ' answered ', (value printString contractTo: 20), '!!!!']! From marcel.taeumel at hpi.de Tue Sep 29 08:07:32 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 29 Sep 2020 10:07:32 +0200 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: References: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> Message-ID: Hi Eliot, hi all! I fixed the issue with Tools-mt.989. The logic was already there in #runUntil. Best, Marcel Am 19.09.2020 23:00:33 schrieb Levente Uzonyi : Hi Christoph, On Sat, 19 Sep 2020, Thiede, Christoph wrote: > > Hi Eliot, > > > very nice finding once again! I #timeProfile'd the menu button action and as I expected, the most expensive operation is the shout styling by the new inspectors, including the decompilation of every method: What was it exactly that you profiled? The screenshot shows that 76.9% was spent in #initializeVariablesFromContext, of which 52.5% of the time was spent in CompiledMethod(CompiledCode) >> #getSource. The rest of the tree is not visible, but these methods have nothing to do with parsing or styling, they initialize the parser and normally should take <1 ms. Also, why is the decompiler producing the source code? > > > [IMAGE] > > > First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but probably that's rather a problem with my sources file, see the thread about this commit). You may want to try compiling a VM where FilePlugin's CreateFile does not set the FILE_FLAG_SEQUENTIAL_SCAN flag, and see if it helps with file reading performance. Levente > > Second, we do not redraw the world while running to the selection, so we do not need to update the inspectors at all. I think we could split up #doStep into some #basicDoStep (which would perform the actual stepping logic) + > some #updateContextDuring: (which would update the stack list and the inspectors), then we would need to trigger the updates only once from #runToSelection:. > As an alternative, we could make this method a bit more complex but responsive by applying the same updating logic from #runUntil. > > What do you think? :-) > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Eliot Miranda > Gesendet: Samstag, 19. September 2020 20:17:12 > An: The general-purpose Squeak developers list; Taeumel, Marcel > Betreff: [squeak-dev] Can we make computing the local variables in the debugger lazy?   > Hi Marcel, > >     can we try and reduce the frequency at which we compute the variables in the context inspector in the debugger?  It is a noticeable performance hit.  I really like the user interface, but the performance hit is making > debugging difficult. > > As an example use this: > > | samples sineTable sound | > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > sineTable := SoundPlayer sineTable: 73. > sineTable doWithIndex: "And let's not deafen anyone..." > [:sample :index| sineTable at: index put: sample // 4]. > samples := SoundBuffer new: 16000. > 1 to: samples size by: sineTable size do: > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. > 1 to: 146 do: > [:i| > samples at: i put: ((samples at: i) * i / 146) asInteger. > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. > sound := SampledSound > samples: samples > samplingRate: 16000. > sound := MixedSound new > add: sound pan: 0.25; > add: sound pan: 0.75; > yourself. > sound computeSamplesForSeconds: sound duration > > > Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an image containing Tools-mt.942).  Debug the above in the workspace.  Position the cursor at "sound computeSamplesForSeconds: sound duration" and do "run > to here".  It is essentially instantaneous. > > Now do the same in a contemporary trunk image.  It takes almost 6 seconds. > > I used to be able to click step as fast as I could and the system would keep up with me.  Now I find that if I click too fast I can accumulate excess clicks and when I stp clicking the system will continue stepping and go too > far. > > I don't want to lose the feedback the new variables list gives us.  But I do find the performance hit tedious.  I wonder could we cache the list and only update it > - when Morphic updates, and > - when the context changes? > > > _,,,^..^,,,_ > best, Eliot > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 29 08:20:36 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 29 Sep 2020 08:20:36 0000 Subject: [squeak-dev] The Trunk: Tools-mt.990.mcz Message-ID: Marcel Taeumel uploaded a new version of Tools to project The Trunk: http://source.squeak.org/trunk/Tools-mt.990.mcz ==================== Summary ==================== Name: Tools-mt.990 Author: mt Time: 29 September 2020, 10:20:33.276444 am UUID: e7da396b-a8ec-7a44-8f15-5ba7d0d30b37 Ancestors: Tools-mt.989 In debuggers, forward model-wake-up call to update inspectors. This fixes the bug where a 'nil' receiver had no fields in the receiver inspector. For example, try debug-it on any expression in the workspace to trigger that bug. Note that the underlying challenge is that the inspectors in debuggers are not stepping. Field updates are triggered manually via #updateInspectors. An inspector's initial object is 'nil'. So, "changing" that to 'nil' will not update the field list in inspectors bc. it is considered redundant and will be ignored there. While stepping is not required to produce the initial field list, the model-wake-up call will usually trigger that initial update in a stand-alone inspector. That's why I think that it is not necessary to change anything in the inspector code but only here in the debugger. =============== Diff against Tools-mt.989 =============== Item was added: + ----- Method: Debugger>>modelWakeUpIn: (in category 'user interface') ----- + modelWakeUpIn: aWindow + + super modelWakeUpIn: aWindow. + self updateInspectors.! From Das.Linux at gmx.de Tue Sep 29 08:28:01 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Tue, 29 Sep 2020 10:28:01 +0200 Subject: [squeak-dev] highdpi testing In-Reply-To: <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> Message-ID: <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> Hi > On 25.09.2020, at 21:43, Thiede, Christoph wrote: > > Hi Tobias, > > sorry I did not answer earlier! This approach looks better for me, however, the wheel is still not absolutely round: > > > (The shadows have a wrong position.) I know, I had a patch that worked in 2016, but things changed and I hand't had the time yet. > > For the first step, we could start by asking the user whether they would like to change the font size (#changeFontSize:) when a new scale factor is detected, couldn't we? No, that ought to be automatic; -t > > PS: @Tom: > > we try drawing things while we load things :) > For the case you did not already read it, I created a changeset that could solve this problem: http://forum.world.st/Changeset-Eliminating-global-state-from-Morphic-tp5121690p5122464.html It also allow me to file in the original DpiAware changeset. > > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Montag, 21. September 2020 15:50:30 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] highdpi testing > > Hi > > > On 19.09.2020, at 19:26, Thiede, Christoph wrote: > > > > Hi, > > > > ah, I had assumed something like this, thanks for fixing, Tom! :-) > > > > #actualScreenScaleFactor is a very helpful tool, but I do not yet see the whole idea of the changeset. > > In what sense is this meant to be a complement of RealEstateAgent >> #scaleFactor or rather an orthogonal concept? > > After loading the changeset and resetting my UI theme, the background image of the world disappeared and all new windows are huge, but they still have a tiny font - am I supposed to set the #scaleFactor manually (to 12, in my example)? > > Where exactly can I see any components in the image that are upscaled by the new changeset? > > > > These do something. > Load in order: > > DpiAware-Kernel > DpiAware-Morpic > DpiAware-Display > > First two are harmless, third will do something. > If nothing changes, do > UserInterfaceTheme cleanUpAndReset. > > Issues ensue. > - Layouting borks. > The assert after the potential relayout > self assert: (self hasProperty: #doLayoutAgain) not > fails already in the docking bar… > - Fonts with UI-Themes do not work. > These are "Deep-referenced" to a pixel size (for Strike fonts) on creation, (so also during UserInterfaceTheme cleanUpAndReset). > However, when the scale factor changes, the pixel-sized fonts don't match the point sizes anymore… > This used to work pre-UI-Themes, as everyone requesting a Font did so by directly asking StrikeFont or TextStyle at the time the > Font was used. Hummm.. > > Hope you enjoy. > -t From karlramberg at gmail.com Tue Sep 29 08:50:55 2020 From: karlramberg at gmail.com (karl ramberg) Date: Tue, 29 Sep 2020 10:50:55 +0200 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> Message-ID: I thought Trunk was for bleeding edge development. And that the bleeding occurs when stuff needs further fixing. We need a middle ground so that the inbox doesn't just become a dead end of bit rotted improvements and fixes. Best, Karl On Tue, Sep 29, 2020 at 8:38 AM Marcel Taeumel wrote: > Hi all! > > +1 to Eliot > +1 to Chris > > +1000 to our community! :-) > > Happy programming. Happy squeaking. > > Best, > Marcel > > Am 29.09.2020 01:01:14 schrieb Chris Muller : > On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda > wrote: > >> Hi Christoph, >> >> On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph < >> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >> >>> Hi Eliot, >>> >>> >>> I'm very sorry for this bug, which was so unnecessary because my image >>> has still a gigantic working copy ...! Tools-ct.988 should fix the >>> issue, I tested it in a fresh trunk image. But it would be probably better >>> if you could test it yourself, too. ;-) >>> >> >> No need to apologise. It's an easy mistake, and you fixed it. As long >> as we're all patient with each other and take responsibility (Andreas said >> "if you break it, you fix it") we're going to get along fine and be >> collectively productive. >> > > The following is not addressed to Christoph or his commit, but to Eliots > comment, above: Patience should begin within our development methodology. > The words above are correct and sweet, and I agree with them, but I feel > the need to caution against the implication that "everything's great as > long as you fix it *afterward*." Maybe for git-based projects, a *commit-first, > fix-later* strategy is what that culture likes and that system can > support, but it's not a good fit for Squeak's trunk. I believe Andreas > understood this, and he indicated that "breaking the trunk is generally > frowned upon." > > When it comes to code less than 24 hours old, no matter how simple it > seems, chances are about 80% that a subsequent "oops, sorry" commit will > need to follow. With "older," (e.g., even only just 48 hours!) *tested* > code, that chance drops significantly. Patience. Restraint. Please. :) > Let our methodology put time to work *for* us, by living with our changes > for a bit (as, it sounds like, Christoph did!) and witness them work in > context. Maybe not this time, but *generally, *you'll have a gist enough > to know whether it should be loaded and tested separately in a clean trunk > first. > > Cheers, > Chris > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 29 08:58:07 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 29 Sep 2020 10:58:07 +0200 Subject: [squeak-dev] tedious programming-in-the-debugger error needs fixing In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> Message-ID: Hi Karl. > I thought Trunk was for bleeding edge development. And that the bleeding occurs when stuff needs further fixing. That shouldn't be an excuse for hastly committing poorly tested changes. ;-) It's a matter of discipline, thought, and sometimes to restrain oneself. Especially when people are not working on Squeak full-time but have to carefully allocate time slots, e.g., on weekends. It can sometimes be a tricky balancing act. Best, Marcel Am 29.09.2020 10:51:18 schrieb karl ramberg : I thought Trunk was for bleeding edge development. And that the bleeding occurs when stuff needs further fixing. We need a middle ground so that the inbox doesn't just become a dead end of bit rotted improvements and fixes. Best, Karl On Tue, Sep 29, 2020 at 8:38 AM Marcel Taeumel wrote: Hi all! +1 to Eliot +1 to Chris +1000 to our community! :-) Happy programming. Happy squeaking. Best, Marcel Am 29.09.2020 01:01:14 schrieb Chris Muller : On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda wrote: Hi Christoph, On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph wrote: Hi Eliot, I'm very sorry for this bug, which was so unnecessary because my image has still a gigantic working copy ...! Tools-ct.988 should fix the issue, I tested it in a fresh trunk image. But it would be probably better if you could test it yourself, too. ;-) No need to apologise.  It's an easy mistake, and you fixed it.  As long as we're all patient with each other and take responsibility (Andreas said "if you break it, you fix it") we're going to get along fine and be collectively productive. The following is not addressed to Christoph or his commit, but to Eliots comment, above:  Patience should begin within our development methodology.  The words above are correct and sweet, and I agree with them, but I feel the need to caution against the implication that "everything's great as long as you fix it afterward."  Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk.  I believe Andreas understood this, and he indicated that "breaking the trunk is generally frowned upon." When it comes to code less than 24 hours old, no matter how simple it seems, chances are about 80% that a subsequent "oops, sorry" commit will need to follow.  With "older," (e.g., even only just 48 hours!) tested code, that chance drops significantly.  Patience.  Restraint.  Please.  :)  Let our methodology put time to work for us, by living with our changes for a bit (as, it sounds like, Christoph did!) and witness them work in context.  Maybe not this time, but generally, you'll have a gist enough to know whether it should be loaded and tested separately in a clean trunk first. Cheers,   Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 29 11:16:49 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 29 Sep 2020 11:16:49 0000 Subject: [squeak-dev] The Inbox: System-mt.1175.mcz Message-ID: A new version of System was added to project The Inbox: http://source.squeak.org/inbox/System-mt.1175.mcz ==================== Summary ==================== Name: System-mt.1175 Author: mt Time: 29 September 2020, 1:16:45.233157 pm UUID: f0acc060-a48b-2c4f-8b56-aaf2f55601b4 Ancestors: System-topa.1174 Adds a way to compute a space tally for objects up to a certain depth. Questions: - Is the interface (i.e. "depth" and "seen") OK? - Is the use of IdentitySet OK? - Can we speed this up somehow? - Do we need depth-messages for the "class analysis" protocol, too? =============== Diff against System-topa.1174 =============== Item was added: + ----- Method: SpaceTally>>spaceForInstance:depth: (in category 'instance size') ----- + spaceForInstance: anObject depth: anInteger + + ^ self spaceForInstance: anObject depth: anInteger seen: IdentitySet new! Item was added: + ----- Method: SpaceTally>>spaceForInstance:depth:seen: (in category 'instance size') ----- + spaceForInstance: anObject depth: anInteger seen: someObjects + + | class depth total | + (someObjects includes: anObject) ifTrue: [^ 0]. + someObjects add: anObject. + class := anObject class. + depth := anInteger - 1. + total := class isVariable + ifTrue: [class byteSizeOfInstanceOfSize: anObject basicSize] + ifFalse: [class isImmediateClass ifTrue: [0] ifFalse: [class byteSizeOfInstance]]. + depth >= 0 ifTrue: [ + anObject isCompiledCode + ifTrue: [ + anObject literalsDo: [:literal | + total := total + (self spaceForInstance: literal depth: depth seen: someObjects)]] + ifFalse: [ + 1 to: anObject basicSize do: [:index | + total := total + (self spaceForInstance: (anObject basicAt: index) depth: depth seen: someObjects)]. + 1 to: class instSize do: [:index | + total := total + (self spaceForInstance: (anObject instVarAt: index) depth: depth seen: someObjects)]]]. + ^ total! Item was added: + ----- Method: SpaceTally>>spaceForInstancesOf:depth: (in category 'instance size') ----- + spaceForInstancesOf: aClass depth: aNumber + "Answer a pair of the number of bytes consumed by all instances of the given class, including their object headers, and the number of instances. Follow each instance's fields up to the given depth. Beware of cycles to shared objects, which will tamper the resulting numbers. + + SpaceTally new spaceForInstancesOf: Form depth: 0. --- Same as #spaceForInstanecsOf: + SpaceTally new spaceForInstancesOf: Form depth: 1. --- Includes memory footprint for bits etc. + SpaceTally new spaceForInstancesOf: Form depth: 2. --- Also includes LargePositiveIntegers in bitmaps ;-) + " + + | instances total sub depth | + instances := aClass allInstances. + instances isEmpty ifTrue: [^#(0 0)]. + total := 0. + instances do: [:each | total := total + (self spaceForInstance: each depth: aNumber)]. + ^{ total. instances size }! From marcel.taeumel at hpi.de Tue Sep 29 11:21:41 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 29 Sep 2020 13:21:41 +0200 Subject: [squeak-dev] The Inbox: System-mt.1175.mcz In-Reply-To: References: Message-ID: ... we may want to add #spaceUsed on Object, too, and refine it for, e.g., Form to use depth = 2. Those depth values depend on the particular object/class design. Yet, being able to call "myMorph spaceUsed" or "myForm spaceUsed" could be a great debugging tool. Best, Marcel Am 29.09.2020 13:16:58 schrieb commits at source.squeak.org : A new version of System was added to project The Inbox: http://source.squeak.org/inbox/System-mt.1175.mcz ==================== Summary ==================== Name: System-mt.1175 Author: mt Time: 29 September 2020, 1:16:45.233157 pm UUID: f0acc060-a48b-2c4f-8b56-aaf2f55601b4 Ancestors: System-topa.1174 Adds a way to compute a space tally for objects up to a certain depth. Questions: - Is the interface (i.e. "depth" and "seen") OK? - Is the use of IdentitySet OK? - Can we speed this up somehow? - Do we need depth-messages for the "class analysis" protocol, too? =============== Diff against System-topa.1174 =============== Item was added: + ----- Method: SpaceTally>>spaceForInstance:depth: (in category 'instance size') ----- + spaceForInstance: anObject depth: anInteger + + ^ self spaceForInstance: anObject depth: anInteger seen: IdentitySet new! Item was added: + ----- Method: SpaceTally>>spaceForInstance:depth:seen: (in category 'instance size') ----- + spaceForInstance: anObject depth: anInteger seen: someObjects + + | class depth total | + (someObjects includes: anObject) ifTrue: [^ 0]. + someObjects add: anObject. + class := anObject class. + depth := anInteger - 1. + total := class isVariable + ifTrue: [class byteSizeOfInstanceOfSize: anObject basicSize] + ifFalse: [class isImmediateClass ifTrue: [0] ifFalse: [class byteSizeOfInstance]]. + depth >= 0 ifTrue: [ + anObject isCompiledCode + ifTrue: [ + anObject literalsDo: [:literal | + total := total + (self spaceForInstance: literal depth: depth seen: someObjects)]] + ifFalse: [ + 1 to: anObject basicSize do: [:index | + total := total + (self spaceForInstance: (anObject basicAt: index) depth: depth seen: someObjects)]. + 1 to: class instSize do: [:index | + total := total + (self spaceForInstance: (anObject instVarAt: index) depth: depth seen: someObjects)]]]. + ^ total! Item was added: + ----- Method: SpaceTally>>spaceForInstancesOf:depth: (in category 'instance size') ----- + spaceForInstancesOf: aClass depth: aNumber + "Answer a pair of the number of bytes consumed by all instances of the given class, including their object headers, and the number of instances. Follow each instance's fields up to the given depth. Beware of cycles to shared objects, which will tamper the resulting numbers. + + SpaceTally new spaceForInstancesOf: Form depth: 0. --- Same as #spaceForInstanecsOf: + SpaceTally new spaceForInstancesOf: Form depth: 1. --- Includes memory footprint for bits etc. + SpaceTally new spaceForInstancesOf: Form depth: 2. --- Also includes LargePositiveIntegers in bitmaps ;-) + " + + | instances total sub depth | + instances := aClass allInstances. + instances isEmpty ifTrue: [^#(0 0)]. + total := 0. + instances do: [:each | total := total + (self spaceForInstance: each depth: aNumber)]. + ^{ total. instances size }! -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 29 12:17:59 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 29 Sep 2020 05:17:59 -0700 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: Message-ID: <2320391A-2585-489B-9539-3714E7098628@gmail.com> Hi Marcel, > > On Sep 3, 2020, at 6:15 AM, Marcel Taeumel wrote: > >  > Hi -- > > #headingAndAutoselectForLiteral:do: is a private helper function to disguise a rather constant value, which is a prefix for a tool's label. It should not be used outside SystemNavigation. That would make things even worse. > > The basic issue here is that there is no specific object for the concerns "user" or "sender". So, it is tediously derived or forwarded in a functional style. > > We should find a way to get rid of #headingAndAutoselectForLiteral:do:. That very name indicates that there is something wrong with the overall invocation of SystemNavigation's public protocol. +1. Making a SystemNavigation stateful so that it can be assigned something for auto select would be great. It could also take a pattern not just a string, and hence deal with multi keyword messages better. If we separate the search and presentation functions there are several benefits too: - we can get a calculus for search, so that we can have and and or. So something like search for all calls on methods that refer to selector a and selector b in class hierarchy c are easily expressed as the and of three elemental searches, fir selector a, for selector b, for class hierarchy c - we can get rid of the duplication in the allFoo and browseAllFoo protocols, keeping the search and just sending browse to the result. - we can derive results in the forms we’d like, so a “navigation” could answer methods, classes, packages, method references, etc As a suggested implementation Object>>smalltalk or Object>>navigation answers a new SystemSearch instance; SystemSearch instances understand basic queries (allCallsOn:, allImplementorsOf: etc), and an and: and or: protocol that take either another SystemSearch or a message, and answer or update the receiver. Then at any time one can send autoSelect: to the SystemSearch, and browse to see its results. BTW, a concise way of creating a message might be cool; Message selector: #allCallsOn: argument: 123 is fine but a little verbose and tedious to type. #allCallsOn: << { 123 } might be nice, or a little too cryptic. I don’t know. Btw, Andreas hated and I hate typing systemNavigation; so long. I implement sn in Object as an abbreviation. Hence my suggestion of smalltalk or navigator above. search would be even better: (self search allCallsOn: #systemNavigation) “sets systemNavigation as the default auto select string” and: #inPackage: << { #Tools }; browse is much more flexible than self sysyemNavigation browseAllCallsOn: #systemNavigation loxalToPackage: #Tools Or have and: be the default combinatorial so one can cascade ands: self search allCallsOn: #systemNavigation; inPackage: #Tools; browse I like this last one. > > Best, > Marcel > >> Am 03.09.2020 15:02:06 schrieb Thiede, Christoph : >> >> Hi Chris, >> >> >> >> thanks for your feedback! >> >> >> > For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. >> >> I think this is very much a question of favor - personally, I prefer guard clauses over the functional style unless both code paths have the same relevance for the whole method. In my opinion, an empty message list is clearly a secondary edge case only. :-) >> >> > I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. >> >> Yes, this is not really an intuitive message name ... What do you think about calling it #readLiteral:withHeadingAndStringDo: instead (plus making it public)? >> >> Best, >> Christoph >> Von: Chris Muller >> Gesendet: Mittwoch, 2. September 2020 23:59:46 >> An: squeak dev; Thiede, Christoph >> Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz >> >> Hi Christoph, >> >> For your code contributions in general, please allow methods to have >> only a single exit as much as possible, as in the attached. >> >> I like the multilingual change, but the purpose of using >> #headingAndAutoselectForLiteral:do: here wasn't obvious to me. >> >> Best, >> Chris >> >> On Wed, Sep 2, 2020 at 9:56 AM wrote: >> > >> > Christoph Thiede uploaded a new version of Tools to project The Inbox: >> > http://source.squeak.org/inbox/Tools-ct.986.mcz >> > >> > ==================== Summary ==================== >> > >> > Name: Tools-ct.986 >> > Author: ct >> > Time: 2 September 2020, 4:55:38.538083 pm >> > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca >> > Ancestors: Tools-ct.985 >> > >> > Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. >> > >> > =============== Diff against Tools-ct.985 =============== >> > >> > Item was changed: >> > ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- >> > addParentMethodsSending: selectorSymbol >> > >> > + ^ self systemNavigation >> > + headingAndAutoselectForLiteral: selectorSymbol >> > + do: [:label :autoSelect | >> > + | methodsList | >> > + methodsList := self systemNavigation allCallsOn: selectorSymbol. >> > + methodsList ifEmpty: [ >> > + ^ self inform: ('There are no {1}' translated format: {label})]. >> > + self >> > - | methodsList | >> > - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty >> > - ifTrue: >> > - [ ^(PopUpMenu labels: ' OK ') >> > - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] >> > - ifFalse: >> > - [ self >> > addParentMessages: methodsList >> > + autoSelectString: autoSelect] >> > - autoSelectString: selectorSymbol ] >> > ! >> > >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 29 12:24:06 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 29 Sep 2020 05:24:06 -0700 Subject: [squeak-dev] Debugger - Proceed button (was: Why is ModificationForbidden not an Error?) In-Reply-To: References: Message-ID: <16069492-6612-4646-9067-BB5EE4EF3357@gmail.com> > On Apr 11, 2020, at 12:13 PM, Jakob Reschke wrote: > >  > Think of this: you may not be able to reasonably proceed after an unresumable error without modifying the system or the context. But the IDE lets you do just that: you can change something on the stack in the debugger, or implement the missing method, then proceed and violà, you just reasonably proceeded from an otherwise unresumable error. > > I suppose the methods you mentioned are recursive/repeating to allow for exactly that kind of workflow. > > One should naturally be allowed to Proceed from the notifier window of a Halt or Warning. > > Which leads us to the topic of exception-specific or context-specific buttons in the notifier once again. I don't remember which topic it was, but I wished for that already once during the last few months. Maybe it was the deprecation warnings and now we have an explanation how to deal with Warnings in the notifier and those text actions that allow to disable the warnings on the spot. And as always I'd like to point out that Common Lisp has a nice concept for responding to exceptions both automatically and interactively, with user-code-registered restart operations that can be invoked from the debugger: http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html In Smalltalk you can already use #return, #retry, #resume etc in an exception handler, but you cannot access the handlers nicely from the debugger, and you cannot "resume somewhere along the way to the exception". *1. Good points. Good analysis. >> Am Sa., 11. Apr. 2020 um 16:38 Uhr schrieb Thiede, Christoph : >> Just another thought: I see some confusion around the Proceed button in the debugger, and I felt the same confusion sometimes ago. Please forgive me for the sacrilege, but should we maybe question its general existence? >> >> Depending on the domain, it actually performs a mix of #retry (made possible by the quite confusing recursive implementation of Object >> #at:, Object >> #doesNotUnderstand: and others, I did not yet found any other motivation than the Proceed button to use recursion here) and ignore (#resumeUnchecked:, especially useful for UnhandledWarnings and Halts). Would it be a reasonable goal to eliminate this button in the pre-debugger window and replace it with two buttons, Restart and Ignore? We could hide even hide the restart button if not appropriate (i.e., the exception is not an error). >> >> What do you think? >> And it would be great if anyone here could explain why Object >> #at: & Co. are recursive! :-) >> >> Best, >> Christoph >> >> Von: Squeak-dev im Auftrag von Thiede, Christoph >> Gesendet: Samstag, 11. April 2020 16:32 Uhr >> An: Chris Muller; The general-purpose Squeak developers list >> Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? >> >> Hi all! Thank you very much for having this interesting discussion :-) >> >> >> >> @Chris: >> >> > > -1. :-) Warnings are Notifications, >> >> > As are Errors. Right? >> >> Nope, sorry ;-) >> >> >> >> >> >> > The two main use cases of ModificationForbidden present opposite perspectives onto it. For "protection from accidentally modifying a literal in a CompiledMethod", I understand the inclination to make it an Error. However, for the context of db handling, [#markDirty + #resume] is normal processing, not an "error", and not even exceptional, either, so perhaps this is just a semantic irritation sensitivity on my part.. sorry. >> >> > But if they want to use ModificationForbidden for a Write Barrier, they'll probably want to extricate it from Error in the handler: >> > >> > [ myDbApp doStuff ] >> > on: ModificationForbidden >> > do: >> > [ : forbidden | >> > forbidden object beWritableObject. >> > forbidden resumptionValue: forbidden retryModificationNoResume. >> > forbidden resume: forbidden resumptionValue ] >> > on: Error >> > do: [ : err | myDbApp logErrorAndNotifyUser ] >> >> This clarification was very helpful for me. I was not aware of your second use case before. >> >> In my opinion, we are talking about two completely different use cases for the same exception. >> Literal protection is a low-level error, comparable to out-of-bounds things and primitive failures. You almost never do want to ignore or fix them. >> Write barriers for #markDirty, on the other hand, sound like a domain-specific topic to me that should neither raise an error, nor eventually signal an UnhandledError, but be resumed automatically (unless handled differently) by a #defaultAction that removes the barrier (and additional handlers could, if desired, turn on some cache or so). >> I have never dealt with Magma or any other DB framework for Smalltalk, but why do you (or would you) use these VM-based mechanisms for a high-level feature? Without knowing any details about your application, personally I would probably design an own exception (DirtyModification?) for that purpose. This would allow you to clearly distinguish between low-level errors ("whoops, I just made an attempt to change a read-only code literal") and higher-level exceptions (DirtyModification). If we would follow my proposal to raise ModificationForbidden from Object instead of Context, you could also consider to override #modificationForbiddenFor:at:put: in your database objects to raise DirtyModification instead of ModificationForbidden (in the same way as some classes override #error or #primitiveError). >> >> However, focusing again on the literal protection aspect, I still don't see when the current default #resume behavior of ModificationForbidden would be ever helpful. MF >> #resume calls Exception >> #resume: which does nothing more than to return resumptionValue! When do we actually need this resumptionValue? >> Why can't we design MF like the following: >> >> #resume - not possible, will signal IllegalResumeAttempt >> #retryModification - retries the modification and returns nothing special >> #forceModification: - makes the object to modify writable, retries the modification and, optionally, makes the object read-only again >> >> > If we don't want to break things (that are somehow wrong according to contemporary notion), we could make it a Warning in the Squeak 5.x release stream and turn it into an error with Squeak 6.0. That is: make it an Error today in Trunk, but if we would create a 5.4 release, we would have to remember changing it back... :-/ >> >> IIRC ModificationForbidden was introduced in 6.0alpha the first time? >> >> Best, >> Christoph >> Von: Squeak-dev im Auftrag von Nicolas Cellier >> Gesendet: Samstag, 11. April 2020 11:16:00 >> An: Chris Muller; The general-purpose Squeak developers list >> Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? >> >> In this case we might want specialized subclasses... >> >>> Le sam. 11 avr. 2020 à 03:45, Chris Muller a écrit : >>> I think so, because if you try to use it for something else, it'll get entangled with your application's standard error handling. Regular apps have error handling like: >>> >>> [ myApp doStuff ] on: Error do: [ : err | myApp logErrorAndNotifyUser ] >>> >>> and so for the use-case, Protect CompiledMethod Literals from Accidental Modification, inheriting from Error will allow the best backward compatibility with existing handlers. >>> >>> But if they want to use ModificationForbidden for a Write Barrier, they'll probably want to extricate it from Error in the handler: >>> >>> [ myDbApp doStuff ] >>> on: ModificationForbidden >>> do: >>> [ : forbidden | >>> forbidden object beWritableObject. >>> forbidden resumptionValue: forbidden retryModificationNoResume. >>> forbidden resume: forbidden resumptionValue ] >>> on: Error >>> do: [ : err | myDbApp logErrorAndNotifyUser ] >>> >>> But now that I'm handling ModificationForbidden separately, what if I want Protect CompiledMethod Literals from Accidental Modification, too? I'm no longer handling correctly for that, because the handler has to assume they're signaled in the context of the DB use case and not the Protection use case. >>> >>> - Chris >>> >>>> On Fri, Apr 10, 2020 at 12:06 PM tim Rowledge wrote: >>>> >>>> >>>> > On 2020-04-09, at 6:55 PM, Chris Muller wrote: >>>> > >>>> > The two main use cases of ModificationForbidden present opposite perspectives onto it. >>>> >>>> In that case perhaps we actually need two different signals? >>>> >>>> tim >>>> -- >>>> tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim >>>> Useful random insult:- Has an inferiority complex, but not a very good one. >>>> >>>> >>>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Tue Sep 29 12:26:29 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 29 Sep 2020 05:26:29 -0700 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: References: Message-ID: <7D0A704E-7BF3-4A6C-B6A7-10A6A274A700@gmail.com> > On Sep 29, 2020, at 1:07 AM, Marcel Taeumel wrote: > >  > Hi Eliot, hi all! > > I fixed the issue with Tools-mt.989. The logic was already there in #runUntil. <3 thank you, thank you, thank you!! > Best, > Marcel >> Am 19.09.2020 23:00:33 schrieb Levente Uzonyi : >> >> Hi Christoph, >> >> On Sat, 19 Sep 2020, Thiede, Christoph wrote: >> >> > >> > Hi Eliot, >> > >> > >> > very nice finding once again! I #timeProfile'd the menu button action and as I expected, the most expensive operation is the shout styling by the new inspectors, including the decompilation of every method: >> >> What was it exactly that you profiled? >> >> The screenshot shows that 76.9% was spent in #initializeVariablesFromContext, >> of which 52.5% of the time was spent in CompiledMethod(CompiledCode) >> >> #getSource. The rest of the tree is not visible, but these methods have >> nothing to do with parsing or styling, they initialize the parser and >> normally should take <1 ms. >> >> Also, why is the decompiler producing the source code? >> >> > >> > >> > [IMAGE] >> > >> > >> > First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but probably that's rather a problem with my sources file, see the thread about this commit). >> >> You may want to try compiling a VM where FilePlugin's CreateFile does not >> set the FILE_FLAG_SEQUENTIAL_SCAN flag, and see if it helps with file >> reading performance. >> >> >> Levente >> >> > >> > Second, we do not redraw the world while running to the selection, so we do not need to update the inspectors at all. I think we could split up #doStep into some #basicDoStep (which would perform the actual stepping logic) + >> > some #updateContextDuring: (which would update the stack list and the inspectors), then we would need to trigger the updates only once from #runToSelection:. >> > As an alternative, we could make this method a bit more complex but responsive by applying the same updating logic from #runUntil. >> > >> > What do you think? :-) >> > >> > Best, >> > Christoph >> > >> > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ >> > Von: Squeak-dev im Auftrag von Eliot Miranda >> > Gesendet: Samstag, 19. September 2020 20:17:12 >> > An: The general-purpose Squeak developers list; Taeumel, Marcel >> > Betreff: [squeak-dev] Can we make computing the local variables in the debugger lazy? >> > Hi Marcel, >> > >> > can we try and reduce the frequency at which we compute the variables in the context inspector in the debugger? It is a noticeable performance hit. I really like the user interface, but the performance hit is making >> > debugging difficult. >> > >> > As an example use this: >> > >> > | samples sineTable sound | >> > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" >> > sineTable := SoundPlayer sineTable: 73. >> > sineTable doWithIndex: "And let's not deafen anyone..." >> > [:sample :index| sineTable at: index put: sample // 4]. >> > samples := SoundBuffer new: 16000. >> > 1 to: samples size by: sineTable size do: >> > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. >> > 1 to: 146 do: >> > [:i| >> > samples at: i put: ((samples at: i) * i / 146) asInteger. >> > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. >> > sound := SampledSound >> > samples: samples >> > samplingRate: 16000. >> > sound := MixedSound new >> > add: sound pan: 0.25; >> > add: sound pan: 0.75; >> > yourself. >> > sound computeSamplesForSeconds: sound duration >> > >> > >> > Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an image containing Tools-mt.942). Debug the above in the workspace. Position the cursor at "sound computeSamplesForSeconds: sound duration" and do "run >> > to here". It is essentially instantaneous. >> > >> > Now do the same in a contemporary trunk image. It takes almost 6 seconds. >> > >> > I used to be able to click step as fast as I could and the system would keep up with me. Now I find that if I click too fast I can accumulate excess clicks and when I stp clicking the system will continue stepping and go too >> > far. >> > >> > I don't want to lose the feedback the new variables list gives us. But I do find the performance hit tedious. I wonder could we cache the list and only update it >> > - when Morphic updates, and >> > - when the context changes? >> > >> > >> > _,,,^..^,,,_ >> > best, Eliot >> > >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From herbertkoenig at gmx.net Tue Sep 29 12:44:46 2020 From: herbertkoenig at gmx.net (Herbert) Date: Tue, 29 Sep 2020 14:44:46 +0200 Subject: [squeak-dev] Development methodology In-Reply-To: <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> Message-ID: <15932e4a-cb75-f5ac-e5e0-135204da5f2b@gmx.net> Hi all, let me just throw in the cost curve (Barry Boehm I think). A bug gets exponentially more expensive, the later it's found. And the fact that for x bugs found during testing you can be sure to deliver n*x bugs to the customer. n maybe <<1 but still. Don't know the source of this any more. I'm bitten by a workflow of quick commits (we are also using Git) as a QA person in my day job where they also add hasty reviews to the quick commits. So I kind of cringed when I read Christoph's post :-). Still a complex or tedious workflow is also bad for quality and productivity so that's the point of the post I fully agree with. I'm too far away from Squeak but would it be a possibility to have small commits in Git (or elsewhere) and consolidated bigger ones for a proper ancestry? At least we need to be very careful when changing the community process. Best regards, Herbert Am 29.09.20 um 01:38 schrieb Thiede, Christoph: > > > Maybe for git-based projects, a commit-first, fix-later strategy is > what that culture likes and that system can support, but it's not a > good fit for Squeak's trunk. > > > And that's why - sorry for rolling out my old chestnut again - I still > do believe that a git-based workflow could make us as a community more > efficient. > > I don't question that manual testing is great, and I don't question > that quality can increase if you give your patches time for maturing, > but when I compare the resulting workflow to the "mainstream" workflow > that I can use anywhere on GitHub, I repeatedly have the dissatisfying > feeling that the inbox/trunk workflow is so slow that it ruins all the > efficiency from the Smalltalkish development workflow (which, however, > unquestionably outperforms the "mainstream" workflow in a dead, > non-live UI without first-class objects for code and tools!). > > This might apply most significantly to small changes that would form a > PR of two or three commits in a git project because our inbox workflow > does not scale so well for changes of such extent. I do not know how > many hours I already have spent on fixing the ancestry of my versions, > comparing them to their ancestors, or re-uploading them, but > it has definitively been too many hours ... > > > Did someone ever investigate this question seriously by doing a study > or so? I would really find the results interesting. > > > Best, > > Christoph > > > ------------------------------------------------------------------------ > *Von:* Squeak-dev im > Auftrag von Chris Muller > *Gesendet:* Dienstag, 29. September 2020 01:00 Uhr > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] tedious programming-in-the-debugger error > needs fixing > On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda > > wrote: > > Hi Christoph, > > On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph > > wrote: > > Hi Eliot, > > > I'm very sorry for this bug, which was so unnecessary because > my image has still a gigantic working copy ...! Tools-ct.988 > should fix the issue, I tested it in a fresh trunk image. But > it would be probably better if you could test it yourself, > too. ;-) > > > No need to apologise.  It's an easy mistake, and you fixed it.  As > long as we're all patient with each other and take responsibility > (Andreas said "if you break it, you fix it") we're going to get > along fine and be collectively productive. > > > The following is not addressed to Christoph or his commit, but to > Eliots comment, above:  Patience should begin within our development > methodology.  The words above are correct and sweet, and I agree with > them, but I feel the need to caution against the implication that > "everything's great as long as you fix it /afterward/." Maybe for > git-based projects, a /commit-first, fix-later/ strategy is what that > culture likes and that system can support, but it's not a good fit for > Squeak's trunk.  I believe Andreas understood this, and he indicated > that "breaking the trunk is generally frowned upon." > > When it comes to code less than 24 hours old, no matter how simple it > seems, chances are about 80% that a subsequent "oops, sorry" commit > will need to follow.  With "older," (e.g., even only just 48 hours!) > _tested_ code, that chance drops significantly. Patience.  Restraint.  > Please.  :)  Let our methodology put time to work /for/ us, by living > with our changes for a bit (as, it sounds like, Christoph did!) and > witness them work in context.  Maybe not this time, but /generally, > /you'll have a gist enough to know whether it should be loaded and > tested separately in a clean trunk first. > > Cheers, >   Chris > -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Tue Sep 29 12:57:38 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Tue, 29 Sep 2020 14:57:38 +0200 Subject: [squeak-dev] Development methodology (was: tedious programming-in-the-debugger error needs fixing) In-Reply-To: <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> <,> <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph. > I repeatedly have the dissatisfying feeling that the inbox/trunk workflow is so slow that it ruins all the efficiency from the Smalltalkish development workflow >From what I experienced so far, its mostly a matter of time and people. Those occasional hiccups with our inbox don't change the fact that reviews take time and effort. Having a fancy "auto-merge this pull request because CI says it is okay" won't make it better. Maybe even worse. Time and people. Or the other way round. ;-) And we cannot just promote all frequent contributors to be Trunk committers to speed up the process. It still takes a lot of time until new Squeaker's understand the essence of what Chris was talking about here: http://forum.world.st/tedious-programming-in-the-debugger-error-needs-fixing-tp5109568p5122658.html [http://forum.world.st/tedious-programming-in-the-debugger-error-needs-fixing-tp5109568p5122658.html] --- That is, the kind of discipline and patience that even experienced Squeak's (and Trunk committers) struggle with from time to time. One cannot just wrap Git and GitHub (tools) around some project and expect that problem magically go away. Just look at the OpenSmalltalk VM repo: https://github.com/OpenSmalltalk/opensmalltalk-vm [https://github.com/OpenSmalltalk/opensmalltalk-vm] --- Many open issues and pull requests, some of them are several years old. Best, Marcel Am 29.09.2020 01:38:22 schrieb Thiede, Christoph : > Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk. And that's why - sorry for rolling out my old chestnut again - I still do believe that a git-based workflow could make us as a community more efficient. I don't question that manual testing is great, and I don't question that quality can increase if you give your patches time for maturing, but when I compare the resulting workflow to the "mainstream" workflow that I can use anywhere on GitHub, I repeatedly have the dissatisfying feeling that the inbox/trunk workflow is so slow that it ruins all the efficiency from the Smalltalkish development workflow (which, however, unquestionably outperforms the "mainstream" workflow in a dead, non-live UI without first-class objects for code and tools!). This might apply most significantly to small changes that would form a PR of two or three commits in a git project because our inbox workflow does not scale so well for changes of such extent. I do not know how many hours I already have spent on fixing the ancestry of my versions, comparing them to their ancestors, or re-uploading them, but it has definitively been too many hours ... Did someone ever investigate this question seriously by doing a study or so? I would really find the results interesting. Best, Christoph Von: Squeak-dev im Auftrag von Chris Muller Gesendet: Dienstag, 29. September 2020 01:00 Uhr An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] tedious programming-in-the-debugger error needs fixing   On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda wrote: Hi Christoph, On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph wrote: Hi Eliot, I'm very sorry for this bug, which was so unnecessary because my image has still a gigantic working copy ...! Tools-ct.988 should fix the issue, I tested it in a fresh trunk image. But it would be probably better if you could test it yourself, too. ;-) No need to apologise.  It's an easy mistake, and you fixed it.  As long as we're all patient with each other and take responsibility (Andreas said "if you break it, you fix it") we're going to get along fine and be collectively productive. The following is not addressed to Christoph or his commit, but to Eliots comment, above:  Patience should begin within our development methodology.  The words above are correct and sweet, and I agree with them, but I feel the need to caution against the implication that "everything's great as long as you fix it afterward."  Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk.  I believe Andreas understood this, and he indicated that "breaking the trunk is generally frowned upon." When it comes to code less than 24 hours old, no matter how simple it seems, chances are about 80% that a subsequent "oops, sorry" commit will need to follow.  With "older," (e.g., even only just 48 hours!) tested code, that chance drops significantly.  Patience.  Restraint.  Please.  :)  Let our methodology put time to work for us, by living with our changes for a bit (as, it sounds like, Christoph did!) and witness them work in context.  Maybe not this time, but generally, you'll have a gist enough to know whether it should be loaded and tested separately in a clean trunk first. Cheers,   Chris -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Tue Sep 29 13:05:56 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 29 Sep 2020 13:05:56 0000 Subject: [squeak-dev] The Inbox: Collections-ct.913.mcz Message-ID: Christoph Thiede uploaded a new version of Collections to project The Inbox: http://source.squeak.org/inbox/Collections-ct.913.mcz ==================== Summary ==================== Name: Collections-ct.913 Author: ct Time: 29 September 2020, 3:05:50.763807 pm UUID: a7262c7e-0824-c543-bb32-2905e45eb317 Ancestors: Collections-eem.912 Adds convenience method for converting text to HTML, #printHtmlOn:breakLines:. =============== Diff against Collections-eem.912 =============== Item was changed: ----- Method: Text>>printHtmlOn: (in category 'html') ----- + printHtmlOn: aStream + + ^ self + printHtmlOn: aStream + breakLines: true! - printHtmlOn: aStream - - (HtmlReadWriter on: aStream) - nextPutText: self.! Item was added: + ----- Method: Text>>printHtmlOn:breakLines: (in category 'html') ----- + printHtmlOn: aStream breakLines: aBoolean + + (HtmlReadWriter on: aStream) + breakLines: aBoolean; + nextPutText: self.! From commits at source.squeak.org Tue Sep 29 13:16:04 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 29 Sep 2020 13:16:04 0000 Subject: [squeak-dev] The Inbox: Collections-ct.914.mcz Message-ID: Christoph Thiede uploaded a new version of Collections to project The Inbox: http://source.squeak.org/inbox/Collections-ct.914.mcz ==================== Summary ==================== Name: Collections-ct.914 Author: ct Time: 29 September 2020, 3:15:58.543807 pm UUID: 6bb73b4a-58ae-be43-a0f4-fb265b32b30f Ancestors: Collections-eem.912 Proposal: Supply start and stop of attributes in Text >> #[removeAttributesThat:][replaceAttributesThat:][by:]. For example, this can be useful for retrieving the part of a text that belongs to an attribute before doing filtering the attribute in some way. =============== Diff against Collections-eem.912 =============== Item was changed: ----- Method: Text>>removeAttributesThat:replaceAttributesThat:by: (in category 'converting') ----- removeAttributesThat: removalBlock replaceAttributesThat: replaceBlock by: convertBlock "Enumerate all attributes in the receiver. Remove those passing removalBlock and replace those passing replaceBlock after converting it through convertBlock" | added removed | "Deliberately optimized for the no-op default." added := removed := nil. runs withStartStopAndValueDo: [ :start :stop :attribs | attribs do: [ :attrib | | new | + (removalBlock cull: attrib cull: start cull: stop) ifTrue:[ - (removalBlock value: attrib) ifTrue:[ removed ifNil:[removed := WriteStream on: #()]. removed nextPut: {start. stop. attrib}. ] ifFalse:[ + (replaceBlock cull: attrib cull: start cull: stop) ifTrue:[ - (replaceBlock value: attrib) ifTrue:[ removed ifNil:[removed := WriteStream on: #()]. removed nextPut: {start. stop. attrib}. + new := convertBlock cull: attrib cull: start cull: stop. - new := convertBlock value: attrib. added ifNil:[added := WriteStream on: #()]. added nextPut: {start. stop. new}. ]. ]. ]. ]. (added == nil and:[removed == nil]) ifTrue:[^self]. "otherwise do the real work" removed ifNotNil:[removed contents do:[:spec| self removeAttribute: spec last from: spec first to: spec second]]. added ifNotNil:[added contents do:[:spec| self addAttribute: spec last from: spec first to: spec second]].! From commits at source.squeak.org Tue Sep 29 13:45:18 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Tue, 29 Sep 2020 13:45:18 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1683.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1683.mcz ==================== Summary ==================== Name: Morphic-ct.1683 Author: ct Time: 29 September 2020, 3:45:11.774971 pm UUID: fb544fe9-159d-8443-8946-e2651331750f Ancestors: Morphic-dtl.1680 Fixes an abandoning bug in menu items. When clicking on a menu item that has a submenu, the menu should not disappear! In addition, do not deselect a menu item once it is clicked, which is not important for regular menus but looks confusing when the menu is configured to #stayUp. =============== Diff against Morphic-dtl.1680 =============== Item was changed: ----- Method: MenuItemMorph>>invokeWithEvent: (in category 'events') ----- invokeWithEvent: evt "Perform the action associated with the given menu item." self isEnabled ifFalse: [^ self]. + (owner notNil and: [self isStayUpItem not] and: [self hasSubMenu not]) ifTrue: [ - (owner notNil and: [self isStayUpItem not]) ifTrue: [ self flag: #workAround. "The tile system invokes menus straightforwardly so the menu might not be in the world." self world ifNotNil: [:world | owner deleteIfPopUp: evt. "Repair damage before invoking the action for better feedback" world displayWorldSafely]]. selector ifNil: [^ self]. Cursor normal showWhile: [ "show cursor in case item opens a new MVC window" selector numArgs isZero ifTrue: [target perform: selector] ifFalse: [target perform: selector withArguments: ( selector numArgs = arguments size ifTrue: [arguments] ifFalse: [arguments copyWith: evt] )] ].! Item was changed: ----- Method: MenuItemMorph>>mouseUp: (in category 'events') ----- mouseUp: evt "Handle a mouse up event. Menu items get activated when the mouse is over them. Do nothing if we're not in a 'valid menu transition', meaning that the current hand focus must be aimed at the owning menu." + - evt hand mouseFocus == owner ifFalse: [ ^self ]. self contentString ifNotNil: [ self contents: self contentString withMarkers: true inverse: true. self refreshWorld. (Delay forMilliseconds: 200) wait ]. + "self deselect: evt." + self invokeWithEvent: evt. ! - self deselect: evt. - self invokeWithEvent: evt. - ! From eliot.miranda at gmail.com Tue Sep 29 16:05:31 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Tue, 29 Sep 2020 09:05:31 -0700 Subject: [squeak-dev] Development methodology In-Reply-To: <15932e4a-cb75-f5ac-e5e0-135204da5f2b@gmx.net> References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> <15932e4a-cb75-f5ac-e5e0-135204da5f2b@gmx.net> Message-ID: Hi Herbert, On Tue, Sep 29, 2020 at 5:44 AM Herbert wrote: > Hi all, > > > let me just throw in the cost curve (Barry Boehm I think). A bug gets > exponentially more expensive, the later it's found. And the fact that for x > bugs found during testing you can be sure to deliver n*x bugs to the > customer. n maybe <<1 but still. Don't know the source of this any more. > Thanks, this accords with my experience. Thank you. It also is a strong argument for continuous integration and tests, *BUT* (and it's a big BUT) only if the test signals can be seen. So lumping lots of tests together so that the one persistently failing test marks the whole suite as having failed isn't helpful. For example, if our SUnit tests were grouped by package then we could see much sooner when a commit of a package broke something. Right now, with everything lumped together the fact that we have some failing tests (an inevitability in complex projects with lots going on, and long term issues that need long term actions to solve) obscures the test results, and means we're not able to use them as we should. If we broke out the test results in sub groups, and didn't report the overall signal, but emphasised the deltas between successive runs of individual groups, we would see when things break. Right now the CI for VM builds aggregates in exactly this way so when I look in my email I see that the tests have failed (so what's new"). Well, yesterday I had time to look (I don't always) and found that the builds are OK up until a run of a build of a newspeak VM, and the VM build is OK, it is the Newspeak bootstrap that fals, and since Newspeak (I think I'm right in thinking) is no longer maintained, this is not a surprise. But instead of simply discarding the Newspeak build to get green tests again, better would be to a) not chain the builds so that an early failure prevents potentially successful subsequent builds, i.e. attempt to build everything b) report to the mailing list a message that specifies which builds have failed, so the subject line would be something like "Travis CI: M builds out of N have failed", not the depressing "the build has failed" I'm bitten by a workflow of quick commits (we are also using Git) as a QA > person in my day job where they also add hasty reviews to the quick > commits. So I kind of cringed when I read Christoph's post :-). > > > Still a complex or tedious workflow is also bad for quality and > productivity so that's the point of the post I fully agree with. > > > I'm too far away from Squeak but would it be a possibility to have small > commits in Git (or elsewhere) and consolidated bigger ones for a proper > ancestry? At least we need to be very careful when changing the community > process. > We have all the infrastructure we need in Monticello (it supports branching and merging, and multiple repositories). We even have something close to what you suggest in production, with release repositories that allow people to stay with e.g. squeak53, and we can push improvements and bug fixes there. But I agree, we want something like a high-frequency trunk and a low-frequency trunk. So we could implement e.g. treatedtrunk, and, say, automate pushing the 3 day, or 7 day old, trunk to treatedtrunk. It would be easy for those wanting a stable trunk process to stay with the treatedtrunk update stream, and to load from trunk the latest greatest that they really want, since the delta between treatedtrunk and trunk would be small, and the delta would be no more than a few days worth of commits. > Best regards, > > > Herbert > > > > Am 29.09.20 um 01:38 schrieb Thiede, Christoph: > > > Maybe for git-based projects, a commit-first, fix-later strategy is > what that culture likes and that system can support, but it's not a good > fit for Squeak's trunk. > > > And that's why - sorry for rolling out my old chestnut again - I still > do believe that a git-based workflow could make us as a community more > efficient. > > I don't question that manual testing is great, and I don't question that > quality can increase if you give your patches time for maturing, but when I > compare the resulting workflow to the "mainstream" workflow that I can use > anywhere on GitHub, I repeatedly have the dissatisfying feeling that the > inbox/trunk workflow is so slow that it ruins all the efficiency from the > Smalltalkish development workflow (which, however, unquestionably > outperforms the "mainstream" workflow in a dead, non-live UI without > first-class objects for code and tools!). > > This might apply most significantly to small changes that would form a PR > of two or three commits in a git project because our inbox workflow does > not scale so well for changes of such extent. I do not know how many > hours I already have spent on fixing the ancestry of my versions, comparing > them to their ancestors, or re-uploading them, but it has definitively > been too many hours ... > > > Did someone ever investigate this question seriously by doing a study or > so? I would really find the results interesting. > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev > im Auftrag von Chris > Muller > *Gesendet:* Dienstag, 29. September 2020 01:00 Uhr > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] tedious programming-in-the-debugger error > needs fixing > > On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda > wrote: > >> Hi Christoph, >> >> On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph < >> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >> >>> Hi Eliot, >>> >>> >>> I'm very sorry for this bug, which was so unnecessary because my image >>> has still a gigantic working copy ...! Tools-ct.988 should fix the >>> issue, I tested it in a fresh trunk image. But it would be probably better >>> if you could test it yourself, too. ;-) >>> >> >> No need to apologise. It's an easy mistake, and you fixed it. As long >> as we're all patient with each other and take responsibility (Andreas said >> "if you break it, you fix it") we're going to get along fine and be >> collectively productive. >> > > The following is not addressed to Christoph or his commit, but to Eliots > comment, above: Patience should begin within our development methodology. > The words above are correct and sweet, and I agree with them, but I feel > the need to caution against the implication that "everything's great as > long as you fix it *afterward*." Maybe for git-based projects, a *commit-first, > fix-later* strategy is what that culture likes and that system can > support, but it's not a good fit for Squeak's trunk. I believe Andreas > understood this, and he indicated that "breaking the trunk is generally > frowned upon." > > When it comes to code less than 24 hours old, no matter how simple it > seems, chances are about 80% that a subsequent "oops, sorry" commit will > need to follow. With "older," (e.g., even only just 48 hours!) *tested* > code, that chance drops significantly. Patience. Restraint. Please. :) > Let our methodology put time to work *for* us, by living with our changes > for a bit (as, it sounds like, Christoph did!) and witness them work in > context. Maybe not this time, but *generally, *you'll have a gist enough > to know whether it should be loaded and tested separately in a clean trunk > first. > > Cheers, > Chris > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From forums.jakob at resfarm.de Tue Sep 29 17:08:38 2020 From: forums.jakob at resfarm.de (Jakob Reschke) Date: Tue, 29 Sep 2020 19:08:38 +0200 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: <6dce22431e5d4e78a36f8f42d65554f7@student.hpi.uni-potsdam.de> References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> <6dce22431e5d4e78a36f8f42d65554f7@student.hpi.uni-potsdam.de> Message-ID: Am Di., 29. Sept. 2020 um 02:37 Uhr schrieb Thiede, Christoph : > > However, is there an option to subscribe to arbitrary packages (such as the JSON package) in order to get notified via email about new changes to it? :-) > Lately I have seen a bot that automatically posts a pull request for your npm package.json if a dependency has a new release... Could have been https://github.com/renovatebot/renovate Now there is probably nothing like "releases" going on in the JSON package, but maybe a similar service can be achieved for Monticello or Metacello configurations. From leves at caesar.elte.hu Tue Sep 29 19:43:37 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Tue, 29 Sep 2020 21:43:37 +0200 (CEST) Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: <6dce22431e5d4e78a36f8f42d65554f7@student.hpi.uni-potsdam.de> References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de>, <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de>, <6dce22431e5d4e78a36f8f42d65554f7@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, On Tue, 29 Sep 2020, Thiede, Christoph wrote: > > Hi all, hi Levente, > > > I just wanted to send you a fix for parsing "\u2139\ufffd", but then I realized that you already had fixed this in the repository. So what, at least I understood my own error! That's really weird because we even had a small discussion about it in this thread: http://forum.world.st/I-d-like-to-contribute-to-the-JSON-project-tp5121353p5121357.html > > > However, is there an option to subscribe to arbitrary packages (such as the JSON package) in order to get notified via email about new changes to it? :-) Email? I don't think so. RSS is probably the closest thing: https://squeaksource.com/feed.rss Levente > > > Best, > > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Levente Uzonyi > Gesendet: Dienstag, 8. September 2020 22:22:10 > An: The general-purpose Squeak developers list > Cc: Niephaus, Fabio > Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project   > Hi Christoph, > > On Tue, 8 Sep 2020, Thiede, Christoph wrote: > > > > > Hi Levente, > > > > > > is there no active development happening on http://www.squeaksource.com/JSON any longer? > > Last commit is from 2016. I wanted to have our changes added to the main > repository[1]. > > > Is your fork specialized for optimized code? > > It has got performance improvements and new features as well. There are > quite a few changes, so I suggest you go through the MC history if you > want to know what has been changed. > > >I was rather trying to reuse existing functionality such as #nextHexDigit. Just for > > interest, how much faster did you get the code by optimizing my changes? ;-) > > I haven't measured it. The parser has been rewritten, so the code is quite > different. But I expect my version to be at least a magnitude faster. > > > Levente > > [1] http://forum.world.st/Another-version-of-JSON-in-the-Inbox-td5066614.html > > > > > > > Best, > > > > Christoph > > > >________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > _ > > Von: Squeak-dev im Auftrag von Levente Uzonyi > > Gesendet: Sonntag, 6. September 2020 02:13:56 > > An: The general-purpose Squeak developers list > > Cc: Niephaus, Fabio > > Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project   > > Hi Christoph, > > > > Your code looks correct to me, though it's a bit too slow for my taste. > > I've fixed it in my fork of JSON[1] as well. > > > > Levente > > [1] http://squeaksource.com/PostgresV3/JSON-ul.55.mcz > > > > On Sat, 5 Sep 2020, Thiede, Christoph wrote: > > > > > > > > Hi all, > > > > > > > > > I'd like to contribute to the JSON project, but apparently I lack the required access rights. Would it be possible to review this patch, which adds UTF-16 support for JSON strings, with the final goal to introduce it into > > the > > > repository? :-) > > > > > > > > > Best, > > > > > > Christoph > > > > > > > > > > > > > > > From asqueaker at gmail.com Tue Sep 29 20:46:59 2020 From: asqueaker at gmail.com (Chris Muller) Date: Tue, 29 Sep 2020 15:46:59 -0500 Subject: [squeak-dev] Question about RemoteTask In-Reply-To: <20200929003306.GB7470@shell.msen.com> References: <90a453f5ab86ec88d0a1e4a63182b0a7@whidbey.com> <20200927000604.GA27742@shell.msen.com> <20200929003306.GB7470@shell.msen.com> Message-ID: Thank you! On Mon, Sep 28, 2020 at 7:33 PM David T. Lewis wrote: > Hi Chris, > > RemoteTask is entirely in memory, no disk access, it forks an exact copy > of the image and VM. The block is evaluated in the child image, and the > result is serialized back to your parent image. The parent and child > image are identical at the point of the fork, so neither is any more or > less secure than the other. > > Regarding your convenience method, please try using OSProcess > class>>outputOf: > instead. This will probably do a much better job for you because it > handles all of the stdio piping without using files, and it also will > present any external command errors in the form of an exception in Squeak. > > Dave > > On Mon, Sep 28, 2020 at 04:23:23PM -0500, Chris Muller wrote: > > Hi Dave, > > > > I saw your RemoteTask the other day and had a quick question about it. > > > > Do you see any pitfalls doing operations that require or provide > sensitive > > security key information as input, or output. > > > > As an analogy, I've been using my own convenience method on OSProcess to > > give me a way get Linux command output into the image, but it does so by > > simply redirecting the command's output to a file, reading that file into > > an in-image String, then deleting the file. But that's not something I > > would want to use if it involved passing a password, for example. > > > > My hope is RemoteTask operates all in memory and does not utilize the > hard > > drive to accomplish that. > > > > Thanks, > > Chris > > > > On Sat, Sep 26, 2020 at 7:06 PM David T. Lewis > wrote: > > > > > > > > On Sat, Sep 26, 2020 at 03:20:10PM -0700, tim Rowledge wrote: > > > > > > > > And multi-core? Well yes I suppose. Except that one can very easily > > > spawn multiple running images using Dave Lewis' OSProcess package, > > > including in ways that do some work and return the results.The spawning > > > takes very little time; for example on said Pi4 it takes 39mS to do > > > > UnixProcess forkHeadlessSqueakAndDoThenQuit: [UnixProcess > helloWorld] > > > > > > > > > Or the somewhat more interesting example: > > > > > > RemoteTask do: [3 + 4] ==> 7 > > > > > > which completes in on the order of 10ms on my PC, and hopefully not too > > > much worse on Pi4. The [3 + 4] block is evaluated in a spawned image > with > > > results returned to the parent image. > > > > > > > > > Dave > > > > > > > > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From herbertkoenig at gmx.net Tue Sep 29 20:58:20 2020 From: herbertkoenig at gmx.net (=?UTF-8?Q?Herbert_K=c3=b6nig?=) Date: Tue, 29 Sep 2020 22:58:20 +0200 Subject: [squeak-dev] Development methodology In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> <15932e4a-cb75-f5ac-e5e0-135204da5f2b@gmx.net> Message-ID: <2f578a11-c8ee-16f7-8690-447a8b88d9f4@gmx.net> Hi Eliot, ...snip... >  *BUT* (and it's a big BUT) only if the test signals can be seen. ...snip... > So lumping lots of tests together so that the one persistently failing > test marks the whole suite as having failed isn't helpful. For > example, if our SUnit tests were grouped by package then we could see > much sooner when a commit of a package broke something.  Right now, > with everything lumped together the fact that we have some failing > tests (an inevitability in complex projects with lots going on, and > long term issues that need long term actions to solve) obscures the > test results, and means we're not able to use them as we should. If we > broke out the test results in sub groups, and didn't report the > overall signal, but emphasised the deltas between successive runs of > individual groups, we would see when things break. Fully agree. I never know what to make of the report that goes to the list. An I would be frustrated searching the problem with that little information. What if Squeak's TestRunner iterates over the packages  and on each failure writes a text file with the packages's name. Jenkins appends the directory list of these files to the mail going to Squeak dev. Sorry I know nothing about Squeak's build system but at my employer ASCII files are what binds the system tests together. Audio processing, GUI, hardware, different cultures, different programming languages, incompatible tools, but we get one detailed report where we can see the single test that failed. > > Right now the CI for VM builds aggregates in exactly this way so when > I look in my email I see that the tests have failed (so what's new").  > Well, yesterday I had time to look (I don't always) and found that the > builds are OK up until a run of a build of a newspeak VM, and the VM > build is OK, it is the Newspeak bootstrap that fals, and since > Newspeak (I think I'm right in thinking) is no longer maintained, this > is not a surprise. > > But instead of simply discarding the Newspeak build to get green tests > again, better would be to > a) not chain the builds so that an early failure prevents potentially > successful subsequent builds, i.e. attempt to build everything > b) report to the mailing list a message that specifies which builds > have failed, so the subject line would be something like "Travis CI: M > builds out of N have failed", not the depressing "the build has failed" No, 'This specific build has failed' please. Even with a few builds you usually start looking at the wrong end. At least I do. Cheers, Herbert > > I'm bitten by a workflow of quick commits (we are also using Git) > as a QA person in my day job where they also add hasty reviews to > the quick commits. So I kind of cringed when I read Christoph's  > post :-). > > > Still a complex or tedious workflow is also bad for quality and > productivity so that's the point of the post I fully agree with. > > > I'm too far away from Squeak but would it be a possibility to have > small commits in Git (or elsewhere) and consolidated bigger ones > for a proper ancestry? At least we need to be very careful when > changing the community process. > > > We have all the infrastructure we need in Monticello (it supports > branching and merging, and multiple repositories).  We even have > something close to what you suggest in production, with release > repositories that allow people to stay with e.g. squeak53, and we can > push improvements and bug fixes there. But I agree, we want something > like a high-frequency trunk and a low-frequency trunk.  So we could > implement e.g. treatedtrunk, and, say, automate pushing the 3 day, or > 7 day old, trunk to treatedtrunk.  It would be easy for those wanting > a stable trunk process to stay with the treatedtrunk update stream, > and to load from trunk the latest greatest that they really want, > since the delta between treatedtrunk and trunk would be small, and the > delta would be no more than a few days worth of commits. > > Best regards, > > > Herbert > > > > Am 29.09.20 um 01:38 schrieb Thiede, Christoph: >> >> > Maybe for git-based projects, a commit-first, fix-later >> strategy is what that culture likes and that system can support, >> but it's not a good fit for Squeak's trunk. >> >> >> And that's why - sorry for rolling out my old chestnut again - I >> still do believe that a git-based workflow could make us as a >> community more efficient. >> >> I don't question that manual testing is great, and I don't >> question that quality can increase if you give your patches time >> for maturing, but when I compare the resulting workflow to the >> "mainstream" workflow that I can use anywhere on GitHub, I >> repeatedly have the dissatisfying feeling that the inbox/trunk >> workflow is so slow that it ruins all the efficiency from the >> Smalltalkish development workflow (which, however, unquestionably >> outperforms the "mainstream" workflow in a dead, non-live UI >> without first-class objects for code and tools!). >> >> This might apply most significantly to small changes that would >> form a PR of two or three commits in a git project because our >> inbox workflow does not scale so well for changes of such >> extent. I do not know how many hours I already have spent on >> fixing the ancestry of my versions, comparing them to their >> ancestors, or re-uploading them, but it has definitively been too >> many hours ... >> >> >> Did someone ever investigate this question seriously by doing a >> study or so? I would really find the results interesting. >> >> >> Best, >> >> Christoph >> >> >> ------------------------------------------------------------------------ >> *Von:* Squeak-dev >> im Auftrag >> von Chris Muller >> *Gesendet:* Dienstag, 29. September 2020 01:00 Uhr >> *An:* The general-purpose Squeak developers list >> *Betreff:* Re: [squeak-dev] tedious programming-in-the-debugger >> error needs fixing >> On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda >> > wrote: >> >> Hi Christoph, >> >> On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph >> > > wrote: >> >> Hi Eliot, >> >> >> I'm very sorry for this bug, which was so unnecessary >> because my image has still a gigantic working copy ...! >> Tools-ct.988 should fix the issue, I tested it in a fresh >> trunk image. But it would be probably better if you could >> test it yourself, too. ;-) >> >> >> No need to apologise.  It's an easy mistake, and you fixed >> it.  As long as we're all patient with each other and take >> responsibility (Andreas said "if you break it, you fix it") >> we're going to get along fine and be collectively productive. >> >> >> The following is not addressed to Christoph or his commit, but to >> Eliots comment, above:  Patience should begin within our >> development methodology.  The words above are correct and sweet, >> and I agree with them, but I feel the need to caution against the >> implication that "everything's great as long as you fix it >> /afterward/." Maybe for git-based projects, a /commit-first, >> fix-later/ strategy is what that culture likes and that system >> can support, but it's not a good fit for Squeak's trunk. I >> believe Andreas understood this, and he indicated that "breaking >> the trunk is generally frowned upon." >> >> When it comes to code less than 24 hours old, no matter how >> simple it seems, chances are about 80% that a subsequent "oops, >> sorry" commit will need to follow.  With "older," (e.g., even >> only just 48 hours!) _tested_ code, that chance drops >> significantly. Patience.  Restraint.  Please.  :)  Let our >> methodology put time to work /for/ us, by living with our changes >> for a bit (as, it sounds like, Christoph did!) and witness them >> work in context.  Maybe not this time, but /generally, /you'll >> have a gist enough to know whether it should be loaded and tested >> separately in a clean trunk first. >> >> Cheers, >>   Chris >> > > > > -- > _,,,^..^,,,_ > best, Eliot > -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Tue Sep 29 21:17:43 2020 From: asqueaker at gmail.com (Chris Muller) Date: Tue, 29 Sep 2020 16:17:43 -0500 Subject: [squeak-dev] The Inbox: System-mt.1175.mcz In-Reply-To: References: Message-ID: One minor performance tweak would be to employ #ifAbsentAdd: on "someObjects" instead of #includes:, followed by #add:. On Tue, Sep 29, 2020 at 6:21 AM Marcel Taeumel wrote: > ... we may want to add #spaceUsed on Object, too, and refine it for, e.g., > Form to use depth = 2. Those depth values depend on the particular > object/class design. Yet, being able to call "myMorph spaceUsed" or "myForm > spaceUsed" could be a great debugging tool. > > Best, > Marcel > > Am 29.09.2020 13:16:58 schrieb commits at source.squeak.org < > commits at source.squeak.org>: > A new version of System was added to project The Inbox: > http://source.squeak.org/inbox/System-mt.1175.mcz > > ==================== Summary ==================== > > Name: System-mt.1175 > Author: mt > Time: 29 September 2020, 1:16:45.233157 pm > UUID: f0acc060-a48b-2c4f-8b56-aaf2f55601b4 > Ancestors: System-topa.1174 > > Adds a way to compute a space tally for objects up to a certain depth. > > Questions: > - Is the interface (i.e. "depth" and "seen") OK? > - Is the use of IdentitySet OK? > - Can we speed this up somehow? > - Do we need depth-messages for the "class analysis" protocol, too? > > =============== Diff against System-topa.1174 =============== > > Item was added: > + ----- Method: SpaceTally>>spaceForInstance:depth: (in category 'instance > size') ----- > + spaceForInstance: anObject depth: anInteger > + > + ^ self spaceForInstance: anObject depth: anInteger seen: IdentitySet new! > > Item was added: > + ----- Method: SpaceTally>>spaceForInstance:depth:seen: (in category > 'instance size') ----- > + spaceForInstance: anObject depth: anInteger seen: someObjects > + > + | class depth total | > + (someObjects includes: anObject) ifTrue: [^ 0]. > + someObjects add: anObject. > + class := anObject class. > + depth := anInteger - 1. > + total := class isVariable > + ifTrue: [class byteSizeOfInstanceOfSize: anObject basicSize] > + ifFalse: [class isImmediateClass ifTrue: [0] ifFalse: [class > byteSizeOfInstance]]. > + depth >= 0 ifTrue: [ > + anObject isCompiledCode > + ifTrue: [ > + anObject literalsDo: [:literal | > + total := total + (self spaceForInstance: literal depth: depth seen: > someObjects)]] > + ifFalse: [ > + 1 to: anObject basicSize do: [:index | > + total := total + (self spaceForInstance: (anObject basicAt: index) > depth: depth seen: someObjects)]. > + 1 to: class instSize do: [:index | > + total := total + (self spaceForInstance: (anObject instVarAt: index) > depth: depth seen: someObjects)]]]. > + ^ total! > > Item was added: > + ----- Method: SpaceTally>>spaceForInstancesOf:depth: (in category > 'instance size') ----- > + spaceForInstancesOf: aClass depth: aNumber > + "Answer a pair of the number of bytes consumed by all instances of the > given class, including their object headers, and the number of instances. > Follow each instance's fields up to the given depth. Beware of cycles to > shared objects, which will tamper the resulting numbers. > + > + SpaceTally new spaceForInstancesOf: Form depth: 0. --- Same as > #spaceForInstanecsOf: > + SpaceTally new spaceForInstancesOf: Form depth: 1. --- Includes memory > footprint for bits etc. > + SpaceTally new spaceForInstancesOf: Form depth: 2. --- Also includes > LargePositiveIntegers in bitmaps ;-) > + " > + > + | instances total sub depth | > + instances := aClass allInstances. > + instances isEmpty ifTrue: [^#(0 0)]. > + total := 0. > + instances do: [:each | total := total + (self spaceForInstance: each > depth: aNumber)]. > + ^{ total. instances size }! > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From asqueaker at gmail.com Tue Sep 29 21:51:32 2020 From: asqueaker at gmail.com (Chris Muller) Date: Tue, 29 Sep 2020 16:51:32 -0500 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: <2320391A-2585-489B-9539-3714E7098628@gmail.com> References: <2320391A-2585-489B-9539-3714E7098628@gmail.com> Message-ID: On Tue, Sep 29, 2020 at 7:18 AM Eliot Miranda wrote: > Hi Marcel, > > > On Sep 3, 2020, at 6:15 AM, Marcel Taeumel wrote: > >  > Hi -- > > #headingAndAutoselectForLiteral:do: is a private helper function to > disguise a rather constant value, which is a prefix for a tool's label. It > should not be used outside SystemNavigation. That would make things even > worse. > > The basic issue here is that there is no specific object for the concerns > "user" or "sender". So, it is tediously derived or forwarded in a > functional style. > > We should find a way to get rid of #headingAndAutoselectForLiteral:do:. > That very name indicates that there is something wrong with the overall > invocation of SystemNavigation's public protocol. > > > +1. Making a SystemNavigation stateful so that it can be assigned > something for auto select would be great. It could also take a pattern not > just a string, and hence deal with multi keyword messages better. > > If we separate the search and presentation functions there are several > benefits too: > > - we can get a calculus for search, so that we can have and and or. So > something like search for all calls on methods that refer to selector a and > selector b in class hierarchy c are easily expressed as the and of three > elemental searches, fir selector a, for selector b, for class hierarchy c > > - we can get rid of the duplication in the allFoo and browseAllFoo > protocols, keeping the search and just sending browse to the result. > > - we can derive results in the forms we’d like, so a “navigation” could > answer methods, classes, packages, method references, etc > > As a suggested implementation Object>>smalltalk or Object>>navigation > answers a new SystemSearch instance; SystemSearch instances understand > basic queries (allCallsOn:, allImplementorsOf: etc), and an and: and or: > protocol that take either another SystemSearch or a message, and answer or > update the receiver. Then at any time one can send autoSelect: to the > SystemSearch, and browse to see its results. > > BTW, a concise way of creating a message might be cool; Message selector: > #allCallsOn: argument: 123 is fine but a little verbose and tedious to > type. #allCallsOn: << { 123 } might be nice, or a little too cryptic. I > don’t know. > +1, I kinda like that! Although, some objects already implement #<< , hmm... For years, I've actually been wanting a convenience constructor for MethodReference. I _wish_ I could write Integer>>#gcd: and get a MethodReference instead of a CompiledMethod, since I can easily send #compiledMethod to a MethodReference if I want that (which, I never do, I only ever want the Reference). I suppose I could implement CompiledMethod>>#asMethodReference, it'd just be so much nicer if it were the other way... > Btw, Andreas hated and I hate typing systemNavigation; so long. I > implement sn in Object as an abbreviation. Hence my suggestion of > smalltalk or navigator above. search would be even better: > > (self search > allCallsOn: #systemNavigation) “sets systemNavigation as the > default auto select string” > and: #inPackage: << { #Tools }; > browse > > is much more flexible than > self sysyemNavigation browseAllCallsOn: #systemNavigation > loxalToPackage: #Tools > > Or have and: be the default combinatorial so one can cascade ands: > > self search > allCallsOn: #systemNavigation; > inPackage: #Tools; > browse > > I like this last one. > A great improvement! Except #search. We should stay with #systemNavigation. We developers have autoComplete, etc., we shouldn't poach Object>>#search from the API namespace from users. For the ultimate method-selecting power in the universe, MaBehaviorFinder takes the above concept one step further. Instead of always starting with "all" and filtering, it provides both adding AND removing filters. So the above example could be: self systemNavigation addCallsOn: #systemNavigation ; "adding filter" selectPackage: #Tools ; "removing filter" browse Or, equally: self systemNavigation addPackage: #Tools ; "adding filter" selectCallsOn: #systemNavigation "removing filter" browse So, various "add" API, ADDS methods to the result, while various "select" API, removes them from the result. (There's even a third family, "reject" which select the opposite of "select"). There's quite a few methods, but executing them in succession on a stateful instance, as you've described for SystemNavigation here, is exactly how it works, and it's fabulous! :-D Just in case anyone is interested in taking a peek at it for inspiration, the following will pop it up: Installer new merge: #maInstaller. (Smalltalk classNamed: #MaInstaller) new merge: #base. (Smalltalk classNamed: #MaBehaviorFinder) browse - Chris > > > Best, > Marcel > > Am 03.09.2020 15:02:06 schrieb Thiede, Christoph < > christoph.thiede at student.hpi.uni-potsdam.de>: > > Hi Chris, > > > thanks for your feedback! > > > For your code contributions in general, please allow methods to have only > a single exit as much as possible, as in the attached. > > I think this is very much a question of favor - personally, I prefer guard > clauses over the functional style unless both code paths have the same > relevance for the whole method. In my opinion, an empty message list is > clearly a secondary edge case only. :-) > > > I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: > here wasn't obvious to me. > > Yes, this is not really an intuitive message name ... What do you > think about calling it #readLiteral:withHeadingAndStringDo: instead (plus > making it public)? > > Best, > Christoph > ------------------------------ > *Von:* Chris Muller > *Gesendet:* Mittwoch, 2. September 2020 23:59:46 > *An:* squeak dev; Thiede, Christoph > *Betreff:* Re: [squeak-dev] The Inbox: Tools-ct.986.mcz > > Hi Christoph, > > For your code contributions in general, please allow methods to have > only a single exit as much as possible, as in the attached. > > I like the multilingual change, but the purpose of using > #headingAndAutoselectForLiteral:do: here wasn't obvious to me. > > Best, > Chris > > On Wed, Sep 2, 2020 at 9:56 AM wrote: > > > > Christoph Thiede uploaded a new version of Tools to project The Inbox: > > http://source.squeak.org/inbox/Tools-ct.986.mcz > > > > ==================== Summary ==================== > > > > Name: Tools-ct.986 > > Author: ct > > Time: 2 September 2020, 4:55:38.538083 pm > > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca > > Ancestors: Tools-ct.985 > > > > Fixes MNU when adding senders of a non-string literal to a message trace > (at the end, FindText was set to a number or something similar). Improves > multilingual support. > > > > =============== Diff against Tools-ct.985 =============== > > > > Item was changed: > > ----- Method: MessageTrace>>addParentMethodsSending: (in category > 'building') ----- > > addParentMethodsSending: selectorSymbol > > > > + ^ self systemNavigation > > + headingAndAutoselectForLiteral: selectorSymbol > > + do: [:label :autoSelect | > > + | methodsList | > > + methodsList := self systemNavigation allCallsOn: > selectorSymbol. > > + methodsList ifEmpty: [ > > + ^ self inform: ('There are no {1}' > translated format: {label})]. > > + self > > - | methodsList | > > - (methodsList := self systemNavigation allCallsOn: > selectorSymbol) isEmpty > > - ifTrue: > > - [ ^(PopUpMenu labels: ' OK ') > > - startUpWithCaption: 'There are no > methods that send ', selectorSymbol ] > > - ifFalse: > > - [ self > > addParentMessages: methodsList > > + autoSelectString: autoSelect] > > - autoSelectString: selectorSymbol ] > > ! > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From leves at caesar.elte.hu Tue Sep 29 21:55:34 2020 From: leves at caesar.elte.hu (Levente Uzonyi) Date: Tue, 29 Sep 2020 23:55:34 +0200 (CEST) Subject: [squeak-dev] The Inbox: System-mt.1175.mcz In-Reply-To: References: Message-ID: Hi Marcel, On Tue, 29 Sep 2020, commits at source.squeak.org wrote: > A new version of System was added to project The Inbox: > http://source.squeak.org/inbox/System-mt.1175.mcz > > ==================== Summary ==================== > > Name: System-mt.1175 > Author: mt > Time: 29 September 2020, 1:16:45.233157 pm > UUID: f0acc060-a48b-2c4f-8b56-aaf2f55601b4 > Ancestors: System-topa.1174 > > Adds a way to compute a space tally for objects up to a certain depth. > > Questions: > - Is the interface (i.e. "depth" and "seen") OK? Yes, except for the variable names. IMHO they should be called depth and seenObjects instead of anInteger and someObjects, respectively. > - Is the use of IdentitySet OK? Yes, it is, however the set should be shared when you iterate over #allInstances to avoid calculating duplicates. > - Can we speed this up somehow? Yes. 2x speedup is possible[1] for Forms with depth 2. However, the code only works for Objects, so the first non-Object proxy will halt the code. If you want to make it work for all objects, you'll have to use mirror primitives, which will reduce the speedup to about 1.25x. > - Do we need depth-messages for the "class analysis" protocol, too? No idea, never really used SpaceTally :) Levente [1] I wrote some code using various optimizations. If you want me to, I can upload it to the Inbox with or without mirror primitives. > > =============== Diff against System-topa.1174 =============== > > Item was added: > + ----- Method: SpaceTally>>spaceForInstance:depth: (in category 'instance size') ----- > + spaceForInstance: anObject depth: anInteger > + > + ^ self spaceForInstance: anObject depth: anInteger seen: IdentitySet new! > > Item was added: > + ----- Method: SpaceTally>>spaceForInstance:depth:seen: (in category 'instance size') ----- > + spaceForInstance: anObject depth: anInteger seen: someObjects > + > + | class depth total | > + (someObjects includes: anObject) ifTrue: [^ 0]. > + someObjects add: anObject. > + class := anObject class. > + depth := anInteger - 1. > + total := class isVariable > + ifTrue: [class byteSizeOfInstanceOfSize: anObject basicSize] > + ifFalse: [class isImmediateClass ifTrue: [0] ifFalse: [class byteSizeOfInstance]]. > + depth >= 0 ifTrue: [ > + anObject isCompiledCode > + ifTrue: [ > + anObject literalsDo: [:literal | > + total := total + (self spaceForInstance: literal depth: depth seen: someObjects)]] > + ifFalse: [ > + 1 to: anObject basicSize do: [:index | > + total := total + (self spaceForInstance: (anObject basicAt: index) depth: depth seen: someObjects)]. > + 1 to: class instSize do: [:index | > + total := total + (self spaceForInstance: (anObject instVarAt: index) depth: depth seen: someObjects)]]]. > + ^ total! > > Item was added: > + ----- Method: SpaceTally>>spaceForInstancesOf:depth: (in category 'instance size') ----- > + spaceForInstancesOf: aClass depth: aNumber > + "Answer a pair of the number of bytes consumed by all instances of the given class, including their object headers, and the number of instances. Follow each instance's fields up to the given depth. Beware of cycles to shared objects, which will tamper the resulting numbers. > + > + SpaceTally new spaceForInstancesOf: Form depth: 0. --- Same as #spaceForInstanecsOf: > + SpaceTally new spaceForInstancesOf: Form depth: 1. --- Includes memory footprint for bits etc. > + SpaceTally new spaceForInstancesOf: Form depth: 2. --- Also includes LargePositiveIntegers in bitmaps ;-) > + " > + > + | instances total sub depth | > + instances := aClass allInstances. > + instances isEmpty ifTrue: [^#(0 0)]. > + total := 0. > + instances do: [:each | total := total + (self spaceForInstance: each depth: aNumber)]. > + ^{ total. instances size }! From asqueaker at gmail.com Tue Sep 29 22:25:17 2020 From: asqueaker at gmail.com (Chris Muller) Date: Tue, 29 Sep 2020 17:25:17 -0500 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> <6dce22431e5d4e78a36f8f42d65554f7@student.hpi.uni-potsdam.de> Message-ID: I forgot about this fork you did Levente, I should probably switch! BUT, JSON is such crucial functionality these days, what do y'all think about integrating it into the base image? I guess we have some basic functionality on WebUtils... looks pretty compact, is there more to JSON than that? > Email? I don't think so. RSS is probably the closest thing: > https://squeaksource.com/feed.rss > > Actually, squeaksource *does* support email notifications (it's how we're getting trunk commit notifications), but only the admin can add/remove them -- I assume because it was intended for publishing to mailing lists, so we should find a way to improve that. Still, in the meantime, an admin of the project *could* add your email, Christoph. Then, it's just a matter of whether or not squeaksource.com instance of SqueakSource is hooked up to a mail server, or not.. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ma.chris.m at gmail.com Tue Sep 29 22:39:57 2020 From: ma.chris.m at gmail.com (Chris Muller) Date: Tue, 29 Sep 2020 17:39:57 -0500 Subject: [squeak-dev] Development methodology (was: tedious programming-in-the-debugger error needs fixing) In-Reply-To: <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> Message-ID: Hi Christoph, (sorry for so much traffic from me today folks!) > And that's why - sorry for rolling out my old chestnut again - I still > do believe that a git-based workflow could make us as a community more > efficient. > I don't question that manual testing is great, and I don't question that > quality can increase if you give your patches time for maturing, but when I > compare the resulting workflow to the "mainstream" workflow that I can use > anywhere on GitHub, I repeatedly have the dissatisfying feeling that the > inbox/trunk workflow is so slow that it ruins all the efficiency from the > Smalltalkish development workflow (which, however, unquestionably > outperforms the "mainstream" workflow in a dead, non-live UI without > first-class objects for code and tools!). > I hear you, and I do think the video-game-fragathon-free-for-all style ;) might be... *interesting* to try out, IF there would not be permanent consequences from such an experiment in terms of our current Monticello implementation, which keeps the entire, ever-growing database of ancestry in RAM. We currently can't afford to make "proposals" or have "dev discussions" there. (Honestly, it seems like there's a good chance for that to be fixable -- some safe, backward-compatible way to trim the ancestry in memory -- but, TMK no attempts (other than MCInfoProxy) have so far been made at addressing it, but that's another discussion...). > This might apply most significantly to small changes that would form a PR > of two or three commits in a git project because our inbox workflow does > not scale so well for changes of such extent. > An Inbox repository pane docked on a tab in your IDE could go a long way toward simulating the dynamism of such a workflow -- some hot buttons to merge, unmerge, Reparent any versions in the Inbox -- does it matter if it came that way vs. "Update Squeak"?. It could help it feel a lot more integrated. > I do not know how many hours I already have spent on fixing the ancestry > of my versions, comparing them to their ancestors, or re-uploading them, > but it has definitively been too many hours ... > It sounds like you're burning time there friend. ;) All that's necessary is to base off _some_ version in trunk (it can be old). When it's integrated, the integrator will (should!) use the "Reparent" button to sculpt the ancestry appropriately for the trunk (*we need an ancestor selector on the commit dialog!)* And this does touch on one reason I'm skeptical of the active-commit git-style workflow -- the overhead. When I'm spending my time writing version notes, committing and updating, *I'm not writing code*. With everyone doing it, everyone must constantly merging everyone elses' active-committal style, and if they're not really tested (*"eh, fix later!"*) then, at least in a live Smalltalk IDE with dependency on that code to your actual ability to work, -- as you encountered the other day with Tobias' commit -- it becomes worthy to scrutinize the "efficiency" of this methodology to ensure it's real and not only perceived. Making a commit "feels" productive, but the extra efficiency from this methodology can only be realized *when and if* it's actually consumed by someone else *and* it was of good enough quality to boost their development. But, our community is small, and busy. By contrast, when I know its a steady-drip of quality commits, I have no qualms about clicking "Update Squeak". I'm confident it'll be a positive click, and not a sideways or even risky click, because I know it's tested upgrades. If you decide you're interested in addressing some of our IDE's limitations in conjunction with an explicit proposal for tweaking us toward more dynamism, I promise I'll at least be open-minded about it. But, I may have some questions... :/ > Did someone ever investigate this question seriously by doing a study or > so? I would really find the results interesting. > https://en.wikipedia.org/wiki/Capability_Maturity_Model#Maturity_models > > > Best, > > Christoph > > ------------------------------ > *Von:* Squeak-dev im > Auftrag von Chris Muller > *Gesendet:* Dienstag, 29. September 2020 01:00 Uhr > *An:* The general-purpose Squeak developers list > *Betreff:* Re: [squeak-dev] tedious programming-in-the-debugger error > needs fixing > > On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda > wrote: > >> Hi Christoph, >> >> On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph < >> Christoph.Thiede at student.hpi.uni-potsdam.de> wrote: >> >>> Hi Eliot, >>> >>> >>> I'm very sorry for this bug, which was so unnecessary because my image >>> has still a gigantic working copy ...! Tools-ct.988 should fix the >>> issue, I tested it in a fresh trunk image. But it would be probably better >>> if you could test it yourself, too. ;-) >>> >> >> No need to apologise. It's an easy mistake, and you fixed it. As long >> as we're all patient with each other and take responsibility (Andreas said >> "if you break it, you fix it") we're going to get along fine and be >> collectively productive. >> > > The following is not addressed to Christoph or his commit, but to Eliots > comment, above: Patience should begin within our development methodology. > The words above are correct and sweet, and I agree with them, but I feel > the need to caution against the implication that "everything's great as > long as you fix it *afterward*." Maybe for git-based projects, a *commit-first, > fix-later* strategy is what that culture likes and that system can > support, but it's not a good fit for Squeak's trunk. I believe Andreas > understood this, and he indicated that "breaking the trunk is generally > frowned upon." > > When it comes to code less than 24 hours old, no matter how simple it > seems, chances are about 80% that a subsequent "oops, sorry" commit will > need to follow. With "older," (e.g., even only just 48 hours!) *tested* > code, that chance drops significantly. Patience. Restraint. Please. :) > Let our methodology put time to work *for* us, by living with our changes > for a bit (as, it sounds like, Christoph did!) and witness them work in > context. Maybe not this time, but *generally, *you'll have a gist enough > to know whether it should be loaded and tested separately in a clean trunk > first. > > Cheers, > Chris > -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Wed Sep 30 00:22:06 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Tue, 29 Sep 2020 20:22:06 -0400 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <, 20200914154934.GA85344@shell.msen.com> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de> Message-ID: <20200930002206.GA34073@shell.msen.com> On Mon, Sep 28, 2020 at 10:55:45AM +0000, Thiede, Christoph wrote: > Hi all, > > > Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? > > > [cid:3848a1c6-d67f-4999-a714-ffafff2b4a22] > No, I did definitely not do that. I opened a FileList and selected the change sets one at a time, and clicked install for each. Installing the second change set locked the image. After reading your email, I did this: 1) Forwarded your email to my dtlewis290 at gmail.com (spam oriented) account so I could view the graphic attachment, which showed that you are using drag and drop when you load the change sets. 2) Opened a GUI file browser on my Ubuntu laptop, and used drag and drop to copy the two change sets to my image. 3) On dropping the second change set into the image, I selected the "... without updating UI" option. That worked. Dave From eliot.miranda at gmail.com Wed Sep 30 07:09:27 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 30 Sep 2020 00:09:27 -0700 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: References: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> Message-ID: Hi Marcel, On Tue, Sep 29, 2020 at 1:07 AM Marcel Taeumel wrote: > Hi Eliot, hi all! > > I fixed the issue with Tools-mt.989. The logic was already there in > #runUntil. > Can you point me to where in ContextInspector the emphasis for the temp vars is chosen? I want to modify ContextInspector to cache the temp names for a given method because I believe this will speed up stepping a lot. However I couldn't find where the emphasis is applied so I'm worried that my cache may break something. If I can see where that is being done I have a better chance at avoiding breaking things. > Best, > Marcel > > Am 19.09.2020 23:00:33 schrieb Levente Uzonyi : > Hi Christoph, > > On Sat, 19 Sep 2020, Thiede, Christoph wrote: > > > > > Hi Eliot, > > > > > > very nice finding once again! I #timeProfile'd the menu button action > and as I expected, the most expensive operation is the shout styling by the > new inspectors, including the decompilation of every method: > > What was it exactly that you profiled? > > The screenshot shows that 76.9% was spent in > #initializeVariablesFromContext, > of which 52.5% of the time was spent in CompiledMethod(CompiledCode) >> > #getSource. The rest of the tree is not visible, but these methods have > nothing to do with parsing or styling, they initialize the parser and > normally should take <1 ms. > > Also, why is the decompiler producing the source code? > > > > > > > [IMAGE] > > > > > > First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but > probably that's rather a problem with my sources file, see the thread about > this commit). > > You may want to try compiling a VM where FilePlugin's CreateFile does not > set the FILE_FLAG_SEQUENTIAL_SCAN flag, and see if it helps with file > reading performance. > > > Levente > > > > > Second, we do not redraw the world while running to the selection, so we > do not need to update the inspectors at all. I think we could split up > #doStep into some #basicDoStep (which would perform the actual stepping > logic) + > > some #updateContextDuring: (which would update the stack list and the > inspectors), then we would need to trigger the updates only once from > #runToSelection:. > > As an alternative, we could make this method a bit more complex but > responsive by applying the same updating logic from #runUntil. > > > > What do you think? :-) > > > > Best, > > Christoph > > > > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > > > Von: Squeak-dev im Auftrag von Eliot Miranda > > Gesendet: Samstag, 19. September 2020 20:17:12 > > An: The general-purpose Squeak developers list; Taeumel, Marcel > > Betreff: [squeak-dev] Can we make computing the local variables in the > debugger lazy? > > Hi Marcel, > > > > can we try and reduce the frequency at which we compute the > variables in the context inspector in the debugger? It is a noticeable > performance hit. I really like the user interface, but the performance hit > is making > > debugging difficult. > > > > As an example use this: > > > > | samples sineTable sound | > > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > > sineTable := SoundPlayer sineTable: 73. > > sineTable doWithIndex: "And let's not deafen anyone..." > > [:sample :index| sineTable at: index put: sample // 4]. > > samples := SoundBuffer new: 16000. > > 1 to: samples size by: sineTable size do: > > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) > with: sineTable startingAt: 1]. > > 1 to: 146 do: > > [:i| > > samples at: i put: ((samples at: i) * i / 146) asInteger. > > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) > asInteger]. > > sound := SampledSound > > samples: samples > > samplingRate: 16000. > > sound := MixedSound new > > add: sound pan: 0.25; > > add: sound pan: 0.75; > > yourself. > > sound computeSamplesForSeconds: sound duration > > > > > > Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an > image containing Tools-mt.942). Debug the above in the workspace. > Position the cursor at "sound computeSamplesForSeconds: sound duration" and > do "run > > to here". It is essentially instantaneous. > > > > Now do the same in a contemporary trunk image. It takes almost 6 > seconds. > > > > I used to be able to click step as fast as I could and the system would > keep up with me. Now I find that if I click too fast I can accumulate > excess clicks and when I stp clicking the system will continue stepping and > go too > > far. > > > > I don't want to lose the feedback the new variables list gives us. But > I do find the performance hit tedious. I wonder could we cache the list > and only update it > > - when Morphic updates, and > > - when the context changes? > > > > > > _,,,^..^,,,_ > > best, Eliot > > > > > > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 09:27:15 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 09:27:15 +0000 Subject: [squeak-dev] Can we make computing the local variables in the debugger lazy? In-Reply-To: References: <9da5acf36e3b4c77bcf4af677adca40b@student.hpi.uni-potsdam.de> , Message-ID: Hi Eliot, > Can you point me to where in ContextInspector the emphasis for the temp vars is chosen? By convention, inspector fields are defined in the category "fields - streaming". So you are probably searching for ContextInspector >> #streamTemporaryVariablesOn: or ContextVariablesInspector >> #streamTemporaryVariablesOn:? Looking forward to your optimization! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Eliot Miranda Gesendet: Mittwoch, 30. September 2020 09:09:27 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Can we make computing the local variables in the debugger lazy? Hi Marcel, On Tue, Sep 29, 2020 at 1:07 AM Marcel Taeumel > wrote: Hi Eliot, hi all! I fixed the issue with Tools-mt.989. The logic was already there in #runUntil. Can you point me to where in ContextInspector the emphasis for the temp vars is chosen? I want to modify ContextInspector to cache the temp names for a given method because I believe this will speed up stepping a lot. However I couldn't find where the emphasis is applied so I'm worried that my cache may break something. If I can see where that is being done I have a better chance at avoiding breaking things. Best, Marcel Am 19.09.2020 23:00:33 schrieb Levente Uzonyi >: Hi Christoph, On Sat, 19 Sep 2020, Thiede, Christoph wrote: > > Hi Eliot, > > > very nice finding once again! I #timeProfile'd the menu button action and as I expected, the most expensive operation is the shout styling by the new inspectors, including the decompilation of every method: What was it exactly that you profiled? The screenshot shows that 76.9% was spent in #initializeVariablesFromContext, of which 52.5% of the time was spent in CompiledMethod(CompiledCode) >> #getSource. The rest of the tree is not visible, but these methods have nothing to do with parsing or styling, they initialize the parser and normally should take <1 ms. Also, why is the decompiler producing the source code? > > > [IMAGE] > > > First, when loading ShoutCore-ct.78 (Inbox), the speed doubles (but probably that's rather a problem with my sources file, see the thread about this commit). You may want to try compiling a VM where FilePlugin's CreateFile does not set the FILE_FLAG_SEQUENTIAL_SCAN flag, and see if it helps with file reading performance. Levente > > Second, we do not redraw the world while running to the selection, so we do not need to update the inspectors at all. I think we could split up #doStep into some #basicDoStep (which would perform the actual stepping logic) + > some #updateContextDuring: (which would update the stack list and the inspectors), then we would need to trigger the updates only once from #runToSelection:. > As an alternative, we could make this method a bit more complex but responsive by applying the same updating logic from #runUntil. > > What do you think? :-) > > Best, > Christoph > > _________________________________________________________________________________________________________________________________________________________________________________________________________________________________ > Von: Squeak-dev im Auftrag von Eliot Miranda > Gesendet: Samstag, 19. September 2020 20:17:12 > An: The general-purpose Squeak developers list; Taeumel, Marcel > Betreff: [squeak-dev] Can we make computing the local variables in the debugger lazy? > Hi Marcel, > > can we try and reduce the frequency at which we compute the variables in the context inspector in the debugger? It is a noticeable performance hit. I really like the user interface, but the performance hit is making > debugging difficult. > > As an example use this: > > | samples sineTable sound | > "1 second of A below middle C (220Hz). 16000 / 220 is 72.72 recurring" > sineTable := SoundPlayer sineTable: 73. > sineTable doWithIndex: "And let's not deafen anyone..." > [:sample :index| sineTable at: index put: sample // 4]. > samples := SoundBuffer new: 16000. > 1 to: samples size by: sineTable size do: > [:i| samples replaceFrom: i to: (i + sineTable size - 1 min: 16000) with: sineTable startingAt: 1]. > 1 to: 146 do: > [:i| > samples at: i put: ((samples at: i) * i / 146) asInteger. > samples at: 16001 - i put: ((samples at: 16001 - i) * i / 146) asInteger]. > sound := SampledSound > samples: samples > samplingRate: 16000. > sound := MixedSound new > add: sound pan: 0.25; > add: sound pan: 0.75; > yourself. > sound computeSamplesForSeconds: sound duration > > > Open a workspace in e.g. a 64-bit image prior to Tools-mt.965 (I used an image containing Tools-mt.942). Debug the above in the workspace. Position the cursor at "sound computeSamplesForSeconds: sound duration" and do "run > to here". It is essentially instantaneous. > > Now do the same in a contemporary trunk image. It takes almost 6 seconds. > > I used to be able to click step as fast as I could and the system would keep up with me. Now I find that if I click too fast I can accumulate excess clicks and when I stp clicking the system will continue stepping and go too > far. > > I don't want to lose the feedback the new variables list gives us. But I do find the performance hit tedious. I wonder could we cache the list and only update it > - when Morphic updates, and > - when the context changes? > > > _,,,^..^,,,_ > best, Eliot > > -- _,,,^..^,,,_ best, Eliot -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 09:40:05 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 09:40:05 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: <20200930002206.GA34073@shell.msen.com> References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <, 20200914154934.GA85344@shell.msen.com> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de>, <20200930002206.GA34073@shell.msen.com> Message-ID: Hm, probably we should integrate the "filein without UI updates" option into the FileList menu, too ...? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Mittwoch, 30. September 2020 02:22:06 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic On Mon, Sep 28, 2020 at 10:55:45AM +0000, Thiede, Christoph wrote: > Hi all, > > > Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? > > > [cid:3848a1c6-d67f-4999-a714-ffafff2b4a22] > No, I did definitely not do that. I opened a FileList and selected the change sets one at a time, and clicked install for each. Installing the second change set locked the image. After reading your email, I did this: 1) Forwarded your email to my dtlewis290 at gmail.com (spam oriented) account so I could view the graphic attachment, which showed that you are using drag and drop when you load the change sets. 2) Opened a GUI file browser on my Ubuntu laptop, and used drag and drop to copy the two change sets to my image. 3) On dropping the second change set into the image, I selected the "... without updating UI" option. That worked. Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 10:14:45 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 10:14:45 +0000 Subject: [squeak-dev] I'd like to contribute to the JSON project In-Reply-To: References: <52fee8937482410fa26d890e1b6344ac@student.hpi.uni-potsdam.de> <5a5835bb6f874016ac6a173dabdd50e6@student.hpi.uni-potsdam.de> <6dce22431e5d4e78a36f8f42d65554f7@student.hpi.uni-potsdam.de> , Message-ID: <2f9d9bdf46dc4af3bc69a76fd0bdc930@student.hpi.uni-potsdam.de> Hi Levente, Hi Chris, > That's really weird because we even had a small discussion about it in this thread: > http://forum.world.st/I-d-like-to-contribute-to-the-JSON-project-tp5121353p5121357.html Of course, I'm just replying to this thread, but I did not realize before that you did not only optimize but also fix my code :) > Email? I don't think so. RSS is probably the closest thing: > https://squeaksource.com/feed.rss Ah! This looks promising! Can we have an RSS feed for every single repository? I cannot find the JSON repository in the linked feed. > BUT, JSON is such crucial functionality these days, what do y'all think about integrating it into the base image? +1! This would also save you some work when writing Metacello baselines where at the moment, you have to specify the dependency manually ... > I guess we have some basic functionality on WebUtils... looks pretty compact, is there more to JSON than that? Interesting, never heard of that before! JSON has some more features indeed, I guess. For example, it provides JsonObject, a subclass of Dictionary that implements dynamic forwarding to its property. I find this extremely handy when writing or prototyping REST applications because you can use these objects polymorphic with a custom class instance! Also, WebUtils do not support some special JSON constructs such as certain string escapes such as \f or UTF-16 char codes (see the start of this thread). > Still, in the meantime, an admin of the project could add your email, Christoph. Then, it's just a matter of whether or not squeaksource.com instance of SqueakSource is hooked up to a mail server, or not.. No need for special treatment :) It would be great to subscribe to emails for every single project, but how expensive would that be? RSS sounds easier to me (as it's already implemented in some way), there are external services that can email you about RSS news. Where are the sources for it, is it SqueakSource, SqueakSource 2, or SqueakSource 3? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Chris Muller Gesendet: Mittwoch, 30. September 2020 00:25:17 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] I'd like to contribute to the JSON project I forgot about this fork you did Levente, I should probably switch! BUT, JSON is such crucial functionality these days, what do y'all think about integrating it into the base image? I guess we have some basic functionality on WebUtils... looks pretty compact, is there more to JSON than that? Email? I don't think so. RSS is probably the closest thing: https://squeaksource.com/feed.rss Actually, squeaksource does support email notifications (it's how we're getting trunk commit notifications), but only the admin can add/remove them -- I assume because it was intended for publishing to mailing lists, so we should find a way to improve that. Still, in the meantime, an admin of the project could add your email, Christoph. Then, it's just a matter of whether or not squeaksource.com instance of SqueakSource is hooked up to a mail server, or not.. -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 10:21:29 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 10:21:29 +0000 Subject: [squeak-dev] highdpi testing In-Reply-To: <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de>, <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> Message-ID: Hi Tobias, > > For the first step, we could start by asking the user whether they would like to change the font size (#changeFontSize:) when a new scale factor is detected, couldn't we? > > No, that ought to be automatic; Personally, I'd prefer a preference for this. ;-) My Windows system often changes its DPI value multiple times a day and #changeFontSize: has a big impact on your image as it replaces the UI theme (for example, all windows are resized and repositioned, custom theme adjustments are lost, the search bar is emptied, and the history of workspaces is cleared). Also, it's an expensive operation that can slow my system down for ca. 10 seconds per open image. I would only change the font size if I need it just at the moment (I always use to keep open several images even if I do not use all of them frequently). Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Dienstag, 29. September 2020 10:28:01 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] highdpi testing Hi > On 25.09.2020, at 21:43, Thiede, Christoph wrote: > > Hi Tobias, > > sorry I did not answer earlier! This approach looks better for me, however, the wheel is still not absolutely round: > > > (The shadows have a wrong position.) I know, I had a patch that worked in 2016, but things changed and I hand't had the time yet. > > For the first step, we could start by asking the user whether they would like to change the font size (#changeFontSize:) when a new scale factor is detected, couldn't we? No, that ought to be automatic; -t > > PS: @Tom: > > we try drawing things while we load things :) > For the case you did not already read it, I created a changeset that could solve this problem: http://forum.world.st/Changeset-Eliminating-global-state-from-Morphic-tp5121690p5122464.html It also allow me to file in the original DpiAware changeset. > > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Montag, 21. September 2020 15:50:30 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] highdpi testing > > Hi > > > On 19.09.2020, at 19:26, Thiede, Christoph wrote: > > > > Hi, > > > > ah, I had assumed something like this, thanks for fixing, Tom! :-) > > > > #actualScreenScaleFactor is a very helpful tool, but I do not yet see the whole idea of the changeset. > > In what sense is this meant to be a complement of RealEstateAgent >> #scaleFactor or rather an orthogonal concept? > > After loading the changeset and resetting my UI theme, the background image of the world disappeared and all new windows are huge, but they still have a tiny font - am I supposed to set the #scaleFactor manually (to 12, in my example)? > > Where exactly can I see any components in the image that are upscaled by the new changeset? > > > > These do something. > Load in order: > > DpiAware-Kernel > DpiAware-Morpic > DpiAware-Display > > First two are harmless, third will do something. > If nothing changes, do > UserInterfaceTheme cleanUpAndReset. > > Issues ensue. > - Layouting borks. > The assert after the potential relayout > self assert: (self hasProperty: #doLayoutAgain) not > fails already in the docking bar… > - Fonts with UI-Themes do not work. > These are "Deep-referenced" to a pixel size (for Strike fonts) on creation, (so also during UserInterfaceTheme cleanUpAndReset). > However, when the scale factor changes, the pixel-sized fonts don't match the point sizes anymore… > This used to work pre-UI-Themes, as everyone requesting a Font did so by directly asking StrikeFont or TextStyle at the time the > Font was used. Hummm.. > > Hope you enjoy. > -t -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 10:46:06 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 10:46:06 +0000 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: <2320391A-2585-489B-9539-3714E7098628@gmail.com>, Message-ID: <24bd3a820a064166811f6f3eaeb619c7@student.hpi.uni-potsdam.de> Very interesting ideas here! However, I wonder a) how a stateful SystemNavigation can be kept backward compatible with the current version. And b) how does the StatefulSystemNavigation differ from a MessageTrace in terms of class responsibilities? Just by the way, I have been dreaming for some time of a version of MessageTrace that has some filter buttons (Material is calling them "chips") on its top that you can use to filter your messages at every time. I'm aware of the existing "filter message list" menu, but I believe the UI can be improved at this point, and filters cannot be reverted again. Some combinable filter buttons like you may knowit from many modern websites (Google, eBay, ...) might be a nice extension IMHO. Maybe some of all these visions could go into the same tool. Just my 2 cents :-) > I _wish_ I could write > > Integer>>#gcd: > > and get a MethodReference instead of a CompiledMethod, since I can easily send #compiledMethod to a MethodReference if I want that (which, I never do, I only ever want the Reference). I suppose I could implement CompiledMethod>>#asMethodReference, it'd just be so much nicer if it were the other way... There is already CompiledMethod >> #methodReference, but I see your point. :-) However, we should be cautious with what Kent Beck says in his Best Practices about binary convenience messages (that's at least how I understand it): "Represent object allocation as a message to one of the arguments to the Complete Creation Method. Add no more than three of these Constructor Methods per system you develop." While #>>, #>>>, #<< etc. all might be useful shortcuts, they are quite contrary to the idea of natural readability of Smalltalk code because nobody understands them without reading their documentation. It's the same as Python things like "a[-3::-1]" which may be shorter than "(anArray allButLast: 2) reverse" but much more cryptic than the first one on the other hand. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Chris Muller Gesendet: Dienstag, 29. September 2020 23:51:32 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz On Tue, Sep 29, 2020 at 7:18 AM Eliot Miranda > wrote: Hi Marcel, On Sep 3, 2020, at 6:15 AM, Marcel Taeumel > wrote:  Hi -- #headingAndAutoselectForLiteral:do: is a private helper function to disguise a rather constant value, which is a prefix for a tool's label. It should not be used outside SystemNavigation. That would make things even worse. The basic issue here is that there is no specific object for the concerns "user" or "sender". So, it is tediously derived or forwarded in a functional style. We should find a way to get rid of #headingAndAutoselectForLiteral:do:. That very name indicates that there is something wrong with the overall invocation of SystemNavigation's public protocol. +1. Making a SystemNavigation stateful so that it can be assigned something for auto select would be great. It could also take a pattern not just a string, and hence deal with multi keyword messages better. If we separate the search and presentation functions there are several benefits too: - we can get a calculus for search, so that we can have and and or. So something like search for all calls on methods that refer to selector a and selector b in class hierarchy c are easily expressed as the and of three elemental searches, fir selector a, for selector b, for class hierarchy c - we can get rid of the duplication in the allFoo and browseAllFoo protocols, keeping the search and just sending browse to the result. - we can derive results in the forms we’d like, so a “navigation” could answer methods, classes, packages, method references, etc As a suggested implementation Object>>smalltalk or Object>>navigation answers a new SystemSearch instance; SystemSearch instances understand basic queries (allCallsOn:, allImplementorsOf: etc), and an and: and or: protocol that take either another SystemSearch or a message, and answer or update the receiver. Then at any time one can send autoSelect: to the SystemSearch, and browse to see its results. BTW, a concise way of creating a message might be cool; Message selector: #allCallsOn: argument: 123 is fine but a little verbose and tedious to type. #allCallsOn: << { 123 } might be nice, or a little too cryptic. I don’t know. +1, I kinda like that! Although, some objects already implement #<< , hmm... For years, I've actually been wanting a convenience constructor for MethodReference. I _wish_ I could write Integer>>#gcd: and get a MethodReference instead of a CompiledMethod, since I can easily send #compiledMethod to a MethodReference if I want that (which, I never do, I only ever want the Reference). I suppose I could implement CompiledMethod>>#asMethodReference, it'd just be so much nicer if it were the other way... Btw, Andreas hated and I hate typing systemNavigation; so long. I implement sn in Object as an abbreviation. Hence my suggestion of smalltalk or navigator above. search would be even better: (self search allCallsOn: #systemNavigation) “sets systemNavigation as the default auto select string” and: #inPackage: << { #Tools }; browse is much more flexible than self sysyemNavigation browseAllCallsOn: #systemNavigation loxalToPackage: #Tools Or have and: be the default combinatorial so one can cascade ands: self search allCallsOn: #systemNavigation; inPackage: #Tools; browse I like this last one. A great improvement! Except #search. We should stay with #systemNavigation. We developers have autoComplete, etc., we shouldn't poach Object>>#search from the API namespace from users. For the ultimate method-selecting power in the universe, MaBehaviorFinder takes the above concept one step further. Instead of always starting with "all" and filtering, it provides both adding AND removing filters. So the above example could be: self systemNavigation addCallsOn: #systemNavigation ; "adding filter" selectPackage: #Tools ; "removing filter" browse Or, equally: self systemNavigation addPackage: #Tools ; "adding filter" selectCallsOn: #systemNavigation "removing filter" browse So, various "add" API, ADDS methods to the result, while various "select" API, removes them from the result. (There's even a third family, "reject" which select the opposite of "select"). There's quite a few methods, but executing them in succession on a stateful instance, as you've described for SystemNavigation here, is exactly how it works, and it's fabulous! :-D Just in case anyone is interested in taking a peek at it for inspiration, the following will pop it up: Installer new merge: #maInstaller. (Smalltalk classNamed: #MaInstaller) new merge: #base. (Smalltalk classNamed: #MaBehaviorFinder) browse - Chris Best, Marcel Am 03.09.2020 15:02:06 schrieb Thiede, Christoph >: Hi Chris, thanks for your feedback! > For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I think this is very much a question of favor - personally, I prefer guard clauses over the functional style unless both code paths have the same relevance for the whole method. In my opinion, an empty message list is clearly a secondary edge case only. :-) > I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Yes, this is not really an intuitive message name ... What do you think about calling it #readLiteral:withHeadingAndStringDo: instead (plus making it public)? Best, Christoph ________________________________ Von: Chris Muller > Gesendet: Mittwoch, 2. September 2020 23:59:46 An: squeak dev; Thiede, Christoph Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz Hi Christoph, For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. Best, Chris On Wed, Sep 2, 2020 at 9:56 AM > wrote: > > Christoph Thiede uploaded a new version of Tools to project The Inbox: > http://source.squeak.org/inbox/Tools-ct.986.mcz > > ==================== Summary ==================== > > Name: Tools-ct.986 > Author: ct > Time: 2 September 2020, 4:55:38.538083 pm > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca > Ancestors: Tools-ct.985 > > Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. > > =============== Diff against Tools-ct.985 =============== > > Item was changed: > ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- > addParentMethodsSending: selectorSymbol > > + ^ self systemNavigation > + headingAndAutoselectForLiteral: selectorSymbol > + do: [:label :autoSelect | > + | methodsList | > + methodsList := self systemNavigation allCallsOn: selectorSymbol. > + methodsList ifEmpty: [ > + ^ self inform: ('There are no {1}' translated format: {label})]. > + self > - | methodsList | > - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty > - ifTrue: > - [ ^(PopUpMenu labels: ' OK ') > - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] > - ifFalse: > - [ self > addParentMessages: methodsList > + autoSelectString: autoSelect] > - autoSelectString: selectorSymbol ] > ! > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 10:48:01 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 10:48:01 +0000 Subject: [squeak-dev] Why is ModificationForbidden not an Error? In-Reply-To: References: <93CBEDF7-D881-4B30-9142-004918314878@rowledge.org> <1600938552895-0.post@n4.nabble.com>, Message-ID: <7cdc2ad647ff4d67bc0cc0aec6a184ea@student.hpi.uni-potsdam.de> Alright, anyone having any new opinion about this? Let's make a decision! :-) Best, Christoph ________________________________ Von: Chris Muller Gesendet: Donnerstag, 24. September 2020 21:33:27 An: The general-purpose Squeak developers list; Thiede, Christoph Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? Hi Christoph, Magma does not depend on ModificationForbidden. My participation in this discussion was merely for my own learning, to try to overcome my skepticism about ModificationForbidden. Changing its superclass shouldn't affect Magma at all. Whether Magma will be able to make use of it is still up in the air. Please feel free to integrate what you and the other folks interested in this decide is best. Thanks! Chris On Thu, Sep 24, 2020 at 4:09 AM Christoph Thiede > wrote: Hi Chris, hi all, after months of stagnation, I'd like to bring up this open discussion again. IMO the current situation in the Trunk is still buggy and the last thing we want developers to have to care about. For example, I recently crashed a number of smalltalkCI builds again because some method raised a ModificationForbidden, which the test runner, obviously, was not prepared for ... >From what I can see, the only problem with my proposal that resists still in the inbox, Kernel-ct.1321, is related to Chris' Magma implementation. I want to respect this use case (and actually, learning a bit more about Magma is already on my long, long list of nice-to-do stuff), but I have to agree with other people saying that ModificationForbidden is a very low-level error and should not need any special treatment. For this reason, I also think that Marcel's changeset is a bit over-engineered for the Trunk, given the particular fact that we do not have plans for any user of ManagedObjects in the Trunk. Chris, did anything happen on your end regarding the handling of ModificationForbidden in Magma? Iirc you mentioned recently that you have reached some kind of feature completeness in your project. Can't we just merge Kernel-ct.1321 for now and resolve this discussion until some new use case arises? For Magma as an external project (still without being familiar at all with its design), I don't understand what would be wrong with handling such exceptions using #on:do:, this would totally suffice to circumvent the #defaultAction implementation. If you do not have the possibility to install an exception handler or a ToolSet, you could still place an extension override method in ModificationForbidden and (conditionally) inject your custom handling logic there. What do you think? I would like to finally get ahead with this issue! :-) Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html -------------- next part -------------- An HTML attachment was scrubbed... URL: From marcel.taeumel at hpi.de Wed Sep 30 11:21:29 2020 From: marcel.taeumel at hpi.de (Marcel Taeumel) Date: Wed, 30 Sep 2020 13:21:29 +0200 Subject: [squeak-dev] highdpi testing In-Reply-To: References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> <,> <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> Message-ID: > Personally, I'd prefer a preference for this. You would only need a preference or confirmation dialog if we fail to implement it correctly. ;-) The goal would be that your content does not get messed up when detecting a DPI change. Best, Marcel Am 30.09.2020 12:21:37 schrieb Thiede, Christoph : Hi Tobias, > > For the first step, we could start by asking the user whether they would like to change the font size (#changeFontSize:) when a new scale factor is detected, couldn't we? >  > No, that ought to be automatic; Personally, I'd prefer a preference for this. ;-) My Windows system often changes its DPI value multiple times a day and #changeFontSize: has a big impact on your image as it replaces the UI theme (for example, all windows are resized and repositioned, custom theme adjustments are lost, the search bar [http://forum.world.st/BUG-Updating-TheWorldMainDockingBar-leaves-dangling-results-widgets-td5110474.html] is emptied, and the history of workspaces is cleared). Also, it's an expensive operation that can slow my system down for ca. 10 seconds per open image. I would only change the font size if I need it just at the moment (I always use to keep open several images even if I do not use all of them frequently). Best, Christoph Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Dienstag, 29. September 2020 10:28:01 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] highdpi testing   Hi > On 25.09.2020, at 21:43, Thiede, Christoph wrote: > > Hi Tobias, > > sorry I did not answer earlier! This approach looks better for me, however, the wheel is still not absolutely round: > > > (The shadows have a wrong position.) I know, I had a patch that worked in 2016, but things changed and I hand't had the time yet. > > For the first step, we could start by asking the user whether they would like to change the font size (#changeFontSize:) when a new scale factor is detected, couldn't we? No, that ought to be automatic; -t > > PS: @Tom: > > we try drawing things while we load things :) > For the case you did not already read it, I created a changeset that could solve this problem: http://forum.world.st/Changeset-Eliminating-global-state-from-Morphic-tp5121690p5122464.html [http://forum.world.st/Changeset-Eliminating-global-state-from-Morphic-tp5121690p5122464.html] It also allow me to file in the original DpiAware changeset. > > Best, > Christoph > Von: Squeak-dev im Auftrag von Tobias Pape > Gesendet: Montag, 21. September 2020 15:50:30 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] highdpi testing >  > Hi > > > On 19.09.2020, at 19:26, Thiede, Christoph wrote: > > > > Hi, > > > > ah, I had assumed something like this, thanks for fixing, Tom! :-) > > > > #actualScreenScaleFactor is a very helpful tool, but I do not yet see the whole idea of the changeset. > > In what sense is this meant to be a complement of RealEstateAgent >> #scaleFactor or rather an orthogonal concept? > > After loading the changeset and resetting my UI theme, the background image of the world disappeared and all new windows are huge, but they still have a tiny font - am I supposed to set the #scaleFactor manually (to 12, in my example)? > > Where exactly can I see any components in the image that are upscaled by the new changeset? > > > > These do something. > Load in order: > > DpiAware-Kernel > DpiAware-Morpic > DpiAware-Display > > First two are harmless, third will do something. > If nothing changes, do >         UserInterfaceTheme cleanUpAndReset. > > Issues ensue. > - Layouting borks. >         The assert after the potential relayout >                 self assert: (self hasProperty: #doLayoutAgain) not >         fails already in the docking bar… > - Fonts with UI-Themes do not work. >   These are "Deep-referenced" to a pixel size (for Strike fonts) on creation, (so also during UserInterfaceTheme cleanUpAndReset). >   However, when the scale factor changes, the pixel-sized fonts don't match the point sizes anymore… >     This used to work pre-UI-Themes, as everyone requesting a Font did so by directly asking StrikeFont or TextStyle at the time the >   Font was used. Hummm.. > > Hope you enjoy. > -t -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Wed Sep 30 12:24:08 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 30 Sep 2020 05:24:08 -0700 Subject: [squeak-dev] The Inbox: Tools-ct.986.mcz In-Reply-To: References: Message-ID: <8CFEB74E-733B-4EC0-9C61-AA301D8F5E31@gmail.com> Hi Chris, > On Sep 29, 2020, at 2:52 PM, Chris Muller wrote: > >> On Tue, Sep 29, 2020 at 7:18 AM Eliot Miranda wrote: >> Hi Marcel, >> >>>> On Sep 3, 2020, at 6:15 AM, Marcel Taeumel wrote: >>>> >>>  >>> Hi -- >>> >>> #headingAndAutoselectForLiteral:do: is a private helper function to disguise a rather constant value, which is a prefix for a tool's label. It should not be used outside SystemNavigation. That would make things even worse. >>> >>> The basic issue here is that there is no specific object for the concerns "user" or "sender". So, it is tediously derived or forwarded in a functional style. >>> >>> We should find a way to get rid of #headingAndAutoselectForLiteral:do:. That very name indicates that there is something wrong with the overall invocation of SystemNavigation's public protocol. >> >> +1. Making a SystemNavigation stateful so that it can be assigned something for auto select would be great. It could also take a pattern not just a string, and hence deal with multi keyword messages better. >> >> If we separate the search and presentation functions there are several benefits too: >> >> - we can get a calculus for search, so that we can have and and or. So something like search for all calls on methods that refer to selector a and selector b in class hierarchy c are easily expressed as the and of three elemental searches, fir selector a, for selector b, for class hierarchy c >> >> - we can get rid of the duplication in the allFoo and browseAllFoo protocols, keeping the search and just sending browse to the result. >> >> - we can derive results in the forms we’d like, so a “navigation” could answer methods, classes, packages, method references, etc >> >> As a suggested implementation Object>>smalltalk or Object>>navigation answers a new SystemSearch instance; SystemSearch instances understand basic queries (allCallsOn:, allImplementorsOf: etc), and an and: and or: protocol that take either another SystemSearch or a message, and answer or update the receiver. Then at any time one can send autoSelect: to the SystemSearch, and browse to see its results. >> >> BTW, a concise way of creating a message might be cool; Message selector: #allCallsOn: argument: 123 is fine but a little verbose and tedious to type. #allCallsOn: << { 123 } might be nice, or a little too cryptic. I don’t know. > > +1, I kinda like that! Although, some objects already implement #<< , hmm... > > For years, I've actually been wanting a convenience constructor for MethodReference. I _wish_ I could write > > Integer>>#gcd: > > and get a MethodReference instead of a CompiledMethod, since I can easily send #compiledMethod to a MethodReference if I want that (which, I never do, I only ever want the Reference). I suppose I could implement CompiledMethod>>#asMethodReference, it'd just be so much nicer if it were the other way... Remember we have unlimited binary selectors now, so I would support you in using either #>>> or #>>>> as a short-hand for deriving a MethodReference. However, I do think we need to produce a proper suite of references here. We need ClassReference and BindingReference as well as MethodReference. I’ve implemented these when implementing the current closure system a decade ago, wanting to compare similar classes. What I actually did was hack a diff tool that was a variation on a method list browser that functioned a bit like a change list. There were MethodReference, ClassReference and TextReference, where TextReference was just a reference whose source was a string/text wrapped by the instance, which could act like either a MethodReference, ClassReference. The tool MethodDiffTool took two parallel sequences of references and diffed then. So one could diff two class or method definitions in the system, or diff a class and methods in the system against a source file/package parsed into TextReference. For completeness we need FooReference for all system objects, so we need them for classes, methods, bindings, and environments. So go ahead and add >>> or >>>> if that works, but please someone, if possible, round out the reference system so that we have some composable tools, not just browsers. Being able to diff generically, which is what a reference system allows, is super important. Right now we have specific tools fir comparing different versions of a method (but not a class nor a binding), and a tool for comparing different versions of a package (& your neat versions extension to the browser), but both of these could be created out of a generic diff tool and suitable reference system, but such an implementation would more easily be extensible to comparing two images, comparing two sources files, etc. And a system with references for all the semantic categories, methods, classes, bindings and environments, would allow search for references and definitions (general terms for senders and implementors), something I need frequently. >> Btw, Andreas hated and I hate typing systemNavigation; so long. I implement sn in Object as an abbreviation. Hence my suggestion of smalltalk or navigator above. search would be even better: >> >> (self search >> allCallsOn: #systemNavigation) “sets systemNavigation as the default auto select string” >> and: #inPackage: << { #Tools }; >> browse >> >> is much more flexible than >> self sysyemNavigation browseAllCallsOn: #systemNavigation loxalToPackage: #Tools >> >> Or have and: be the default combinatorial so one can cascade ands: >> >> self search >> allCallsOn: #systemNavigation; >> inPackage: #Tools; >> browse >> >> I like this last one. > > A great improvement! Except #search. We should stay with #systemNavigation. We developers have autoComplete, etc., we shouldn't poach Object>>#search from the API namespace from users. > > For the ultimate method-selecting power in the universe, MaBehaviorFinder takes the above concept one step further. Instead of always starting with "all" and filtering, it provides both adding AND removing filters. So the above example could be: > > self systemNavigation > addCallsOn: #systemNavigation ; "adding filter" > selectPackage: #Tools ; "removing filter" > browse > > Or, equally: > > self systemNavigation > addPackage: #Tools ; "adding filter" > selectCallsOn: #systemNavigation "removing filter" > browse > > So, various "add" API, ADDS methods to the result, while various "select" API, removes them from the result. (There's even a third family, "reject" which select the opposite of "select"). Cool (very) but systemNavigation is tooooooo loooooooong. And unless you show me an application which implements the selector search high up in the class hierarchy I accuse you of the tail wagging the fog turfing me to type s y s t e m N a v I g a t I o n over and over and over again (I got a spam call yesterday afternoon: Hello.. Hello, how are you today sir? I’m fine, who is this? I’m so and so from such and such Ok, and...? I want to ask you one question. Fire away Do you have arthritis? No (hangs up) > > There's quite a few methods, but executing them in succession on a stateful instance, as you've described for SystemNavigation here, is exactly how it works, and it's fabulous! :-D Cool. > > Just in case anyone is interested in taking a peek at it for inspiration, the following will pop it up: > > Installer new merge: #maInstaller. > (Smalltalk classNamed: #MaInstaller) new merge: #base. > (Smalltalk classNamed: #MaBehaviorFinder) browse > > - Chris > >> >>> >>> Best, >>> Marcel >>> >>>> Am 03.09.2020 15:02:06 schrieb Thiede, Christoph : >>>> >>>> Hi Chris, >>>> >>>> >>>> >>>> thanks for your feedback! >>>> >>>> >>>> > For your code contributions in general, please allow methods to have only a single exit as much as possible, as in the attached. >>>> >>>> I think this is very much a question of favor - personally, I prefer guard clauses over the functional style unless both code paths have the same relevance for the whole method. In my opinion, an empty message list is clearly a secondary edge case only. :-) >>>> >>>> > I like the multilingual change, but the purpose of using #headingAndAutoselectForLiteral:do: here wasn't obvious to me. >>>> >>>> Yes, this is not really an intuitive message name ... What do you think about calling it #readLiteral:withHeadingAndStringDo: instead (plus making it public)? >>>> >>>> Best, >>>> Christoph >>>> Von: Chris Muller >>>> Gesendet: Mittwoch, 2. September 2020 23:59:46 >>>> An: squeak dev; Thiede, Christoph >>>> Betreff: Re: [squeak-dev] The Inbox: Tools-ct.986.mcz >>>> >>>> Hi Christoph, >>>> >>>> For your code contributions in general, please allow methods to have >>>> only a single exit as much as possible, as in the attached. >>>> >>>> I like the multilingual change, but the purpose of using >>>> #headingAndAutoselectForLiteral:do: here wasn't obvious to me. >>>> >>>> Best, >>>> Chris >>>> >>>> On Wed, Sep 2, 2020 at 9:56 AM wrote: >>>> > >>>> > Christoph Thiede uploaded a new version of Tools to project The Inbox: >>>> > http://source.squeak.org/inbox/Tools-ct.986.mcz >>>> > >>>> > ==================== Summary ==================== >>>> > >>>> > Name: Tools-ct.986 >>>> > Author: ct >>>> > Time: 2 September 2020, 4:55:38.538083 pm >>>> > UUID: b4cdf611-f04b-0b40-8f61-34429f414cca >>>> > Ancestors: Tools-ct.985 >>>> > >>>> > Fixes MNU when adding senders of a non-string literal to a message trace (at the end, FindText was set to a number or something similar). Improves multilingual support. >>>> > >>>> > =============== Diff against Tools-ct.985 =============== >>>> > >>>> > Item was changed: >>>> > ----- Method: MessageTrace>>addParentMethodsSending: (in category 'building') ----- >>>> > addParentMethodsSending: selectorSymbol >>>> > >>>> > + ^ self systemNavigation >>>> > + headingAndAutoselectForLiteral: selectorSymbol >>>> > + do: [:label :autoSelect | >>>> > + | methodsList | >>>> > + methodsList := self systemNavigation allCallsOn: selectorSymbol. >>>> > + methodsList ifEmpty: [ >>>> > + ^ self inform: ('There are no {1}' translated format: {label})]. >>>> > + self >>>> > - | methodsList | >>>> > - (methodsList := self systemNavigation allCallsOn: selectorSymbol) isEmpty >>>> > - ifTrue: >>>> > - [ ^(PopUpMenu labels: ' OK ') >>>> > - startUpWithCaption: 'There are no methods that send ', selectorSymbol ] >>>> > - ifFalse: >>>> > - [ self >>>> > addParentMessages: methodsList >>>> > + autoSelectString: autoSelect] >>>> > - autoSelectString: selectorSymbol ] >>>> > ! >>>> > >>>> > >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Wed Sep 30 12:27:01 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 30 Sep 2020 05:27:01 -0700 Subject: [squeak-dev] Development methodology In-Reply-To: <2f578a11-c8ee-16f7-8690-447a8b88d9f4@gmx.net> References: <2f578a11-c8ee-16f7-8690-447a8b88d9f4@gmx.net> Message-ID: <75E0802B-EE33-4A73-AF4B-E911C8EA80F1@gmail.com> Hi Herbert, will toppist did brevity. I agree with both of your responses below. _,,,^..^,,,_ (phone) > On Sep 29, 2020, at 1:58 PM, Herbert König wrote: > >  Hi Eliot, > > ...snip... >> >> *BUT* (and it's a big BUT) only if the test signals can be seen. > ...snip... > >> So lumping lots of tests together so that the one persistently failing test marks the whole suite as having failed isn't helpful. For example, if our SUnit tests were grouped by package then we could see much sooner when a commit of a package broke something. Right now, with everything lumped together the fact that we have some failing tests (an inevitability in complex projects with lots going on, and long term issues that need long term actions to solve) obscures the test results, and means we're not able to use them as we should. If we broke out the test results in sub groups, and didn't report the overall signal, but emphasised the deltas between successive runs of individual groups, we would see when things break. > Fully agree. I never know what to make of the report that goes to the list. An I would be frustrated searching the problem with that little information. > > What if Squeak's TestRunner iterates over the packages and on each failure writes a text file with the packages's name. Jenkins appends the directory list of these files to the mail going to Squeak dev. > > Sorry I know nothing about Squeak's build system but at my employer ASCII files are what binds the system tests together. Audio processing, GUI, hardware, different cultures, different programming languages, incompatible tools, but we get one detailed report where we can see the single test that failed. > >> >> Right now the CI for VM builds aggregates in exactly this way so when I look in my email I see that the tests have failed (so what's new"). Well, yesterday I had time to look (I don't always) and found that the builds are OK up until a run of a build of a newspeak VM, and the VM build is OK, it is the Newspeak bootstrap that fals, and since Newspeak (I think I'm right in thinking) is no longer maintained, this is not a surprise. >> >> But instead of simply discarding the Newspeak build to get green tests again, better would be to >> a) not chain the builds so that an early failure prevents potentially successful subsequent builds, i.e. attempt to build everything >> b) report to the mailing list a message that specifies which builds have failed, so the subject line would be something like "Travis CI: M builds out of N have failed", not the depressing "the build has failed" > > No, 'This specific build has failed' please. Even with a few builds you usually start looking at the wrong end. At least I do. > > Cheers, > > Herbert > >> >>> I'm bitten by a workflow of quick commits (we are also using Git) as a QA person in my day job where they also add hasty reviews to the quick commits. So I kind of cringed when I read Christoph's post :-). >>> >>> >>> >>> Still a complex or tedious workflow is also bad for quality and productivity so that's the point of the post I fully agree with. >>> >>> >>> >>> I'm too far away from Squeak but would it be a possibility to have small commits in Git (or elsewhere) and consolidated bigger ones for a proper ancestry? At least we need to be very careful when changing the community process. >>> >> >> We have all the infrastructure we need in Monticello (it supports branching and merging, and multiple repositories). We even have something close to what you suggest in production, with release repositories that allow people to stay with e.g. squeak53, and we can push improvements and bug fixes there. But I agree, we want something like a high-frequency trunk and a low-frequency trunk. So we could implement e.g. treatedtrunk, and, say, automate pushing the 3 day, or 7 day old, trunk to treatedtrunk. It would be easy for those wanting a stable trunk process to stay with the treatedtrunk update stream, and to load from trunk the latest greatest that they really want, since the delta between treatedtrunk and trunk would be small, and the delta would be no more than a few days worth of commits. >>> Best regards, >>> >>> >>> >>> Herbert >>> >>> >>> >>> >>> >>>> Am 29.09.20 um 01:38 schrieb Thiede, Christoph: >>>> > Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk. >>>> >>>> >>>> >>>> And that's why - sorry for rolling out my old chestnut again - I still do believe that a git-based workflow could make us as a community more efficient. >>>> >>>> I don't question that manual testing is great, and I don't question that quality can increase if you give your patches time for maturing, but when I compare the resulting workflow to the "mainstream" workflow that I can use anywhere on GitHub, I repeatedly have the dissatisfying feeling that the inbox/trunk workflow is so slow that it ruins all the efficiency from the Smalltalkish development workflow (which, however, unquestionably outperforms the "mainstream" workflow in a dead, non-live UI without first-class objects for code and tools!). >>>> >>>> This might apply most significantly to small changes that would form a PR of two or three commits in a git project because our inbox workflow does not scale so well for changes of such extent. I do not know how many hours I already have spent on fixing the ancestry of my versions, comparing them to their ancestors, or re-uploading them, but it has definitively been too many hours ... >>>> >>>> >>>> >>>> Did someone ever investigate this question seriously by doing a study or so? I would really find the results interesting. >>>> >>>> >>>> >>>> Best, >>>> >>>> Christoph >>>> >>>> >>>> Von: Squeak-dev im Auftrag von Chris Muller >>>> Gesendet: Dienstag, 29. September 2020 01:00 Uhr >>>> An: The general-purpose Squeak developers list >>>> Betreff: Re: [squeak-dev] tedious programming-in-the-debugger error needs fixing >>>> >>>> On Mon, Sep 28, 2020 at 10:07 AM Eliot Miranda wrote: >>>>> Hi Christoph, >>>>> >>>>> On Fri, Sep 25, 2020 at 7:58 AM Thiede, Christoph wrote: >>>>>> Hi Eliot, >>>>>> >>>>>> >>>>>> >>>>>> I'm very sorry for this bug, which was so unnecessary because my image has still a gigantic working copy ...! Tools-ct.988 should fix the issue, I tested it in a fresh trunk image. But it would be probably better if you could test it yourself, too. ;-) >>>>>> >>>>> >>>>> No need to apologise. It's an easy mistake, and you fixed it. As long as we're all patient with each other and take responsibility (Andreas said "if you break it, you fix it") we're going to get along fine and be collectively productive. >>>> >>>> The following is not addressed to Christoph or his commit, but to Eliots comment, above: Patience should begin within our development methodology. The words above are correct and sweet, and I agree with them, but I feel the need to caution against the implication that "everything's great as long as you fix it afterward." Maybe for git-based projects, a commit-first, fix-later strategy is what that culture likes and that system can support, but it's not a good fit for Squeak's trunk. I believe Andreas understood this, and he indicated that "breaking the trunk is generally frowned upon." >>>> >>>> When it comes to code less than 24 hours old, no matter how simple it seems, chances are about 80% that a subsequent "oops, sorry" commit will need to follow. With "older," (e.g., even only just 48 hours!) tested code, that chance drops significantly. Patience. Restraint. Please. :) Let our methodology put time to work for us, by living with our changes for a bit (as, it sounds like, Christoph did!) and witness them work in context. Maybe not this time, but generally, you'll have a gist enough to know whether it should be loaded and tested separately in a clean trunk first. >>>> >>>> Cheers, >>>> Chris >>>> >>>> >>> >> >> >> -- >> _,,,^..^,,,_ >> best, Eliot >> >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eliot.miranda at gmail.com Wed Sep 30 12:43:57 2020 From: eliot.miranda at gmail.com (Eliot Miranda) Date: Wed, 30 Sep 2020 05:43:57 -0700 Subject: [squeak-dev] Why is ModificationForbidden not an Error? In-Reply-To: <7cdc2ad647ff4d67bc0cc0aec6a184ea@student.hpi.uni-potsdam.de> References: <7cdc2ad647ff4d67bc0cc0aec6a184ea@student.hpi.uni-potsdam.de> Message-ID: <1775378D-EF3B-4876-95FA-960ECD7DE045@gmail.com> > On Sep 30, 2020, at 3:48 AM, Thiede, Christoph wrote: > >  > Alright, anyone having any new opinion about this? Let's make a decision! :-) > I don’t see how it can be other than an error. If one attempts to modify a literal that is an error. If one attempts to modify a read-only object then that is an error, unless some smart system is overloading read-only-ness to implement a read barrier. > Best, > > Christoph > > Von: Chris Muller > Gesendet: Donnerstag, 24. September 2020 21:33:27 > An: The general-purpose Squeak developers list; Thiede, Christoph > Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? > > Hi Christoph, > > Magma does not depend on ModificationForbidden. My participation in this discussion was merely for my own learning, to try to overcome my skepticism about ModificationForbidden. Changing its superclass shouldn't affect Magma at all. Whether Magma will be able to make use of it is still up in the air. Please feel free to integrate what you and the other folks interested in this decide is best. > > Thanks! > Chris > >> On Thu, Sep 24, 2020 at 4:09 AM Christoph Thiede wrote: >> Hi Chris, hi all, >> >> after months of stagnation, I'd like to bring up this open discussion again. >> IMO the current situation in the Trunk is still buggy and the last thing we >> want developers to have to care about. For example, I recently crashed a >> number of smalltalkCI builds again because some method raised a >> ModificationForbidden, which the test runner, obviously, was not prepared >> for ... This makes no sense to me. Test runners collect tests that error just as they collect tests that assert fail. Why would a ModificationForbidden Another be considered an error? >> From what I can see, the only problem with my proposal that resists still in >> the inbox, Kernel-ct.1321, is related to Chris' Magma implementation. I want >> to respect this use case (and actually, learning a bit more about Magma is >> already on my long, long list of nice-to-do stuff), but I have to agree with >> other people saying that ModificationForbidden is a very low-level error and >> should not need any special treatment. For this reason, I also think that >> Marcel's changeset is a bit over-engineered for the Trunk, given the >> particular fact that we do not have plans for any user of ManagedObjects in >> the Trunk. >> >> Chris, did anything happen on your end regarding the handling of >> ModificationForbidden in Magma? Iirc you mentioned recently that you have >> reached some kind of feature completeness in your project. Can't we just >> merge Kernel-ct.1321 for now and resolve this discussion until some new use >> case arises? For Magma as an external project (still without being familiar >> at all with its design), I don't understand what would be wrong with >> handling such exceptions using #on:do:, this would totally suffice to >> circumvent the #defaultAction implementation. If you do not have the >> possibility to install an exception handler or a ToolSet, you could still >> place an extension override method in ModificationForbidden and >> (conditionally) inject your custom handling logic there. >> >> What do you think? I would like to finally get ahead with this issue! :-) I don’t want yo build something that is incompatible with gemstone. Currently we don’t have their attention; Ogaro had thrrr attention. So we have to do the work of ensuring that ModificationForbidden supports the Gemstone use case; it’s a very very important one. on:do: is completely inadequate for this. One needs a system wide exception handler where any ModificationForbidden is routed through a manager that is able to identify that the object in question is in fact being mapped to a database (or a remote system, or a breakout ring system etc). But on:do: handlers are for specific flows of control (currently within a single thread) and cannot serve as system wide handlers. Here’s a simplified use case. The programmer wants to be able to trace wherever in the system an object is modified. It may occur in arbitrary processes, not just in the current flow of control. To do this efficiently the programmer uses ModificationForbidden to catch a modification, log it, make the object readable, effect the modification, make the object read-only again and proceed. This *cannot* be done with an on:do: handler. >> >> Best, >> Christoph >> >> >> >> -- >> Sent from: http://forum.world.st/Squeak-Dev-f45488.html >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From christoph.thiede at student.hpi.uni-potsdam.de Wed Sep 30 14:22:55 2020 From: christoph.thiede at student.hpi.uni-potsdam.de (Christoph Thiede) Date: Wed, 30 Sep 2020 09:22:55 -0500 (CDT) Subject: [squeak-dev] Development methodology (was: tedious programming-in-the-debugger error needs fixing) In-Reply-To: References: <1a26fe2675b14feaa16b483d54afa361@student.hpi.uni-potsdam.de> <1599137423163-0.post@n4.nabble.com> <23476c3e0a68492c8cc917c38ba2c9d4@student.hpi.uni-potsdam.de> Message-ID: <1601475775699-0.post@n4.nabble.com> Hi all, thanks for this insightful discussion! :-) > (sorry for so much traffic from me today folks!) No need to worry, I'm glad about any message by you! I will give a short case study and tell you how I think it probably could be performed more efficiently using the GitHub workflow: 1) I'm proposing a new extension to some Squeak component, let's say a menu for the HelpBrowser. To do this, I upload a new version to the inbox (Package-ct.2 based on Package-xyz.1). 2) Some kind person is giving me some feedback on the proposal so I am reworking it. To share the new version with the community, I have two options: A. Upload a new inbox version (Package-ct.3) that depends on Package-ct.2 or B. upload a new inbox version that contains a copy of all changes of Package-ct.2, too, that is based on Package-xyz.1 again, and that explains that Package-ct.2 can moved into the treated inbox now. Usually, I choose option B because it makes it easier to read the complete diff of my proposal compared to the trunk (but let's see step 3 for this decision). In every case, I need to leave a note referring to Package-ct.2. 3) Some kind person would like to clean up the inbox, including reviewing and eventually merging of my proposal, but where should they find all relevant information? Regarding to Package-ct.2, the Monticello Browser does not give you any hint that it is stale and should be moved into the treated inbox (provided that I chose option B in step 2) because you cannot see the discussion from the mailing list there. The mail-archive thread about Package-ct.2, too, does not contain any reference to Package-ct.3 (unless I'm sending another piece of noise manually). On the other hand, if I chose option 2.A, you cannot review Package-ct.3 standalone neither in the Monticello Browser nor in the mailing thread. And things get more complicated if just another review is left on the proposal before it can be merged ... Of course, as I am not a merging person, I cannot tell whether step 3 exactly describes your workflow as a merger, but I am facing very similar issues when trying to get an overview of my own contribution. And also, we had several small incidents in the past when something was merged that actually should have been improved before (just for example, Regex-Core-ct.56). So how could this look differently when applying the GitHub workflow? 1) For the initial proposal, I'm opening a new Pull Request to merge "helpbrowser-menu" into "trunk" with a single commit commit-a. 2) To react on the kind person's feedback, I add another commit commit-b to this branch. GitHub, the GitBrowser, and every other git client will automatically display the connection between both commits, and because commit-a is no longer a head commit, it will not be considered as a complete feature by anybody any longer. 3) Every subsequent feedback can be left directly to the Pull Request thread (which can be easily synchronized with the mailing list using a bot similar to the OpenSmalltalk-VM-Bot) and be resolved by just another commit to this branch. All version information is kept at one single place. *) As a bonus, GitHub offers a bunch of useful features that make it easier to overview and organize all contributions. For example, we can automatically run the CI for every Pull Request (which I'm sure would help us to introduce a smaller number of bugs into the Trunk/master); issue/PR references are automatically displayed on both ends of a link; people can be assigned to certain PRs; labels can be used to mark a PR as draft, fix, enhancement, review, etc. and much more ... Also, there is already an existing project to integrate GitHub conversations into Squeak, see SqueakIssueIntegration. *) Another plus could be organizing all packages in one repository to get rid of changesets sent to the list which I find truly unhandy as they are not really designed for versioning. > Those occasional hiccups with our inbox don't change the fact that reviews > take time and effort. Having a fancy "auto-merge this pull request because > CI says it is okay" won't make it better. Maybe even worse. I agree that we will need to keep some policies, e.g. "checkout a change and test it before merging" it, but unless this policy is violated, I do not see how the quality should be impaired. But won't you agree that reviewing stuff gets easier if you only have to check one location instead of two or three, and requesting changes does not require to start the next from the beginning again? > Just look at the OpenSmalltalk VM repo: > https://github.com/OpenSmalltalk/opensmalltalk-vm --- Many open issues and > pull requests, some of them are several years old. Hm, given a total of 335 PRs, 12 open PRs is not such a bad number, that is about 3.6% of all contributions. The percentage of open inbox commits 733/(733+12744) is 5.4% which is somewhat worse, and this does not even include all the unmerged changesets from the mailing list ... :-) > When I'm spending my time writing version notes, committing and updating, > I'm not writing code. I cannot follow you at this point. Whether you upload a version to the inbox or create a GH PR, you have to document your changes to some extent either way. The difference is that if you make a follow-up commit to an existing PR, you can be very brief (e.g. "fix a typo") and squash your commits when merging the PR. > With everyone doing it, everyone must constantly merging everyone elses' > active-committal style, and if they're not really tested ("eh, fix > later!") then > Making a commit "feels" productive, but the extra efficiency from this > methodology can only be realized when and if it's actually consumed by > someone else and it was of good enough quality to boost their development. There is no need to lower code quality just because of using a branching concept, is it? CI can help us to *improve* quality but does not force us to merge more often. Just in the opposite, it would be possible to develop against an extra stable branch as proposed by Herbert (unless too many people are touching the same components at the same time). And also, git has a feature called rebase, so there is no need for too many merge commits :-) > we need an ancestor selector on the commit dialog! +1 > But, our community is small That's another good point, I'd love to see more contributions by more people on the Squeak project! But from my point of view, the mailing-list-based workflow is simply a quite conservative approach that might keep many potentially interested casual Squeakers away from participating in our community. I think the following article provides a very good summary of my thought on this matter: "Relying on plain-text email is a 'barrier to entry' for kernel development, says Linux Foundation board member" (https://www.theregister.com/2020/08/25/linux_kernel_email/). Marcel argued that the mailing list works as a filter for keeping unexperienced or unqualified people away from our community, but I believe our community is too small for throwing away all these possible contributions. Best, Christoph -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 15:50:43 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 15:50:43 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <, 20200914154934.GA85344@shell.msen.com> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de>, <20200930002206.GA34073@shell.msen.com>, Message-ID: Here is another version of the changeset that does not even longer raise a debugger when removing the current hand. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Mittwoch, 30. September 2020 11:40:05 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic Hm, probably we should integrate the "filein without UI updates" option into the FileList menu, too ...? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Mittwoch, 30. September 2020 02:22:06 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic On Mon, Sep 28, 2020 at 10:55:45AM +0000, Thiede, Christoph wrote: > Hi all, > > > Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? > > > [cid:3848a1c6-d67f-4999-a714-ffafff2b4a22] > No, I did definitely not do that. I opened a FileList and selected the change sets one at a time, and clicked install for each. Installing the second change set locked the image. After reading your email, I did this: 1) Forwarded your email to my dtlewis290 at gmail.com (spam oriented) account so I could view the graphic attachment, which showed that you are using drag and drop when you load the change sets. 2) Opened a GUI file browser on my Ubuntu laptop, and used drag and drop to copy the two change sets to my image. 3) On dropping the second change set into the image, I selected the "... without updating UI" option. That worked. Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: Hide activeVariables.4.cs URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 16:22:23 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 16:22:23 +0000 Subject: [squeak-dev] Changeset: Eliminating global state from Morphic In-Reply-To: References: <20200912173950.GA35925@shell.msen.com> <18c0e74293834ac6bc4af5be15dad436@student.hpi.uni-potsdam.de> <7B5B2DB1-9A8E-45D1-B566-F4B45A6C9774@rowledge.org> <, 20200914154934.GA85344@shell.msen.com> <316eb771b4674076a884bb4caea31175@student.hpi.uni-potsdam.de>, <20200930002206.GA34073@shell.msen.com>, , Message-ID: This version includes a test method, #testActiveVariablesObsoletion, that makes sure that no one will try to reference one of the deprecated ActiveVariable bindings again in the future. Thanks to Marcel for the tip! Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Mittwoch, 30. September 2020 17:50:43 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic Here is another version of the changeset that does not even longer raise a debugger when removing the current hand. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Mittwoch, 30. September 2020 11:40:05 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic Hm, probably we should integrate the "filein without UI updates" option into the FileList menu, too ...? Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Mittwoch, 30. September 2020 02:22:06 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Changeset: Eliminating global state from Morphic On Mon, Sep 28, 2020 at 10:55:45AM +0000, Thiede, Christoph wrote: > Hi all, > > > Dave, I double-checked it. When loading the second changeset, can you confirm that you used the new option in the drop handler dialog? > > > [cid:3848a1c6-d67f-4999-a714-ffafff2b4a22] > No, I did definitely not do that. I opened a FileList and selected the change sets one at a time, and clicked install for each. Installing the second change set locked the image. After reading your email, I did this: 1) Forwarded your email to my dtlewis290 at gmail.com (spam oriented) account so I could view the graphic attachment, which showed that you are using drag and drop when you load the change sets. 2) Opened a GUI file browser on my Ubuntu laptop, and used drag and drop to copy the two change sets to my image. 3) On dropping the second change set into the image, I selected the "... without updating UI" option. That worked. Dave -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: Hide activeVariables.5.cs URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 17:06:14 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 17:06:14 +0000 Subject: [squeak-dev] Metacello broken again In-Reply-To: <53086722e7f443a79e1aa74757004963@student.hpi.uni-potsdam.de> References: <2c3359358004407498a8d3331c381203@student.hpi.uni-potsdam.de>, , , <53086722e7f443a79e1aa74757004963@student.hpi.uni-potsdam.de> Message-ID: <0b4db772e37c42aa819c1565f6cec34c@student.hpi.uni-potsdam.de> Hi all, I would not reply to this thread if I did not perceive a similar issue just again. Installer ensureRecentMetacello --> Could not resolve: BaselineOfMetacello [BaselineOfMetacello] in C:\path\to\package-cache github://Metacello/metacello:master/repository ERROR: 'GoferRepositoryError: UndefinedObject>>isAlphaNumeric' Are there any server errors again? The SAR works, of course, but if I try to #installGitInfrastructure after loading the SAR and skip #ensureRecentMetacello, I receive another error: MessageNotUnderstood: Array>>setLoadsInMetacelloProject: from MetacelloCypressBaselineProjectSpec(MetacelloProjectSpec)>>loads:. Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Thiede, Christoph Gesendet: Mittwoch, 2. September 2020 23:37:06 An: Squeak Dev Betreff: Re: [squeak-dev] Metacello broken again Thank you Tom :-) ________________________________ Von: Squeak-dev im Auftrag von Beckmann, Tom Gesendet: Dienstag, 1. September 2020 12:32:05 An: Squeak Dev Betreff: Re: [squeak-dev] Metacello broken again ...and now the servers are fixed, too :) http://forum.world.st/VOMongoConnectionError-when-dowloading-mcz-from-smalltalkhub-com-tp5120870p5121140.html ________________________________________ From: Squeak-dev on behalf of Beckmann, Tom Sent: Tuesday, September 1, 2020 12:23:39 PM To: Squeak Dev Subject: Re: [squeak-dev] Metacello broken again Hi Christoph, the smalltalkhub servers recently had some reconfigurations that are potentially the culprit [1] (just heard back, they are already on it :)). I attached a .sar for bootstrapping based on this issue's [2] suggestion for a new bootstrapping process that should allow you to get a working metacello in the meantime. Best, Tom [1] http://forum.world.st/VOMongoConnectionError-when-dowloading-mcz-from-smalltalkhub-com-tp5120870p5121135.html [2] https://github.com/Metacello/metacello/issues/524 ________________________________________ From: Squeak-dev on behalf of Thiede, Christoph Sent: Tuesday, September 1, 2020 11:56:10 AM To: Squeak Dev Subject: [squeak-dev] Metacello broken again Hi all, I did not follow the recent Metacello discussions again, but just now in my fresh Trunk image, Metacello new failed again multiple times saying 'retry with alternate repository failed: ''MessageNotUnderstood: UndefinedObject>>contentStreamFromEncoding:'''. Is this a known problem? How can I solve this without waiting for any server to be online again? After this command failed, you cannot even retry it, because #Metacello is no longer a registered class in the image. This is rather inconvenient ... Best, Christoph -------------- next part -------------- An HTML attachment was scrubbed... URL: From tim at rowledge.org Wed Sep 30 17:20:45 2020 From: tim at rowledge.org (tim Rowledge) Date: Wed, 30 Sep 2020 10:20:45 -0700 Subject: [squeak-dev] highdpi testing In-Reply-To: References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> Message-ID: <3EF3A6EF-2F5D-4E04-A53C-4D8E53740B5C@rowledge.org> A strange and possibly relevant though crossed my mind as I read that email - what happens with windows that cross physical display boundaries where the displays have different dpi values? tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Useful random insult:- His spirit guide is a three-toed sloth. From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 17:24:12 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 17:24:12 +0000 Subject: [squeak-dev] highdpi testing In-Reply-To: <3EF3A6EF-2F5D-4E04-A53C-4D8E53740B5C@rowledge.org> References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> , <3EF3A6EF-2F5D-4E04-A53C-4D8E53740B5C@rowledge.org> Message-ID: On Windows, it appears to be a democratic thing - the majority of pixels wins in the question of DPI value. But I'm not absolutely sure about this and cannot test this right now. I don't believe it would be a good idea to override this behavior, even if we could ... Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von tim Rowledge Gesendet: Mittwoch, 30. September 2020 19:20:45 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] highdpi testing A strange and possibly relevant though crossed my mind as I read that email - what happens with windows that cross physical display boundaries where the displays have different dpi values? tim -- tim Rowledge; tim at rowledge.org; http://www.rowledge.org/tim Useful random insult:- His spirit guide is a three-toed sloth. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lewis at mail.msen.com Wed Sep 30 17:26:30 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 30 Sep 2020 13:26:30 -0400 Subject: [squeak-dev] Why is ModificationForbidden not an Error? In-Reply-To: <1775378D-EF3B-4876-95FA-960ECD7DE045@gmail.com> References: <7cdc2ad647ff4d67bc0cc0aec6a184ea@student.hpi.uni-potsdam.de> <1775378D-EF3B-4876-95FA-960ECD7DE045@gmail.com> Message-ID: <20200930172630.GA50082@shell.msen.com> I am not involved in this discussion, but if I understand the conversation there is no longer any disagreement about what needs to be done, so we just need to make it happen in trunk. I think there was an inbox submission a while back that could be merged. +1000 for bringing this issue to a resolution, and thanks Christoph for moving it forward. Dave On Wed, Sep 30, 2020 at 05:43:57AM -0700, Eliot Miranda wrote: > > > > On Sep 30, 2020, at 3:48 AM, Thiede, Christoph wrote: > > > > ??? > > Alright, anyone having any new opinion about this? Let's make a decision! :-) > > > > > I don???t see how it can be other than an error. If one attempts to modify a literal that is an error. If one attempts to modify a read-only object then that is an error, unless some smart system is overloading read-only-ness to implement a read barrier. > > > Best, > > > > Christoph > > > > Von: Chris Muller > > Gesendet: Donnerstag, 24. September 2020 21:33:27 > > An: The general-purpose Squeak developers list; Thiede, Christoph > > Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? > > > > Hi Christoph, > > > > Magma does not depend on ModificationForbidden. My participation in this discussion was merely for my own learning, to try to overcome my skepticism about ModificationForbidden. Changing its superclass shouldn't affect Magma at all. Whether Magma will be able to make use of it is still up in the air. Please feel free to integrate what you and the other folks interested in this decide is best. > > > > Thanks! > > Chris > > > >> On Thu, Sep 24, 2020 at 4:09 AM Christoph Thiede wrote: > >> Hi Chris, hi all, > >> > >> after months of stagnation, I'd like to bring up this open discussion again. > >> IMO the current situation in the Trunk is still buggy and the last thing we > >> want developers to have to care about. For example, I recently crashed a > >> number of smalltalkCI builds again because some method raised a > >> ModificationForbidden, which the test runner, obviously, was not prepared > >> for ... > > This makes no sense to me. Test runners collect tests that error just as they collect tests that assert fail. Why would a ModificationForbidden Another be considered an error? > > > > >> From what I can see, the only problem with my proposal that resists still in > >> the inbox, Kernel-ct.1321, is related to Chris' Magma implementation. I want > >> to respect this use case (and actually, learning a bit more about Magma is > >> already on my long, long list of nice-to-do stuff), but I have to agree with > >> other people saying that ModificationForbidden is a very low-level error and > >> should not need any special treatment. For this reason, I also think that > >> Marcel's changeset is a bit over-engineered for the Trunk, given the > >> particular fact that we do not have plans for any user of ManagedObjects in > >> the Trunk. > >> > >> Chris, did anything happen on your end regarding the handling of > >> ModificationForbidden in Magma? Iirc you mentioned recently that you have > >> reached some kind of feature completeness in your project. Can't we just > >> merge Kernel-ct.1321 for now and resolve this discussion until some new use > >> case arises? For Magma as an external project (still without being familiar > >> at all with its design), I don't understand what would be wrong with > >> handling such exceptions using #on:do:, this would totally suffice to > >> circumvent the #defaultAction implementation. If you do not have the > >> possibility to install an exception handler or a ToolSet, you could still > >> place an extension override method in ModificationForbidden and > >> (conditionally) inject your custom handling logic there. > >> > >> What do you think? I would like to finally get ahead with this issue! :-) > > I don???t want yo build something that is incompatible with gemstone. Currently we don???t have their attention; Ogaro had thrrr attention. So we have to do the work of ensuring that ModificationForbidden supports the Gemstone use case; it???s a very very important one. on:do: is completely inadequate for this. One needs a system wide exception handler where any ModificationForbidden is routed through a manager that is able to identify that the object in question is in fact being mapped to a database (or a remote system, or a breakout ring system etc). But on:do: handlers are for specific flows of control (currently within a single thread) and cannot serve as system wide handlers. > > Here???s a simplified use case. The programmer wants to be able to trace wherever in the system an object is modified. It may occur in arbitrary processes, not just in the current flow of control. To do this efficiently the programmer uses ModificationForbidden to catch a modification, log it, make the object readable, effect the modification, make the object read-only again and proceed. This *cannot* be done with an on:do: handler. > > >> > >> Best, > >> Christoph > >> > >> > >> > >> -- > >> Sent from: http://forum.world.st/Squeak-Dev-f45488.html > >> > > > From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 17:29:43 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 17:29:43 +0000 Subject: [squeak-dev] Why is ModificationForbidden not an Error? In-Reply-To: <20200930172630.GA50082@shell.msen.com> References: <7cdc2ad647ff4d67bc0cc0aec6a184ea@student.hpi.uni-potsdam.de> <1775378D-EF3B-4876-95FA-960ECD7DE045@gmail.com>, <20200930172630.GA50082@shell.msen.com> Message-ID: > I think there was an inbox submission a while back that could be merged. Here it is: Kernel-ct.1321 😊 Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von David T. Lewis Gesendet: Mittwoch, 30. September 2020 19:26:30 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? I am not involved in this discussion, but if I understand the conversation there is no longer any disagreement about what needs to be done, so we just need to make it happen in trunk. I think there was an inbox submission a while back that could be merged. +1000 for bringing this issue to a resolution, and thanks Christoph for moving it forward. Dave On Wed, Sep 30, 2020 at 05:43:57AM -0700, Eliot Miranda wrote: > > > > On Sep 30, 2020, at 3:48 AM, Thiede, Christoph wrote: > > > > ??? > > Alright, anyone having any new opinion about this? Let's make a decision! :-) > > > > > I don???t see how it can be other than an error. If one attempts to modify a literal that is an error. If one attempts to modify a read-only object then that is an error, unless some smart system is overloading read-only-ness to implement a read barrier. > > > Best, > > > > Christoph > > > > Von: Chris Muller > > Gesendet: Donnerstag, 24. September 2020 21:33:27 > > An: The general-purpose Squeak developers list; Thiede, Christoph > > Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? > > > > Hi Christoph, > > > > Magma does not depend on ModificationForbidden. My participation in this discussion was merely for my own learning, to try to overcome my skepticism about ModificationForbidden. Changing its superclass shouldn't affect Magma at all. Whether Magma will be able to make use of it is still up in the air. Please feel free to integrate what you and the other folks interested in this decide is best. > > > > Thanks! > > Chris > > > >> On Thu, Sep 24, 2020 at 4:09 AM Christoph Thiede wrote: > >> Hi Chris, hi all, > >> > >> after months of stagnation, I'd like to bring up this open discussion again. > >> IMO the current situation in the Trunk is still buggy and the last thing we > >> want developers to have to care about. For example, I recently crashed a > >> number of smalltalkCI builds again because some method raised a > >> ModificationForbidden, which the test runner, obviously, was not prepared > >> for ... > > This makes no sense to me. Test runners collect tests that error just as they collect tests that assert fail. Why would a ModificationForbidden Another be considered an error? > > > > >> From what I can see, the only problem with my proposal that resists still in > >> the inbox, Kernel-ct.1321, is related to Chris' Magma implementation. I want > >> to respect this use case (and actually, learning a bit more about Magma is > >> already on my long, long list of nice-to-do stuff), but I have to agree with > >> other people saying that ModificationForbidden is a very low-level error and > >> should not need any special treatment. For this reason, I also think that > >> Marcel's changeset is a bit over-engineered for the Trunk, given the > >> particular fact that we do not have plans for any user of ManagedObjects in > >> the Trunk. > >> > >> Chris, did anything happen on your end regarding the handling of > >> ModificationForbidden in Magma? Iirc you mentioned recently that you have > >> reached some kind of feature completeness in your project. Can't we just > >> merge Kernel-ct.1321 for now and resolve this discussion until some new use > >> case arises? For Magma as an external project (still without being familiar > >> at all with its design), I don't understand what would be wrong with > >> handling such exceptions using #on:do:, this would totally suffice to > >> circumvent the #defaultAction implementation. If you do not have the > >> possibility to install an exception handler or a ToolSet, you could still > >> place an extension override method in ModificationForbidden and > >> (conditionally) inject your custom handling logic there. > >> > >> What do you think? I would like to finally get ahead with this issue! :-) > > I don???t want yo build something that is incompatible with gemstone. Currently we don???t have their attention; Ogaro had thrrr attention. So we have to do the work of ensuring that ModificationForbidden supports the Gemstone use case; it???s a very very important one. on:do: is completely inadequate for this. One needs a system wide exception handler where any ModificationForbidden is routed through a manager that is able to identify that the object in question is in fact being mapped to a database (or a remote system, or a breakout ring system etc). But on:do: handlers are for specific flows of control (currently within a single thread) and cannot serve as system wide handlers. > > Here???s a simplified use case. The programmer wants to be able to trace wherever in the system an object is modified. It may occur in arbitrary processes, not just in the current flow of control. To do this efficiently the programmer uses ModificationForbidden to catch a modification, log it, make the object readable, effect the modification, make the object read-only again and proceed. This *cannot* be done with an on:do: handler. > > >> > >> Best, > >> Christoph > >> > >> > >> > >> -- > >> Sent from: http://forum.world.st/Squeak-Dev-f45488.html > >> > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From commits at source.squeak.org Wed Sep 30 17:46:46 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 30 Sep 2020 17:46:46 0000 Subject: [squeak-dev] The Inbox: Tools-ct.991.mcz Message-ID: Christoph Thiede uploaded a new version of Tools to project The Inbox: http://source.squeak.org/inbox/Tools-ct.991.mcz ==================== Summary ==================== Name: Tools-ct.991 Author: ct Time: 30 September 2020, 7:46:39.949807 pm UUID: 4d2f75ef-336d-cc4c-aa0d-dd4f7ff99fc7 Ancestors: Tools-mt.990 Fixes a bug in DebuggerMethodMap's rangeForPC lookup Steps to reproduce: c := Object newSubclass. c compile: 'foo: foo foo = #foo ifTrue: [^ true]. ^ (foo ifNil: [^ false]) = #bar'. c new foo: #bar. "Debug it. Step into #foo:, step over #=. Before this commit, the selection was '^ true'" The error was that #findNearbyBinaryIndex: uses to return the lower possible index if there is no exact match. For debugging, we cannot need this behavior, because we want to select the next operation to be executed. Furthermore, this commit refactors some duplication with DebuggerMethodMapForFullBlockCompiledMethod. Please review! Uploaded again due to totally broken ancestry. Replaces Tools-ct.956, which can be moved into the treated inbox. =============== Diff against Tools-mt.990 =============== Item was changed: ----- Method: DebuggerMethodMap>>rangeForPC:in:contextIsActiveContext: (in category 'source mapping') ----- rangeForPC: contextsConcretePC in: method contextIsActiveContext: contextIsActiveContext + "Answer the indices in the source code for the supplied pc. If the context is the active context (is at the hot end of the stack) then its pc is the current pc. But if the context isn't, because it is suspended sending a message, then its current pc is the previous pc." - "Answer the indices in the source code for the supplied pc. - If the context is the actve context (is at the hot end of the stack) - then its pc is the current pc. But if the context isn't, because it is - suspended sending a message, then its current pc is the previous pc." + | pc i end sortedMap | - | pc i end | pc := method abstractPCForConcretePC: (contextIsActiveContext + ifTrue: [contextsConcretePC] + ifFalse: [(method pcPreviousTo: contextsConcretePC) + ifNil: [contextsConcretePC]]). + (self abstractSourceMapForMethod: method) + at: pc + ifPresent: [:range | ^ range]. + sortedMap := self sortedSourceMapForMethod: method. + sortedMap ifEmpty: [^ 1 to: 0]. + i := sortedMap + findBinaryIndex: [:assoc | pc - assoc key] + ifNone: [:lower :upper | upper]. + i < 1 ifTrue: [^ 1 to: 0]. + i > sortedMap size ifTrue: [ + end := sortedMap inject: 0 into: [:prev :this | + prev max: this value last]. + ^ end + 1 to: end]. + ^ (sortedMap at: i) value! - ifTrue: [contextsConcretePC] - ifFalse: [(method pcPreviousTo: contextsConcretePC) - ifNotNil: [:prevpc| prevpc] - ifNil: [contextsConcretePC]]). - (self abstractSourceMap includesKey: pc) ifTrue: - [^self abstractSourceMap at: pc]. - sortedSourceMap ifNil: - [sortedSourceMap := self abstractSourceMap associations - replace: [ :each | each copy ]; - sort]. - sortedSourceMap isEmpty ifTrue: [^1 to: 0]. - i := sortedSourceMap findNearbyBinaryIndex: [:assoc| pc - assoc key]. - i < 1 ifTrue: [^1 to: 0]. - i > sortedSourceMap size ifTrue: - [end := sortedSourceMap inject: 0 into: - [:prev :this | prev max: this value last]. - ^end+1 to: end]. - ^(sortedSourceMap at: i) value - - "| method source scanner map | - method := DebuggerMethodMap compiledMethodAt: #rangeForPC:in:contextIsActiveContext:. - source := method getSourceFromFile asString. - scanner := InstructionStream on: method. - map := method debuggerMap. - Array streamContents: - [:ranges| - [scanner atEnd] whileFalse: - [| range | - range := map rangeForPC: scanner pc in: method contextIsActiveContext: true. - ((map abstractSourceMap includesKey: scanner abstractPC) - and: [range first ~= 0]) ifTrue: - [ranges nextPut: (source copyFrom: range first to: range last)]. - scanner interpretNextInstructionFor: InstructionClient new]]"! Item was added: + ----- Method: DebuggerMethodMap>>sortedSourceMap (in category 'private') ----- + sortedSourceMap + + ^ sortedSourceMap ifNil: [ + sortedSourceMap := self abstractSourceMap associations + replace: [:each | each copy]; + sort]! Item was added: + ----- Method: DebuggerMethodMap>>sortedSourceMapForMethod: (in category 'source mapping') ----- + sortedSourceMapForMethod: aCompiledMethod + + ^ self sortedSourceMap! Item was changed: ----- Method: DebuggerMethodMapForFullBlockCompiledMethods>>abstractSourceMap (in category 'source mapping') ----- abstractSourceMap + + ^ self shouldNotImplement! - self shouldNotImplement! Item was removed: - ----- Method: DebuggerMethodMapForFullBlockCompiledMethods>>rangeForPC:in:contextIsActiveContext: (in category 'source mapping') ----- - rangeForPC: contextsConcretePC in: method contextIsActiveContext: contextIsActiveContext - "Answer the indices in the source code for the supplied pc. - If the context is the actve context (is at the hot end of the stack) - then its pc is the current pc. But if the context isn't, because it is - suspended sending a message, then its current pc is the previous pc." - - | pc i end mapForMethod sortedMap | - pc := method abstractPCForConcretePC: (contextIsActiveContext - ifTrue: [contextsConcretePC] - ifFalse: [(method pcPreviousTo: contextsConcretePC) - ifNotNil: [:prevpc| prevpc] - ifNil: [contextsConcretePC]]). - ((mapForMethod := self abstractSourceMapForMethod: method) includesKey: pc) ifTrue: - [^mapForMethod at: pc]. - sortedSourceMap ifNil: - [sortedSourceMap := IdentityDictionary new]. - sortedMap := sortedSourceMap - at: method - ifAbsentPut: [mapForMethod associations - replace: [ :each | each copy ]; - sort]. - sortedMap isEmpty ifTrue: [^1 to: 0]. - i := sortedMap findNearbyBinaryIndex: [:assoc| pc - assoc key]. - i < 1 ifTrue: [^1 to: 0]. - i > sortedMap size ifTrue: - [end := sortedMap inject: 0 into: - [:prev :this | prev max: this value last]. - ^end+1 to: end]. - ^(sortedMap at: i) value - - "| method source scanner map | - method := DebuggerMethodMapForFullBlockCompiledMethods compiledMethodAt: #rangeForPC:in:contextIsActiveContext:. - source := method getSourceFromFile asString. - scanner := InstructionStream on: method. - map := method debuggerMap. - Array streamContents: - [:ranges| - [scanner atEnd] whileFalse: - [| range | - range := map rangeForPC: scanner pc in: method contextIsActiveContext: true. - ((map abstractSourceMap includesKey: scanner abstractPC) - and: [range first ~= 0]) ifTrue: - [ranges nextPut: (source copyFrom: range first to: range last)]. - scanner interpretNextInstructionFor: InstructionClient new]]"! Item was added: + ----- Method: DebuggerMethodMapForFullBlockCompiledMethods>>sortedSourceMap (in category 'source mapping') ----- + sortedSourceMap + + ^ self shouldNotImplement! Item was added: + ----- Method: DebuggerMethodMapForFullBlockCompiledMethods>>sortedSourceMapForMethod: (in category 'source mapping') ----- + sortedSourceMapForMethod: method + + sortedSourceMap ifNil: [ + sortedSourceMap := IdentityDictionary new]. + ^ sortedSourceMap + at: method + ifAbsentPut: [(self abstractSourceMapForMethod: method) associations + replace: [ :each | each copy ]; + sort]! From robert.withers at pm.me Wed Sep 30 18:23:06 2020 From: robert.withers at pm.me (Robert) Date: Wed, 30 Sep 2020 18:23:06 +0000 Subject: [squeak-dev] *****SPAM***** Looking to build a WeatherStation for my sailboat In-Reply-To: <9c36fbb3-57d3-9fe5-bf33-8dae45999c10@pm.me> References: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> <9c36fbb3-57d3-9fe5-bf33-8dae45999c10@pm.me> Message-ID: Hey Tim, check it! Re: recent discussions about multi-touch 12” displays. Lookie-Lookie what I have just learned comes with the sailboat I bought. It is the Zeus3S 7”? I guess 7” chart plotter, a $1,300 value with maps for entire east coast! Bam! We’re cooking now! Ready for my trans-Atlantic trip to Portugal by way of the Azores, a 2022. This chart plotter can handle it! Global. It is good to know where I am and what lay ahead! https://www.bandg.com/en-gb/bg/type/chartplotter/bg-zeus3-7-mfdworld-basemap So the obvious question is whether I can figure out how to boot Squeak on this chart plotter!!! Shizaam! Honestly, with freely downloadable chart data, I was thinking of combining your weather station (w depth sounder) with a Squeak-based navigation chart plotter capability. Then I learned about my Zeus 3! :-D Kindly, Robert . .. ... ‘...^,^ On Sun, Sep 27, 2020 at 16:22, Robert Withers wrote: > Hi tim, > > On 9/27/20 2:07 PM, tim Rowledge wrote: > >> On 2020-09-27, at 6:38 AM, Robert Withers [](mailto:robert.withers at pm.me) wrote: >> >>> I finally got my sailboat! A long-term dream of mine, a bucket list item! I bought her last week and I leave Thursday with my mate Speedy, to sail her down from CT to NC. I will rename her SV Slosh outta Oriental, NC. WOOT! WOOT! WOOT! >> >> Congratulations on getting your very own money slurping hole in the water :-) > > Yes, I totally underestimated the upfront costs. So I will distribute my fiscal load, month over month. Yes, I am retired! Me and my dog are heading to open water! She is going to love live-aboard, I think! > > --- > > I will approach my 1972 boat in the same fashion I approached my truck. I bought a 1997 Pathfinder, the key feature being a manual transmission. This turns out to be fairly rare in trucks, most are sold with only an automatic transmission, these days. What a horrible outcome! I like changing my own gears, thank you very much. The truck being a '97, there were certain maintenance issues. As well I had some upgrades I wanted (tow hitch!). My approach was to generate 3 lists: maintenance list, upgrade list, exploratory list. > > Doing the same with my boat, I will add weather instrumentation to my upgrade list and to my exploratory list. My upgrade list already has such items as gimbaled 3-burner/oven boat stove, air conditioner, 15 W A/C generator, 12v refrigerator, battery recharger and now includes a distributed Weather Station. It will take time, but I will get there. I am just watching my money to ensure my emergency funds and maintenance are well covered. > > My monthly costs for the boat are $100/mo boat insurance and $265/mo live-aboard slip fees. Yearly costs are haul, bottom paint and zincs (~$2,000/year). My upgrade list is pricey! > >>> In thinking about her capabilities, on the water, I will always be curious about the weather. >> >> I understand knowing about the weather is quite important on a boat. Something about 100ft waves etc? > > Yes, especially if I follow through on my plan to sail to Morocco in 2022. Big Atlantic swell! > >>> Tim, your WeatherStation sprung immediately to mind. I found several websites, I am perusing, but I am a little lost on which hardware to get and bringing her up. [1][2][3]. Am I supposed to buy the OurWeather device? >> >> I'd be very surprised if the very basic stuff I bought would survive more than a few weeks at sea, it simply isn't that sort of quality. I can't imagine the lightweight plastic cup anemometer surviving the first heavy gust of wind! >> >> So, that definitely means some research into what gear you can find that is suitably tough. I imagine it will not be cheap because nothing about a boat is cheap. > > Yes and so the weather sensing lands on my exploratory list! Not too much is cheap. NC slips are cheaper than so and so... > >>> The plot and rotary dial displays are AWESOME! I'd love to have a rotary dial for both the barometer, as you have shown, as well as the anemometer. Perhaps on a waterproof display at the helm...that would be good. >> >> If you want to use any of the stuff I wrote you'll need to find some sensor hardware that has some variety of output that could be handled by a Pi or similar SBC - maybe even something like an ESP-32 board would be smart here. The cheap stuff simply uses a magnetic reed switch on the anemometer (grief, that's hard to type) and so you get to count pulses and have all the fun of sorting out de-bouncing etc. The wind direction vane does some sort of cockamamie resistor network and magentic reed switches that require an ADC and some rather hope-and-guess maths to work out where it is pointing - and only 8 options, so not very useful on a boat. > > My boat may already have various meteorological sensors, that I would just need to figure out how to interface with. I will fidn out more this Thursday. From reading your WeatherStation page and other sources, it is sounding like the data collection issue is two-fold: A) communications interface and B) data format. > >> I'm told that all the smart people are using ultrasonic based sensors that use differential signal propagation to work out both the wind speed and direction with no moving parts. And apparently, can work out the rain density from the impacts of raindrops on the flat plate sensor. Cool stuff. The nearest I've seen to the mil-spec system a friend is working on is >> https://www.kickstarter.com/projects/weatherflow/tempest-a-revolutionary-personal-weather-system?ref=discovery_category_newest&term=weather > > Alright, this is quite cool. I dug a bit deeper and found a site to buy the Tempest device: https://shop.weatherflow.com/collections/frontpage/products/tempest. This only sells the Tempest sensor device and a WiFi hub, no battery backup. > > From the page you linked it describes an option: > > Tempest - Storm & Fire Ready Kit > > This includes: "with Pro Hub and battery backup. Includes mobile / cellular communication fail-over during WiFi outages. Battery backup provides power to the Hub for up to seven days. Includes a prepaid data plan for at least 750 hours of continuous data streaming during WiFi outages." > > Perhaps there is some way to buy the Kickstarter package. > > I also researched, briefly a weatherproof option for the RPi. I found this: https://www.crowdsupply.com/openh/rubicon. Stackable to include the hub and battery backup with the stackable option? Perhaps. If the sensor is Wifi/Bluetooth, then this Pi may be stored in the cabin. But the display,a at the helm, would need weatherproofing. My curiosity is a weatherproof touch display at the helm. On the exploratory list! > >> This is all going to take some research with your boating contacts to find a good system. The downside is that any commercial system will almost certainly have its own display suff. You could do much worse than looking at >> https://microship.com >> and contacting Steve; he has done this sort of thing for many years and may have parts to point to or even sell. > > Alright, lots to explore! I will save his contact! > >> Once you've got sensors that can talk to an ESP or a Pi (so various digital connections or i2c or spi or even bluetooth?) you can consider getting the data to some other machine to display. > > Yes, this sounds the right approach! I read, I think in the Tempest stuff, about a EU weather data standard. > >> It may be that your sensors can connect directly to that display machine, saving one step. For a home built display you might go with a Pi attached to their 7" multi-touch display inside a very well sealed box. > > Yes, though it would be fantastic to have the touch screen (12"?) available/accessible in the weather. > >> I'm afraid this may take longer than 'by Thursday' :-) > > That is for sure! I am excited to dig into it, over time! > > Thank you for your guidance, tim! I look forwards to learning more. > > Kindly, > Robert > >> tim >> -- >> tim Rowledge; >> tim at rowledge.org >> ; >> http://www.rowledge.org/tim >> Useful Latin Phrases:- Tam exanimis quam tunica nehru fio. = I am as dead as the nehru jacket. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: IMG_0547.JPG Type: image/jpeg Size: 992108 bytes Desc: not available URL: From commits at source.squeak.org Wed Sep 30 18:23:31 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 30 Sep 2020 18:23:31 0000 Subject: [squeak-dev] The Inbox: Morphic-ct.1689.mcz Message-ID: A new version of Morphic was added to project The Inbox: http://source.squeak.org/inbox/Morphic-ct.1689.mcz ==================== Summary ==================== Name: Morphic-ct.1689 Author: ct Time: 30 September 2020, 8:23:24.908605 pm UUID: d37597bc-aeb8-354c-8500-bb8e4b5f5373 Ancestors: Morphic-eem.1686 If the label in a button is truncated, use it as fallback balloon text. Also speeds up drawing logic a little bit (but I did not measure the impact). Snippets to quickly try out: (PluggableButtonMorphPlus on: PluggableTextMorphPlus new getState: #isNil action: #halt label: #yourself) openInHand (PluggableButtonMorphPlus on: 'foo\bar' withCRs getState: #isNil action: #halt label: #yourself) openInHand Resize and hover them. Originally inspired by this problem: http://forum.world.st/The-Inbox-Morphic-ct-1634-mcz-td5112623.html#a5112644:~:text=posts-,%3E%20Would%20like%20that%20the%20text%20on%20the%20buttons%20did%20not%20abbreviate%20down.,Hm%2C%20should%20the%20default%20balloon%20text%20for%20each%20button%20in%20Squeak%20be%20its%20content%20if%20abbreviated%3F =============== Diff against Morphic-eem.1686 =============== Item was changed: Morph subclass: #PluggableButtonMorph + instanceVariableNames: 'model label font getStateSelector actionSelector getLabelSelector getMenuSelector shortcutCharacter askBeforeChanging triggerOnMouseDown offColor onColor feedbackColor showSelectionFeedback allButtons arguments argumentsProvider argumentsSelector style hoverColor borderColor textColor labelOffset wantsGradient truncateLabel' - instanceVariableNames: 'model label font getStateSelector actionSelector getLabelSelector getMenuSelector shortcutCharacter askBeforeChanging triggerOnMouseDown offColor onColor feedbackColor showSelectionFeedback allButtons arguments argumentsProvider argumentsSelector style hoverColor borderColor textColor labelOffset wantsGradient' classVariableNames: 'GradientButton RoundedButtonCorners' poolDictionaries: '' category: 'Morphic-Pluggable Widgets'! !PluggableButtonMorph commentStamp: '' prior: 0! A PluggableButtonMorph is a combination of an indicator for a boolean value stored in its model and an action button. The action of a button is often, but not always, to toggle the boolean value that it shows. Its pluggable selectors are: getStateSelector fetch a boolean value from the model actionSelector invoke this button's action on the model getLabelSelector fetch this button's lable from the model getMenuSelector fetch a pop-up menu for this button from the model Any of the above selectors can be nil, meaning that the model does not supply behavior for the given action, and the default behavior should be used. For example, if getStateSelector is nil, then this button shows the state of a read-only boolean that is always false. The model informs its view(s) of changes by sending #changed: to itself with getStateSelector as a parameter. The view tells the model when the button is pressed by sending actionSelector. If the actionSelector takes one or more arguments, then the following are relevant: arguments A list of arguments to provide when the actionSelector is called. argumentsProvider The object that is sent the argumentSelector to obtain arguments, if dynamic argumentsSelector The message sent to the argumentProvider to obtain the arguments. Options: askBeforeChanging have model ask user before allowing a change that could lose edits triggerOnMouseDown do this button's action on mouse down (vs. up) transition shortcutCharacter a place to record an optional shortcut key ! Item was added: + ----- Method: PluggableButtonMorph>>balloonText (in category 'accessing') ----- + balloonText + + | labelToUse | + super balloonText ifNotNil: [:text | ^ text]. + (truncateLabel ifNil: [false]) ifFalse: [^ nil]. + + labelToUse := self label. + ^ labelToUse isMorph + ifTrue: [Character startOfHeader asText + addAttribute: labelToUse asTextAnchor; + yourself] + ifFalse: [labelToUse asString]! Item was changed: ----- Method: PluggableButtonMorph>>drawLabelOn: (in category 'drawing') ----- drawLabelOn: aCanvas | fontToUse labelToUse colorToUse labelWidth layoutBounds drawBlock | self label ifNil: [^ self]. + - layoutBounds := self layoutBounds. labelToUse := self label asString. fontToUse := self font. colorToUse := self textColorToUse. + labelWidth := fontToUse widthOfString: labelToUse. + truncateLabel := labelWidth > layoutBounds width. + "Support very narrow buttons. Shrink text to monogram then." ((layoutBounds width < self labelShrinkThreshold and: [self hResizing ~~ #shrinkWrap]) and: [labelToUse size > 3]) ifTrue: [ labelToUse := labelToUse first asString. "Show first character only." + fontToUse := fontToUse emphasized: (TextEmphasis bold) emphasisCode. + labelWidth := fontToUse widthOfString: labelToUse]. - fontToUse := fontToUse emphasized: (TextEmphasis bold) emphasisCode]. - labelWidth := fontToUse widthOfString: labelToUse. - drawBlock := [:c | c drawString: labelToUse at: (layoutBounds center x - (labelWidth // 2) max: (layoutBounds left)) @ (layoutBounds center y - (fontToUse height // 2)) font: fontToUse color: colorToUse]. + - self clipSubmorphs ifTrue: [aCanvas clipBy: layoutBounds during: drawBlock] + ifFalse: [drawBlock value: aCanvas].! - ifFalse: [drawBlock value: aCanvas]! Item was changed: ----- Method: PluggableButtonMorph>>drawMorphLabelOn: (in category 'drawing') ----- drawMorphLabelOn: aCanvas + | layoutBounds labelToUse | - | layoutBounds | layoutBounds := self layoutBounds. + labelToUse := self label. - - self label privateFullMoveBy: (layoutBounds center - self label center). + truncateLabel := (labelToUse extent <= layoutBounds extent) not. + labelToUse privateFullMoveBy: layoutBounds center - labelToUse center. + self clipSubmorphs ifTrue: [aCanvas clipBy: layoutBounds + during: [:c | c fullDrawMorph: labelToUse]] + ifFalse: [aCanvas fullDrawMorph: labelToUse].! - during: [:c | c fullDrawMorph: self label]] - ifFalse: [aCanvas fullDrawMorph: self label].! Item was changed: ----- Method: PluggableButtonMorph>>veryDeepInner: (in category 'copying') ----- veryDeepInner: deepCopier "Copy all of my instance variables. Some need to be not copied at all, but shared. Warning!!!! Every instance variable defined in this class must be handled. We must also implement veryDeepFixupWith:. See DeepCopier class comment." super veryDeepInner: deepCopier. "model := model. Weakly copied" label := label veryDeepCopyWith: deepCopier. "getStateSelector := getStateSelector. a Symbol" "actionSelector := actionSelector. a Symbol" "getLabelSelector := getLabelSelector. a Symbol" "getMenuSelector := getMenuSelector. a Symbol" shortcutCharacter := shortcutCharacter veryDeepCopyWith: deepCopier. askBeforeChanging := askBeforeChanging veryDeepCopyWith: deepCopier. triggerOnMouseDown := triggerOnMouseDown veryDeepCopyWith: deepCopier. offColor := offColor veryDeepCopyWith: deepCopier. onColor := onColor veryDeepCopyWith: deepCopier. feedbackColor := feedbackColor veryDeepCopyWith: deepCopier. hoverColor := hoverColor veryDeepCopyWith: deepCopier. borderColor := borderColor veryDeepCopyWith: deepCopier. textColor := textColor veryDeepCopyWith: deepCopier. labelOffset := labelOffset veryDeepCopyWith: deepCopier. allButtons := nil. "a cache" arguments := arguments veryDeepCopyWith: deepCopier. argumentsProvider := argumentsProvider veryDeepCopyWith: deepCopier. "argumentsSelector := argumentsSelector. a Symbol" + style := style. "a Symbol" + "truncateLabel := truncateLabel."! - style := style. "a Symbol"! From robert.withers at pm.me Wed Sep 30 18:29:43 2020 From: robert.withers at pm.me (Robert) Date: Wed, 30 Sep 2020 18:29:43 +0000 Subject: [squeak-dev] *****SPAM***** Looking to build a WeatherStation for my sailboat In-Reply-To: References: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> <9c36fbb3-57d3-9fe5-bf33-8dae45999c10@pm.me> Message-ID: Sorry for my email size. I meant to delete my high-res photo of Angel! 😇 Kindly, Robert . .. ... ‘...^,^ On Wed, Sep 30, 2020 at 14:23, Robert wrote: > Hey Tim, check it! > > Re: recent discussions about multi-touch 12” displays. Lookie-Lookie what I have just learned comes with the sailboat I bought. It is the Zeus3S 7”? I guess 7” chart plotter, a $1,300 value with maps for entire east coast! Bam! We’re cooking now! Ready for my trans-Atlantic trip to Portugal by way of the Azores, a 2022. This chart plotter can handle it! Global. It is good to know where I am and what lay ahead! > > https://www.bandg.com/en-gb/bg/type/chartplotter/bg-zeus3-7-mfdworld-basemap > > So the obvious question is whether I can figure out how to boot Squeak on this chart plotter!!! Shizaam! > > Honestly, with freely downloadable chart data, I was thinking of combining your weather station (w depth sounder) with a Squeak-based navigation chart plotter capability. Then I learned about my Zeus 3! :-D > > Kindly, > Robert > . .. ... ‘...^,^ -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: IMG_0547.JPG Type: image/jpeg Size: 992108 bytes Desc: not available URL: From commits at source.squeak.org Wed Sep 30 19:23:53 2020 From: commits at source.squeak.org (commits at source.squeak.org) Date: Wed, 30 Sep 2020 19:23:53 0000 Subject: [squeak-dev] The Inbox: Graphics-ct.441.mcz Message-ID: Christoph Thiede uploaded a new version of Graphics to project The Inbox: http://source.squeak.org/inbox/Graphics-ct.441.mcz ==================== Summary ==================== Name: Graphics-ct.441 Author: ct Time: 30 September 2020, 9:23:41.380807 pm UUID: 7b1f1c65-2eca-4442-a0da-88e6975377f2 Ancestors: Graphics-pre.439 Proposal: Isolate alpha channel when printing a named color. This allows it to reuse the color name even for translucent color. Example: (Color red alpha: 0.4) printString Output (new): 'Color red alpha: 0.4' Output (old): '(TranslucentColor r: 1 g: 0.0 b: 0.0 alpha: 0.4)' =============== Diff against Graphics-pre.439 =============== Item was changed: ----- Method: Color>>printOn: (in category 'printing') ----- printOn: aStream + | name | + name := self asNontranslucentColor name. + name ifNil: [^ self storeOn: aStream]. + + aStream + nextPutAll: 'Color '; + nextPutAll: name. + self isTranslucent ifTrue: [ + aStream + nextPutAll: ' alpha: '; + print: self alpha maxDecimalPlaces: 3].! - (name := self name) ifNotNil: - [^ aStream - nextPutAll: 'Color '; - nextPutAll: name]. - self storeOn: aStream. - ! From lewis at mail.msen.com Wed Sep 30 19:59:06 2020 From: lewis at mail.msen.com (David T. Lewis) Date: Wed, 30 Sep 2020 15:59:06 -0400 Subject: [squeak-dev] Can Kernel-ct.1321 be merged into trunk now? (was: Why is ModificationForbidden not an Error?) In-Reply-To: References: <7cdc2ad647ff4d67bc0cc0aec6a184ea@student.hpi.uni-potsdam.de> <20200930172630.GA50082@shell.msen.com> Message-ID: <20200930195906.GA75715@shell.msen.com> Changing the subject line to ask the question directly. Can Kernel-ct.1321 be merged into trunk now? See below and prior discussions. Thanks, Dave On Wed, Sep 30, 2020 at 05:29:43PM +0000, Thiede, Christoph wrote: > > I think there was an inbox submission a while back that could be merged. > > > Here it is: Kernel-ct.1321 ???? > > > Best, > > Christoph > > ________________________________ > Von: Squeak-dev im Auftrag von David T. Lewis > Gesendet: Mittwoch, 30. September 2020 19:26:30 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? > > I am not involved in this discussion, but if I understand the conversation > there is no longer any disagreement about what needs to be done, so we > just need to make it happen in trunk. I think there was an inbox submission > a while back that could be merged. > > +1000 for bringing this issue to a resolution, and thanks Christoph for > moving it forward. > > Dave > > On Wed, Sep 30, 2020 at 05:43:57AM -0700, Eliot Miranda wrote: > > > > > > > On Sep 30, 2020, at 3:48 AM, Thiede, Christoph wrote: > > > > > > ??? > > > Alright, anyone having any new opinion about this? Let's make a decision! :-) > > > > > > > > > I don???t see how it can be other than an error. If one attempts to modify a literal that is an error. If one attempts to modify a read-only object then that is an error, unless some smart system is overloading read-only-ness to implement a read barrier. > > > > > Best, > > > > > > Christoph > > > > > > Von: Chris Muller > > > Gesendet: Donnerstag, 24. September 2020 21:33:27 > > > An: The general-purpose Squeak developers list; Thiede, Christoph > > > Betreff: Re: [squeak-dev] Why is ModificationForbidden not an Error? > > > > > > Hi Christoph, > > > > > > Magma does not depend on ModificationForbidden. My participation in this discussion was merely for my own learning, to try to overcome my skepticism about ModificationForbidden. Changing its superclass shouldn't affect Magma at all. Whether Magma will be able to make use of it is still up in the air. Please feel free to integrate what you and the other folks interested in this decide is best. > > > > > > Thanks! > > > Chris > > > > > >> On Thu, Sep 24, 2020 at 4:09 AM Christoph Thiede wrote: > > >> Hi Chris, hi all, > > >> > > >> after months of stagnation, I'd like to bring up this open discussion again. > > >> IMO the current situation in the Trunk is still buggy and the last thing we > > >> want developers to have to care about. For example, I recently crashed a > > >> number of smalltalkCI builds again because some method raised a > > >> ModificationForbidden, which the test runner, obviously, was not prepared > > >> for ... > > > > This makes no sense to me. Test runners collect tests that error just as they collect tests that assert fail. Why would a ModificationForbidden Another be considered an error? > > > > > > > > >> From what I can see, the only problem with my proposal that resists still in > > >> the inbox, Kernel-ct.1321, is related to Chris' Magma implementation. I want > > >> to respect this use case (and actually, learning a bit more about Magma is > > >> already on my long, long list of nice-to-do stuff), but I have to agree with > > >> other people saying that ModificationForbidden is a very low-level error and > > >> should not need any special treatment. For this reason, I also think that > > >> Marcel's changeset is a bit over-engineered for the Trunk, given the > > >> particular fact that we do not have plans for any user of ManagedObjects in > > >> the Trunk. > > >> > > >> Chris, did anything happen on your end regarding the handling of > > >> ModificationForbidden in Magma? Iirc you mentioned recently that you have > > >> reached some kind of feature completeness in your project. Can't we just > > >> merge Kernel-ct.1321 for now and resolve this discussion until some new use > > >> case arises? For Magma as an external project (still without being familiar > > >> at all with its design), I don't understand what would be wrong with > > >> handling such exceptions using #on:do:, this would totally suffice to > > >> circumvent the #defaultAction implementation. If you do not have the > > >> possibility to install an exception handler or a ToolSet, you could still > > >> place an extension override method in ModificationForbidden and > > >> (conditionally) inject your custom handling logic there. > > >> > > >> What do you think? I would like to finally get ahead with this issue! :-) > > > > I don???t want yo build something that is incompatible with gemstone. Currently we don???t have their attention; Ogaro had thrrr attention. So we have to do the work of ensuring that ModificationForbidden supports the Gemstone use case; it???s a very very important one. on:do: is completely inadequate for this. One needs a system wide exception handler where any ModificationForbidden is routed through a manager that is able to identify that the object in question is in fact being mapped to a database (or a remote system, or a breakout ring system etc). But on:do: handlers are for specific flows of control (currently within a single thread) and cannot serve as system wide handlers. > > > > Here???s a simplified use case. The programmer wants to be able to trace wherever in the system an object is modified. It may occur in arbitrary processes, not just in the current flow of control. To do this efficiently the programmer uses ModificationForbidden to catch a modification, log it, make the object readable, effect the modification, make the object read-only again and proceed. This *cannot* be done with an on:do: handler. > > > > >> > > >> Best, > > >> Christoph > > >> > > >> > > >> > > >> -- > > >> Sent from: http://forum.world.st/Squeak-Dev-f45488.html > > >> > > > > > > > > > From robert.withers at pm.me Wed Sep 30 20:01:20 2020 From: robert.withers at pm.me (Robert Withers) Date: Wed, 30 Sep 2020 20:01:20 +0000 Subject: [squeak-dev] *****SPAM***** Looking to build a WeatherStation for my sailboat In-Reply-To: References: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> <9c36fbb3-57d3-9fe5-bf33-8dae45999c10@pm.me> Message-ID: <1f1446c4-313d-9424-6ed2-6ebfcdeac8ed@pm.me> I am searching for the processor for the Zeus 3S 7" and cannot find anything more than the "high-performance quad-core processor". The so called "Glass Helm" device has a iMX 8 integrated six-core processor. I could find the tech specs. All the docs I see only state: "The Zeus³'s high-performance processor". *grumble* K, r On 9/30/20 2:23 PM, Robert wrote: > Hey Tim, check it! > > Re: recent discussions about multi-touch 12” displays. Lookie-Lookie what I have just learned comes with the sailboat I bought. It is the Zeus3S 7”? I guess 7” chart plotter, a $1,300 value with maps for entire east coast! Bam! We’re cooking now! Ready for my trans-Atlantic trip to Portugal by way of the Azores, a 2022. This chart plotter can handle it! Global. It is good to know where I am and what lay ahead! > > https://www.bandg.com/en-gb/bg/type/chartplotter/bg-zeus3-7-mfdworld-basemap > > So the obvious question is whether I can figure out how to boot Squeak on this chart plotter!!! Shizaam! > > Honestly, with freely downloadable chart data, I was thinking of combining your weather station (w depth sounder) with a Squeak-based navigation chart plotter capability. Then I learned about my Zeus 3! :-D > > Kindly, > Robert > . .. ... ‘...^,^ -------------- next part -------------- An HTML attachment was scrubbed... URL: From Das.Linux at gmx.de Wed Sep 30 20:05:07 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Wed, 30 Sep 2020 22:05:07 +0200 Subject: [squeak-dev] highdpi testing In-Reply-To: <3EF3A6EF-2F5D-4E04-A53C-4D8E53740B5C@rowledge.org> References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> <3EF3A6EF-2F5D-4E04-A53C-4D8E53740B5C@rowledge.org> Message-ID: <9C14F442-2320-43F1-A20D-D9EC2F19553F@gmx.de> > On 30.09.2020, at 19:20, tim Rowledge wrote: > > A strange and possibly relevant though crossed my mind as I read that email - what happens with windows that cross physical display boundaries where the displays have different dpi values? That is exactly what I wrapped my brain around. On the VM-Side, Windows and Mac do the sane thing reporting a different scale factor when the majority of window estate is on a new display. I tried emulating that in the X/Unix implementation. On the Image side, the scale factor is checked at the same time as the display size, and if it changes, a callback is triggered to update everything interested, such as fonts or morphs. Best regards -Tobias From Das.Linux at gmx.de Wed Sep 30 20:10:56 2020 From: Das.Linux at gmx.de (Tobias Pape) Date: Wed, 30 Sep 2020 22:10:56 +0200 Subject: [squeak-dev] highdpi testing In-Reply-To: References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> <3EF3A6EF-2F5D-4E04-A53C-4D8E53740B5C@rowledge.org> Message-ID: <8F19CF2D-979F-483F-927B-768430E0E1DB@gmx.de> > On 30.09.2020, at 19:24, Thiede, Christoph wrote: > > On Windows, it appears to be a democratic thing - the majority of pixels wins in the question of DPI value. But I'm not absolutely sure about this and cannot test this right now. I don't believe it would be a good idea to override this behavior, even if we could ... Yes. Although I made it modifyable on Unix, just out of tradition that unix leaks every screw… (https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/platforms/unix/vm-display-X11/sqUnixX11Scale.c#L182) So, anyone interested in me putting the VM stuff into the main branch? -t From robert.withers at pm.me Wed Sep 30 20:15:59 2020 From: robert.withers at pm.me (Robert Withers) Date: Wed, 30 Sep 2020 20:15:59 +0000 Subject: [squeak-dev] *****SPAM***** Looking to build a WeatherStation for my sailboat In-Reply-To: References: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> <9c36fbb3-57d3-9fe5-bf33-8dae45999c10@pm.me> Message-ID: I asked on StackOverflow: https://stackoverflow.com/questions/64145054/what-processor-runs-the-bg-zeus-3s-chartplotter-and-can-we-get-into-the-boot-c On 9/30/20 4:01 PM, Robert Withers wrote: > I am searching for the processor for the Zeus 3S 7" and cannot find anything more than the "high-performance quad-core processor". The so called "Glass Helm" device has a iMX 8 integrated six-core processor. I could find the tech specs. All the docs I see only state: "The Zeus³'s high-performance processor". *grumble* > > K, r > > On 9/30/20 2:23 PM, Robert wrote: > >> Hey Tim, check it! >> >> Re: recent discussions about multi-touch 12” displays. Lookie-Lookie what I have just learned comes with the sailboat I bought. It is the Zeus3S 7”? I guess 7” chart plotter, a $1,300 value with maps for entire east coast! Bam! We’re cooking now! Ready for my trans-Atlantic trip to Portugal by way of the Azores, a 2022. This chart plotter can handle it! Global. It is good to know where I am and what lay ahead! >> >> https://www.bandg.com/en-gb/bg/type/chartplotter/bg-zeus3-7-mfdworld-basemap >> >> So the obvious question is whether I can figure out how to boot Squeak on this chart plotter!!! Shizaam! >> >> Honestly, with freely downloadable chart data, I was thinking of combining your weather station (w depth sounder) with a Squeak-based navigation chart plotter capability. Then I learned about my Zeus 3! :-D >> >> Kindly, >> Robert >> . .. ... ‘...^,^ -- K, r -------------- next part -------------- An HTML attachment was scrubbed... URL: From robert.withers at pm.me Wed Sep 30 20:18:09 2020 From: robert.withers at pm.me (Robert Withers) Date: Wed, 30 Sep 2020 20:18:09 +0000 Subject: [squeak-dev] Looking to build a WeatherStation for my sailboat In-Reply-To: References: <264c00e8-6f50-0834-3069-4aff5db9646b@pm.me> <8A398B94-C273-4B1A-B9F9-0BA69281A8F0@rowledge.org> <9c36fbb3-57d3-9fe5-bf33-8dae45999c10@pm.me> Message-ID: <3e22ae11-a506-f821-ce2f-8a2c53252ec3@pm.me> Removed the SPAM moniker...Jeez, what paranoia? F*cks hard with my PTSD. On 9/30/20 4:15 PM, Robert Withers wrote: > I asked on StackOverflow: https://stackoverflow.com/questions/64145054/what-processor-runs-the-bg-zeus-3s-chartplotter-and-can-we-get-into-the-boot-c > > On 9/30/20 4:01 PM, Robert Withers wrote: > >> I am searching for the processor for the Zeus 3S 7" and cannot find anything more than the "high-performance quad-core processor". The so called "Glass Helm" device has a iMX 8 integrated six-core processor. I could find the tech specs. All the docs I see only state: "The Zeus³'s high-performance processor". *grumble* >> >> K, r >> >> On 9/30/20 2:23 PM, Robert wrote: >> >>> Hey Tim, check it! >>> >>> Re: recent discussions about multi-touch 12” displays. Lookie-Lookie what I have just learned comes with the sailboat I bought. It is the Zeus3S 7”? I guess 7” chart plotter, a $1,300 value with maps for entire east coast! Bam! We’re cooking now! Ready for my trans-Atlantic trip to Portugal by way of the Azores, a 2022. This chart plotter can handle it! Global. It is good to know where I am and what lay ahead! >>> >>> https://www.bandg.com/en-gb/bg/type/chartplotter/bg-zeus3-7-mfdworld-basemap >>> >>> So the obvious question is whether I can figure out how to boot Squeak on this chart plotter!!! Shizaam! >>> >>> Honestly, with freely downloadable chart data, I was thinking of combining your weather station (w depth sounder) with a Squeak-based navigation chart plotter capability. Then I learned about my Zeus 3! :-D >>> >>> Kindly, >>> Robert >>> . .. ... ‘...^,^ > > -- > K, r -- K, r -------------- next part -------------- An HTML attachment was scrubbed... URL: From Christoph.Thiede at student.hpi.uni-potsdam.de Wed Sep 30 20:28:37 2020 From: Christoph.Thiede at student.hpi.uni-potsdam.de (Thiede, Christoph) Date: Wed, 30 Sep 2020 20:28:37 +0000 Subject: [squeak-dev] highdpi testing In-Reply-To: <8F19CF2D-979F-483F-927B-768430E0E1DB@gmx.de> References: <47A2DB7D-432A-4BAE-AD10-B1E55F184FF0@gmx.de> <7384b2c3a16a4eabb75cca5e20fbdba0@student.hpi.uni-potsdam.de> <766e3b2651bc45cc90d0dd98cb1a7285@student.hpi.uni-potsdam.de> <3C5069AF-F210-4547-970E-C6720BAA7B1E@gmx.de> <34a2635c80b94a41af835147c8c80f76@student.hpi.uni-potsdam.de> <3733C5DF-E6B0-4721-885C-5649C852E421@gmx.de> <3EF3A6EF-2F5D-4E04-A53C-4D8E53740B5C@rowledge.org> , <8F19CF2D-979F-483F-927B-768430E0E1DB@gmx.de> Message-ID: > So, anyone interested in me putting the VM stuff into the main branch? +1! :-) Best, Christoph ________________________________ Von: Squeak-dev im Auftrag von Tobias Pape Gesendet: Mittwoch, 30. September 2020 22:10:56 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] highdpi testing > On 30.09.2020, at 19:24, Thiede, Christoph wrote: > > On Windows, it appears to be a democratic thing - the majority of pixels wins in the question of DPI value. But I'm not absolutely sure about this and cannot test this right now. I don't believe it would be a good idea to override this behavior, even if we could ... Yes. Although I made it modifyable on Unix, just out of tradition that unix leaks every screw… (https://github.com/OpenSmalltalk/opensmalltalk-vm/blob/krono/highdpi-v2/platforms/unix/vm-display-X11/sqUnixX11Scale.c#L182) So, anyone interested in me putting the VM stuff into the main branch? -t -------------- next part -------------- An HTML attachment was scrubbed... URL: