Author: Anton Gulenko <anton.gule...@googlemail.com> Branch: rstrategies Changeset: r1037:ef35a189c1ff Date: 2014-08-20 16:13 +0200 http://bitbucket.org/pypy/lang-smalltalk/changeset/ef35a189c1ff/
Log: Merged. diff too long, truncating to 2000 out of 364571 lines diff --git a/images/Squeak4.5-12568.changes b/images/Squeak4.5-12568.changes deleted file mode 100644 --- a/images/Squeak4.5-12568.changes +++ /dev/null @@ -1,39 +0,0 @@ -'From Squeak4.1 of 17 April 2010 [latest update: #9957] on 17 April 2010 at 5:22:05 pm'! ----STARTUP----{17 April 2010 . 5:21:54 pm} as C:\Squeak\4.0\4.1-final\Squeak4.1.image! Smalltalk appendChangesTo: 'SqueakV41.sources'.! ----QUIT----{17 April 2010 . 5:22:11 pm} Squeak4.1.image priorSource: 89! ----STARTUP----{24 May 2010 . 8:07:26 pm} as C:\Squeak\4.2\Squeak4.1.image! ----SNAPSHOT----{24 May 2010 . 8:08:14 pm} Squeak4.2.image priorSource: 229! !HashedCollection commentStamp: 'ul 4/12/2010 22:37' prior: 0! I am an abstract collection of objects that implement hash and equality in a consitent way. This means that whenever two objects are equal, their hashes have to be equal too. If two objects are equal then I can only store one of them. Hashes are expected to be integers (preferably SmallIntegers). I also expect that the objects contained by me do not change their hashes. If that happens, hash invariants have to be re-established, which can be done by #rehash. Since I'm abstract, no instances of me should exist. My subclasses should implement #scanFor:, #fixCollisionsFrom: and #noCheckNoGrowFillFrom:. Instance Variables array: <ArrayedCollection> (typically Array or WeakArray) tally: <Integer> (non-negative) array - An array whose size is a prime number, it's non-nil elements are the elements of the collection, and whose nil elements are empty slots. There is always at least one nil. In fact I try to keep my "load" at 75% or less so that hashing will work well. tally - The number of elements in the collection. The array size is always greater than this. Implementation details: I implement a hash table which uses open addressing with linear probing as the method of collision resolution. Searching for an element or a free slot for an element is done by #scanFor: which should return the index of the slot in array corresponding to it's argument. When an element is removed #fixCollisionsFrom: should rehash all elements in array between the original index of the removed element, wrapping around after the last slot until reaching an empty slot. My maximum load factor (75%) is hardcoded in #atNewIndex:put:, so it can only be changed by overriding that method. When my load factor reaches this limit I replace my array with a larger one (see #grow) ensuring that my load factor will be less than or equal to 50%. The new array is filled by #noCheckNoGrowFillFrom: which should use #scanForEmptySlotFor: instead of #scanFor: for better performance. I do not shrink. ! !WeakKeyDictionary methodsFor: 'private' stamp: 'ul 4/12/2010 22:59'! compact "Reduce the size of array so that the load factor will be ~75%." | newCapacity | newCapacity := self class goodPrimeAtLeast: self slowSize * 4 // 3. self growTo: newCapacity! ! !Collection methodsFor: 'adding' stamp: 'ul 4/12/2010 22:33' prior: 18816249! add: newObject withOccurrences: anInteger "Add newObject anInteger times to the receiver. Do nothing if anInteger is less than one. Answer newObject." anInteger timesRepeat: [self add: newObject]. ^ newObject! ! !HashedCollection class methodsFor: 'initialize-release' stamp: 'ul 4/12/2010 23:49'! compactAll "HashedCollection compactAll" self allSubclassesDo: #compactAllInstances! ! !HashedCollection class methodsFor: 'initialize-release' stamp: 'ul 4/12/2010 23:49'! compactAllInstances "Do not use #allInstancesDo: because compact may create new instances." self allInstances do: #compact! ! !HashedCollection class methodsFor: 'sizing' stamp: 'ul 4/7/2010 00:17' prior: 55063414! goodPrimes "Answer a sorted array of prime numbers less than one billion that make good hash table sizes. Should be expanded as needed. See comments below code" ^#( 5 11 17 23 31 43 59 79 107 149 199 269 359 479 641 857 1151 1549 2069 2237 2423 2617 2797 2999 3167 3359 3539 3727 3911 4441 4787 5119 5471 5801 6143 6521 6827 7177 7517 7853 8783 9601 10243 10867 11549 12239 12919 13679 14293 15013 15731 17569 19051 20443 21767 23159 24611 25847 27397 28571 30047 31397 35771 38201 40841 43973 46633 48989 51631 54371 57349 60139 62969 70589 76091 80347 85843 90697 95791 101051 106261 111143 115777 120691 126311 140863 150523 160969 170557 181243 190717 201653 211891 221251 232591 242873 251443 282089 300869 321949 341227 362353 383681 401411 422927 443231 464951 482033 504011 562621 605779 647659 681607 723623 763307 808261 844709 886163 926623 967229 1014617 1121987 1201469 1268789 1345651 1429531 1492177 1577839 1651547 1722601 1800377 1878623 1942141 2028401 2242727 2399581 2559173 2686813 2836357 3005579 3144971 3283993 3460133 3582923 3757093 3903769 4061261 4455361 4783837 5068529 5418079 5680243 6000023 6292981 6611497 6884641 7211599 7514189 7798313 8077189 9031853 9612721 10226107 10745291 11338417 11939203 12567671 13212697 13816333 14337529 14938571 15595673 16147291 17851577 18993941 20180239 21228533 22375079 23450491 24635579 25683871 26850101 27921689 29090911 30153841 31292507 32467307 35817611 37983761 40234253 42457253 44750177 46957969 49175831 51442639 53726417 55954637 58126987 60365939 62666977 64826669 71582779 76039231 80534381 84995153 89500331 93956777 98470819 102879613 107400389 111856841 116365721 120819287 125246581 129732203 143163379 152076289 161031319 169981667 179000669 187913573 196826447 205826729 214748357 223713691 232679021 241591901 250504801 259470131 285162679 301939921 318717121 335494331 352271573 369148753 385926017 402603193 419480419 436157621 453034849 469712051 486589307 503366497 520043707 570475349 603929813 637584271 671138659 704693081 738247541 771801929 805356457 838910803 872365267 905919671 939574117 973128521 1006682977 1040137411 1073741833) "The above primes past 2069 were chosen carefully so that they do not interact badly with 1664525 (used by hashMultiply), and so that gcd(p, (256^k) +/- a) = 1, for 0<a<=32 and 0<k<=8. See Knuth's TAOCP for details." "The above primes also try to map the values of ((0 to: 4095) collect: [ :each | each << 18 \\ prime ]) sort to an equidistant sequence of numbers. This helps to avoid the collision of chains in identity-based hashed collections. To do that they were chosen to return a low value when the following block is evaluated with them as argument: [ :prime | | slots cost optimalDistance previous | slots := Array new: 4097. 0 to: 4095 do: [ :ea | slots at: ea + 1 put: ea * 262144 \\ prime ]. slots at: 4097 put: prime. slots sort. cost := 0. optimalDistance := prime // 4096. 2 to: 4097 do: [ :index | | newCost | newCost := optimalDistance - ((slots at: index) - (slots at: index - 1)). newCost > cost ifTrue: [ cost := newCost ] ]. cost ]."! ! !HashedCollection methodsFor: 'adding' stamp: 'ul 4/12/2010 22:38' prior: 53647096! add: newObject withOccurrences: anInteger "Add newObject anInteger times to the receiver. Do nothing if anInteger is less than one. Answer newObject." anInteger < 1 ifTrue: [ ^newObject ]. ^self add: newObject "I can only store an object once." ! ! !HashedCollection methodsFor: 'private' stamp: 'ul 4/12/2010 22:53'! compact "Reduce the size of array so that the load factor will be ~75%." | newCapacity | newCapacity := self class goodPrimeAtLeast: tally * 4 // 3. self growTo: newCapacity! ! !WeakSet methodsFor: 'private' stamp: 'ul 4/12/2010 22:59'! compact "Reduce the size of array so that the load factor will be ~75%." | newCapacity | newCapacity := self class goodPrimeAtLeast: self slowSize * 4 // 3. self growTo: newCapacity! ! !Symbol class methodsFor: 'class initialization' stamp: 'ul 4/13/2010 00:00' prior: 30357901! compactSymbolTable "Reduce the size of the symbol table so that it holds all existing symbols with 25% free space." | oldSize | Smalltalk garbageCollect. oldSize := SymbolTable capacity. SymbolTable compact. ^(oldSize - SymbolTable capacity) printString, ' slot(s) reclaimed'! ! KeyedIdentitySet class removeSelector: #goodPrimes! WeakIdentityKeyDictionary class removeSelector: #goodPrimes! IdentitySet class removeSelector: #goodPrimes! IdentityDictionary class removeSelector: #goodPrimes! "Collections"! !HashedCollectionTest methodsFor: 'test - class - sizing' stamp: 'ul 4/7/2010 00:18' prior: 58761579! testPrimes: primes | badPrimes | badPrimes := #(3 5 71 139 479 5861 277421). "These primes are less than the hashMultiply constant (1664525) and 1664525 \\ prime is close to 0 (mod prime). The following snippet reproduces these numbers: | hashMultiplyConstant | hashMultiplyConstant := 1 hashMultiply. (Integer primesUpTo: hashMultiplyConstant) select: [ :each | | remainder | remainder := hashMultiplyConstant \\ each. remainder <= 1 or: [ remainder + 1 = each ] ]." self assert: primes isSorted. primes do: [ :each | self assert: each isPrime. self deny: (each > 2069 and: [ badPrimes includes: each ]) ]. self assert: ( primes select: [ :p | | result | result := false. p > 2069 ifTrue: [ 1 to: 8 do: [ :k | 1 to: 32 do: [ :a | (p gcd: (256 raisedTo: k) + a) = 1 ifFalse: [ result := true ]. (p gcd: (256 raisedTo: k) - a) = 1 ifFalse: [ result := true ] ] ] ]. result ]) isEmpty.! ! HashedCollectionTest removeSelector: #testGoodPrimesForIdentityBasedHashedCollections! "CollectionsTests"! !MCMczReader methodsFor: 'as yet unclassified' stamp: 'bf 4/18/2010 18:38' prior: 22938947! extractInfoFrom: dict ^MCWorkingCopy infoFromDictionary: dict cache: self infoCache! ! !MCWorkingCopy class methodsFor: 'as yet unclassified' stamp: 'bf 4/19/2010 00:39' prior: 23215403! infoFromDictionary: aDictionary cache: cache | id | id := (aDictionary at: #id) asString. ^ cache at: id ifAbsentPut: [MCVersionInfo name: (aDictionary at: #name ifAbsent: ['']) id: (UUID fromString: id) message: (aDictionary at: #message ifAbsent: ['']) date: ([Date fromString: (aDictionary at: #date)] ifError: [nil]) time: ([Time fromString: (aDictionary at: #time)] ifError: [nil]) author: (aDictionary at: #author ifAbsent: ['']) ancestors: (self ancestorsFromArray: (aDictionary at: #ancestors ifAbsent: []) cache: cache) stepChildren: (self ancestorsFromArray: (aDictionary at: #stepChildren ifAbsent: []) cache: cache)]! ! !MCVersionInfo methodsFor: 'converting' stamp: 'bf 4/18/2010 23:25' prior: 23175569! asDictionary ^ Dictionary new at: #name put: name; at: #id put: id asString; at: #message put: message; at: #date put: date; at: #time put: time; at: #author put: author; at: #ancestors put: (self ancestors collect: [:a | a asDictionary]); yourself! ! "Monticello"! !BlockContextTest methodsFor: 'running' stamp: 'md 9/6/2005 19:56' prior: 50431957! setUp super setUp. aBlockContext := [100@100 corner: 200@200]. contextOfaBlockContext := thisContext.! ! !BehaviorTest methodsFor: 'tests' stamp: 'md 2/18/2006 16:42' prior: 17365994! testBinding self assert: Object binding value = Object. self assert: Object binding key = #Object. self assert: Object class binding value = Object class. "returns nil for Metaclasses... like Encoder>>#associationFor:" self assert: Object class binding key = nil.! ! !CompledMethodTrailerTest methodsFor: 'testing' stamp: 'Igor.Stasenko 12/13/2009 21:13' prior: 53956757! testEmbeddingSourceCode | trailer newTrailer code | trailer := CompiledMethodTrailer new. code := 'foo'. trailer sourceCode: code. newTrailer := trailer testEncoding. self assert: (trailer kind == #EmbeddedSourceQCompress ). self assert: (newTrailer sourceCode = code). "the last bytecode index must be at 0" self assert: (newTrailer endPC = 0). code := 'testEmbeddingSourceCode | trailer newTrailer code | trailer := CompiledMethodTrailer new. trailer sourceCode: code. newTrailer := trailer testEncoding. self assert: (newTrailer sourceCode = code).'. trailer sourceCode: code. self assert: (trailer kind == #EmbeddedSourceZip ). newTrailer := trailer testEncoding. self assert: (newTrailer sourceCode = code). "the last bytecode index must be at 0" self assert: (newTrailer endPC = 0). ! ! !CompledMethodTrailerTest methodsFor: 'testing' stamp: 'Igor.Stasenko 12/13/2009 21:13' prior: 53957691! testEmbeddingTempNames | trailer newTrailer code | trailer := CompiledMethodTrailer new. code := 'foo'. trailer tempNames: code. newTrailer := trailer testEncoding. self assert: (trailer kind == #TempsNamesQCompress ). self assert: (newTrailer tempNames = code). "the last bytecode index must be at 0" self assert: (newTrailer endPC = 0). code := 'testEmbeddingSourceCode | trailer newTrailer code | trailer := CompiledMethodTrailer new. trailer sourceCode: code. newTrailer := trailer testEncoding. self assert: (newTrailer sourceCode = code).'. trailer tempNames: code. self assert: (trailer kind == #TempsNamesZip ). newTrailer := trailer testEncoding. self assert: (newTrailer tempNames = code). "the last bytecode index must be at 0" self assert: (newTrailer endPC = 0). ! ! !CompledMethodTrailerTest methodsFor: 'testing' stamp: 'Igor.Stasenko 12/13/2009 21:17' prior: 53958613! testEncodingNoTrailer | trailer | trailer := CompiledMethodTrailer new. "by default it should be a no-trailer" self assert: (trailer kind == #NoTrailer ). self assert: (trailer size = 1). trailer := trailer testEncoding. self assert: (trailer kind == #NoTrailer ). self assert: (trailer size = 1). "the last bytecode index must be at 0" self assert: (trailer endPC = 0). ! ! !CompledMethodTrailerTest methodsFor: 'testing' stamp: 'Igor.Stasenko 12/13/2009 21:14' prior: 53959109! testEncodingSourcePointer | trailer | trailer := CompiledMethodTrailer new. CompiledMethod allInstancesDo: [:method | | ptr | trailer method: method. self assert: ( (ptr := method sourcePointer) == trailer sourcePointer). "the last bytecode index must be at 0" ptr ~= 0 ifTrue: [ self assert: (method endPC = trailer endPC) ]. ].! ! !CompledMethodTrailerTest methodsFor: 'testing' stamp: 'Igor.Stasenko 12/13/2009 21:15' prior: 53959564! testEncodingVarLengthSourcePointer | trailer newTrailer | trailer := CompiledMethodTrailer new. trailer sourcePointer: 1. newTrailer := trailer testEncoding. self assert: (newTrailer sourcePointer = 1). trailer sourcePointer: 16r100000000000000. newTrailer := trailer testEncoding. self assert: (newTrailer sourcePointer = 16r100000000000000). "the last bytecode index must be at 0" self assert: (newTrailer endPC = 0). ! ! !CompledMethodTrailerTest methodsFor: 'testing' stamp: 'Igor.Stasenko 12/13/2009 21:15' prior: 53960108! testSourceByIdentifierEncoding | trailer id | trailer := CompiledMethodTrailer new. id := UUID new asString. trailer sourceIdentifier: id. self assert: (trailer kind == #SourceByStringIdentifier ). trailer := trailer testEncoding. self assert: (trailer kind == #SourceByStringIdentifier ). self assert: (trailer sourceIdentifier = id). "the last bytecode index must be at 0" self assert: (trailer endPC = 0). ! ! !CompledMethodTrailerTest methodsFor: 'testing' stamp: 'Igor.Stasenko 12/13/2009 21:49' prior: 53960643! testSourceBySelectorEncoding | trailer | trailer := CompiledMethodTrailer new. trailer setSourceBySelector. self assert: (trailer kind == #SourceBySelector ). self assert: (trailer size = 1). trailer := trailer testEncoding. self assert: (trailer kind == #SourceBySelector ). self assert: (trailer size = 1). "the last bytecode index must be at 0" self assert: (trailer endPC = 0). ! ! !CategorizerTest methodsFor: 'running' stamp: 'mtf 9/10/2007 10:10' prior: 18074036! setUp categorizer := Categorizer defaultList: #(a b c d e). categorizer classifyAll: #(a b c) under: 'abc'. categorizer addCategory: 'unreal'.! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 10:17' prior: 18074267! testClassifyNewElementNewCategory categorizer classify: #f under: #nice. self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' a b c) (''unreal'') (''nice'' f) '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 10:18' prior: 18074541! testClassifyNewElementOldCategory categorizer classify: #f under: #unreal. self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' a b c) (''unreal'' f) '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 10:17' prior: 18074806! testClassifyOldElementNewCategory categorizer classify: #e under: #nice. self assert: categorizer printString = '(''as yet unclassified'' d) (''abc'' a b c) (''unreal'') (''nice'' e) '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 12:54' prior: 18075078! testClassifyOldElementOldCategory categorizer classify: #e under: #unreal. self assert: categorizer printString = '(''as yet unclassified'' d) (''abc'' a b c) (''unreal'' e) '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 10:22' prior: 18075341! testDefaultCategoryIsTransient "Test that category 'as yet unclassified' disapears when all it's elements are removed'" categorizer classifyAll: #(d e) under: #abc. self assert: categorizer printString = '(''abc'' a b c d e) (''unreal'') '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/11/2007 15:15' prior: 18075669! testNullCategory "Test that category 'as yet unclassified' disapears when all it's elements are removed'" | aCategorizer | aCategorizer := Categorizer defaultList: #(). self assert: aCategorizer printString = '(''as yet unclassified'') '. self assert: aCategorizer categories = #('no messages'). aCategorizer classify: #a under: #b. self assert: aCategorizer printString = '(''b'' a) '. self assert: aCategorizer categories = #(b).! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 12:57' prior: 18076194! testRemoveEmptyCategory categorizer removeCategory: #unreal. self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' a b c) '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 12:55' prior: 18076430! testRemoveExistingElement categorizer removeElement: #a. self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' b c) (''unreal'') '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 12:59' prior: 18076673! testRemoveNonEmptyCategory self should: [categorizer removeCategory: #abc] raise: Error. self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' a b c) (''unreal'') '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 12:59' prior: 18076950! testRemoveNonExistingCategory categorizer removeCategory: #nice. self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' a b c) (''unreal'') '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 12:57' prior: 18077203! testRemoveNonExistingElement categorizer removeElement: #f. self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' a b c) (''unreal'') '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/11/2007 14:49' prior: 18077451! testRemoveThenRename categorizer removeCategory: #unreal. categorizer renameCategory: #abc toBe: #unreal. self assert: categorizer printString = '(''as yet unclassified'' d e) (''unreal'' a b c) '! ! !CategorizerTest methodsFor: 'testing' stamp: 'mtf 9/10/2007 10:14' prior: 18077736! testUnchanged self assert: categorizer printString = '(''as yet unclassified'' d e) (''abc'' a b c) (''unreal'') '! ! "KernelTests"! !SmalltalkImage methodsFor: 'accessing' stamp: 'ul 4/18/2010 22:22'! at: key ifPresentAndInMemory: aBlock "Lookup the given key in the receiver. If it is present, answer the value of evaluating the given block with the value associated with the key. Otherwise, answer nil." ^globals at: key ifPresentAndInMemory: aBlock! ! !SmalltalkImage methodsFor: 'image' stamp: 'dtl 4/11/2010 11:45'! image "Answer the object to query about the current object memory and execution environment." ^self! ! !SmalltalkImage methodsFor: 'image' stamp: 'dtl 4/11/2010 11:47'! imageFormatVersion "Answer an integer identifying the type of image. The image version number may identify the format of the image (e.g. 32 or 64-bit word size) or specific requirements of the image (e.g. block closure support required). This invokes an optional primitive that may not be available on all virtual machines." "Smalltalk image imageFormatVersion" <primitive: 'primitiveImageFormatVersion'> self notify: 'This virtual machine does not support the optional primitive #primitiveImageFormatVersion' translated. ^''! ! !SmalltalkImage methodsFor: 'vm' stamp: 'dtl 4/11/2010 11:38'! interpreterSourceVersion "Answer a string corresponding to the version of the interpreter source. This represents the version level of the Smalltalk source code (interpreter and various plugins) that is translated to C by a CCodeGenerator, as distinct from the external platform source code, typically written in C and managed separately for each platform. An optional primitive is invoked that may not be available on all virtual machines." "Smalltalk vm interpreterSourceVersion" <primitive: 'primitiveInterpreterSourceVersion'> self notify: 'This virtual machine does not support the optional primitive #primitiveInterpreterSourceVersion' translated. ^''! ! !SmalltalkImage methodsFor: 'vm' stamp: 'dtl 4/11/2010 11:39'! platformSourceVersion "Answer a string corresponding to the version of the external platform source code, typically written in C and managed separately for each platform. This invokes an optional primitive that may not be available on all virtual machines." "Smalltalk vm platformSourceVersion" <primitive: 'primitivePlatformSourceVersion'> self notify: 'This virtual machine does not support the optional primitive #primitivePlatformSourceVersion' translated. ^''! ! !SmalltalkImage methodsFor: 'image' stamp: 'md 5/16/2006 12:34' prior: 58536670! version "Answer the version of this release." ^SystemVersion current version! ! !SmalltalkImage methodsFor: 'vm' stamp: 'dtl 4/11/2010 11:39'! versionLabel "Answer a string corresponding to the version of virtual machine. This represents the version level of the Smalltalk source code (interpreter and various plugins) that is translated to C by a CCodeGenerator, in addition to the external platform source code, typically written in C and managed separately for each platform. This invokes an optional primitive that may not be available on all virtual machines. See also vmVersion, which answers a string identifying the image from which virtual machine sources were generated." "Smalltalk vm versionLabel" <primitive: 'primitiveVMVersion'> self notify: 'This virtual machine does not support the optional primitive #primitiveVMVersion' translated. ^''! ! !SmalltalkImage methodsFor: 'vm' stamp: 'dtl 4/11/2010 11:15'! vm "Answer the object to query about virtual machine." ^self! ! !SmalltalkImage methodsFor: 'image' stamp: 'dtl 1/4/2010 21:40' prior: 58537225! wordSize "Answer the size in bytes of an object pointer or word in the object memory. The value does not change for a given image, but may be modified by a SystemTracer when converting the image to another format. The value is cached in WordSize to avoid the performance overhead of repeatedly consulting the VM." "Smalltalk wordSize" ^ WordSize ifNil: [WordSize := [SmalltalkImage current vmParameterAt: 40] on: Error do: [4]]! ! "System"! !SMLoaderPlus commentStamp: 'btr 12/1/2006 15:16' prior: 0! A simple package loader that is currently the standard UI for SqueakMap (the model is an SMSqueakMap instance). It uses ToolBuilder to construct its window. You can open one with: SMLoaderPlus open Instance Variables categoriesToFilterIds: <OrderedCollection> The set of categories to filter the packages list. filters: <OrderedCollection> The set of filters to apply to the packages list. map: <SMSqueakMap> The model SqueakMap. packagesList: <OrderedCollection> The list of packages from the map. selectedCategory: <SMCategory> The current category. selectedItem: <SMPackage> The selected package or release. window: <PluggableSystemWindow> The window, held only so we can reOpen.! !SMLoaderCategoricalPlus commentStamp: 'btr 12/4/2006 15:47' prior: 0! A variant package loader that uses a more-or-less standard Smalltalk-80 browser perspective of selecting categories in one pane and then selecting items within in the next pane. You can open one with: SMLoaderCategoricalPlus open! !SMLoader commentStamp: 'btr 11/30/2006 18:00' prior: 27913009! A simple package loader that is currently the standard UI for SqueakMap (the model is an SMSqueakMap instance). You can open one with: SMLoader open! !SMLoaderCategorical commentStamp: 'btr 12/1/2006 15:16' prior: 0! A variant package loader that uses a more-or-less standard Smalltalk-80 browser perspective of selecting categories in one pane and then selecting items within in the next pane. You can open one with: SMLoaderCategorical open! !SMLoaderCategoricalPlus class methodsFor: 'menu registration' stamp: 'btr 12/1/2006 18:06'! initialize Smalltalk at: #ToolBuilder ifPresent: [:tb | (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue: [TheWorldMenu registerOpenCommand: {self openMenuString. {self. #open}}]]! ! !SMLoaderCategoricalPlus class methodsFor: 'menu registration' stamp: 'btr 12/1/2006 17:34'! openMenuString ^ 'SqueakMap Categories'! ! !SMLoaderCategoricalPlus class methodsFor: 'menu registration' stamp: 'btr 12/1/2006 17:34'! removeFromSystem (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue: [TheWorldMenu unregisterOpenCommand: self openMenuString]. self removeFromSystem: true! ! !SMLoaderCategoricalPlus class methodsFor: 'menu registration' stamp: 'btr 12/1/2006 17:34'! unload (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue: [TheWorldMenu unregisterOpenCommand: self openMenuString].! ! !SMLoaderCategoricalPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:50'! buildFancyWith: aBuilder "Creates a variant of the window where the package pane is split between installed and uninstalled packages." | buttonBarHeight searchHeight vertDivide horizDivide | buttonBarHeight := 0.07. searchHeight := 0.07. vertDivide := 0.5. horizDivide := 0.6. builder := aBuilder. window := builder build: (builder pluggableWindowSpec new model: self; label: #label; children: (OrderedCollection new add: ((self buildButtonBarWith: builder) frame: (0 @ 0 corner: 1 @ buttonBarHeight); yourself); add: ((self buildCategoriesListWith: builder) frame: (0 @ buttonBarHeight corner: vertDivide @ horizDivide); yourself); add: ((self buildSearchPaneWith: builder) frame: (vertDivide @ buttonBarHeight corner: 1 @ (buttonBarHeight + searchHeight)); yourself); add: ((self buildNotInstalledPackagesListWith: builder) frame: (vertDivide @ (buttonBarHeight + searchHeight) corner: 1 @ (horizDivide / 2)); yourself); add: ((self buildInstalledPackagesListWith: builder) frame: (vertDivide @ (horizDivide / 2) corner: 1 @ horizDivide); yourself); add: ((self buildPackagePaneWith: builder) frame: (0 @ horizDivide corner: 1 @ 1); yourself); yourself)). window on: #mouseEnter send: #paneTransition: to: window. window on: #mouseLeave send: #paneTransition: to: window. self setUpdatablePanesFrom: #(#installedPackageList #notInstalledPackageList ). currentPackageList := #notInstalled. window extent: self initialExtent. ^ window! ! !SMLoaderCategoricalPlus methodsFor: 'interface' stamp: 'btr 12/1/2006 17:56'! buildInstalledPackagesListWith: aBuilder ^ aBuilder pluggableTreeSpec new model: self; roots: #installedPackageList; getSelectedPath: #selectedItemPath; setSelected: #selectedItem:; menu: #packagesMenu:; label: #itemLabel:; getChildren: #itemChildren:; hasChildren: #itemHasChildren:; autoDeselect: true; wantsDrop: true; yourself! ! !SMLoaderCategoricalPlus methodsFor: 'interface' stamp: 'btr 12/1/2006 17:52'! buildNotInstalledPackagesListWith: aBuilder ^ aBuilder pluggableTreeSpec new model: self; roots: #notInstalledPackageList; getSelectedPath: #selectedItemPath; setSelected: #selectedItem:; menu: #packagesMenu:; label: #itemLabel:; getChildren: #itemChildren:; hasChildren: #itemHasChildren:; autoDeselect: true; wantsDrop: true; yourself! ! !SMLoaderCategoricalPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:55'! buildWith: aBuilder | buttonBarHeight searchHeight vertDivide horizDivide | buttonBarHeight := 0.07. searchHeight := 0.07. vertDivide := 0.5. horizDivide := 0.6. builder := aBuilder. window := builder build: (builder pluggableWindowSpec new model: self; label: #label; children: (OrderedCollection new add: ((self buildButtonBarWith: builder) frame: (0 @ 0 corner: 1 @ buttonBarHeight); yourself); add: ((self buildCategoriesListWith: builder) frame: (0 @ buttonBarHeight corner: vertDivide @ horizDivide); yourself); add: ((self buildSearchPaneWith: builder) frame: (vertDivide @ buttonBarHeight corner: 1 @ (buttonBarHeight + searchHeight))); add: ((self buildPackagesListWith: builder) frame: (vertDivide @ (buttonBarHeight + searchHeight) corner: 1 @ horizDivide)); add: ((self buildPackagePaneWith: builder) frame: (0 @ horizDivide corner: 1 @ 1)); yourself)). window on: #mouseEnter send: #paneTransition: to: window. window on: #mouseLeave send: #paneTransition: to: window. window extent: self initialExtent. ^ window! ! !SMLoaderCategoricalPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 17:34'! currentPackageList ^currentPackageList! ! !SMLoaderCategoricalPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 17:34'! currentPackageList: aSymbol currentPackageList := aSymbol. self changed: #installButtonLabel.! ! !SMLoaderCategoricalPlus methodsFor: 'interface' stamp: 'btr 12/4/2006 15:55'! defaultLabel ^ 'Categorical ' , super defaultLabel! ! !SMLoaderCategoricalPlus methodsFor: 'interface' stamp: 'btr 12/4/2006 15:58'! installButtonLabel ^ self currentPackageList = #notInstalled ifTrue: ['Install the above package'] ifFalse: ['Remove the above package']! ! !SMLoaderCategoricalPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 17:52'! installedPackageList ^self packageList select: [:e | e isInstalled]! ! !SMLoaderCategoricalPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 18:02'! installedPackagesListIndex ^ self currentPackageList = #installed ifTrue: [self packagesListIndex] ifFalse: [0]! ! !SMLoaderCategoricalPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 17:34'! installedPackagesListIndex: anObject packagesListIndex := anObject. self currentPackageList ~= #installed ifTrue: [self currentPackageList: #installed. self changed: #currentPackageList]. self noteChanged! ! !SMLoaderCategoricalPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 17:34'! isOn ^false! ! !SMLoaderCategoricalPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 17:53'! notInstalledPackageList ^self packageList reject: [:e | e isInstalled]! ! !SMLoaderCategoricalPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 18:02'! notInstalledPackagesListIndex ^ self currentPackageList = #notInstalled ifTrue: [self packagesListIndex] ifFalse: [0]! ! !SMLoaderCategoricalPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 18:03'! notInstalledPackagesListIndex: anObject packagesListIndex := anObject. self currentPackageList ~= #notInstalled ifTrue: [self currentPackageList: #notInstalled. self changed: #currentPackageList]. self changed: #packagesListIndex. "update my selection" self noteChanged. self contentsChanged! ! !SMLoaderCategoricalPlus methodsFor: 'private' stamp: 'btr 12/1/2006 17:53'! noteChanged self changed: #installedPackageList. self changed: #notInstalledPackageList. super noteChanged." self changed: #packageNameList. self changed: #packagesListIndex. self changed: #categoriesForPackage. self contentsChanged."! ! !SMLoaderCategoricalPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 17:34'! packageList ^ self packages select: [:e | (e categories anySatisfy: [:cat | cat = self selectedCategory]) and: [(filters ifNil: [#()]) allSatisfy: [:currFilter | (self perform: currFilter) value: e]]]! ! !SMLoaderPlus class methodsFor: 'parts bin' stamp: 'btr 11/22/2006 15:02'! descriptionForPartsBin ^self partName: 'Package Loader' categories: #(Tools) documentation: 'SqueakMap UI' ! ! !SMLoaderPlus class methodsFor: 'class initialization' stamp: 'btr 12/1/2006 15:47'! initialize "Hook us up in the world menu." "self initialize" Smalltalk at: #ToolBuilder ifPresent: [:tb | self registerInFlapsRegistry. (Preferences windowColorFor: #SMLoader) = Color white "not set" ifTrue: [ Preferences setWindowColorFor: #SMLoader to: (Color colorFrom: self windowColorSpecification brightColor) ]. (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue: [| oldCmds | oldCmds := TheWorldMenu registry select: [:cmd | cmd first includesSubString: 'Package Loader']. oldCmds do: [:cmd | TheWorldMenu unregisterOpenCommand: cmd first]. TheWorldMenu registerOpenCommand: {self openMenuString. {self. #open}}]]. DefaultFilters := OrderedCollection new. DefaultCategoriesToFilterIds := OrderedCollection new! ! !SMLoaderPlus class methodsFor: 'new-morph participation' stamp: 'btr 11/22/2006 15:16'! initializedInstance ^ (ToolBuilder open: self new) extent: 400@400! ! !SMLoaderPlus class methodsFor: 'instance creation' stamp: 'btr 11/22/2006 15:02'! new "Create a SqueakMap loader on the default map." ^self newOn: SMSqueakMap default! ! !SMLoaderPlus class methodsFor: 'instance creation' stamp: 'btr 11/22/2006 15:02'! newOn: aMap "Create a SqueakMap loader on given map." ^super new on: aMap; yourself! ! !SMLoaderPlus class methodsFor: 'new-morph participation' stamp: 'btr 11/22/2006 15:16'! newStandAlone ^ ToolBuilder open: self new! ! !SMLoaderPlus class methodsFor: 'instance creation' stamp: 'btr 11/23/2006 11:13'! open "Create and open a SqueakMap Loader." "SMLoaderPlus open" ^ (Smalltalk at: #ToolBuilder) open: self new! ! !SMLoaderPlus class methodsFor: 'class initialization' stamp: 'btr 11/30/2006 21:50'! openMenuString ^ 'SqueakMap Catalog'! ! !SMLoaderPlus class methodsFor: 'instance creation' stamp: 'btr 11/23/2006 11:21'! openOn: aSqueakMap "Create and open a SqueakMap Loader on a given map." "self openOn: SqueakMap default" ^ (Smalltalk at: #ToolBuilder) open: (self newOn: aSqueakMap)! ! !SMLoaderPlus class methodsFor: 'new-morph participation' stamp: 'btr 11/22/2006 15:18'! prototypicalToolWindow ^ ToolBuilder open: self new; applyModelExtent; yourself! ! !SMLoaderPlus class methodsFor: 'new-morph participation' stamp: 'btr 11/22/2006 15:02'! registerInFlapsRegistry "Register the receiver in the system's flaps registry." self environment at: #Flaps ifPresent: [:cl | (cl respondsTo: #registerQuad:forFlapNamed:) ifTrue: [cl registerQuad: #(#SMLoader #prototypicalToolWindow 'Package Loader' 'The SqueakMap Package Loader' ) forFlapNamed: 'Tools']]! ! !SMLoaderPlus class methodsFor: 'class initialization' stamp: 'btr 11/30/2006 21:50'! unload (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue: [TheWorldMenu unregisterOpenCommand: self openMenuString]. self environment at: #Flaps ifPresent: [:cl | cl unregisterQuadsWithReceiver: self] ! ! !SMLoaderPlus class methodsFor: 'window color' stamp: 'btr 11/22/2006 15:02'! windowColorSpecification "Answer a WindowColorSpec object that declares my preference." ^WindowColorSpec classSymbol: self name wording: 'Package Loader' brightColor: Color yellow muchLighter duller pastelColor: Color yellow veryMuchLighter duller helpMessage: 'The SqueakMap Package Loader'! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 15:02'! addFiltersToMenu: aMenu | filterSymbol help | self filterSpecs do: [:filterArray | filterSymbol := filterArray second. help := filterArray third. aMenu addUpdating: #showFilterString: target: self selector: #toggleFilterState: argumentList: (Array with: filterSymbol). aMenu balloonTextForLastItem: help]. aMenu addLine; addList: #(('Clear all filters' uncheckFilters 'Unchecks all filters to list all packages')) ! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! addSelectedCategoryAsFilter "Add a new filter that filters on the currently selected category. Make it enabled as default." categoriesToFilterIds add: self selectedCategory id! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 16:11'! askToLoadUpdates "Check how old the map is and ask to update it if it is older than 10 days or if there is no map on disk." | available | available := map isCheckpointAvailable. (available not or: [ (Date today subtractDate: (Date fromSeconds: (map directory directoryEntryFor: map lastCheckpointFilename) modificationTime)) > 3]) ifTrue: [ (self confirm: (available ifTrue: ['The map on disk is more than 10 days old, update it from the Internet?'] ifFalse: ['There is no map on disk, fetch it from the Internet?'])) ifTrue: [self loadUpdates]]! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/1/2006 01:43'! browseCacheDirectory "Open a FileList2 on the directory for the package or release." | item dir win | item := self selectedPackageOrRelease ifNil: [^ nil]. dir := item isPackage ifTrue: [map cache directoryForPackage: item] ifFalse: [map cache directoryForPackageRelease: item]. win := FileList2 morphicViewOnDirectory: dir. "withLabel: item name, ' cache directory'." win openInWorld! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:56'! buildButtonBarWith: aBuilder ^ aBuilder pluggablePanelSpec new model: self; layout: #horizontal; children: (self commandSpecs select: [ :spec | spec fourth includes: #all] thenCollect: [ :spec | aBuilder pluggableActionButtonSpec new model: self; label: spec first; action: spec second; help: spec third; enabled: ((spec fourth includes: #item) ifTrue: [#hasSelectedItem]); yourself]); name: #buttonBar; yourself! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/22/2006 15:02'! buildButtonNamed: labelText helpText: balloon action: action | btn | btn := PluggableButtonMorph on: self getState: nil action: action. btn color: Color transparent; hResizing: #shrinkWrap; vResizing: #spaceFill; label: labelText; setBalloonText: balloon; onColor: Color transparent offColor: Color transparent. ^ btn! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:56'! buildCategoriesListWith: aBuilder "Create the hierarchical list holding the category tree." ^ aBuilder pluggableTreeSpec new model: self; roots: #categoryList; getSelectedPath: #selectedCategoryPath; getChildren: #categoryChildren:; hasChildren: #categoryHasChildren:; setSelected: #selectedCategory:; menu: #categoriesMenu:; label: #categoryLabel:; autoDeselect: true; wantsDrop: true; name: #categoriesList; yourself! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:57'! buildPackagePaneWith: aBuilder "Create the text area to the right in the loader." ^ aBuilder pluggableTextSpec new model: self; getText: #itemDescription; name: #packagePane; yourself! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:57'! buildPackagesListWith: aBuilder "Create the hierarchical list holding the packages and releases." ^ aBuilder pluggableTreeSpec new model: self; roots: #packageList; getSelectedPath: #selectedItemPath; setSelected: #selectedItem:; menu: #packagesMenu:; label: #itemLabel:; getChildren: #itemChildren:; hasChildren: #itemHasChildren:; autoDeselect: true; wantsDrop: true; name: #packagesList; yourself! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:57'! buildSearchPaneWith: aBuilder ^ aBuilder pluggableInputFieldSpec new model: self; selection: #searchSelection; getText: #searchText; setText: #findPackage:notifying:; name: #search; yourself! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 06:54'! buildWith: aBuilder "Create the package loader window." | buttonBarHeight vertDivide horizDivide | buttonBarHeight := 0.07. vertDivide := 0.6. horizDivide := 0.3. builder := aBuilder. window := builder build: (builder pluggableWindowSpec new model: self; label: #label; children: (OrderedCollection new add: ((self buildButtonBarWith: builder) frame: (0 @ 0 corner: 1 @ buttonBarHeight)); add: ((self buildSearchPaneWith: builder) frame: (0 @ buttonBarHeight corner: horizDivide @ (buttonBarHeight * 2))); add: ((self buildPackagesListWith: builder) frame: (0 @ (buttonBarHeight * 2) corner: horizDivide @ vertDivide)); add: ((self buildCategoriesListWith: builder) frame: (0 @ vertDivide corner: horizDivide @ 1)); add: ((self buildPackagePaneWith: builder) frame: (horizDivide @ buttonBarHeight corner: 1 @ 1)); yourself); yourself). window on: #mouseEnter send: #paneTransition: to: window. window on: #mouseLeave send: #paneTransition: to: window. window extent: self initialExtent. ^ window! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 12/1/2006 01:38'! cachePackageReleaseAndOfferToCopy "Cache package release, then offer to copy it somewhere. Answer the chosen file's location after copy, or the cache location if no directory was chosen." | release installer newDir newName newFile oldFile oldName | release := self selectedPackageOrRelease. release isPackageRelease ifFalse: [ self error: 'Should be a package release!!']. installer := SMInstaller forPackageRelease: release. [UIManager default informUser: 'Caching ' , release asString during: [installer cache]] on: Error do: [:ex | | msg | msg := ex messageText ifNil: [ex asString]. self informException: ex msg: ('Error occurred during download:\', msg, '\') withCRs. ^nil ]. installer isCached ifFalse: [self inform: 'Download failed, see transcript for details'. ^nil]. oldName := installer fullFileName. newDir := FileList2 modalFolderSelector: installer directory. newDir ifNil: [ ^oldName ]. newDir = installer directory ifTrue: [ ^oldName ]. newName := newDir fullNameFor: installer fileName. newFile := FileStream newFileNamed: newName. newFile ifNil: [ ^oldName ]. newFile binary. oldFile := FileStream readOnlyFileNamed: oldName. oldFile ifNil: [ ^nil ]. oldFile binary. [[ newDir copyFile: oldFile toFile: newFile ] ensure: [ oldFile close. newFile close ]] on: Error do: [ :ex | ^oldName ]. ^newName! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 15:02'! categoriesMenu: aMenu "Answer the categories-list menu." self selectedCategory ifNotNil: [aMenu addList: self categorySpecificOptions; addLine]. aMenu addList: self generalOptions. self addFiltersToMenu: aMenu. ^aMenu! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/24/2006 14:44'! categoryChildren: aCategory ^ aCategory subCategories! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/24/2006 14:45'! categoryHasChildren: aCategory ^ aCategory hasSubCategories! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/24/2006 14:46'! categoryLabel: aCategory ^ aCategory name! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 11/30/2006 21:01'! categoryList "Create the category list for the hierarchical list. We sort the categories by name but ensure that 'Squeak versions' is first if it exists." | list first | list := (map categories select: [:each | each parent isNil]) asArray sort: [:c1 :c2 | c1 name <= c2 name]. first := list detect: [:any | any name = 'Squeak versions'] ifNone: []. first ifNotNil: [list := list copyWithout: first. list := {first} , list]. ^ list! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 15:02'! categorySpecificOptions | choices | choices := OrderedCollection new. (categoriesToFilterIds includes: self selectedCategory id) ifTrue: [ choices add: #('Remove filter' #removeSelectedCategoryAsFilter 'Remove the filter for the selected category.')] ifFalse: [ choices add: #('Add as filter' #addSelectedCategoryAsFilter 'Add the selection as a filter to hide unrelated packages.')]. categoriesToFilterIds isEmpty ifFalse: [ choices add: #('Remove all filters' #removeCategoryFilters 'Remove all category filters.')]. ^ choices! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 11/22/2006 15:02'! changeFilters: anObject "Update my selection." | oldItem index | oldItem := self selectedPackageOrRelease. filters := anObject. self packagesListIndex: ((index := self packageList indexOf: oldItem) ifNil: [0] ifNotNil: [index]). self noteChanged! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 18:01'! commandSpecFor: selector ^ self commandSpecs detect: [:spec | spec second = selector]! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 18:00'! commandSpecs ^ #(('Install' installPackageRelease 'Install the latest version from the server.' (item all)) ('Email' emailPackageMaintainers 'Open an editor to send an email to the owner and co-maintainers of this package.' (item all)) ('Browse cache' browseCacheDirectory 'Browse cache directory of the selection.' (item all)) ('Copy from cache' cachePackageReleaseAndOfferToCopy 'Download selected release into cache first if needed, and then offer to copy it somewhere else.' (item)) ('Force download into cache' downloadPackageRelease 'Force a download of the selected release into the cache.' (item)) ('Update' loadUpdates 'Update the package index from the servers.' (all)) ('Upgrade All' upgradeInstalledPackagesConfirm 'Upgrade all installed packages (conf8irming each).' (all)) ('Upgrade all installed packages' upgradeInstalledPackagesNoConfirm '' (item)) ('Upgrade all installed packages confirming each' upgradeInstalledPackagesConfirm '' (item)) ('Copy list' listInPasteBuffer 'Puts the list as text into the clipboard.' (all)) ('Save filters' saveFiltersAsDefault 'Saves the current filters as default.' (all)) ('Help' help 'What is this?' (all)))! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/22/2006 15:02'! defaultButtonPaneHeight "Answer the user's preferred default height for new button panes." ^ Preferences parameterAt: #defaultButtonPaneHeight ifAbsentPut: [25]! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 01:50'! defaultLabel ^ 'SqueakMap Package Loader'! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 12/1/2006 01:38'! downloadPackageRelease "Force a download of the selected package release into the cache." | release | release := self selectedPackageOrRelease. release isPackageRelease ifFalse: [ self error: 'Should be a package release!!']. [UIManager default informUser: 'Downloading ' , release asString during: [ (SMInstaller forPackageRelease: release) download] ] on: Error do: [:ex | | msg | msg := ex messageText ifNil: [ex asString]. self informException: ex msg: ('Error occurred during download:\', msg, '\') withCRs]! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! emailPackageMaintainers "Send mail to package owner and co-maintainers." | item package toAddresses | item := self selectedPackageOrRelease ifNil: [^ nil]. package := item isPackageRelease ifTrue: [item package] ifFalse: [item]. "(this logic should be moved to MailMessage as soon as it can handle multiple To: addresses)" toAddresses := '<', package owner email, '>'. package maintainers ifNotNil: [ package maintainers do: [:maintainer | toAddresses := toAddresses, ', <', maintainer email, '>']]. SMUtilities sendMailTo: toAddresses regardingPackageRelease: item! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! filterAdd: anObject self changeFilters: (self filters copyWith: anObject) ! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 11/22/2006 15:02'! filterAutoInstall ^[:package | package isInstallable]! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 12/1/2006 01:42'! filterAvailable ^[:package | package isAvailable]! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 11/22/2006 15:02'! filterInstalled ^[:package | package isInstalled]! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 11/22/2006 15:02'! filterNotInstalledYet ^[:package | package isInstalled not]! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 12/1/2006 01:42'! filterNotUptoDate ^[:package | package isAvailable]! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 11/22/2006 15:02'! filterPublished ^[:package | package isPublished]! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! filterRemove: anObject self changeFilters: (self filters copyWithout: anObject) ! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 12/1/2006 01:43'! filterSafelyAvailable ^[:package | package isSafelyAvailable]! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/30/2006 21:07'! filterSpecs "Return a specification for the filter menu. Is called each time." | specs | specs := #(#('Auto-installable packages' #filterAutoInstall 'display only packages that can be installed automatically') #('New available packages' #filterAvailable 'display only packages that are not installed or that have newer releases available.') #('New safely-available packages' #filterSafelyAvailable 'display only packages that are not installed or that have newer releases available that are safe to install, meaning that they are published and meant for the current version of Squeak.') #('Installed packages' #filterInstalled 'Display only packages that are installed.') #('Published packages' #filterPublished 'Display only packages that have at least one published release.') ) asOrderedCollection. categoriesToFilterIds do: [:catId | specs add: {'Packages in ' , (map object: catId) name. catId. 'Display only packages that are in the category.'}]. ^ specs! ! !SMLoaderPlus methodsFor: 'filters' stamp: 'btr 12/1/2006 01:43'! filterVersion "Ignore spaces in the version string, they're sometimes spurious. Not used anymore." ^[:package | package categories anySatisfy: [:cat | (cat name, '*') match: (Smalltalk version copyWithout: $ ) ]]! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! filters ^filters! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/24/2006 13:49'! findPackage: aString notifying: aView "Search and select a package with the given (sub) string in the name or description. " | index list match descriptions | match := aString asString asLowercase. index := self packagesListIndex. list := self packageNameList. list isEmpty ifTrue: [^ self]. descriptions := self packageList collect: [:e | e description]. index + 1 to: list size do: [:i | (((list at: i) includesSubstring: match caseSensitive: false) or: [(descriptions at: i) includesSubstring: match caseSensitive: false]) ifTrue: [^ self packagesListIndex: i]]. "wrap around" 1 to: index do: [:i | (((list at: i) includesSubstring: match caseSensitive: false) or: [(descriptions at: i) includesSubstring: match caseSensitive: false]) ifTrue: [^ self packagesListIndex: i]]. self inform: 'No package matching ' , aString asString! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 15:02'! generalOptions ^#( #('Upgrade all installed packages' upgradeInstalledPackagesNoConfirm) #('Upgrade all installed packages confirming each' upgradeInstalledPackagesConfirm) #('Put list in paste buffer' listInPasteBuffer) #('Save filters as default' saveFiltersAsDefault) #- ) ! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 11/22/2006 18:36'! hasSelectedItem ^ self selectedPackageOrRelease notNil! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/1/2006 01:44'! help "Present help text. If there is a web server available, offer to open it. Use the WebBrowser registry if possible, or Scamper if available." | message browserClass | message := 'Welcome to the SqueakMap package loader. The names of packages are followed by versions: (installed -> latest). If there is no arrow, your installed version of the package is the latest. Bold packages and releases have been installed. The checkbox menu items modify which packages you''ll see. Take a look at them - only some packages are shown initially. The options available for a package depend on how it was packaged. Comment on a package by emailing the author or the squeak list.'. browserClass := Smalltalk at: #WebBrowser ifPresent: [ :registry | registry default ]. browserClass := browserClass ifNil: [ Smalltalk at: #Scamper ifAbsent: [ ^self inform: message ]]. (self confirm: message, ' Would you like to view more detailed help on the SqueakMap swiki page?') ifTrue: [ browserClass openOnUrl: 'http://wiki.squeak.org/2726' asUrl]! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 11/22/2006 15:02'! informException: ex msg: msg "Tell the user that an error has occurred. Offer to open debug notifier." (self confirm: msg, 'Would you like to open a debugger?') ifTrue: [ex pass]! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/5/2006 05:28'! initialExtent ^500@400! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! installPackageRelease "Install selected package or release. The cache is used." | item release | item := self selectedPackageOrRelease ifNil: [^ nil]. item isPackageRelease ifTrue: [ (item isPublished or: [self confirm: 'Selected release is not published yet, install anyway?']) ifTrue: [^self installPackageRelease: item]] ifFalse: [ release := item lastPublishedReleaseForCurrentSystemVersion. release ifNil: [ (self confirm: 'The package has no published release for your Squeak version, try releases for any Squeak version?') ifTrue: [ release := item lastPublishedRelease. release ifNil: [ (self confirm: 'The package has no published release at all, take the latest of the unpublished releases?') ifTrue: [release := item lastRelease]]]]. release ifNotNil: [^self installPackageRelease: release]]! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 12/1/2006 01:53'! installPackageRelease: aRelease "Install a package release. The cache is used." | myRelease installer | aRelease isCompatibleWithCurrentSystemVersion ifFalse: [(self confirm: 'The package you are about to install is not listed as being compatible with your image version (', SystemVersion current majorMinorVersion, '), so the package may not work properly. Do you still want to proceed with the install?') ifFalse: [^ self]]. myRelease := self installedReleaseOfMe. installer := SMInstaller forPackageRelease: aRelease. [UIManager default informUser: 'Downloading ' , aRelease asString during: [installer download]. UIManager default informUser: 'Installing ' , aRelease asString during: [ installer install. myRelease = self installedReleaseOfMe ifFalse: [self reOpen] ifTrue: [self noteChanged]] ] on: Error do: [:ex | | msg | msg := ex messageText ifNil:[ex asString]. self informException: ex msg: ('Error occurred during install:\', msg, '\') withCRs].! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 11/22/2006 15:02'! installedReleaseOfMe "Return the release of the installed package loader." ^SMSqueakMap default installedReleaseOf: (SMSqueakMap default packageWithId: '941c0108-4039-4071-9863-a8d7d2b3d4a3').! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/24/2006 14:44'! itemChildren: anItem ^ anItem isPackage ifTrue: [anItem releases] ifFalse: [#()]! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 11/22/2006 19:56'! itemDescription ^ self selectedPackageOrRelease ifNil: ['<No package selected>'] ifNotNilDo: [:item | item fullDescription]! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/24/2006 14:45'! itemHasChildren: anItem ^ anItem isPackage and: [anItem releases notEmpty]! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/1/2006 01:44'! itemLabel: anItem | label | label := anItem isPackage ifTrue: [anItem name , (anItem versionLabel ifEmpty: [''] ifNotEmptyDo: [:lbl | ' (' , anItem versionLabel , ')'])] ifFalse: [anItem smartVersion]. ^ anItem isInstalled ifTrue: [label asText allBold] ifFalse: [label]! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 11/24/2006 17:17'! label ^ self labelForShown: (packagesList ifNil: [self packageList])! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! labelForFilter: aFilterSymbol ^(self filterSpecs detect: [:fs | fs second = aFilterSymbol]) first! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 01:50'! labelForShown: packagesShown "Update the label of the window." ^ self defaultLabel , ' (', (packagesShown size < map packages size ifTrue: [packagesShown size printString, ' shown out of '] ifFalse: ['']) , map packages size printString, ' packages)'! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! listInPasteBuffer "Useful when talking with people etc. Uses the map to produce a nice String." Clipboard clipboardText: (String streamContents: [:s | packagesList do: [:p | s nextPutAll: p nameWithVersionLabel; cr ]]) asText! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 12/1/2006 01:31'! loadUpdates [UIManager default informUser: 'Loading Updates' during: [ map loadUpdates. self noteChanged ] ] on: Error do: [:ex | self informException: ex msg: ('Error occurred when updating map:\', ex messageText, '\') withCRs]! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 11/24/2006 14:05'! noteChanged filters ifNil: [^ self reOpen]. map ifNotNil: [packagesList := nil. selectedCategory := nil. self changed: #categoryList. self changed: #packageList. self changed: #packagesListIndex. "update my selection" self contentsChanged]! ! !SMLoaderPlus methodsFor: 'initialization' stamp: 'btr 11/22/2006 16:11'! on: aSqueakMap "Initialize instance." map := aSqueakMap. map synchWithDisk. filters := DefaultFilters copy. categoriesToFilterIds := DefaultCategoriesToFilterIds copy. self askToLoadUpdates! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! package: aPackage filteredByCategory: aCategory "Answer true if the package should be shown if we filter on <aCategory>. It should be shown if itself or any of its releases has the category." | releases | releases := aPackage releases. ^(aPackage hasCategoryOrSubCategoryOf: aCategory) or: [ releases anySatisfy: [:rel | rel hasCategoryOrSubCategoryOf: aCategory]]! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 01:49'! packageList "Return a list of the SMPackages that should be visible by applying all the filters. Also filter based on the currently selected category - if any." | list | list := packagesList ifNil: [packagesList := self packageListCalculated]. selectedCategory ifNotNil: [ list := list select: [:each | self package: each filteredByCategory: selectedCategory]]. self updateLabel: list. ^ list! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 01:49'! packageListCalculated "Return a list of the SMPackages that should be visible by applying all the filters. Also filter based on the currently selected category - if any." ^ self packages select: [:p | filters allSatisfy: [:currFilter | currFilter isSymbol ifTrue: [(self perform: currFilter) value: p] ifFalse: [self package: p filteredByCategory: (map object: currFilter)]]]! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 01:50'! packageNameList ^ self packageList collect: [:e | e name]! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 18:30'! packageSpecificOptions | choices packageOrRelease | packageOrRelease := self selectedPackageOrRelease. choices := OrderedCollection new. packageOrRelease isInstallable ifTrue: [ choices add: (self commandSpecFor: #installPackageRelease)]. (packageOrRelease isDownloadable and: [packageOrRelease isCached]) ifTrue: [ choices add: (self commandSpecFor: #browseCacheDirectory)]. (packageOrRelease isPackageRelease and: [packageOrRelease isDownloadable]) ifTrue: [ choices add: (self commandSpecFor: #cachePackageReleaseAndOfferToCopy). choices add: (self commandSpecFor: #downloadPackageRelease)]. choices add: (self commandSpecFor: #emailPackageMaintainers). ^ choices! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 11/22/2006 16:11'! packages "We request the packages as sorted by name by default." ^map packagesByName asArray ! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 11/24/2006 14:01'! packagesListIndex ^ self packageList indexOf: self selectedItem! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 11/24/2006 14:01'! packagesListIndex: anObject self selectedItem: (anObject = 0 ifFalse: [self packageList at: anObject])! ! !SMLoaderPlus methodsFor: 'menus' stamp: 'btr 11/22/2006 15:02'! packagesMenu: aMenu "Answer the packages-list menu." self selectedPackageOrRelease ifNotNil: [aMenu addList: self packageSpecificOptions; addLine]. aMenu addList: self generalOptions. self addFiltersToMenu: aMenu. ^aMenu! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 12/1/2006 01:45'! perform: selector orSendTo: otherTarget "Selector was just chosen from a menu by a user. If can respond, then perform it on myself. If not, send it to otherTarget, presumably the editPane from which the menu was invoked." ^ (self respondsTo: selector) ifTrue: [self perform: selector] ifFalse: [super perform: selector orSendTo: otherTarget]! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 11/26/2006 23:22'! reOpen "Close this package loader, probably because it has been updated, and open a new one." self inform: 'This package loader has been upgraded and will be closed and reopened to avoid strange side effects.'. window delete. (Smalltalk at: self class name) open! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! removeCategoryFilters "Remove all category filters." categoriesToFilterIds := OrderedCollection new! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! removeSelectedCategoryAsFilter "Remove the filter that filters on the currently selected category." categoriesToFilterIds remove: self selectedCategory id! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! saveFiltersAsDefault "Save the current filters as default so that they are selected the next time the loader is opened." DefaultFilters := filters copy. DefaultCategoriesToFilterIds := categoriesToFilterIds copy! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/24/2006 14:35'! searchSelection "Selects all of the default search text so that a type-in overwrites it." ^ {1. self searchText size}! ! !SMLoaderPlus methodsFor: 'interface' stamp: 'btr 11/24/2006 14:35'! searchText "A dummy default search text so that the field describes its purpose." ^ 'Search packages'! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 11/24/2006 14:02'! selectedCategory "Return selected category." ^ selectedCategory! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 16:37'! selectedCategory: anSMCategory "Change the selected category." selectedCategory := anSMCategory. selectedCategory ifNotNil: [(selectedCategory objects includes: self selectedItem) ifFalse: [self selectedItem: nil]]. self changed: #selectedCategory. self changed: #packageList! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 11/24/2006 14:52'! selectedCategoryPath "Return selected category's path." | path | path := #(). selectedCategory ifNotNil: [selectedCategory parent ifNotNilDo: [:p | path := path copyWith: p]. path := path copyWith: selectedCategory]. ^ path collect: [:cat | self categoryLabel: cat]! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 11/24/2006 14:02'! selectedItem ^ selectedItem! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 16:27'! selectedItem: anItem "This == workaround protects us from recursion since ToolBuilder's tree widgets will always tell us that the selection has been updated when we tell it that the selection path has been updated. Cleaner solutions invited." anItem == selectedItem ifFalse: [ selectedItem := anItem. self changed: #selectedItemPath. self changed: #itemDescription. self changed: #hasSelectedItem]! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 12/1/2006 16:16'! selectedItemPath | path | path := #(). (selectedItem isKindOf: SMPackageRelease) ifTrue: [path := path copyWith: selectedItem package]. selectedItem ifNotNil: [path := path copyWith: selectedItem]. ^ path! ! !SMLoaderPlus methodsFor: 'accessing' stamp: 'btr 11/24/2006 14:03'! selectedPackageOrRelease "Return selected package or package release." ^ selectedItem! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! showFilterString: aFilterSymbol ^(self stateForFilter: aFilterSymbol), (self labelForFilter: aFilterSymbol)! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! stateForFilter: aFilterSymbol ^(self filters includes: aFilterSymbol) ifTrue: ['<yes>'] ifFalse: ['<no>'] ! ! !SMLoaderPlus methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 15:02'! toggleFilterState: aFilterSymbol ^(self filters includes: (aFilterSymbol)) ifTrue: [self filterRemove: aFilterSymbol] ifFalse: [self filterAdd: aFilterSymbol]! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! uncheckFilters "Uncheck all filters." filters := OrderedCollection new. self noteChanged! ! !SMLoaderPlus methodsFor: 'lists' stamp: 'btr 12/1/2006 01:50'! updateLabel: packagesShown "Update the label of the window." window ifNotNilDo: [:w | w setLabel: (self labelForShown: packagesShown)]! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 12/1/2006 01:29'! upgradeInstalledPackages "Tries to upgrade all installed packages to the latest published release for this version of Squeak. So this is a conservative approach." | installed old myRelease toUpgrade info | installed := map installedPackages. old := map oldPackages. old isEmpty ifTrue: [ ^self inform: 'All ', installed size printString, ' installed packages are up to date.']. toUpgrade := map upgradeableAndOldPackages. toUpgrade isEmpty ifTrue: [ ^self inform: 'None of the ', old size printString, ' old packages of the ', installed size printString, ' installed can be automatically upgraded. You need to upgrade them manually.']. info := old size < toUpgrade size ifTrue: [ 'Of the ', old size printString, ' old packages only ', toUpgrade size printString, ' can be upgraded. The following packages will not be upgraded: ', (String streamContents: [:s | (old removeAll: toUpgrade; yourself) do: [:p | s nextPutAll: p nameWithVersionLabel; cr]])] ifFalse: ['All old packages upgradeable.']. (self confirm: info, ' About to upgrade the following packages: ', (String streamContents: [:s | toUpgrade do: [:p | s nextPutAll: p nameWithVersionLabel; cr]]), 'Proceed?') ifTrue: [ myRelease := self installedReleaseOfMe. [UIManager default informUser: 'Upgrading Installed Packages' during: [ map upgradeOldPackages. self inform: toUpgrade size printString, ' packages successfully upgraded.'. myRelease = self installedReleaseOfMe ifFalse: [self reOpen] ifTrue: [self noteChanged]] ] on: Error do: [:ex | self informException: ex msg: ('Error occurred when upgrading old packages:\', ex messageText, '\') withCRs]]! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! upgradeInstalledPackagesConfirm "Tries to upgrade all installed packages to the latest published release for this version of Squeak. Confirms on each upgrade." ^ self upgradeInstalledPackagesConfirm: true! ! !SMLoaderPlus methodsFor: 'private' stamp: 'btr 12/1/2006 01:29'! upgradeInstalledPackagesConfirm: confirmEach "Tries to upgrade all installed packages to the latest published release for this version of Squeak. If confirmEach is true we ask for every upgrade. " | installed old myRelease toUpgrade info | installed := map installedPackages. old := map oldPackages. old isEmpty ifTrue: [^ self inform: 'All ' , installed size printString , ' installed packages are up to date.']. toUpgrade := map upgradeableAndOldPackages. toUpgrade isEmpty ifTrue: [^ self inform: 'None of the ' , old size printString , ' old packages of the ' , installed size printString , ' installed can be automatically upgraded. You need to upgrade them manually.']. info := old size < toUpgrade size ifTrue: ['Of the ' , old size printString , ' old packages only ' , toUpgrade size printString , ' can be upgraded. The following packages will not be upgraded: ' , (String streamContents: [:s | (old removeAll: toUpgrade; yourself) do: [:p | s nextPutAll: p nameWithVersionLabel; cr]])] ifFalse: ['All old packages upgradeable.']. (self confirm: info , ' About to upgrade the following packages: ' , (String streamContents: [:s | toUpgrade do: [:p | s nextPutAll: p nameWithVersionLabel; cr]]) , 'Proceed?') ifTrue: [myRelease := self installedReleaseOfMe. [UIManager default informUser: 'Upgrading Installed Packages' during: [confirmEach ifTrue: [map upgradeOldPackagesConfirmBlock: [:p | self confirm: 'Upgrade ' , p installedRelease packageNameWithVersion , ' to ' , (p lastPublishedReleaseForCurrentSystemVersionNewerThan: p installedRelease) listName , '?']] ifFalse: [map upgradeOldPackages]. self inform: toUpgrade size printString , ' packages successfully processed.'. myRelease = self installedReleaseOfMe ifTrue: [self noteChanged] ifFalse: [self reOpen]]] on: Error do: [:ex | self informException: ex msg: ('Error occurred when upgrading old packages:\' , ex messageText , '\') withCRs]]! ! !SMLoaderPlus methodsFor: 'actions' stamp: 'btr 11/22/2006 15:02'! upgradeInstalledPackagesNoConfirm "Tries to upgrade all installed packages to the latest published release for this version of Squeak. No confirmation on each upgrade." ^ self upgradeInstalledPackagesConfirm: false! ! !SMPackageWrapper methodsFor: 'comparing' stamp: 'dvf 9/21/2003 16:25' prior: 27998626! = anObject ^self withoutListWrapper = anObject withoutListWrapper! ! !SMPackageWrapper methodsFor: 'converting' stamp: 'btr 11/22/2006 00:54' prior: 27998778! asString | string | string := item name, ' (', item versionLabel, ')'. item isInstalled ifTrue: [string := string asText allBold]. "(string includesSubString: '->') ifTrue: [string := string asText color: Color green]." ^ string! ! !SMPackageWrapper methodsFor: 'accessing' stamp: 'dvf 10/14/2003 18:58' prior: 27998902! contents ^item releases reversed collect: [:e | SMPackageReleaseWrapper with: e]! ! !SMPackageWrapper methodsFor: 'testing' stamp: 'dvf 9/21/2003 16:25' prior: 27999070! hash ^self withoutListWrapper hash! ! !SMPackageWrapper methodsFor: 'accessing' stamp: 'btr 11/22/2006 16:55'! help ^ 'This shows all packages with their releases that should be displayed according the current filter.'! ! !SMPackageWrapper methodsFor: 'accessing' stamp: 'btr 11/22/2006 16:49'! label ^ self asString! ! !SMPackageWrapper methodsFor: 'printing' stamp: 'dvf 9/21/2003 16:22' prior: 27999192! printOn: aStream aStream nextPutAll: 'wrapper for: ', item printString! ! !SMCategoryWrapper methodsFor: 'comparing' stamp: 'ar 2/9/2004 02:13' prior: 27849043! = anObject ^self withoutListWrapper = anObject withoutListWrapper! ! !SMCategoryWrapper methodsFor: 'converting' stamp: 'btr 11/30/2006 18:53' prior: 27849195! asString ^ item name , ' (' , self numberOfObjects printString , ')'! ! !SMCategoryWrapper methodsFor: 'accessing' stamp: 'ar 2/9/2004 02:35' prior: 27849301! category ^item! ! !SMCategoryWrapper methodsFor: 'accessing' stamp: 'btr 11/30/2006 21:02' prior: 27849402! contents ^ item subCategories collect: [:n | self class with: n model: n]! ! !SMCategoryWrapper methodsFor: 'model access' stamp: 'btr 11/30/2006 21:02'! getList ^ Array with: (self class with: self contents model: model)! ! !SMCategoryWrapper methodsFor: 'testing' stamp: 'btr 11/30/2006 18:53'! hasContents ^ item hasSubCategories! ! !SMCategoryWrapper methodsFor: 'comparing' stamp: 'ar 2/9/2004 02:13' prior: 27849700! hash ^self withoutListWrapper hash! ! !SMCategoryWrapper methodsFor: 'accessing' stamp: 'btr 11/22/2006 16:56'! help ^ 'The categories are structured in a tree. Packages and package releases belong to several categories. You can add one or more categories as filters and enable them in the menu.'! ! !SMCategoryWrapper methodsFor: 'accessing' stamp: 'BJP 11/22/2002 14:17'! model ^model! ! !SMCategoryWrapper methodsFor: 'accessing' stamp: 'btr 11/30/2006 18:53'! numberOfObjects " | total | total _ 0. model allCategoriesDo: [:c | total _ total + c objects size]. ^total" ^item objects size! ! !SMPackageReleaseWrapper methodsFor: 'converting' stamp: 'btr 11/30/2006 21:30' prior: 27997393! asString "Show installed releases with a trailing asterisk." | string | string := item smartVersion. "Older SMBase versions don't have isInstalled.'" (item respondsTo: #isInstalled) ifTrue: [item isInstalled ifTrue: [string := (string , ' *') asText allBold]]. ^ string! ! !SMPackageReleaseWrapper methodsFor: 'accessing' stamp: 'btr 11/22/2006 17:14'! contents ^ #()! ! !SMPackageReleaseWrapper methodsFor: 'accessing' stamp: 'btr 11/22/2006 16:49'! label ^ self asString ! ! !SMLoader class methodsFor: 'class initialization' stamp: 'btr 12/1/2006 15:47' prior: 27944626! initialize "Hook us up in the world menu." "self initialize" Smalltalk at: #ToolBuilder ifAbsent: [self registerInFlapsRegistry. (Preferences windowColorFor: #SMLoader) = Color white ifTrue: ["not set" Preferences setWindowColorFor: #SMLoader to: (Color colorFrom: self windowColorSpecification brightColor)]. (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue: [| oldCmds | oldCmds := TheWorldMenu registry select: [:cmd | cmd first includesSubString: 'Package Loader']. oldCmds do: [:cmd | TheWorldMenu unregisterOpenCommand: cmd first]. TheWorldMenu registerOpenCommand: {self openMenuString. {self. #open}}]]. DefaultFilters := OrderedCollection new. DefaultCategoriesToFilterIds := OrderedCollection new! ! !SMLoader class methodsFor: 'class initialization' stamp: 'btr 11/30/2006 21:52'! openMenuString ^ 'SqueakMap Catalog'! ! !SMLoader class methodsFor: 'class initialization' stamp: 'btr 11/30/2006 21:52' prior: 27945298! unload (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue: [TheWorldMenu unregisterOpenCommand: self openMenuString]. self environment at: #Flaps ifPresent: [:cl | cl unregisterQuadsWithReceiver: self] ! ! !SMLoader methodsFor: 'menus' stamp: 'btr 11/21/2006 16:08' prior: 54331069! addFiltersToMenu: aMenu | filterSymbol help | self filterSpecs do: [:filterArray | filterSymbol := filterArray second. help := filterArray third. aMenu addUpdating: #showFilterString: target: self selector: #toggleFilterState: argumentList: (Array with: filterSymbol). aMenu balloonTextForLastItem: help]. aMenu addLine; addList: #(('Clear all filters' uncheckFilters 'Unchecks all filters to list all packages')) ! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/22/2006 01:15' prior: 27927912! browseCacheDirectory "Open a FileList2 on the directory for the package or release." | item dir win | item := self selectedPackageOrRelease ifNil: [^ nil]. item ifNil: [^nil]. dir := item isPackage ifTrue: [model cache directoryForPackage: item] ifFalse: [model cache directoryForPackageRelease: item]. win := FileList2 morphicViewOnDirectory: dir. " withLabel: item name, ' cache directory'." win openInWorld ! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/22/2006 14:52'! buildButtonBar | aRow btn | aRow := AlignmentMorph newRow beSticky. aRow color: Color transparent; clipSubmorphs: true. self buttonSpecs do: [:spec | btn := self buildButtonNamed: spec first helpText: spec third action: spec second. aRow addMorphBack: btn] separatedBy: [aRow addTransparentSpacerOfSize: 3@0]. ^ aRow! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/22/2006 01:27'! buildButtonNamed: labelText helpText: balloon action: action | btn | btn := PluggableButtonMorph on: self getState: nil action: action. btn color: Color transparent; hResizing: #shrinkWrap; vResizing: #spaceFill; label: labelText; setBalloonText: balloon; onColor: Color transparent offColor: Color transparent. ^ btn! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/30/2006 19:04' prior: 27928394! buildMorphicCategoriesList "Create the hierarchical list holding the category tree." | list | list := (SimpleHierarchicalListMorph on: self list: #categoryWrapperList selected: #selectedCategoryWrapper changeSelected: #selectedCategoryWrapper: menu: #categoriesMenu: keystroke: nil) autoDeselect: true; enableDrag: false; enableDrop: true; yourself. list setBalloonText: 'The categories are structured in a tree. Packages and package releases belong to several categories. You can add one or more categories as filters and enable them in the menu.'. "list scroller submorphs do:[:each| list expandAll: each]." list adjustSubmorphPositions. ^ list! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/22/2006 00:22' prior: 27929139! buildMorphicPackagesList "Create the hierarchical list holding the packages and releases." ^(SimpleHierarchicalListMorph on: self list: #packageWrapperList selected: #selectedItemWrapper changeSelected: #selectedItemWrapper: menu: #packagesMenu: keystroke: nil) autoDeselect: false; enableDrag: false; enableDrop: true; setBalloonText: 'This shows all packages with their releases that should be displayed according the current filter.'; yourself! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/30/2006 21:13'! buildPackageButtonBar | aRow | "Somewhat patterned after IRCe's buttonRow method." aRow := AlignmentMorph newRow beSticky. aRow color: Color transparent; clipSubmorphs: true. ^ aRow! ! !SMLoader methodsFor: 'interface' stamp: 'gk 5/5/2006 02:05' prior: 27929686! buildPackagePane "Create the text area to the right in the loader." | ptm | ptm := PluggableTextMorph on: self text: #contents accept: nil readSelection: nil "#packageSelection " menu: nil. ptm setBalloonText: 'This is where the selected package or package release is displayed.'. ptm lock. ^ptm! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/30/2006 21:08' prior: 27930070! buildSearchPane "Cribbed from MessageNames>>inMorphicWindowWithInitialSearchString:" | typeInView searchButton typeInPane | typeInView := PluggableTextMorph on: self text: nil accept: #findPackage:notifying: readSelection: nil menu: nil. typeInView acceptOnCR: true; vResizing: #spaceFill; hResizing: #spaceFill; setTextMorphToSelectAllOnMouseEnter; askBeforeDiscardingEdits: false; setProperty: #alwaysAccept toValue: true. (typeInView respondsTo: #hideScrollBarsIndefinitely) ifTrue: [typeInView hideScrollBarsIndefinitely] ifFalse: [typeInView hideScrollBarIndefinitely]. searchButton := SimpleButtonMorph new target: typeInView; color: Color white; label: 'Search'; actionSelector: #accept; arguments: #(); yourself. typeInPane := AlignmentMorph newRow. typeInPane vResizing: #shrinkWrap; hResizing: #shrinkWrap; listDirection: #leftToRight; addMorphFront: searchButton; addTransparentSpacerOfSize: 6 @ 0; addMorphBack: typeInView; setBalloonText: 'Type into the pane, then press Search (or hit RETURN) to visit the next package matching what you typed.'. ^ typeInPane! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/22/2006 14:24'! buttonSpecs ^ #(('Install' installPackageRelease 'Install the latest version from the server.') ('Email' emailPackageMaintainers 'Open an editor to send an email to the owner and co-maintainers of this package.') ('Browse cache' browseCacheDirectory 'Browse cache directory of the selection.') ('Update' loadUpdates 'Update the package index from the servers.') ('Upgrade All' upgradeInstalledPackagesConfirm 'Upgrade all installed packages (confirming each).') ('Help' help 'What is this?'))! ! !SMLoader methodsFor: 'menus' stamp: 'btr 11/21/2006 16:11' prior: 27936393! categorySpecificOptions | choices | choices := OrderedCollection new. (categoriesToFilterIds includes: self selectedCategory id) ifTrue: [ choices add: #('Remove filter' #removeSelectedCategoryAsFilter 'Remove the filter for the selected category.')] ifFalse: [ choices add: #('Add as filter' #addSelectedCategoryAsFilter 'Add the selection as a filter to hide unrelated packages.')]. categoriesToFilterIds isEmpty ifFalse: [ choices add: #('Remove all filters' #removeCategoryFilters 'Remove all category filters.')]. ^ choices! ! !SMLoader methodsFor: 'lists' stamp: 'btr 11/30/2006 21:01' prior: 27933585! categoryWrapperList "Create the wrapper list for the hierarchical list. We sort the categories by name but ensure that 'Squeak versions' is first if it exists." | list first | list := (model categories select: [:each | each parent isNil]) asArray sort: [:c1 :c2 | c1 name <= c2 name]. first := list detect: [:any | any name = 'Squeak versions'] ifNone: []. first ifNotNil: [list := list copyWithout: first. list := {first} , list]. ^ list collect: [:cat | SMCategoryWrapper with: cat model: self]! ! !SMLoader methodsFor: 'filter utilities' stamp: 'gk 7/10/2004 15:45' prior: 27913226! changeFilters: anObject "Update my selection." | oldItem index | oldItem := self selectedPackageOrRelease. filters := anObject. self packagesListIndex: ((index := self packageList indexOf: oldItem) ifNil: [0] ifNotNil: [index]). self noteChanged! ! !SMLoader methodsFor: 'interface' stamp: 'btr 11/30/2006 17:30' prior: 27930584! createWindow | buttonBarHeight searchHeight vertDivide horizDivide | buttonBarHeight := 0.07. searchHeight := 0.07. vertDivide := 0.3. horizDivide := 0.6. self addMorph: (self buildButtonBar borderWidth: 0) frame: (0.0 @ 0.0 corner: 1.0 @ buttonBarHeight). self addMorph: (self buildSearchPane borderWidth: 0) frame: (0.0 @ buttonBarHeight corner: vertDivide @ searchHeight). self addMorph: (self buildMorphicPackagesList borderWidth: 0) frame: (0.0 @ (buttonBarHeight + searchHeight) corner: vertDivide @ horizDivide). self addMorph: (self buildMorphicCategoriesList borderWidth: 0) frame: (0.0 @ horizDivide corner: vertDivide @ 1.0). self addMorph: (self buildPackagePane borderWidth: 0) frame: (vertDivide @ buttonBarHeight corner: 1.0 @ 1.0). self on: #mouseEnter send: #paneTransition: to: self. self on: #mouseLeave send: #paneTransition: to: self! ! !SMLoader methodsFor: 'interface' stamp: 'gk 7/12/2004 11:14' prior: 27931214! defaultButtonPaneHeight "Answer the user's preferred default height for new button panes." ^ Preferences parameterAt: #defaultButtonPaneHeight ifAbsentPut: [25]! ! !SMLoader methodsFor: 'interface' stamp: 'btr 12/1/2006 02:01'! defaultLabel ^'SqueakMap Package Loader'! ! !SMLoader methodsFor: 'actions' stamp: 'btr 11/22/2006 01:14' prior: 27917579! emailPackageMaintainers "Send mail to package owner and co-maintainers." | item package toAddresses | item := self selectedPackageOrRelease ifNil: [^ nil]. package := item isPackageRelease ifTrue: [item package] ifFalse: [item]. "(this logic should be moved to MailMessage as soon as it can handle multiple To: addresses)" toAddresses := '<', package owner email, '>'. package maintainers ifNotNil: [ package maintainers do: [:maintainer | toAddresses := toAddresses, ', <', maintainer email, '>']]. SMUtilities sendMailTo: toAddresses regardingPackageRelease: item! ! !SMLoader methodsFor: 'filter utilities' stamp: 'btr 11/22/2006 00:14' prior: 27923782! filterSpecs "Return a specification for the filter menu. Is called each time." | specs | specs := #( #('Auto-installable packages' #filterAutoInstall 'display only packages that can be installed automatically') #('New available packages' #filterAvailable 'display only packages that are not installed or that have newer releases available.') #('New safely-available packages' #filterSafelyAvailable 'display only packages that are not installed or that have newer releases available that are safe to install, meaning that they are published and meant for the current version of Squeak.') _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit