The Trunk: SMLoader-gk.58.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

The Trunk: SMLoader-gk.58.mcz

commits-2
Göran Krampe uploaded a new version of SMLoader to project The Trunk:
http://source.squeak.org/trunk/SMLoader-gk.58.mcz

==================== Summary ====================

Name: SMLoader-gk.58
Author: gk
Time: 11 April 2010, 11:47:03.284 pm
UUID: d87ee0ad-c424-44e0-9d75-822dabf41a0c
Ancestors: SMLoader-nice.38, SMLoader-gk.57, SMLoader-mtf.56

Merged some fixed underscore assignments.

=============== Diff against SMLoader-nice.38 ===============

Item was added:
+ ----- Method: SMLoaderPlus>>buildWith: (in category 'interface') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategorical>>createFancyWindow (in category 'interface') -----
+ createFancyWindow
+ "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.
+ self addMorph: (self buildButtonBar borderWidth: 0)
+ frame: (0 @ 0 corner: 1 @ buttonBarHeight).
+ self addMorph: (self buildSearchPane borderWidth: 0)
+ frame: (0 @ buttonBarHeight corner: vertDivide @ searchHeight).
+ self addMorph: (self buildMorphicCategoriesList borderWidth: 0)
+ frame: (0 @ (buttonBarHeight + searchHeight) corner: vertDivide @ horizDivide).
+ self addMorph: (self buildMorphicNotInstalledPackagesList borderWidth: 0)
+ frame: (vertDivide @ buttonBarHeight corner: 1 @ (horizDivide / 2)).
+ self addMorph: (self buildMorphicInstalledPackagesList borderWidth: 0)
+ frame: (vertDivide @ (horizDivide / 2) corner: 1 @ horizDivide).
+ self addMorph: (self buildPackagePane borderWidth: 0)
+ frame: (0 @ horizDivide corner: 1 @ 1).
+ self on: #mouseEnter send: #paneTransition: to: self.
+ self on: #mouseLeave send: #paneTransition: to: self.
+ self setUpdatablePanesFrom: #(#installedPackageNameList #notInstalledPackageNameList ).
+ currentPackageList := #notInstalled.
+ self setLabel: self defaultLabel!

Item was changed:
+ ----- Method: SMPackageWrapper>>asString (in category 'converting') -----
- ----- Method: SMPackageWrapper>>asString (in category 'as yet unclassified') -----
  asString
+ | string |
+ string := item name, ' (', item versionLabel, ')'.
+ item isInstalled ifTrue: [string := string asText allBold].
+ "(string includesSubString: '->') ifTrue: [string := string asText color: Color green]."
+ ^ string!
- ^item nameWithVersionLabel!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>unload (in category 'class initialization') -----
+ unload
+ (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue:
+ [TheWorldMenu unregisterOpenCommand: self openMenuString].
+ self environment at: #Flaps ifPresent: [:cl |
+ cl unregisterQuadsWithReceiver: self] !

Item was added:
+ ----- Method: SMLoaderPlus>>categoryHasChildren: (in category 'interface') -----
+ categoryHasChildren: aCategory
+ ^ aCategory hasSubCategories!

Item was added:
+ ----- Method: SMLoader>>installPackageWithDependents: (in category 'actions') -----
+ installPackageWithDependents: package
+
+ | myRelease |
+ myRelease := self installedReleaseOfMe.
+ [Cursor wait showWhile: [
+ package installWithDependencies.
+ myRelease = self installedReleaseOfMe ifFalse: [self reOpen].
+ self noteChanged]
+ ] on: Error do: [:ex |
+ | msg |
+ msg := ex messageText ifNil:[ex asString].
+ self informException: ex msg: ('Error occurred during install:\', msg, '\') withCRs].!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>installedPackageList (in category 'lists') -----
+ installedPackageList
+ ^self packageList select: [:e | e isInstalled]!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>packageList (in category 'lists') -----
+ packageList
+ ^ self packages
+ select: [:e | (e categories
+ anySatisfy: [:cat | cat = self selectedCategory])
+ and: [(filters ifNil: [#()])
+ allSatisfy: [:currFilter | (self perform: currFilter)
+ value: e]]]!

Item was added:
+ ----- Method: SMLoaderPlus>>reOpen (in category 'private') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus classSide>>openMenuString (in category 'menu registration') -----
+ openMenuString
+ ^ 'SqueakMap Categories'!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>open (in category 'instance creation') -----
+ open
+ "Create and open a SqueakMap Loader."
+
+ "SMLoaderPlus open"
+
+ ^ (Smalltalk at: #ToolBuilder) open: self new!

Item was changed:
+ ----- Method: SMCategoryWrapper>>= (in category 'comparing') -----
- ----- Method: SMCategoryWrapper>>= (in category 'as yet unclassified') -----
  = anObject
  ^self withoutListWrapper = anObject withoutListWrapper!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>isOn (in category 'accessing') -----
+ isOn
+ ^false!

Item was changed:
+ ----- Method: SMLoader>>help (in category 'interface') -----
- ----- Method: SMLoader>>help (in category 'gui building') -----
  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 (installed version -> latest version).
- The names of packages are followed by (installed version -> latest version).
  If there is no arrow, your installed version of the package is the latest.
+ Installed packages and releases are also in bold.
  The checkbox menu items at the bottom let you 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.
- 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.
  If you like a package or have comments on it, please contact
  the author or the squeak mailing 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://minnow.cc.gatech.edu/squeak/2726' asUrl]!

Item was changed:
+ ----- Method: SMCategoryWrapper>>category (in category 'accessing') -----
- ----- Method: SMCategoryWrapper>>category (in category 'as yet unclassified') -----
  category
  ^item!

Item was changed:
+ ----- Method: SMCategoryWrapper>>contents (in category 'accessing') -----
- ----- Method: SMCategoryWrapper>>contents (in category 'as yet unclassified') -----
  contents
+ ^ item subCategories
+ collect: [:n | self class with: n model: n]!
- "This is the message that returns the contents of this wrapper.
- We return a collection of wrappers around all the children of our model."
-
- ^item subCategories collect: [:e | SMCategoryWrapper with: e]!

Item was added:
+ ----- Method: SMLoader>>defaultLabel (in category 'interface') -----
+ defaultLabel
+ ^'SqueakMap Package Loader'!

Item was added:
+ SMLoaderPlus subclass: #SMLoaderCategoricalPlus
+ instanceVariableNames: 'currentPackageList packagesListIndex'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'SMLoader'!
+
+ !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!

Item was changed:
  ----- Method: SMLoader>>generalOptions (in category 'menus') -----
  generalOptions
+ ^#( #('Upgrade all installed packages' upgradeInstalledPackagesNoConfirm)
+ #('Upgrade all installed packages confirming each' upgradeInstalledPackagesConfirm)
+ #('Put list in paste buffer' listInPasteBuffer)
+ #('Save filters as default' saveFiltersAsDefault)
- ^#(#('help' #help)
- #('update map from the net' loadUpdates)
- #('upgrade all installed packages' upgradeInstalledPackagesNoConfirm)
- #('upgrade all installed packages confirming each' upgradeInstalledPackagesConfirm)
- #('put list in paste buffer' listInPasteBuffer)
- #('save filters as default' saveFiltersAsDefault)
  #- )
 
  !

Item was added:
+ ----- Method: SMLoaderPlus>>filterVersion (in category 'filters') -----
+ 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: $ ) ]]!

Item was added:
+ ----- Method: SMLoaderPlus>>selectedCategory: (in category 'accessing') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>selectedCategoryPath (in category 'accessing') -----
+ 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]!

Item was added:
+ ----- Method: SMLoader>>buttonSpecs (in category 'interface') -----
+ 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?'))!

Item was added:
+ ----- Method: SMLoaderCategorical>>currentPackageList: (in category 'accessing') -----
+ currentPackageList: aSymbol
+ currentPackageList := aSymbol.
+ self changed: #installButtonLabel.!

Item was added:
+ ----- Method: SMLoaderPlus>>categoryList (in category 'lists') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>packageListCalculated (in category 'lists') -----
+ 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)]]]!

Item was added:
+ ----- Method: SMLoaderPlus>>selectedPackageOrRelease (in category 'accessing') -----
+ selectedPackageOrRelease
+ "Return selected package or package release."
+ ^ selectedItem!

Item was added:
+ ----- Method: SMLoaderPlus>>buildButtonBarWith: (in category 'interface') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategorical>>installButtonLabel (in category 'interface') -----
+ installButtonLabel
+ ^ self currentPackageList = #notInstalled
+ ifTrue: ['Install the above package']
+ ifFalse: ['Remove the above package']!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>installedPackagesListIndex: (in category 'accessing') -----
+ installedPackagesListIndex: anObject
+ packagesListIndex := anObject.
+ self currentPackageList ~= #installed
+ ifTrue: [self currentPackageList: #installed.
+ self changed: #currentPackageList].
+ self noteChanged!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>windowColorSpecification (in category 'window color') -----
+ 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'!

Item was added:
+ ----- Method: SMLoaderPlus>>stateForFilter: (in category 'filter utilities') -----
+ stateForFilter: aFilterSymbol
+ ^(self filters includes: aFilterSymbol) ifTrue: ['<yes>'] ifFalse: ['<no>']
+
+ !

Item was changed:
+ ----- Method: SMLoader>>createWindow (in category 'interface') -----
- ----- Method: SMLoader>>createWindow (in category 'gui building') -----
  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).
- "Create the package loader window."
-
  self addMorph: (self buildSearchPane borderWidth: 0)
+ frame: (0.0 @ buttonBarHeight corner: vertDivide @ searchHeight).
- frame: (0.0 @ 0.0 corner: 0.3 @ 0.07).
  self addMorph: (self buildMorphicPackagesList borderWidth: 0)
+ frame: (0.0 @ (buttonBarHeight + searchHeight) corner: vertDivide @ horizDivide).
- frame: (0.0 @ 0.07 corner: 0.3 @ 0.6).
  self addMorph: (self buildMorphicCategoriesList borderWidth: 0)
+ frame: (0.0 @ horizDivide corner: vertDivide @ 1.0).
- frame: (0.0 @ 0.6 corner: 0.3 @ 1.0).
  self addMorph: (self buildPackagePane borderWidth: 0)
+ frame: (vertDivide @ buttonBarHeight corner: 1.0 @ 1.0).
- frame: (0.3 @ 0.0 corner: 1.0 @ 1.0).
  self on: #mouseEnter send: #paneTransition: to: self.
  self on: #mouseLeave send: #paneTransition: to: self!

Item was added:
+ ----- Method: SMLoaderPlus>>categoryLabel: (in category 'interface') -----
+ categoryLabel: aCategory
+ ^ aCategory name!

Item was added:
+ SMLoader subclass: #SMLoaderCategorical
+ instanceVariableNames: 'currentPackageList packagesListIndex'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'SMLoader'!
+
+ !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!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>initialize (in category 'class initialization') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategorical>>currentPackageList (in category 'accessing') -----
+ currentPackageList
+ ^currentPackageList!

Item was added:
+ ----- Method: SMLoaderPlus>>commandSpecs (in category 'menus') -----
+ 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)))!

Item was added:
+ ----- Method: SMLoaderPlus>>package:filteredByCategory: (in category 'filter utilities') -----
+ 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]]!

Item was added:
+ ----- Method: SMLoaderPlus>>addSelectedCategoryAsFilter (in category 'actions') -----
+ addSelectedCategoryAsFilter
+ "Add a new filter that filters on the currently selected category.
+ Make it enabled as default."
+
+ categoriesToFilterIds add: self selectedCategory id!

Item was added:
+ ----- Method: SMCategoryWrapper>>help (in category 'accessing') -----
+ 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.'!

Item was changed:
  ----- Method: SMLoader classSide>>unload (in category 'class initialization') -----
  unload
  (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue:
+ [TheWorldMenu unregisterOpenCommand: self openMenuString].
- [TheWorldMenu unregisterOpenCommand: 'SqueakMap Package Loader'].
  self environment at: #Flaps ifPresent: [:cl |
  cl unregisterQuadsWithReceiver: self] !

Item was added:
+ ----- Method: SMLoaderPlus>>toggleFilterState: (in category 'filter utilities') -----
+ toggleFilterState: aFilterSymbol
+
+ ^(self filters includes: (aFilterSymbol))
+ ifTrue: [self filterRemove: aFilterSymbol]
+ ifFalse: [self filterAdd: aFilterSymbol]!

Item was added:
+ ----- Method: SMLoaderPlus>>hasSelectedItem (in category 'private') -----
+ hasSelectedItem
+ ^ self selectedPackageOrRelease notNil!

Item was added:
+ ----- Method: SMLoaderPlus>>filterNotUptoDate (in category 'filters') -----
+ filterNotUptoDate
+ ^[:package | package isAvailable]!

Item was changed:
+ ----- Method: SMPackageWrapper>>hash (in category 'testing') -----
- ----- Method: SMPackageWrapper>>hash (in category 'as yet unclassified') -----
  hash
  ^self withoutListWrapper hash!

Item was added:
+ ----- Method: SMLoaderPlus>>itemHasChildren: (in category 'interface') -----
+ itemHasChildren: anItem
+ ^ anItem isPackage and: [anItem releases notEmpty]!

Item was changed:
  ----- Method: SMLoader>>reOpen (in category 'private') -----
  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.'.
  self delete.
+ (Smalltalk at: self class name) open!
- SMLoader open!

Item was added:
+ ----- Method: SMLoaderPlus>>emailPackageMaintainers (in category 'actions') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>filters (in category 'filter utilities') -----
+ filters
+ ^filters!

Item was added:
+ ----- Method: SMLoaderPlus>>upgradeInstalledPackagesNoConfirm (in category 'actions') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>buildInstalledPackagesListWith: (in category 'interface') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>filterNotInstalledYet (in category 'filters') -----
+ filterNotInstalledYet
+ ^[:package | package isInstalled not]!

Item was changed:
  SystemWindow subclass: #SMLoader
  instanceVariableNames: 'packagesList selectedItemWrapper selectedCategoryWrapper filters categoriesToFilterIds'
  classVariableNames: 'DefaultCategoriesToFilterIds DefaultFilters'
  poolDictionaries: ''
  category: 'SMLoader'!
 
+ !SMLoader commentStamp: 'btr 11/30/2006 18:00' prior: 0!
+ A simple package loader that is currently the standard UI for SqueakMap (the model is an SMSqueakMap instance).
+ You can open one with:
- !SMLoader commentStamp: '<historical>' prior: 0!
- 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!

Item was added:
+ ----- Method: SMLoaderPlus>>uncheckFilters (in category 'actions') -----
+ uncheckFilters
+ "Uncheck all filters."
+
+ filters := OrderedCollection new.
+ self noteChanged!

Item was added:
+ ----- Method: SMPackageWrapper>>label (in category 'accessing') -----
+ label
+ ^ self asString!

Item was added:
+ ----- Method: SMLoaderPlus>>labelForShown: (in category 'lists') -----
+ 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)'!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>buildFancyWith: (in category 'interface') -----
+ 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!

Item was changed:
+ ----- Method: SMCategoryWrapper>>asString (in category 'converting') -----
- ----- Method: SMCategoryWrapper>>asString (in category 'as yet unclassified') -----
  asString
+ ^ item name , ' (' , self numberOfObjects printString , ')'!
- ^item name!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>newOn: (in category 'instance creation') -----
+ newOn: aMap
+ "Create a SqueakMap loader on given map."
+
+ ^super new on: aMap; yourself!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>currentPackageList: (in category 'accessing') -----
+ currentPackageList: aSymbol
+ currentPackageList := aSymbol.
+ self changed: #installButtonLabel.!

Item was added:
+ ----- Method: SMLoaderPlus>>cachePackageReleaseAndOfferToCopy (in category 'actions') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>filterSafelyAvailable (in category 'filters') -----
+ filterSafelyAvailable
+ ^[:package | package isSafelyAvailable]!

Item was added:
+ ----- Method: SMPackageReleaseWrapper>>contents (in category 'accessing') -----
+ contents
+ ^ #()!

Item was added:
+ ----- Method: SMLoaderCategorical>>noteChanged (in category 'private') -----
+ noteChanged
+ self changed: #installedPackageNameList.
+ self changed: #notInstalledPackageNameList.
+ super noteChanged."
+ self changed: #packageNameList.
+ self changed: #packagesListIndex.
+ self changed: #categoriesForPackage.
+ self contentsChanged."!

Item was added:
+ ----- Method: SMLoaderPlus>>labelForFilter: (in category 'filter utilities') -----
+ labelForFilter: aFilterSymbol
+ ^(self filterSpecs detect: [:fs | fs second = aFilterSymbol]) first!

Item was added:
+ ----- Method: SMLoaderPlus>>installedReleaseOfMe (in category 'private') -----
+ installedReleaseOfMe
+ "Return the release of the installed package loader."
+
+ ^SMSqueakMap default installedReleaseOf: (SMSqueakMap default packageWithId: '941c0108-4039-4071-9863-a8d7d2b3d4a3').!

Item was added:
+ ----- Method: SMLoaderCategorical>>defaultLabel (in category 'interface') -----
+ defaultLabel
+ ^ 'SqueakMap Categorical Package Loader'!

Item was added:
+ ----- Method: SMLoaderPlus>>initialExtent (in category 'interface') -----
+ initialExtent
+ ^500@400!

Item was added:
+ ----- Method: SMLoaderPlus>>label (in category 'lists') -----
+ label
+ ^ self
+ labelForShown: (packagesList
+ ifNil: [self packageList])!

Item was added:
+ ----- Method: SMLoaderPlus>>changeFilters: (in category 'accessing') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>filterAutoInstall (in category 'filters') -----
+ filterAutoInstall
+ ^[:package | package isInstallable]!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>new (in category 'instance creation') -----
+ new
+ "Create a SqueakMap loader on the default map."
+
+ ^self newOn: SMSqueakMap default!

Item was changed:
  ----- Method: SMLoader>>selectedPackageOrRelease (in category 'private') -----
  selectedPackageOrRelease
  "Return selected package or package release."
 
+ ^self selectedItemWrapper ifNotNilDo: [:w | w withoutListWrapper]!
- ^(self selectedItemWrapper isNil) ifFalse: [self selectedItemWrapper withoutListWrapper]!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>installButtonLabel (in category 'interface') -----
+ installButtonLabel
+ ^ self currentPackageList = #notInstalled
+ ifTrue: ['Install the above package']
+ ifFalse: ['Remove the above package']!

Item was added:
+ ----- Method: SMCategoryWrapper>>numberOfObjects (in category 'accessing') -----
+ numberOfObjects
+ " | total |
+ total _ 0.
+ model allCategoriesDo: [:c |
+ total _ total + c objects size].
+ ^total"
+ ^item objects size!

Item was added:
+ ----- Method: SMLoaderCategorical classSide>>removeFromSystem (in category 'menu registration') -----
+ removeFromSystem
+ (TheWorldMenu respondsTo: #registerOpenCommand:)
+ ifTrue: [TheWorldMenu unregisterOpenCommand: self openMenuString].
+ self removeFromSystem: true!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>buildNotInstalledPackagesListWith: (in category 'interface') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategorical>>notInstalledPackagesListIndex: (in category 'accessing') -----
+ notInstalledPackagesListIndex: anObject
+ packagesListIndex := anObject.
+ self currentPackageList ~= #notInstalled ifTrue:
+ [self currentPackageList: #notInstalled.
+ self changed: #currentPackageList].
+ self changed: #packagesListIndex.
+ "update my selection"
+ self noteChanged.
+ self contentsChanged!

Item was added:
+ ----- Method: SMLoaderCategorical>>notInstalledPackagesListIndex (in category 'accessing') -----
+ notInstalledPackagesListIndex
+ self currentPackageList = #notInstalled
+ ifTrue: [^ self packagesListIndex]
+ ifFalse: [^ 0]!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>initializedInstance (in category 'new-morph participation') -----
+ initializedInstance
+ ^ (ToolBuilder open: self new) extent: 400@400!

Item was added:
+ ----- Method: SMLoaderPlus>>packageList (in category 'lists') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>currentPackageList (in category 'accessing') -----
+ currentPackageList
+ ^currentPackageList!

Item was changed:
  ----- Method: SMLoader classSide>>initialize (in category 'class initialization') -----
  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}}]].
-
- self registerInFlapsRegistry.
- (Preferences windowColorFor: #SMLoader) = Color white "not set"
- ifTrue: [ Preferences setWindowColorFor: #SMLoader to: (Color colorFrom: self windowColorSpecification brightColor) ].
- (TheWorldMenu respondsTo: #registerOpenCommand:)
-          ifTrue: [
- TheWorldMenu registerOpenCommand: {'SqueakMap Package Loader'. {self. #open}}.
- TheWorldMenu unregisterOpenCommand: 'Package Loader'].
  DefaultFilters := OrderedCollection new.
+ DefaultCategoriesToFilterIds := OrderedCollection new!
- DefaultCategoriesToFilterIds := OrderedCollection new
- !

Item was added:
+ ----- Method: SMLoaderPlus>>categoryChildren: (in category 'interface') -----
+ categoryChildren: aCategory
+ ^ aCategory subCategories!

Item was added:
+ ----- Method: SMLoaderPlus>>findPackage:notifying: (in category 'actions') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>openMenuString (in category 'class initialization') -----
+ openMenuString
+ ^ 'SqueakMap Catalog'!

Item was added:
+ ----- Method: SMLoaderPlus>>upgradeInstalledPackages (in category 'actions') -----
+ 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]]!

Item was added:
+ ----- Method: SMLoaderCategorical>>createWindow (in category 'interface') -----
+ createWindow
+ | buttonBarHeight searchHeight vertDivide horizDivide |
+ buttonBarHeight := 0.07.
+ searchHeight := 0.07.
+ vertDivide := 0.5.
+ horizDivide := 0.6.
+ self addMorph: (self buildButtonBar borderWidth: 0)
+ frame: (0 @ 0 corner: 1 @ buttonBarHeight).
+ self addMorph: (self buildMorphicCategoriesList borderWidth: 0)
+ frame: (0 @ buttonBarHeight corner: vertDivide @ horizDivide).
+ self addMorph: (self buildSearchPane borderWidth: 0)
+ frame: (vertDivide @ buttonBarHeight corner: 1 @ (buttonBarHeight + searchHeight)).
+ self addMorph: (self buildMorphicPackagesList borderWidth: 0)
+ frame: (vertDivide @ (buttonBarHeight + searchHeight) corner: 1 @ horizDivide).
+ self addMorph: (self buildPackagePane borderWidth: 0)
+ frame: (0 @ horizDivide corner: 1 @ 1).
+ self on: #mouseEnter send: #paneTransition: to: self.
+ self on: #mouseLeave send: #paneTransition: to: self.
+ self setLabel: self defaultLabel!

Item was added:
+ ----- Method: SMCategoryWrapper>>hasContents (in category 'testing') -----
+ hasContents
+ ^ item hasSubCategories!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>registerInFlapsRegistry (in category 'new-morph participation') -----
+ 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']]!

Item was added:
+ ----- Method: SMLoaderCategorical classSide>>unload (in category 'menu registration') -----
+ unload
+ (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue:
+ [TheWorldMenu unregisterOpenCommand: self openMenuString].!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>notInstalledPackageList (in category 'lists') -----
+ notInstalledPackageList
+ ^self packageList reject: [:e | e isInstalled]!

Item was added:
+ ----- Method: SMLoaderPlus>>perform:orSendTo: (in category 'interface') -----
+ 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]!

Item was added:
+ ----- Method: SMLoaderPlus>>selectedCategory (in category 'accessing') -----
+ selectedCategory
+ "Return selected category."
+ ^ selectedCategory!

Item was added:
+ ----- Method: SMLoaderPlus>>filterPublished (in category 'filters') -----
+ filterPublished
+ ^[:package | package isPublished]!

Item was added:
+ ----- Method: SMLoaderPlus>>packageNameList (in category 'lists') -----
+ packageNameList
+ ^ self packageList collect: [:e | e name]!

Item was changed:
  ----- Method: SMLoader>>emailPackageMaintainers (in category 'actions') -----
  emailPackageMaintainers
  "Send mail to package owner and co-maintainers."
 
  | item package toAddresses |
+ item := self selectedPackageOrRelease ifNil: [^ nil].
- item := self selectedPackageOrRelease.
  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!

Item was added:
+ ----- Method: SMLoaderPlus>>packageSpecificOptions (in category 'menus') -----
+ 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!

Item was changed:
+ ----- Method: SMLoader>>changeFilters: (in category 'filter utilities') -----
- ----- Method: SMLoader>>changeFilters: (in category 'accessing') -----
  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!

Item was changed:
  ----- Method: SMLoader>>categoryWrapperList (in category 'lists') -----
  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]!
- list := (((model categories select:[:each | each parent == nil]) asArray
- sort:[:c1 :c2 | c1 name <= c2 name])).
- first := list detect:[:any | any name = 'Squeak versions'] ifNone:[nil].
- first ifNotNil:[
- list := list copyWithout: first.
- list := {first}, list].
- ^list collect:[:cat | SMCategoryWrapper with: cat model: self].!

Item was added:
+ ----- Method: SMLoaderPlus>>buildPackagesListWith: (in category 'interface') -----
+ 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!

Item was changed:
  ----- Method: SMLoader>>selectedCategoryWrapper: (in category 'accessing') -----
  selectedCategoryWrapper: aWrapper
  selectedCategoryWrapper := aWrapper.
+ (aWrapper notNil and:
+ [aWrapper withoutListWrapper objects includes: selectedItemWrapper withoutListWrapper])
+ ifFalse: [self selectedItemWrapper: nil].
- self selectedItemWrapper: nil.
  self changed: #selectedCategoryWrapper.
  self changed: #packageWrapperList.!

Item was added:
+ ----- Method: SMLoaderCategorical>>notInstalledPackageNameList (in category 'lists') -----
+ notInstalledPackageNameList
+ ^self packageList select: [:e | e isInstalled not]!

Item was added:
+ ----- Method: SMLoaderPlus>>updateLabel: (in category 'lists') -----
+ updateLabel: packagesShown
+ "Update the label of the window."
+ window ifNotNilDo: [:w | w setLabel: (self labelForShown: packagesShown)]!

Item was changed:
+ ----- Method: SMLoader>>buildPackagePane (in category 'interface') -----
- ----- Method: SMLoader>>buildPackagePane (in category 'gui building') -----
  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!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>noteChanged (in category 'private') -----
+ noteChanged
+ self changed: #installedPackageList.
+ self changed: #notInstalledPackageList.
+ super noteChanged."
+ self changed: #packageNameList.
+ self changed: #packagesListIndex.
+ self changed: #categoriesForPackage.
+ self contentsChanged."!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>defaultLabel (in category 'interface') -----
+ defaultLabel
+ ^ 'Categorical ' , super defaultLabel!

Item was changed:
+ ----- Method: SMPackageReleaseWrapper>>asString (in category 'converting') -----
- ----- Method: SMPackageReleaseWrapper>>asString (in category 'as yet unclassified') -----
  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!
-
- ^ item isInstalled
- ifTrue: [item smartVersion, ' *']
- ifFalse: [item smartVersion]!

Item was added:
+ ----- Method: SMLoaderPlus>>defaultButtonPaneHeight (in category 'interface') -----
+ defaultButtonPaneHeight
+ "Answer the user's preferred default height for new button panes."
+
+ ^ Preferences parameterAt: #defaultButtonPaneHeight ifAbsentPut: [25]!

Item was changed:
+ ----- Method: SMCategoryWrapper>>hash (in category 'comparing') -----
- ----- Method: SMCategoryWrapper>>hash (in category 'as yet unclassified') -----
  hash
  ^self withoutListWrapper hash!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus classSide>>removeFromSystem (in category 'menu registration') -----
+ removeFromSystem
+ (TheWorldMenu respondsTo: #registerOpenCommand:)
+ ifTrue: [TheWorldMenu unregisterOpenCommand: self openMenuString].
+ self removeFromSystem: true!

Item was added:
+ ----- Method: SMLoaderPlus>>installPackageRelease: (in category 'private') -----
+ 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].!

Item was added:
+ ----- Method: SMLoaderPlus>>selectedItemPath (in category 'accessing') -----
+ selectedItemPath
+ | path |
+ path := #().
+ (selectedItem isKindOf: SMPackageRelease)
+ ifTrue: [path := path copyWith: selectedItem package].
+ selectedItem
+ ifNotNil: [path := path copyWith: selectedItem].
+ ^ path!

Item was added:
+ ----- Method: SMLoaderPlus>>commandSpecFor: (in category 'menus') -----
+ commandSpecFor: selector
+ ^ self commandSpecs detect: [:spec | spec second = selector]!

Item was added:
+ ----- Method: SMLoaderPlus>>buildCategoriesListWith: (in category 'interface') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>notInstalledPackagesListIndex: (in category 'accessing') -----
+ notInstalledPackagesListIndex: anObject
+ packagesListIndex := anObject.
+ self currentPackageList ~= #notInstalled ifTrue:
+ [self currentPackageList: #notInstalled.
+ self changed: #currentPackageList].
+ self changed: #packagesListIndex.
+ "update my selection"
+ self noteChanged.
+ self contentsChanged!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>notInstalledPackagesListIndex (in category 'accessing') -----
+ notInstalledPackagesListIndex
+ ^ self currentPackageList = #notInstalled
+ ifTrue: [self packagesListIndex]
+ ifFalse: [0]!

Item was added:
+ ----- Method: SMLoaderPlus>>installPackageRelease (in category 'actions') -----
+ 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]]!

Item was added:
+ ----- Method: SMLoaderPlus>>filterSpecs (in category 'filter utilities') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>buildButtonNamed:helpText:action: (in category 'interface') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>categorySpecificOptions (in category 'menus') -----
+ 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!

Item was added:
+ ----- Method: SMLoader>>buildButtonBar (in category 'interface') -----
+ 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!

Item was changed:
  ----- Method: SMLoader>>packageList (in category 'lists') -----
  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 selectedCategory |
  list := packagesList ifNil: [
  packagesList := self packages select: [:p |
+ (filters ifNil: [#()]) allSatisfy: [:currFilter |
- filters allSatisfy: [:currFilter |
  currFilter isSymbol
  ifTrue: [(self perform: currFilter) value: p]
  ifFalse: [
  self package: p
  filteredByCategory: (model object: currFilter)]]]].
+ selectedCategoryWrapper ifNotNil:
+ [selectedCategory := selectedCategoryWrapper category.
+ list := list select: [:each | self package: each filteredByCategory: selectedCategory]].
- selectedCategoryWrapper ifNil:[self updateLabel: list. ^list].
- selectedCategory := selectedCategoryWrapper category.
- list := list select: [:each | self package: each filteredByCategory: selectedCategory].
  self updateLabel: list.
  ^list!

Item was added:
+ ----- Method: SMLoaderPlus>>buildPackagePaneWith: (in category 'interface') -----
+ buildPackagePaneWith: aBuilder
+ "Create the text area to the right in the loader."
+
+ ^ aBuilder pluggableTextSpec new model: self; getText: #itemDescription; name: #packagePane; yourself!

Item was changed:
  ----- Method: SMLoader>>findPackage:notifying: (in category 'actions') -----
  findPackage: aString notifying: aView
+ "Search and select a package with the given (sub) string in the name or description."
+ | index list match descriptions |
- "Search and select a package with the given (sub) string"
-
- | index list match |
  match := aString asString asLowercase.
  index := self packagesListIndex.
  list := self packageNameList.
  list isEmpty ifTrue: [^self].
+ descriptions := self packageWrapperList collect: [:e | e withoutListWrapper description].
  index + 1 to: list size
  do:
  [:i |
+ (((list at: i) includesSubstring: match caseSensitive: false) or:
+ [(descriptions at: i) includesSubstring: match caseSensitive: false])
- ((list at: i) asLowercase includesSubString: match)
  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])
- ((list at: i) asLowercase includesSubString: match)
  ifTrue: [^self packagesListIndex: i]].
  self inform: 'No package matching ' , aString asString!

Item was changed:
+ ----- Method: SMLoader>>perform:orSendTo: (in category 'interface') -----
- ----- Method: SMLoader>>perform:orSendTo: (in category 'gui building') -----
  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]!

Item was added:
+ ----- Method: SMLoader classSide>>openMenuString (in category 'class initialization') -----
+ openMenuString
+ ^ 'SqueakMap Catalog'!

Item was added:
+ ----- Method: SMLoaderPlus>>addFiltersToMenu: (in category 'menus') -----
+ 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'))
+ !

Item was changed:
  ----- Method: SMLoader>>upgradeInstalledPackages (in category 'actions') -----
  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 := model installedPackages.
  old := model oldPackages.
  old isEmpty ifTrue: [
  ^self inform: 'All ', installed size printString, ' installed packages are up to date.'].
  toUpgrade := model 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.
- old size < toUpgrade size ifTrue: [
- info := '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.'].
- ifFalse: [info := '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.
  [Cursor wait showWhile: [
  model 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]]!

Item was added:
+ ----- Method: SMLoaderPlus>>saveFiltersAsDefault (in category 'actions') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>buildWith: (in category 'interface') -----
+ 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!

Item was added:
+ ----- Method: SMLoaderPlus>>upgradeInstalledPackagesConfirm (in category 'actions') -----
+ upgradeInstalledPackagesConfirm
+ "Tries to upgrade all installed packages to the latest published release for this
+ version of Squeak. Confirms on each upgrade."
+
+ ^ self upgradeInstalledPackagesConfirm: true!

Item was changed:
+ ----- Method: SMLoader>>buildMorphicCategoriesList (in category 'interface') -----
- ----- Method: SMLoader>>buildMorphicCategoriesList (in category 'gui building') -----
  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 := (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 scroller submorphs do:[:each| list expandAll: each]."
  list adjustSubmorphPositions.
+ ^ list!
- ^list!

Item was added:
+ ----- Method: SMLoaderPlus>>itemLabel: (in category 'interface') -----
+ 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]!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus classSide>>unload (in category 'menu registration') -----
+ unload
+ (TheWorldMenu respondsTo: #registerOpenCommand:) ifTrue:
+ [TheWorldMenu unregisterOpenCommand: self openMenuString].!

Item was added:
+ ----- Method: SMLoaderPlus>>filterAdd: (in category 'filter utilities') -----
+ filterAdd: anObject
+
+ self changeFilters: (self filters copyWith: anObject)
+ !

Item was added:
+ ----- Method: SMLoaderPlus>>on: (in category 'initialization') -----
+ on: aSqueakMap
+ "Initialize instance."
+
+ map := aSqueakMap.
+ map synchWithDisk.
+ filters := DefaultFilters copy.
+ categoriesToFilterIds := DefaultCategoriesToFilterIds copy.
+ self askToLoadUpdates!

Item was changed:
  ----- Method: SMLoader>>selectedCategory (in category 'accessing') -----
  selectedCategory
  "Return selected category."
+ ^selectedCategoryWrapper ifNotNilDo: [:w | w withoutListWrapper]!
-
- ^(self selectedCategoryWrapper isNil)
- ifFalse: [self selectedCategoryWrapper withoutListWrapper]!

Item was added:
+ ----- Method: SMLoaderPlus>>filterRemove: (in category 'filter utilities') -----
+ filterRemove: anObject
+
+ self changeFilters: (self filters copyWithout: anObject)
+ !

Item was changed:
  ----- Method: SMLoader>>packageSpecificOptions (in category 'menus') -----
  packageSpecificOptions
  | choices packageOrRelease |
  packageOrRelease := self selectedPackageOrRelease.
  choices := OrderedCollection new.
  packageOrRelease isInstallable ifTrue: [
+ choices add: self buttonSpecs first].
- choices add: #('install' #installPackageRelease 'Install selected package or release, first downloading into the cache if needed.')].
  (packageOrRelease isDownloadable and: [packageOrRelease isCached]) ifTrue: [
+ choices add: self buttonSpecs third].
- choices add: #('browse cache' #browseCacheDirectory 'Browse cache directory of selected package or package release.')].
 
  (packageOrRelease isPackageRelease and: [packageOrRelease isDownloadable]) ifTrue: [
+ choices add: #('Copy from cache' #cachePackageReleaseAndOfferToCopy 'Download selected release into cache first if needed, and then offer to copy it somewhere else.' ).
+ choices add: #('Force download into cache' #downloadPackageRelease 'Force a download of the selected release into the cache.' )].
+ choices add: self buttonSpecs second.
- choices add: #('copy from cache' #cachePackageReleaseAndOfferToCopy 'Download selected release into cache first if needed, and then offer to copy it somewhere else.' ).
- choices add: #('force download into cache' #downloadPackageRelease 'Force a download of the selected release into the cache.' )].
- choices add: #('email package maintainers' emailPackageMaintainers 'Open an editor to send an email to the owner and co-maintainers of this package.').
  ^ choices!

Item was added:
+ ----- Method: SMLoaderCategorical classSide>>initialize (in category 'menu registration') -----
+ initialize
+ Smalltalk at: #ToolBuilder ifAbsent: [
+ (TheWorldMenu respondsTo: #registerOpenCommand:)
+ ifTrue: [TheWorldMenu registerOpenCommand: {self openMenuString. {self. #open}}]]!

Item was added:
+ ----- Method: SMLoaderPlus>>categoriesMenu: (in category 'menus') -----
+ categoriesMenu: aMenu
+ "Answer the categories-list menu."
+
+ self selectedCategory
+ ifNotNil: [aMenu addList: self categorySpecificOptions; addLine].
+ aMenu addList: self generalOptions.
+ self addFiltersToMenu: aMenu.
+ ^aMenu!

Item was added:
+ ----- Method: SMLoaderPlus>>upgradeInstalledPackagesConfirm: (in category 'private') -----
+ 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]]!

Item was changed:
+ ----- Method: SMLoader>>updateLabel: (in category 'interface') -----
- ----- Method: SMLoader>>updateLabel: (in category 'lists') -----
  updateLabel: packagesShown
  "Update the label of the window."
 
+ self setLabel: self defaultLabel , ' (',
+ (packagesShown size < model packages size ifTrue: [packagesShown size printString,
+ ' shown out of '] ifFalse: ['']) , model packages size printString, ' packages)'!
- self setLabel: 'SqueakMap Package Loader (', packagesShown size printString,
- '/', model packages size printString, ')'!

Item was added:
+ ----- Method: SMLoaderPlus>>listInPasteBuffer (in category 'actions') -----
+ 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!

Item was changed:
+ ----- Method: SMLoader>>openAsMorph (in category 'initialization') -----
- ----- Method: SMLoader>>openAsMorph (in category 'gui building') -----
  openAsMorph
  "Open the loader as a Morphic window."
  "SMLoader new openAsMorph"
 
  ^self createWindow openInWorld!

Item was changed:
  ----- Method: SMLoader>>selectedItemWrapper: (in category 'accessing') -----
  selectedItemWrapper: aWrapper
  selectedItemWrapper := aWrapper.
  self changed: #selectedItemWrapper.
  self contentsChanged!

Item was added:
+ ----- Method: SMLoaderCategorical>>buildMorphicNotInstalledPackagesList (in category 'interface') -----
+ buildMorphicNotInstalledPackagesList
+ | list |
+ (list := PluggableListMorph new)
+ on: self
+ list: #notInstalledPackageNameList
+ selected: #notInstalledPackagesListIndex
+ changeSelected: #notInstalledPackagesListIndex:
+ menu: #packagesMenu:
+ keystroke: #packagesListKey:from:.
+ ^ list!

Item was added:
+ ----- Method: SMLoaderPlus>>packages (in category 'private') -----
+ packages
+ "We request the packages as sorted by name by default."
+
+ ^map packagesByName asArray
+ !

Item was added:
+ ----- Method: SMLoaderPlus classSide>>descriptionForPartsBin (in category 'parts bin') -----
+ descriptionForPartsBin
+ ^self partName: 'Package Loader'
+ categories: #(Tools)
+ documentation: 'SqueakMap UI'
+ !

Item was added:
+ ----- Method: SMLoaderPlus>>browseCacheDirectory (in category 'interface') -----
+ 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!

Item was changed:
+ ----- Method: SMLoader>>defaultButtonPaneHeight (in category 'interface') -----
- ----- Method: SMLoader>>defaultButtonPaneHeight (in category 'gui building') -----
  defaultButtonPaneHeight
  "Answer the user's preferred default height for new button panes."
 
  ^ Preferences parameterAt: #defaultButtonPaneHeight ifAbsentPut: [25]!

Item was changed:
  ----- Method: SMLoader>>addFiltersToMenu: (in category 'menus') -----
  addFiltersToMenu: aMenu
+ | filterSymbol help |
+ self filterSpecs do: [:filterArray |
-
- self filterSpecs do: [:filterArray | | help filterSymbol |
  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'))
- addList: #(('uncheck all filters' uncheckFilters 'unchecks all filters so that all packages are listed'))
  !

Item was changed:
+ ----- Method: SMLoader>>buildMorphicPackagesList (in category 'interface') -----
- ----- Method: SMLoader>>buildMorphicPackagesList (in category 'gui building') -----
  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.';
- setBalloonText: 'Here all packages with their releases are listed that should be displayed according the current filter.';
  yourself!

Item was changed:
  ----- Method: SMLoader>>categorySpecificOptions (in category 'menus') -----
  categorySpecificOptions
  | choices |
  choices := OrderedCollection new.
  (categoriesToFilterIds includes: self selectedCategory id)
  ifTrue: [
+ choices add: #('Remove filter' #removeSelectedCategoryAsFilter 'Remove the filter for the selected category.')]
- 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.')].
- choices add: #('add as filter' #addSelectedCategoryAsFilter 'Add the selected category as a filter so that only packages in that category are shown.')].
  categoriesToFilterIds isEmpty ifFalse: [
+ choices add: #('Remove all filters' #removeCategoryFilters 'Remove all category filters.')].
- choices add: #('remove all category filters' #removeCategoryFilters 'Remove all category filters.')].
  ^ choices!

Item was changed:
+ ----- Method: SMPackageWrapper>>contents (in category 'accessing') -----
- ----- Method: SMPackageWrapper>>contents (in category 'as yet unclassified') -----
  contents
  ^item releases reversed collect: [:e | SMPackageReleaseWrapper with: e]!

Item was added:
+ ----- Method: SMLoaderCategorical>>installedPackagesListIndex (in category 'accessing') -----
+ installedPackagesListIndex
+ self currentPackageList = #installed
+ ifTrue: [^ self packagesListIndex]
+ ifFalse: [^ 0]!

Item was changed:
  ----- Method: SMLoader>>installPackageRelease (in category 'actions') -----
  installPackageRelease
  "Install selected package or release.
  The cache is used."
 
  | item release |
+ item := self selectedPackageOrRelease ifNil: [^ nil].
- item := self selectedPackageOrRelease.
  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]]!

Item was changed:
  ----- Method: SMLoader>>filterSpecs (in category 'filter utilities') -----
  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.'))
- #('display only auto-installable packages' #filterAutoInstall 'display only packages that can be installed automatically')
- #('display only new available packages' #filterAvailable 'display only packages that are not installed or that have newer releases available.')
- #('display only 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.')
- #('display only installed packages' #filterInstalled 'display only packages that are installed.')
- #('display only published packages' #filterPublished 'display only packages that have at least one published release.'))
  asOrderedCollection.
  categoriesToFilterIds do: [:catId |
+ specs add: {'Packages in ', (model object: catId) name. catId. 'Display only packages that are in the category.'}].
- specs add: {'display only packages in ', (model object: catId) name. catId. 'display only packages that are in the category.'}].
  ^ specs!

Item was added:
+ ----- Method: SMLoader>>buildButtonNamed:helpText:action: (in category 'interface') -----
+ 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!

Item was changed:
+ ----- Method: SMPackageWrapper>>= (in category 'comparing') -----
- ----- Method: SMPackageWrapper>>= (in category 'as yet unclassified') -----
  = anObject
  ^self withoutListWrapper = anObject withoutListWrapper!

Item was added:
+ ----- Method: SMLoaderPlus>>selectedItem: (in category 'accessing') -----
+ 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]!

Item was added:
+ ----- Method: SMLoaderPlus>>removeCategoryFilters (in category 'actions') -----
+ removeCategoryFilters
+ "Remove all category filters."
+
+ categoriesToFilterIds := OrderedCollection new!

Item was added:
+ ----- Method: SMLoaderPlus>>showFilterString: (in category 'filter utilities') -----
+ showFilterString: aFilterSymbol
+ ^(self stateForFilter: aFilterSymbol), (self labelForFilter: aFilterSymbol)!

Item was added:
+ ----- Method: SMLoaderCategorical>>installedPackageNameList (in category 'lists') -----
+ installedPackageNameList
+ ^self packageList select: [:e | e isInstalled]!

Item was added:
+ ----- Method: SMLoaderPlus>>packagesListIndex: (in category 'accessing') -----
+ packagesListIndex: anObject
+ self
+ selectedItem: (anObject = 0
+ ifFalse: [self packageList at: anObject])!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus classSide>>initialize (in category 'menu registration') -----
+ initialize
+ Smalltalk at: #ToolBuilder ifPresent: [:tb |
+ (TheWorldMenu respondsTo: #registerOpenCommand:)
+ ifTrue: [TheWorldMenu registerOpenCommand: {self openMenuString. {self. #open}}]]!

Item was added:
+ ----- Method: SMLoaderCategorical>>packageList (in category 'lists') -----
+ packageList
+ ^ self packages
+ select: [:e | (e categories
+ anySatisfy: [:cat | cat = self selectedCategory])
+ and: [(filters ifNil: [#()])
+ allSatisfy: [:currFilter | (self perform: currFilter)
+ value: e]]]!

Item was added:
+ ----- Method: SMLoaderPlus>>noteChanged (in category 'private') -----
+ 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]!

Item was added:
+ ----- Method: SMPackageReleaseWrapper>>label (in category 'accessing') -----
+ label
+ ^ self asString
+ !

Item was changed:
+ ----- Method: SMLoader>>buildSearchPane (in category 'interface') -----
- ----- Method: SMLoader>>buildSearchPane (in category 'gui building') -----
  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!
- | typeInView |
- typeInView := PluggableTextMorph on: self
- text: nil accept: #findPackage:notifying:
- readSelection: nil menu: nil.
- typeInView setBalloonText:'To find a package type in a fragment of its name and hit return'.
- typeInView acceptOnCR: true.
- (typeInView respondsTo: #hideScrollBarsIndefinitely) ifTrue: [
- typeInView hideScrollBarsIndefinitely]
- ifFalse: [typeInView hideScrollBarIndefinitely].
- ^typeInView!

Item was added:
+ ----- Method: SMLoader>>buildPackageButtonBar (in category 'interface') -----
+ buildPackageButtonBar
+ | aRow |
+ "Somewhat patterned after IRCe's buttonRow method."
+ aRow := AlignmentMorph newRow beSticky.
+ aRow color: Color transparent;
+ clipSubmorphs: true.
+ ^ aRow!

Item was added:
+ ----- Method: SMLoaderPlus>>removeSelectedCategoryAsFilter (in category 'actions') -----
+ removeSelectedCategoryAsFilter
+ "Remove the filter that filters on the currently selected category."
+
+ categoriesToFilterIds remove: self selectedCategory id!

Item was added:
+ ----- Method: SMLoaderCategorical classSide>>openMenuString (in category 'menu registration') -----
+ openMenuString
+ ^ 'SqueakMap Categories'!

Item was added:
+ ----- Method: SMCategoryWrapper>>getList (in category 'model access') -----
+ getList
+ ^ Array
+ with: (self class with: self contents model: model)!

Item was added:
+ ----- Method: SMLoaderPlus>>searchText (in category 'interface') -----
+ searchText
+ "A dummy default search text so that the field describes its purpose."
+ ^ 'Search packages'!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>openOn: (in category 'instance creation') -----
+ openOn: aSqueakMap
+ "Create and open a SqueakMap Loader on a given map."
+
+ "self openOn: SqueakMap default"
+
+ ^ (Smalltalk at: #ToolBuilder) open: (self newOn: aSqueakMap)!

Item was added:
+ ----- Method: SMLoaderPlus>>packagesMenu: (in category 'menus') -----
+ packagesMenu: aMenu
+ "Answer the packages-list menu."
+
+ self selectedPackageOrRelease
+ ifNotNil: [aMenu addList: self packageSpecificOptions; addLine].
+ aMenu addList: self generalOptions.
+ self addFiltersToMenu: aMenu.
+ ^aMenu!

Item was added:
+ ----- Method: SMLoaderPlus>>buildSearchPaneWith: (in category 'interface') -----
+ buildSearchPaneWith: aBuilder
+ ^ aBuilder pluggableInputFieldSpec new model: self;
+ selection: #searchSelection;
+ getText: #searchText; setText: #findPackage:notifying:; name: #search; yourself!

Item was added:
+ ----- Method: SMLoaderCategorical>>isOn (in category 'accessing') -----
+ isOn
+ ^false!

Item was added:
+ ----- Method: SMLoaderPlus>>packagesListIndex (in category 'accessing') -----
+ packagesListIndex
+ ^ self packageList indexOf: self selectedItem!

Item was added:
+ ----- Method: SMLoaderPlus>>help (in category 'interface') -----
+ 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]!

Item was added:
+ ----- Method: SMPackageWrapper>>help (in category 'accessing') -----
+ help
+ ^ 'This shows all packages with their releases that should be displayed according the current filter.'!

Item was added:
+ ----- Method: SMLoaderPlus>>defaultLabel (in category 'lists') -----
+ defaultLabel
+ ^ 'SqueakMap Package Loader'!

Item was added:
+ ----- Method: SMCategoryWrapper>>model (in category 'accessing') -----
+ model
+ ^model!

Item was added:
+ ----- Method: SMLoaderPlus>>itemDescription (in category 'private') -----
+ itemDescription
+ ^ self selectedPackageOrRelease
+ ifNil: ['<No package selected>']
+ ifNotNilDo: [:item | item fullDescription]!

Item was added:
+ ----- Method: SMLoaderPlus>>loadUpdates (in category 'actions') -----
+ 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]!

Item was changed:
+ ----- Method: SMPackageWrapper>>printOn: (in category 'printing') -----
- ----- Method: SMPackageWrapper>>printOn: (in category 'as yet unclassified') -----
  printOn: aStream
  aStream nextPutAll: 'wrapper for: ', item printString!

Item was added:
+ ----- Method: SMLoaderPlus>>informException:msg: (in category 'private') -----
+ 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]!

Item was added:
+ ----- Method: SMLoaderPlus>>filterAvailable (in category 'filters') -----
+ filterAvailable
+ ^[:package | package isAvailable]!

Item was changed:
  ----- Method: SMLoader>>upgradeInstalledPackagesConfirm: (in category 'private') -----
  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 := model installedPackages.
  old := model oldPackages.
  old isEmpty ifTrue: [
  ^self inform: 'All ', installed size printString, ' installed packages are up to date.'].
  toUpgrade := model 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.
- old size < toUpgrade size ifTrue: [
- info := '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.'].
- ifFalse: [info := '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.
  [Cursor wait showWhile: [
  confirmEach ifTrue: [
  model upgradeOldPackagesConfirmBlock: [:p |
  self confirm: 'Upgrade ', p installedRelease packageNameWithVersion, ' to ',
  (p lastPublishedReleaseForCurrentSystemVersionNewerThan: p installedRelease) listName, '?']]
  ifFalse: [model upgradeOldPackages].
  self inform: toUpgrade size printString, ' packages successfully processed.'.
  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]]!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>newStandAlone (in category 'new-morph participation') -----
+ newStandAlone
+ ^ ToolBuilder open: self new!

Item was added:
+ Model subclass: #SMLoaderPlus
+ instanceVariableNames: 'packagesList selectedItem selectedCategory filters categoriesToFilterIds map builder window'
+ classVariableNames: 'DefaultCategoriesToFilterIds DefaultFilters'
+ poolDictionaries: ''
+ category: 'SMLoader'!
+
+ !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.!

Item was added:
+ ----- Method: SMLoaderPlus>>searchSelection (in category 'interface') -----
+ searchSelection
+ "Selects all of the default search text so that a type-in overwrites it."
+ ^ {1. self searchText size}!

Item was added:
+ ----- Method: SMLoaderPlus classSide>>prototypicalToolWindow (in category 'new-morph participation') -----
+ prototypicalToolWindow
+ ^ ToolBuilder open: self new; applyModelExtent; yourself!

Item was added:
+ ----- Method: SMLoaderPlus>>itemChildren: (in category 'interface') -----
+ itemChildren: anItem
+ ^ anItem isPackage
+ ifTrue: [anItem releases]
+ ifFalse: [#()]!

Item was changed:
+ ----- Method: SMLoader>>browseCacheDirectory (in category 'interface') -----
- ----- Method: SMLoader>>browseCacheDirectory (in category 'gui building') -----
  browseCacheDirectory
  "Open a FileList2 on the directory for the package or release."
 
  | item dir win |
+ item := self selectedPackageOrRelease ifNil: [^ nil].
- item := self selectedPackageOrRelease.
  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
  !

Item was added:
+ ----- Method: SMLoaderPlus>>filterInstalled (in category 'filters') -----
+ filterInstalled
+ ^[:package | package isInstalled]!

Item was added:
+ ----- Method: SMLoaderCategorical>>buildMorphicInstalledPackagesList (in category 'interface') -----
+ buildMorphicInstalledPackagesList
+ | list |
+ (list := PluggableListMorph new)
+ on: self
+ list: #installedPackageNameList
+ selected: #installedPackagesListIndex
+ changeSelected: #installedPackagesListIndex:
+ menu: #packagesMenu:
+ keystroke: #packagesListKey:from:.
+ ^ list!

Item was added:
+ ----- Method: SMLoaderPlus>>downloadPackageRelease (in category 'actions') -----
+ 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]!

Item was added:
+ ----- Method: SMLoaderPlus>>generalOptions (in category 'menus') -----
+ generalOptions
+ ^#( #('Upgrade all installed packages' upgradeInstalledPackagesNoConfirm)
+ #('Upgrade all installed packages confirming each' upgradeInstalledPackagesConfirm)
+ #('Put list in paste buffer' listInPasteBuffer)
+ #('Save filters as default' saveFiltersAsDefault)
+ #- )
+
+ !

Item was added:
+ ----- Method: SMLoaderPlus>>selectedItem (in category 'accessing') -----
+ selectedItem
+ ^ selectedItem!

Item was added:
+ ----- Method: SMLoaderPlus>>askToLoadUpdates (in category 'actions') -----
+ 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]]!

Item was added:
+ ----- Method: SMLoaderCategorical>>installedPackagesListIndex: (in category 'accessing') -----
+ installedPackagesListIndex: anObject
+ packagesListIndex := anObject.
+ self currentPackageList ~= #installed
+ ifTrue: [self currentPackageList: #installed.
+ self changed: #currentPackageList].
+ self noteChanged!

Item was added:
+ ----- Method: SMLoaderCategoricalPlus>>installedPackagesListIndex (in category 'accessing') -----
+ installedPackagesListIndex
+ ^ self currentPackageList = #installed
+ ifTrue: [self packagesListIndex]
+ ifFalse: [0]!

Item was changed:
  ----- Method: SMLoader>>packagesListIndex: (in category 'accessing') -----
+ packagesListIndex: anObject
+ self
+ selectedItemWrapper: (anObject ifNotNil: [anObject = 0
+ ifFalse: [self packageWrapperList at: anObject]])!
- packagesListIndex: anObject
- self selectedItemWrapper: (anObject = 0 ifTrue:[nil] ifFalse: [(self packageWrapperList at: anObject)])
- !

Item was removed:
- ----- Method: SMLoader>>addPackagesTo:at:plus: (in category 'gui building') -----
- addPackagesTo: window at: fractions plus: verticalOffset
- "Add the list for packages, and answer the verticalOffset plus the height added"
-
- | divider listMorph |
- listMorph := self buildMorphicPackagesList.
- listMorph borderWidth: 0.
- divider := BorderedSubpaneDividerMorph forBottomEdge.
- Preferences alternativeWindowLook ifTrue:[
- divider extent: 4@4; color: Color transparent; borderColor: #raised; borderWidth: 2.
- ].
- window
- addMorph: listMorph
-
- !

Item was removed:
- ----- Method: SMLoader>>paneColorOld (in category 'gui building') -----
- paneColorOld
- ^ Color yellow muchLighter duller!