Chris Muller uploaded a new version of ToolBuilder-Morphic to project The Trunk:
http://source.squeak.org/trunk/ToolBuilder-Morphic-cmm.164.mcz
==================== Summary ====================
Name: ToolBuilder-Morphic-cmm.164
Author: cmm
Time: 31 May 2016, 4:26:42.939857 pm
UUID: bd968a1e-4af0-4499-98b4-bd3fa99f7f14
Ancestors: ToolBuilder-Morphic-mt.163
- Make #balloonText, like #printString, less disruptive to the user-interface when there's a problem.
- Clarify the name of the preference which allows filtering hierarchical lists by only their labels.
=============== Diff against ToolBuilder-Morphic-mt.163 ===============
Item was changed:
----- Method: PluggableTextMorphPlus>>drawBalloonTextOn: (in category 'drawing') -----
+ drawBalloonTextOn: aCanvas
- drawBalloonTextOn: aCanvas
"Show balloon text in the text morph if it has no contents."
+ textMorph contents ifNotEmpty: [ ^ self ].
+ ([ self balloonText ]
+ on: Error
+ do: [ : err | 'error in balloonText' ]) ifNotNil:
+ [ : text | aCanvas
-
- textMorph contents ifNotEmpty: [^ self].
-
- self balloonText ifNotNil: [:text |
- aCanvas
drawString: text
+ in: (self innerBounds insetBy: (5 @ 2 corner: 0 @ 0))
+ font: textMorph textStyle defaultFont "I want italic here"
+ color: (Color gray: 0.7) ]!
- in: (self innerBounds insetBy: (5@2 corner: 0@0))
- font: textMorph textStyle defaultFont
- color: (Color gray: 0.7)].!
Item was changed:
----- Method: PluggableTreeMorph class>>filterByLabelsOnly (in category 'preferences') -----
filterByLabelsOnly
+ <preference: 'Filterable Trees by labels only'
- <preference: 'Filterable Lists by labels only'
category: 'scrolling'
description: 'When using the Filterable Lists option, set this to only match the labels, not the contents, of hierarchical lists. Otherwise, search both labels and contents will be matched.'
type: #Boolean>
^ FilterByLabelsOnly ifNil: [ false ]!
Marcel Taeumel uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-mt.1027.mcz
==================== Summary ====================
Name: Kernel-mt.1027
Author: mt
Time: 31 May 2016, 10:43:24.59966 am
UUID: a9379948-081f-fb4f-a55d-5f72c6d674e5
Ancestors: Kernel-pre.1026
Appendix to Morphic-mt.1154.
=============== Diff against Kernel-pre.1026 ===============
Item was changed:
----- Method: Model class>>windowActiveOnFirstClick (in category 'preferences') -----
windowActiveOnFirstClick
+ <preference: 'Windows'' Contents Are Always Active'
- <preference: 'Windows Active On First Click'
category: 'windows'
+ description: 'When enabled, the widgets of background windows are sensitive to mouse input. When disabled, clicking background window will only make it the active window, without changing, for example, selections in lists or text fields.'
- description: 'When enabled, the widgets of background (inactive) windows are sensitive to mouse input. When disabled, clicking background window will only make it the active window, without changing, for example, selections in lists or text fields.'
type: #Boolean>
^ WindowActiveOnFirstClick ifNil: [ false ]!
Item was changed:
----- Method: Model class>>windowActiveOnFirstClick: (in category 'preferences') -----
windowActiveOnFirstClick: aBoolean
+ WindowActiveOnFirstClick := aBoolean.
+ (Smalltalk classNamed: #SystemWindow) ifNotNil: [:c | c reconfigureWindowsForFocus].!
- WindowActiveOnFirstClick := aBoolean.!
Marcel Taeumel uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-mt.1154.mcz
==================== Summary ====================
Name: Morphic-mt.1154
Author: mt
Time: 31 May 2016, 10:41:59.45866 am
UUID: 37fee264-be7d-a442-8ea5-cf72d229cab3
Ancestors: Morphic-ul.1153
Another refactoring of SystemWindow. Improves readability of window properties:
Windows Raise on Any Click
Windows' Contents Are Always Active
Windows' Controls Are Always Active
Thanks to Chris for pointing out several issues.
=============== Diff against Morphic-ul.1153 ===============
Item was added:
+ ----- Method: CornerGripMorph>>mouseDown: (in category 'as yet unclassified') -----
+ mouseDown: anEvent
+ "Disable drop shadow to improve performance."
+
+ super mouseDown: anEvent.
+
+ target ifNil: [^ self].
+ target fastFramingOn ifFalse: [
+ self setProperty: #targetHadDropShadow toValue: target hasDropShadow.
+ target hasDropShadow: false].!
Item was changed:
----- Method: CornerGripMorph>>mouseMove: (in category 'as yet unclassified') -----
mouseMove: anEvent
| delta |
target ifNil: [^ self].
target fastFramingOn
ifTrue: [delta := target doFastWindowReframe: self ptName]
ifFalse: [
- target hasDropShadow: false.
delta := lastMouse ifNil: [0@0] ifNotNil: [anEvent cursorPoint - lastMouse].
lastMouse := anEvent cursorPoint.
self apply: delta.
self bounds: (self bounds origin + delta extent: self bounds extent)].!
Item was changed:
----- Method: CornerGripMorph>>mouseUp: (in category 'as yet unclassified') -----
mouseUp: anEvent
target ifNil: [^ self].
target fastFramingOn ifFalse: [
+ (self valueOfProperty: #targetHadDropShadow ifAbsent: [false]) ifTrue: [target hasDropShadow: true].
+ self removeProperty: #targetHadDropShadow].!
- target hasDropShadow: Preferences menuAppearance3d].!
Item was changed:
MorphicModel subclass: #SystemWindow
instanceVariableNames: 'labelString stripes label closeBox collapseBox paneMorphs paneRects collapsedFrame fullFrame isCollapsed isActive menuBox mustNotClose labelWidgetAllowance updatablePanes allowReframeHandles labelArea expandBox'
+ classVariableNames: 'ClickOnLabelToEdit CloseBoxFrame CloseBoxImageFlat CloseBoxImageGradient CollapseBoxImageFlat CollapseBoxImageGradient DoubleClickOnLabelToExpand ExpandBoxFrame ExpandBoxImageFlat ExpandBoxImageGradient FocusFollowsMouse GradientWindow HideExpandButton MenuBoxFrame MenuBoxImageFlat MenuBoxImageGradient ResizeAlongEdges ReuseWindows TopWindow WindowTitleActiveOnFirstClick WindowsRaiseOnClick'
- classVariableNames: 'ClickOnLabelToEdit CloseBoxFrame CloseBoxImageFlat CloseBoxImageGradient CollapseBoxImageFlat CollapseBoxImageGradient DoubleClickOnLabelToExpand ExpandBoxFrame ExpandBoxImageFlat ExpandBoxImageGradient FocusFollowsMouse GradientWindow HideExpandButton MenuBoxFrame MenuBoxImageFlat MenuBoxImageGradient ResizeAlongEdges ReuseWindows TopWindow WindowsActiveOnlyOnTop'
poolDictionaries: ''
category: 'Morphic-Windows'!
!SystemWindow commentStamp: '<historical>' prior: 0!
SystemWindow is the Morphic equivalent of StandardSystemView -- a labelled container for rectangular views, with iconic facilities for close, collapse/expand, and resizing.
The attribute onlyActiveOnTop, if set to true (and any call to activate will set this), determines that only the top member of a collection of such windows on the screen shall be active. To be not active means that a mouse click in any region will only result in bringing the window to the top and then making it active.!
Item was added:
+ ----- Method: SystemWindow class>>windowTitleActiveOnFirstClick (in category 'preferences') -----
+ windowTitleActiveOnFirstClick
+ <preference: 'Windows'' Controls Are Always Active'
+ category: 'windows'
+ description: '... except for grips and splitters. Those remain always active and an option to bring the window to the front.'
+ type: #Boolean>
+ ^ WindowTitleActiveOnFirstClick ifNil: [ true ]!
Item was added:
+ ----- Method: SystemWindow class>>windowTitleActiveOnFirstClick: (in category 'preferences') -----
+ windowTitleActiveOnFirstClick: boolean
+ WindowTitleActiveOnFirstClick := boolean.
+ self reconfigureWindowsForFocus.!
Item was removed:
- ----- Method: SystemWindow class>>windowsActiveOnlyOnTop (in category 'preferences') -----
- windowsActiveOnlyOnTop
- <preference: 'Windows Active Only On Top'
- category: 'windows'
- description: 'If true, a click anywhere within a background window will raise it above all other windows to become the active window. If false, all windows remain active and occluded windows will only raise when clicking in the title bar, splitters, or grips.'
- type: #Boolean>
- ^ WindowsActiveOnlyOnTop ifNil: [ true ]!
Item was removed:
- ----- Method: SystemWindow class>>windowsActiveOnlyOnTop: (in category 'preferences') -----
- windowsActiveOnlyOnTop: aBoolean
-
- aBoolean = WindowsActiveOnlyOnTop ifTrue: [^ self].
- WindowsActiveOnlyOnTop := aBoolean.
- self reconfigureWindowsForFocus.!
Item was added:
+ ----- Method: SystemWindow class>>windowsRaiseOnClick (in category 'preferences') -----
+ windowsRaiseOnClick
+ <preference: 'Windows Raise On Any Click'
+ category: 'windows'
+ description: 'If false, windows only raise when clicking on window decorations.'
+ type: #Boolean>
+ ^ WindowsRaiseOnClick ifNil: [ true ]!
Item was added:
+ ----- Method: SystemWindow class>>windowsRaiseOnClick: (in category 'preferences') -----
+ windowsRaiseOnClick: boolean
+ WindowsRaiseOnClick := boolean.!
Item was changed:
----- Method: SystemWindow>>activate (in category 'focus') -----
activate
"Bring the receiver to the top. If I am modal, bring along my modal owning window and my model child as well."
self isActive ifTrue: [self lookFocused. ^ self].
self topRendererOrSelf owner ifNil: [^ self "avoid spurious activate when drop in trash"].
self isActive: true.
"Special handling for expanded windows."
self isCollapsed ifFalse: [
model modelWakeUpIn: self.
self positionSubmorphs].
self submorphsDo: [:each | each unlock].
+ self lookFocused.!
- self
- lookFocused;
- updateFocusLookAtHand.!
Item was changed:
----- Method: SystemWindow>>beKeyWindow (in category 'top window') -----
beKeyWindow
"Let me be the most important window on the screen. I am at the top and I can have a shadow to get more attention by the user. I am the window that is responsible for window keyboard shortcuts."
| oldKeyWindow |
self isKeyWindow ifTrue: [^ self].
oldKeyWindow := TopWindow.
TopWindow := self.
PasteUpMorph globalCommandKeysEnabled ifTrue:
[ self activeHand addKeyboardListener: self ].
self
unlockWindowDecorations; "here, because all windows might be active anyway"
activate; "if not already active, activate now"
comeToFront. "key windows are on top"
"Change appearance to get noticed."
self hasDropShadow: Preferences menuAppearance3d.
(self valueOfProperty: #borderWidthWhenActive)
ifNotNil: [:bw | self acquireBorderWidth: bw].
oldKeyWindow ifNotNil: [:wnd |
wnd passivateIfNeeded.
self activeHand removeKeyboardListener: oldKeyWindow.
"Change appearance to not look prettier than the new key window."
wnd hasDropShadow: false.
(wnd valueOfProperty: #borderWidthWhenInactive)
+ ifNotNil: [:bw | wnd acquireBorderWidth: bw]].
+
+ "Synchronize focus look with position of current hand because any call could have made this window the new key window."
+ self updateFocusLookAtHand.!
- ifNotNil: [:bw | wnd acquireBorderWidth: bw]].!
Item was added:
+ ----- Method: SystemWindow>>handleMouseDown: (in category 'events') -----
+ handleMouseDown: evt
+
+ "If my submorphs handled the events, we still need to use this hook to raise."
+ (self isKeyWindow not
+ and: [self class windowsRaiseOnClick
+ or: [self windowDecorations anySatisfy: [:morph | morph bounds containsPoint: evt position]] ])
+ ifTrue: [self beKeyWindow].
+
+ ^ super handleMouseDown: evt!
Item was changed:
----- Method: SystemWindow>>justDroppedInto:event: (in category 'geometry') -----
justDroppedInto: aMorph event: anEvent
+ isCollapsed
+ ifTrue: [self position: ((self position max: 0@0) grid: 8@8).
+ collapsedFrame := self bounds]
+ ifFalse: [fullFrame := self bounds].
+
+ self beKeyWindow.
+ self hasDropShadow: true. "See #startDragFromLabel:."
+
+ aMorph == self world ifTrue: [self assureLabelAreaVisible].
+
(ToolBuilder openToolsAttachedToMouseCursor and: (self hasProperty: #initialDrop))
ifTrue: [
self removeProperty: #initialDrop.
(self submorphs detect: [:m | m isKindOf: BottomRightGripMorph] ifNone: [])
ifNotNil: [:grip |
+ grip
+ referencePoint: anEvent position;
+ setProperty: #targetHadDropShadow toValue: true "See MorphicToolBuilder >> #open:".
+ self hasDropShadow: false.
- grip referencePoint: anEvent position.
anEvent hand newMouseFocus: grip]].
-
- self hasDropShadow: (self isKeyWindow and: [Preferences menuAppearance3d]).
-
- isCollapsed
- ifTrue: [self position: ((self position max: 0@0) grid: 8@8).
- collapsedFrame := self bounds]
- ifFalse: [fullFrame := self bounds.
- self beKeyWindow].
- aMorph == self world ifTrue: [self assureLabelAreaVisible].
-
^super justDroppedInto: aMorph event: anEvent!
Item was changed:
----- Method: SystemWindow>>lockWindowDecorations (in category 'focus') -----
lockWindowDecorations
"Lock all window decrations, that is grips, splitters, and title bar."
+ self windowDecorations do: [:m | m lock].!
- self submorphsDo: [:m |
- (self paneMorphs includes: m)
- ifFalse: [m lock]].!
Item was added:
+ ----- Method: SystemWindow>>lockWindowTitle (in category 'focus') -----
+ lockWindowTitle
+
+ labelArea ifNotNil: [:m | m lock].!
Item was changed:
----- Method: SystemWindow>>lookFocused (in category 'focus') -----
lookFocused
label ifNotNil: [ label color: Color black ].
+
+ (self isKeyWindow or: [self class windowTitleActiveOnFirstClick])
-
- (self isKeyWindow or: [model windowActiveOnFirstClick])
ifTrue: [self undimWindowButtons].
+
-
self paneColorToUse in: [ : col |
self
setStripeColorsFrom: col ;
adoptPaneColor: col].!
Item was changed:
----- Method: SystemWindow>>mouseDown: (in category 'events') -----
mouseDown: evt
- | wasKeyWindow |
- (wasKeyWindow := self isKeyWindow) ifFalse: [
- evt hand releaseKeyboardFocus.
- self beKeyWindow].
-
- "If the window was locked, we did unlock it by now. If the user does not want to invest an additional click to interact with an actual widget, re-process the event."
- (wasKeyWindow not and: [model windowActiveOnFirstClick])
- ifTrue: [
- evt wasHandled: false.
- ^ self processEvent: evt].
-
evt hand
waitForClicksOrDrag: self
event: evt
selectors: { nil. nil. nil. #startDragFromLabel: }
threshold: HandMorph dragThreshold.!
Item was changed:
----- Method: SystemWindow>>mouseEnter: (in category 'events') -----
mouseEnter: anEvent
"Handle a mouseEnter event, meaning the mouse just entered my bounds with no button pressed. The default response is to let my eventHandler, if any, handle it."
super mouseEnter: anEvent.
+ self isActive ifTrue: [self lookFocused].!
- self isActive
- ifTrue: [self lookFocused]
- ifFalse: [model windowActiveOnFirstClick
- ifTrue: [self undimWindowButtons]].!
Item was changed:
----- Method: SystemWindow>>mouseEnterDragging: (in category 'events') -----
mouseEnterDragging: evt
"unlock children for drop operations"
self flag: #performance. "mt: There may be no need to change appearance if no widget wants the drop."
+ self isActive ifTrue: [self lookFocused].
- self isActive
- ifTrue: [self lookFocused]
- ifFalse: [model windowActiveOnFirstClick
- ifTrue: [self undimWindowButtons]].
(self isActive not and: [evt hand hasSubmorphs]) ifTrue: [
self activate. "unlock contents for drop"
evt hand addMouseListener: self. "for drop completion on submorph"
].!
Item was changed:
----- Method: SystemWindow>>mouseLeave: (in category 'events') -----
mouseLeave: anEvent
"Handle a mouseEnter event, meaning the mouse just entered my bounds with no button pressed. The default response is to let my eventHandler, if any, handle it."
super mouseLeave: anEvent.
+ model windowActiveOnFirstClick ifTrue: [self lookUnfocused].!
- (self isActive and: [self class windowsActiveOnlyOnTop not])
- ifTrue: [self lookUnfocused]
- ifFalse: [self isKeyWindow
- ifFalse: [self dimWindowButtons]].!
Item was changed:
----- Method: SystemWindow>>mouseLeaveDragging: (in category 'events') -----
mouseLeaveDragging: evt
"Passivate after drop operations if needed."
+ model windowActiveOnFirstClick ifTrue: [self lookUnfocused].
- (self isActive and: [self class windowsActiveOnlyOnTop not])
- ifTrue: [self lookUnfocused]
- ifFalse: [self isKeyWindow
- ifFalse: [self dimWindowButtons]].
(self isKeyWindow not and: [evt hand hasSubmorphs]) ifTrue:[
self passivateIfNeeded.
evt hand removeMouseListener: self. "no more drop completion possible on submorph"
].!
Item was changed:
----- Method: SystemWindow>>passivateIfNeeded (in category 'focus') -----
passivateIfNeeded
+ model windowActiveOnFirstClick
+ ifFalse: [self passivate]
+ ifTrue: [self lookUnfocused].
+
+ self unlockWindowDecorations.
+
+ self class windowTitleActiveOnFirstClick
+ ifFalse: [self lockWindowTitle]
+ ifTrue: [self unlockWindowTitle].!
- self class windowsActiveOnlyOnTop
- ifTrue: [self passivate]
- ifFalse: [self lockWindowDecorations; lookUnfocused].!
Item was changed:
----- Method: SystemWindow>>unlockWindowDecorations (in category 'focus') -----
unlockWindowDecorations
"Unlock all window decrations, that is grips, splitters, and title bar."
+ self windowDecorations do: [:m | m unlock].
- self submorphsDo: [:m |
- (self paneMorphs includes: m)
- ifFalse: [m unlock]].
-
+ "Migrate old window instances. Can be removed in the future."
- "Migrate old window instances."
labelArea ifNotNil: [:m | m submorphsDo: [:sm | sm unlock]].!
Item was added:
+ ----- Method: SystemWindow>>unlockWindowTitle (in category 'focus') -----
+ unlockWindowTitle
+
+ labelArea ifNotNil: [:m | m unlock].!
Item was changed:
----- Method: SystemWindow>>updateFocusLookAtHand (in category 'focus') -----
updateFocusLookAtHand
"If there is more than one active window, look for the mouse cursor and update the window focus look accordingly. This method is not on the class-side because we need our world and some active hand."
+ model windowActiveOnFirstClick ifFalse: [^ self].
- self class windowsActiveOnlyOnTop ifTrue: [^ self].
((self class windowsIn: self world)
do: [:window | window lookUnfocused];
select: [:window | window bounds containsPoint: self activeHand position])
+ ifNotEmpty: [:windowsPointed | windowsPointed first lookFocused "only to foremost window"].!
- ifNotEmpty: [:windowsPointed | windowsPointed first lookFocused "only to foremost window"].
-
- self class keyWindow lookFocused.!
Item was added:
+ ----- Method: SystemWindow>>windowDecorations (in category 'focus') -----
+ windowDecorations
+
+ ^ self submorphs copyWithoutAll: self paneMorphs!
Item was changed:
+ (PackageInfo named: 'Morphic') postscript: 'SystemWindow reconfigureWindowsForFocus.'!
- (PackageInfo named: 'Morphic') postscript: 'MorphicAlarmQueue allInstancesDo: #migrate'!
Patrick Rein uploaded a new version of Help-Squeak-TerseGuide to project The Trunk:
http://source.squeak.org/trunk/Help-Squeak-TerseGuide-pre.6.mcz
==================== Summary ====================
Name: Help-Squeak-TerseGuide-pre.6
Author: pre
Time: 25 May 2016, 10:31:44.454733 am
UUID: d0c651c0-c1cb-46c8-982a-ea2ec9f08c60
Ancestors: Help-Squeak-TerseGuide-kfr.5
Added a section on exception handling
=============== Diff against Help-Squeak-TerseGuide-kfr.5 ===============
Item was added:
+ ----- Method: TerseGuideHelp class>>exceptionHandling (in category 'pages') -----
+ exceptionHandling
+
+ ^HelpTopic
+ title: 'Exception Handling'
+ contents:
+
+ '"************************************************************************
+ * Exceptions: *
+ ************************************************************************"
+ | x |
+ x := Object new.
+ x error: ''Throwing an error''.
+
+ NotFound signal. "simply throwing a specific exception"
+ NotFound signal: ''Throwing with a message''. "throwing an exception with a custom text"
+
+ x := [ 5 / 0 ] "simple exception handler"
+ on: ZeroDivide do: [ 10 ].
+ x := [ 5 / 0 ] "processing the exception in an exception handler"
+ on: Error do: [:e | Transcript showln: e printString ].
+ x := [ 5 / 0 ] "different exception handlers for the same block"
+ on: ZeroDivide do: [ 10 ]
+ on: MessageNotUnderstood do: [ 20 ].
+ x := [ 5 / 0 ] "one exception handler for two different exceptions"
+ on: ZeroDivide , ArithmeticError do: [ 10 ].
+
+
+ '!
Item was changed:
----- Method: TerseGuideHelp class>>pages (in category 'accessing') -----
pages
^ #( introduction transcript assignment constants boolean arithmetic
+ bitwise conversion block exceptionHandling methodCall conditionalStatement
- bitwise conversion block methodCall conditionalStatement
iterationStatement character symbol string array orderedCollection
sortedCollection bag set interval association dictionary internalStream
fileStream date time point rectangle pen dynamic metaclass debugging
misc )!
Patrick Rein uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-pre.1026.mcz
==================== Summary ====================
Name: Kernel-pre.1026
Author: pre
Time: 25 May 2016, 10:25:41.183733 am
UUID: 389c2806-d933-41e0-9dd0-4f4035f3455c
Ancestors: Kernel-mt.1025
Improves argument naming in exception handling methods to make discovery of ExceptionSet easier.
=============== Diff against Kernel-mt.1025 ===============
Item was changed:
----- Method: BlockClosure>>on:do: (in category 'exceptions') -----
+ on: exceptionOrExceptionSet do: handlerAction
- on: exception do: handlerAction
"Evaluate the receiver in the scope of an exception handler."
| handlerActive |
<primitive: 199> "just a marker, fail and execute the following"
handlerActive := true.
^ self value!
Item was changed:
----- Method: BlockClosure>>on:do:on:do: (in category 'exceptions') -----
+ on: exception1 do: block1 on: exception2 do: block2
- on: exc1 do: block1 on: exc2 do: block2
^[
self
+ on: exception1
- on: exc1
do: block1 ]
+ on: exception2
- on: exc2
do: block2!
Item was changed:
----- Method: BlockClosure>>on:do:on:do:on:do: (in category 'exceptions') -----
+ on: exception1 do: block1 on: exception2 do: block2 on: exception3 do: block3
- on: exc1 do: block1 on: exc2 do: block2 on: exc3 do: block3
^[
self
+ on: exception1
- on: exc1
do: block1 ]
+ on: exception2
- on: exc2
do: block2
+ on: exception3
- on: exc3
do: block3!
Item was changed:
----- Method: BlockContext>>on:do: (in category 'exceptions') -----
+ on: exceptionOrExceptionSet do: handlerAction
- on: exception do: handlerAction
"Evaluate the receiver in the scope of an exception handler."
| handlerActive |
<primitive: 199>
handlerActive := true.
^self value!
Item was changed:
----- Method: BlockContext>>on:do:on:do: (in category 'exceptions') -----
+ on: exception1 do: block1 on: exception2 do: block2
- on: exc1 do: block1 on: exc2 do: block2
^[
self
+ on: exception1
- on: exc1
do: block1 ]
+ on: exception2
- on: exc2
do: block2!
Item was changed:
----- Method: BlockContext>>on:do:on:do:on:do: (in category 'exceptions') -----
+ on: exception1 do: block1 on: exception2 do: block2 on: exception3 do: block3
- on: exc1 do: block1 on: exc2 do: block2 on: exc3 do: block3
^[
self
+ on: exception1
- on: exc1
do: block1 ]
+ on: exception2
- on: exc2
do: block2
+ on: exception3
- on: exc3
do: block3!